Problem
llm.FallbackAdapter applies a single attempt_timeout to every provider, so a fallback inherits the primary's latency target. For voice agents the two needs conflict:
- The primary's timeout should stay tight so a slow turn fails over quickly and the caller isn't left in silence.
- A fallback typically runs cold — no warmed prompt cache, no session affinity — so its time-to-first-token is meaningfully higher than the primary's, and it needs a longer window to actually serve.
With one shared value there is no good setting: raising it to accommodate the fallback slows every ordinary failover, while keeping it tight regularly cuts off a healthy fallback mid-attempt — turning a one-provider blip into a fully failed request and marking a working provider unavailable. We hit exactly this in production.
Proposal
An optional fallback_attempt_timeout on FallbackAdapter.__init__, applying to every LLM after the first (serving attempts and recovery checks alike):
FallbackAdapter([primary, fallback], attempt_timeout=5.0, fallback_attempt_timeout=8.0)
Default None keeps today's behavior (attempt_timeout applies to all providers), so the change is fully backward compatible.
Presumably the same reasoning applies to the STT/TTS fallback adapters, but this proposal scopes to LLM where the cold-start gap is largest.
Implementation
Draft PR with the change and tests: #6915. Happy to adjust scope/naming based on discussion here.
🤖 Generated with Claude Code
Problem
llm.FallbackAdapterapplies a singleattempt_timeoutto every provider, so a fallback inherits the primary's latency target. For voice agents the two needs conflict:With one shared value there is no good setting: raising it to accommodate the fallback slows every ordinary failover, while keeping it tight regularly cuts off a healthy fallback mid-attempt — turning a one-provider blip into a fully failed request and marking a working provider unavailable. We hit exactly this in production.
Proposal
An optional
fallback_attempt_timeoutonFallbackAdapter.__init__, applying to every LLM after the first (serving attempts and recovery checks alike):Default
Nonekeeps today's behavior (attempt_timeoutapplies to all providers), so the change is fully backward compatible.Presumably the same reasoning applies to the STT/TTS fallback adapters, but this proposal scopes to LLM where the cold-start gap is largest.
Implementation
Draft PR with the change and tests: #6915. Happy to adjust scope/naming based on discussion here.
🤖 Generated with Claude Code