tokenbudget 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.
- tokenbudget-0.1.0/.gitignore +59 -0
- tokenbudget-0.1.0/LICENSE +21 -0
- tokenbudget-0.1.0/PKG-INFO +356 -0
- tokenbudget-0.1.0/README.md +309 -0
- tokenbudget-0.1.0/pyproject.toml +120 -0
- tokenbudget-0.1.0/src/tokenbudget/__init__.py +47 -0
- tokenbudget-0.1.0/src/tokenbudget/budget.py +171 -0
- tokenbudget-0.1.0/src/tokenbudget/cache.py +153 -0
- tokenbudget-0.1.0/src/tokenbudget/exceptions.py +57 -0
- tokenbudget-0.1.0/src/tokenbudget/pricing.py +144 -0
- tokenbudget-0.1.0/src/tokenbudget/providers/__init__.py +5 -0
- tokenbudget-0.1.0/src/tokenbudget/providers/anthropic.py +142 -0
- tokenbudget-0.1.0/src/tokenbudget/providers/base.py +45 -0
- tokenbudget-0.1.0/src/tokenbudget/providers/custom.py +73 -0
- tokenbudget-0.1.0/src/tokenbudget/providers/openai.py +168 -0
- tokenbudget-0.1.0/src/tokenbudget/reports.py +126 -0
- tokenbudget-0.1.0/src/tokenbudget/tracker.py +222 -0
- tokenbudget-0.1.0/src/tokenbudget/utils.py +49 -0
- tokenbudget-0.1.0/tests/__init__.py +1 -0
- tokenbudget-0.1.0/tests/test_budget.py +102 -0
- tokenbudget-0.1.0/tests/test_cache.py +75 -0
- tokenbudget-0.1.0/tests/test_pricing.py +81 -0
- tokenbudget-0.1.0/tests/test_providers.py +107 -0
- tokenbudget-0.1.0/tests/test_reports.py +103 -0
- tokenbudget-0.1.0/tests/test_tracker.py +105 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
|
|
23
|
+
# Virtual environments
|
|
24
|
+
venv/
|
|
25
|
+
env/
|
|
26
|
+
ENV/
|
|
27
|
+
|
|
28
|
+
# Testing
|
|
29
|
+
.pytest_cache/
|
|
30
|
+
.coverage
|
|
31
|
+
htmlcov/
|
|
32
|
+
coverage.xml
|
|
33
|
+
*.cover
|
|
34
|
+
|
|
35
|
+
# IDEs
|
|
36
|
+
.vscode/
|
|
37
|
+
.idea/
|
|
38
|
+
*.swp
|
|
39
|
+
*.swo
|
|
40
|
+
*~
|
|
41
|
+
|
|
42
|
+
# OS
|
|
43
|
+
.DS_Store
|
|
44
|
+
Thumbs.db
|
|
45
|
+
|
|
46
|
+
# Cache
|
|
47
|
+
tokenbudget_cache/
|
|
48
|
+
*.pkl
|
|
49
|
+
|
|
50
|
+
# Reports
|
|
51
|
+
usage_report.csv
|
|
52
|
+
usage_report.json
|
|
53
|
+
usage.csv
|
|
54
|
+
usage.json
|
|
55
|
+
|
|
56
|
+
# Mypy
|
|
57
|
+
.mypy_cache/
|
|
58
|
+
.dmypy.json
|
|
59
|
+
dmypy.json
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TokenBudget Contributors
|
|
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,356 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tokenbudget
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lightweight token tracking, cost management, and budget enforcement for LLM API calls
|
|
5
|
+
Project-URL: Homepage, https://github.com/yourusername/tokenbudget
|
|
6
|
+
Project-URL: Documentation, https://github.com/yourusername/tokenbudget#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/yourusername/tokenbudget
|
|
8
|
+
Project-URL: Issues, https://github.com/yourusername/tokenbudget/issues
|
|
9
|
+
Author: TokenBudget Contributors
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ai,anthropic,budget,claude,cost-tracking,gpt,llm,openai,tokens
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Requires-Dist: pydantic>=2.0.0
|
|
26
|
+
Requires-Dist: typing-extensions>=4.5.0; python_version < '3.11'
|
|
27
|
+
Provides-Extra: all
|
|
28
|
+
Requires-Dist: anthropic>=0.18.0; extra == 'all'
|
|
29
|
+
Requires-Dist: google-generativeai>=0.3.0; extra == 'all'
|
|
30
|
+
Requires-Dist: openai>=1.0.0; extra == 'all'
|
|
31
|
+
Requires-Dist: tiktoken>=0.5.0; extra == 'all'
|
|
32
|
+
Provides-Extra: anthropic
|
|
33
|
+
Requires-Dist: anthropic>=0.18.0; extra == 'anthropic'
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: black>=23.0.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
41
|
+
Provides-Extra: google
|
|
42
|
+
Requires-Dist: google-generativeai>=0.3.0; extra == 'google'
|
|
43
|
+
Provides-Extra: openai
|
|
44
|
+
Requires-Dist: openai>=1.0.0; extra == 'openai'
|
|
45
|
+
Requires-Dist: tiktoken>=0.5.0; extra == 'openai'
|
|
46
|
+
Description-Content-Type: text/markdown
|
|
47
|
+
|
|
48
|
+
# TokenBudget
|
|
49
|
+
|
|
50
|
+
**Stop bleeding money on LLM API calls.**
|
|
51
|
+
|
|
52
|
+
A lightweight Python library for tracking tokens, managing costs, and enforcing budgets across all major LLM providers.
|
|
53
|
+
|
|
54
|
+
[](https://www.python.org/downloads/)
|
|
55
|
+
[](https://opensource.org/licenses/MIT)
|
|
56
|
+
[](https://badge.fury.io/py/tokenbudget)
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## The Problem
|
|
61
|
+
|
|
62
|
+
You're building with LLMs and:
|
|
63
|
+
- Costs spiral out of control with no visibility
|
|
64
|
+
- No idea which API calls are eating your budget
|
|
65
|
+
- Production bills that make you cry
|
|
66
|
+
- Clunky observability platforms that require external services
|
|
67
|
+
|
|
68
|
+
**There's no simple `pip install` library that just works.**
|
|
69
|
+
|
|
70
|
+
## The Solution
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from tokenbudget import TokenTracker, budget
|
|
74
|
+
|
|
75
|
+
tracker = TokenTracker()
|
|
76
|
+
client = tracker.wrap_openai(openai.OpenAI())
|
|
77
|
+
|
|
78
|
+
# Every call is tracked automatically
|
|
79
|
+
response = client.chat.completions.create(
|
|
80
|
+
model="gpt-4o",
|
|
81
|
+
messages=[{"role": "user", "content": "Hello"}]
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
print(tracker.usage)
|
|
85
|
+
# Usage(total_tokens=25, total_cost_usd=0.000375, calls=1)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
That's it. No platforms, no external services, no configuration.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Features
|
|
93
|
+
|
|
94
|
+
**Token Tracking** - Automatic tracking for OpenAI, Anthropic, Google
|
|
95
|
+
**Cost Calculation** - Built-in pricing database (always up-to-date)
|
|
96
|
+
**Budget Enforcement** - Decorators to prevent overspending
|
|
97
|
+
**Response Caching** - Save money with zero-cost cached responses
|
|
98
|
+
**Usage Reports** - Beautiful tables + CSV/JSON exports
|
|
99
|
+
**Multi-Provider** - One tracker for all your LLM calls
|
|
100
|
+
**Thread-Safe** - Works seamlessly in concurrent applications
|
|
101
|
+
**Async Support** - Works with async clients out of the box
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Installation
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Basic installation
|
|
109
|
+
pip install tokenbudget
|
|
110
|
+
|
|
111
|
+
# With OpenAI support
|
|
112
|
+
pip install tokenbudget[openai]
|
|
113
|
+
|
|
114
|
+
# With Anthropic support
|
|
115
|
+
pip install tokenbudget[anthropic]
|
|
116
|
+
|
|
117
|
+
# With everything
|
|
118
|
+
pip install tokenbudget[all]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Quick Examples
|
|
124
|
+
|
|
125
|
+
### 1. Basic Tracking
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from tokenbudget import TokenTracker
|
|
129
|
+
import openai
|
|
130
|
+
|
|
131
|
+
tracker = TokenTracker()
|
|
132
|
+
client = tracker.wrap_openai(openai.OpenAI())
|
|
133
|
+
|
|
134
|
+
response = client.chat.completions.create(
|
|
135
|
+
model="gpt-4o",
|
|
136
|
+
messages=[{"role": "user", "content": "What is 2+2?"}]
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
print(f"Tokens: {tracker.usage.total_tokens}")
|
|
140
|
+
print(f"Cost: ${tracker.usage.total_cost_usd:.6f}")
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 2. Budget Enforcement
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
from tokenbudget import budget, BudgetExceeded
|
|
147
|
+
|
|
148
|
+
@budget(max_cost_usd=1.00, max_tokens=50000)
|
|
149
|
+
def my_llm_pipeline(data):
|
|
150
|
+
# All LLM calls inside are tracked
|
|
151
|
+
# Raises BudgetExceeded if limit is hit
|
|
152
|
+
result = process_with_llm(data)
|
|
153
|
+
return result
|
|
154
|
+
|
|
155
|
+
# Or as context manager
|
|
156
|
+
with budget(max_cost_usd=0.50) as ctx:
|
|
157
|
+
response = client.chat.completions.create(...)
|
|
158
|
+
print(f"Remaining: ${ctx.remaining_budget:.4f}")
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 3. Multi-Provider Tracking
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
import openai
|
|
165
|
+
import anthropic
|
|
166
|
+
from tokenbudget import TokenTracker
|
|
167
|
+
|
|
168
|
+
tracker = TokenTracker()
|
|
169
|
+
|
|
170
|
+
# Track OpenAI
|
|
171
|
+
openai_client = tracker.wrap_openai(openai.OpenAI())
|
|
172
|
+
openai_client.chat.completions.create(model="gpt-4o", ...)
|
|
173
|
+
|
|
174
|
+
# Track Anthropic
|
|
175
|
+
anthropic_client = tracker.wrap_anthropic(anthropic.Anthropic())
|
|
176
|
+
anthropic_client.messages.create(model="claude-sonnet-4-5", ...)
|
|
177
|
+
|
|
178
|
+
# Combined reporting
|
|
179
|
+
print(f"Total cost: ${tracker.total_cost_usd:.4f}")
|
|
180
|
+
print(tracker.usage_by_provider)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 4. Response Caching
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
tracker = TokenTracker(cache="memory")
|
|
187
|
+
client = tracker.wrap_openai(openai.OpenAI())
|
|
188
|
+
|
|
189
|
+
# First call - costs money
|
|
190
|
+
response1 = client.chat.completions.create(model="gpt-4o", ...)
|
|
191
|
+
|
|
192
|
+
# Identical call - FREE (cached)
|
|
193
|
+
response2 = client.chat.completions.create(model="gpt-4o", ...)
|
|
194
|
+
|
|
195
|
+
stats = tracker.cache_stats
|
|
196
|
+
print(f"Saved: ${stats.saved_cost_usd:.4f}")
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### 5. Usage Reports
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from tokenbudget import generate_table_report
|
|
203
|
+
|
|
204
|
+
print(generate_table_report(tracker))
|
|
205
|
+
|
|
206
|
+
# Output:
|
|
207
|
+
# ┌─────────────────────────────────────┐
|
|
208
|
+
# │ TokenBudget Usage Report │
|
|
209
|
+
# ├─────────────────────────────────────┤
|
|
210
|
+
# │ Provider │ Calls │ Tokens │ Cost │
|
|
211
|
+
# │ openai │ 15 │ 12.3k │ $0.24 │
|
|
212
|
+
# │ anthropic │ 8 │ 8.1k │ $0.18 │
|
|
213
|
+
# ├─────────────────────────────────────┤
|
|
214
|
+
# │ Total │ 23 │ 20.4k │ $0.42 │
|
|
215
|
+
# └─────────────────────────────────────┘
|
|
216
|
+
|
|
217
|
+
# Export to CSV/JSON
|
|
218
|
+
tracker.export_csv("usage.csv")
|
|
219
|
+
tracker.export_json("usage.json")
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Supported Models
|
|
225
|
+
|
|
226
|
+
**OpenAI**
|
|
227
|
+
`gpt-4o`, `gpt-4o-mini`, `gpt-4-turbo`, `gpt-4`, `gpt-3.5-turbo`, `o1`, `o1-mini`, `o3-mini`
|
|
228
|
+
|
|
229
|
+
**Anthropic**
|
|
230
|
+
`claude-opus-4-5`, `claude-sonnet-4-5`, `claude-haiku-4-5`, `claude-3-5-sonnet`, `claude-3-opus`
|
|
231
|
+
|
|
232
|
+
**Google**
|
|
233
|
+
`gemini-2.0-flash`, `gemini-2.0-pro`, `gemini-1.5-pro`, `gemini-1.5-flash`
|
|
234
|
+
|
|
235
|
+
Need a custom model? Easy:
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
from tokenbudget import register_model
|
|
239
|
+
|
|
240
|
+
register_model(
|
|
241
|
+
"my-custom-model",
|
|
242
|
+
input_per_1k=0.001,
|
|
243
|
+
output_per_1k=0.002,
|
|
244
|
+
provider="custom"
|
|
245
|
+
)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## API Reference
|
|
251
|
+
|
|
252
|
+
### TokenTracker
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
tracker = TokenTracker(cache=None) # cache: "memory", "disk", or None
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Methods:**
|
|
259
|
+
- `wrap_openai(client)` - Wrap OpenAI client
|
|
260
|
+
- `wrap_anthropic(client)` - Wrap Anthropic client
|
|
261
|
+
- `track(model, prompt_tokens, completion_tokens, provider)` - Manual tracking
|
|
262
|
+
- `reset()` - Reset all statistics
|
|
263
|
+
|
|
264
|
+
**Properties:**
|
|
265
|
+
- `usage` - Overall usage stats
|
|
266
|
+
- `usage_by_provider` - Per-provider breakdown
|
|
267
|
+
- `total_cost_usd` - Total cost across all calls
|
|
268
|
+
- `cache_stats` - Cache hit/miss statistics
|
|
269
|
+
|
|
270
|
+
### Budget Enforcement
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
@budget(max_cost_usd=None, max_tokens=None, tracker=None)
|
|
274
|
+
def my_function():
|
|
275
|
+
...
|
|
276
|
+
|
|
277
|
+
# Or as context manager
|
|
278
|
+
with budget(max_cost_usd=1.0) as ctx:
|
|
279
|
+
...
|
|
280
|
+
print(ctx.remaining_budget)
|
|
281
|
+
print(ctx.current_usage)
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Exceptions:**
|
|
285
|
+
- `BudgetExceeded` - Cost limit exceeded
|
|
286
|
+
- `TokenLimitReached` - Token limit exceeded
|
|
287
|
+
|
|
288
|
+
### Pricing
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
from tokenbudget import get_price, register_model, calculate_cost
|
|
292
|
+
|
|
293
|
+
# Get model pricing
|
|
294
|
+
price = get_price("gpt-4o")
|
|
295
|
+
print(price.input_per_1k, price.output_per_1k)
|
|
296
|
+
|
|
297
|
+
# Calculate cost
|
|
298
|
+
cost = calculate_cost("gpt-4o", input_tokens=1000, output_tokens=500)
|
|
299
|
+
|
|
300
|
+
# Register custom model
|
|
301
|
+
register_model("my-model", input_per_1k=0.001, output_per_1k=0.002)
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Reports
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
from tokenbudget import generate_table_report, export_csv, export_json
|
|
308
|
+
|
|
309
|
+
# Pretty table
|
|
310
|
+
print(generate_table_report(tracker))
|
|
311
|
+
|
|
312
|
+
# Export
|
|
313
|
+
export_csv(tracker, "usage.csv")
|
|
314
|
+
export_json(tracker, "usage.json")
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## Custom Providers
|
|
320
|
+
|
|
321
|
+
Support any LLM provider:
|
|
322
|
+
|
|
323
|
+
```python
|
|
324
|
+
from tokenbudget.providers.custom import CustomProvider
|
|
325
|
+
|
|
326
|
+
custom = CustomProvider(
|
|
327
|
+
tracker=tracker,
|
|
328
|
+
provider_name="my-llm-service",
|
|
329
|
+
extract_model=lambda r: r["model"],
|
|
330
|
+
extract_prompt_tokens=lambda r: r["usage"]["input"],
|
|
331
|
+
extract_completion_tokens=lambda r: r["usage"]["output"],
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
# Track your custom response
|
|
335
|
+
custom.track(api_response)
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Contributing
|
|
341
|
+
|
|
342
|
+
Contributions welcome! Please feel free to submit a Pull Request.
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## License
|
|
347
|
+
|
|
348
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Author
|
|
353
|
+
|
|
354
|
+
Built by a developer tired of surprise LLM bills.
|
|
355
|
+
|
|
356
|
+
If this saved you money, consider starring the repo.
|