Repository navigation
fix(auth): model-scoped lockout on passthrough provider credit exhaustion - #13548
diegosouzapw merged 11 commits into
Conversation
…tion When a provider with passthrough models (e.g. b.ai, third-party aggregators) returns HTTP 400 with 'credit insufficient balance' or quota exhaustion on a specific paid model, markAccountUnavailable previously treated it as a connection-wide terminal credits_exhausted error because status was 400 (not 404/429/500) and checkFallbackError was only exempting compatible providers instead of all per-model-quota providers. This commit: 1. Replaces !isCompatibleProvider(provider) with !hasPerModelQuota(provider, _model) in checkFallbackError so passthrough providers do not emit connection-wide creditsExhausted: true. 2. In markAccountUnavailable, checks isModelLevelQuotaOrRateLimit on per-model-quota providers so non-terminal quota/credits exhaustion is handled via model-only lockout instead of poisoning the entire connection. 3. Adds unit tests validating b.ai credit insufficient balance keeps the connection active while locking out only the requested model.
|
Thanks for tracking down the passthrough-vs-terminal scoping bug — the reproduction and |
…ion without connection override Add a changelog fragment for the passthrough credit-lockout fix and a regression test proving checkFallbackError() applies the fixed COOLDOWN_MS.paymentRequired cooldown (not the previous escalating backoff) to *-compatible-* provider nicknames even when the connection has no explicit providerSpecificData.passthroughModels override, since isCompatibleProvider() already matches on the nickname prefix alone. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
…through lockout Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
… (codex/gemini/github) Only passthrough/aggregator and *-compatible-* providers get the model-scoped credit lockout; providers with a per-model lock only keep the account-level credits_exhausted semantics. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Keeps the 'credits' lockout reason for per-model 402s; the widened model-scoped gate only applies to other quota/credit classifications. Ceiling for auth.ts rebaselined to the measured post-commit value. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
# Conflicts: # config/quality/file-size-baseline.json
Keeps src/sse/services/auth.ts within its frozen file-size ceiling after merging the tip; the PR's rebaseline entry is dropped. No behavior change.
e9e4815
into
cheaper-inference:release/v3.8.51
…ge wave (#15052) Base-red drain after the 2026-09-28/29 release-drain wave: one production defect (compression preview fuzzy toggle was a no-op since #14983), three intentional contract changes propagated (#14848, #13548, #13487 inventory site), two cross-test state leaks fixed with a refusal-memory reset. Each red reproduced on the tip and green with the fix.
… 4) (#15109) Release-captain base-red fix (v3.8.51 release PR #11442, unit shards 3-4): one production defect (proxyLogger pulled into every settings→proxies import and queried the DB at import time; helper extracted to src/lib/proxyLogHost.ts) and seven contract propagations from #14732, #15044, #15067, #13548, #14117×#14844, #12810. 74/74 across the seven files, 176/176 proxy-log neighbours, typecheck clean.
Summary
When a provider with passthrough models (e.g.,
bai/thebai, third-party aggregators, or multi-model nodes) returns HTTP 400 withcredit insufficient balanceor quota exhaustion on a specific paid model, OmniRoute previously marked the entire connection as terminalcredits_exhaustedwithrateLimitedUntil = null.This disabled all free or available models on the same connection (e.g.,
bai/qwen3.8-flash) until a manual "Retest" was triggered or the 30-minute background reprobe timer elapsed.This PR ensures credit exhaustion on per-model-quota providers is properly scoped as a model-level lockout (
reason: "quota_exhausted") rather than a whole-connection terminal failure.Root Cause
checkFallbackError()inopen-sse/services/accountFallback.ts:PR fix(providers): cegp/gpt-5.6-luna dead — 403 insufficient_quota reset never fires, should mark model unhealthy #8247 added
!isCompatibleProvider(provider)to prevent compatible providers from emittingcreditsExhausted: true. However,isCompatibleProvideronly matchesopenai-compatible-/anthropic-compatible-prefixes, omitting built-in passthrough providers likebai.markAccountUnavailable()insrc/sse/services/auth.ts:The model-lockout conditional check was restricted to
(status === 404 || isNvidiaModelGone || status === 429 || status >= 500). When an upstream passthrough provider returns HTTP 400 withcredit insufficient balance, status 400 did not match this condition, falling through toresolveTerminalConnectionStatus()and terminating the entire account connection.Key Changes
open-sse/services/accountFallback.ts):Replaced
!isCompatibleProvider(provider)with!hasPerModelQuota(provider, _model)when returningcreditsExhausted: true. Built-in passthrough providers, compatible providers, and multi-model configurations now avoid emitting connection-widecreditsExhausted. Account-level balance exhaustion (e.g. Moonshot empty wallet) continues to terminate the whole connection as expected.markAccountUnavailable(src/sse/services/auth.ts):Added
isModelLevelQuotaOrRateLimitcheck so that non-permanent quota/credit exhaustion (RateLimitReason.QUOTA_EXHAUSTEDorcreditsExhausted) triggers model-only lockout withreason: "quota_exhausted". Sibling models on the same connection remain active and eligible.tests/unit/auth-passthrough-credit-lockout.test.ts):b.aiHTTP 400 credit insufficient balance locks only the requested model (glm-5.3-flash), connection remainsactive, and free model (qwen3.8-flash) remains unlocked.openai) still terminate the connection withcredits_exhausted.Testing