bitfrost 0.1.0b1__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.
Files changed (68) hide show
  1. bitfrost-0.1.0b1/.gitignore +107 -0
  2. bitfrost-0.1.0b1/CHANGELOG.md +33 -0
  3. bitfrost-0.1.0b1/LICENSE +21 -0
  4. bitfrost-0.1.0b1/PKG-INFO +217 -0
  5. bitfrost-0.1.0b1/README.md +148 -0
  6. bitfrost-0.1.0b1/pyproject.toml +164 -0
  7. bitfrost-0.1.0b1/src/bitfrost/__init__.py +60 -0
  8. bitfrost-0.1.0b1/src/bitfrost/_brand.py +196 -0
  9. bitfrost-0.1.0b1/src/bitfrost/_readers.py +260 -0
  10. bitfrost-0.1.0b1/src/bitfrost/attribute_mapper.py +383 -0
  11. bitfrost-0.1.0b1/src/bitfrost/backends/__init__.py +24 -0
  12. bitfrost-0.1.0b1/src/bitfrost/backends/base.py +41 -0
  13. bitfrost-0.1.0b1/src/bitfrost/backends/console.py +131 -0
  14. bitfrost-0.1.0b1/src/bitfrost/backends/jsonl.py +114 -0
  15. bitfrost-0.1.0b1/src/bitfrost/backends/otlp.py +144 -0
  16. bitfrost-0.1.0b1/src/bitfrost/backends/sqlite.py +308 -0
  17. bitfrost-0.1.0b1/src/bitfrost/backends/tee.py +122 -0
  18. bitfrost-0.1.0b1/src/bitfrost/backends/voight.py +125 -0
  19. bitfrost-0.1.0b1/src/bitfrost/cli.py +366 -0
  20. bitfrost-0.1.0b1/src/bitfrost/console_renderer.py +321 -0
  21. bitfrost-0.1.0b1/src/bitfrost/conventions.py +171 -0
  22. bitfrost-0.1.0b1/src/bitfrost/exporter.py +191 -0
  23. bitfrost-0.1.0b1/src/bitfrost/identity.py +112 -0
  24. bitfrost-0.1.0b1/src/bitfrost/ingest.py +337 -0
  25. bitfrost-0.1.0b1/src/bitfrost/instrument.py +557 -0
  26. bitfrost-0.1.0b1/src/bitfrost/pricing.py +140 -0
  27. bitfrost-0.1.0b1/src/bitfrost/privacy.py +348 -0
  28. bitfrost-0.1.0b1/src/bitfrost/serve/__init__.py +52 -0
  29. bitfrost-0.1.0b1/src/bitfrost/serve/api.py +493 -0
  30. bitfrost-0.1.0b1/src/bitfrost/serve/app.py +58 -0
  31. bitfrost-0.1.0b1/src/bitfrost/serve/static/app.js +278 -0
  32. bitfrost-0.1.0b1/src/bitfrost/serve/static/chart.min.js +20 -0
  33. bitfrost-0.1.0b1/src/bitfrost/serve/static/favicon.svg +19 -0
  34. bitfrost-0.1.0b1/src/bitfrost/serve/static/index.html +136 -0
  35. bitfrost-0.1.0b1/src/bitfrost/serve/static/styles.css +167 -0
  36. bitfrost-0.1.0b1/src/bitfrost/tui.py +232 -0
  37. bitfrost-0.1.0b1/src/bitfrost/types.py +142 -0
  38. bitfrost-0.1.0b1/tests/__init__.py +0 -0
  39. bitfrost-0.1.0b1/tests/fixtures/anthropic_instrumentation_span.json +57 -0
  40. bitfrost-0.1.0b1/tests/fixtures/anthropic_span_v132.json +27 -0
  41. bitfrost-0.1.0b1/tests/fixtures/openai_instrumentation_span.json +64 -0
  42. bitfrost-0.1.0b1/tests/fixtures/openai_span_v132.json +31 -0
  43. bitfrost-0.1.0b1/tests/fixtures/voight_event_payload_target.json +78 -0
  44. bitfrost-0.1.0b1/tests/integration/__init__.py +0 -0
  45. bitfrost-0.1.0b1/tests/integration/test_docs_examples.py +41 -0
  46. bitfrost-0.1.0b1/tests/integration/test_e2e_serve_dashboard.py +81 -0
  47. bitfrost-0.1.0b1/tests/integration/test_serve_static.py +87 -0
  48. bitfrost-0.1.0b1/tests/unit/__init__.py +0 -0
  49. bitfrost-0.1.0b1/tests/unit/test_attribute_mapper.py +793 -0
  50. bitfrost-0.1.0b1/tests/unit/test_backends_console.py +198 -0
  51. bitfrost-0.1.0b1/tests/unit/test_backends_jsonl.py +109 -0
  52. bitfrost-0.1.0b1/tests/unit/test_backends_otlp.py +125 -0
  53. bitfrost-0.1.0b1/tests/unit/test_backends_sqlite.py +303 -0
  54. bitfrost-0.1.0b1/tests/unit/test_backends_tee.py +105 -0
  55. bitfrost-0.1.0b1/tests/unit/test_backends_voight.py +152 -0
  56. bitfrost-0.1.0b1/tests/unit/test_brand.py +64 -0
  57. bitfrost-0.1.0b1/tests/unit/test_cli.py +199 -0
  58. bitfrost-0.1.0b1/tests/unit/test_console_renderer.py +229 -0
  59. bitfrost-0.1.0b1/tests/unit/test_exporter.py +297 -0
  60. bitfrost-0.1.0b1/tests/unit/test_identity.py +106 -0
  61. bitfrost-0.1.0b1/tests/unit/test_ingest.py +338 -0
  62. bitfrost-0.1.0b1/tests/unit/test_instrument.py +234 -0
  63. bitfrost-0.1.0b1/tests/unit/test_instrument_litellm.py +309 -0
  64. bitfrost-0.1.0b1/tests/unit/test_pricing.py +114 -0
  65. bitfrost-0.1.0b1/tests/unit/test_privacy.py +333 -0
  66. bitfrost-0.1.0b1/tests/unit/test_readers.py +189 -0
  67. bitfrost-0.1.0b1/tests/unit/test_serve_api.py +281 -0
  68. bitfrost-0.1.0b1/tests/unit/test_tui.py +149 -0
@@ -0,0 +1,107 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ *.manifest
31
+ *.spec
32
+
33
+ # Installer logs
34
+ pip-log.txt
35
+ pip-delete-this-directory.txt
36
+
37
+ # Unit test / coverage reports
38
+ htmlcov/
39
+ .tox/
40
+ .nox/
41
+ .coverage
42
+ .coverage.*
43
+ .cache
44
+ nosetests.xml
45
+ coverage.xml
46
+ *.cover
47
+ *.py,cover
48
+ .hypothesis/
49
+ .pytest_cache/
50
+ cover/
51
+
52
+ # Translations
53
+ *.mo
54
+ *.pot
55
+
56
+ # Environments
57
+ .env
58
+ .env.local
59
+ .env.*.local
60
+ .venv
61
+ env/
62
+ venv/
63
+ ENV/
64
+ env.bak/
65
+ venv.bak/
66
+
67
+ # IDEs
68
+ .idea/
69
+ .vscode/
70
+ *.swp
71
+ *.swo
72
+ *~
73
+ .DS_Store
74
+
75
+ # mypy
76
+ .mypy_cache/
77
+ .dmypy.json
78
+ dmypy.json
79
+
80
+ # Ruff
81
+ .ruff_cache/
82
+
83
+ # Type checking
84
+ .pytype/
85
+
86
+ # Cython debug symbols
87
+ cython_debug/
88
+
89
+ # Pyenv
90
+ .python-version.local
91
+
92
+ # Local development
93
+ *.local
94
+ scratch/
95
+ notes-local.md
96
+
97
+ # SQLite databases (bitfrost test artifacts)
98
+ *.sqlite
99
+ *.sqlite3
100
+ *.db
101
+
102
+ # JSONL test artifacts
103
+ *.jsonl
104
+
105
+ # Generated docs
106
+ docs/_build/
107
+ site/
@@ -0,0 +1,33 @@
1
+ # Changelog
2
+
3
+ All notable changes to `bitfrost` are documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] — 2026-06-04
9
+
10
+ First public release.
11
+
12
+ ### Added
13
+
14
+ - **OpenTelemetry SpanExporter** (`BitfrostExporter`) that maps GenAI spans to a
15
+ normalized event shape, supporting both the v1.27 and v1.32+ semantic-convention
16
+ attribute generations.
17
+ - **Five backends**: `ConsoleBackend` (rich terminal), `SQLiteBackend` (persistent
18
+ local log, WAL mode), `JSONLBackend` (replay-able), `OTLPBackend` (JSON over HTTP
19
+ to any collector/webhook), and `VoightBackend` (optional hosted dashboards), plus
20
+ `TeeBackend` to fan out to several at once.
21
+ - **Auto-instrument helpers**: `instrument_openai`, `instrument_anthropic`,
22
+ `instrument_litellm`, `instrument_smolagents`, `instrument_auto`, and `quickstart`.
23
+ - **CLI**: `bitfrost watch / replay / query / vacuum / tui / serve`.
24
+ - **Interactive TUI** (`bitfrost tui`) and an **offline web dashboard**
25
+ (`bitfrost serve`) with live charts, filters, per-span detail, and an SSE feed.
26
+ - **Privacy**: three levels (`minimal` / `standard` / `full`) with PII scrubbing
27
+ (12 patterns + credit-card Luhn) applied in-process before export.
28
+ - **Embedded pricing** for local cost estimates (top models; Decimal arithmetic).
29
+
30
+ ### Notes
31
+
32
+ - Python 3.10–3.13. MIT licensed.
33
+ - The core has no dependency on any hosted service; Voight is opt-in.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Voight
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,217 @@
1
+ Metadata-Version: 2.4
2
+ Name: bitfrost
3
+ Version: 0.1.0b1
4
+ Summary: Drop-in OpenTelemetry observability for Python LLM apps. Standalone or with any OTLP backend.
5
+ Project-URL: Homepage, https://voight.xyz
6
+ Project-URL: Documentation, https://docs.voight.xyz/python/bitfrost
7
+ Project-URL: Repository, https://github.com/Voightxyz/bitfrost
8
+ Project-URL: Issues, https://github.com/Voightxyz/bitfrost/issues
9
+ Project-URL: Changelog, https://github.com/Voightxyz/bitfrost/blob/main/CHANGELOG.md
10
+ Author-email: Voight <hello@voight.xyz>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: anthropic,gen-ai,litellm,llm,monitoring,observability,openai,opentelemetry,otel,smolagents,tracing
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Classifier: Topic :: System :: Monitoring
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: <3.14,>=3.10
28
+ Requires-Dist: opentelemetry-api>=1.27
29
+ Requires-Dist: opentelemetry-sdk>=1.27
30
+ Provides-Extra: all
31
+ Requires-Dist: httpx<1.0,>=0.24; extra == 'all'
32
+ Requires-Dist: rich>=13.0; extra == 'all'
33
+ Requires-Dist: sse-starlette>=2.0; extra == 'all'
34
+ Requires-Dist: starlette>=0.40; extra == 'all'
35
+ Requires-Dist: textual>=0.50; extra == 'all'
36
+ Requires-Dist: typer>=0.12; extra == 'all'
37
+ Requires-Dist: uvicorn>=0.30; extra == 'all'
38
+ Provides-Extra: cli
39
+ Requires-Dist: rich>=13.0; extra == 'cli'
40
+ Requires-Dist: typer>=0.12; extra == 'cli'
41
+ Provides-Extra: dev
42
+ Requires-Dist: build>=1.2; extra == 'dev'
43
+ Requires-Dist: mypy>=1.10; extra == 'dev'
44
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
45
+ Requires-Dist: pytest>=8.0; extra == 'dev'
46
+ Requires-Dist: ruff>=0.6; extra == 'dev'
47
+ Requires-Dist: twine>=5.0; extra == 'dev'
48
+ Provides-Extra: http
49
+ Requires-Dist: httpx<1.0,>=0.24; extra == 'http'
50
+ Provides-Extra: rich
51
+ Requires-Dist: rich>=13.0; extra == 'rich'
52
+ Provides-Extra: serve
53
+ Requires-Dist: rich>=13.0; extra == 'serve'
54
+ Requires-Dist: sse-starlette>=2.0; extra == 'serve'
55
+ Requires-Dist: starlette>=0.40; extra == 'serve'
56
+ Requires-Dist: uvicorn>=0.30; extra == 'serve'
57
+ Provides-Extra: test
58
+ Requires-Dist: anthropic>=0.40; extra == 'test'
59
+ Requires-Dist: litellm>=1.40; extra == 'test'
60
+ Requires-Dist: openai>=1.40; extra == 'test'
61
+ Requires-Dist: openinference-instrumentation-smolagents>=0.1; extra == 'test'
62
+ Requires-Dist: opentelemetry-instrumentation-anthropic>=0.30; extra == 'test'
63
+ Requires-Dist: opentelemetry-instrumentation-openai>=0.30; extra == 'test'
64
+ Requires-Dist: smolagents>=1.0; extra == 'test'
65
+ Requires-Dist: textual>=0.50; extra == 'test'
66
+ Provides-Extra: tui
67
+ Requires-Dist: textual>=0.50; extra == 'tui'
68
+ Description-Content-Type: text/markdown
69
+
70
+ <div align="center">
71
+
72
+ <img src="https://raw.githubusercontent.com/Voightxyz/bitfrost/main/assets/logo.svg" alt="bitfrost" height="56" />
73
+
74
+ # bitfrost
75
+
76
+ **Drop-in OpenTelemetry observability for Python LLM apps.**
77
+ Standalone or with any OTLP backend — runs on your machine, sends nothing unless you ask it to.
78
+
79
+ [![PyPI](https://img.shields.io/pypi/v/bitfrost.svg)](https://pypi.org/project/bitfrost/)
80
+ [![Python](https://img.shields.io/pypi/pyversions/bitfrost.svg)](https://pypi.org/project/bitfrost/)
81
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
82
+ [![CI](https://github.com/Voightxyz/bitfrost/actions/workflows/ci.yml/badge.svg)](https://github.com/Voightxyz/bitfrost/actions/workflows/ci.yml)
83
+
84
+ </div>
85
+
86
+ ---
87
+
88
+ Bitfrost turns the OpenTelemetry spans your LLM libraries already emit into a clean, queryable event stream — in your terminal, in a local web dashboard, in SQLite, or shipped to any backend you choose. One line to start, zero accounts required.
89
+
90
+ ```python
91
+ import bitfrost
92
+
93
+ bitfrost.quickstart(agent="my-app") # auto-detects openai / anthropic / litellm / smolagents
94
+ # ...your normal LLM calls now stream to the terminal, color-coded, with tokens + cost.
95
+ ```
96
+
97
+ <div align="center">
98
+ <img src="https://raw.githubusercontent.com/Voightxyz/bitfrost/main/assets/dashboard.png" alt="bitfrost serve — local web dashboard" width="100%" />
99
+ <br />
100
+ <em><code>bitfrost serve capture.db</code> — a local, offline dashboard. No account, no upload.</em>
101
+ </div>
102
+
103
+ ## Why bitfrost
104
+
105
+ - **Standalone first.** Capture to your terminal, a JSONL file, or SQLite with zero configuration and zero network calls. Voight is one optional backend, never a requirement.
106
+ - **Drop-in.** Built on the OpenTelemetry GenAI semantic conventions, so it works with the instrumentation libraries you already use — across both the v1.27 and v1.32+ attribute generations.
107
+ - **Batteries included.** Five backends, four auto-instrument helpers, a rich CLI, an interactive TUI, and an offline web dashboard.
108
+ - **Private by default.** Three privacy levels with PII scrubbing (12 patterns + Luhn) applied before any event leaves the process.
109
+ - **Never crashes your app.** Every failure path degrades gracefully — a dead endpoint or a missing optional dependency never takes down your code.
110
+
111
+ ## Install
112
+
113
+ ```bash
114
+ pip install bitfrost # core
115
+ pip install 'bitfrost[cli,serve]' # + CLI, TUI and local web dashboard
116
+ pip install 'bitfrost[all]' # everything
117
+ ```
118
+
119
+ Python 3.10–3.13.
120
+
121
+ ## Capture anywhere
122
+
123
+ Pick a backend and pass it to any instrument helper (or `quickstart`). All concrete backends live under `bitfrost.backends.*`:
124
+
125
+ | Backend | Import | Use it for |
126
+ |---|---|---|
127
+ | Console | `bitfrost.backends.console.ConsoleBackend` | live, color-coded terminal output |
128
+ | SQLite | `bitfrost.backends.sqlite.SQLiteBackend` | persistent local log; powers `bitfrost serve` |
129
+ | JSONL | `bitfrost.backends.jsonl.JSONLBackend` | one JSON object per line; replay-able |
130
+ | OTLP/HTTP | `bitfrost.backends.otlp.OTLPBackend` | POST events as JSON to any collector or webhook |
131
+ | Voight | `bitfrost.backends.voight.VoightBackend` | hosted dashboards (optional, opt-in) |
132
+ | Tee | `bitfrost.backends.tee.TeeBackend` | fan out to several backends at once |
133
+
134
+ ```python
135
+ import bitfrost
136
+ from bitfrost.backends.sqlite import SQLiteBackend
137
+
138
+ bitfrost.instrument_openai(backend=SQLiteBackend("events.db"), agent="my-app")
139
+ # then: bitfrost serve events.db
140
+ ```
141
+
142
+ Fan out to several at once:
143
+
144
+ ```python
145
+ import bitfrost
146
+ from bitfrost.backends.tee import TeeBackend
147
+ from bitfrost.backends.console import ConsoleBackend
148
+ from bitfrost.backends.sqlite import SQLiteBackend
149
+
150
+ bitfrost.instrument_auto(
151
+ backend=TeeBackend(ConsoleBackend(), SQLiteBackend("events.db")),
152
+ agent="my-app",
153
+ )
154
+ ```
155
+
156
+ ## Auto-instrument helpers
157
+
158
+ ```python
159
+ import bitfrost
160
+
161
+ bitfrost.instrument_openai() # opentelemetry-instrumentation-openai
162
+ bitfrost.instrument_anthropic() # opentelemetry-instrumentation-anthropic
163
+ bitfrost.instrument_litellm() # litellm CustomLogger adapter
164
+ bitfrost.instrument_smolagents() # openinference-instrumentation-smolagents
165
+ bitfrost.instrument_auto() # instrument every supported lib that's installed
166
+ ```
167
+
168
+ Already wiring OpenTelemetry yourself? Skip the helpers and attach the exporter to your own `TracerProvider`:
169
+
170
+ ```python
171
+ from opentelemetry.sdk.trace import TracerProvider
172
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
173
+ from bitfrost.exporter import BitfrostExporter
174
+ from bitfrost.backends.console import ConsoleBackend
175
+
176
+ provider = TracerProvider()
177
+ provider.add_span_processor(
178
+ BatchSpanProcessor(BitfrostExporter(ConsoleBackend(), agent="my-app"))
179
+ )
180
+ ```
181
+
182
+ ## CLI
183
+
184
+ ```bash
185
+ bitfrost watch capture.db # live tail, one styled line per event
186
+ bitfrost replay capture.jsonl # re-render a captured run start to finish
187
+ bitfrost query capture.db "SELECT model, COUNT(*) FROM events GROUP BY model"
188
+ bitfrost vacuum capture.db --keep-days 7
189
+ bitfrost tui capture.db # full-screen interactive dashboard
190
+ bitfrost serve capture.db # local web dashboard at http://127.0.0.1:8080
191
+ ```
192
+
193
+ ## Privacy
194
+
195
+ Three levels, applied in-process before any event is sent:
196
+
197
+ - `minimal` — metadata only, no prompt/response content
198
+ - `standard` *(default)* — content kept, PII scrubbed (12 patterns + credit-card Luhn check)
199
+ - `full` — everything verbatim (use only for local debugging)
200
+
201
+ ```python
202
+ bitfrost.instrument_openai(privacy="minimal")
203
+ ```
204
+
205
+ ## Standalone, or with Voight
206
+
207
+ Bitfrost is useful entirely on its own — the core has no dependency on any hosted service. If you want managed dashboards, team sharing, and per-user cost attribution, the optional `VoightBackend` ships your events to [voight.xyz](https://voight.xyz). Everything else stays exactly the same.
208
+
209
+ ## Documentation
210
+
211
+ - [Cookbook](https://github.com/Voightxyz/bitfrost/blob/main/docs/cookbook.md) — task-oriented recipes
212
+ - [Write your own backend](https://github.com/Voightxyz/bitfrost/blob/main/docs/custom_backend.md) — the `ExportBackend` protocol
213
+ - Full docs: [docs.voight.xyz/python/bitfrost](https://docs.voight.xyz/python/bitfrost)
214
+
215
+ ## License
216
+
217
+ MIT. Built by [Voight](https://voight.xyz).
@@ -0,0 +1,148 @@
1
+ <div align="center">
2
+
3
+ <img src="https://raw.githubusercontent.com/Voightxyz/bitfrost/main/assets/logo.svg" alt="bitfrost" height="56" />
4
+
5
+ # bitfrost
6
+
7
+ **Drop-in OpenTelemetry observability for Python LLM apps.**
8
+ Standalone or with any OTLP backend — runs on your machine, sends nothing unless you ask it to.
9
+
10
+ [![PyPI](https://img.shields.io/pypi/v/bitfrost.svg)](https://pypi.org/project/bitfrost/)
11
+ [![Python](https://img.shields.io/pypi/pyversions/bitfrost.svg)](https://pypi.org/project/bitfrost/)
12
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
13
+ [![CI](https://github.com/Voightxyz/bitfrost/actions/workflows/ci.yml/badge.svg)](https://github.com/Voightxyz/bitfrost/actions/workflows/ci.yml)
14
+
15
+ </div>
16
+
17
+ ---
18
+
19
+ Bitfrost turns the OpenTelemetry spans your LLM libraries already emit into a clean, queryable event stream — in your terminal, in a local web dashboard, in SQLite, or shipped to any backend you choose. One line to start, zero accounts required.
20
+
21
+ ```python
22
+ import bitfrost
23
+
24
+ bitfrost.quickstart(agent="my-app") # auto-detects openai / anthropic / litellm / smolagents
25
+ # ...your normal LLM calls now stream to the terminal, color-coded, with tokens + cost.
26
+ ```
27
+
28
+ <div align="center">
29
+ <img src="https://raw.githubusercontent.com/Voightxyz/bitfrost/main/assets/dashboard.png" alt="bitfrost serve — local web dashboard" width="100%" />
30
+ <br />
31
+ <em><code>bitfrost serve capture.db</code> — a local, offline dashboard. No account, no upload.</em>
32
+ </div>
33
+
34
+ ## Why bitfrost
35
+
36
+ - **Standalone first.** Capture to your terminal, a JSONL file, or SQLite with zero configuration and zero network calls. Voight is one optional backend, never a requirement.
37
+ - **Drop-in.** Built on the OpenTelemetry GenAI semantic conventions, so it works with the instrumentation libraries you already use — across both the v1.27 and v1.32+ attribute generations.
38
+ - **Batteries included.** Five backends, four auto-instrument helpers, a rich CLI, an interactive TUI, and an offline web dashboard.
39
+ - **Private by default.** Three privacy levels with PII scrubbing (12 patterns + Luhn) applied before any event leaves the process.
40
+ - **Never crashes your app.** Every failure path degrades gracefully — a dead endpoint or a missing optional dependency never takes down your code.
41
+
42
+ ## Install
43
+
44
+ ```bash
45
+ pip install bitfrost # core
46
+ pip install 'bitfrost[cli,serve]' # + CLI, TUI and local web dashboard
47
+ pip install 'bitfrost[all]' # everything
48
+ ```
49
+
50
+ Python 3.10–3.13.
51
+
52
+ ## Capture anywhere
53
+
54
+ Pick a backend and pass it to any instrument helper (or `quickstart`). All concrete backends live under `bitfrost.backends.*`:
55
+
56
+ | Backend | Import | Use it for |
57
+ |---|---|---|
58
+ | Console | `bitfrost.backends.console.ConsoleBackend` | live, color-coded terminal output |
59
+ | SQLite | `bitfrost.backends.sqlite.SQLiteBackend` | persistent local log; powers `bitfrost serve` |
60
+ | JSONL | `bitfrost.backends.jsonl.JSONLBackend` | one JSON object per line; replay-able |
61
+ | OTLP/HTTP | `bitfrost.backends.otlp.OTLPBackend` | POST events as JSON to any collector or webhook |
62
+ | Voight | `bitfrost.backends.voight.VoightBackend` | hosted dashboards (optional, opt-in) |
63
+ | Tee | `bitfrost.backends.tee.TeeBackend` | fan out to several backends at once |
64
+
65
+ ```python
66
+ import bitfrost
67
+ from bitfrost.backends.sqlite import SQLiteBackend
68
+
69
+ bitfrost.instrument_openai(backend=SQLiteBackend("events.db"), agent="my-app")
70
+ # then: bitfrost serve events.db
71
+ ```
72
+
73
+ Fan out to several at once:
74
+
75
+ ```python
76
+ import bitfrost
77
+ from bitfrost.backends.tee import TeeBackend
78
+ from bitfrost.backends.console import ConsoleBackend
79
+ from bitfrost.backends.sqlite import SQLiteBackend
80
+
81
+ bitfrost.instrument_auto(
82
+ backend=TeeBackend(ConsoleBackend(), SQLiteBackend("events.db")),
83
+ agent="my-app",
84
+ )
85
+ ```
86
+
87
+ ## Auto-instrument helpers
88
+
89
+ ```python
90
+ import bitfrost
91
+
92
+ bitfrost.instrument_openai() # opentelemetry-instrumentation-openai
93
+ bitfrost.instrument_anthropic() # opentelemetry-instrumentation-anthropic
94
+ bitfrost.instrument_litellm() # litellm CustomLogger adapter
95
+ bitfrost.instrument_smolagents() # openinference-instrumentation-smolagents
96
+ bitfrost.instrument_auto() # instrument every supported lib that's installed
97
+ ```
98
+
99
+ Already wiring OpenTelemetry yourself? Skip the helpers and attach the exporter to your own `TracerProvider`:
100
+
101
+ ```python
102
+ from opentelemetry.sdk.trace import TracerProvider
103
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
104
+ from bitfrost.exporter import BitfrostExporter
105
+ from bitfrost.backends.console import ConsoleBackend
106
+
107
+ provider = TracerProvider()
108
+ provider.add_span_processor(
109
+ BatchSpanProcessor(BitfrostExporter(ConsoleBackend(), agent="my-app"))
110
+ )
111
+ ```
112
+
113
+ ## CLI
114
+
115
+ ```bash
116
+ bitfrost watch capture.db # live tail, one styled line per event
117
+ bitfrost replay capture.jsonl # re-render a captured run start to finish
118
+ bitfrost query capture.db "SELECT model, COUNT(*) FROM events GROUP BY model"
119
+ bitfrost vacuum capture.db --keep-days 7
120
+ bitfrost tui capture.db # full-screen interactive dashboard
121
+ bitfrost serve capture.db # local web dashboard at http://127.0.0.1:8080
122
+ ```
123
+
124
+ ## Privacy
125
+
126
+ Three levels, applied in-process before any event is sent:
127
+
128
+ - `minimal` — metadata only, no prompt/response content
129
+ - `standard` *(default)* — content kept, PII scrubbed (12 patterns + credit-card Luhn check)
130
+ - `full` — everything verbatim (use only for local debugging)
131
+
132
+ ```python
133
+ bitfrost.instrument_openai(privacy="minimal")
134
+ ```
135
+
136
+ ## Standalone, or with Voight
137
+
138
+ Bitfrost is useful entirely on its own — the core has no dependency on any hosted service. If you want managed dashboards, team sharing, and per-user cost attribution, the optional `VoightBackend` ships your events to [voight.xyz](https://voight.xyz). Everything else stays exactly the same.
139
+
140
+ ## Documentation
141
+
142
+ - [Cookbook](https://github.com/Voightxyz/bitfrost/blob/main/docs/cookbook.md) — task-oriented recipes
143
+ - [Write your own backend](https://github.com/Voightxyz/bitfrost/blob/main/docs/custom_backend.md) — the `ExportBackend` protocol
144
+ - Full docs: [docs.voight.xyz/python/bitfrost](https://docs.voight.xyz/python/bitfrost)
145
+
146
+ ## License
147
+
148
+ MIT. Built by [Voight](https://voight.xyz).
@@ -0,0 +1,164 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "bitfrost"
7
+ version = "0.1.0b1"
8
+ description = "Drop-in OpenTelemetry observability for Python LLM apps. Standalone or with any OTLP backend."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.10,<3.14"
13
+ authors = [
14
+ { name = "Voight", email = "hello@voight.xyz" },
15
+ ]
16
+ keywords = [
17
+ "opentelemetry",
18
+ "otel",
19
+ "observability",
20
+ "llm",
21
+ "gen-ai",
22
+ "openai",
23
+ "anthropic",
24
+ "smolagents",
25
+ "litellm",
26
+ "tracing",
27
+ "monitoring",
28
+ ]
29
+ classifiers = [
30
+ "Development Status :: 3 - Alpha",
31
+ "Intended Audience :: Developers",
32
+ "License :: OSI Approved :: MIT License",
33
+ "Operating System :: OS Independent",
34
+ "Programming Language :: Python :: 3",
35
+ "Programming Language :: Python :: 3 :: Only",
36
+ "Programming Language :: Python :: 3.10",
37
+ "Programming Language :: Python :: 3.11",
38
+ "Programming Language :: Python :: 3.12",
39
+ "Programming Language :: Python :: 3.13",
40
+ "Topic :: Software Development :: Libraries :: Python Modules",
41
+ "Topic :: System :: Monitoring",
42
+ "Typing :: Typed",
43
+ ]
44
+ dependencies = [
45
+ "opentelemetry-api>=1.27",
46
+ "opentelemetry-sdk>=1.27",
47
+ ]
48
+
49
+ [project.optional-dependencies]
50
+ http = [
51
+ "httpx>=0.24,<1.0",
52
+ ]
53
+ rich = [
54
+ "rich>=13.0",
55
+ ]
56
+ cli = [
57
+ "typer>=0.12",
58
+ "rich>=13.0",
59
+ ]
60
+ serve = [
61
+ "starlette>=0.40",
62
+ "uvicorn>=0.30",
63
+ "sse-starlette>=2.0",
64
+ "rich>=13.0",
65
+ ]
66
+ tui = [
67
+ "textual>=0.50",
68
+ ]
69
+ all = [
70
+ "bitfrost[http,rich,cli,serve,tui]",
71
+ ]
72
+ dev = [
73
+ "pytest>=8.0",
74
+ "pytest-cov>=5.0",
75
+ "ruff>=0.6",
76
+ "mypy>=1.10",
77
+ "build>=1.2",
78
+ "twine>=5.0",
79
+ ]
80
+ # Integration libraries the test suite + mypy need present. Bitfrost
81
+ # imports these lazily (they're the user's deps, not ours), but the
82
+ # unit tests patch their instrumentors and the LiteLLM adapter
83
+ # subclasses litellm's CustomLogger — so CI must install them for the
84
+ # suite to run and for mypy to see real types instead of Any. Kept in a
85
+ # dedicated group so a plain `pip install bitfrost[dev]` for docs/lint
86
+ # work doesn't drag in litellm + smolagents.
87
+ test = [
88
+ # The instrumentation packages import the underlying SDK at module
89
+ # load (the openai/anthropic instrumentors `import openai` /
90
+ # `import anthropic`), so the SDKs themselves must be present for the
91
+ # instrumentor submodules to import at all.
92
+ "openai>=1.40",
93
+ "anthropic>=0.40",
94
+ "opentelemetry-instrumentation-openai>=0.30",
95
+ "opentelemetry-instrumentation-anthropic>=0.30",
96
+ "litellm>=1.40",
97
+ "smolagents>=1.0",
98
+ "openinference-instrumentation-smolagents>=0.1",
99
+ "textual>=0.50",
100
+ ]
101
+
102
+ [project.urls]
103
+ Homepage = "https://voight.xyz"
104
+ Documentation = "https://docs.voight.xyz/python/bitfrost"
105
+ Repository = "https://github.com/Voightxyz/bitfrost"
106
+ Issues = "https://github.com/Voightxyz/bitfrost/issues"
107
+ Changelog = "https://github.com/Voightxyz/bitfrost/blob/main/CHANGELOG.md"
108
+
109
+ [project.scripts]
110
+ bitfrost = "bitfrost.cli:app"
111
+
112
+ [tool.hatch.build.targets.wheel]
113
+ packages = ["src/bitfrost"]
114
+
115
+ [tool.hatch.build.targets.sdist]
116
+ include = [
117
+ "/src",
118
+ "/tests",
119
+ "/README.md",
120
+ "/CHANGELOG.md",
121
+ "/LICENSE",
122
+ "/pyproject.toml",
123
+ ]
124
+
125
+ [tool.pytest.ini_options]
126
+ minversion = "8.0"
127
+ testpaths = ["tests"]
128
+ python_files = ["test_*.py"]
129
+ python_classes = ["Test*"]
130
+ python_functions = ["test_*"]
131
+ addopts = [
132
+ "-ra",
133
+ "--strict-markers",
134
+ "--strict-config",
135
+ ]
136
+ filterwarnings = [
137
+ "error",
138
+ "ignore::DeprecationWarning",
139
+ # starlette 1.2+ emits a deprecation when its TestClient is used with
140
+ # httpx (it plans to switch to httpx2). httpx still works and the
141
+ # TestClient is test-only, so we silence it rather than pin starlette
142
+ # or add an unstable dep. Matched by message so the filter needs no
143
+ # importable warning class (works even in a serve-less install).
144
+ "ignore:Using `httpx` with `starlette.testclient`:",
145
+ ]
146
+
147
+ [tool.coverage.run]
148
+ source = ["src/bitfrost"]
149
+ branch = true
150
+ parallel = true
151
+
152
+ [tool.coverage.report]
153
+ exclude_lines = [
154
+ "pragma: no cover",
155
+ "raise NotImplementedError",
156
+ "if TYPE_CHECKING:",
157
+ "if __name__ == .__main__.:",
158
+ "\\.\\.\\.",
159
+ ]
160
+ show_missing = true
161
+ skip_covered = false
162
+
163
+ [tool.coverage.paths]
164
+ source = ["src", ".tox/*/lib/python*/site-packages"]