mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-20 14:12:59 +03:00
fix(kiro): avoid treating high-traffic 429s as quota exhaustion (#2153)
Integrated into release/v3.8.0 — fixes transient Kiro 429s being incorrectly classified as quota exhaustion
This commit is contained in:
@@ -474,8 +474,14 @@ export function lockModelIfPerModelQuota(
|
||||
export function shouldMarkAccountExhaustedFrom429(
|
||||
provider: string | null | undefined,
|
||||
model: string | null | undefined = null,
|
||||
connectionPassthroughModels?: boolean
|
||||
connectionPassthroughModels?: boolean,
|
||||
failureKind?: FailureKind
|
||||
): boolean {
|
||||
// A plain 429 means transient rate limiting / high traffic for many OAuth providers.
|
||||
// Only connection-poison the quota cache when the upstream body explicitly says
|
||||
// the long-window quota is exhausted; otherwise fallback should try another account
|
||||
// without making this one look quota-depleted for 5 minutes.
|
||||
if (failureKind === "rate_limit" || failureKind === "transient") return false;
|
||||
return (
|
||||
shouldPreserveQuotaSignalsFor429(provider) &&
|
||||
!hasPerModelQuota(provider, model, connectionPassthroughModels)
|
||||
|
||||
@@ -1064,15 +1064,18 @@ async function handleSingleModelChat(
|
||||
dailyQuotaExhausted = true;
|
||||
}
|
||||
|
||||
// 7. Mark account as quota-exhausted on 429 response (non-daily-quota errors)
|
||||
// For providers that route quota/cooldown at model scope, a 429 on one model
|
||||
// does not mean the whole connection is exhausted.
|
||||
// Daily quota errors are handled above; only process regular rate_limit here
|
||||
// 7. Mark account as quota-exhausted only for explicit long-window quota signals.
|
||||
// A plain 429/high-traffic response should trigger fallback/cooldown, not poison
|
||||
// quotaCache as exhausted for 5 minutes while usage quota may still be available.
|
||||
if (!dailyQuotaExhausted) {
|
||||
const passthroughModels = credentials.providerSpecificData?.passthroughModels;
|
||||
const failureKind =
|
||||
result.status === 429
|
||||
? classify429FromError({ status: result.status, message: errorStr })
|
||||
: undefined;
|
||||
if (
|
||||
result.status === 429 &&
|
||||
shouldMarkAccountExhaustedFrom429(provider, model, passthroughModels)
|
||||
shouldMarkAccountExhaustedFrom429(provider, model, passthroughModels, failureKind)
|
||||
) {
|
||||
markAccountExhaustedFrom429(credentials.connectionId, provider);
|
||||
}
|
||||
|
||||
@@ -295,6 +295,21 @@ test("shouldMarkAccountExhaustedFrom429 skips connection poisoning for compatibl
|
||||
assert.equal(shouldMarkAccountExhaustedFrom429("claude", "claude-sonnet-4-6"), true);
|
||||
});
|
||||
|
||||
test("shouldMarkAccountExhaustedFrom429 does not poison quota cache for transient 429s", () => {
|
||||
assert.equal(
|
||||
shouldMarkAccountExhaustedFrom429("kiro", "claude-opus-4.7", undefined, "rate_limit"),
|
||||
false
|
||||
);
|
||||
assert.equal(
|
||||
shouldMarkAccountExhaustedFrom429("kiro", "claude-opus-4.7", undefined, "transient"),
|
||||
false
|
||||
);
|
||||
assert.equal(
|
||||
shouldMarkAccountExhaustedFrom429("kiro", "claude-opus-4.7", undefined, "quota_exhausted"),
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
test("hasPerModelQuota returns true for GitHub Copilot provider (#1624)", () => {
|
||||
assert.equal(hasPerModelQuota("github"), true);
|
||||
assert.equal(hasPerModelQuota("github", "gpt-5.1-codex-max"), true);
|
||||
|
||||
@@ -66,6 +66,13 @@ test("classify429: 429 without quota keyword returns 'rate_limit'", () => {
|
||||
}),
|
||||
"rate_limit"
|
||||
);
|
||||
assert.equal(
|
||||
classify429({
|
||||
status: 429,
|
||||
body: "I am experiencing high traffic, please try again shortly.",
|
||||
}),
|
||||
"rate_limit"
|
||||
);
|
||||
});
|
||||
|
||||
test("looksLikeQuotaExhausted: detects all known keyword variants", () => {
|
||||
|
||||
Reference in New Issue
Block a user