mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 02:42:24 +03:00
Validado em lote numa worktree combinada com os 14 PRs desta campanha de error-boundary sobre o tip de `release/v3.8.51`: `typecheck:core` limpo e **120/120** nos 23 arquivos de teste que os PRs trazem. Um ponto que só apareceu no tree combinado: **#12465 e #12466 criam o mesmo arquivo novo** `open-sse/utils/streamReadiness.ts` (que não existe no tip) com desenhos divergentes de cancelamento — `cancelled` + `releaseLock` imediato num, `readInFlight`/`cancelRequested` com `cancelReader` fire-and-forget no outro. Adotei a versão do #12466, que difere e defere o release do lock para quando a leitura em voo termina, e validei a escolha rodando as suítes dos **dois** PRs contra ela: 21/21 no readiness compartilhado e 22/22 incluindo o boundary do Perplexity.
111 lines
5.1 KiB
TypeScript
111 lines
5.1 KiB
TypeScript
import { sanitizeErrorMessage } from "./error.ts";
|
|
|
|
export const CODEX_PUBLIC_ERROR_MESSAGE = sanitizeErrorMessage("Codex provider request failed");
|
|
|
|
export interface CodexPublicError {
|
|
message: string;
|
|
type: string;
|
|
code: string;
|
|
}
|
|
|
|
interface CodexPublicErrorInput {
|
|
status?: number | null;
|
|
type?: unknown;
|
|
code?: unknown;
|
|
}
|
|
|
|
interface CodexPublicErrorRule {
|
|
type: string;
|
|
allowsStatus: (status: number) => boolean;
|
|
}
|
|
|
|
const exactStatuses =
|
|
(...statuses: number[]) =>
|
|
(status: number): boolean =>
|
|
statuses.includes(status);
|
|
|
|
const CODEX_PUBLIC_ERROR_RULES = new Map<string, CodexPublicErrorRule>([
|
|
["browser_stream_inconsistent", { type: "server_error", allowsStatus: exactStatuses(502) }],
|
|
["chatgpt_session_expired", { type: "authentication_error", allowsStatus: exactStatuses(401) }],
|
|
["chatgpt_submission_ambiguous", { type: "server_error", allowsStatus: exactStatuses(502) }],
|
|
["chatgpt_submitted_turn_failed", { type: "server_error", allowsStatus: exactStatuses(502) }],
|
|
["chatgpt_subscription_unavailable", { type: "server_error", allowsStatus: exactStatuses(503) }],
|
|
["client_cancelled", { type: "invalid_request_error", allowsStatus: exactStatuses(499) }],
|
|
["client_closed_request", { type: "invalid_request_error", allowsStatus: exactStatuses(499) }],
|
|
["codex_app_server_turn_failed", { type: "provider_error", allowsStatus: exactStatuses(502) }],
|
|
[
|
|
"compaction_control_unavailable",
|
|
{ type: "invalid_request_error", allowsStatus: exactStatuses(409) },
|
|
],
|
|
[
|
|
"compaction_handoff_failed",
|
|
{ type: "invalid_request_error", allowsStatus: exactStatuses(409) },
|
|
],
|
|
[
|
|
"compaction_source_unavailable",
|
|
{ type: "invalid_request_error", allowsStatus: exactStatuses(409) },
|
|
],
|
|
["connector_not_found", { type: "connector_error", allowsStatus: exactStatuses(424) }],
|
|
[
|
|
"context_length_exceeded",
|
|
{ type: "invalid_request_error", allowsStatus: exactStatuses(400, 413) },
|
|
],
|
|
["insufficient_quota", { type: "insufficient_quota", allowsStatus: exactStatuses(429) }],
|
|
["invalid_api_key", { type: "authentication_error", allowsStatus: exactStatuses(401) }],
|
|
["invalid_output_schema", { type: "invalid_request_error", allowsStatus: exactStatuses(400) }],
|
|
["invalid_request_error", { type: "invalid_request_error", allowsStatus: exactStatuses(400) }],
|
|
["multipart_protocol_violation", { type: "server_error", allowsStatus: exactStatuses(502) }],
|
|
["origin_rejected", { type: "invalid_request_error", allowsStatus: exactStatuses(403) }],
|
|
["permission_denied", { type: "permission_error", allowsStatus: exactStatuses(403) }],
|
|
["prompt_attachment_integrity", { type: "server_error", allowsStatus: exactStatuses(502) }],
|
|
["rate_limit_exceeded", { type: "rate_limit_error", allowsStatus: exactStatuses(429) }],
|
|
["server_is_overloaded", { type: "server_error", allowsStatus: exactStatuses(503) }],
|
|
[
|
|
"structured_output_validation_failed",
|
|
{ type: "server_error", allowsStatus: exactStatuses(502) },
|
|
],
|
|
["subscription_required", { type: "permission_error", allowsStatus: exactStatuses(403) }],
|
|
[
|
|
"upstream_server_error",
|
|
{
|
|
type: "server_error",
|
|
allowsStatus: (status) => status >= 500 && status <= 599 && status !== 503,
|
|
},
|
|
],
|
|
[
|
|
"upstream_websocket_connect_failed",
|
|
{ type: "provider_error", allowsStatus: exactStatuses(502) },
|
|
],
|
|
["upstream_websocket_error", { type: "provider_error", allowsStatus: exactStatuses(502) }],
|
|
["usage_limit_reached", { type: "rate_limit_error", allowsStatus: exactStatuses(429) }],
|
|
]);
|
|
|
|
function defaultPublicClassification(status: number): Pick<CodexPublicError, "type" | "code"> {
|
|
if (status === 429) return { type: "rate_limit_error", code: "rate_limit_exceeded" };
|
|
if (status === 401) return { type: "authentication_error", code: "invalid_api_key" };
|
|
if (status === 403) return { type: "permission_error", code: "permission_denied" };
|
|
if (status === 499) return { type: "invalid_request_error", code: "client_closed_request" };
|
|
if (status === 503) return { type: "server_error", code: "server_is_overloaded" };
|
|
if (status >= 500) return { type: "server_error", code: "upstream_server_error" };
|
|
return { type: "invalid_request_error", code: "invalid_request_error" };
|
|
}
|
|
|
|
/**
|
|
* Project an internally classified Codex failure onto its public Responses contract.
|
|
*
|
|
* Upstream message, code, and type fields are untrusted. The public message is fixed,
|
|
* while code/type retain only closed, protocol-level identifiers already produced by
|
|
* OmniRoute. Everything else falls back to the HTTP status classification.
|
|
*/
|
|
export function projectCodexPublicError(input: CodexPublicErrorInput): CodexPublicError {
|
|
const status =
|
|
typeof input.status === "number" && Number.isInteger(input.status) ? input.status : 502;
|
|
const fallback = defaultPublicClassification(status);
|
|
const rule =
|
|
typeof input.code === "string" ? CODEX_PUBLIC_ERROR_RULES.get(input.code) : undefined;
|
|
if (!rule || !rule.allowsStatus(status)) {
|
|
return { message: CODEX_PUBLIC_ERROR_MESSAGE, ...fallback };
|
|
}
|
|
return { message: CODEX_PUBLIC_ERROR_MESSAGE, type: rule.type, code: input.code as string };
|
|
}
|