This commit is contained in:
Your Name
2026-10-23 11:39:56 +08:00
parent 86476920cf
commit ff98014fa2
11 changed files with 766 additions and 20 deletions
+21 -1
View File
@@ -1350,6 +1350,8 @@ class DifyChatService
$buffer = '';
$state = self::newStreamState();
$upstreamTiming = is_callable($options['on_upstream_timing'] ?? null) ? new DifyStreamTiming() : null;
$state['timing'] = $upstreamTiming;
$state['on_task'] = $options['on_task'] ?? null;
$state['on_terminal'] = $options['on_terminal'] ?? null;
$notify = self::transferProgressNotifier($options);
@@ -1416,9 +1418,10 @@ class DifyChatService
$responseCode = 0;
$errorBody = '';
$errorBodyOverflow = false;
$header = static function ($handle, string $line) use (&$responseCode, &$errorBody, &$errorBodyOverflow, $markSubmitted): int {
$header = static function ($handle, string $line) use (&$responseCode, &$errorBody, &$errorBodyOverflow, $markSubmitted, $upstreamTiming): int {
if (preg_match('/^HTTP\/\S+\s+(\d{3})(?:\s|$)/i', trim($line), $matches) === 1) {
$responseCode = (int) $matches[1];
$upstreamTiming?->headers($responseCode);
$errorBody = '';
$errorBodyOverflow = false;
if ($responseCode >= 200 && $responseCode < 300) $markSubmitted();
@@ -1432,9 +1435,11 @@ class DifyChatService
&$responseCode,
&$errorBody,
&$errorBodyOverflow,
$upstreamTiming,
$forward,
$abort
): int {
$upstreamTiming?->body(strlen($chunk));
if ($responseCode < 200 || $responseCode >= 300) {
// Keep a bounded JSON error only until its safe code/reason has
// been extracted. Never feed HTTP error bodies to the SSE parser.
@@ -1499,6 +1504,15 @@ class DifyChatService
// A callback failure may escape libcurl before the regular cancel
// poll. Make the coordinated stop attempt before closing this handle.
try { $abort(); } catch (\Throwable $ignored) {}
if ($upstreamTiming !== null) {
try {
$upstreamTiming->transport(['connect_ms' => curl_getinfo($ch, CURLINFO_CONNECT_TIME) * 1000,
'tls_ms' => curl_getinfo($ch, CURLINFO_APPCONNECT_TIME) * 1000,
'header_ttfb_ms' => curl_getinfo($ch, CURLINFO_STARTTRANSFER_TIME) * 1000,
'transfer_ms' => curl_getinfo($ch, CURLINFO_TOTAL_TIME) * 1000,
'http_status' => $httpCode, 'curl_errno' => $errno]);
} catch (\Throwable $ignored) {}
}
curl_close($ch);
}
@@ -1522,6 +1536,9 @@ class DifyChatService
$state['upstream_reason'] = $metadata['upstream_reason'];
}
unset($errorBody);
if ($upstreamTiming !== null) {
try { ($options['on_upstream_timing'])($upstreamTiming); } catch (\Throwable $ignored) {}
}
return [
'errno' => $errno,
@@ -1686,6 +1703,7 @@ class DifyChatService
}
}
$event = strtolower((string) ($decoded['event'] ?? ''));
if (($state['timing'] ?? null) instanceof DifyStreamTiming) $state['timing']->frame($decoded, $protocol);
if ($event === 'workflow_finished') {
$rawStatus = $decoded['data']['status'] ?? '';
$status = is_string($rawStatus) ? strtolower($rawStatus) : '';
@@ -1720,6 +1738,7 @@ class DifyChatService
$state['message_id'] = (string) ($decoded['message_id'] ?? $state['message_id']);
} else {
if ($state['finished'] || $state['upstream_error']) return;
if (($state['timing'] ?? null) instanceof DifyStreamTiming) $state['timing']->frame($decoded, $protocol);
if (is_array($decoded['error'] ?? null)) {
$state['upstream_error'] = true;
$metadata = self::upstreamErrorMetadata($decoded);
@@ -1745,6 +1764,7 @@ class DifyChatService
return;
}
if ($state['discard_deltas']) return;
if (($state['timing'] ?? null) instanceof DifyStreamTiming) $state['timing']->delivered();
$state['content'] .= $delta;
$state['emitted'] = true;
}