mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-02 21:32:10 +03:00
#7268: classifyProviderError() only inspected the response body for model-unavailable wording on 400/403/404, so a 401 body like "Model X is not supported" (free-tier/aggregator providers) fell through to a generic UNAUTHORIZED classification. Because chatCore.ts only calls lockModel(..., "model_not_found", ...) on the MODEL_NOT_FOUND branch, the broken model was never locked out and auto-combo kept re-selecting it every request. Added a shared containsModelUnavailableMessage() regex (bounded, ReDoS-safe) in errorClassifier.ts, consulted by the 401 branch before falling back to ACCOUNT_DEACTIVATED/UNAUTHORIZED, and reused by modelFamilyFallback.ts's isModelUnavailableError() for the literal "<model> is not supported" phrasing. #7387: applySessionStickiness() (combo-level session stickiness) only gated a sticky pin's release on testStatus (credits_exhausted/banned/expired) and rateLimitedUntil (#6692's fix). It never consulted isAccountQuotaExhausted() (src/domain/quotaCache.ts) — the authoritative per-window (5h/weekly) quota signal that src/sse/services/auth.ts and sessionAffinityPin.ts (the provider-level pin) already gate on. A connection whose quota window was depleted, but that hadn't yet received a hard failure severe enough to flip testStatus/rateLimitedUntil, was re-promoted to position 0 on every request regardless of routing strategy. Added isStickyConnectionQuotaExhausted(), a dynamic-import seam (mirroring resolveConnectionHealth/resolveSaturation, no new static edge from open-sse/ into src/domain/) with an injectable checker for tests, gating the release condition alongside the existing checks. Regression tests: tests/unit/repro-7268-401-model-not-supported-lockout.test.ts, tests/unit/repro-7387-sticky-quota-exhausted.test.ts (both RED before, GREEN after). Existing sticky/error-classifier suites re-run and stay green. Closes #7268 Closes #7387