I am wondering if there is inbuilt functionality i...
# ask-ai
b
I am wondering if there is inbuilt functionality in livekit to add redundancies in the stt, llm and tts models. Since there are times when the stt, or the llm or the tts stops working, so if there is inbuilt functionality to look at the live agent, and incase something stops working, or goes down, it automatically switches models to other providers that might work.
✅ 1
t
Thanks for reaching out, Aryan. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — that’s a great question and I understand why you’d want automatic failover for STT, LLM, and TTS providers. Short answer: Yes. LiveKit provides a built‑in fallback mechanism you can use to add redundancy for STT, LLM, and TTS via the
FallbackAdapter
. 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)
Copy code
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?
b
A quick follow up question, does the fallback adapter only work when the agent is starting up, so that if while starting up the model doesnt work, it switches to another? or if the model stops working during the call, it can still switch to another? also does the fallback adapter chose randomly? or the first one defined comes first, and then it keeps going down the list?
t
Thanks — good follow-up. I’ll answer both parts directly. 1) When does the fallback happen? • The
FallbackAdapter
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?
b
For STT, primary is Sarvam AI, secondary is deepgram, tertiary is Google Cloud STT. For LLM, primary is Groq, secondary is Cerebras, tertiary is OpenAI GPT, fourth is Gemini. For TTS, Primary is Google Cloud TTS, Second is Groq, Third is Cerebras, Fourth is GPT
t
Thanks — this is helpful. I'll confirm how LiveKit's fallback works for your exact provider lists and offer a ready-to-use configuration plus a few practical tips to test and harden failover. Summary for your setup • LiveKit’s
FallbackAdapter
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)
Copy code
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)
Copy code
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)
Copy code
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 | LiveKit
b
Draft an exact agent session please
t
Thanks — I’ll draft an exact, ready-to-use
AgentSession
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)
Copy code
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 | LiveKit
b
Since i want to test the fallback adapter, i need to block the requests to Sarvam for the STT, groq for the LLm and google cloud for the TTS. i need to know how the livekit agent works, where it sends the request from, is it using the livekit server, and i need to block i thtere? which i cant? or does it do the processing on my PC? what is the server for? what is my server for? what happens on my PC?
t
Thanks — good, focused questions. I’ll explain exactly where requests originate, what each component (LiveKit server, your agent server, and the client/PC) does, and where you need to block traffic to test failover of Sarvam (STT), Groq (LLM), and Google Cloud (TTS). Short answer • AI requests (STT, LLM, TTS) are sent from your agent process (your server/worker), not from the LiveKit server or the client PC. • To test failover you must block outbound requests from the machine or environment where your agent runs. • The LiveKit server only routes media and handles signaling; it does not call STT/LLM/TTS providers. What each component does • Your agent server (where AgentSession runs) ◦ Orchestrates the voice pipeline: receives audio, calls STT → LLM → TTS, and returns audio/results to the session. ◦ Makes direct outbound API calls to Sarvam, Groq, Google Cloud, etc. ◦ This is the place to block network access or simulate provider failures for testing fallback. • LiveKit server (SFU / signaling) ◦ Routes WebRTC media and manages rooms/participants. ◦ Does NOT perform AI inference or call STT/LLM/TTS providers. • Client device (browser/PC/mobile) ◦ Captures/plays audio and connects to LiveKit via WebRTC. ◦ Does not perform AI model calls (except if you intentionally run agent code there). Where to block for your tests • Block on the agent host (recommended): configure firewall rules, iptables, a cloud security group, or a proxy on the machine or in the VPC where the agent runs to deny egress to the provider endpoints you want to simulate as down: ◦ Block Sarvam endpoints to simulate primary STT outage. ◦ Block Groq endpoints to simulate primary LLM outage. ◦ Block Google Cloud TTS endpoints to simulate primary TTS outage. • Alternatives: ◦ Use a local hosts file or DNS override on the agent host to point provider domains to localhost/blackhole. ◦ Configure a proxy for outbound traffic from the agent and have the proxy return errors for targeted domains. ◦ Programmatically simulate failure in your code by throwing an unrecoverable error from the primary provider client during testing (useful when you cannot change network rules). Why blocking on the LiveKit server or client won’t work • LiveKit server does not make the external AI calls, so blocking it won’t stop your agent’s requests. • Blocking on the client would disrupt audio/media transport and make tests less accurate (it would break the call media instead of just the AI provider connection). Testing tips to verify fallback behavior 1. Prepare logs: enable detailed logs in your agent so you can see the error that caused fallback and the adapter switching to the next provider. 2. Controlled network block: ◦ On the agent host, add a firewall rule or DNS override for the provider domain(s). ◦ Run a live session and confirm STT/LLM/TTS requests to the blocked domain fail and the FallbackAdapter switches to the next provider. 1. Timeout test: ◦ Configure an aggressive timeout for the primary provider client (if supported) so calls time out quickly and trigger fallback. 1. Programmatic test: ◦ Add a short test-only wrapper that raises an unrecoverable exception for the primary provider client to force fallback without network changes. 1. Observe session continuity: ◦ Confirm the agent continues the audio roundtrip (STT → LLM → TTS) with the fallback providers and that end-to-end latency remains acceptable. Practical examples of blocking methods (pick one) • Linux iptables (agent host) ◦ Block by IP or domain (IP blocking is more reliable): ▪︎ iptables -A OUTPUT -d provider-ip -j REJECT • Hosts file (quick local test) ◦ Add to /etc/hosts: ▪︎ 127.0.0.1 api.sarvam.example ▪︎ 127.0.0.1 api.groq.example • Proxy-based blocking ◦ Route agent traffic through an HTTP proxy and configure the proxy to return 5xx for blocked hosts. • In-code simulation ◦ During staging tests, wrap the primary provider client and force it to raise an error on the first request. Final checklist before testing • Confirm your agent process is the one making provider calls (not some other service). • Make sure agent logs are verbose for adapter/provider errors. • Ensure your fallback list is correct in order and that all fallback providers are reachable. • Test in staging, not production, to avoid impacting live users. If you want, I can: • Provide the exact iptables/hosts file commands tailored to the real provider domains you use. • Draft a small code snippet to simulate a primary-provider failure in your AgentSession for safe staging tests. • Recommend specific timeout and retry settings for faster failover during testing. Which would you like next — the iptables/DNS commands for Sarvam/Groq/Google Cloud, or a code snippet to simulate failures in your agent code? Sources: livekit/livekit_composite | DeepWiki | Agent server overview | LiveKit Documentation | Agents Framework | livekit/livekit_composite | DeepWiki | Deepgram STT | LiveKit Documentation | Models overview | LiveKit Documentation, with interactive elements
b
While testing I found that it retries connections 3 times, that too with an escalating time gap of 1s, 2.1s and 4.2s respectively. It takes 8 second to disregard the first option, and it tries the first option again with the same gap twice. Rertying for 8s thrice. How do i change this behavior? I know i can edit the plugin itself, but that is a stupid way of going about it. Surely fallback will have a method of changing how agressively I want to fallback
t
Thanks — great detail and a fair frustration. I’ll be direct about what you can change and show practical alternatives so you can control how aggressively the system falls back without hacking plugin internals. Short answer • There is no exposed API on the
FallbackAdapter
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)
Copy code
# 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)
Copy code
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 elements
b
yes please
t
Great — I’ll provide a ready-to-run Python example that does three things: • Wraps your primary providers (Sarvam for STT, Groq for LLM, Google Cloud for TTS) with a configurable fail-fast wrapper that enforces a custom retry/backoff policy. • Shows how to plug those wrapped providers into
FallbackAdapter
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)
Copy code
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.
Copy code
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 elements
b
can you help me with a fallback adapter for llm, using groq as the first and google vertex ai as second?
t
Thanks — I can do that. Below is a ready-to-use Python example that sets up an LLM
FallbackAdapter
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)
Copy code
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 elements
b
lets just have the snippet without any fast fallback, the small fallback adapter snippet which i can just as is into my agent definition, with groq as priority 1 and google vertex as priority 2.
t
Thanks — here’s a minimal, ready-to-paste Python snippet that creates an LLM
FallbackAdapter
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.
Copy code
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 elements
b
So I've come across an issue. The STT fallback adapter switches to the other provider and stays on that provider, but the LLM fallback adapter will try the first LLM every time if its not working before switching to the fallback LLM. is there no way to make it sticky? such that if the LLM doesnt work once, it marks it as not working and uses the secondary LLM for X turns after which it will try the first LLM again? because this is not proper behavior, as just the retrying + switching takes longer than the entire turn should take. If you have another solution for this except for mine, you can give that too.
t
Thanks — I hear your frustration. You’re right that the LLM behavior you described (re-trying the primary on every turn before falling back) is not ideal for low-latency turn-based agents. I’ll be direct and actionable. Short answer • There is no built‑in option on the LLM
FallbackAdapter
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
Copy code
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)
Copy code
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