agentreplay 0.1.2__py3-none-any.whl → 0.4.2__py3-none-any.whl
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.
- agentreplay/__init__.py +152 -10
- agentreplay/context.py +133 -0
- agentreplay/decorators.py +541 -0
- agentreplay/install_pth.py +6 -2
- agentreplay/privacy.py +452 -0
- agentreplay/sdk.py +578 -0
- agentreplay/wrappers.py +522 -0
- agentreplay-0.4.2.dist-info/METADATA +1083 -0
- {agentreplay-0.1.2.dist-info → agentreplay-0.4.2.dist-info}/RECORD +13 -9
- agentreplay-0.1.2.dist-info/METADATA +0 -285
- {agentreplay-0.1.2.dist-info → agentreplay-0.4.2.dist-info}/WHEEL +0 -0
- {agentreplay-0.1.2.dist-info → agentreplay-0.4.2.dist-info}/entry_points.txt +0 -0
- {agentreplay-0.1.2.dist-info → agentreplay-0.4.2.dist-info}/licenses/LICENSE +0 -0
- {agentreplay-0.1.2.dist-info → agentreplay-0.4.2.dist-info}/top_level.txt +0 -0
agentreplay/__init__.py
CHANGED
|
@@ -12,7 +12,35 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
"""
|
|
15
|
+
"""
|
|
16
|
+
Agentreplay Python SDK - Agent Trace Engine for LLM Agents.
|
|
17
|
+
|
|
18
|
+
Modern, ergonomic SDK for observability in LLM applications.
|
|
19
|
+
|
|
20
|
+
Quick Start:
|
|
21
|
+
>>> import agentreplay
|
|
22
|
+
>>>
|
|
23
|
+
>>> # Initialize with env vars (recommended)
|
|
24
|
+
>>> agentreplay.init()
|
|
25
|
+
>>>
|
|
26
|
+
>>> # Or with explicit config
|
|
27
|
+
>>> agentreplay.init(
|
|
28
|
+
... api_key="your-key",
|
|
29
|
+
... project_id="my-project",
|
|
30
|
+
... )
|
|
31
|
+
>>>
|
|
32
|
+
>>> # Trace functions with decorator
|
|
33
|
+
>>> @agentreplay.traceable
|
|
34
|
+
>>> def my_llm_function(query: str) -> str:
|
|
35
|
+
... return call_llm(query)
|
|
36
|
+
>>>
|
|
37
|
+
>>> # Wrap OpenAI for automatic tracing
|
|
38
|
+
>>> from openai import OpenAI
|
|
39
|
+
>>> client = agentreplay.wrap_openai(OpenAI())
|
|
40
|
+
>>>
|
|
41
|
+
>>> # Ensure traces are sent before exit
|
|
42
|
+
>>> agentreplay.flush()
|
|
43
|
+
"""
|
|
16
44
|
|
|
17
45
|
from agentreplay.client import AgentreplayClient
|
|
18
46
|
from agentreplay.models import SpanType, AgentFlowEdge
|
|
@@ -32,44 +60,158 @@ from agentreplay.exceptions import (
|
|
|
32
60
|
)
|
|
33
61
|
|
|
34
62
|
# Agent Context Tracking
|
|
35
|
-
from agentreplay.context import
|
|
63
|
+
from agentreplay.context import (
|
|
64
|
+
AgentContext,
|
|
65
|
+
set_context,
|
|
66
|
+
get_global_context,
|
|
67
|
+
clear_context,
|
|
68
|
+
with_context,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Auto-instrumentation (Pure OpenTelemetry) - Optional
|
|
72
|
+
try:
|
|
73
|
+
from agentreplay.auto_instrument import auto_instrument, setup_instrumentation
|
|
74
|
+
except ImportError:
|
|
75
|
+
auto_instrument = None # type: ignore
|
|
76
|
+
setup_instrumentation = None # type: ignore
|
|
77
|
+
|
|
78
|
+
# OTEL Bridge & Bootstrap - Optional (requires opentelemetry-sdk)
|
|
79
|
+
try:
|
|
80
|
+
from agentreplay.bootstrap import init_otel_instrumentation, is_initialized
|
|
81
|
+
from agentreplay.otel_bridge import get_tracer
|
|
82
|
+
except ImportError:
|
|
83
|
+
init_otel_instrumentation = None # type: ignore
|
|
84
|
+
is_initialized = lambda: False # type: ignore
|
|
85
|
+
get_tracer = None # type: ignore
|
|
86
|
+
|
|
87
|
+
# =============================================================================
|
|
88
|
+
# Ergonomic Top-Level API (v0.4+)
|
|
89
|
+
# =============================================================================
|
|
36
90
|
|
|
37
|
-
#
|
|
38
|
-
from agentreplay.
|
|
91
|
+
# SDK Lifecycle
|
|
92
|
+
from agentreplay.sdk import (
|
|
93
|
+
init,
|
|
94
|
+
get_client,
|
|
95
|
+
get_batching_client,
|
|
96
|
+
flush,
|
|
97
|
+
shutdown,
|
|
98
|
+
reset,
|
|
99
|
+
get_stats,
|
|
100
|
+
ping,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Decorators & Tracing
|
|
104
|
+
from agentreplay.decorators import (
|
|
105
|
+
traceable,
|
|
106
|
+
observe, # Langfuse-style alias
|
|
107
|
+
trace,
|
|
108
|
+
start_span,
|
|
109
|
+
get_current_span,
|
|
110
|
+
SpanKind,
|
|
111
|
+
ActiveSpan,
|
|
112
|
+
)
|
|
39
113
|
|
|
40
|
-
#
|
|
41
|
-
from agentreplay.
|
|
42
|
-
|
|
114
|
+
# Client Wrappers
|
|
115
|
+
from agentreplay.wrappers import (
|
|
116
|
+
wrap_openai,
|
|
117
|
+
wrap_anthropic,
|
|
118
|
+
wrap_method,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Privacy
|
|
122
|
+
from agentreplay.privacy import (
|
|
123
|
+
configure_privacy,
|
|
124
|
+
redact_payload,
|
|
125
|
+
redact_string,
|
|
126
|
+
hash_pii,
|
|
127
|
+
add_pattern,
|
|
128
|
+
add_scrub_path,
|
|
129
|
+
privacy_context,
|
|
130
|
+
)
|
|
43
131
|
|
|
44
|
-
__version__ = "0.
|
|
132
|
+
__version__ = "0.4.2"
|
|
45
133
|
|
|
46
134
|
__all__ = [
|
|
135
|
+
# ==========================================================================
|
|
136
|
+
# MODERN ERGONOMIC API (Recommended)
|
|
137
|
+
# ==========================================================================
|
|
138
|
+
|
|
139
|
+
# Initialization (one-liner setup)
|
|
140
|
+
"init",
|
|
141
|
+
"flush",
|
|
142
|
+
"shutdown",
|
|
143
|
+
"reset",
|
|
144
|
+
"get_stats",
|
|
145
|
+
"ping",
|
|
146
|
+
|
|
147
|
+
# Tracing decorators
|
|
148
|
+
"traceable",
|
|
149
|
+
"observe", # Langfuse-style alias
|
|
150
|
+
"trace",
|
|
151
|
+
"start_span",
|
|
152
|
+
"get_current_span",
|
|
153
|
+
"SpanKind",
|
|
154
|
+
"ActiveSpan",
|
|
155
|
+
|
|
156
|
+
# Client wrappers (one-liner instrumentation)
|
|
157
|
+
"wrap_openai",
|
|
158
|
+
"wrap_anthropic",
|
|
159
|
+
"wrap_method",
|
|
160
|
+
|
|
161
|
+
# Context management
|
|
162
|
+
"set_context",
|
|
163
|
+
"get_global_context",
|
|
164
|
+
"clear_context",
|
|
165
|
+
"with_context",
|
|
166
|
+
"AgentContext", # Class-based context
|
|
167
|
+
|
|
168
|
+
# Privacy
|
|
169
|
+
"configure_privacy",
|
|
170
|
+
"redact_payload",
|
|
171
|
+
"redact_string",
|
|
172
|
+
"hash_pii",
|
|
173
|
+
"add_pattern",
|
|
174
|
+
"add_scrub_path",
|
|
175
|
+
"privacy_context",
|
|
176
|
+
|
|
177
|
+
# ==========================================================================
|
|
178
|
+
# CORE API (Advanced Usage)
|
|
179
|
+
# ==========================================================================
|
|
180
|
+
|
|
47
181
|
# Core client
|
|
182
|
+
"get_client",
|
|
183
|
+
"get_batching_client",
|
|
48
184
|
"AgentreplayClient",
|
|
49
185
|
"BatchingAgentreplayClient",
|
|
186
|
+
|
|
50
187
|
# Models
|
|
51
188
|
"SpanType",
|
|
52
189
|
"AgentFlowEdge",
|
|
53
190
|
"Span",
|
|
191
|
+
|
|
54
192
|
# Configuration
|
|
55
193
|
"AgentreplayConfig",
|
|
56
194
|
"get_config",
|
|
57
195
|
"set_config",
|
|
58
196
|
"reset_config",
|
|
197
|
+
|
|
59
198
|
# Session management
|
|
60
199
|
"Session",
|
|
200
|
+
|
|
61
201
|
# Retry utilities
|
|
62
202
|
"retry_with_backoff",
|
|
63
|
-
|
|
64
|
-
"AgentContext",
|
|
203
|
+
|
|
65
204
|
# Auto-instrumentation (Pure OpenTelemetry)
|
|
66
205
|
"auto_instrument",
|
|
67
206
|
"setup_instrumentation",
|
|
207
|
+
|
|
68
208
|
# OTEL Initialization
|
|
69
209
|
"init_otel_instrumentation",
|
|
70
210
|
"is_initialized",
|
|
211
|
+
|
|
71
212
|
# OTEL Bridge
|
|
72
213
|
"get_tracer",
|
|
214
|
+
|
|
73
215
|
# Exceptions
|
|
74
216
|
"AgentreplayError",
|
|
75
217
|
"AuthenticationError",
|
agentreplay/context.py
CHANGED
|
@@ -166,3 +166,136 @@ def set_user_id(user_id: str):
|
|
|
166
166
|
Token that can be used to reset the context
|
|
167
167
|
"""
|
|
168
168
|
return _user_id.set(user_id)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
# =============================================================================
|
|
172
|
+
# Ergonomic set_context Function
|
|
173
|
+
# =============================================================================
|
|
174
|
+
|
|
175
|
+
# Global context for all spans (merged with span-level context)
|
|
176
|
+
_global_context: dict = {}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def set_context(
|
|
180
|
+
*,
|
|
181
|
+
user_id: Optional[str] = None,
|
|
182
|
+
session_id: Optional[str] = None,
|
|
183
|
+
agent_id: Optional[str] = None,
|
|
184
|
+
workflow_id: Optional[str] = None,
|
|
185
|
+
**extra: str,
|
|
186
|
+
) -> None:
|
|
187
|
+
"""Set global context that applies to all subsequent spans.
|
|
188
|
+
|
|
189
|
+
This is a convenience function for setting context that should
|
|
190
|
+
apply to all traces/spans. The context persists until reset.
|
|
191
|
+
|
|
192
|
+
For request-scoped context, use the `AgentContext` context manager
|
|
193
|
+
or pass context directly to `@traceable` or `trace()`.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
user_id: User identifier
|
|
197
|
+
session_id: Session identifier
|
|
198
|
+
agent_id: Agent identifier
|
|
199
|
+
workflow_id: Workflow identifier
|
|
200
|
+
**extra: Additional key-value pairs
|
|
201
|
+
|
|
202
|
+
Example:
|
|
203
|
+
>>> from agentreplay import init, set_context
|
|
204
|
+
>>>
|
|
205
|
+
>>> init()
|
|
206
|
+
>>> set_context(user_id="user-123", session_id="session-456")
|
|
207
|
+
>>>
|
|
208
|
+
>>> # All subsequent traces include this context
|
|
209
|
+
>>> @traceable
|
|
210
|
+
>>> def my_function():
|
|
211
|
+
... return "hello"
|
|
212
|
+
"""
|
|
213
|
+
global _global_context
|
|
214
|
+
|
|
215
|
+
if user_id is not None:
|
|
216
|
+
_global_context["user_id"] = user_id
|
|
217
|
+
set_user_id(user_id)
|
|
218
|
+
|
|
219
|
+
if session_id is not None:
|
|
220
|
+
_global_context["session_id"] = session_id
|
|
221
|
+
set_session_id(session_id)
|
|
222
|
+
|
|
223
|
+
if agent_id is not None:
|
|
224
|
+
_global_context["agent_id"] = agent_id
|
|
225
|
+
set_agent_id(agent_id)
|
|
226
|
+
|
|
227
|
+
if workflow_id is not None:
|
|
228
|
+
_global_context["workflow_id"] = workflow_id
|
|
229
|
+
set_workflow_id(workflow_id)
|
|
230
|
+
|
|
231
|
+
# Store extra context
|
|
232
|
+
_global_context.update(extra)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def get_global_context() -> dict:
|
|
236
|
+
"""Get the current global context.
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
Dictionary of global context key-value pairs
|
|
240
|
+
"""
|
|
241
|
+
return _global_context.copy()
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def clear_context() -> None:
|
|
245
|
+
"""Clear all global context."""
|
|
246
|
+
global _global_context
|
|
247
|
+
_global_context = {}
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def with_context(**context) -> "ContextScope":
|
|
251
|
+
"""Create a context scope for the current async context.
|
|
252
|
+
|
|
253
|
+
This is like `set_context` but only applies within the current
|
|
254
|
+
async task or thread, and automatically cleans up when the scope exits.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
**context: Key-value pairs to set in context
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
ContextScope context manager
|
|
261
|
+
|
|
262
|
+
Example:
|
|
263
|
+
>>> async def handle_request(user_id: str):
|
|
264
|
+
... with with_context(user_id=user_id, request_id="req-123"):
|
|
265
|
+
... # Context only applies within this block
|
|
266
|
+
... result = await my_traced_function()
|
|
267
|
+
... return result
|
|
268
|
+
"""
|
|
269
|
+
return ContextScope(**context)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class ContextScope:
|
|
273
|
+
"""Context manager for request-scoped context.
|
|
274
|
+
|
|
275
|
+
Automatically sets and resets context variables.
|
|
276
|
+
"""
|
|
277
|
+
|
|
278
|
+
def __init__(self, **context):
|
|
279
|
+
self.context = context
|
|
280
|
+
self.tokens = []
|
|
281
|
+
|
|
282
|
+
def __enter__(self):
|
|
283
|
+
if "user_id" in self.context:
|
|
284
|
+
self.tokens.append(_user_id.set(self.context["user_id"]))
|
|
285
|
+
if "session_id" in self.context:
|
|
286
|
+
self.tokens.append(_session_id.set(self.context["session_id"]))
|
|
287
|
+
if "agent_id" in self.context:
|
|
288
|
+
self.tokens.append(_agent_id.set(self.context["agent_id"]))
|
|
289
|
+
if "workflow_id" in self.context:
|
|
290
|
+
self.tokens.append(_workflow_id.set(self.context["workflow_id"]))
|
|
291
|
+
return self
|
|
292
|
+
|
|
293
|
+
def __exit__(self, *args):
|
|
294
|
+
for token in reversed(self.tokens):
|
|
295
|
+
token.var.reset(token)
|
|
296
|
+
|
|
297
|
+
async def __aenter__(self):
|
|
298
|
+
return self.__enter__()
|
|
299
|
+
|
|
300
|
+
async def __aexit__(self, *args):
|
|
301
|
+
return self.__exit__(*args)
|