tokenspy 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,27 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment: release
11
+ permissions:
12
+ id-token: write # required for Trusted Publishing (OIDC)
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: "3.11"
20
+
21
+ - name: Build package
22
+ run: |
23
+ pip install hatchling build
24
+ python -m build
25
+
26
+ - name: Publish to PyPI
27
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,46 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install dev dependencies
25
+ run: pip install -e ".[dev]"
26
+
27
+ - name: Lint with ruff
28
+ run: ruff check llmspy/ tests/
29
+
30
+ - name: Run tests
31
+ run: pytest tests/ -v --tb=short
32
+
33
+ # Validate the package builds cleanly
34
+ build:
35
+ runs-on: ubuntu-latest
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+
39
+ - uses: actions/setup-python@v5
40
+ with:
41
+ python-version: "3.11"
42
+
43
+ - name: Build package
44
+ run: |
45
+ pip install hatchling build
46
+ python -m build
@@ -0,0 +1,28 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+
9
+ # Virtual environments
10
+ .venv/
11
+ venv/
12
+ env/
13
+
14
+ # Testing
15
+ .pytest_cache/
16
+ .coverage
17
+ htmlcov/
18
+
19
+ # llmspy data
20
+ .llmspy/
21
+ llmspy_report.html
22
+
23
+ # IDEs
24
+ .vscode/
25
+ .idea/
26
+
27
+ # macOS
28
+ .DS_Store
tokenspy-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pinaki Mishra
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,340 @@
1
+ Metadata-Version: 2.4
2
+ Name: tokenspy
3
+ Version: 0.1.0
4
+ Summary: cProfile for LLMs — find which function is burning your AI budget. Flame graph output, zero-config, no proxy.
5
+ Project-URL: Homepage, https://github.com/pinakimishra95/llm-cost-profiler
6
+ Project-URL: Documentation, https://github.com/pinakimishra95/llm-cost-profiler#readme
7
+ Project-URL: Issues, https://github.com/pinakimishra95/llm-cost-profiler/issues
8
+ Author-email: Pinaki Mishra <pinakimishra95@hotmail.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: agents,ai,anthropic,cost,flamegraph,llm,openai,optimization,profiler,token
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.10
22
+ Provides-Extra: all
23
+ Requires-Dist: anthropic>=0.30.0; extra == 'all'
24
+ Requires-Dist: openai>=1.30.0; extra == 'all'
25
+ Provides-Extra: anthropic
26
+ Requires-Dist: anthropic>=0.30.0; extra == 'anthropic'
27
+ Provides-Extra: dev
28
+ Requires-Dist: mypy>=1.10.0; extra == 'dev'
29
+ Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
30
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
31
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
32
+ Provides-Extra: google
33
+ Requires-Dist: google-generativeai>=0.7.0; extra == 'google'
34
+ Provides-Extra: openai
35
+ Requires-Dist: openai>=1.30.0; extra == 'openai'
36
+ Description-Content-Type: text/markdown
37
+
38
+ # llmspy 🔥
39
+
40
+ <div align="center">
41
+
42
+ **You're spending $800/month on LLMs. Which function is burning it?**
43
+
44
+ *Find out in one line. No proxy. No signup. No traffic rerouting.*
45
+
46
+ [![PyPI version](https://badge.fury.io/py/tokenspy.svg)](https://badge.fury.io/py/tokenspy)
47
+ [![Tests](https://github.com/pinakimishra95/llm-cost-profiler/actions/workflows/tests.yml/badge.svg)](https://github.com/pinakimishra95/llm-cost-profiler/actions)
48
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
49
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
50
+ [![Zero dependencies](https://img.shields.io/badge/dependencies-zero-brightgreen.svg)](https://pypi.org/project/tokenspy/)
51
+
52
+ ```bash
53
+ pip install tokenspy
54
+ ```
55
+
56
+ </div>
57
+
58
+ ---
59
+
60
+ ## The Problem
61
+
62
+ You get an OpenAI invoice. It says **$800 this month**. You have no idea which function caused it.
63
+
64
+ ```python
65
+ def run_pipeline(query):
66
+ docs = fetch_and_summarize(query) # ← costs $600?
67
+ entities = extract_entities(docs) # ← or this one?
68
+ return generate_report(entities) # ← or this one?
69
+ ```
70
+
71
+ Langfuse and Helicone force you to reroute traffic through their proxy. Sign up. Configure. Break your local setup.
72
+
73
+ **llmspy takes 1 line. No proxy. No signup. Runs entirely on your machine.**
74
+
75
+ ---
76
+
77
+ ## The Fix
78
+
79
+ ```python
80
+ import llmspy
81
+
82
+ @llmspy.profile
83
+ def run_pipeline(query):
84
+ docs = fetch_and_summarize(query)
85
+ entities = extract_entities(docs)
86
+ return generate_report(entities)
87
+
88
+ run_pipeline("Analyze Q3 earnings")
89
+ llmspy.report()
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Output
95
+
96
+ ```
97
+ ╔══════════════════════════════════════════════════════════════════════╗
98
+ ║ llmspy cost report ║
99
+ ║ total: $0.0523 · 18,734 tokens · 3 calls ║
100
+ ╠══════════════════════════════════════════════════════════════════════╣
101
+ ║ ║
102
+ ║ fetch_and_summarize $0.038 ████████████░░░░ 73% ║
103
+ ║ └─ gpt-4o $0.038 ████████████░░░░ 73% ║
104
+ ║ └─ 12,000 tokens ║
105
+ ║ ║
106
+ ║ generate_report $0.011 ████░░░░░░░░░░░░ 21% ║
107
+ ║ └─ gpt-4o $0.011 ████░░░░░░░░░░░░ 21% ║
108
+ ║ └─ 3,600 tokens ║
109
+ ║ ║
110
+ ║ extract_entities $0.003 █░░░░░░░░░░░░░░░ 6% ║
111
+ ║ └─ gpt-4o-mini $0.003 █░░░░░░░░░░░░░░░ 6% ║
112
+ ║ └─ 3,134 tokens ║
113
+ ║ ║
114
+ ╠══════════════════════════════════════════════════════════════════════╣
115
+ ║ Optimization hints ║
116
+ ║ ║
117
+ ║ 🔴 fetch_and_summarize [gpt-4o] ║
118
+ ║ Switch to gpt-4o-mini — 94% cheaper (~$540/month savings) ║
119
+ ║ ║
120
+ ║ 🟡 fetch_and_summarize [gpt-4o] ║
121
+ ║ Avg input: 12,000 tokens. Trim context or limit retrieval. ║
122
+ ╚══════════════════════════════════════════════════════════════════════╝
123
+ ```
124
+
125
+ **Now you know: `fetch_and_summarize` is burning 73% of your budget. Fix that one function, cut your bill by $540/month.**
126
+
127
+ ---
128
+
129
+ ## Quick Start
130
+
131
+ ### Decorator (most common)
132
+
133
+ ```python
134
+ import llmspy
135
+
136
+ @llmspy.profile
137
+ def summarize_docs(docs: list[str]) -> str:
138
+ return openai_client.chat.completions.create(
139
+ model="gpt-4o",
140
+ messages=[{"role": "user", "content": "\n".join(docs)}]
141
+ ).choices[0].message.content
142
+
143
+ summarize_docs(my_docs)
144
+ llmspy.report() # prints flame graph to terminal
145
+ llmspy.report("html") # writes llmspy_report.html, opens in browser
146
+ ```
147
+
148
+ ### Context Manager
149
+
150
+ ```python
151
+ with llmspy.session("research_task") as s:
152
+ response = anthropic_client.messages.create(
153
+ model="claude-haiku-4-5",
154
+ messages=[{"role": "user", "content": query}]
155
+ )
156
+
157
+ print(f"Cost: {s.cost_str}") # "$0.0012"
158
+ print(f"Tokens: {s.tokens}") # 3,240
159
+ print(f"Calls: {s.calls}") # 1
160
+ ```
161
+
162
+ ### Programmatic Access
163
+
164
+ ```python
165
+ data = llmspy.stats()
166
+ # {
167
+ # "total_cost_usd": 0.042,
168
+ # "total_tokens": 15000,
169
+ # "total_calls": 3,
170
+ # "by_function": {"summarize_docs": 0.038, "generate_report": 0.004},
171
+ # "by_model": {"gpt-4o": 0.040, "gpt-4o-mini": 0.002},
172
+ # "calls": [...],
173
+ # }
174
+ ```
175
+
176
+ ### Persistent Tracking Across Sessions
177
+
178
+ ```python
179
+ # In your app startup:
180
+ llmspy.init(persist=True) # saves to ~/.llmspy/usage.db
181
+
182
+ # Decorate as normal — costs accumulate across restarts
183
+ @llmspy.profile
184
+ def my_agent(query):
185
+ ...
186
+ ```
187
+
188
+ ---
189
+
190
+ ## How It Works
191
+
192
+ llmspy monkey-patches the SDK client **in-process** — the same technique used by `py-spy` and `line_profiler`:
193
+
194
+ ```
195
+ Your Code
196
+
197
+ ├── @llmspy.profile ────────────────────────────── sets active function
198
+
199
+ └── openai_client.chat.completions.create(...)
200
+
201
+ └── llmspy interceptor (in-process monkey-patch)
202
+ ├── calls original SDK method
203
+ ├── reads response.usage (tokens)
204
+ ├── looks up cost in built-in pricing table
205
+ ├── records: function · model · tokens · cost · duration
206
+ └── returns response UNCHANGED to your code
207
+
208
+ llmspy.report() → renders flame graph from recorded data
209
+ ```
210
+
211
+ **No proxy server. No HTTP interception. No environment variables. No configuration.**
212
+
213
+ Your code runs exactly as before. llmspy just watches and keeps score.
214
+
215
+ ---
216
+
217
+ ## HTML Flame Graph
218
+
219
+ ```python
220
+ llmspy.report(format="html")
221
+ ```
222
+
223
+ Opens a self-contained HTML file in your browser — zero JS dependencies, pure SVG:
224
+
225
+ ```
226
+ ┌─────────────────────────────────────────────────────────────────┐
227
+ │ llmspy — Total: $0.0523 (18,734 tokens) │
228
+ ├─────────────────────────────────────────────────────────────────┤
229
+ │ │
230
+ │ fetch_and_summarize ████████████████████████████████ 73% │
231
+ │ generate_report ████████████ 21% │
232
+ │ extract_entities ████ 6% │
233
+ │ │
234
+ │ ┌──────────────────────────────────────────────────────────┐ │
235
+ │ │ Model │ Cost │ % │ Input │ Output │ │
236
+ │ │ gpt-4o │ $0.049 │ 94% │ 15,600 │ 4,200 │ │
237
+ │ │ gpt-4o-mini │ $0.003 │ 6% │ 3,134 │ 500 │ │
238
+ │ └──────────────────────────────────────────────────────────┘ │
239
+ └─────────────────────────────────────────────────────────────────┘
240
+ ```
241
+
242
+ ---
243
+
244
+ ## Supported Providers
245
+
246
+ Automatically detected — nothing to configure:
247
+
248
+ | Provider | Package | Intercepted |
249
+ |---|---|---|
250
+ | **OpenAI** | `openai>=1.0` | `chat.completions.create` (sync + async) |
251
+ | **Anthropic** | `anthropic>=0.30` | `messages.create` (sync + async) |
252
+ | **Google** | `google-generativeai>=0.7` | `generate_content` |
253
+
254
+ ---
255
+
256
+ ## Built-in Pricing Table
257
+
258
+ 30+ models, updated Feb 2026. No API call needed.
259
+
260
+ | Model | Input $/1M | Output $/1M |
261
+ |---|---|---|
262
+ | claude-opus-4-6 | $15.00 | $75.00 |
263
+ | claude-sonnet-4-6 | $3.00 | $15.00 |
264
+ | claude-haiku-4-5 | $0.80 | $4.00 |
265
+ | gpt-4o | $2.50 | $10.00 |
266
+ | gpt-4o-mini | $0.15 | $0.60 |
267
+ | o1 | $15.00 | $60.00 |
268
+ | gemini-1.5-pro | $1.25 | $5.00 |
269
+ | gemini-1.5-flash | $0.075 | $0.30 |
270
+
271
+ [→ Full pricing table](llmspy/pricing.py)
272
+
273
+ ---
274
+
275
+ ## API Reference
276
+
277
+ | Symbol | Description |
278
+ |---|---|
279
+ | `@llmspy.profile` | Decorator — profile all LLM calls inside the function |
280
+ | `llmspy.session(name)` | Context manager — profile calls in a `with` block |
281
+ | `llmspy.report()` | Print text flame graph to terminal |
282
+ | `llmspy.report(format="html")` | Write + open HTML flame graph in browser |
283
+ | `llmspy.stats()` | Return full breakdown as a dict |
284
+ | `llmspy.reset()` | Clear all recorded calls |
285
+ | `llmspy.init(persist=True)` | Enable SQLite persistence across sessions |
286
+
287
+ ---
288
+
289
+ ## Comparison
290
+
291
+ | | Langfuse | Helicone | LiteLLM Proxy | **llmspy** |
292
+ |---|---|---|---|---|
293
+ | Requires proxy / gateway | ✅ yes | ✅ yes | ✅ yes | **❌ no** |
294
+ | Requires signup | ✅ yes | ✅ yes | ❌ no | **❌ no** |
295
+ | Local-first | ❌ no | ❌ no | ⚡ partial | **✅ yes** |
296
+ | Zero dependencies | ❌ no | ❌ no | ❌ no | **✅ yes** |
297
+ | Flame graph output | ❌ no | ❌ no | ❌ no | **✅ yes** |
298
+ | `@decorator` API | ❌ no | ❌ no | ❌ no | **✅ yes** |
299
+ | Optimization hints | ❌ no | ⚡ partial | ❌ no | **✅ yes** |
300
+ | Works offline | ❌ no | ❌ no | ⚡ partial | **✅ yes** |
301
+
302
+ ---
303
+
304
+ ## Roadmap
305
+
306
+ - [ ] Streaming response support (`stream=True`)
307
+ - [ ] Token budget alerts: `@llmspy.profile(budget_usd=0.10)`
308
+ - [ ] LangChain / LangGraph integration
309
+ - [ ] CLI: `llmspy history`, `llmspy report`
310
+ - [ ] GitHub Actions annotation (cost diff per PR)
311
+ - [ ] Cost comparison across git commits
312
+
313
+ ---
314
+
315
+ ## Contributing
316
+
317
+ ```bash
318
+ git clone https://github.com/pinakimishra95/llm-cost-profiler
319
+ cd llm-cost-profiler
320
+ pip install -e ".[dev]"
321
+ pytest tests/ # 59 tests, ~0.1s
322
+ ```
323
+
324
+ Issues and PRs welcome — especially for new provider support and updated pricing.
325
+
326
+ ---
327
+
328
+ ## License
329
+
330
+ MIT © [Pinaki Mishra](https://github.com/pinakimishra95). See [LICENSE](LICENSE).
331
+
332
+ ---
333
+
334
+ <div align="center">
335
+
336
+ **Star this repo if you're tired of mystery LLM invoices.** ⭐
337
+
338
+ [GitHub](https://github.com/pinakimishra95/llm-cost-profiler) · [PyPI](https://pypi.org/project/tokenspy/) · [Issues](https://github.com/pinakimishra95/llm-cost-profiler/issues)
339
+
340
+ </div>