ai-token-tracker 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.
Files changed (26) hide show
  1. ai_token_tracker-0.1.0/PKG-INFO +300 -0
  2. ai_token_tracker-0.1.0/README.md +265 -0
  3. ai_token_tracker-0.1.0/pyproject.toml +61 -0
  4. ai_token_tracker-0.1.0/setup.cfg +4 -0
  5. ai_token_tracker-0.1.0/src/ai_token_tracker/__init__.py +33 -0
  6. ai_token_tracker-0.1.0/src/ai_token_tracker/classifier.py +105 -0
  7. ai_token_tracker-0.1.0/src/ai_token_tracker/constants.py +32 -0
  8. ai_token_tracker-0.1.0/src/ai_token_tracker/envelope.py +67 -0
  9. ai_token_tracker-0.1.0/src/ai_token_tracker/ingestion_client.py +146 -0
  10. ai_token_tracker-0.1.0/src/ai_token_tracker/interception.py +524 -0
  11. ai_token_tracker-0.1.0/src/ai_token_tracker/interception_scope.py +53 -0
  12. ai_token_tracker-0.1.0/src/ai_token_tracker/options.py +43 -0
  13. ai_token_tracker-0.1.0/src/ai_token_tracker/py.typed +0 -0
  14. ai_token_tracker-0.1.0/src/ai_token_tracker/runtime.py +13 -0
  15. ai_token_tracker-0.1.0/src/ai_token_tracker/sdk_client.py +160 -0
  16. ai_token_tracker-0.1.0/src/ai_token_tracker/types.py +89 -0
  17. ai_token_tracker-0.1.0/src/ai_token_tracker.egg-info/PKG-INFO +300 -0
  18. ai_token_tracker-0.1.0/src/ai_token_tracker.egg-info/SOURCES.txt +24 -0
  19. ai_token_tracker-0.1.0/src/ai_token_tracker.egg-info/dependency_links.txt +1 -0
  20. ai_token_tracker-0.1.0/src/ai_token_tracker.egg-info/requires.txt +15 -0
  21. ai_token_tracker-0.1.0/src/ai_token_tracker.egg-info/top_level.txt +1 -0
  22. ai_token_tracker-0.1.0/tests/test_ingestion_client_integration.py +75 -0
  23. ai_token_tracker-0.1.0/tests/test_interception.py +170 -0
  24. ai_token_tracker-0.1.0/tests/test_interception_scope.py +21 -0
  25. ai_token_tracker-0.1.0/tests/test_options_and_mapping.py +43 -0
  26. ai_token_tracker-0.1.0/tests/test_sdk_client.py +77 -0
@@ -0,0 +1,300 @@
1
+ Metadata-Version: 2.4
2
+ Name: ai-token-tracker
3
+ Version: 0.1.0
4
+ Summary: Python SDK for capturing LLM traffic and ingesting envelopes into Ai Token Tracker
5
+ Author: Ai Token Tracker
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ai-token-tracker/ai-token-tracker
8
+ Project-URL: Repository, https://github.com/ai-token-tracker/ai-token-tracker
9
+ Project-URL: Documentation, https://github.com/ai-token-tracker/ai-token-tracker/tree/main/sdks/python
10
+ Project-URL: Issues, https://github.com/ai-token-tracker/ai-token-tracker/issues
11
+ Keywords: llm,observability,openai,anthropic,sdk
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: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ Provides-Extra: requests
25
+ Requires-Dist: requests>=2.31; extra == "requests"
26
+ Provides-Extra: httpx
27
+ Requires-Dist: httpx>=0.27; extra == "httpx"
28
+ Provides-Extra: all
29
+ Requires-Dist: requests>=2.31; extra == "all"
30
+ Requires-Dist: httpx>=0.27; extra == "all"
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest>=8.2; extra == "dev"
33
+ Requires-Dist: requests>=2.31; extra == "dev"
34
+ Requires-Dist: httpx>=0.27; extra == "dev"
35
+
36
+ # Ai Token Tracker Python SDK
37
+
38
+ `ai-token-tracker` captures outbound LLM traffic and posts ingest envelopes to Ai Token Tracker.
39
+
40
+ Feature parity targets:
41
+ 1. Automatic HTTP interception (`requests`, `httpx` sync/async)
42
+ 2. SDK wrapper integration (`begin_llm_call` scope)
43
+ 3. Diagnostics fallback (metadata-only safety net)
44
+
45
+ ## Requirements
46
+
47
+ - Python 3.10+
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ pip install ai-token-tracker
53
+ ```
54
+
55
+ Optional extras:
56
+
57
+ ```bash
58
+ pip install ai-token-tracker[requests]
59
+ pip install ai-token-tracker[httpx]
60
+ pip install ai-token-tracker[all]
61
+ ```
62
+
63
+ ## Defaults
64
+
65
+ - API base URL: `http://localhost:8082` (internal, hardcoded)
66
+ - `enable_auto_interception=True`
67
+ - `enable_diagnostics_fallback=True`
68
+ - Ingest auth header (internal): `X-API-Key`
69
+ - Ingest path (internal): `/ingest`
70
+
71
+ ## Quick Start (Plain Script)
72
+
73
+ ```python
74
+ from ai_token_tracker import (
75
+ AiTokenTrackerIngestionClient,
76
+ AiTokenTrackerOptions,
77
+ AiTokenTrackerSdkClient,
78
+ )
79
+
80
+ ingestion = AiTokenTrackerIngestionClient(
81
+ AiTokenTrackerOptions(auth_token="atk_your_key")
82
+ )
83
+
84
+ sdk = AiTokenTrackerSdkClient(ingestion)
85
+
86
+ scope = sdk.begin_llm_call(
87
+ provider_hint="openai",
88
+ method="POST",
89
+ url="https://api.openai.com/v1/responses",
90
+ request={"model": "gpt-4.1-mini", "input": "hello"},
91
+ custom_filters={"workflow": "quickstart"},
92
+ )
93
+
94
+ try:
95
+ provider_response = {"id": "resp_123", "output": [{"type": "output_text", "text": "hi"}]}
96
+ scope.complete(provider_response, status_code=200)
97
+ except Exception as ex:
98
+ scope.fail(ex, status_code=500)
99
+ raise
100
+ finally:
101
+ ingestion.close()
102
+ ```
103
+
104
+ ## Automatic HTTP Interception
105
+
106
+ Interception is explicit install + option-gated:
107
+
108
+ ```python
109
+ import requests
110
+ from ai_token_tracker import (
111
+ AiTokenTrackerIngestionClient,
112
+ AiTokenTrackerOptions,
113
+ install_http_interception,
114
+ )
115
+
116
+ client = AiTokenTrackerIngestionClient(
117
+ AiTokenTrackerOptions(
118
+ auth_token="atk_your_key",
119
+ enable_auto_interception=True,
120
+ )
121
+ )
122
+
123
+ install_http_interception(client)
124
+
125
+ # Classified request is captured and ingested automatically.
126
+ requests.post(
127
+ "https://api.openai.com/v1/responses",
128
+ headers={"Authorization": "Bearer sk_test"},
129
+ json={"model": "gpt-4.1-mini", "input": "hello"},
130
+ timeout=10,
131
+ )
132
+
133
+ client.close() # unpatches interceptors + shuts down background worker
134
+ ```
135
+
136
+ ### Scoped Custom Filters For Automatic Interception
137
+
138
+ Use interception scope to attach custom filters without wrapper integration:
139
+
140
+ ```python
141
+ import requests
142
+ from ai_token_tracker import (
143
+ AiTokenTrackerIngestionClient,
144
+ AiTokenTrackerInterceptionScope,
145
+ AiTokenTrackerOptions,
146
+ install_http_interception,
147
+ )
148
+
149
+ client = AiTokenTrackerIngestionClient(
150
+ AiTokenTrackerOptions(auth_token="atk_your_key", enable_auto_interception=True)
151
+ )
152
+ install_http_interception(client)
153
+
154
+ scope = AiTokenTrackerInterceptionScope.create({"workflow": "social_post_generation"})
155
+ scope.add_custom_filters({"job_id": "job-42", "customer_id": "cust-19"})
156
+
157
+ scope.open()
158
+ try:
159
+ requests.post(
160
+ "https://api.openai.com/v1/responses",
161
+ headers={"Authorization": "Bearer sk_test"},
162
+ json={"model": "gpt-4.1-mini", "input": "hello"},
163
+ timeout=10,
164
+ )
165
+ finally:
166
+ scope.close()
167
+ ```
168
+
169
+ Always close scope in `finally` to avoid leaking filters to later calls.
170
+
171
+ ## SDK Wrapper Example
172
+
173
+ ```python
174
+ from ai_token_tracker import AiTokenTrackerIngestionClient, AiTokenTrackerOptions, AiTokenTrackerSdkClient
175
+
176
+ ingestion = AiTokenTrackerIngestionClient(AiTokenTrackerOptions(auth_token="atk_your_key"))
177
+ tracker = AiTokenTrackerSdkClient(ingestion)
178
+
179
+ scope = tracker.begin_llm_call(
180
+ provider_hint="anthropic",
181
+ method="POST",
182
+ url="https://api.anthropic.com/v1/messages",
183
+ request={"model": "claude-sonnet-4-6", "max_tokens": 256, "messages": [{"role": "user", "content": "hello"}]},
184
+ custom_filters={"tenant": "acme", "job": "summarization"},
185
+ )
186
+
187
+ scope.complete({"id": "msg_123"}, status_code=200)
188
+ ingestion.close()
189
+ ```
190
+
191
+ ## Diagnostics Fallback Setup
192
+
193
+ Diagnostics fallback is best-effort metadata capture for uncovered traffic.
194
+
195
+ ```python
196
+ from ai_token_tracker import AiTokenTrackerIngestionClient, AiTokenTrackerOptions, install_http_interception
197
+
198
+ client = AiTokenTrackerIngestionClient(
199
+ AiTokenTrackerOptions(
200
+ auth_token="atk_your_key",
201
+ enable_diagnostics_fallback=True,
202
+ )
203
+ )
204
+ install_http_interception(client)
205
+ ```
206
+
207
+ Fallback behavior:
208
+ - captures method, url, status, headers
209
+ - does not guarantee request/response bodies
210
+ - intended for visibility and coverage-gap detection, not full fidelity auditing
211
+
212
+ ## FastAPI Example
213
+
214
+ ```python
215
+ from contextlib import asynccontextmanager
216
+ from fastapi import FastAPI
217
+ from ai_token_tracker import (
218
+ AiTokenTrackerIngestionClient,
219
+ AiTokenTrackerOptions,
220
+ AiTokenTrackerSdkClient,
221
+ install_http_interception,
222
+ )
223
+
224
+ ingestion = AiTokenTrackerIngestionClient(
225
+ AiTokenTrackerOptions(auth_token="atk_your_key", enable_auto_interception=True)
226
+ )
227
+ tracker = AiTokenTrackerSdkClient(ingestion)
228
+
229
+ @asynccontextmanager
230
+ async def lifespan(app: FastAPI):
231
+ install_http_interception(ingestion)
232
+ yield
233
+ ingestion.close()
234
+
235
+ app = FastAPI(lifespan=lifespan)
236
+
237
+ @app.get("/health")
238
+ def health() -> dict[str, str]:
239
+ return {"status": "ok"}
240
+ ```
241
+
242
+ ## Background Worker / Job Pipeline Example
243
+
244
+ ```python
245
+ from ai_token_tracker import AiTokenTrackerIngestionClient, AiTokenTrackerOptions, AiTokenTrackerSdkClient
246
+
247
+ ingestion = AiTokenTrackerIngestionClient(AiTokenTrackerOptions(auth_token="atk_your_key"))
248
+ tracker = AiTokenTrackerSdkClient(ingestion)
249
+
250
+ def run_job(job_id: str, prompt: str) -> None:
251
+ scope = tracker.begin_llm_call(
252
+ provider_hint="openai",
253
+ method="POST",
254
+ url="https://api.openai.com/v1/responses",
255
+ request={"model": "gpt-4.1-mini", "input": prompt},
256
+ custom_filters={"job_id": job_id, "pipeline": "nightly"},
257
+ )
258
+ try:
259
+ response = {"id": f"resp_{job_id}", "output": [{"type": "output_text", "text": "done"}]}
260
+ scope.complete(response)
261
+ except Exception as ex:
262
+ scope.fail(ex)
263
+ raise
264
+
265
+ run_job("42", "summarize daily usage")
266
+ ingestion.close()
267
+ ```
268
+
269
+ ## Custom Filters Guidance
270
+
271
+ `custom_filters` is key/value metadata attached to each envelope:
272
+ - keep keys stable (`tenant`, `workflow`, `job_id`, `environment`)
273
+ - keep values low-cardinality when possible
274
+ - avoid putting secrets or full prompt bodies in filters
275
+
276
+ ## Verification Checklist
277
+
278
+ 1. Use SDK with valid `auth_token` (ingest URL is internal and fixed by SDK).
279
+ 2. Create client with valid `auth_token`.
280
+ 3. For interception, set option flag and call `install_http_interception(client)`.
281
+ 4. Send known LLM request (`/v1/responses` or `/chat/completions`).
282
+ 5. Confirm ingest API receives `POST /ingest` with `X-API-Key` and expected envelope JSON.
283
+ 6. Confirm app flow still works if ingest endpoint is unavailable (tracking paths are non-throwing).
284
+
285
+ ## Error Handling + Logging
286
+
287
+ - `track(...)` and scoped `complete(...)` / `fail(...)` are non-throwing for ingest failures.
288
+ - Warnings logged for ingestion failures and non-2xx ingest status.
289
+ - Debug logs used for interception/fallback internal capture issues.
290
+
291
+ ## Public API Summary
292
+
293
+ - `AiTokenTrackerOptions(auth_token, enable_auto_interception=True, enable_diagnostics_fallback=True)`
294
+ - `AiTokenTrackerIngestionClient.track(...) -> TrackResult`
295
+ - `AiTokenTrackerIngestionClient.track_async(...) -> TrackResult`
296
+ - `AiTokenTrackerIngestionClient.close()`
297
+ - `AiTokenTrackerSdkClient.begin_llm_call(...) -> CallScope`
298
+ - `CallScope.complete(...)`, `CallScope.fail(...)`
299
+ - `CallScope.complete_async(...)`, `CallScope.fail_async(...)`
300
+ - `install_http_interception(client)`, `uninstall_http_interception(client)`
@@ -0,0 +1,265 @@
1
+ # Ai Token Tracker Python SDK
2
+
3
+ `ai-token-tracker` captures outbound LLM traffic and posts ingest envelopes to Ai Token Tracker.
4
+
5
+ Feature parity targets:
6
+ 1. Automatic HTTP interception (`requests`, `httpx` sync/async)
7
+ 2. SDK wrapper integration (`begin_llm_call` scope)
8
+ 3. Diagnostics fallback (metadata-only safety net)
9
+
10
+ ## Requirements
11
+
12
+ - Python 3.10+
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pip install ai-token-tracker
18
+ ```
19
+
20
+ Optional extras:
21
+
22
+ ```bash
23
+ pip install ai-token-tracker[requests]
24
+ pip install ai-token-tracker[httpx]
25
+ pip install ai-token-tracker[all]
26
+ ```
27
+
28
+ ## Defaults
29
+
30
+ - API base URL: `http://localhost:8082` (internal, hardcoded)
31
+ - `enable_auto_interception=True`
32
+ - `enable_diagnostics_fallback=True`
33
+ - Ingest auth header (internal): `X-API-Key`
34
+ - Ingest path (internal): `/ingest`
35
+
36
+ ## Quick Start (Plain Script)
37
+
38
+ ```python
39
+ from ai_token_tracker import (
40
+ AiTokenTrackerIngestionClient,
41
+ AiTokenTrackerOptions,
42
+ AiTokenTrackerSdkClient,
43
+ )
44
+
45
+ ingestion = AiTokenTrackerIngestionClient(
46
+ AiTokenTrackerOptions(auth_token="atk_your_key")
47
+ )
48
+
49
+ sdk = AiTokenTrackerSdkClient(ingestion)
50
+
51
+ scope = sdk.begin_llm_call(
52
+ provider_hint="openai",
53
+ method="POST",
54
+ url="https://api.openai.com/v1/responses",
55
+ request={"model": "gpt-4.1-mini", "input": "hello"},
56
+ custom_filters={"workflow": "quickstart"},
57
+ )
58
+
59
+ try:
60
+ provider_response = {"id": "resp_123", "output": [{"type": "output_text", "text": "hi"}]}
61
+ scope.complete(provider_response, status_code=200)
62
+ except Exception as ex:
63
+ scope.fail(ex, status_code=500)
64
+ raise
65
+ finally:
66
+ ingestion.close()
67
+ ```
68
+
69
+ ## Automatic HTTP Interception
70
+
71
+ Interception is explicit install + option-gated:
72
+
73
+ ```python
74
+ import requests
75
+ from ai_token_tracker import (
76
+ AiTokenTrackerIngestionClient,
77
+ AiTokenTrackerOptions,
78
+ install_http_interception,
79
+ )
80
+
81
+ client = AiTokenTrackerIngestionClient(
82
+ AiTokenTrackerOptions(
83
+ auth_token="atk_your_key",
84
+ enable_auto_interception=True,
85
+ )
86
+ )
87
+
88
+ install_http_interception(client)
89
+
90
+ # Classified request is captured and ingested automatically.
91
+ requests.post(
92
+ "https://api.openai.com/v1/responses",
93
+ headers={"Authorization": "Bearer sk_test"},
94
+ json={"model": "gpt-4.1-mini", "input": "hello"},
95
+ timeout=10,
96
+ )
97
+
98
+ client.close() # unpatches interceptors + shuts down background worker
99
+ ```
100
+
101
+ ### Scoped Custom Filters For Automatic Interception
102
+
103
+ Use interception scope to attach custom filters without wrapper integration:
104
+
105
+ ```python
106
+ import requests
107
+ from ai_token_tracker import (
108
+ AiTokenTrackerIngestionClient,
109
+ AiTokenTrackerInterceptionScope,
110
+ AiTokenTrackerOptions,
111
+ install_http_interception,
112
+ )
113
+
114
+ client = AiTokenTrackerIngestionClient(
115
+ AiTokenTrackerOptions(auth_token="atk_your_key", enable_auto_interception=True)
116
+ )
117
+ install_http_interception(client)
118
+
119
+ scope = AiTokenTrackerInterceptionScope.create({"workflow": "social_post_generation"})
120
+ scope.add_custom_filters({"job_id": "job-42", "customer_id": "cust-19"})
121
+
122
+ scope.open()
123
+ try:
124
+ requests.post(
125
+ "https://api.openai.com/v1/responses",
126
+ headers={"Authorization": "Bearer sk_test"},
127
+ json={"model": "gpt-4.1-mini", "input": "hello"},
128
+ timeout=10,
129
+ )
130
+ finally:
131
+ scope.close()
132
+ ```
133
+
134
+ Always close scope in `finally` to avoid leaking filters to later calls.
135
+
136
+ ## SDK Wrapper Example
137
+
138
+ ```python
139
+ from ai_token_tracker import AiTokenTrackerIngestionClient, AiTokenTrackerOptions, AiTokenTrackerSdkClient
140
+
141
+ ingestion = AiTokenTrackerIngestionClient(AiTokenTrackerOptions(auth_token="atk_your_key"))
142
+ tracker = AiTokenTrackerSdkClient(ingestion)
143
+
144
+ scope = tracker.begin_llm_call(
145
+ provider_hint="anthropic",
146
+ method="POST",
147
+ url="https://api.anthropic.com/v1/messages",
148
+ request={"model": "claude-sonnet-4-6", "max_tokens": 256, "messages": [{"role": "user", "content": "hello"}]},
149
+ custom_filters={"tenant": "acme", "job": "summarization"},
150
+ )
151
+
152
+ scope.complete({"id": "msg_123"}, status_code=200)
153
+ ingestion.close()
154
+ ```
155
+
156
+ ## Diagnostics Fallback Setup
157
+
158
+ Diagnostics fallback is best-effort metadata capture for uncovered traffic.
159
+
160
+ ```python
161
+ from ai_token_tracker import AiTokenTrackerIngestionClient, AiTokenTrackerOptions, install_http_interception
162
+
163
+ client = AiTokenTrackerIngestionClient(
164
+ AiTokenTrackerOptions(
165
+ auth_token="atk_your_key",
166
+ enable_diagnostics_fallback=True,
167
+ )
168
+ )
169
+ install_http_interception(client)
170
+ ```
171
+
172
+ Fallback behavior:
173
+ - captures method, url, status, headers
174
+ - does not guarantee request/response bodies
175
+ - intended for visibility and coverage-gap detection, not full fidelity auditing
176
+
177
+ ## FastAPI Example
178
+
179
+ ```python
180
+ from contextlib import asynccontextmanager
181
+ from fastapi import FastAPI
182
+ from ai_token_tracker import (
183
+ AiTokenTrackerIngestionClient,
184
+ AiTokenTrackerOptions,
185
+ AiTokenTrackerSdkClient,
186
+ install_http_interception,
187
+ )
188
+
189
+ ingestion = AiTokenTrackerIngestionClient(
190
+ AiTokenTrackerOptions(auth_token="atk_your_key", enable_auto_interception=True)
191
+ )
192
+ tracker = AiTokenTrackerSdkClient(ingestion)
193
+
194
+ @asynccontextmanager
195
+ async def lifespan(app: FastAPI):
196
+ install_http_interception(ingestion)
197
+ yield
198
+ ingestion.close()
199
+
200
+ app = FastAPI(lifespan=lifespan)
201
+
202
+ @app.get("/health")
203
+ def health() -> dict[str, str]:
204
+ return {"status": "ok"}
205
+ ```
206
+
207
+ ## Background Worker / Job Pipeline Example
208
+
209
+ ```python
210
+ from ai_token_tracker import AiTokenTrackerIngestionClient, AiTokenTrackerOptions, AiTokenTrackerSdkClient
211
+
212
+ ingestion = AiTokenTrackerIngestionClient(AiTokenTrackerOptions(auth_token="atk_your_key"))
213
+ tracker = AiTokenTrackerSdkClient(ingestion)
214
+
215
+ def run_job(job_id: str, prompt: str) -> None:
216
+ scope = tracker.begin_llm_call(
217
+ provider_hint="openai",
218
+ method="POST",
219
+ url="https://api.openai.com/v1/responses",
220
+ request={"model": "gpt-4.1-mini", "input": prompt},
221
+ custom_filters={"job_id": job_id, "pipeline": "nightly"},
222
+ )
223
+ try:
224
+ response = {"id": f"resp_{job_id}", "output": [{"type": "output_text", "text": "done"}]}
225
+ scope.complete(response)
226
+ except Exception as ex:
227
+ scope.fail(ex)
228
+ raise
229
+
230
+ run_job("42", "summarize daily usage")
231
+ ingestion.close()
232
+ ```
233
+
234
+ ## Custom Filters Guidance
235
+
236
+ `custom_filters` is key/value metadata attached to each envelope:
237
+ - keep keys stable (`tenant`, `workflow`, `job_id`, `environment`)
238
+ - keep values low-cardinality when possible
239
+ - avoid putting secrets or full prompt bodies in filters
240
+
241
+ ## Verification Checklist
242
+
243
+ 1. Use SDK with valid `auth_token` (ingest URL is internal and fixed by SDK).
244
+ 2. Create client with valid `auth_token`.
245
+ 3. For interception, set option flag and call `install_http_interception(client)`.
246
+ 4. Send known LLM request (`/v1/responses` or `/chat/completions`).
247
+ 5. Confirm ingest API receives `POST /ingest` with `X-API-Key` and expected envelope JSON.
248
+ 6. Confirm app flow still works if ingest endpoint is unavailable (tracking paths are non-throwing).
249
+
250
+ ## Error Handling + Logging
251
+
252
+ - `track(...)` and scoped `complete(...)` / `fail(...)` are non-throwing for ingest failures.
253
+ - Warnings logged for ingestion failures and non-2xx ingest status.
254
+ - Debug logs used for interception/fallback internal capture issues.
255
+
256
+ ## Public API Summary
257
+
258
+ - `AiTokenTrackerOptions(auth_token, enable_auto_interception=True, enable_diagnostics_fallback=True)`
259
+ - `AiTokenTrackerIngestionClient.track(...) -> TrackResult`
260
+ - `AiTokenTrackerIngestionClient.track_async(...) -> TrackResult`
261
+ - `AiTokenTrackerIngestionClient.close()`
262
+ - `AiTokenTrackerSdkClient.begin_llm_call(...) -> CallScope`
263
+ - `CallScope.complete(...)`, `CallScope.fail(...)`
264
+ - `CallScope.complete_async(...)`, `CallScope.fail_async(...)`
265
+ - `install_http_interception(client)`, `uninstall_http_interception(client)`
@@ -0,0 +1,61 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ai-token-tracker"
7
+ version = "0.1.0"
8
+ description = "Python SDK for capturing LLM traffic and ingesting envelopes into Ai Token Tracker"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "Ai Token Tracker" }
14
+ ]
15
+ keywords = ["llm", "observability", "openai", "anthropic", "sdk"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Software Development :: Libraries :: Python Modules",
26
+ "Typing :: Typed"
27
+ ]
28
+ dependencies = []
29
+
30
+ [project.optional-dependencies]
31
+ requests = ["requests>=2.31"]
32
+ httpx = ["httpx>=0.27"]
33
+ all = [
34
+ "requests>=2.31",
35
+ "httpx>=0.27"
36
+ ]
37
+ dev = [
38
+ "pytest>=8.2",
39
+ "requests>=2.31",
40
+ "httpx>=0.27"
41
+ ]
42
+
43
+ [project.urls]
44
+ Homepage = "https://github.com/ai-token-tracker/ai-token-tracker"
45
+ Repository = "https://github.com/ai-token-tracker/ai-token-tracker"
46
+ Documentation = "https://github.com/ai-token-tracker/ai-token-tracker/tree/main/sdks/python"
47
+ Issues = "https://github.com/ai-token-tracker/ai-token-tracker/issues"
48
+
49
+ [tool.setuptools]
50
+ package-dir = { "" = "src" }
51
+ include-package-data = true
52
+
53
+ [tool.setuptools.packages.find]
54
+ where = ["src"]
55
+
56
+ [tool.setuptools.package-data]
57
+ ai_token_tracker = ["py.typed"]
58
+
59
+ [tool.pytest.ini_options]
60
+ testpaths = ["tests"]
61
+ addopts = "-ra"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,33 @@
1
+ """Ai Token Tracker Python SDK."""
2
+
3
+ from .classifier import DefaultLlmRequestClassifier
4
+ from .envelope import map_tracked_call_to_envelope
5
+ from .ingestion_client import AiTokenTrackerIngestionClient
6
+ from .interception_scope import AiTokenTrackerInterceptionScope
7
+ from .runtime import install_http_interception, uninstall_http_interception
8
+ from .sdk_client import AiTokenTrackerSdkClient, CallScope
9
+ from .types import (
10
+ AiTokenTrackerOptions,
11
+ IngestionEnvelope,
12
+ LlmRequestClassificationInput,
13
+ LlmRequestClassifier,
14
+ TrackResult,
15
+ TrackedLlmCall,
16
+ )
17
+
18
+ __all__ = [
19
+ "AiTokenTrackerIngestionClient",
20
+ "AiTokenTrackerOptions",
21
+ "AiTokenTrackerSdkClient",
22
+ "AiTokenTrackerInterceptionScope",
23
+ "CallScope",
24
+ "DefaultLlmRequestClassifier",
25
+ "IngestionEnvelope",
26
+ "LlmRequestClassificationInput",
27
+ "LlmRequestClassifier",
28
+ "TrackResult",
29
+ "TrackedLlmCall",
30
+ "install_http_interception",
31
+ "map_tracked_call_to_envelope",
32
+ "uninstall_http_interception",
33
+ ]