This commit is contained in:
Your Name
2026-10-09 14:13:57 +08:00
parent 51d8fd409e
commit 5706e9a6a5
15 changed files with 732 additions and 162 deletions
@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace app\adminapi\service;
use think\facade\Log;
/** A request-local, patient-free timing record for the diagnosis assistant SSE path. */
final class AssistantSseTiming
{
private const PHASES = ['prepare', 'sse_start', 'first_delta', 'upstream_done', 'sse_done'];
private const NUMBER_FIELDS = [
'duration_ms', 'aggregate_ms', 'compaction_ms', 'retry_ms',
'source_bytes', 'query_bytes', 'attachment_count',
'delta_bytes', 'delta_count', 'first_delta_ms',
];
private const BOOLEAN_FIELDS = ['compaction_attempted', 'retry_attempted', 'first_delta_observed'];
private const ERROR_CODES = [
'AI_ASSISTANT_FAILED', 'UPSTREAM_UNAVAILABLE', 'UPSTREAM_REJECTED',
'INCOMPLETE_RESPONSE', 'EMPTY_RESPONSE', 'UPSTREAM_TIMEOUT',
'CONFIG_DISABLED', 'INVALID_PROFILE', 'CONFIG_MISSING', 'CONFIG_INVALID',
'CURL_UNAVAILABLE', 'SKILL_UNAVAILABLE', 'CLIENT_DISCONNECTED',
'STREAM_DELIVERY_FAILED', 'REQUEST_BUILD_FAILED', 'CURL_INIT_FAILED',
'UPSTREAM_BUSY', 'INVALID_RESPONSE',
];
private string $traceId;
public function __construct()
{
$this->traceId = bin2hex(random_bytes(16));
}
public function id(): string
{
return $this->traceId;
}
public static function elapsedMs(int $startedAt): int
{
return max(0, (int) floor((hrtime(true) - $startedAt) / 1000000));
}
/**
* Only a fixed set of numeric and boolean metadata reaches the log. In particular,
* arbitrary prompt text, patient identifiers, URLs and credentials are discarded.
*
* @param array<string,mixed> $metrics
* @return array<string,int|bool|string>
*/
public function event(string $phase, array $metrics = []): array
{
$event = [
'trace_id' => $this->traceId,
'phase' => in_array($phase, self::PHASES, true) ? $phase : 'upstream_done',
];
foreach (self::NUMBER_FIELDS as $field) {
if (isset($metrics[$field]) && is_int($metrics[$field])) {
$event[$field] = max(0, $metrics[$field]);
}
}
foreach (self::BOOLEAN_FIELDS as $field) {
if (isset($metrics[$field]) && is_bool($metrics[$field])) {
$event[$field] = $metrics[$field];
}
}
if (isset($metrics['status']) && in_array($metrics['status'], ['ok', 'error', 'exception', 'disconnected'], true)) {
$event['status'] = $metrics['status'];
}
if (isset($metrics['error_code']) && in_array($metrics['error_code'], self::ERROR_CODES, true)) {
$event['error_code'] = $metrics['error_code'];
}
return $event;
}
/** @param array<string,mixed> $metrics */
public function log(string $phase, array $metrics = []): void
{
try {
Log::info('diagnosis ai assistant timing ' . json_encode($this->event($phase, $metrics)));
} catch (\Throwable $ignored) {
// Telemetry must never interrupt the clinical request.
}
}
}