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
@@ -59,6 +59,14 @@ $logic = file_get_contents(dirname(__DIR__) . '/app/adminapi/logic/tcm/Diagnosis
$validate = file_get_contents(dirname(__DIR__) . '/app/adminapi/validate/tcm/DiagnosisValidate.php');
$auth = file_get_contents(dirname(__DIR__) . '/app/adminapi/http/middleware/AuthMiddleware.php');
assistantStreamExpect(is_string($controller) && is_string($logic) && is_string($validate) && is_string($auth), 'stream implementation sources are readable');
assistantStreamExpect(
str_contains($controller, 'diagnosis ai assistant sse failed'),
'unexpected SSE failures retain a privacy-safe server log entry'
);
assistantStreamExpect(
!str_contains($controller, "'exception_message' => \$e->getMessage()"),
'unexpected SSE failures never log raw exception messages'
);
$actionStart = strpos($controller, 'public function aiAssistantStream()');
$checkAt = strpos($controller, "goCheck('aiAssistant')", $actionStart);
+8 -1
View File
@@ -115,8 +115,15 @@ difyStreamExpect(
callDifyStreamPrivate('shouldTryNextProtocol', [[
'errno' => 0,
'http_code' => 400,
], false]) === false,
'business input rejection is not hidden by an alternate protocol attempt'
);
difyStreamExpect(
callDifyStreamPrivate('shouldTryNextProtocol', [[
'errno' => 0,
'http_code' => 404,
], false]) === true,
'an ambiguous endpoint rejected before generation tries the alternate protocol'
'an unavailable path tries the alternate protocol'
);
difyStreamExpect(
callDifyStreamPrivate('shouldTryNextProtocol', [[
@@ -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');