|
| 1 | +# floe-guard |
| 2 | + |
| 3 | +**A local budget guardrail for AI agents.** It hard-stops your agent *before its |
| 4 | +next LLM call* when it would cross a spend ceiling — so a runaway loop dies at |
| 5 | +$0.10 instead of $4,000. No account, no signup, no network. Runs in your process. |
| 6 | + |
| 7 | +```bash |
| 8 | +pip install floe-guard |
| 9 | +``` |
| 10 | + |
| 11 | +```python |
| 12 | +from floe_guard import BudgetGuard |
| 13 | + |
| 14 | +guard = BudgetGuard(limit_usd=5.00) # your ceiling |
| 15 | +guard.check() # before each LLM call — raises if it'd cross |
| 16 | +response = call_your_llm(...) # your existing call |
| 17 | +guard.record("gpt-4o", response.usage.prompt_tokens, response.usage.completion_tokens) |
| 18 | +``` |
| 19 | + |
| 20 | +When the next call would cross the ceiling, the guard raises `BudgetExceeded` and |
| 21 | +prints: |
| 22 | + |
| 23 | +``` |
| 24 | +BUDGET EXCEEDED — call blocked |
| 25 | + spent so far: $5.001250 | ceiling: $5.000000 |
| 26 | + The next call would cross your budget; floe-guard stopped your agent before it ran. |
| 27 | +``` |
| 28 | + |
| 29 | +_Animated demo coming — run `python examples/runaway_loop.py` to watch it stop a loop live._ |
| 30 | +<!-- TODO: record docs/stop-the-loop.gif and restore the embed:  --> |
| 31 | + |
| 32 | +## See it stop a loop (no API key needed) |
| 33 | + |
| 34 | +```bash |
| 35 | +python examples/runaway_loop.py |
| 36 | +``` |
| 37 | + |
| 38 | +This rigs a loop against a **stub LLM** — no real API key, no account, no network. |
| 39 | +It prices each fake `gpt-4o` call offline and the guard halts the loop after a few |
| 40 | +iterations. This is the reproducible "stop the loop" demo. |
| 41 | + |
| 42 | +## How it works |
| 43 | + |
| 44 | +The guard sits **in the call path**, not on an event bus. A passive listener is |
| 45 | +told about spend *after the fact* and can't halt anything — so enforcement has to |
| 46 | +be the thing standing in front of the next call: |
| 47 | + |
| 48 | +- **`check()`** runs before each LLM call. It predicts the next call's cost from |
| 49 | + the last one and raises `BudgetExceeded` if that would cross your ceiling — the |
| 50 | + call never runs. (A running-total check also catches an overshoot if an estimate |
| 51 | + came in low.) |
| 52 | +- **`record(model, prompt_tokens, completion_tokens)`** runs after each response. |
| 53 | + It prices the tokens **offline** from a bundled |
| 54 | + [LiteLLM cost map](src/floe_guard/cost_map.json) and adds the USD to a running |
| 55 | + total. |
| 56 | + |
| 57 | +### Unpriceable models fail closed |
| 58 | + |
| 59 | +If a model isn't in the cost map and you didn't supply a price, the guard **warns |
| 60 | +loudly and refuses** (`UnpriceableModelError`) rather than silently treat it as |
| 61 | +free — *you can't cap spend you can't measure.* Give it a price to enforce it: |
| 62 | + |
| 63 | +```python |
| 64 | +from floe_guard import BudgetGuard, ManualPrice |
| 65 | + |
| 66 | +guard = BudgetGuard( |
| 67 | + limit_usd=5.00, |
| 68 | + price_overrides={"my-self-hosted-model": ManualPrice(1e-6, 2e-6)}, # USD/token |
| 69 | +) |
| 70 | +# or, set fail_closed=False to warn-and-skip for models you accept un-metered. |
| 71 | +``` |
| 72 | + |
| 73 | +## Framework adapters (optional extras) |
| 74 | + |
| 75 | +### CrewAI |
| 76 | + |
| 77 | +```bash |
| 78 | +pip install floe-guard[crewai] |
| 79 | +``` |
| 80 | + |
| 81 | +```python |
| 82 | +from crewai import Crew |
| 83 | +from floe_guard import BudgetGuard |
| 84 | +from floe_guard.integrations.crewai import guard_crew |
| 85 | + |
| 86 | +guard = BudgetGuard(limit_usd=1.00) |
| 87 | +guard_crew(guard) # one line — enforces across the whole crew |
| 88 | +Crew(agents=[...], tasks=[...]).kickoff() |
| 89 | +``` |
| 90 | + |
| 91 | +CrewAI runs on LiteLLM, so one callback caps every agent and task under a single |
| 92 | +budget. |
| 93 | + |
| 94 | +### LiteLLM |
| 95 | + |
| 96 | +```bash |
| 97 | +pip install floe-guard[litellm] |
| 98 | +``` |
| 99 | + |
| 100 | +```python |
| 101 | +from floe_guard import BudgetGuard |
| 102 | +from floe_guard.integrations.litellm import guarded_completion |
| 103 | + |
| 104 | +guard = BudgetGuard(limit_usd=1.00) |
| 105 | +response = guarded_completion(guard, model="gpt-4o", messages=[...]) |
| 106 | +``` |
| 107 | + |
| 108 | +Prefer the LiteLLM-native callback? Register `budget_guard_callback(guard)` on |
| 109 | +`litellm.callbacks`. |
| 110 | + |
| 111 | +### Coming next |
| 112 | + |
| 113 | +LangChain (callback) and the Vercel AI SDK (TypeScript middleware) are next. Open |
| 114 | +an issue if you want one sooner. |
| 115 | + |
| 116 | +## Honest about what this is |
| 117 | + |
| 118 | +floe-guard is a **local, estimate-based** guardrail. It prices tokens from a |
| 119 | +vendored cost map *inside your process*: |
| 120 | + |
| 121 | +- The cost map can drift as vendors change prices — refresh it like any snapshot. |
| 122 | +- It only sees the vendors you instrument. |
| 123 | +- A determined agent or a bug could route around an in-process check. |
| 124 | + |
| 125 | +It's genuinely useful on its own, and it's honest about its limits. No inflated |
| 126 | +metrics, no "zero defaults" claims — it's a free local stop, not a vault. |
| 127 | + |
| 128 | +## Upgrade to hosted Floe |
| 129 | + |
| 130 | +When you need the ceiling to be **un-bypassable** and **cross-vendor**, hosted |
| 131 | +Floe moves enforcement server-side against a real credit line: |
| 132 | + |
| 133 | +- **Un-bypassable** — enforced at the spend rail, not in your process. |
| 134 | +- **Cross-vendor** — one budget over LLM tokens *and* paid (x402) tool calls. |
| 135 | +- **Team budgets + analytics** — shared ceilings, per-agent isolation, spend history. |
| 136 | + |
| 137 | +Set `FLOE_API_KEY` and floe-guard exposes a hook to delegate enforcement to |
| 138 | +hosted Floe (see [`src/floe_guard/hosted.py`](src/floe_guard/hosted.py) — wiring |
| 139 | +the live endpoint is in progress; the local guard is fully functional today). |
| 140 | + |
| 141 | +→ **[dev-dashboard.floelabs.xyz](https://dev-dashboard.floelabs.xyz)** · |
| 142 | +**[floelabs.xyz](https://floelabs.xyz)** |
| 143 | + |
| 144 | +## Development |
| 145 | + |
| 146 | +```bash |
| 147 | +pip install -e ".[dev]" |
| 148 | +pytest |
| 149 | +ruff check . |
| 150 | +``` |
| 151 | + |
| 152 | +## License |
| 153 | + |
| 154 | +MIT — see [LICENSE](LICENSE). |
0 commit comments