/** * Reading a router's own account of what it tried. * * A gateway fronting many models reports failure as one message listing every upstream it * attempted, which arrives looking like a single opaque error: * * Stream ended before producing a non-ping SSE event * [oc/deepseek-v4-flash-free (400), oc/nemotron-3-ultra-free (502)] (bad_gateway) * * Treated as one failure that is worth retrying, which is what it looks like, the run retries * against the same exhausted pool and then gives up — reported three times in a row by the same * person, on the same alias, with the same two models. * * The list is the useful part, and the status beside each name says something different about each: * * - 5xx is the upstream being unwell. Retrying can reach a healthy one. * - 429 is a quota. Retrying can work once it resets, which is usually seconds. * - 4xx other than 429 is the REQUEST being refused, and it will be refused identically every * time. On a free model it is almost always the prompt exceeding a context window far smaller * than the alias implies. Retrying that is time spent to arrive at the same answer. * * So a pool where every attempt was refused is not transient, however transient the wrapper says * it was, and saying which models failed and how turns "it broke again" into a decision the person * can actually make. */ export interface RouterAttempt { model: string; status: number; } export interface RouterFailure { attempts: RouterAttempt[]; /** True when at least one upstream failure could plausibly succeed on another try. */ worthRetrying: boolean; /** What to tell the person, when there is something worth telling them. */ advice: string; } /** * The per-model attempts a router listed, if it listed any. * * Deliberately narrow: it matches a bracketed list of `name (status)` pairs and nothing else, * because guessing at the shape of a message from an unknown gateway is how a parser starts * inventing failures that never happened. */ export declare function parseRouterAttempts(message: string): RouterAttempt[]; /** * Whether one upstream could plausibly go differently next time. * * The reason matters as much as the number. A 400 saying the prompt was too long will say it again * to the identical request, so retrying is time spent to reach the same answer. A 400 saying the * model was unavailable is a different claim entirely — the router may pick a different upstream, or * the same one may come back — and treating those two as one thing meant a recoverable failure was * reported as final. */ export declare function statusWorthRetrying(status: number, reason?: string | null): boolean; /** * The reason the gateway actually gave, if it gave one. * * Everything before the bracketed list of attempts, with the wrapper noise stripped. Worth having * because a status alone is nearly always ambiguous and the sentence beside it usually is not. */ export declare function statedReason(message: string): string | null; /** * What a status means, in words a person can act on. * * The reason the gateway gave outranks anything inferred from the number, and getting this wrong * cost somebody real time: every 400 was described as "usually the prompt exceeding its context * window", so a gateway plainly saying "Model is unavailable" was reported as a context problem and * sent them looking at the length of their conversation. A status is a category; the sentence beside * it is the fact. */ export declare function describeStatus(status: number, reason?: string | null): string; /** * Reads a router failure, if that is what this is. * * Returns null for anything that does not carry a per-model list, so ordinary errors keep their * ordinary handling rather than being re-interpreted by a parser that wanted to find something. */ export declare function readRouterFailure(message: string): RouterFailure | null; /** * A router that re-routed to somewhere you have no key for. * * Reported verbatim: * * omni does not recognise model "auto/best-coding". Use /models to pick one it offers. * No active credentials for provider: antigravity (model_not_found) * * Both halves are misleading on their own. The gateway does recognise the alias — it was routing * fine a moment earlier — and the provider named is not one anybody chose. * * What this said next was invented. From one regex match it asserted that the model asked for was * an alias, that the request carried an image, and that removing the attachment would fix it — * none of which it had looked at. Someone who had picked a specific free model, with no image * anywhere near the request, was told their alias had resolved to a vision-capable upstream and * advised to send the message without the attachment. Three claims, all confident, all wrong, and * the one true sentence — that the gateway routed somewhere with no credentials — was buried among * them. The docstring even promised this was reported "only when an image was attached", a guard * the code never had. * * So the advice is now assembled from what is known. The alias sentence needs a name that looks * like an alias; the image paragraph needs an image; and when a specific model was asked for, the * honest reading is the opposite of the old one — the routing decision was the gateway's, not the * name's, and the fix is on the gateway side. */ export interface ReroutedFailure { /** The upstream the router picked, which the user has no credential for. */ provider: string; /** The alias that was asked for, when the message names one. */ alias?: string; advice: string; } /** What the caller knows about the request that failed. Absent facts are not guessed. */ export interface ReroutedContext { /** The model actually asked for, when the caller knows it. */ model?: string; /** Whether this request carried an image. Undefined means nobody looked. */ hadImage?: boolean; } export declare function readReroutedFailure(message: string, context?: ReroutedContext): ReroutedFailure | null; //# sourceMappingURL=router-failure.d.ts.map