Floe Guard 0.23.2 shows why a budget check needs a reservation
A voice agent can spend money in four places during one conversation: speech recognition, the language model, speech synthesis, and the phone connection. A dashboard can total those charges later. A budget control has to decide whether more paid work may begin while other work is still running.
Floe Guard 0.23.2, published to PyPI on August 29, is a small open-source library built around this problem. Its documentation states a limit that applies to other agent systems too. Checking the remaining budget and reserving part of it are different operations. Under concurrency, only the reservation can enforce a shared ceiling.
The pre-call check is intentionally coarse
Floe Guard has admission helpers for deciding whether an inbound voice call should start. The gate source says the check reads remaining_usd without reserving that amount.
Suppose an agent has $0.12 left and expects each new call to cost $0.08. Two calls arrive together. Both can read the same $0.12 headroom before either call records spend, so both pass. The system has admitted $0.16 of expected work against $0.12.
The package accepts estimated_call_usd, which lets a call be rejected before the budget reaches zero. With the default estimate of 0.0, the helper rejects only after the remaining budget is exhausted. A better estimate improves the decision, but two concurrent requests can still observe the same headroom.
Floe Guard labels the gate a non-binding preflight and coarse admission control. Calling that read a hard ceiling would promise more than the code does.
Reserve before the paid turn
The binding control sits deeper in the call path. Floe Guard's voice adapter documentation describes a reserve, settle, and release loop:
- Reserve estimated budget before an LLM turn starts.
- Settle the reservation after the framework reports actual usage.
- Release it if the turn is interrupted before usable metrics arrive.
An atomic reservation changes shared state before another worker checks it. In the $0.12 example, the first $0.08 reservation leaves $0.04. The second turn is blocked instead of reading the old total.
Voice systems also need the release step. A caller may talk over the agent, a response may be cancelled, or the connection may close. Keeping every abandoned reservation would eventually block valid work. Releasing a hold before the paid work has stopped would let spend escape the ledger. The adapter has to connect each lifecycle event to the correct reservation.
This resembles the state problem in our guide to reviewable agent tool interfaces: a result needs an identity and a transition that later events can reconcile.
Metrics are evidence, not enforcement
Voice frameworks already expose usage data. Pipecat's metrics documentation says enable_usage_metrics=True enables usage reporting, including LLMUsageMetricsData for token use. LiveKit's data hooks expose metrics at component, turn, live-session, and final-session scopes. Its LLM metrics include prompt, completion, and cached-token counts.
Those events support settlement and auditing. They arrive after work has started, so they cannot stop the call that produced them. A listener that only adds charges performs accounting. Enforcement needs code before the paid operation and an estimate large enough to reserve.
A useful trace records the admission decision, reservation ID and amount, settlement amount, released holds, and price snapshot. Otherwise a team may see that the budget was exceeded without learning which boundary failed. Our agent tracing guide covers the broader case for recording decisions instead of only outputs.
The phone bill is still an estimate
Floe Guard can meter named STT, LLM, TTS, and telephony legs from bundled price maps. Its README lists several limits that a production design should expose.
The voice prices are dated snapshots of public list rates. Telephony coverage is US-only in version 1. Some TTS prices are converted from audio minutes using an assumed character rate, and some session overhead is not modeled. A model or vendor absent from the map may require a manual override. The local guard is a pacing ceiling, not a guarantee that its total will match the invoice.
Call admission has another limit. Once a call starts, the gate does not end it partway through. Per-turn reservations can block later model turns, but telephony continues to accrue by the minute. A product needs an explicit exhaustion behavior, such as finishing the current response, speaking a short closing line, or routing to a non-AI fallback. The budget rule affects what the caller hears.
One process is not a fleet
A local lock can coordinate threads in one process. Floe Guard says multiple Python processes can share one UTC-day ceiling only when they open the same SQLite file on storage with reliable locking. Separate serverless instances with separate local files do not coordinate.
That deployment detail changes the guarantee. "Every worker has a $10 guard" does not mean "the service has a $10 guard." A fleet-wide ceiling needs one authoritative reservation store or server-side enforcement. The component allowed to admit spend should also have the smallest practical scope and a reviewable failure mode, as described in our least-privilege checklist for agent tools.
If model traffic uses a gateway such as ISH API, one cost leg may be easier to centralize. Speech, telephony, and paid tools can still sit elsewhere. A cost receipt should state which legs it covers.
A budget-control contract worth testing
Before calling a budget feature a hard ceiling, test concurrent arrivals and interrupted turns:
- Admission is non-binding unless it reserves funds. Record the expected-call estimate it used.
- Reserve each paid turn atomically before execution, then settle actual usage or release the hold.
- For every cost leg, record its source, unit, price date, and unpriceable failure policy.
- Parallel workers need one authoritative budget state.
- Traces should connect admission, reservation, settlement, release, and the final receipt.
- Define what the caller hears when the next turn is blocked.
Use the preflight to reject obviously unaffordable calls. At the boundary where paid work begins, reserve atomically. Settle that reservation against the usage evidence reported by the voice framework, or release it when an interrupted turn produces no usable metrics.



