Skip to content

Commit 75c944e

Browse files
committed
docs: document Pipecat + LiveKit voice adapters and TS-parity position
The README led with generic LLM adapters and never surfaced the two voice adapters that ship in the Python package (Pipecat, LiveKit), so a voice-AI builder (STT->LLM->TTS) couldn't tell floe-guard covers their stack. - Add ### Pipecat (voice) and ### LiveKit (voice) sections with runnable Python examples using the real import paths (floe_guard.integrations.pipecat.FloeBudgetGuardProcessor and floe_guard.integrations.livekit.LiveKitBudgetGuard). - Surface both in the top-line "Works with" list. - Add a compact adapter x language matrix making the Python-only reality of the voice adapters explicit. - Add an explicit TypeScript voice-parity paragraph: no TS voice adapter today, JS path is Vercel AI middleware (text), voice builders use the Floe proxy or the Python adapters; not yet available, no committed roadmap. - Cross-link the Floe docs "Add Floe to your existing pipeline" guide. Docs-only. No code touched.
1 parent b8faa47 commit 75c944e

1 file changed

Lines changed: 123 additions & 2 deletions

File tree

README.md

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ process.
1515

1616
Works with [CrewAI](#crewai) · [LiteLLM](#litellm) · [LangChain](#langchain) ·
1717
[LangGraph](#langgraph) · [OpenAI](#openai) · [Anthropic](#anthropic) ·
18-
[Gemini](#google-gemini) · [Vercel AI SDK](#vercel-ai-sdk) — or any stack, via
19-
plain `check()` / `record()`.
18+
[Gemini](#google-gemini) · [Vercel AI SDK](#vercel-ai-sdk) — and voice pipelines
19+
via [Pipecat](#pipecat-voice) · [LiveKit](#livekit-voice) — or any stack, via
20+
plain `check()` / `record()`. See the [adapter matrix](#adapter-matrix) for what
21+
ships in Python vs TypeScript.
2022
The hard-stop is contract-based: gate each call through the guard — adapters do
2123
it for LLM calls; for paid tools, [`reserve_tool()` / `settle_tool()`](#tool-spend-under-the-same-ceiling)
2224
block *before* the call runs (`record_tool()` alone meters a call after the
@@ -552,6 +554,125 @@ The middleware `check()`s before each call (throwing `BudgetExceeded` to halt th
552554
run) and `record()`s priced usage after — same semantics as the Python guard. See
553555
[`js/README.md`](js/README.md).
554556

557+
## Voice adapters (STT → LLM → TTS)
558+
559+
A voice pipeline has no single call site to wrap: the LLM sits inside a running
560+
session and turns fire continuously for the life of a call. So instead of a
561+
function wrapper, these adapters enforce **per turn** — reserve before the LLM
562+
call (so a turn is blocked *before* its TTS/audio spend piles on top of a call
563+
that would already cross the ceiling), settle on the real usage the pipeline
564+
reports, and release a turn that ends without ever reporting usage (an
565+
interrupted turn) so the reservation never leaks against the ceiling. The token
566+
cost map is LLM-only, so STT/TTS spend is metered only when you supply per-unit
567+
prices. Both voice adapters are **Python-only** today.
568+
569+
### Pipecat (voice)
570+
571+
```bash
572+
pip install floe-guard[pipecat]
573+
```
574+
575+
Drop a `FloeBudgetGuardProcessor` into the pipeline directly after the LLM
576+
service. It reserves on each turn's `LLMFullResponseStartFrame` and settles from
577+
the `LLMUsageMetricsData` Pipecat emits — so the pipeline's `PipelineTask` must
578+
be created with `enable_metrics=True, enable_usage_metrics=True`.
579+
580+
```python
581+
from pipecat.pipeline.pipeline import Pipeline
582+
from pipecat.pipeline.task import PipelineTask, PipelineParams
583+
from floe_guard import BudgetGuard
584+
from floe_guard.integrations.pipecat import FloeBudgetGuardProcessor
585+
586+
guard = BudgetGuard(limit_usd=1.00)
587+
588+
pipeline = Pipeline([
589+
transport.input(),
590+
stt,
591+
context_aggregator.user(),
592+
llm,
593+
FloeBudgetGuardProcessor(guard, model="gpt-4o"), # meters AND hard-stops
594+
tts,
595+
transport.output(),
596+
context_aggregator.assistant(),
597+
])
598+
task = PipelineTask(
599+
pipeline,
600+
params=PipelineParams(enable_metrics=True, enable_usage_metrics=True),
601+
)
602+
```
603+
604+
By default a blocked turn pushes a fatal `ErrorFrame` that terminates the
605+
pipeline — the hard-stop every other adapter gives you. Pass an
606+
`on_budget_exceeded` async callback to speak a graceful "wrapping up" line first
607+
instead of cutting the call dead.
608+
609+
### LiveKit (voice)
610+
611+
```bash
612+
pip install floe-guard[livekit]
613+
```
614+
615+
`LiveKitBudgetGuard.attach(session, agent)` wires the reserve-before /
616+
settle-after contract onto a LiveKit `AgentSession`: it reserves in the agent's
617+
`llm_node` and settles on the session's `metrics_collected` `LLMMetrics`.
618+
LiveKit's `LLMMetrics` doesn't report the served model, so cost settles against
619+
the `model` you pass here.
620+
621+
```python
622+
from livekit.agents import AgentSession
623+
from floe_guard import BudgetGuard, ManualPrice
624+
from floe_guard.integrations.livekit import LiveKitBudgetGuard
625+
626+
guard = BudgetGuard(
627+
limit_usd=1.00,
628+
price_overrides={"gemini-2.0-flash": ManualPrice(0.30e-6, 2.50e-6)},
629+
)
630+
budget = LiveKitBudgetGuard(guard, model="gemini-2.0-flash")
631+
632+
session = AgentSession(...)
633+
budget.attach(session, agent) # wire reserve / settle / release
634+
await session.start(agent=agent, room=ctx.room)
635+
```
636+
637+
Pass `stt_usd_per_second` / `tts_usd_per_1k_chars` to also meter STT/TTS spend
638+
(often a voice agent's larger bill) via `record_tool`, and an `on_budget_exceeded`
639+
async callback to speak a wrap-up line before a turn ends. See
640+
[`examples/voice_turn_budget.py`](examples/voice_turn_budget.py).
641+
642+
## Adapter matrix
643+
644+
| Adapter | Python | TypeScript |
645+
|---|---|---|
646+
| OpenAI || via [Vercel AI SDK](#vercel-ai-sdk) |
647+
| Anthropic || via [Vercel AI SDK](#vercel-ai-sdk) |
648+
| Google Gemini || via [Vercel AI SDK](#vercel-ai-sdk) |
649+
| LangChain |||
650+
| LangGraph |||
651+
| CrewAI |||
652+
| LiteLLM |||
653+
| Vercel AI SDK |||
654+
| **Pipecat** (voice) |||
655+
| **LiveKit** (voice) |||
656+
657+
The TypeScript package is a single Vercel AI SDK middleware that guards any model
658+
the AI SDK wraps (OpenAI, Anthropic, Gemini, …) — it is not a per-framework
659+
adapter set like the Python package.
660+
661+
### TypeScript voice parity
662+
663+
**There is no TypeScript voice adapter today.** The JS package
664+
([`js/`](js/)) ships one surface — the Vercel AI SDK middleware — and it is
665+
**text-only**: it guards a wrapped `LanguageModel`, not a running STT → LLM → TTS
666+
session. A voice/telephony builder on a Node stack should either drive the LLM
667+
turn through the Vercel AI middleware and meter STT/TTS spend by hand via
668+
`recordTool`, or run the LLM leg through the **hosted [Floe proxy](#upgrade-to-hosted-floe)**
669+
(un-bypassable, cross-vendor) — or use the Python Pipecat / LiveKit adapters
670+
above, which enforce the whole turn. A native TS Pipecat/LiveKit adapter is **not
671+
yet available**; there is no committed roadmap for one.
672+
673+
For wiring floe-guard into an existing voice pipeline, see the Floe docs:
674+
**[Add Floe to your existing pipeline](https://floe-labs.gitbook.io/docs/getting-started/integrate-existing-pipeline)**.
675+
555676
## Honest about what this is
556677

557678
floe-guard is a **local, estimate-based** guardrail. It prices tokens from a

0 commit comments

Comments
 (0)