VoiceGateway

guard()

Wrap one provider with a fallback chain, a rate limit and a spend cap. It returns the same type it wrapped, so it drops into your session unchanged.

attach() watches. guard() acts. It wraps a single native provider, a LiveKit plugin or a Pipecat service, and returns a drop-in of the same type.

llm = voicegateway.guard(
    openai.LLM(model="gpt-4o-mini"),
    fallback=[openai.LLM(model="gpt-4o")],  # tried in order when the primary errors
    rate_limit="60/min",                     # or "5/s"
    budget="$5.00/day",                      # or "$100/month"
    project="my-agent",                      # whose spend the budget reads
)

Guard only the providers that need it. Every argument is optional.

What each control does

ControlBehaviorWhen it trips
fallbackRuns each fallback in order until one succeeds. The row attach() writes carries status="fallback" and fallback_from=<primary>.Every provider failed: the last error is raised
rate_limitA token bucket parsed from "60/min" or "5/s"The bucket is empty: RateLimitExceeded
budgetReads the window's spend from the rows attach() already wroteSpend is at or over the cap: BudgetExceededError

The budget closes the loop: enforcement reads the same cost data the meter records. That is also why guard() writes no metrics of its own. With attach() on the session, nothing is counted twice.

Fallback covers failures before the first chunk

LLM and TTS stream. An error before the primary yields its first chunk moves on to the next fallback. Once a chunk has arrived, the request is committed to that provider and a later failure surfaces instead of being patched over. STT is a single request and response, so it is all or nothing.

Size a fallback chain for "the primary is down", not "the primary died mid-sentence".

What it does not do

  • Manage keys. You pass providers you already configured.
  • Meter. Cost and latency come from attach().
  • Cross frameworks. A LiveKit guard takes LiveKit fallbacks; a Pipecat guard takes Pipecat fallbacks.

It raises ImportError when the provider's framework extra is missing, and ValueError for a rate_limit or budget it cannot parse.

On this page