How OmniRoute Manages Model Lifecycle Policies: A Three-Layer Resilience System
OmniRoute v3.8.51 implements a three-layer resilience system—provider circuit breaker, connection cooldown, and model lockout—to dynamically govern model lifecycle policies from request validation through provider execution.
OmniRoute's model lifecycle policies prevent cascading failures by isolating problems at different granularities. Rather than treating all errors equally, the system distinguishes between provider-wide outages, individual connection issues, and model-specific failures. This architecture ensures that a single bad API key or exhausted quota won't cripple the entire routing pipeline.
Three-Layer Policy Architecture
OmniRoute organizes its lifecycle policies into three distinct layers, each targeting a different scope of failure.
| Layer | Scope | Purpose |
|---|---|---|
| Provider Circuit Breaker | Entire provider (e.g., openai, anthropic) |
Halts traffic to consistently failing upstreams |
| Connection Cooldown | Individual connection/account/key | Temporarily skips bad credentials while preserving other connections |
| Model Lockout | Provider + connection + specific model | Isolates per-model failures like quota limits or missing deployments |
The routing layer in open-sse/services/combo.ts consults all three layers before selecting a target, automatically filtering out any provider, connection, or model that violates current policy state.
Provider Circuit Breaker: System-Wide Protection
The provider circuit breaker stops sending traffic to an entire provider when upstream failures exceed configured thresholds. This protects OmniRoute from wasting resources on endpoints that are fundamentally unreachable.
Implementation Details
The circuit breaker lives in src/shared/utils/circuitBreaker.ts and implements a four-state machine:
CLOSED— Normal operation, requests allowedDEGRADED— Partial failure, limited traffic permittedOPEN— Circuit tripped, all requests blockedHALF_OPEN— Testing recovery with a single probe request
Configuration resides in PROVIDER_PROFILES within open-sse/config/constants.ts. The src/sse/handlers/chatHelpers.ts module applies these profiles during request validation.
// Checking if a provider is allowed to execute
import { getCircuitBreaker } from '@/shared/utils/circuitBreaker';
import { Provider } from '@/shared/constants/providers';
async function canExecute(provider: Provider): Promise<boolean> {
const breaker = getCircuitBreaker(provider);
// State refreshes lazily from timestamps on each read
return breaker.canExecute(); // true if CLOSED/DEGRADED, false if OPEN
}
The breaker uses lazy state evaluation—timestamps like rateLimitedUntil are checked on every canExecute() call rather than relying on background timers. This eliminates coordination overhead while enabling automatic recovery.
Connection Cooldown: Granular Credential Isolation
When a specific API key or account hits rate limits, the connection cooldown layer marks that individual connection unavailable without affecting other connections to the same provider.
Key Implementation Files
src/sse/services/auth.ts::markAccountUnavailable()— triggers cooldown after failure detectionopen-sse/services/accountFallback.ts::checkFallbackError()— validates cooldown status before routingsrc/lib/resilience/settings.ts— stores cooldown durations and retry policies
// Marking an account unavailable after a 429 response
import { markAccountUnavailable } from '@/sse/services/auth';
async function handleRateLimited(err: any, accountId: string) {
if (err.status === 429) {
await markAccountUnavailable(accountId, {
retryAfterMs: err.retryAfter ?? 3000, // 3-second fallback
});
}
}
Cooldown state persists as timestamps, allowing automatic expiration without scheduled jobs. A connection becomes eligible for retry once its retryAfterMs period elapses.
Model Lockout: Fine-Grained Failure Isolation
The most granular layer, model lockout, handles failures specific to individual models within a connection—such as per-model quota exhaustion or deployments that no longer exist.
Lockout Logic
Implemented in open-sse/services/accountFallback.ts, model lockout prevents the routing layer from selecting compromised models while preserving healthy ones on the same connection. The combo routing logic consults this state before finalizing target selection.
// Disabling a specific model after quota exhaustion
import { disableModelLockout } from '@/open-sse/services/accountFallback';
async function handleModelQuota(err: any, modelId: string, connectionId: string) {
if (err.status === 429 && err.model === modelId) {
await disableModelLockout(connectionId, modelId);
}
}
Model lockout records per-model error flags that persist independently of connection cooldown state. This means a 429 on gpt-4-turbo won't disable gpt-4o on the same credential.
Policy Engine Integration
Higher-level services interact with lifecycle policies through src/domain/policyEngine.ts, which abstracts the three layers into a unified query interface. This abstraction allows routing components to check policy compliance without directly managing state machines or timestamp arithmetic.
The policy engine's lazy evaluation design ensures zero background overhead—no timers, no polling loops, no goroutines. State transitions happen inline with request processing, and expiration is computed on demand.
Thresholds and Configuration
Default thresholds live in src/lib/resilience/settings.ts and can be overridden via environment variables:
| Failure Type | Typical Threshold | Action |
|---|---|---|
| Provider HTTP 5xx | 5 errors in 60 seconds | Circuit breaker → OPEN |
| Connection 429 | 3 consecutive rate limits | Cooldown for retryAfter duration |
| Model-specific 429 | 1 error with model attribution | Model lockout for 5 minutes |
These defaults balance rapid failure detection against unnecessary isolation of transient issues.
Code Examples: Complete Policy Flow
// Full request pipeline with policy checks
import { getCircuitBreaker } from '@/shared/utils/circuitBreaker';
import { checkFallbackError } from '@/open-sse/services/accountFallback';
import { selectComboTarget } from '@/open-sse/services/combo';
async function routeRequest(request: ChatRequest) {
// Layer 1: Provider-level check
const providerBreaker = getCircuitBreaker(request.provider);
if (!await providerBreaker.canExecute()) {
throw new ProviderUnavailableError(request.provider);
}
// Layer 2 & 3: Connection and model checks happen inside combo routing
const target = await selectComboTarget(request, {
filterCooldown: true,
filterLockedModels: true,
});
if (!target) {
throw new NoAvailableModelsError();
}
return target;
}
The selectComboTarget function encapsulates the interaction between connection cooldown and model lockout layers, returning only targets that satisfy all active policies.
Summary
OmniRoute's model lifecycle policy system provides:
- Three isolation layers protecting against provider, connection, and model-level failures
- Lazy state evaluation eliminating background timers and enabling automatic recovery
- Configurable thresholds via
src/lib/resilience/settings.tsand environment variables - Unified policy engine abstraction in
src/domain/policyEngine.tsfor clean integration
These mechanisms ensure that upstream outages, rate limits, and model-specific issues are contained without manual intervention or global service degradation.
Frequently Asked Questions
What triggers a provider circuit breaker to open?
The provider circuit breaker transitions to OPEN when HTTP 5xx errors exceed the threshold defined in PROVIDER_PROFILES—typically 5 errors within 60 seconds. Once open, the breaker blocks all traffic to that provider until the HALF_OPEN probe succeeds or the timeout expires.
How does OmniRoute recover from a connection cooldown automatically?
Connection cooldowns store rateLimitedUntil timestamps that are checked lazily on each routing decision. No background process monitors expiration; instead, checkFallbackError() in open-sse/services/accountFallback.ts validates whether the cooldown period has elapsed before filtering out a connection.
Can model lockout affect other connections using the same provider?
No. Model lockout is scoped to a specific connectionId + modelId pair as implemented in open-sse/services/accountFallback.ts. A gpt-4 lockout on one API key does not impact gpt-4 availability on other credentials or providers.
Where are default resilience thresholds configured?
Default thresholds for all three policy layers reside in src/lib/resilience/settings.ts, with provider-specific profiles in open-sse/config/constants.ts. These values can be overridden through environment variables without code changes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →