floe-guard 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,19 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .venv/
9
+ venv/
10
+
11
+ # Tooling
12
+ .pytest_cache/
13
+ .ruff_cache/
14
+ .mypy_cache/
15
+
16
+ # OS / editor
17
+ .DS_Store
18
+ .vscode/
19
+ .idea/
@@ -0,0 +1,54 @@
1
+ # Contributing to floe-guard
2
+
3
+ `floe-guard` is a local budget guardrail for AI agents: it hard-stops an agent
4
+ before its next LLM call when it would cross a spend ceiling. It runs in-process
5
+ with no account. Hosted Floe is the upgrade path — enforcement moves server-side
6
+ so the ceiling becomes un-bypassable and cross-vendor.
7
+
8
+ Contributions are welcome.
9
+
10
+ ## Development setup
11
+
12
+ ```bash
13
+ git clone https://github.com/Floe-Labs/floe-guard.git
14
+ cd floe-guard
15
+ pip install -e ".[dev]"
16
+ ```
17
+
18
+ Run the checks before opening a PR:
19
+
20
+ ```bash
21
+ pytest # tests
22
+ ruff check . # lint
23
+ ruff format . # format
24
+ ```
25
+
26
+ The optional adapters need their extras to run/import:
27
+
28
+ ```bash
29
+ pip install -e ".[crewai]" # CrewAI adapter
30
+ pip install -e ".[litellm]" # LiteLLM adapter
31
+ ```
32
+
33
+ ## Contribution flow
34
+
35
+ 1. Fork the repo and create a branch off `main` (e.g. `feat/your-change`).
36
+ 2. Make your change with tests, and keep `pytest` + `ruff` green.
37
+ 3. Open a **draft pull request** against `main` and describe what changed and why.
38
+
39
+ ## Code style
40
+
41
+ - Python 3.10+ with type hints.
42
+ - Formatting and linting are handled by `ruff` (config in `pyproject.toml`) — run
43
+ `ruff format .` and `ruff check .` before pushing.
44
+ - Keep the core (`src/floe_guard/`) dependency-free; framework integrations belong
45
+ in `src/floe_guard/integrations/` behind an optional extra.
46
+ - Prefer small, focused changes with tests that describe behavior.
47
+
48
+ ## Areas we'd love help with
49
+
50
+ - **LangChain adapter** (callback-based enforcement).
51
+ - **Vercel AI SDK adapter** (TypeScript middleware / port).
52
+ - **Cost-map coverage** — keeping the bundled pricing map current and broad.
53
+
54
+ Open an issue first if you want to discuss a larger change.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Floe Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,204 @@
1
+ Metadata-Version: 2.4
2
+ Name: floe-guard
3
+ Version: 0.1.0
4
+ Summary: Local budget guardrail for AI agents — hard-stops a runaway loop before its next LLM call crosses a spend ceiling. No account, no network.
5
+ Project-URL: Homepage, https://floelabs.xyz
6
+ Project-URL: Dashboard, https://dev-dashboard.floelabs.xyz
7
+ Project-URL: Source, https://github.com/Floe-Labs/floe-guard
8
+ Author: Floe Labs
9
+ License: MIT License
10
+
11
+ Copyright (c) 2026 Floe Labs
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ License-File: LICENSE
31
+ Keywords: agents,anthropic,budget,cost,crewai,guardrail,litellm,llm,openai
32
+ Classifier: Development Status :: 4 - Beta
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Programming Language :: Python :: 3
36
+ Classifier: Programming Language :: Python :: 3.10
37
+ Classifier: Programming Language :: Python :: 3.11
38
+ Classifier: Programming Language :: Python :: 3.12
39
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
40
+ Requires-Python: >=3.10
41
+ Provides-Extra: crewai
42
+ Requires-Dist: crewai>=0.30; extra == 'crewai'
43
+ Requires-Dist: litellm>=1.0; extra == 'crewai'
44
+ Provides-Extra: dev
45
+ Requires-Dist: pytest>=7.0; extra == 'dev'
46
+ Requires-Dist: ruff>=0.4; extra == 'dev'
47
+ Provides-Extra: litellm
48
+ Requires-Dist: litellm>=1.0; extra == 'litellm'
49
+ Description-Content-Type: text/markdown
50
+
51
+ # floe-guard
52
+
53
+ **A local budget guardrail for AI agents.** It hard-stops your agent *before its
54
+ next LLM call* when it would cross a spend ceiling — so a runaway loop dies at
55
+ $0.10 instead of $4,000. No account, no signup, no network. Runs in your process.
56
+
57
+ ```bash
58
+ pip install floe-guard
59
+ ```
60
+
61
+ ```python
62
+ from floe_guard import BudgetGuard
63
+
64
+ guard = BudgetGuard(limit_usd=5.00) # your ceiling
65
+ guard.check() # before each LLM call — raises if it'd cross
66
+ response = call_your_llm(...) # your existing call
67
+ guard.record("gpt-4o", response.usage.prompt_tokens, response.usage.completion_tokens)
68
+ ```
69
+
70
+ When the next call would cross the ceiling, the guard raises `BudgetExceeded` and
71
+ prints:
72
+
73
+ ```
74
+ BUDGET EXCEEDED — call blocked
75
+ spent so far: $5.001250 | ceiling: $5.000000
76
+ The next call would cross your budget; floe-guard stopped your agent before it ran.
77
+ ```
78
+
79
+ _Animated demo coming — run `python examples/runaway_loop.py` to watch it stop a loop live._
80
+ <!-- TODO: record docs/stop-the-loop.gif and restore the embed: ![stop the loop](docs/stop-the-loop.gif) -->
81
+
82
+ ## See it stop a loop (no API key needed)
83
+
84
+ ```bash
85
+ python examples/runaway_loop.py
86
+ ```
87
+
88
+ This rigs a loop against a **stub LLM** — no real API key, no account, no network.
89
+ It prices each fake `gpt-4o` call offline and the guard halts the loop after a few
90
+ iterations. This is the reproducible "stop the loop" demo.
91
+
92
+ ## How it works
93
+
94
+ The guard sits **in the call path**, not on an event bus. A passive listener is
95
+ told about spend *after the fact* and can't halt anything — so enforcement has to
96
+ be the thing standing in front of the next call:
97
+
98
+ - **`check()`** runs before each LLM call. It predicts the next call's cost from
99
+ the last one and raises `BudgetExceeded` if that would cross your ceiling — the
100
+ call never runs. (A running-total check also catches an overshoot if an estimate
101
+ came in low.)
102
+ - **`record(model, prompt_tokens, completion_tokens)`** runs after each response.
103
+ It prices the tokens **offline** from a bundled
104
+ [LiteLLM cost map](src/floe_guard/cost_map.json) and adds the USD to a running
105
+ total.
106
+
107
+ ### Unpriceable models fail closed
108
+
109
+ If a model isn't in the cost map and you didn't supply a price, the guard **warns
110
+ loudly and refuses** (`UnpriceableModelError`) rather than silently treat it as
111
+ free — *you can't cap spend you can't measure.* Give it a price to enforce it:
112
+
113
+ ```python
114
+ from floe_guard import BudgetGuard, ManualPrice
115
+
116
+ guard = BudgetGuard(
117
+ limit_usd=5.00,
118
+ price_overrides={"my-self-hosted-model": ManualPrice(1e-6, 2e-6)}, # USD/token
119
+ )
120
+ # or, set fail_closed=False to warn-and-skip for models you accept un-metered.
121
+ ```
122
+
123
+ ## Framework adapters (optional extras)
124
+
125
+ ### CrewAI
126
+
127
+ ```bash
128
+ pip install floe-guard[crewai]
129
+ ```
130
+
131
+ ```python
132
+ from crewai import Crew
133
+ from floe_guard import BudgetGuard
134
+ from floe_guard.integrations.crewai import guard_crew
135
+
136
+ guard = BudgetGuard(limit_usd=1.00)
137
+ guard_crew(guard) # one line — enforces across the whole crew
138
+ Crew(agents=[...], tasks=[...]).kickoff()
139
+ ```
140
+
141
+ CrewAI runs on LiteLLM, so one callback caps every agent and task under a single
142
+ budget.
143
+
144
+ ### LiteLLM
145
+
146
+ ```bash
147
+ pip install floe-guard[litellm]
148
+ ```
149
+
150
+ ```python
151
+ from floe_guard import BudgetGuard
152
+ from floe_guard.integrations.litellm import guarded_completion
153
+
154
+ guard = BudgetGuard(limit_usd=1.00)
155
+ response = guarded_completion(guard, model="gpt-4o", messages=[...])
156
+ ```
157
+
158
+ Prefer the LiteLLM-native callback? Register `budget_guard_callback(guard)` on
159
+ `litellm.callbacks`.
160
+
161
+ ### Coming next
162
+
163
+ LangChain (callback) and the Vercel AI SDK (TypeScript middleware) are next. Open
164
+ an issue if you want one sooner.
165
+
166
+ ## Honest about what this is
167
+
168
+ floe-guard is a **local, estimate-based** guardrail. It prices tokens from a
169
+ vendored cost map *inside your process*:
170
+
171
+ - The cost map can drift as vendors change prices — refresh it like any snapshot.
172
+ - It only sees the vendors you instrument.
173
+ - A determined agent or a bug could route around an in-process check.
174
+
175
+ It's genuinely useful on its own, and it's honest about its limits. No inflated
176
+ metrics, no "zero defaults" claims — it's a free local stop, not a vault.
177
+
178
+ ## Upgrade to hosted Floe
179
+
180
+ When you need the ceiling to be **un-bypassable** and **cross-vendor**, hosted
181
+ Floe moves enforcement server-side against a real credit line:
182
+
183
+ - **Un-bypassable** — enforced at the spend rail, not in your process.
184
+ - **Cross-vendor** — one budget over LLM tokens *and* paid (x402) tool calls.
185
+ - **Team budgets + analytics** — shared ceilings, per-agent isolation, spend history.
186
+
187
+ Set `FLOE_API_KEY` and floe-guard exposes a hook to delegate enforcement to
188
+ hosted Floe (see [`src/floe_guard/hosted.py`](src/floe_guard/hosted.py) — wiring
189
+ the live endpoint is in progress; the local guard is fully functional today).
190
+
191
+ → **[dev-dashboard.floelabs.xyz](https://dev-dashboard.floelabs.xyz)** ·
192
+ **[floelabs.xyz](https://floelabs.xyz)**
193
+
194
+ ## Development
195
+
196
+ ```bash
197
+ pip install -e ".[dev]"
198
+ pytest
199
+ ruff check .
200
+ ```
201
+
202
+ ## License
203
+
204
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,154 @@
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: ![stop the loop](docs/stop-the-loop.gif) -->
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,0 +1,27 @@
1
+ # Security Policy
2
+
3
+ ## Supported versions
4
+
5
+ Only the latest released version of `floe-guard` is supported with security fixes.
6
+ Please upgrade to the latest version before reporting an issue.
7
+
8
+ ## Reporting a vulnerability
9
+
10
+ **Do not open a public GitHub issue for security vulnerabilities.**
11
+
12
+ Report them privately by email to **security@floelabs.xyz**. If you do not get a
13
+ response, use **hello@floelabs.xyz** as a fallback contact.
14
+
15
+ Please include:
16
+
17
+ - a description of the vulnerability and its impact,
18
+ - steps to reproduce (a minimal proof of concept helps),
19
+ - the affected version.
20
+
21
+ ## What to expect
22
+
23
+ - We aim to acknowledge your report within **3 business days**.
24
+ - We will keep you updated on our assessment and the fix.
25
+ - Please give us a reasonable window to release a fix before any public disclosure.
26
+
27
+ Thank you for helping keep `floe-guard` and its users safe.
@@ -0,0 +1,6 @@
1
+ TODO: record this GIF.
2
+
3
+ This is a placeholder for docs/stop-the-loop.gif — a short screen recording of
4
+ `python examples/runaway_loop.py` running and the guard printing
5
+ `BUDGET EXCEEDED — call blocked`. Replace this file with the recorded .gif
6
+ (keep the filename so the README reference resolves).
@@ -0,0 +1,58 @@
1
+ """Stop-the-loop demo — runs with NO API key and NO account.
2
+
3
+ A naive agent loop that calls an LLM forever. The only thing standing between you
4
+ and a five-figure overnight bill is ``floe-guard``: it hard-stops the loop before
5
+ the call that would cross your $0.10 ceiling.
6
+
7
+ Run it::
8
+
9
+ python examples/runaway_loop.py
10
+
11
+ The "LLM" here is a stub that returns fixed token usage — no network, no key, no
12
+ crewai/litellm needed. The cost is computed offline from the bundled cost map,
13
+ exactly as it would be for a real ``gpt-4o`` call.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from floe_guard import BudgetExceeded, BudgetGuard
19
+
20
+ MODEL = "gpt-4o"
21
+
22
+
23
+ def stub_llm(prompt: str) -> dict[str, object]:
24
+ """A fake LLM call. No network, no API key — returns fixed token usage."""
25
+ return {
26
+ "model": MODEL,
27
+ "text": "...thinking... let me call myself again...",
28
+ "prompt_tokens": 1_000,
29
+ "completion_tokens": 1_000,
30
+ }
31
+
32
+
33
+ def main() -> None:
34
+ # $0.10 ceiling. gpt-4o at 1k in + 1k out ≈ $0.0125/call, so the guard should
35
+ # stop the loop after a handful of iterations — well before any real damage.
36
+ guard = BudgetGuard(limit_usd=0.10)
37
+
38
+ print(f"Starting a runaway loop with a ${guard.limit_usd:.2f} budget...\n")
39
+ call = 0
40
+ while True: # a real runaway loop never decides to stop on its own
41
+ call += 1
42
+ try:
43
+ guard.check() # <-- the kill-switch: raises before the crossing call
44
+ except BudgetExceeded:
45
+ print(f"\nLoop stopped at call #{call}. The agent never got to spend past the budget.")
46
+ break
47
+
48
+ response = stub_llm("keep going")
49
+ cost = guard.record(
50
+ str(response["model"]),
51
+ int(response["prompt_tokens"]), # type: ignore[arg-type]
52
+ int(response["completion_tokens"]), # type: ignore[arg-type]
53
+ )
54
+ print(f" call #{call}: +${cost:.4f} (running total ${guard.spent_usd:.4f})")
55
+
56
+
57
+ if __name__ == "__main__":
58
+ main()
@@ -0,0 +1,52 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "floe-guard"
7
+ version = "0.1.0"
8
+ description = "Local budget guardrail for AI agents — hard-stops a runaway loop before its next LLM call crosses a spend ceiling. No account, no network."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { file = "LICENSE" }
12
+ authors = [{ name = "Floe Labs" }]
13
+ keywords = ["llm", "agents", "budget", "guardrail", "crewai", "litellm", "cost", "openai", "anthropic"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: Software Development :: Libraries :: Python Modules",
23
+ ]
24
+ dependencies = []
25
+
26
+ [project.optional-dependencies]
27
+ litellm = ["litellm>=1.0"]
28
+ crewai = ["crewai>=0.30", "litellm>=1.0"]
29
+ dev = ["pytest>=7.0", "ruff>=0.4"]
30
+
31
+ [project.urls]
32
+ Homepage = "https://floelabs.xyz"
33
+ Dashboard = "https://dev-dashboard.floelabs.xyz"
34
+ Source = "https://github.com/Floe-Labs/floe-guard"
35
+
36
+ [tool.hatch.build.targets.wheel]
37
+ packages = ["src/floe_guard"]
38
+
39
+ [tool.hatch.build.targets.wheel.force-include]
40
+ "src/floe_guard/cost_map.json" = "floe_guard/cost_map.json"
41
+
42
+ [tool.ruff]
43
+ line-length = 100
44
+ target-version = "py310"
45
+ src = ["src", "tests", "examples"]
46
+
47
+ [tool.ruff.lint]
48
+ select = ["E", "F", "I", "UP", "B", "W"]
49
+
50
+ [tool.pytest.ini_options]
51
+ testpaths = ["tests"]
52
+ filterwarnings = ["error::floe_guard.errors.UnpriceableModelWarning"]
@@ -0,0 +1,37 @@
1
+ """floe-guard — a local, framework-agnostic budget guardrail for AI agents.
2
+
3
+ Hard-stops an agent before its next LLM call when it would cross a spend ceiling.
4
+ Zero account, no network, runs in-process. Hosted Floe is the un-bypassable,
5
+ cross-vendor upgrade path (see the README).
6
+
7
+ from floe_guard import BudgetGuard
8
+
9
+ guard = BudgetGuard(limit_usd=5.00)
10
+ guard.check() # before each LLM call (may raise)
11
+ guard.record("gpt-4o", 1200, 350) # after each response
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from .errors import (
17
+ BudgetExceeded,
18
+ FloeGuardError,
19
+ UnpriceableModelError,
20
+ UnpriceableModelWarning,
21
+ )
22
+ from .guard import BudgetGuard
23
+ from .pricing import ManualPrice, PricedModel, price_tokens, resolve_price
24
+
25
+ __version__ = "0.1.0"
26
+
27
+ __all__ = [
28
+ "BudgetGuard",
29
+ "BudgetExceeded",
30
+ "FloeGuardError",
31
+ "UnpriceableModelError",
32
+ "UnpriceableModelWarning",
33
+ "ManualPrice",
34
+ "PricedModel",
35
+ "price_tokens",
36
+ "resolve_price",
37
+ ]