Files
OmniRoute/open-sse/services/accountFallback/perModelFailureScope.ts
Kizuno18 6d0dc5a50c fix(combo): scope Claude model failures to the model, not the account (#12340)
* fix(combo): scope Claude model failures to the model, not the account

A priority combo whose steps are five models on one Claude OAuth connection
stops at step 1. hasPerModelQuota() returns false for the claude provider, so
markAccountUnavailable() records a model-specific 404 or 5xx against the
connection row, and getPersistedConnectionCooldownSkipReason() then skips every
sibling step before dispatch. Verified with a combo whose first step names a
model that cannot exist: the 404 lands and step 2 is never tried, while the
same combo works as soon as step 2 points at a different account.

A Claude OAuth connection multiplexes Fable 5, Opus 5/4.8/4.7/4.6, Sonnet and
Haiku behind one credential, which is the multiplexing hasPerModelQuota already
describes. Its quota is a separate question: a 429 on a Max subscription is
account-wide, and shouldMarkAccountExhaustedFrom429 pins that. So this adds
hasPerModelFailureScope() for the non-quota statuses and leaves 429 alone.

Combo exhaustion gets the same treatment for 404, which names one model the
account cannot serve rather than a bad connection. The empty-content 502 was
already exempt through isEmptyContentFailure.

Closes #12334

* fix(combo): extract Claude per-model failure scope to keep file-size caps

Move hasPerModelFailureScope into a leaf so frozen accountFallback.ts and
auth.ts stay at their file-size caps after #12334. Register
combo-claude-per-model-scope.test.ts in Stryker tap.testFiles so the
mutation-test-coverage gate sees the covering unit test.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-09-18 11:28:20 -03:00

29 lines
1.3 KiB
TypeScript

/**
* accountFallback/perModelFailureScope.ts — whether a NON-QUOTA failure should lock
* one model instead of cooling the whole connection.
*
* Extracted from services/accountFallback.ts (file-size gate, #12334). A Claude
* OAuth connection multiplexes Fable 5, Opus, Sonnet and Haiku behind one
* credential, so a 404/5xx names one model. Quota is a separate question:
* hasPerModelQuota("claude") is false and a 429 stays account-wide.
*/
import { resolveProviderId } from "../../../src/shared/constants/providers";
import { hasPerModelQuota } from "../accountFallback.ts";
/**
* @param status - When 429, only per-model *quota* providers qualify. Omit for
* the non-quota question (404/5xx), which also includes Claude.
*/
export function hasPerModelFailureScope(
provider: string | null | undefined,
model: string | null | undefined = null,
connectionPassthroughModels?: boolean,
status?: number
): boolean {
if (status === 429) return hasPerModelQuota(provider, model, connectionPassthroughModels);
if (hasPerModelQuota(provider, model, connectionPassthroughModels)) return true;
if (typeof connectionPassthroughModels === "boolean") return connectionPassthroughModels;
if (!provider) return false;
return resolveProviderId(provider) === "claude";
}