This commit is contained in:
Your Name
2026-08-27 15:41:09 +08:00
parent 74ff568ba4
commit 75e214dc08
6 changed files with 303 additions and 111 deletions
@@ -186,6 +186,80 @@ expectSame([], $plan[1]['files'], 'the fallback attempt sends no attachments');
expectSame(9, count($plan[1]['omitted']), 'the fallback attempt declares every attachment');
expectSame(1, count(callPrivate('buildAttemptPlan', [[], []])), 'a request without attachments is attempted once');
$inputPlan = callPrivate('buildInputAttemptPlan', [['prompt_version' => 'v2']]);
expectSame(2, count($inputPlan), 'structured Dify inputs get one compatibility fallback');
expectSame([], $inputPlan[1], 'the compatibility fallback uses an empty inputs object');
expectSame([[]], callPrivate('buildInputAttemptPlan', [[]]), 'empty inputs are not retried twice');
$difyInputSpec = ['protocol' => 'dify'];
$openAiInputSpec = ['protocol' => 'openai'];
expectSame(
true,
callPrivate('isInputRejection', [
['errno' => 0, 'http_code' => 400, 'body' => '{"code":"invalid_param"}'],
$difyInputSpec,
['prompt_version' => 'v2'],
]),
'Dify invalid_param retries with query-only input'
);
expectSame(
false,
callPrivate('isInputRejection', [
['errno' => 0, 'http_code' => 400, 'body' => '{"code":"invalid_param"}'],
$difyInputSpec,
[],
]),
'an already empty inputs object is never retried'
);
expectSame(
false,
callPrivate('isInputRejection', [
['errno' => 0, 'http_code' => 400, 'body' => '{"code":"invalid_param"}'],
$openAiInputSpec,
['prompt_version' => 'v2'],
]),
'OpenAI protocol does not use the Dify input fallback'
);
expectSame(
false,
callPrivate('isInputRejection', [
['errno' => 0, 'http_code' => 400, 'body' => '{"code":"provider_quota_exceeded"}'],
$difyInputSpec,
['prompt_version' => 'v2'],
]),
'quota and provider failures are not submitted twice'
);
expectSame(
true,
callPrivate('isInputRejection', [
[
'errno' => 0,
'http_code' => 200,
'upstream_error' => true,
'upstream_code' => 'invalid_param',
'emitted' => false,
],
$difyInputSpec,
['prompt_version' => 'v2'],
]),
'a streaming invalid_param before any delta also retries without inputs'
);
expectSame(
false,
callPrivate('isInputRejection', [
[
'errno' => 0,
'http_code' => 200,
'upstream_error' => true,
'upstream_code' => 'invalid_param',
'emitted' => true,
],
$difyInputSpec,
['prompt_version' => 'v2'],
]),
'a stream that already emitted content is never replayed'
);
expectSame(true, callPrivate('shouldRetryWithoutFiles', [400, $capped['kept']]), 'invalid_param retries without attachments');
expectSame(true, callPrivate('shouldRetryWithoutFiles', [413, $capped['kept']]), 'oversized attachments retry without attachments');
expectSame(false, callPrivate('shouldRetryWithoutFiles', [400, []]), 'a text-only rejection is not retried');