bumpy-student-61140
03/09/2026, 1:23 PMtall-airline-16756
03/09/2026, 1:23 PMtall-airline-16756
03/09/2026, 1:23 PMFallbackAdapter. You can list multiple providers for each service and LiveKit will automatically try the next provider if the current one fails, so your agent keeps working when a provider goes down.
What this gives you
• Automatic runtime failover for unrecoverable inference errors (STT/LLM/TTS).
• Ability to specify provider priority (primary → fallback → next).
• Works within agent sessions so the switch is transparent to the conversation flow.
Example (Python)
from livekit.agents import llm, stt, tts
from livekit.plugins import deepgram, openai, assemblyai
session = AgentSession(
stt=stt.FallbackAdapter([
deepgram.STT(),
assemblyai.STT(),
]),
llm=llm.FallbackAdapter([
openai.LLM(model="gpt-4o"),
openai.LLM.with_azure(model="gpt-4o", ...),
]),
# configure TTS similarly
)
A few practical notes and next steps
• Fallback is triggered on unrecoverable errors in the inference pipeline; it retries with the next configured provider at runtime.
• If you’d like, I can help you design a fallback list for your current providers (which ones to use as primary vs. fallback) or suggest monitoring/logging best practices to detect when an automatic failover occurs.
• Tell me which providers you currently use for STT, LLM, and TTS (and any constraints like region or latency needs) and I’ll suggest a fallback configuration and tips to test failover behavior safely.
Would you like help drafting the exact AgentSession configuration for your stack or running a test plan for failover?
Sources: Python Agents: Handling exceptions in your entrypoint | LiveKit | How does end-of-utterance detection work in conversations?bumpy-student-61140
03/10/2026, 6:41 AMtall-airline-16756
03/10/2026, 6:41 AMFallbackAdapter can switch providers at runtime, not only at startup. If a provider encounters an unrecoverable error during a call or inference request, the adapter will automatically retry using the next provider in the list so the agent can continue the session.
2) How does the adapter choose the next provider?
• The adapter follows the order you provide. It tries the first provider in the list, and if that fails it moves to the next, and so on. It does not pick providers randomly.
If you’d like, I can:
• Help you craft an ordered fallback list for your specific STT/LLM/TTS providers, considering reliability and latency.
• Suggest a safe test plan you can run to simulate provider failures and verify the runtime failover behavior. Which providers do you currently use for each service?bumpy-student-61140
03/10/2026, 6:46 AMtall-airline-16756
03/10/2026, 6:47 AMFallbackAdapter provides runtime failover (not just at startup). If a provider returns an unrecoverable error or times out during a call, the adapter automatically retries the request with the next provider in the ordered list.
• The adapter follows the order you supply — it does not choose randomly. The first provider is tried first, then the second if the first fails, and so on.
Suggested configurations (Python-like examples)
• STT (Primary: Sarvam AI, Secondary: Deepgram, Tertiary: Google Cloud STT)
from livekit.agents import stt
from livekit.plugins import sarvam, deepgram, google
stt_adapter = stt.FallbackAdapter([
sarvam.STT(), # primary
deepgram.STT(), # secondary
google.STT(), # tertiary
])
• LLM (Primary: Groq, Secondary: Cerebras, Tertiary: OpenAI GPT, Fourth: Gemini)
from livekit.agents import llm
from livekit.plugins import groq, cerebras, openai, google
llm_adapter = llm.FallbackAdapter([
groq.LLM(), # primary
cerebras.LLM(), # secondary
openai.LLM(model="gpt-4o"), # tertiary
google.LLM(model="gemini"), # fourth
])
• TTS (Primary: Google Cloud TTS, Second: Groq, Third: Cerebras, Fourth: GPT)
from livekit.agents import tts
from livekit.plugins import google, groq, cerebras, openai
tts_adapter = tts.FallbackAdapter([
google.TTS(), # primary
groq.TTS(), # second (if plugin supported)
cerebras.TTS(), # third (if plugin supported)
openai.TTS(), # fourth (if available)
])
A couple of important practical notes
• Provider support: Ensure each provider has a supported plugin/adapter in your runtime and is configured with correct credentials. Some providers (e.g., Groq/Cerebras) may primarily expose LLMs — confirm TTS STT client support exists for those providers before adding them as a TTS/STT fallback.
• Failure conditions: The adapter triggers on unrecoverable errors and timeouts returned by the provider. Transient errors that are retried internally by a provider SDK may not count as immediate failover — you can tune timeouts and retry behavior at the adapter or provider client layer.
• Ordering: Because the adapter is ordered, put the most reliable/lowest-latency provider first. Fallbacks incur extra latency while switching, so keep a minimal, preferred order where possible.
Testing failover
1. Controlled outage: Temporarily block API keys/network access for the primary provider and confirm the session fails over to the secondary while the call is active.
2. Induce timeouts: Configure a very short request timeout for the primary and validate the adapter advances to the next provider.
3. Observe logs: Enable and collect adapter/provider logs to capture the exact error leading to fallback and the provider chosen next.
4. Session continuity: Run a full audio roundtrip (STT → LLM → TTS) under simulated failure to confirm the agent continues the conversation without manual intervention.
If you’d like, I can:
• Draft an exact AgentSession snippet combining these adapters for your environment.
• Recommend timeout/retry settings and logging fields to make failover events easy to detect.
• Propose a short test plan (step-by-step) you can run safely in staging.
Which of those would you like me to prepare next? Also, confirm whether Groq and Cerebras have TTS/STT plugins in your deployment so I can adjust the TTS configuration if needed.
Sources: Python Agents: Handling exceptions in your entrypoint | LiveKitbumpy-student-61140
03/10/2026, 6:47 AMtall-airline-16756
03/10/2026, 6:48 AMAgentSession for your provider order (STT: Sarvam → Deepgram → Google Cloud STT; LLM: Groq → Cerebras → OpenAI GPT → Gemini; TTS: Google Cloud TTS → Groq → Cerebras → OpenAI). I’ll also include short notes about credentials and a couple of recommended timeout/log settings to help with testing.
Please replace placeholder API keys / deployment names with your real values and confirm Groq/Cerebras have the TTS/STT plugins available in your runtime before using them for TTS or STT.
AgentSession example (Python)
from livekit.agents import AgentSession, stt as stt_module, llm as llm_module, tts as tts_module
from livekit.plugins import sarvam, deepgram, google, groq, cerebras, openai
# Optional: tune timeouts / retries on provider constructors if supported, e.g.:
# deepgram.STT(timeout=5.0)
session = AgentSession(
stt=stt_module.FallbackAdapter([
sarvam.STT(api_key="SARVAM_API_KEY"), # Primary: Sarvam AI
deepgram.STT(api_key="DEEPGRAM_API_KEY"), # Secondary: Deepgram
google.STT(credentials="GOOGLE_CLOUD_CREDS_JSON"), # Tertiary: Google Cloud STT
]),
llm=llm_module.FallbackAdapter([
groq.LLM(api_key="GROQ_API_KEY"), # Primary: Groq
cerebras.LLM(api_key="CEREBRAS_API_KEY"), # Secondary: Cerebras
openai.LLM(model="gpt-4o", api_key="OPENAI_API_KEY"), # Tertiary: OpenAI GPT
google.LLM(model="gemini-pro", credentials="GOOGLE_CREDS"), # Fourth: Gemini (Google)
]),
tts=tts_module.FallbackAdapter([
google.TTS(credentials="GOOGLE_CLOUD_CREDS_JSON"), # Primary: Google Cloud TTS
groq.TTS(api_key="GROQ_API_KEY"), # Second: Groq (ensure plugin exists)
cerebras.TTS(api_key="CEREBRAS_API_KEY"), # Third: Cerebras (ensure plugin exists)
openai.TTS(api_key="OPENAI_API_KEY"), # Fourth: OpenAI TTS (if available)
]),
# Add your VAD / turn-detection config here (example placeholder)
# vad=your_vad_instance,
# other session settings...
)
Quick practical notes
• Ordering: The adapter tries providers in the order listed. It does not choose randomly.
• Runtime failover: Failover happens at runtime — if a provider errors or times out during a call, the adapter will retry with the next provider in the list.
• Failure triggers: The adapter triggers on unrecoverable errors and timeouts. If a provider SDK performs internal retries, you may need to tune its retry/timeout settings so failover happens within your desired window.
• Plugin availability: Confirm Groq and Cerebras provide TTS/STT plugins in your deployment. If they don’t support TTS/STT, remove them from the TTS/STT lists and keep them only for LLM.
• Credentials: Provide each plugin with the correct credentials (API keys, service account JSON, or deployment names for Azure-style providers).
• Latency: Switching providers adds delay. Place the most reliable/lowest-latency provider first to minimize user impact.
Recommended test steps (quick)
1. Controlled outage: Revoke or block the primary provider key in staging and verify the session fails over to the secondary during an active call.
2. Timeout test: Temporarily set an aggressive timeout on the primary provider to force fallback and observe behavior.
3. Logging: Enable adapter/provider logs to capture error messages and the chosen fallback provider for each failover event.
If you want, I can:
• Produce a version of this snippet with specific timeout/retry parameters tuned for aggressive failover testing.
• Generate a step-by-step staging test plan you can run (commands and expected results).
• Adjust the TTS configuration if you confirm Groq/Cerebras do NOT provide TTS plugins in your environment.
Which follow-up would you like first — tuned timeouts, a test plan, or a finalized snippet with your real credential placeholders filled in?
Sources: Python Agents: Handling exceptions in your entrypoint | LiveKitbumpy-student-61140
03/18/2026, 7:07 AMtall-airline-16756
03/18/2026, 7:09 AMbumpy-student-61140
03/24/2026, 9:32 AMtall-airline-16756
03/24/2026, 9:33 AMFallbackAdapter to tune its internal retry/backoff counts or intervals. The adapter uses an internal exponential-retry pattern (the 1s, 2.1s, 4.2s sequence you saw) and will retry the primary several times before moving on.
• Because that retry loop is implemented inside the adapter, the supported ways to change behavior are at the edges: either change provider-level timeouts/retries or wrap providers with your own logic before handing them to FallbackAdapter.
What you can do (ordered from easiest to most control)
1) Reduce provider-side timeouts so the adapter gives up faster
• Many provider clients let you set a per-request timeout. Shortening that timeout causes the provider call to fail sooner, which triggers the adapter to advance earlier.
• Example approach: when creating the provider client (if supported), pass a shorter timeout or request deadline so each attempt completes (fast-fails) quicker.
2) Wrap the provider client with your own retry/fail-fast layer (recommended)
• Create a thin adapter around the primary provider that enforces your own retry policy/backoff. If your wrapper returns an unrecoverable error quickly, FallbackAdapter will move to the next provider sooner.
• This gives you full control (max attempts, fixed vs exponential backoff, total wait time) without editing plugin code.
3) Implement a custom fallback implementation instead of FallbackAdapter
• If you need centralized control across providers (e.g., global max fallback duration, different behavior per stage), implement your own fallback orchestration: try provider A with your policy, if it fails, call provider B, etc.
• This is more work but gives deterministic behavior.
4) Simulate/force fail-fast in staging for tests
• For testing only, have your agent throw an unrecoverable exception for the primary provider on demand (feature flag or env var) so you can validate the rest of your chain and measure latency.
Practical examples (conceptual)
• Shorten provider timeout (pseudo-Python)
# If provider supports timeout configuration
primary = sarvam.STT(timeout=1.0) # fails faster
session = AgentSession(stt=stt.FallbackAdapter([primary, deepgram.STT(), google.STT()]))
• Wrapper that fail-fast after N quick tries (pseudo-Python)
class FastFailProvider:
def __init__(self, provider, max_attempts=1):
self.provider = provider
self.max_attempts = max_attempts
def transcribe(self, audio):
for i in range(self.max_attempts):
try:
return self.provider.transcribe(audio, timeout=1.0)
except Exception:
if i == self.max_attempts - 1:
# raise an unrecoverable error so FallbackAdapter moves on
raise
# else: short sleep or immediate retry
Then pass FastFailProvider(sarvam_client, max_attempts=1) into the FallbackAdapter.
What I recommend
• Start with provider-level timeouts to get faster failover with minimal code changes.
• If that’s insufficient, create a small wrapper for the primary provider that enforces your desired retry/backoff and fails fast to trigger fallback.
• Use structured logs to capture when fallback happens so you can tune timings iteratively.
If you want, I can:
• Produce an exact code snippet for your stack (Python) that wraps Sarvam, Groq, and Google Cloud clients with configurable retry/backoff and shows how to plug those into FallbackAdapter.
• Recommend concrete timeout and retry values to meet a specific maximum failover time (tell me the total failover budget you want, e.g., 1s, 2s, 3s).
Which would you like me to prepare next — a ready-to-run wrapper + AgentSession for your providers, or suggested timeout/retry values to target a specific failover window?
Sources: Build Your First AI Voice Agent in Python: Complete Tutorial | LiveKit | Build Your First AI Voice Agent in Python: Complete Tutorial | LiveKit | Agents Framework | livekit/livekit_composite | DeepWiki, with interactive elementsbumpy-student-61140
03/24/2026, 9:45 AMtall-airline-16756
03/24/2026, 9:45 AMFallbackAdapter so the adapter moves on quickly when you want it to.
• Includes suggested timeout/retry values and notes about testing.
Replace placeholder API calls/constructors with the exact methods your provider SDKs expose if they differ; the wrapper pattern is what matters.
1) Design goals
• Keep plugin code untouched.
• Fail fast for the primary according to your chosen policy (so FallbackAdapter advances sooner).
• Allow per-provider configuration (max attempts, per-attempt timeout, backoff multiplier).
• Keep fallbacks (deepgram, cerebras, openai, google TTS, etc.) as normal provider instances.
2) Example wrapper + AgentSession (conceptual, ready to adapt)
import time
from typing import Any, Callable
# Example provider interfaces used by LiveKit plugins:
# provider.transcribe(audio, timeout=...) -> result
# provider.generate(prompt, timeout=...) -> result
# provider.speak(text, timeout=...) -> audio_bytes
class FailFastWrapper:
"""
Generic wrapper to enforce a custom retry/backoff policy around a provider client.
If final attempt fails, raises the underlying exception to trigger FallbackAdapter.
"""
def __init__(
self,
client: Any,
max_attempts: int = 1,
attempt_timeout: float = 1.0,
backoff_factor: float = 2.0,
initial_delay: float = 0.0,
jitter: float = 0.0,
):
self._client = client
self.max_attempts = max_attempts
self.attempt_timeout = attempt_timeout
self.backoff_factor = backoff_factor
self.initial_delay = initial_delay
self.jitter = jitter
def _sleep_backoff(self, attempt: int):
if attempt <= 0:
return
delay = (self.initial_delay if attempt == 1 else
self.attempt_timeout * (self.backoff_factor ** (attempt - 1)))
# optional simple jitter
if self.jitter:
delay = delay * (1.0 + (self.jitter * (0.5 - 0.5)))
time.sleep(delay)
# STT example method
def transcribe(self, audio, **kwargs):
last_exc = None
for i in range(self.max_attempts):
try:
# Call underlying provider method; pass the per-attempt timeout
return self._client.transcribe(audio, timeout=self.attempt_timeout, **kwargs)
except Exception as e:
last_exc = e
# if last attempt, raise so FallbackAdapter sees an unrecoverable error
if i == self.max_attempts - 1:
raise
# short backoff before next attempt
self._sleep_backoff(i + 1)
# LLM example method
def generate(self, prompt, **kwargs):
last_exc = None
for i in range(self.max_attempts):
try:
return self._client.generate(prompt, timeout=self.attempt_timeout, **kwargs)
except Exception as e:
last_exc = e
if i == self.max_attempts - 1:
raise
self._sleep_backoff(i + 1)
# TTS example method
def speak(self, text, **kwargs):
last_exc = None
for i in range(self.max_attempts):
try:
return self._client.speak(text, timeout=self.attempt_timeout, **kwargs)
except Exception as e:
last_exc = e
if i == self.max_attempts - 1:
raise
self._sleep_backoff(i + 1)
3) Plugging wrapped providers into AgentSession
• Example assumes plugin constructors exist as shown earlier. Pass wrapper instances in place of raw clients for primary providers.
from livekit.agents import AgentSession, stt as stt_module, llm as llm_module, tts as tts_module
from livekit.plugins import sarvam, deepgram, google, groq, cerebras, openai
# Suggested aggressive failover policy:
# total failover budget for primary ≈ 1s per attempt * max_attempts + backoffs
# Example: max_attempts=1, attempt_timeout=0.8 => primary will fail fast in ~0.8s
# Configure wrappers for your primaries
sarvam_client = sarvam.STT(api_key="SARVAM_API_KEY")
sarvam_fast = FailFastWrapper(
client=sarvam_client,
max_attempts=1, # try once then fail to trigger fallback
attempt_timeout=0.8, # seconds per attempt
backoff_factor=2.0,
initial_delay=0.0,
jitter=0.0,
)
groq_client = groq.LLM(api_key="GROQ_API_KEY")
groq_fast = FailFastWrapper(
client=groq_client,
max_attempts=1,
attempt_timeout=0.8,
)
google_tts_client = google.TTS(credentials="GOOGLE_CREDS_JSON")
google_tts_fast = FailFastWrapper(
client=google_tts_client,
max_attempts=1,
attempt_timeout=0.8,
)
# Build fallback adapters with wrapped primaries and regular fallbacks
stt_adapter = stt_module.FallbackAdapter([
sarvam_fast, # wrapped primary
deepgram.STT(api_key="DEEPGRAM_API_KEY"),
google.STT(credentials="GOOGLE_CREDS_JSON"),
])
llm_adapter = llm_module.FallbackAdapter([
groq_fast, # wrapped primary
cerebras.LLM(api_key="CEREBRAS_API_KEY"),
openai.LLM(model="gpt-4o", api_key="OPENAI_API_KEY"),
google.LLM(model="gemini-pro", credentials="GOOGLE_CREDS_JSON"),
])
tts_adapter = tts_module.FallbackAdapter([
google_tts_fast, # wrapped primary
groq.TTS(api_key="GROQ_API_KEY"), # include if plugin exists
cerebras.TTS(api_key="CEREBRAS_API_KEY"), # include if plugin exists
openai.TTS(api_key="OPENAI_API_KEY"), # include if plugin exists
])
session = AgentSession(
stt=stt_adapter,
llm=llm_adapter,
tts=tts_adapter,
# other session config...
)
4) Suggested timeout/retry values to hit target failover windows
• Goal: total time before failing primary and moving to next provider.
• If you want the primary to be abandoned within ~1s: set max_attempts=1 and attempt_timeout=0.8
• If you want ~2s: set max_attempts=2 and attempt_timeout=0.9 with minimal backoff.
• If you want ~3s: set max_attempts=3 and attempt_timeout=0.9 and small backoff (0.2s)
Recommended starting points (safe for staging):
• Fast fail: max_attempts=1, attempt_timeout=0.8
• Moderate: max_attempts=2, attempt_timeout=1.0, backoff_factor=1.5
• Conservative: max_attempts=3, attempt_timeout=1.5, backoff_factor=2.0
5) Testing checklist
• Enable verbose logs for your agent and provider clients to see attempted calls, timeouts, and exceptions.
• Validate in staging: block Sarvam/Groq/Google TTS (via iptables or hosts) and confirm wrapper causes FallbackAdapter to switch quickly.
• Measure end-to-end latency to ensure user experience is acceptable.
• Confirm no silent retries are happening inside provider SDKs (some SDKs hide retries—reduce or disable those if possible).
6) Notes & caveats
• The wrapper must expose the same method names the LiveKit agent expects (transcribe, generate, speak, or whatever your plugin uses). Adjust method names to match your provider SDK.
• Some provider SDKs have internal retries; short timeouts may not be respected unless you disable those SDK-level retries.
• This approach avoids editing plugin source and gives you programmatic control over failover aggressiveness.
• If you need global centralized control (e.g., different failover timing depending on session state), you can extend the wrapper to consult runtime config or feature flags.
If you want, I can:
• Turn this conceptual example into a paste-ready snippet using the exact method names for Sarvam, Groq, and Google SDKs if you share the exact client method signatures your environment uses.
• Provide a ready iptables/hosts file snippet to block the provider domains for your staging tests.
• Provide a variant that uses a more sophisticated backoff (fixed, capped exponential, jittered) with the tenacity library.
Which follow-up would you like — adapt the wrapper to your exact SDK method names, or the iptables / hosts commands to block the providers for staging tests?
, with interactive elementsbumpy-student-61140
03/25/2026, 7:58 AMtall-airline-16756
03/25/2026, 7:59 AMFallbackAdapter with Groq as the primary and Google Vertex AI (Vertex) as the secondary. It includes a fail-fast wrapper so you can control retries/timeouts for the primary provider without editing plugin code.
Notes before you copy:
• Replace placeholder API keys, project/region, and client constructors with the exact calls your environment uses.
• Ensure your runtime has the Groq and Google Vertex plugins/drivers available and configured.
• The wrapper methods (generate) should match the method names the LiveKit agent expects from the plugin client; adjust names if needed.
Example: Groq primary → Vertex AI secondary (with fail-fast wrapper)
import time
from typing import Any
# Generic fail-fast wrapper for LLMs
class FailFastLLM:
def __init__(self, client: Any, max_attempts: int = 1, attempt_timeout: float = 1.0, backoff_factor: float = 1.5):
"""
client: underlying LLM client with a 'generate' or similar method.
max_attempts: number of quick attempts before giving up.
attempt_timeout: per-attempt timeout in seconds (passed to client if supported).
backoff_factor: multiplier for sleep between attempts (optional).
"""
self.client = client
self.max_attempts = max_attempts
self.attempt_timeout = attempt_timeout
self.backoff_factor = backoff_factor
def generate(self, prompt: str, **kwargs):
last_exc = None
for attempt in range(self.max_attempts):
try:
# Pass attempt_timeout in case client supports it
return self.client.generate(prompt, timeout=self.attempt_timeout, **kwargs)
except Exception as e:
last_exc = e
if attempt == self.max_attempts - 1:
# Final attempt failed — raise so FallbackAdapter moves on
raise
# short backoff before retrying
time.sleep(self.attempt_timeout * (self.backoff_factor ** attempt))
# Safety: raise last exception if loop exits unexpectedly
raise last_exc
# --- Example provider clients (replace with actual plugin constructors) ---
# Example placeholders:
# groq_client = groq.LLM(api_key="GROQ_API_KEY")
# vertex_client = google_vertex.LLM(project="my-project", region="us-central1", credentials="...")
# Replace these with your real client constructors
groq_client = ... # instantiate your Groq LLM client
vertex_client = ... # instantiate your Google Vertex AI LLM client
# Wrap Groq with a fail-fast policy (try once, short timeout)
groq_fast = FailFastLLM(client=groq_client, max_attempts=1, attempt_timeout=0.8, backoff_factor=1.5)
# Vertex as normal secondary (no special wrapper, or you can wrap similarly)
vertex_normal = vertex_client
# --- Build FallbackAdapter and AgentSession ---
from livekit.agents import AgentSession, llm as llm_module
llm_adapter = llm_module.FallbackAdapter([
groq_fast, # primary (wrapped to fail-fast)
vertex_normal, # secondary: Google Vertex AI
])
session = AgentSession(
llm=llm_adapter,
# include other session config as needed (stt/tts/vad/etc.)
)
Testing checklist
• In staging, block Groq endpoint or set groq_client to raise to confirm the adapter falls back to Vertex.
• Enable debug logs on your agent to confirm the error and the fallback event.
• If Vertex has SDK-level retries, consider configuring its timeout/retries so tests are deterministic.
If you want, I can:
• Produce a concrete snippet with the exact Groq and Google Vertex client constructors and method names if you share the SDK versions or the client code you use.
• Provide a variant that uses a small library like tenacity for more advanced retry/backoff logic in the wrapper.
Which would you like next — adapt the snippet to your exact SDKs, or add a tenacity-based wrapper with jittered backoff?
, with interactive elementsbumpy-student-61140
03/25/2026, 10:01 AMtall-airline-16756
03/25/2026, 10:02 AMFallbackAdapter with Groq as priority 1 and Google Vertex AI as priority 2. Drop this directly into your AgentSession configuration and replace the placeholder constructors/credentials with your actual values.
from livekit.agents import AgentSession, llm as llm_module
from livekit.plugins import groq, google # adjust imports to match your runtime plugins
# Instantiate provider clients (replace with your real init args)
groq_client = groq.LLM(api_key="GROQ_API_KEY")
vertex_client = google.LLM(project="MY_PROJECT", region="us-central1", credentials="GOOGLE_CREDS_JSON")
# Build the LLM fallback adapter: Groq (primary) -> Google Vertex AI (secondary)
llm_adapter = llm_module.FallbackAdapter([
groq_client, # primary
vertex_client, # secondary (Google Vertex AI)
])
# Use in your AgentSession
session = AgentSession(
llm=llm_adapter,
# add stt/tts/vad/etc. as needed
)
Notes
• Replace the plugin constructors and credential placeholders with the exact calls and keys your environment uses.
• This snippet does not add any custom retry or fail-fast logic — it simply hands the two providers to the FallbackAdapter in priority order (Groq first, Vertex second).
• For testing, you can block Groq endpoints or simulate failures to confirm the adapter falls back to Vertex.
If you want, I can also:
• Convert this to your exact Groq/Vertex client constructors if you share the SDK names/versions, or
• Add a quick test snippet that simulates Groq failing so you can confirm fallback in staging. Which would you prefer?, with interactive elementsbumpy-student-61140
03/25/2026, 11:12 AMtall-airline-16756
03/25/2026, 11:12 AMFallbackAdapter to make it “sticky” (i.e., mark the primary failed and skip it for X turns).
• The practical way to get sticky behavior is to add a small stateful layer (a circuit‑breaker / cooldown) in front of the LLM provider so the adapter never sees repeated attempts at the primary while it’s unhealthy.
• I’ll give you a ready-to-use pattern (circuit breaker) you can drop into your AgentSession, plus alternatives and testing tips.
Recommended solution (what I suggest)
• Wrap the primary LLM client with a circuit-breaker that:
◦ Records failures and opens the circuit for a configured cooldown (or number of turns).
◦ While open, the wrapper immediately raises an unrecoverable error (so the FallbackAdapter uses the secondary).
◦ After cooldown, the wrapper probes the primary (or allows a trial request) and closes the circuit on success.
• Use a small in-memory store for single-process agents, or Redis/shared store for multi-worker setups so state is shared across agent instances.
Drop-in circuit-breaker wrapper (Python)
• Behavior:
◦ fail_threshold: number of failures to open circuit
◦ cooldown_seconds: how long to stay open before trying again
◦ optional: trial_requests: allow 1 trial request after cooldown
import time
from typing import Any
class CircuitBreakerLLM:
def __init__(self, client: Any, fail_threshold: int = 1, cooldown_seconds: float = 30.0):
"""
client: underlying LLM client with a `generate` (or similar) method.
fail_threshold: how many failures to open the circuit (1 = open on first failure).
cooldown_seconds: how long to skip the primary before allowing a trial.
"""
self.client = client
self.fail_threshold = fail_threshold
self.cooldown_seconds = cooldown_seconds
# state
self.failure_count = 0
self.open_until = 0.0 # epoch time until which circuit is open
def _is_open(self) -> bool:
return time.time() < self.open_until
def _record_failure(self):
self.failure_count += 1
if self.failure_count >= self.fail_threshold:
self.open_until = time.time() + self.cooldown_seconds
def _record_success(self):
self.failure_count = 0
self.open_until = 0.0
def generate(self, prompt: str, **kwargs):
# If circuit is open, immediately raise so FallbackAdapter moves on
if self._is_open():
raise RuntimeError("Primary LLM circuit open; using fallback")
try:
result = self.client.generate(prompt, **kwargs)
# success: reset failure state
self._record_success()
return result
except Exception as e:
# record failure and open circuit if threshold reached
self._record_failure()
# raise so FallbackAdapter treats as unrecoverable and uses next provider
raise
How to plug into AgentSession (Groq primary → Vertex secondary)
from livekit.agents import AgentSession, llm as llm_module
from livekit.plugins import groq, google # adjust to your runtime imports
# instantiate provider clients (replace args with your real constructors)
groq_client = groq.LLM(api_key="GROQ_API_KEY")
vertex_client = google.LLM(project="MY_PROJECT", region="us-central1", credentials="GOOGLE_CREDS_JSON")
# wrap Groq with circuit breaker (open on first failure, stay open 30s)
groq_cb = CircuitBreakerLLM(client=groq_client, fail_threshold=1, cooldown_seconds=30.0)
llm_adapter = llm_module.FallbackAdapter([
groq_cb, # primary (stateful, sticky)
vertex_client, # secondary
])
session = AgentSession(llm=llm_adapter)
Why this fixes your problem
• While the circuit is open, the wrapper raises immediately. The FallbackAdapter therefore uses the secondary without retrying the failing primary — eliminating the repeated wait/retry cost per turn.
• After cooldown, the wrapper allows a trial; on success it resets and primary becomes active again.
• You can tune cooldown_seconds and fail_threshold to match your tolerance (e.g., skip primary for N turns or for T seconds).
Variants / extra options
• Turn-based stickiness: instead of time-based cooldown, increment a turn counter (persisted per session) and skip primary for X turns. Use a shared store if multiple agent processes handle turns for the same session.
• Health probing: run a background probe (lightweight request) to mark primary healthy again and close circuit sooner if it recovers.
• Use a circuit-breaker library (e.g., pybreaker or custom) if you prefer more features (metrics, events).
• Use Redis or another shared cache for distributed deployments so all workers honor the same breaker state.
Testing suggestions
• Set cooldown_seconds small in staging (e.g., 5s) to validate open/close and observe behavior.
• Force the primary to fail (hosts file or wrapper that throws) and verify the adapter immediately uses the secondary and does not retry the primary each turn.
• Add clear logging in the wrapper for state transitions (opened, trial allowed, closed) so you can audit behavior in tests.
If you want, I can:
• Produce a turn-count based wrapper (skip primary for N turns) instead of time-based cooldown.
• Add Redis-backed state to the wrapper with sample code (useful for multi-worker setups).
• Provide a version that logs events in a structured way (JSON) to make monitoring easier.
Which option would you like next — time-based circuit-breaker (we already have that), turn-count cooldown, or a Redis-backed shared circuit-breaker for multi-worker setups?
Sources: Build Your First AI Voice Agent in Python: Complete Tutorial | LiveKit | Build Your First AI Voice Agent in Python: Complete Tutorial | LiveKit | OpenRouter LLM plugin guide | LiveKit Documentation, with interactive elements