更新
This commit is contained in:
@@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user