agentreplay 0.1.2__py3-none-any.whl → 0.1.3__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 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
- """Agentreplay Python SDK - Agent Trace Engine for LLM Agents."""
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 AgentContext
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
- # Auto-instrumentation (Pure OpenTelemetry)
38
- from agentreplay.auto_instrument import auto_instrument, setup_instrumentation
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
- # OTEL Bridge & Bootstrap
41
- from agentreplay.bootstrap import init_otel_instrumentation, is_initialized
42
- from agentreplay.otel_bridge import get_tracer
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.1.2"
132
+ __version__ = "0.1.3"
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
- # Agent Context
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)