ADRs
ADR-0077: Provider Fallback Routing
Error-driven opt-in model fallback on circuit_open/5xx/timeout with per-replica breakers and pre-stream cutoff
ADR-0077: Provider Fallback Routing
- Status: Accepted
- Date: 2026-09-12
- Authors: IBEX Harness team
- Milestone: 4.C.4 Fallback routing
- Related: ADR-0075, ADR-0076, ADR-0033
Context
Circuit breakers (4.C.3) fail fast when a provider is unhealthy but do not recover the request. Org model policies (4.C.2) gate allow/deny but deferred fallback_chain until a consumer existed. Milestone 4.C.4 adds transparent substitution to a configured next model when the primary Complete fails for breaker-open, 5xx (after adapter retries), or timeout — never for 4xx (including 429).
Decision
- Error-driven breaker observation only. Detect open via
errors.As→*provider.ProviderErrorwithReason == ErrorReasonCircuitOpenon the failed primaryComplete. No preflightState()peek and no widening ofprovider.CircuitBreaker. - Per-replica breaker state is accepted. Breakers remain in-process gobreaker instances. Fallback decisions based on locally observed Complete failures may differ across proxy replicas. Shared Redis breaker state is explicitly out of scope.
- Trigger matrix: eligible =
circuit_openOR HTTP 5xx OR timeout. Never 4xx, including 429. BYO requests (APIKeyOverride/BaseURLOverride) never fallback. - Opt-in:
org_model_policies.fallback_chain TEXT[] NOT NULL DEFAULT '{}'withcardinality <= 8(API + DB). Empty = disabled. Non-empty on the first-match allow policy for the original model = opt-in. Runtime hop depth capped byIBEX_PROVIDER_FALLBACK_MAX_DEPTH(default 1) asmin(len(chain), max_depth). - Every hop must pass
ForOrg. Deny or unknown model → skip hop. Policy infrastructure failure → abort fallback and surface the original primary error (fail closed). - Streaming hard cutoff: fallback only when primary
Completefails beforeforwardSSEStream(before any client bytes). After SSE headers flush, never fallback (double-billing / duplicate client events). - Headers
X-IBEX-Provider-Fallback/X-IBEX-Provider-Usedonly on successful substitution (after response acceptance, immediately beforeWriteHeader). Chain exhausted → the original mapped primary error; no fallback headers. - ClickHouse: additive
original_model,fallback_model,fallback_reasononibex.llm_traces. Usage attributes to the fallback model; audit fields record original + reason. Non-fallback rows leave new fields null/empty.
Consequences
- Operators configure chains on allow policies; depth is a process env knob.
- Multi-replica open detection is best-effort and LB-dependent; document for operators.
- Streaming clients still get fail-fast 503 on mid-stream upstream death without model swap.
Was this page helpful?
Edit on GitHub
Last updated on