agentreplay 0.1.2__tar.gz → 0.1.3__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 (47) hide show
  1. agentreplay-0.1.3/PKG-INFO +1138 -0
  2. agentreplay-0.1.3/README.md +1039 -0
  3. {agentreplay-0.1.2 → agentreplay-0.1.3}/pyproject.toml +16 -11
  4. agentreplay-0.1.3/src/agentreplay/__init__.py +223 -0
  5. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/context.py +133 -0
  6. agentreplay-0.1.3/src/agentreplay/decorators.py +545 -0
  7. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/install_pth.py +6 -2
  8. agentreplay-0.1.3/src/agentreplay/privacy.py +452 -0
  9. agentreplay-0.1.3/src/agentreplay/sdk.py +578 -0
  10. agentreplay-0.1.3/src/agentreplay/wrappers.py +522 -0
  11. agentreplay-0.1.3/src/agentreplay.egg-info/PKG-INFO +1138 -0
  12. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay.egg-info/SOURCES.txt +4 -0
  13. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay.egg-info/requires.txt +10 -5
  14. agentreplay-0.1.2/PKG-INFO +0 -285
  15. agentreplay-0.1.2/README.md +0 -191
  16. agentreplay-0.1.2/src/agentreplay/__init__.py +0 -81
  17. agentreplay-0.1.2/src/agentreplay.egg-info/PKG-INFO +0 -285
  18. {agentreplay-0.1.2 → agentreplay-0.1.3}/LICENSE +0 -0
  19. {agentreplay-0.1.2 → agentreplay-0.1.3}/setup.cfg +0 -0
  20. {agentreplay-0.1.2 → agentreplay-0.1.3}/setup.py +0 -0
  21. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/auto_instrument/__init__.py +0 -0
  22. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/auto_instrument/openai.py +0 -0
  23. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/batching.py +0 -0
  24. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/bootstrap.py +0 -0
  25. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/circuit_breaker.py +0 -0
  26. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/client.py +0 -0
  27. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/config.py +0 -0
  28. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/env_config.py +0 -0
  29. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/env_init.py +0 -0
  30. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/exceptions.py +0 -0
  31. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/genai.py +0 -0
  32. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/genai_conventions.py +0 -0
  33. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/langchain_tracer.py +0 -0
  34. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/models.py +0 -0
  35. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/otel_bridge.py +0 -0
  36. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/patch.py +0 -0
  37. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/propagation.py +0 -0
  38. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/py.typed +0 -0
  39. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/retry.py +0 -0
  40. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/sampling.py +0 -0
  41. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/session.py +0 -0
  42. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/sitecustomize.py +0 -0
  43. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/span.py +0 -0
  44. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay/unified.py +0 -0
  45. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay.egg-info/dependency_links.txt +0 -0
  46. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay.egg-info/entry_points.txt +0 -0
  47. {agentreplay-0.1.2 → agentreplay-0.1.3}/src/agentreplay.egg-info/top_level.txt +0 -0
@@ -0,0 +1,1138 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentreplay
3
+ Version: 0.1.3
4
+ Summary: Agentreplay is a high-performance observability and tracing platform for LLM agents and AI apps, combining semantic search, specialized evals (RAGAS, G-Eval, toxicity), and Git-like versioning of prompts and responses.
5
+ Author-email: Sushanth <sushanth@agentreplay.dev>
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://agentreplay.dev
8
+ Project-URL: Documentation, https://agentreplay.dev
9
+ Project-URL: Repository, https://github.com/agentreplay/agentreplay
10
+ Project-URL: Issues, https://github.com/agentreplay/agentreplay/issues
11
+ Project-URL: Changelog, https://github.com/agentreplay/agentreplay/blob/main/CHANGELOG.md
12
+ Keywords: llm,agents,tracing,observability,ai,opentelemetry,ragas,evaluation,prompts,semantic-search,langchain,openai
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: Apache Software License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Topic :: System :: Monitoring
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.8
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: pydantic>=2.0.0
29
+ Requires-Dist: httpx>=0.24.0
30
+ Provides-Extra: otel
31
+ Requires-Dist: opentelemetry-api>=1.20.0; extra == "otel"
32
+ Requires-Dist: opentelemetry-sdk>=1.20.0; extra == "otel"
33
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.20.0; extra == "otel"
34
+ Requires-Dist: opentelemetry-instrumentation>=0.41b0; extra == "otel"
35
+ Requires-Dist: opentelemetry-instrumentation-openai>=0.48.0; extra == "otel"
36
+ Provides-Extra: dev
37
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
38
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
39
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
40
+ Requires-Dist: black>=23.0.0; extra == "dev"
41
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
42
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
43
+ Requires-Dist: bandit>=1.7.0; extra == "dev"
44
+ Requires-Dist: types-requests>=2.31.0; extra == "dev"
45
+ Provides-Extra: langchain
46
+ Requires-Dist: langchain>=0.3.0; extra == "langchain"
47
+ Requires-Dist: langchain-openai>=0.2.0; extra == "langchain"
48
+ Provides-Extra: langgraph
49
+ Requires-Dist: langgraph>=0.2.0; extra == "langgraph"
50
+ Requires-Dist: langchain>=0.3.0; extra == "langgraph"
51
+ Provides-Extra: llamaindex
52
+ Requires-Dist: llama-index>=0.11.0; extra == "llamaindex"
53
+ Requires-Dist: llama-index-core>=0.11.0; extra == "llamaindex"
54
+ Provides-Extra: llamaindex-workflows
55
+ Requires-Dist: llama-index>=0.11.0; extra == "llamaindex-workflows"
56
+ Requires-Dist: llama-index-workflows>=0.2.0; extra == "llamaindex-workflows"
57
+ Provides-Extra: openai-agents
58
+ Requires-Dist: openai-agents>=0.5.0; extra == "openai-agents"
59
+ Provides-Extra: autogen
60
+ Requires-Dist: autogen-agentchat>=0.4.0; extra == "autogen"
61
+ Requires-Dist: autogen-core>=0.4.0; extra == "autogen"
62
+ Provides-Extra: semantic-kernel
63
+ Requires-Dist: semantic-kernel>=1.0.0; extra == "semantic-kernel"
64
+ Provides-Extra: crewai
65
+ Requires-Dist: crewai>=0.1.0; extra == "crewai"
66
+ Provides-Extra: smolagents
67
+ Requires-Dist: smolagents>=1.0.0; extra == "smolagents"
68
+ Provides-Extra: pydantic-ai
69
+ Requires-Dist: pydantic-ai>=1.0.0; extra == "pydantic-ai"
70
+ Provides-Extra: strands
71
+ Requires-Dist: strands-sdk>=1.0.0; extra == "strands"
72
+ Provides-Extra: google-adk
73
+ Requires-Dist: google-adk>=1.0.0; extra == "google-adk"
74
+ Provides-Extra: all-frameworks
75
+ Requires-Dist: langchain>=0.3.0; extra == "all-frameworks"
76
+ Requires-Dist: langchain-openai>=0.2.0; extra == "all-frameworks"
77
+ Requires-Dist: langgraph>=0.2.0; extra == "all-frameworks"
78
+ Requires-Dist: llama-index>=0.11.0; extra == "all-frameworks"
79
+ Requires-Dist: openai-agents>=0.5.0; extra == "all-frameworks"
80
+ Requires-Dist: autogen-agentchat>=0.4.0; extra == "all-frameworks"
81
+ Requires-Dist: semantic-kernel>=1.0.0; extra == "all-frameworks"
82
+ Requires-Dist: crewai>=0.1.0; extra == "all-frameworks"
83
+ Requires-Dist: smolagents>=1.0.0; extra == "all-frameworks"
84
+ Requires-Dist: pydantic-ai>=1.0.0; extra == "all-frameworks"
85
+ Provides-Extra: all
86
+ Requires-Dist: langchain>=0.3.0; extra == "all"
87
+ Requires-Dist: langchain-openai>=0.2.0; extra == "all"
88
+ Requires-Dist: langgraph>=0.2.0; extra == "all"
89
+ Requires-Dist: llama-index>=0.11.0; extra == "all"
90
+ Requires-Dist: openai-agents>=0.5.0; extra == "all"
91
+ Requires-Dist: autogen-agentchat>=0.4.0; extra == "all"
92
+ Requires-Dist: semantic-kernel>=1.0.0; extra == "all"
93
+ Requires-Dist: crewai>=0.1.0; extra == "all"
94
+ Requires-Dist: smolagents>=1.0.0; extra == "all"
95
+ Requires-Dist: pydantic-ai>=1.0.0; extra == "all"
96
+ Requires-Dist: pytest>=7.0.0; extra == "all"
97
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "all"
98
+ Dynamic: license-file
99
+
100
+ # Agentreplay Python SDK
101
+
102
+ [![PyPI version](https://badge.fury.io/py/agentreplay.svg)](https://badge.fury.io/py/agentreplay)
103
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
104
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
105
+ [![CI](https://github.com/agentreplay/agentreplay/actions/workflows/ci-python.yaml/badge.svg)](https://github.com/agentreplay/agentreplay/actions/workflows/ci-python.yaml)
106
+
107
+ **The observability platform for LLM agents and AI applications.** Trace every LLM call, tool invocation, and agent step with minimal code changes.
108
+
109
+ ---
110
+
111
+ ## ✨ Features
112
+
113
+ | Feature | Description |
114
+ |---------|-------------|
115
+ | 🚀 **Zero-Config Setup** | Works out of the box with environment variables |
116
+ | 🎯 **One-Liner Instrumentation** | Wrap OpenAI/Anthropic clients in one line |
117
+ | 🔧 **Decorator-Based Tracing** | `@traceable` for any function |
118
+ | 🔄 **Async Native** | Full support for async/await patterns |
119
+ | 🔒 **Privacy First** | Built-in PII redaction and scrubbing |
120
+ | 📊 **Token Tracking** | Automatic token usage capture |
121
+ | 🌐 **Framework Agnostic** | Works with LangChain, LlamaIndex, CrewAI, etc. |
122
+ | ⚡ **Batched Transport** | Efficient background sending with retry |
123
+
124
+ ---
125
+
126
+ ## 📦 Installation
127
+
128
+ ```bash
129
+ # Basic installation (minimal dependencies)
130
+ pip install agentreplay
131
+
132
+ # With OpenTelemetry support
133
+ pip install agentreplay[otel]
134
+
135
+ # With LangChain integration
136
+ pip install agentreplay[langchain]
137
+
138
+ # With LangGraph integration
139
+ pip install agentreplay[langgraph]
140
+
141
+ # With LlamaIndex integration
142
+ pip install agentreplay[llamaindex]
143
+
144
+ # Full installation (all integrations)
145
+ pip install agentreplay[otel,langchain,langgraph,llamaindex]
146
+ ```
147
+
148
+ ---
149
+
150
+ ## 🚀 Quick Start
151
+
152
+ ### 1. Set Environment Variables
153
+
154
+ ```bash
155
+ export AGENTREPLAY_API_KEY="your-api-key"
156
+ export AGENTREPLAY_PROJECT_ID="my-project"
157
+ # Optional
158
+ export AGENTREPLAY_BASE_URL="https://api.agentreplay.io"
159
+ ```
160
+
161
+ ### 2. Enable Auto-Instrumentation
162
+
163
+ After installing the package, run the install command to enable zero-code auto-instrumentation:
164
+
165
+ ```bash
166
+ # Run this once after pip install
167
+ agentreplay-install
168
+ ```
169
+
170
+ This installs a `.pth` file that automatically initializes Agentreplay when Python starts, enabling automatic tracing of OpenAI, Anthropic, and other LLM libraries **without any code changes**.
171
+
172
+ #### Option A: Zero-Code (after running agentreplay-install)
173
+
174
+ ```bash
175
+ # Just run your script - instrumentation happens automatically!
176
+ python my_app.py
177
+ ```
178
+
179
+ #### Option B: Code-Based Initialization
180
+
181
+ Add these two lines at the **very beginning** of your main file, before any other imports:
182
+
183
+ ```python
184
+ import agentreplay
185
+ agentreplay.init() # Must be called before importing OpenAI, Anthropic, etc.
186
+
187
+ # Now import your LLM clients - they're automatically traced!
188
+ from openai import OpenAI
189
+
190
+ client = OpenAI()
191
+ response = client.chat.completions.create(
192
+ model="gpt-4",
193
+ messages=[{"role": "user", "content": "Hello!"}]
194
+ )
195
+ # All OpenAI calls are automatically traced!
196
+
197
+ # Ensure traces are sent before exit
198
+ agentreplay.flush()
199
+ ```
200
+
201
+ > ⚠️ **Important**: `agentreplay.init()` must be called **before** importing OpenAI, Anthropic, or other LLM libraries for auto-instrumentation to work.
202
+
203
+ #### Managing Auto-Instrumentation
204
+
205
+ ```bash
206
+ # Install auto-instrumentation (run once after pip install)
207
+ agentreplay-install
208
+
209
+ # Check if installed
210
+ agentreplay-install --check
211
+
212
+ # Uninstall auto-instrumentation
213
+ agentreplay-install --uninstall
214
+ ```
215
+
216
+ ### 3. Manual Tracing with Decorators
217
+
218
+ ```python
219
+ import agentreplay
220
+
221
+ # Initialize (reads from env vars automatically)
222
+ agentreplay.init()
223
+
224
+ # Trace any function with a simple decorator
225
+ @agentreplay.traceable
226
+ def my_ai_function(query: str) -> str:
227
+ # Your AI logic here
228
+ return f"Response to: {query}"
229
+
230
+ # Call your function - it's automatically traced!
231
+ result = my_ai_function("What is the capital of France?")
232
+
233
+ # Ensure all traces are sent before exit
234
+ agentreplay.flush()
235
+ ```
236
+
237
+ That's it! Your function calls are now being traced and sent to Agentreplay.
238
+
239
+ ---
240
+
241
+ ## 🔧 Core API Reference
242
+
243
+ ### Initialization
244
+
245
+ ```python
246
+ import agentreplay
247
+
248
+ # Option 1: Environment variables (recommended for production)
249
+ agentreplay.init()
250
+
251
+ # Option 2: Explicit configuration
252
+ agentreplay.init(
253
+ api_key="your-api-key",
254
+ project_id="my-project",
255
+ base_url="https://api.agentreplay.io",
256
+
257
+ # Optional settings
258
+ tenant_id="default", # Multi-tenant identifier
259
+ agent_id="default", # Default agent ID
260
+ enabled=True, # Set False to disable in tests
261
+ capture_input=True, # Capture function inputs
262
+ capture_output=True, # Capture function outputs
263
+ batch_size=100, # Batch size before sending
264
+ flush_interval=5.0, # Auto-flush interval in seconds
265
+ debug=False, # Enable debug logging
266
+ )
267
+
268
+ # Check if SDK is initialized
269
+ from agentreplay.sdk import is_initialized
270
+ if is_initialized():
271
+ print("SDK is ready!")
272
+
273
+ # Get current configuration
274
+ config = agentreplay.sdk.get_config()
275
+ print(f"Project: {config.project_id}")
276
+ ```
277
+
278
+ ---
279
+
280
+ ## 🎯 The `@traceable` Decorator
281
+
282
+ The primary and most Pythonic way to instrument your code:
283
+
284
+ ### Basic Usage
285
+
286
+ ```python
287
+ from agentreplay import traceable
288
+
289
+ # Just add the decorator - that's it!
290
+ @traceable
291
+ def process_query(query: str) -> str:
292
+ return call_llm(query)
293
+
294
+ # Works with any function signature
295
+ @traceable
296
+ def complex_function(data: dict, *, optional_param: str = "default") -> list:
297
+ return process(data, optional_param)
298
+ ```
299
+
300
+ ### With Options
301
+
302
+ ```python
303
+ from agentreplay import traceable, SpanKind
304
+
305
+ # Custom span name and kind
306
+ @traceable(name="openai_chat", kind=SpanKind.LLM)
307
+ def call_openai(messages: list) -> str:
308
+ return openai_client.chat.completions.create(
309
+ model="gpt-4",
310
+ messages=messages
311
+ )
312
+
313
+ # Disable input capture for sensitive data
314
+ @traceable(capture_input=False)
315
+ def authenticate(password: str) -> bool:
316
+ return verify_password(password)
317
+
318
+ # Disable output capture
319
+ @traceable(capture_output=False)
320
+ def get_secret() -> str:
321
+ return fetch_secret_from_vault()
322
+
323
+ # Add static metadata
324
+ @traceable(metadata={"version": "2.0", "model": "gpt-4", "team": "ml"})
325
+ def enhanced_query(query: str) -> str:
326
+ return process(query)
327
+ ```
328
+
329
+ ### Async Functions
330
+
331
+ Full async/await support - no changes needed:
332
+
333
+ ```python
334
+ import asyncio
335
+ from agentreplay import traceable, SpanKind
336
+
337
+ @traceable(kind=SpanKind.LLM)
338
+ async def async_llm_call(prompt: str) -> str:
339
+ response = await openai_client.chat.completions.create(
340
+ model="gpt-4",
341
+ messages=[{"role": "user", "content": prompt}]
342
+ )
343
+ return response.choices[0].message.content
344
+
345
+ @traceable
346
+ async def process_batch(queries: list[str]) -> list[str]:
347
+ # All concurrent calls are traced with proper parent-child relationships
348
+ tasks = [async_llm_call(q) for q in queries]
349
+ return await asyncio.gather(*tasks)
350
+
351
+ # Run
352
+ results = asyncio.run(process_batch(["query1", "query2", "query3"]))
353
+ ```
354
+
355
+ ---
356
+
357
+ ## 📐 Context Manager: `trace()`
358
+
359
+ For more control over span attributes and timing:
360
+
361
+ ```python
362
+ from agentreplay import trace, SpanKind
363
+
364
+ def complex_operation(query: str) -> dict:
365
+ with trace("process_query", kind=SpanKind.CHAIN) as span:
366
+ # Set input data
367
+ span.set_input({"query": query, "timestamp": time.time()})
368
+
369
+ # Nested span for document retrieval
370
+ with trace("retrieve_documents", kind=SpanKind.RETRIEVER) as retriever_span:
371
+ docs = vector_db.search(query, top_k=5)
372
+ retriever_span.set_output({"document_count": len(docs)})
373
+ retriever_span.set_attribute("vector_db", "pinecone")
374
+
375
+ # Nested span for LLM generation
376
+ with trace("generate_response", kind=SpanKind.LLM) as llm_span:
377
+ llm_span.set_model("gpt-4", provider="openai")
378
+ response = generate_response(query, docs)
379
+ llm_span.set_token_usage(
380
+ prompt_tokens=150,
381
+ completion_tokens=200,
382
+ total_tokens=350
383
+ )
384
+ llm_span.set_output({"response_length": len(response)})
385
+
386
+ # Add events for debugging
387
+ span.add_event("processing_complete", {"doc_count": len(docs)})
388
+
389
+ # Set final output
390
+ span.set_output({"response": response, "sources": len(docs)})
391
+
392
+ return {"response": response, "sources": docs}
393
+ ```
394
+
395
+ ### Manual Span Control
396
+
397
+ For cases where you need explicit control:
398
+
399
+ ```python
400
+ from agentreplay import start_span, SpanKind
401
+
402
+ def long_running_operation():
403
+ span = start_span("background_job", kind=SpanKind.TOOL)
404
+ span.set_input({"job_type": "data_sync"})
405
+
406
+ try:
407
+ # Long running work...
408
+ for i in range(100):
409
+ process_item(i)
410
+ if i % 10 == 0:
411
+ span.add_event("progress", {"completed": i})
412
+
413
+ span.set_output({"items_processed": 100})
414
+
415
+ except Exception as e:
416
+ span.set_error(e)
417
+ raise
418
+
419
+ finally:
420
+ span.end() # Always call end()
421
+ ```
422
+
423
+ ---
424
+
425
+ ## 🔌 LLM Client Wrappers
426
+
427
+ ### OpenAI (Recommended)
428
+
429
+ One line to instrument all OpenAI calls:
430
+
431
+ ```python
432
+ from openai import OpenAI
433
+ from agentreplay import init, wrap_openai, flush
434
+
435
+ init()
436
+
437
+ # Wrap the client - all calls are now traced automatically!
438
+ client = wrap_openai(OpenAI())
439
+
440
+ # Use normally - tracing happens in the background
441
+ response = client.chat.completions.create(
442
+ model="gpt-4",
443
+ messages=[
444
+ {"role": "system", "content": "You are a helpful assistant."},
445
+ {"role": "user", "content": "Explain quantum computing in simple terms."}
446
+ ],
447
+ temperature=0.7,
448
+ )
449
+
450
+ print(response.choices[0].message.content)
451
+
452
+ # Embeddings are traced too
453
+ embedding = client.embeddings.create(
454
+ model="text-embedding-ada-002",
455
+ input="Hello world"
456
+ )
457
+
458
+ flush()
459
+ ```
460
+
461
+ ### Async OpenAI
462
+
463
+ ```python
464
+ from openai import AsyncOpenAI
465
+ from agentreplay import init, wrap_openai
466
+
467
+ init()
468
+
469
+ # Works with async client too
470
+ async_client = wrap_openai(AsyncOpenAI())
471
+
472
+ async def main():
473
+ response = await async_client.chat.completions.create(
474
+ model="gpt-4",
475
+ messages=[{"role": "user", "content": "Hello!"}]
476
+ )
477
+ return response.choices[0].message.content
478
+ ```
479
+
480
+ ### Anthropic
481
+
482
+ ```python
483
+ from anthropic import Anthropic
484
+ from agentreplay import init, wrap_anthropic, flush
485
+
486
+ init()
487
+
488
+ # Wrap the Anthropic client
489
+ client = wrap_anthropic(Anthropic())
490
+
491
+ # Use normally
492
+ message = client.messages.create(
493
+ model="claude-3-opus-20240229",
494
+ max_tokens=1024,
495
+ messages=[
496
+ {"role": "user", "content": "Explain the theory of relativity."}
497
+ ]
498
+ )
499
+
500
+ print(message.content[0].text)
501
+ flush()
502
+ ```
503
+
504
+ ### Disable Content Capture
505
+
506
+ For privacy-sensitive applications:
507
+
508
+ ```python
509
+ # Don't capture message content, only metadata
510
+ client = wrap_openai(OpenAI(), capture_content=False)
511
+
512
+ # Traces will still include:
513
+ # - Model name
514
+ # - Token counts
515
+ # - Latency
516
+ # - Error information
517
+ # But NOT the actual messages or responses
518
+ ```
519
+
520
+ ---
521
+
522
+ ## 🏷️ Context Management
523
+
524
+ ### Global Context
525
+
526
+ Set context that applies to ALL subsequent traces:
527
+
528
+ ```python
529
+ from agentreplay import set_context, get_global_context, clear_context
530
+
531
+ # Set user context (persists until cleared)
532
+ set_context(
533
+ user_id="user-123",
534
+ session_id="session-456",
535
+ agent_id="support-bot",
536
+ )
537
+
538
+ # Add more context later
539
+ set_context(
540
+ environment="production",
541
+ version="1.2.0",
542
+ region="us-west-2",
543
+ )
544
+
545
+ # Get current global context
546
+ context = get_global_context()
547
+ print(context) # {'user_id': 'user-123', 'session_id': 'session-456', ...}
548
+
549
+ # Clear all context
550
+ clear_context()
551
+ ```
552
+
553
+ ### Request-Scoped Context
554
+
555
+ For web applications with per-request context:
556
+
557
+ ```python
558
+ from agentreplay import with_context
559
+
560
+ async def handle_api_request(request):
561
+ # Context only applies within this block
562
+ with with_context(
563
+ user_id=request.user_id,
564
+ request_id=request.headers.get("X-Request-ID"),
565
+ ip_address=request.client_ip,
566
+ ):
567
+ # All traces here include this context
568
+ result = await process_request(request)
569
+
570
+ # Context automatically cleared after block
571
+ return result
572
+
573
+ # Async version works too
574
+ async def async_handler(request):
575
+ async with with_context(user_id=request.user_id):
576
+ return await async_process(request)
577
+ ```
578
+
579
+ ### Multi-Agent Context
580
+
581
+ For multi-agent systems like CrewAI or AutoGen:
582
+
583
+ ```python
584
+ from agentreplay import AgentContext
585
+
586
+ def run_multi_agent_workflow():
587
+ # Each agent gets its own context
588
+ with AgentContext(
589
+ agent_id="researcher",
590
+ session_id="workflow-123",
591
+ workflow_id="content-creation",
592
+ ):
593
+ # All LLM calls here are tagged with agent_id="researcher"
594
+ research_results = researcher_agent.run()
595
+
596
+ with AgentContext(
597
+ agent_id="writer",
598
+ session_id="workflow-123", # Same session
599
+ workflow_id="content-creation",
600
+ ):
601
+ # These calls are tagged with agent_id="writer"
602
+ article = writer_agent.run(research_results)
603
+
604
+ with AgentContext(
605
+ agent_id="editor",
606
+ session_id="workflow-123",
607
+ workflow_id="content-creation",
608
+ ):
609
+ final_article = editor_agent.run(article)
610
+
611
+ return final_article
612
+ ```
613
+
614
+ ---
615
+
616
+ ## 🔒 Privacy & Data Redaction
617
+
618
+ ### Configure Privacy Settings
619
+
620
+ ```python
621
+ from agentreplay import configure_privacy
622
+
623
+ configure_privacy(
624
+ # Enable built-in patterns for common PII
625
+ use_builtin_patterns=True, # Emails, credit cards, SSNs, phones, API keys
626
+
627
+ # Add custom regex patterns
628
+ redact_patterns=[
629
+ r"secret-\w+", # Custom secret format
630
+ r"internal-id-\d+", # Internal IDs
631
+ r"password:\s*\S+", # Password fields
632
+ ],
633
+
634
+ # Completely scrub these JSON paths
635
+ scrub_paths=[
636
+ "input.password",
637
+ "input.credentials.api_key",
638
+ "output.user.ssn",
639
+ "metadata.internal_token",
640
+ ],
641
+
642
+ # Hash PII instead of replacing with [REDACTED]
643
+ # Allows tracking unique values without exposing data
644
+ hash_pii=True,
645
+ hash_salt="your-secret-salt-here",
646
+
647
+ # Custom replacement text
648
+ redacted_text="[SENSITIVE]",
649
+ )
650
+ ```
651
+
652
+ ### Built-in Redaction Patterns
653
+
654
+ The SDK includes patterns for:
655
+
656
+ | Type | Example | Redacted As |
657
+ |------|---------|-------------|
658
+ | Email | user@example.com | [REDACTED] |
659
+ | Credit Card | 4111-1111-1111-1111 | [REDACTED] |
660
+ | SSN | 123-45-6789 | [REDACTED] |
661
+ | Phone (US) | +1-555-123-4567 | [REDACTED] |
662
+ | Phone (Intl) | +44-20-1234-5678 | [REDACTED] |
663
+ | API Key | sk-proj-abc123... | [REDACTED] |
664
+ | Bearer Token | Bearer eyJ... | [REDACTED] |
665
+ | JWT | eyJhbG... | [REDACTED] |
666
+ | IP Address | 192.168.1.1 | [REDACTED] |
667
+
668
+ ### Manual Redaction
669
+
670
+ ```python
671
+ from agentreplay import redact_payload, redact_string, hash_pii
672
+
673
+ # Redact an entire payload
674
+ data = {
675
+ "user": {
676
+ "email": "john@example.com",
677
+ "phone": "+1-555-123-4567",
678
+ },
679
+ "message": "My credit card is 4111-1111-1111-1111",
680
+ "api_key": "sk-proj-abcdefghijk",
681
+ }
682
+
683
+ safe_data = redact_payload(data)
684
+ # Result:
685
+ # {
686
+ # "user": {
687
+ # "email": "[REDACTED]",
688
+ # "phone": "[REDACTED]",
689
+ # },
690
+ # "message": "My credit card is [REDACTED]",
691
+ # "api_key": "[REDACTED]",
692
+ # }
693
+
694
+ # Redact a single string
695
+ safe_message = redact_string("Contact me at user@example.com")
696
+ # "Contact me at [REDACTED]"
697
+
698
+ # Hash for consistent anonymization (same input = same hash)
699
+ user_hash = hash_pii("user@example.com")
700
+ # "[HASH:a1b2c3d4]"
701
+
702
+ # Useful for analytics without exposing PII
703
+ print(f"User {user_hash} performed action")
704
+ ```
705
+
706
+ ### Temporary Privacy Context
707
+
708
+ ```python
709
+ from agentreplay.privacy import privacy_context
710
+
711
+ # Add extra redaction rules for a specific block
712
+ with privacy_context(
713
+ redact_patterns=[r"internal-token-\w+"],
714
+ scrub_paths=["metadata.debug_info"],
715
+ ):
716
+ # These rules only apply within this block
717
+ result = process_sensitive_data(data)
718
+
719
+ # Original rules restored after block
720
+ ```
721
+
722
+ ---
723
+
724
+ ## 📊 Span Kinds
725
+
726
+ Use semantic span kinds for better visualization and filtering:
727
+
728
+ ```python
729
+ from agentreplay import SpanKind
730
+
731
+ # Available span kinds
732
+ SpanKind.CHAIN # Orchestration, workflows, pipelines
733
+ SpanKind.LLM # LLM API calls (OpenAI, Anthropic, etc.)
734
+ SpanKind.TOOL # Tool/function calls, actions
735
+ SpanKind.RETRIEVER # Vector DB search, document retrieval
736
+ SpanKind.EMBEDDING # Embedding generation
737
+ SpanKind.GUARDRAIL # Safety checks, content filtering
738
+ SpanKind.CACHE # Cache operations
739
+ SpanKind.HTTP # HTTP requests
740
+ SpanKind.DB # Database queries
741
+ ```
742
+
743
+ Example usage:
744
+
745
+ ```python
746
+ from agentreplay import traceable, SpanKind
747
+
748
+ @traceable(kind=SpanKind.RETRIEVER)
749
+ def search_documents(query: str) -> list:
750
+ return vector_db.similarity_search(query, k=5)
751
+
752
+ @traceable(kind=SpanKind.LLM)
753
+ def generate_answer(query: str, docs: list) -> str:
754
+ return llm.generate(query, context=docs)
755
+
756
+ @traceable(kind=SpanKind.CHAIN)
757
+ def rag_pipeline(query: str) -> str:
758
+ docs = search_documents(query)
759
+ return generate_answer(query, docs)
760
+ ```
761
+
762
+ ---
763
+
764
+ ## ⚙️ Lifecycle Management
765
+
766
+ ### Flushing Traces
767
+
768
+ Always ensure traces are sent before your application exits:
769
+
770
+ ```python
771
+ import agentreplay
772
+
773
+ agentreplay.init()
774
+
775
+ # Your application code...
776
+
777
+ # Option 1: Manual flush with timeout
778
+ agentreplay.flush(timeout=10.0) # Wait up to 10 seconds
779
+
780
+ # Option 2: Full graceful shutdown
781
+ agentreplay.shutdown(timeout=30.0) # Flush and cleanup
782
+
783
+ # Option 3: Auto-registered (init() registers atexit handler automatically)
784
+ # Traces are flushed on normal program exit
785
+ ```
786
+
787
+ ### Serverless / AWS Lambda
788
+
789
+ **Critical**: Always flush explicitly before the function returns!
790
+
791
+ ```python
792
+ import agentreplay
793
+
794
+ agentreplay.init()
795
+
796
+ @agentreplay.traceable
797
+ def process_event(event):
798
+ # Your logic here
799
+ return {"processed": True}
800
+
801
+ def lambda_handler(event, context):
802
+ try:
803
+ result = process_event(event)
804
+ return {
805
+ "statusCode": 200,
806
+ "body": json.dumps(result)
807
+ }
808
+ finally:
809
+ # CRITICAL: Flush before Lambda freezes
810
+ agentreplay.flush(timeout=5.0)
811
+ ```
812
+
813
+ ### FastAPI / Starlette
814
+
815
+ ```python
816
+ from fastapi import FastAPI
817
+ from contextlib import asynccontextmanager
818
+ import agentreplay
819
+
820
+ @asynccontextmanager
821
+ async def lifespan(app: FastAPI):
822
+ # Startup
823
+ agentreplay.init()
824
+ yield
825
+ # Shutdown
826
+ agentreplay.shutdown(timeout=10.0)
827
+
828
+ app = FastAPI(lifespan=lifespan)
829
+
830
+ @app.post("/chat")
831
+ async def chat(request: ChatRequest):
832
+ # Traces are sent automatically in background
833
+ return await process_chat(request)
834
+ ```
835
+
836
+ ### Diagnostics
837
+
838
+ ```python
839
+ import agentreplay
840
+
841
+ # Get SDK statistics
842
+ stats = agentreplay.get_stats()
843
+ print(f"Spans sent: {stats.get('spans_sent', 0)}")
844
+ print(f"Spans pending: {stats.get('spans_pending', 0)}")
845
+ print(f"Errors: {stats.get('errors', 0)}")
846
+ print(f"Batches sent: {stats.get('batches_sent', 0)}")
847
+
848
+ # Health check - verify backend connectivity
849
+ if agentreplay.ping():
850
+ print("✅ Backend is reachable")
851
+ else:
852
+ print("❌ Cannot reach backend")
853
+ ```
854
+
855
+ ---
856
+
857
+ ## 🔗 Framework Integrations
858
+
859
+ ### LangChain
860
+
861
+ ```python
862
+ from langchain_openai import ChatOpenAI
863
+ from langchain.schema import HumanMessage
864
+ import agentreplay
865
+
866
+ agentreplay.init()
867
+
868
+ @agentreplay.traceable(name="langchain_qa", kind=agentreplay.SpanKind.CHAIN)
869
+ def answer_question(question: str) -> str:
870
+ llm = ChatOpenAI(model="gpt-4", temperature=0)
871
+ response = llm.invoke([HumanMessage(content=question)])
872
+ return response.content
873
+
874
+ result = answer_question("What is machine learning?")
875
+ agentreplay.flush()
876
+ ```
877
+
878
+ ### LangGraph
879
+
880
+ ```python
881
+ from langgraph.graph import StateGraph, END
882
+ import agentreplay
883
+
884
+ agentreplay.init()
885
+
886
+ @agentreplay.traceable(name="agent_node", kind=agentreplay.SpanKind.LLM)
887
+ def agent_node(state):
888
+ # Agent logic
889
+ response = llm.invoke(state["messages"])
890
+ return {"messages": state["messages"] + [response]}
891
+
892
+ @agentreplay.traceable(name="tool_node", kind=agentreplay.SpanKind.TOOL)
893
+ def tool_node(state):
894
+ # Tool execution
895
+ result = execute_tool(state["tool_call"])
896
+ return {"messages": state["messages"] + [result]}
897
+
898
+ # Build graph with traced nodes
899
+ workflow = StateGraph(State)
900
+ workflow.add_node("agent", agent_node)
901
+ workflow.add_node("tools", tool_node)
902
+ # ... rest of graph definition
903
+ ```
904
+
905
+ ### CrewAI
906
+
907
+ ```python
908
+ from crewai import Agent, Task, Crew
909
+ import agentreplay
910
+
911
+ agentreplay.init()
912
+
913
+ # Wrap the LLM client
914
+ wrapped_llm = agentreplay.wrap_openai(OpenAI())
915
+
916
+ # Track each agent with context
917
+ with agentreplay.AgentContext(agent_id="researcher", workflow_id="article-creation"):
918
+ researcher = Agent(
919
+ role="Senior Researcher",
920
+ goal="Find comprehensive information",
921
+ llm=wrapped_llm,
922
+ )
923
+
924
+ with agentreplay.AgentContext(agent_id="writer", workflow_id="article-creation"):
925
+ writer = Agent(
926
+ role="Content Writer",
927
+ goal="Write engaging articles",
928
+ llm=wrapped_llm,
929
+ )
930
+
931
+ # Run the crew
932
+ crew = Crew(agents=[researcher, writer], tasks=[research_task, write_task])
933
+ result = crew.kickoff()
934
+
935
+ agentreplay.flush()
936
+ ```
937
+
938
+ ### LlamaIndex
939
+
940
+ ```python
941
+ from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
942
+ import agentreplay
943
+
944
+ agentreplay.init()
945
+
946
+ @agentreplay.traceable(name="index_documents", kind=agentreplay.SpanKind.EMBEDDING)
947
+ def build_index(directory: str):
948
+ documents = SimpleDirectoryReader(directory).load_data()
949
+ return VectorStoreIndex.from_documents(documents)
950
+
951
+ @agentreplay.traceable(name="query_index", kind=agentreplay.SpanKind.RETRIEVER)
952
+ def query(index, question: str):
953
+ query_engine = index.as_query_engine()
954
+ return query_engine.query(question)
955
+
956
+ index = build_index("./documents")
957
+ response = query(index, "What is the main topic?")
958
+ agentreplay.flush()
959
+ ```
960
+
961
+ ---
962
+
963
+ ## 🌐 Environment Variables
964
+
965
+ | Variable | Description | Default |
966
+ |----------|-------------|---------|
967
+ | `AGENTREPLAY_API_KEY` | API key for authentication | **Required** |
968
+ | `AGENTREPLAY_PROJECT_ID` | Project identifier | **Required** |
969
+ | `AGENTREPLAY_BASE_URL` | API base URL | `https://api.agentreplay.io` |
970
+ | `AGENTREPLAY_TENANT_ID` | Tenant identifier | `default` |
971
+ | `AGENTREPLAY_AGENT_ID` | Default agent ID | `default` |
972
+ | `AGENTREPLAY_ENABLED` | Enable/disable tracing | `true` |
973
+ | `AGENTREPLAY_DEBUG` | Enable debug logging | `false` |
974
+ | `AGENTREPLAY_BATCH_SIZE` | Spans per batch | `100` |
975
+ | `AGENTREPLAY_FLUSH_INTERVAL` | Auto-flush interval (seconds) | `5.0` |
976
+ | `AGENTREPLAY_CAPTURE_INPUT` | Capture function inputs | `true` |
977
+ | `AGENTREPLAY_CAPTURE_OUTPUT` | Capture function outputs | `true` |
978
+
979
+ ---
980
+
981
+ ## 🧪 Testing
982
+
983
+ ### Disable Tracing in Tests
984
+
985
+ ```python
986
+ import agentreplay
987
+ import pytest
988
+
989
+ @pytest.fixture(autouse=True)
990
+ def disable_tracing():
991
+ """Disable tracing for all tests."""
992
+ agentreplay.init(enabled=False)
993
+ yield
994
+ agentreplay.reset()
995
+
996
+ def test_my_function():
997
+ # Tracing is disabled, no network calls
998
+ result = my_traced_function("test")
999
+ assert result == expected
1000
+ ```
1001
+
1002
+ Or use environment variable:
1003
+
1004
+ ```bash
1005
+ AGENTREPLAY_ENABLED=false pytest
1006
+ ```
1007
+
1008
+ ### Mock the SDK
1009
+
1010
+ ```python
1011
+ from unittest.mock import patch
1012
+
1013
+ def test_with_mock():
1014
+ with patch('agentreplay.flush'):
1015
+ # flush() won't actually send data
1016
+ result = my_function()
1017
+ ```
1018
+
1019
+ ---
1020
+
1021
+ ## 📚 Complete API Reference
1022
+
1023
+ ### Top-Level Functions
1024
+
1025
+ | Function | Description |
1026
+ |----------|-------------|
1027
+ | `init(**config)` | Initialize the SDK with configuration |
1028
+ | `flush(timeout=None)` | Send all pending traces |
1029
+ | `shutdown(timeout=None)` | Graceful shutdown with flush |
1030
+ | `reset()` | Reset SDK state completely |
1031
+ | `get_stats()` | Get diagnostic statistics |
1032
+ | `ping()` | Check backend connectivity |
1033
+
1034
+ ### Decorators & Tracing
1035
+
1036
+ | Function | Description |
1037
+ |----------|-------------|
1038
+ | `@traceable` | Decorator for function tracing |
1039
+ | `@observe` | Alias for `@traceable` (Langfuse-style) |
1040
+ | `trace(name, **opts)` | Context manager for creating spans |
1041
+ | `start_span(name, **opts)` | Create a manual span |
1042
+ | `get_current_span()` | Get the currently active span |
1043
+
1044
+ ### Client Wrappers
1045
+
1046
+ | Function | Description |
1047
+ |----------|-------------|
1048
+ | `wrap_openai(client, **opts)` | Wrap OpenAI client |
1049
+ | `wrap_anthropic(client, **opts)` | Wrap Anthropic client |
1050
+ | `wrap_method(obj, method, **opts)` | Wrap any method |
1051
+
1052
+ ### Context Management
1053
+
1054
+ | Function | Description |
1055
+ |----------|-------------|
1056
+ | `set_context(**ctx)` | Set global context |
1057
+ | `get_global_context()` | Get current global context |
1058
+ | `clear_context()` | Clear all global context |
1059
+ | `with_context(**ctx)` | Scoped context manager |
1060
+ | `AgentContext(...)` | Class-based agent context |
1061
+
1062
+ ### Privacy
1063
+
1064
+ | Function | Description |
1065
+ |----------|-------------|
1066
+ | `configure_privacy(**opts)` | Configure redaction settings |
1067
+ | `redact_payload(data)` | Redact sensitive data from dict |
1068
+ | `redact_string(text)` | Redact patterns from string |
1069
+ | `hash_pii(value, salt=None)` | Hash PII for anonymization |
1070
+ | `add_pattern(regex)` | Add redaction pattern at runtime |
1071
+ | `add_scrub_path(path)` | Add scrub path at runtime |
1072
+
1073
+ ### ActiveSpan Methods
1074
+
1075
+ | Method | Description |
1076
+ |--------|-------------|
1077
+ | `set_input(data)` | Set span input data |
1078
+ | `set_output(data)` | Set span output data |
1079
+ | `set_attribute(key, value)` | Set a single attribute |
1080
+ | `set_attributes(dict)` | Set multiple attributes |
1081
+ | `add_event(name, attrs)` | Add a timestamped event |
1082
+ | `set_error(exception)` | Record an error |
1083
+ | `set_token_usage(...)` | Set LLM token counts |
1084
+ | `set_model(model, provider)` | Set model information |
1085
+ | `end()` | End the span |
1086
+
1087
+ ---
1088
+
1089
+ ## 🤝 Contributing
1090
+
1091
+ We welcome contributions! See [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines.
1092
+
1093
+ ```bash
1094
+ # Clone the repository
1095
+ git clone https://github.com/agentreplay/agentreplay.git
1096
+ cd agentreplay/sdks/python
1097
+
1098
+ # Create virtual environment
1099
+ python -m venv .venv
1100
+ source .venv/bin/activate # or `.venv\Scripts\activate` on Windows
1101
+
1102
+ # Install in development mode
1103
+ pip install -e ".[dev]"
1104
+
1105
+ # Run tests
1106
+ pytest tests/ -v
1107
+
1108
+ # Run linter
1109
+ ruff check src/
1110
+
1111
+ # Run type checker
1112
+ mypy src/agentreplay
1113
+
1114
+ # Run formatter
1115
+ ruff format src/
1116
+ ```
1117
+
1118
+ ---
1119
+
1120
+ ## 📄 License
1121
+
1122
+ Apache 2.0 - see [LICENSE](../../LICENSE) for details.
1123
+
1124
+ ---
1125
+
1126
+ ## 🔗 Links
1127
+
1128
+ - 📖 [Documentation](https://docs.agentreplay.io)
1129
+ - 💻 [GitHub Repository](https://github.com/agentreplay/agentreplay)
1130
+ - 📦 [PyPI Package](https://pypi.org/project/agentreplay/)
1131
+ - 💬 [Discord Community](https://discord.gg/agentreplay)
1132
+ - 🐦 [Twitter](https://twitter.com/agentreplay)
1133
+
1134
+ ---
1135
+
1136
+ <p align="center">
1137
+ Made with ❤️ by the Agentreplay team
1138
+ </p>