更新
This commit is contained in:
@@ -18,6 +18,9 @@ use think\facade\Log;
|
||||
*/
|
||||
class DiagnosisAiLogic extends BaseLogic
|
||||
{
|
||||
/** @var string Safe machine-readable code for the current assistant request. */
|
||||
private static $assistantErrorCode = 'AI_ASSISTANT_FAILED';
|
||||
|
||||
private const PROMPT_VERSION = 'patient-context-case-explain-v2';
|
||||
|
||||
private const ASSISTANT_PROMPT_VERSION = 'patient-context-assistant-v2';
|
||||
@@ -354,6 +357,7 @@ class DiagnosisAiLogic extends BaseLogic
|
||||
int $adminId,
|
||||
array $adminInfo
|
||||
): ?array {
|
||||
self::$assistantErrorCode = 'AI_ASSISTANT_FAILED';
|
||||
$diagnosis = self::loadAuthorizedDiagnosis(
|
||||
$diagnosisId,
|
||||
$adminId,
|
||||
@@ -433,6 +437,14 @@ class DiagnosisAiLogic extends BaseLogic
|
||||
$diagnosisId = (int) ($prepared['diagnosis_id'] ?? 0);
|
||||
$profile = (string) ($prepared['profile'] ?? '');
|
||||
$adminId = (int) ($prepared['admin_id'] ?? 0);
|
||||
$deliveredDelta = false;
|
||||
$forwardDelta = static function (string $delta) use (&$deliveredDelta, $onDelta) {
|
||||
$accepted = $onDelta($delta);
|
||||
if ($accepted !== false) {
|
||||
$deliveredDelta = true;
|
||||
}
|
||||
return $accepted;
|
||||
};
|
||||
|
||||
try {
|
||||
$result = DifyChatService::streamChat(
|
||||
@@ -440,7 +452,7 @@ class DiagnosisAiLogic extends BaseLogic
|
||||
is_array($prepared['inputs'] ?? null) ? $prepared['inputs'] : [],
|
||||
(string) ($prepared['query'] ?? ''),
|
||||
(string) ($prepared['user'] ?? ''),
|
||||
$onDelta,
|
||||
$forwardDelta,
|
||||
$shouldAbort,
|
||||
is_array($prepared['files'] ?? null) ? $prepared['files'] : []
|
||||
);
|
||||
@@ -452,6 +464,7 @@ class DiagnosisAiLogic extends BaseLogic
|
||||
$e,
|
||||
(string) ($prepared['task'] ?? '')
|
||||
);
|
||||
self::$assistantErrorCode = 'UPSTREAM_UNAVAILABLE';
|
||||
self::setError('AI 助手暂时不可用,请稍后重试');
|
||||
return null;
|
||||
}
|
||||
@@ -464,6 +477,47 @@ class DiagnosisAiLogic extends BaseLogic
|
||||
(string) ($prepared['task'] ?? ''),
|
||||
is_array($result) ? $result : []
|
||||
);
|
||||
|
||||
// Some Dify-compatible gateways accept blocking chat but reject or
|
||||
// incompletely terminate streaming responses. Before any delta has
|
||||
// reached the doctor it is safe to make one blocking compatibility
|
||||
// attempt; after a delta, retrying could duplicate clinical text.
|
||||
$streamErrorCode = strtoupper(trim((string) ($result['error_code'] ?? '')));
|
||||
if (
|
||||
!$deliveredDelta
|
||||
&& in_array(
|
||||
$streamErrorCode,
|
||||
['UPSTREAM_REJECTED', 'INCOMPLETE_RESPONSE', 'EMPTY_RESPONSE'],
|
||||
true
|
||||
)
|
||||
) {
|
||||
try {
|
||||
$result = DifyChatService::chat(
|
||||
$profile,
|
||||
is_array($prepared['inputs'] ?? null) ? $prepared['inputs'] : [],
|
||||
(string) ($prepared['query'] ?? ''),
|
||||
(string) ($prepared['user'] ?? ''),
|
||||
is_array($prepared['files'] ?? null) ? $prepared['files'] : []
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
self::logAssistantFailure(
|
||||
$diagnosisId,
|
||||
$profile,
|
||||
$adminId,
|
||||
$e,
|
||||
(string) ($prepared['task'] ?? '')
|
||||
);
|
||||
}
|
||||
if (empty($result['ok'])) {
|
||||
self::logAssistantUpstreamError(
|
||||
$diagnosisId,
|
||||
$profile,
|
||||
$adminId,
|
||||
(string) ($prepared['task'] ?? ''),
|
||||
is_array($result) ? $result : []
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self::formatAssistantResult($prepared, $result);
|
||||
@@ -480,6 +534,7 @@ class DiagnosisAiLogic extends BaseLogic
|
||||
// 附带上游错误码,让医生反馈时管理员能直接定位是配置、体积还是上游拒绝。
|
||||
$message = (string) ($result['error'] ?? 'AI 助手暂时不可用,请稍后重试');
|
||||
$errorCode = trim((string) ($result['error_code'] ?? ''));
|
||||
self::$assistantErrorCode = self::normaliseAssistantErrorCode($errorCode);
|
||||
if ($errorCode !== '') {
|
||||
$message .= '(' . $errorCode . ')';
|
||||
}
|
||||
@@ -488,6 +543,7 @@ class DiagnosisAiLogic extends BaseLogic
|
||||
}
|
||||
$content = self::cleanText($result['content'] ?? '', self::MAX_REPORT_LENGTH, true);
|
||||
if ($content === '') {
|
||||
self::$assistantErrorCode = 'EMPTY_RESPONSE';
|
||||
self::setError('AI 助手未返回内容,请重试');
|
||||
return null;
|
||||
}
|
||||
@@ -520,6 +576,20 @@ class DiagnosisAiLogic extends BaseLogic
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/** Return a safe code for the current SSE terminal error event. */
|
||||
public static function getAssistantErrorCode(): string
|
||||
{
|
||||
return self::normaliseAssistantErrorCode(self::$assistantErrorCode);
|
||||
}
|
||||
|
||||
private static function normaliseAssistantErrorCode(string $code): string
|
||||
{
|
||||
$code = strtoupper(trim($code));
|
||||
return preg_match('/^[A-Z][A-Z0-9_]{2,63}$/', $code) === 1
|
||||
? $code
|
||||
: 'AI_ASSISTANT_FAILED';
|
||||
}
|
||||
|
||||
/** @return array<string,mixed>|null */
|
||||
private static function parsePrescriptionDraft(string $content): ?array
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user