fix: 归类 v0.1 provider 可用性失败
This commit is contained in:
@@ -435,7 +435,10 @@ function normalizeCodexNotification(message: JsonRecord): { events: BackendEvent
|
||||
const status = terminalStatusFromValue(turn.status);
|
||||
const error = asRecordAt(turn, "error");
|
||||
const messageText = typeof error.message === "string" ? redactText(error.message) : null;
|
||||
return { events: [{ type: "backend_status", payload: { phase: method, terminalStatus: status } }], terminal: { status, failureKind: status === "completed" ? null : classifyMessageFailureKind(messageText ?? turn.status, "backend-failed"), message: messageText } };
|
||||
const failureKind = status === "completed" ? null : classifyMessageFailureKind(messageText ?? turn.status, "backend-failed");
|
||||
const events: BackendEvent[] = [{ type: "backend_status", payload: { phase: method, terminalStatus: status } }];
|
||||
if (failureKind) events.push({ type: "error", payload: { failureKind, error: redactJson(error), phase: method } });
|
||||
return { events, terminal: { status, failureKind, message: messageText } };
|
||||
}
|
||||
return { events: [{ type: "backend_status", payload: { phase: method } }] };
|
||||
}
|
||||
@@ -570,11 +573,19 @@ function classifyMessageFailureKind(message: string, fallback: FailureKind): Fai
|
||||
const text = String(message || "").toLowerCase();
|
||||
if (/rate.?limit|too many requests|\b429\b/u.test(text)) return "provider-rate-limited";
|
||||
if (/\b401\b|\b403\b|unauthori[sz]ed|forbidden|invalid api key|authentication|auth failed|oauth|access token/u.test(text)) return "provider-auth-failed";
|
||||
if (isProviderUnavailableMessage(text)) return "provider-unavailable";
|
||||
if (/timed out|timeout|idle timeout/u.test(text)) return "backend-timeout";
|
||||
if (/invalid json|json parse/u.test(text)) return "backend-json-parse-error";
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function isProviderUnavailableMessage(text: string): boolean {
|
||||
if (/\b(?:http(?:\s+status)?|status(?:\s+code)?|code)\s*[:=]?\s*5\d\d\b/u.test(text)) return true;
|
||||
if (/\b5\d\d\b/u.test(text) && /service unavailable|bad gateway|gateway timeout|internal server error|provider|upstream|response\s*stream\s*disconnected|responsestreamdisconnected/u.test(text)) return true;
|
||||
if (/service unavailable|temporar(?:y|ily) unavailable|provider (?:is )?unavailable|provider availability|upstream (?:is )?unavailable/u.test(text)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function positiveTimeout(value: number): number {
|
||||
return Number.isFinite(value) && value > 0 ? Math.max(1, Math.floor(value)) : requestTimeoutCapMs;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ export type FailureKind =
|
||||
| "backend-timeout"
|
||||
| "provider-auth-failed"
|
||||
| "provider-rate-limited"
|
||||
| "provider-unavailable"
|
||||
| "infra-failed"
|
||||
| "cancelled";
|
||||
|
||||
|
||||
@@ -34,11 +34,13 @@ const selfTest: SelfTestCase = async (context) => {
|
||||
await access(path.join(projectedHome, "config.toml"));
|
||||
|
||||
await runFailureCase({ client, managerUrl: server.baseUrl, context, mode: "missing-turn-result", expectedStatus: "failed", expectedFailureKind: "backend-response-invalid" });
|
||||
await runFailureCase({ client, managerUrl: server.baseUrl, context, mode: "provider-503-rpc-error", expectedStatus: "failed", expectedFailureKind: "provider-unavailable" });
|
||||
await runFailureCase({ client, managerUrl: server.baseUrl, context, mode: "provider-503-terminal", expectedStatus: "failed", expectedFailureKind: "provider-unavailable" });
|
||||
await runFailureCase({ client, managerUrl: server.baseUrl, context, mode: "invalid-json", expectedStatus: "failed", expectedFailureKind: "backend-json-parse-error" });
|
||||
await runFailureCase({ client, managerUrl: server.baseUrl, context, mode: "missing-terminal", expectedStatus: "failed", expectedFailureKind: "backend-timeout", timeoutMs: 500 });
|
||||
await runSpawnFailureCase({ client, managerUrl: server.baseUrl, context });
|
||||
|
||||
return { name: "codex-stdio", tests: ["runner-lease-heartbeat", "codex-stdio-fake-turn", "codex-stdio-projected-writable-home", "codex-stdio-missing-turn-result", "codex-stdio-invalid-json", "codex-stdio-timeout", "codex-stdio-spawn-failure"] };
|
||||
return { name: "codex-stdio", tests: ["runner-lease-heartbeat", "codex-stdio-fake-turn", "codex-stdio-projected-writable-home", "codex-stdio-missing-turn-result", "codex-stdio-provider-503-rpc-error", "codex-stdio-provider-503-terminal", "codex-stdio-invalid-json", "codex-stdio-timeout", "codex-stdio-spawn-failure"] };
|
||||
} finally {
|
||||
await new Promise<void>((resolve) => server.server.close(() => resolve()));
|
||||
}
|
||||
|
||||
@@ -35,6 +35,10 @@ for await (const line of rl) {
|
||||
respond(message.id, {});
|
||||
continue;
|
||||
}
|
||||
if (mode === "provider-503-rpc-error") {
|
||||
respond(message.id, null, { code: -32000, message: "responseStreamDisconnected: HTTP 503 Service Unavailable from provider" });
|
||||
continue;
|
||||
}
|
||||
if (mode === "missing-terminal") {
|
||||
turnCounter += 1;
|
||||
const turn = { id: `turn_selftest_${turnCounter}`, status: "running" };
|
||||
@@ -42,6 +46,14 @@ for await (const line of rl) {
|
||||
respond(message.id, { turn });
|
||||
continue;
|
||||
}
|
||||
if (mode === "provider-503-terminal") {
|
||||
turnCounter += 1;
|
||||
const turn = { id: `turn_selftest_${turnCounter}`, status: "failed", error: { message: "HTTP 503 Service Unavailable" } };
|
||||
notify("turn/started", { turn: { id: turn.id, status: "running" } });
|
||||
notify("turn/completed", { turn });
|
||||
respond(message.id, { turn });
|
||||
continue;
|
||||
}
|
||||
turnCounter += 1;
|
||||
const turn = { id: `turn_selftest_${turnCounter}`, status: "completed" };
|
||||
notify("turn/started", { turn });
|
||||
|
||||
Reference in New Issue
Block a user