agentracer 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,53 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ id-token: write
9
+
10
+ jobs:
11
+ test:
12
+ name: Run tests
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ matrix:
16
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python ${{ matrix.python-version }}
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+
25
+ - name: Install dependencies
26
+ run: pip install -e ".[dev]"
27
+
28
+ - name: Run tests
29
+ run: pytest tests/ -v
30
+
31
+ publish:
32
+ name: Build and publish
33
+ needs: test
34
+ runs-on: ubuntu-latest
35
+ environment: pypi
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+
39
+ - name: Set up Python
40
+ uses: actions/setup-python@v5
41
+ with:
42
+ python-version: "3.12"
43
+
44
+ - name: Install build tools
45
+ run: pip install build
46
+
47
+ - name: Build package
48
+ run: python -m build
49
+
50
+ - name: Publish to PyPI
51
+ uses: pypa/gh-action-pypi-publish@release/v1
52
+ with:
53
+ password: ${{ secrets.PYPI_API_TOKEN }}
@@ -0,0 +1,5 @@
1
+ __pycache__/
2
+ dist/
3
+ *.egg-info/
4
+ .pytest_cache/
5
+ build/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Agentracer
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,567 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentracer
3
+ Version: 0.1.0
4
+ Summary: Lightweight AI incident detection. Catch cost spikes, latency anomalies, and prompt bloat before they hit your users.
5
+ Project-URL: Homepage, https://agentracer.dev
6
+ Project-URL: Repository, https://github.com/aqib-ilyas/agentracer-python
7
+ Project-URL: Documentation, https://agentracer.dev/docs
8
+ Author-email: Agentracer <hello@agentracer.dev>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai,anthropic,cost-tracking,gemini,llm,observability,openai
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.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.9
24
+ Requires-Dist: httpx>=0.24.0
25
+ Provides-Extra: all
26
+ Requires-Dist: anthropic>=0.18.0; extra == 'all'
27
+ Requires-Dist: google-generativeai>=0.3.0; extra == 'all'
28
+ Requires-Dist: openai>=1.0.0; extra == 'all'
29
+ Provides-Extra: anthropic
30
+ Requires-Dist: anthropic>=0.18.0; extra == 'anthropic'
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
33
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
34
+ Provides-Extra: gemini
35
+ Requires-Dist: google-generativeai>=0.3.0; extra == 'gemini'
36
+ Provides-Extra: openai
37
+ Requires-Dist: openai>=1.0.0; extra == 'openai'
38
+ Description-Content-Type: text/markdown
39
+
40
+ # Agentracer Python SDK
41
+
42
+ Lightweight AI observability. Catch cost spikes, latency anomalies, and prompt bloat before they hit your users.
43
+
44
+ [![PyPI version](https://badge.fury.io/py/agentracer.svg)](https://pypi.org/project/agentracer/)
45
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
46
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
47
+
48
+ ## Installation
49
+
50
+ ```bash
51
+ pip install agentracer
52
+ ```
53
+
54
+ With provider-specific extras:
55
+
56
+ ```bash
57
+ # OpenAI support
58
+ pip install agentracer[openai]
59
+
60
+ # Anthropic support
61
+ pip install agentracer[anthropic]
62
+
63
+ # Google Gemini support
64
+ pip install agentracer[gemini]
65
+
66
+ # All providers
67
+ pip install agentracer[all]
68
+ ```
69
+
70
+ ## Quick Start
71
+
72
+ ```python
73
+ import agentracer
74
+
75
+ # Initialize once at app startup
76
+ agentracer.init(
77
+ tracker_api_key="at_your_api_key",
78
+ project_id="your_project_id",
79
+ environment="production"
80
+ )
81
+ ```
82
+
83
+ ## Usage
84
+
85
+ ### OpenAI
86
+
87
+ ```python
88
+ from agentracer.openai import openai
89
+
90
+ # Use exactly like the normal OpenAI client
91
+ response = openai.chat.completions.create(
92
+ model="gpt-4o",
93
+ messages=[{"role": "user", "content": "Hello!"}],
94
+ feature_tag="chatbot" # optional: tag this call
95
+ )
96
+ ```
97
+
98
+ ### Anthropic
99
+
100
+ ```python
101
+ from agentracer.anthropic import anthropic
102
+
103
+ response = anthropic.messages.create(
104
+ model="claude-sonnet-4-20250514",
105
+ max_tokens=1024,
106
+ messages=[{"role": "user", "content": "Hello!"}],
107
+ feature_tag="summarizer" # optional: tag this call
108
+ )
109
+ ```
110
+
111
+ ### Google Gemini
112
+
113
+ ```python
114
+ import google.generativeai as genai
115
+ from agentracer.gemini import gemini
116
+
117
+ # Configure your API key as usual
118
+ genai.configure(api_key="your-gemini-api-key")
119
+
120
+ model = gemini.GenerativeModel("gemini-1.5-pro")
121
+ response = model.generate_content(
122
+ "Hello!",
123
+ feature_tag="content-gen" # optional: tag this call
124
+ )
125
+ ```
126
+
127
+ ## Custom Client Configuration
128
+
129
+ The default imports (`openai`, `anthropic`) create clients using environment variables. To pass custom configuration like `api_key` or `base_url`, use the class constructors:
130
+
131
+ ### OpenAI
132
+
133
+ ```python
134
+ from agentracer.openai import TrackedOpenAI
135
+
136
+ openai = TrackedOpenAI(api_key="sk-...", base_url="https://custom.api/v1")
137
+
138
+ response = openai.chat.completions.create(
139
+ model="gpt-4o",
140
+ messages=[{"role": "user", "content": "Hello!"}]
141
+ )
142
+ ```
143
+
144
+ ### Anthropic
145
+
146
+ ```python
147
+ from agentracer.anthropic import TrackedAnthropic
148
+
149
+ anthropic = TrackedAnthropic(api_key="sk-ant-...")
150
+
151
+ response = anthropic.messages.create(
152
+ model="claude-sonnet-4-20250514",
153
+ max_tokens=1024,
154
+ messages=[{"role": "user", "content": "Hello!"}]
155
+ )
156
+ ```
157
+
158
+ ## Async Support
159
+
160
+ For async applications, use the async client wrappers:
161
+
162
+ ### Async OpenAI
163
+
164
+ ```python
165
+ from agentracer.openai import TrackedAsyncOpenAI
166
+
167
+ client = TrackedAsyncOpenAI()
168
+
169
+ async def chat(message: str):
170
+ response = await client.chat.completions.create(
171
+ model="gpt-4o",
172
+ messages=[{"role": "user", "content": message}],
173
+ feature_tag="async-chatbot"
174
+ )
175
+ return response.choices[0].message.content
176
+ ```
177
+
178
+ ### Async Anthropic
179
+
180
+ ```python
181
+ from agentracer.anthropic import TrackedAsyncAnthropic
182
+
183
+ client = TrackedAsyncAnthropic()
184
+
185
+ async def chat(message: str):
186
+ response = await client.messages.create(
187
+ model="claude-sonnet-4-20250514",
188
+ max_tokens=1024,
189
+ messages=[{"role": "user", "content": message}],
190
+ feature_tag="async-summarizer"
191
+ )
192
+ return response.content[0].text
193
+ ```
194
+
195
+ ## Streaming
196
+
197
+ All providers support streaming. Token usage is automatically tracked after the stream completes.
198
+
199
+ ### OpenAI Streaming
200
+
201
+ ```python
202
+ from agentracer.openai import openai
203
+
204
+ stream = openai.chat.completions.create(
205
+ model="gpt-4o",
206
+ messages=[{"role": "user", "content": "Write a poem"}],
207
+ stream=True,
208
+ feature_tag="poet"
209
+ )
210
+
211
+ for chunk in stream:
212
+ content = chunk.choices[0].delta.content
213
+ if content:
214
+ print(content, end="")
215
+ # Telemetry is sent automatically after the stream ends
216
+ ```
217
+
218
+ ### Anthropic Streaming
219
+
220
+ ```python
221
+ from agentracer.anthropic import anthropic
222
+
223
+ stream = anthropic.messages.create(
224
+ model="claude-sonnet-4-20250514",
225
+ max_tokens=1024,
226
+ messages=[{"role": "user", "content": "Write a poem"}],
227
+ stream=True,
228
+ feature_tag="poet"
229
+ )
230
+
231
+ for event in stream:
232
+ if event.type == "content_block_delta":
233
+ print(event.delta.text, end="")
234
+ ```
235
+
236
+ ### Gemini Streaming
237
+
238
+ ```python
239
+ from agentracer.gemini import gemini
240
+
241
+ model = gemini.GenerativeModel("gemini-1.5-pro")
242
+
243
+ stream = model.generate_content(
244
+ "Write a poem",
245
+ stream=True,
246
+ feature_tag="poet"
247
+ )
248
+
249
+ for chunk in stream:
250
+ print(chunk.text, end="")
251
+ ```
252
+
253
+ > Streaming works transparently -- usage is captured from the final chunk (OpenAI), SSE events (Anthropic), or chunk metadata (Gemini), then sent as a single telemetry event after the stream finishes.
254
+
255
+ ## Feature Tags
256
+
257
+ Feature tags let you group and track costs by feature across your application. There are three ways to apply them:
258
+
259
+ ### 1. Inline (per-call)
260
+
261
+ Pass `feature_tag` directly on any tracked call:
262
+
263
+ ```python
264
+ response = openai.chat.completions.create(
265
+ model="gpt-4o",
266
+ messages=[{"role": "user", "content": "Summarize this"}],
267
+ feature_tag="summarizer"
268
+ )
269
+ ```
270
+
271
+ ### 2. Decorator
272
+
273
+ Use the `@observe` decorator to tag all LLM calls inside a function:
274
+
275
+ ```python
276
+ import agentracer
277
+
278
+ @agentracer.observe(feature_tag="chatbot")
279
+ def handle_chat(message: str):
280
+ response = openai.chat.completions.create(
281
+ model="gpt-4o",
282
+ messages=[{"role": "user", "content": message}]
283
+ )
284
+ return response
285
+
286
+ # Async functions work too
287
+ @agentracer.observe(feature_tag="async-chatbot")
288
+ async def handle_chat_async(message: str):
289
+ response = await client.chat.completions.create(
290
+ model="gpt-4o",
291
+ messages=[{"role": "user", "content": message}]
292
+ )
293
+ return response
294
+ ```
295
+
296
+ ### 3. Context Manager
297
+
298
+ Use `feature_context` for block-level tagging:
299
+
300
+ ```python
301
+ from agentracer import feature_context
302
+
303
+ with feature_context("report-generator"):
304
+ # All LLM calls in this block get tagged "report-generator"
305
+ summary = openai.chat.completions.create(
306
+ model="gpt-4o",
307
+ messages=[{"role": "user", "content": "Summarize the data"}]
308
+ )
309
+ analysis = anthropic.messages.create(
310
+ model="claude-sonnet-4-20250514",
311
+ max_tokens=2048,
312
+ messages=[{"role": "user", "content": "Analyze trends"}]
313
+ )
314
+ ```
315
+
316
+ ## Agent Runs
317
+
318
+ Track multi-step agent workflows as a single run with `AgentRun`. Each LLM call inside the run is automatically recorded as a step.
319
+
320
+ ```python
321
+ from agentracer import AgentRun
322
+ from agentracer.openai import openai
323
+
324
+ with AgentRun(run_name="research-agent", feature_tag="research") as run:
325
+ # Step 1: Plan
326
+ plan = openai.chat.completions.create(
327
+ model="gpt-4o",
328
+ messages=[{"role": "user", "content": "Plan a research outline"}]
329
+ )
330
+
331
+ # Step 2: Execute
332
+ result = openai.chat.completions.create(
333
+ model="gpt-4o",
334
+ messages=[{"role": "user", "content": f"Research: {plan.choices[0].message.content}"}]
335
+ )
336
+
337
+ # Step 3: Summarize
338
+ summary = openai.chat.completions.create(
339
+ model="gpt-4o-mini",
340
+ messages=[{"role": "user", "content": f"Summarize: {result.choices[0].message.content}"}]
341
+ )
342
+ # All 3 steps are tracked under a single run with timing, tokens, and status
343
+ ```
344
+
345
+ ### Async Agent Runs
346
+
347
+ `AgentRun` also works as an async context manager:
348
+
349
+ ```python
350
+ from agentracer import AgentRun
351
+ from agentracer.openai import TrackedAsyncOpenAI
352
+
353
+ client = TrackedAsyncOpenAI()
354
+
355
+ async def run_agent(query: str):
356
+ async with AgentRun(run_name="async-agent", feature_tag="agent") as run:
357
+ response = await client.chat.completions.create(
358
+ model="gpt-4o",
359
+ messages=[{"role": "user", "content": query}]
360
+ )
361
+ return response.choices[0].message.content
362
+ ```
363
+
364
+ ### AgentRun Parameters
365
+
366
+ | Parameter | Type | Default | Description |
367
+ |-----------|------|---------|-------------|
368
+ | `run_name` | `str` | `None` | Name for this run (shown in dashboard) |
369
+ | `feature_tag` | `str` | `"unknown"` | Feature tag applied to all steps |
370
+ | `end_user_id` | `str` | `None` | ID of the end user triggering this run |
371
+ | `run_id` | `str` | auto-generated | Custom run ID (UUID v4 generated if omitted) |
372
+
373
+ ### What gets tracked per run
374
+
375
+ - **Start/end timestamps** and total duration
376
+ - **Status**: `completed` or `failed` (auto-detected from exceptions)
377
+ - **Each step**: model, provider, tokens, latency, success/failure
378
+ - **Aggregates**: total steps, total tokens, total cost, total latency
379
+
380
+ ## Manual Tracking
381
+
382
+ For custom integrations or providers not yet supported, use `track()` directly:
383
+
384
+ ```python
385
+ import agentracer
386
+
387
+ agentracer.track(
388
+ model="gpt-4o",
389
+ input_tokens=150,
390
+ output_tokens=300,
391
+ latency_ms=420.5,
392
+ feature_tag="custom-pipeline",
393
+ provider="openai",
394
+ end_user_id="user-123", # optional: track per-user costs
395
+ cached_tokens=50, # optional: cached/prompt-cache tokens
396
+ )
397
+ ```
398
+
399
+ ## FastAPI Example
400
+
401
+ ```python
402
+ from fastapi import FastAPI
403
+ import agentracer
404
+ from agentracer.openai import TrackedAsyncOpenAI
405
+
406
+ app = FastAPI()
407
+ client = TrackedAsyncOpenAI()
408
+
409
+ agentracer.init(
410
+ tracker_api_key="at_your_api_key",
411
+ project_id="your_project_id",
412
+ environment="production"
413
+ )
414
+
415
+ @app.post("/chat")
416
+ @agentracer.observe(feature_tag="chatbot")
417
+ async def chat(message: str):
418
+ response = await client.chat.completions.create(
419
+ model="gpt-4o",
420
+ messages=[{"role": "user", "content": message}]
421
+ )
422
+ return {"reply": response.choices[0].message.content}
423
+
424
+ @app.post("/summarize")
425
+ async def summarize(text: str):
426
+ with agentracer.feature_context("summarizer"):
427
+ response = await client.chat.completions.create(
428
+ model="gpt-4o-mini",
429
+ messages=[{"role": "user", "content": f"Summarize: {text}"}]
430
+ )
431
+ return {"summary": response.choices[0].message.content}
432
+
433
+ @app.post("/agent")
434
+ async def agent(query: str):
435
+ async with agentracer.AgentRun(run_name="qa-agent", feature_tag="agent") as run:
436
+ response = await client.chat.completions.create(
437
+ model="gpt-4o",
438
+ messages=[{"role": "user", "content": query}]
439
+ )
440
+ return {"answer": response.choices[0].message.content}
441
+ ```
442
+
443
+ ## Django Example
444
+
445
+ ```python
446
+ # settings.py
447
+ import agentracer
448
+
449
+ agentracer.init(
450
+ tracker_api_key="at_your_api_key",
451
+ project_id="your_project_id",
452
+ environment="production"
453
+ )
454
+
455
+ # views.py
456
+ from agentracer.openai import openai
457
+ from agentracer import feature_context, AgentRun
458
+
459
+ def chat_view(request):
460
+ message = request.POST.get("message")
461
+
462
+ with feature_context("django-chatbot"):
463
+ response = openai.chat.completions.create(
464
+ model="gpt-4o",
465
+ messages=[{"role": "user", "content": message}]
466
+ )
467
+
468
+ return JsonResponse({"reply": response.choices[0].message.content})
469
+
470
+ def agent_view(request):
471
+ query = request.POST.get("query")
472
+
473
+ with AgentRun(run_name="django-agent", feature_tag="agent"):
474
+ response = openai.chat.completions.create(
475
+ model="gpt-4o",
476
+ messages=[{"role": "user", "content": query}]
477
+ )
478
+
479
+ return JsonResponse({"answer": response.choices[0].message.content})
480
+ ```
481
+
482
+ ## Configuration
483
+
484
+ ```python
485
+ agentracer.init(
486
+ tracker_api_key="at_your_api_key", # Required: your API key from the dashboard
487
+ project_id="your_project_id", # Required: your project ID
488
+ environment="production", # Optional: environment name (default: "production")
489
+ host="https://api.agentracer.dev", # Optional: API host (default: production)
490
+ debug=False, # Optional: enable debug logging (default: False)
491
+ enabled=True # Optional: enable/disable tracking (default: True)
492
+ )
493
+ ```
494
+
495
+ | Parameter | Type | Default | Description |
496
+ |-----------|------|---------|-------------|
497
+ | `tracker_api_key` | `str` | *required* | Your API key from the Agentracer dashboard |
498
+ | `project_id` | `str` | *required* | Your project ID |
499
+ | `environment` | `str` | `"production"` | Environment name (e.g., `"staging"`, `"development"`) |
500
+ | `host` | `str` | `"https://api.agentracer.dev"` | API endpoint |
501
+ | `debug` | `bool` | `False` | Print telemetry payloads to console |
502
+ | `enabled` | `bool` | `True` | Set to `False` to disable all tracking |
503
+
504
+ ## What We Track
505
+
506
+ Every LLM call automatically captures:
507
+
508
+ | Field | Description |
509
+ |-------|-------------|
510
+ | `project_id` | Your project identifier |
511
+ | `provider` | LLM provider (`openai`, `anthropic`, `gemini`, `custom`) |
512
+ | `model` | Model name (e.g., `gpt-4o`, `claude-sonnet-4-20250514`) |
513
+ | `feature_tag` | Feature label for cost grouping |
514
+ | `input_tokens` | Number of input/prompt tokens |
515
+ | `output_tokens` | Number of output/completion tokens |
516
+ | `cached_tokens` | Prompt-cached tokens (OpenAI, Anthropic, Gemini) |
517
+ | `latency_ms` | Request latency in milliseconds |
518
+ | `success` | Whether the call succeeded |
519
+ | `error_type` | Exception class name on failure |
520
+ | `environment` | Deployment environment |
521
+ | `end_user_id` | End user identifier (when provided) |
522
+
523
+ We **never** capture prompts, responses, or any PII. Only metadata and usage metrics.
524
+
525
+ ## Troubleshooting
526
+
527
+ ### Telemetry not appearing in the dashboard
528
+
529
+ 1. **Check your API key and project ID** -- ensure they match what's in your [dashboard](https://agentracer.dev/dashboard).
530
+ 2. **Enable debug mode** to see what's being sent:
531
+ ```python
532
+ agentracer.init(
533
+ tracker_api_key="at_your_api_key",
534
+ project_id="your_project_id",
535
+ debug=True
536
+ )
537
+ ```
538
+ 3. **Check that tracking is enabled** -- make sure you haven't set `enabled=False`.
539
+ 4. **Verify network connectivity** -- the SDK sends data to `https://api.agentracer.dev/api/ingest`.
540
+
541
+ ### Import errors for provider modules
542
+
543
+ If you see `ImportError: openai package not installed`, install the provider extra:
544
+
545
+ ```bash
546
+ pip install agentracer[openai]
547
+ # or
548
+ pip install agentracer[anthropic]
549
+ # or
550
+ pip install agentracer[gemini]
551
+ ```
552
+
553
+ ### Feature tags showing as "unknown"
554
+
555
+ Make sure you're setting feature tags using one of the three methods:
556
+
557
+ - Inline: `feature_tag="my-feature"` on the call
558
+ - Decorator: `@agentracer.observe(feature_tag="my-feature")`
559
+ - Context manager: `with agentracer.feature_context("my-feature"):`
560
+
561
+ ### SDK not throwing errors
562
+
563
+ This is by design. The SDK uses a fire-and-forget pattern and silently catches all exceptions to ensure it never impacts your application's performance or reliability.
564
+
565
+ ## License
566
+
567
+ MIT