Compare commits

...

2 Commits

Author SHA1 Message Date
Probe Test
b18fa27195 test(nvidia): read PROVIDER_BREAKER_FAILURE_STATUSES from chatPredicates.ts
Same root cause as the chat.ts import fix in this PR: the const was extracted out
of chat.ts into chatPredicates.ts, so the nvidia-quota Phase-1 guard (which greps
the source for the `= new Set([...])` declaration to prove 429 was not added to the
breaker classification) must read chatPredicates.ts, not chat.ts. Now 13/0.
2026-07-23 03:58:39 -03:00
Probe Test
ba247bedd5 fix(sse): export PROVIDER_BREAKER_FAILURE_STATUSES so chat.ts stops throwing ReferenceError
Base-red slice 4 (single root cause across the whole handleChat cluster).

src/sse/handlers/chat.ts:1273 references PROVIDER_BREAKER_FAILURE_STATUSES to decide
whether an all-rate-limited provider result should trip the provider breaker, but the
constant was only a FILE-LOCAL const in chatPredicates.ts (a refactor extracted it out
of chat.ts and never re-exported it). Every request that reached that branch threw
`ReferenceError: PROVIDER_BREAKER_FAILURE_STATUSES is not defined`, so the global
fallback and breaker-gate paths blew up — surfacing as "All models failed |
PROVIDER_BREAKER_FAILURE_STATUSES is not defined" and breaking the handleChat coverage
tests (combo-error passthrough, 503 for cooled-down/open-breaker, budget-error, model
cooldown, body-derived retry-after, non-JSON rate-limit bodies).

Fix: export the const from chatPredicates.ts and import it in chat.ts (one canonical
definition, restoring the pre-refactor behavior).

Validated: chat-route-coverage 15/0 (was 12/3), chat-cooldown-aware-retry 6/0,
chat-rate-limit-body-lock 2/0; breaker guards (7907, combo-breaker-429, openrouter-6842)
unchanged; typecheck:core clean.
2026-07-23 03:51:29 -03:00
3 changed files with 10 additions and 9 deletions

View File

@@ -75,6 +75,7 @@ import {
import {
isAntigravityMissingProjectError,
shouldTripProviderBreakerForResult,
PROVIDER_BREAKER_FAILURE_STATUSES,
} from "./chatPredicates";
import { connectionHasExtraKeys } from "@omniroute/open-sse/services/apiKeyRotator.ts";
import {

View File

@@ -1,7 +1,7 @@
import { isLocalStreamLifecycleError } from "../../shared/utils/circuitBreaker";
import { isRequestScopedUpstreamFailure } from "./comboFailureLogging";
const PROVIDER_BREAKER_FAILURE_STATUSES = new Set([408, 500, 502, 503, 504]);
export const PROVIDER_BREAKER_FAILURE_STATUSES = new Set([408, 500, 502, 503, 504]);
// #7907/#7908: single-model breaker trip bypasses the `isFailure` option (only applies
// inside `breaker.execute()`), so it needs its own `isLocalStreamLifecycleError` guard —

View File

@@ -189,7 +189,11 @@ test("getProviderConcurrencyCap resolves override -> static default -> fallback"
);
setProviderQuotaOverrides({ nvidia: { concurrency: 3 } });
try {
assert.equal(getProviderConcurrencyCap("nvidia", 99), 3, "override wins over the static default");
assert.equal(
getProviderConcurrencyCap("nvidia", 99),
3,
"override wins over the static default"
);
} finally {
setProviderQuotaOverrides(null);
}
@@ -218,13 +222,9 @@ test("nvidia 429 does not trip the provider circuit breaker (unchanged 408/500/5
// is a documentation-alignment guard proving Phase 1 didn't accidentally widen
// PROVIDER_BREAKER_FAILURE_STATUSES to include 429 (which would collapse the
// per-model lockout this PR adds into a whole-connection/provider outage).
const chatHandlerPath = path.join(
process.cwd(),
"src",
"sse",
"handlers",
"chat.ts"
);
// The PROVIDER_BREAKER_FAILURE_STATUSES declaration was extracted from chat.ts into
// the sibling chatPredicates.ts (chat.ts now imports it); read it from its home.
const chatHandlerPath = path.join(process.cwd(), "src", "sse", "handlers", "chatPredicates.ts");
const source = fs.readFileSync(chatHandlerPath, "utf8");
const match = source.match(/PROVIDER_BREAKER_FAILURE_STATUSES\s*=\s*new Set\(\[([^\]]+)\]\)/);
assert.ok(match, "PROVIDER_BREAKER_FAILURE_STATUSES declaration found");