fix: emit terminal otel spans for command status (#230)

This commit is contained in:
Lyon
2026-06-23 11:44:49 +08:00
committed by GitHub
parent 0ac6872a7e
commit c0a5704c10
+16 -7
View File
@@ -827,34 +827,43 @@ async function route({ method, url, body, store, sourceCommit, authSummary, runn
store.getRun(runId), store.getRun(runId),
store.appendEvent(runId, type, payload), store.appendEvent(runId, type, payload),
]); ]);
emitRunEventOtelSpan(type, payload, run, startedAt); await emitRunEventOtelSpan(type, payload, run, startedAt);
return event as unknown as JsonValue; return event as unknown as JsonValue;
} }
const statusMatch = path.match(/^\/api\/v1\/runs\/([^/]+)\/status$/u); const statusMatch = path.match(/^\/api\/v1\/runs\/([^/]+)\/status$/u);
if (method === "PATCH" && statusMatch) { if (method === "PATCH" && statusMatch) {
const startedAt = Date.now();
const runId = statusMatch[1] ?? "";
const record = asRecord(body, "status"); const record = asRecord(body, "status");
const terminalStatus = record.terminalStatus === "completed" || record.terminalStatus === "failed" || record.terminalStatus === "blocked" || record.terminalStatus === "cancelled" ? record.terminalStatus : "failed"; const terminalStatus = record.terminalStatus === "completed" || record.terminalStatus === "failed" || record.terminalStatus === "blocked" || record.terminalStatus === "cancelled" ? record.terminalStatus : "failed";
return await store.finishRun(statusMatch[1] ?? "", { const run = await store.finishRun(runId, {
terminalStatus, terminalStatus,
failureKind: typeof record.failureKind === "string" ? record.failureKind as never : null, failureKind: typeof record.failureKind === "string" ? record.failureKind as never : null,
failureMessage: typeof record.failureMessage === "string" ? record.failureMessage : null, failureMessage: typeof record.failureMessage === "string" ? record.failureMessage : null,
...(typeof record.threadId === "string" ? { threadId: record.threadId } : {}), ...(typeof record.threadId === "string" ? { threadId: record.threadId } : {}),
...(typeof record.turnId === "string" ? { turnId: record.turnId } : {}), ...(typeof record.turnId === "string" ? { turnId: record.turnId } : {}),
}) as unknown as JsonValue; });
await emitRunEventOtelSpan("terminal_status", { terminalStatus, failureKind: typeof record.failureKind === "string" ? record.failureKind : null, message: typeof record.failureMessage === "string" ? record.failureMessage : null, threadId: typeof record.threadId === "string" ? record.threadId : null, turnId: typeof record.turnId === "string" ? record.turnId : null }, run, startedAt);
return run as unknown as JsonValue;
} }
const ackMatch = path.match(/^\/api\/v1\/commands\/([^/]+)\/ack$/u); const ackMatch = path.match(/^\/api\/v1\/commands\/([^/]+)\/ack$/u);
if (method === "POST" && ackMatch) return await store.ackCommand(ackMatch[1] ?? "") as unknown as JsonValue; if (method === "POST" && ackMatch) return await store.ackCommand(ackMatch[1] ?? "") as unknown as JsonValue;
const commandStatusMatch = path.match(/^\/api\/v1\/commands\/([^/]+)\/status$/u); const commandStatusMatch = path.match(/^\/api\/v1\/commands\/([^/]+)\/status$/u);
if (method === "PATCH" && commandStatusMatch) { if (method === "PATCH" && commandStatusMatch) {
const startedAt = Date.now();
const record = asRecord(body, "commandStatus"); const record = asRecord(body, "commandStatus");
const terminalStatus = record.terminalStatus === "completed" || record.terminalStatus === "failed" || record.terminalStatus === "blocked" || record.terminalStatus === "cancelled" ? record.terminalStatus : "failed"; const terminalStatus = record.terminalStatus === "completed" || record.terminalStatus === "failed" || record.terminalStatus === "blocked" || record.terminalStatus === "cancelled" ? record.terminalStatus : "failed";
return await store.finishCommand(commandStatusMatch[1] ?? "", { const command = await store.finishCommand(commandStatusMatch[1] ?? "", {
terminalStatus, terminalStatus,
failureKind: typeof record.failureKind === "string" ? record.failureKind as never : null, failureKind: typeof record.failureKind === "string" ? record.failureKind as never : null,
failureMessage: typeof record.failureMessage === "string" ? record.failureMessage : null, failureMessage: typeof record.failureMessage === "string" ? record.failureMessage : null,
...(typeof record.threadId === "string" ? { threadId: record.threadId } : {}), ...(typeof record.threadId === "string" ? { threadId: record.threadId } : {}),
...(typeof record.turnId === "string" ? { turnId: record.turnId } : {}), ...(typeof record.turnId === "string" ? { turnId: record.turnId } : {}),
}) as unknown as JsonValue; });
const run = await store.getRun(command.runId);
await emitRunEventOtelSpan("backend_status", { phase: "command-terminal", commandId: command.id, state: command.state, terminalStatus, failureKind: typeof record.failureKind === "string" ? record.failureKind : null, message: typeof record.failureMessage === "string" ? record.failureMessage : null, threadId: typeof record.threadId === "string" ? record.threadId : null, turnId: typeof record.turnId === "string" ? record.turnId : null }, run, startedAt);
if (command.type === "turn") await emitRunEventOtelSpan("terminal_status", { terminalStatus, failureKind: typeof record.failureKind === "string" ? record.failureKind : null, message: typeof record.failureMessage === "string" ? record.failureMessage : null, commandId: command.id, threadId: typeof record.threadId === "string" ? record.threadId : null, turnId: typeof record.turnId === "string" ? record.turnId : null }, run, startedAt);
return command as unknown as JsonValue;
} }
const commandCancelMatch = path.match(/^\/api\/v1\/commands\/([^/]+)\/cancel$/u); const commandCancelMatch = path.match(/^\/api\/v1\/commands\/([^/]+)\/cancel$/u);
if (method === "POST" && commandCancelMatch) { if (method === "POST" && commandCancelMatch) {
@@ -1072,14 +1081,14 @@ function commandIsTerminal(command: CommandRecord): boolean {
return command.state === "completed" || command.state === "failed" || command.state === "cancelled"; return command.state === "completed" || command.state === "failed" || command.state === "cancelled";
} }
function emitRunEventOtelSpan(type: RunEvent["type"], payload: JsonRecord, run: RunRecord, startedAt: number): void { async function emitRunEventOtelSpan(type: RunEvent["type"], payload: JsonRecord, run: RunRecord, startedAt: number): Promise<void> {
const phase = stringJsonValue(payload.phase); const phase = stringJsonValue(payload.phase);
const terminalStatus = stringJsonValue(payload.terminalStatus); const terminalStatus = stringJsonValue(payload.terminalStatus);
const failureKind = stringJsonValue(payload.failureKind); const failureKind = stringJsonValue(payload.failureKind);
const eventName = runEventOtelSpanName(type, phase, terminalStatus, failureKind); const eventName = runEventOtelSpanName(type, phase, terminalStatus, failureKind);
if (!eventName) return; if (!eventName) return;
const isError = type === "error" || terminalStatus === "failed" || terminalStatus === "blocked"; const isError = type === "error" || terminalStatus === "failed" || terminalStatus === "blocked";
void emitAgentRunOtelSpan(eventName, run, process.env, { await emitAgentRunOtelSpan(eventName, run, process.env, {
startTimeMs: startedAt, startTimeMs: startedAt,
kind: 2, kind: 2,
status: isError ? "error" : "ok", status: isError ? "error" : "ok",