fix: 区分 provider retry 与终态失败

This commit is contained in:
lyon
2026-06-20 09:51:30 +08:00
parent 40a54eef20
commit c51dd122e2
5 changed files with 151 additions and 11 deletions
+57 -5
View File
@@ -1612,6 +1612,7 @@ function agentRunProviderTrace({ base = {}, result = {}, terminalStatus = null }
terminalStatus: terminalStatus ?? result?.terminalStatus ?? null,
failureKind: result?.failureKind ?? null,
failureMessage: result?.failureMessage ?? result?.blocker?.message ?? null,
willRetry: result?.willRetry === true ? true : undefined,
valuesPrinted: false
};
}
@@ -1692,16 +1693,36 @@ function agentRunReplyText(result = {}) {
function agentRunResultToCodeAgentPayload({ base, result, traceStore, traceId, appendResultEvent = true }) {
const terminalStatus = String(result?.terminalStatus ?? "");
const terminal = terminalStatus === "completed" || terminalStatus === "failed" || terminalStatus === "blocked" || terminalStatus === "cancelled";
const normalizedTerminalStatus = terminalStatus.trim().toLowerCase().replace(/_/gu, "-");
const retryableInterruption = normalizedTerminalStatus !== "completed" && isRetryableProviderInterruptionPayload(result);
const terminal = !retryableInterruption && ["completed", "failed", "blocked", "cancelled", "canceled"].includes(normalizedTerminalStatus);
if (!terminal) {
return decorateAgentRunRunningResult({ base, mapping: base.agentRun, traceStore, traceId });
if (retryableInterruption && appendResultEvent) {
traceStore.append(traceId, agentRunProviderRetryEvent({ base, payload: result, status: "retrying", label: "agentrun:provider-retry:provider-stream-disconnected" }), base.agentRun);
}
const runningMapping = retryableInterruption ? {
...base.agentRun,
status: "retrying",
commandState: "retrying",
failureKind: normalizedFailureKind(result?.failureKind),
willRetry: true
} : base.agentRun;
const running = decorateAgentRunRunningResult({ base, mapping: runningMapping, traceStore, traceId });
if (!retryableInterruption) return running;
return {
...running,
status: "retrying",
providerTrace: agentRunProviderTrace({ base, result, terminalStatus }),
transient: { failureKind: normalizedFailureKind(result?.failureKind), willRetry: true, valuesRedacted: true },
valuesPrinted: false
};
}
const now = nowIso();
const runnerTrace = traceStore.snapshot(traceId, agentRunTraceMeta({}, {}));
const terminalEventCreatedAt = agentRunResultTraceCreatedAt(runnerTrace, now);
const providerTrace = agentRunProviderTrace({ base, result, terminalStatus });
const replyText = agentRunReplyText(result);
if (terminalStatus === "completed") {
if (normalizedTerminalStatus === "completed") {
const traceReplyText = agentRunTraceTerminalAssistantText(runnerTrace);
const completedReplyText = replyText || traceReplyText;
const finalResponse = completedReplyText ? agentRunCompletedFinalResponse({ base, result, traceId, now, replyText: completedReplyText }) : null;
@@ -2222,15 +2243,46 @@ function mapAgentRunEvent(event, mapping = {}) {
return { ...base, type: "diff", eventType: "status", status: "running", label: "agentrun:diff", message: textPayload(payload, "diff") };
}
if (event.type === "error") {
return { ...base, type: "error", eventType: "error", status: "failed", label: `agentrun:error:${String(payload.failureKind ?? "backend")}`, errorCode: payload.failureKind ?? "agentrun_error", message: textPayload(payload, "AgentRun error") };
if (isRetryableProviderInterruptionPayload(payload)) return agentRunProviderRetryEvent({ base, payload, status: "retrying", label: `agentrun:provider-retry:${String(payload.failureKind ?? "provider-stream-disconnected")}` });
return { ...base, type: "error", eventType: "error", status: "failed", label: `agentrun:error:${String(payload.failureKind ?? "backend")}`, errorCode: payload.failureKind ?? "agentrun_error", failureKind: payload.failureKind ?? null, willRetry: payload.willRetry === true ? true : undefined, message: textPayload(payload, "AgentRun error") };
}
if (event.type === "terminal_status") {
const terminalStatus = String(payload.terminalStatus ?? "failed");
return { ...base, type: "result", eventType: "terminal", status: terminalStatus === "cancelled" ? "canceled" : terminalStatus, label: `agentrun:terminal:${terminalStatus}`, errorCode: payload.failureKind ?? null, message: textPayload(payload, `AgentRun terminal status ${terminalStatus}`), terminal: true };
if (isRetryableProviderInterruptionPayload(payload)) return agentRunProviderRetryEvent({ base, payload, status: "retrying", label: `agentrun:provider-retry:${terminalStatus}` });
return { ...base, type: "result", eventType: "terminal", status: terminalStatus === "cancelled" ? "canceled" : terminalStatus, label: `agentrun:terminal:${terminalStatus}`, errorCode: payload.failureKind ?? null, failureKind: payload.failureKind ?? null, willRetry: payload.willRetry === true ? true : undefined, message: textPayload(payload, `AgentRun terminal status ${terminalStatus}`), terminal: true };
}
return { ...base, type: "backend", eventType: "backend", status: "running", label: `agentrun:event:${String(event.type ?? "unknown")}`, message: textPayload(payload, "AgentRun event") };
}
function agentRunProviderRetryEvent({ base = {}, payload = {}, status = "retrying", label = "agentrun:provider-retry" } = {}) {
const failureKind = normalizedFailureKind(payload.failureKind ?? payload.errorCode ?? "provider-stream-disconnected");
return {
...base,
type: "provider_retry",
eventType: "backend",
status,
label,
errorCode: failureKind,
failureKind,
willRetry: payload.willRetry === true,
retryable: true,
transient: true,
terminal: false,
retryOf: payload.retryOf ?? null,
terminalStatus: payload.terminalStatus ?? null,
message: textPayload(payload, "Provider stream disconnected; AgentRun will retry."),
valuesPrinted: false
};
}
function isRetryableProviderInterruptionPayload(payload = {}) {
return normalizedFailureKind(payload?.failureKind ?? payload?.errorCode ?? payload?.code) === "provider-stream-disconnected" && payload?.willRetry === true;
}
function normalizedFailureKind(value) {
return String(value ?? "").trim().toLowerCase().replace(/_/gu, "-") || null;
}
function agentRunTraceEvent(event = {}, backendSource = {}) {
const source = backendSource && typeof backendSource === "object" ? backendSource : { backendProfile: backendSource };
const backendProfile = firstNonEmpty(event.backendProfile, source.backendProfile, source.providerProfile, source.agentRun?.backendProfile);