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 +152 -10
- agentreplay/context.py +133 -0
- agentreplay/decorators.py +545 -0
- agentreplay/install_pth.py +6 -2
- agentreplay/privacy.py +452 -0
- agentreplay/sdk.py +578 -0
- agentreplay/wrappers.py +522 -0
- agentreplay-0.1.3.dist-info/METADATA +1138 -0
- {agentreplay-0.1.2.dist-info → agentreplay-0.1.3.dist-info}/RECORD +13 -9
- agentreplay-0.1.2.dist-info/METADATA +0 -285
- {agentreplay-0.1.2.dist-info → agentreplay-0.1.3.dist-info}/WHEEL +0 -0
- {agentreplay-0.1.2.dist-info → agentreplay-0.1.3.dist-info}/entry_points.txt +0 -0
- {agentreplay-0.1.2.dist-info → agentreplay-0.1.3.dist-info}/licenses/LICENSE +0 -0
- {agentreplay-0.1.2.dist-info → agentreplay-0.1.3.dist-info}/top_level.txt +0 -0
agentreplay/wrappers.py
ADDED
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
# Copyright 2025 Sushanth (https://github.com/sushanthpy)
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
SDK client wrappers for automatic instrumentation.
|
|
17
|
+
|
|
18
|
+
Provides one-liner wrappers for popular LLM SDKs that automatically
|
|
19
|
+
trace all API calls without code changes.
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
>>> from openai import OpenAI
|
|
23
|
+
>>> from agentreplay import init, wrap_openai
|
|
24
|
+
>>>
|
|
25
|
+
>>> init()
|
|
26
|
+
>>> client = wrap_openai(OpenAI())
|
|
27
|
+
>>>
|
|
28
|
+
>>> # All calls are now traced automatically!
|
|
29
|
+
>>> response = client.chat.completions.create(
|
|
30
|
+
... model="gpt-4",
|
|
31
|
+
... messages=[{"role": "user", "content": "Hello!"}]
|
|
32
|
+
... )
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
import functools
|
|
36
|
+
import time
|
|
37
|
+
import logging
|
|
38
|
+
from typing import TypeVar, Any, Optional, Callable, Dict
|
|
39
|
+
|
|
40
|
+
logger = logging.getLogger(__name__)
|
|
41
|
+
|
|
42
|
+
T = TypeVar("T")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# =============================================================================
|
|
46
|
+
# OpenAI Wrapper
|
|
47
|
+
# =============================================================================
|
|
48
|
+
|
|
49
|
+
def wrap_openai(client: T, *, capture_content: bool = True) -> T:
|
|
50
|
+
"""Wrap an OpenAI client for automatic tracing.
|
|
51
|
+
|
|
52
|
+
Traces all chat.completions.create() and completions.create() calls,
|
|
53
|
+
automatically capturing:
|
|
54
|
+
- Model name
|
|
55
|
+
- Messages/prompt
|
|
56
|
+
- Response content
|
|
57
|
+
- Token usage
|
|
58
|
+
- Latency
|
|
59
|
+
- Errors
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
client: OpenAI client instance
|
|
63
|
+
capture_content: Whether to capture message content (disable for privacy)
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Wrapped client with same interface
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
>>> from openai import OpenAI
|
|
70
|
+
>>> from agentreplay import wrap_openai
|
|
71
|
+
>>>
|
|
72
|
+
>>> client = wrap_openai(OpenAI())
|
|
73
|
+
>>>
|
|
74
|
+
>>> response = client.chat.completions.create(
|
|
75
|
+
... model="gpt-4",
|
|
76
|
+
... messages=[{"role": "user", "content": "Hello!"}]
|
|
77
|
+
... ) # Automatically traced!
|
|
78
|
+
"""
|
|
79
|
+
try:
|
|
80
|
+
from agentreplay.decorators import ActiveSpan, SpanKind, _current_span
|
|
81
|
+
|
|
82
|
+
# Get original methods
|
|
83
|
+
original_chat_create = client.chat.completions.create
|
|
84
|
+
original_chat_create_async = getattr(
|
|
85
|
+
getattr(client, "chat", None),
|
|
86
|
+
"completions", None
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Check if async client
|
|
90
|
+
is_async = hasattr(client, "_async_client") or "AsyncOpenAI" in type(client).__name__
|
|
91
|
+
|
|
92
|
+
if is_async:
|
|
93
|
+
return _wrap_openai_async(client, capture_content)
|
|
94
|
+
|
|
95
|
+
# Wrap sync chat.completions.create
|
|
96
|
+
@functools.wraps(original_chat_create)
|
|
97
|
+
def wrapped_chat_create(*args, **kwargs):
|
|
98
|
+
# Get parent span
|
|
99
|
+
parent = _current_span.get()
|
|
100
|
+
|
|
101
|
+
# Create span
|
|
102
|
+
span = ActiveSpan(
|
|
103
|
+
name="openai.chat.completions.create",
|
|
104
|
+
kind=SpanKind.LLM,
|
|
105
|
+
parent_id=parent.span_id if parent else None,
|
|
106
|
+
trace_id=parent.trace_id if parent else None,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Set attributes
|
|
110
|
+
model = kwargs.get("model", "unknown")
|
|
111
|
+
span.set_model(model, provider="openai")
|
|
112
|
+
span.set_attribute("llm.request.type", "chat")
|
|
113
|
+
|
|
114
|
+
# Capture input
|
|
115
|
+
if capture_content:
|
|
116
|
+
messages = kwargs.get("messages", [])
|
|
117
|
+
span.set_input({"messages": messages})
|
|
118
|
+
|
|
119
|
+
with span:
|
|
120
|
+
try:
|
|
121
|
+
response = original_chat_create(*args, **kwargs)
|
|
122
|
+
|
|
123
|
+
# Capture output
|
|
124
|
+
if capture_content and hasattr(response, "choices") and response.choices:
|
|
125
|
+
output_content = response.choices[0].message.content
|
|
126
|
+
span.set_output({"content": output_content})
|
|
127
|
+
|
|
128
|
+
# Capture token usage
|
|
129
|
+
if hasattr(response, "usage") and response.usage:
|
|
130
|
+
span.set_token_usage(
|
|
131
|
+
prompt_tokens=response.usage.prompt_tokens,
|
|
132
|
+
completion_tokens=response.usage.completion_tokens,
|
|
133
|
+
total_tokens=response.usage.total_tokens,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Capture finish reason
|
|
137
|
+
if hasattr(response, "choices") and response.choices:
|
|
138
|
+
span.set_attribute(
|
|
139
|
+
"llm.response.finish_reason",
|
|
140
|
+
response.choices[0].finish_reason
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
return response
|
|
144
|
+
|
|
145
|
+
except Exception as e:
|
|
146
|
+
span.set_error(e)
|
|
147
|
+
raise
|
|
148
|
+
|
|
149
|
+
# Monkey-patch
|
|
150
|
+
client.chat.completions.create = wrapped_chat_create
|
|
151
|
+
|
|
152
|
+
# Also wrap embeddings if present
|
|
153
|
+
if hasattr(client, "embeddings"):
|
|
154
|
+
_wrap_openai_embeddings(client, capture_content)
|
|
155
|
+
|
|
156
|
+
return client
|
|
157
|
+
|
|
158
|
+
except Exception as e:
|
|
159
|
+
logger.warning(f"Failed to wrap OpenAI client: {e}. Returning unwrapped client.")
|
|
160
|
+
return client
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _wrap_openai_async(client: T, capture_content: bool) -> T:
|
|
164
|
+
"""Wrap async OpenAI client."""
|
|
165
|
+
try:
|
|
166
|
+
from agentreplay.decorators import ActiveSpan, SpanKind, _current_span
|
|
167
|
+
|
|
168
|
+
original_chat_create = client.chat.completions.create
|
|
169
|
+
|
|
170
|
+
@functools.wraps(original_chat_create)
|
|
171
|
+
async def wrapped_chat_create(*args, **kwargs):
|
|
172
|
+
parent = _current_span.get()
|
|
173
|
+
|
|
174
|
+
span = ActiveSpan(
|
|
175
|
+
name="openai.chat.completions.create",
|
|
176
|
+
kind=SpanKind.LLM,
|
|
177
|
+
parent_id=parent.span_id if parent else None,
|
|
178
|
+
trace_id=parent.trace_id if parent else None,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
model = kwargs.get("model", "unknown")
|
|
182
|
+
span.set_model(model, provider="openai")
|
|
183
|
+
span.set_attribute("llm.request.type", "chat")
|
|
184
|
+
|
|
185
|
+
if capture_content:
|
|
186
|
+
messages = kwargs.get("messages", [])
|
|
187
|
+
span.set_input({"messages": messages})
|
|
188
|
+
|
|
189
|
+
with span:
|
|
190
|
+
try:
|
|
191
|
+
response = await original_chat_create(*args, **kwargs)
|
|
192
|
+
|
|
193
|
+
if capture_content and hasattr(response, "choices") and response.choices:
|
|
194
|
+
output_content = response.choices[0].message.content
|
|
195
|
+
span.set_output({"content": output_content})
|
|
196
|
+
|
|
197
|
+
if hasattr(response, "usage") and response.usage:
|
|
198
|
+
span.set_token_usage(
|
|
199
|
+
prompt_tokens=response.usage.prompt_tokens,
|
|
200
|
+
completion_tokens=response.usage.completion_tokens,
|
|
201
|
+
total_tokens=response.usage.total_tokens,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
if hasattr(response, "choices") and response.choices:
|
|
205
|
+
span.set_attribute(
|
|
206
|
+
"llm.response.finish_reason",
|
|
207
|
+
response.choices[0].finish_reason
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
return response
|
|
211
|
+
|
|
212
|
+
except Exception as e:
|
|
213
|
+
span.set_error(e)
|
|
214
|
+
raise
|
|
215
|
+
|
|
216
|
+
client.chat.completions.create = wrapped_chat_create
|
|
217
|
+
return client
|
|
218
|
+
|
|
219
|
+
except Exception as e:
|
|
220
|
+
logger.warning(f"Failed to wrap async OpenAI client: {e}")
|
|
221
|
+
return client
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _wrap_openai_embeddings(client: T, capture_content: bool) -> None:
|
|
225
|
+
"""Wrap OpenAI embeddings."""
|
|
226
|
+
try:
|
|
227
|
+
from agentreplay.decorators import ActiveSpan, SpanKind, _current_span
|
|
228
|
+
|
|
229
|
+
original_create = client.embeddings.create
|
|
230
|
+
|
|
231
|
+
@functools.wraps(original_create)
|
|
232
|
+
def wrapped_create(*args, **kwargs):
|
|
233
|
+
parent = _current_span.get()
|
|
234
|
+
|
|
235
|
+
span = ActiveSpan(
|
|
236
|
+
name="openai.embeddings.create",
|
|
237
|
+
kind=SpanKind.EMBEDDING,
|
|
238
|
+
parent_id=parent.span_id if parent else None,
|
|
239
|
+
trace_id=parent.trace_id if parent else None,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
model = kwargs.get("model", "text-embedding-ada-002")
|
|
243
|
+
span.set_model(model, provider="openai")
|
|
244
|
+
span.set_attribute("llm.request.type", "embedding")
|
|
245
|
+
|
|
246
|
+
# Capture input count (not content for privacy)
|
|
247
|
+
input_data = kwargs.get("input", [])
|
|
248
|
+
if isinstance(input_data, str):
|
|
249
|
+
span.set_attribute("embedding.input_count", 1)
|
|
250
|
+
else:
|
|
251
|
+
span.set_attribute("embedding.input_count", len(input_data))
|
|
252
|
+
|
|
253
|
+
with span:
|
|
254
|
+
try:
|
|
255
|
+
response = original_create(*args, **kwargs)
|
|
256
|
+
|
|
257
|
+
if hasattr(response, "usage") and response.usage:
|
|
258
|
+
span.set_token_usage(
|
|
259
|
+
prompt_tokens=response.usage.prompt_tokens,
|
|
260
|
+
total_tokens=response.usage.total_tokens,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
if hasattr(response, "data"):
|
|
264
|
+
span.set_attribute("embedding.output_count", len(response.data))
|
|
265
|
+
|
|
266
|
+
return response
|
|
267
|
+
|
|
268
|
+
except Exception as e:
|
|
269
|
+
span.set_error(e)
|
|
270
|
+
raise
|
|
271
|
+
|
|
272
|
+
client.embeddings.create = wrapped_create
|
|
273
|
+
|
|
274
|
+
except Exception as e:
|
|
275
|
+
logger.debug(f"Failed to wrap embeddings: {e}")
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
# =============================================================================
|
|
279
|
+
# Anthropic Wrapper
|
|
280
|
+
# =============================================================================
|
|
281
|
+
|
|
282
|
+
def wrap_anthropic(client: T, *, capture_content: bool = True) -> T:
|
|
283
|
+
"""Wrap an Anthropic client for automatic tracing.
|
|
284
|
+
|
|
285
|
+
Traces all messages.create() calls, automatically capturing:
|
|
286
|
+
- Model name
|
|
287
|
+
- Messages/prompt
|
|
288
|
+
- Response content
|
|
289
|
+
- Token usage
|
|
290
|
+
- Latency
|
|
291
|
+
- Errors
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
client: Anthropic client instance
|
|
295
|
+
capture_content: Whether to capture message content
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
Wrapped client with same interface
|
|
299
|
+
|
|
300
|
+
Example:
|
|
301
|
+
>>> from anthropic import Anthropic
|
|
302
|
+
>>> from agentreplay import wrap_anthropic
|
|
303
|
+
>>>
|
|
304
|
+
>>> client = wrap_anthropic(Anthropic())
|
|
305
|
+
>>>
|
|
306
|
+
>>> message = client.messages.create(
|
|
307
|
+
... model="claude-3-opus-20240229",
|
|
308
|
+
... messages=[{"role": "user", "content": "Hello!"}]
|
|
309
|
+
... ) # Automatically traced!
|
|
310
|
+
"""
|
|
311
|
+
try:
|
|
312
|
+
from agentreplay.decorators import ActiveSpan, SpanKind, _current_span
|
|
313
|
+
|
|
314
|
+
# Check if async
|
|
315
|
+
is_async = "AsyncAnthropic" in type(client).__name__
|
|
316
|
+
|
|
317
|
+
if is_async:
|
|
318
|
+
return _wrap_anthropic_async(client, capture_content)
|
|
319
|
+
|
|
320
|
+
original_messages_create = client.messages.create
|
|
321
|
+
|
|
322
|
+
@functools.wraps(original_messages_create)
|
|
323
|
+
def wrapped_messages_create(*args, **kwargs):
|
|
324
|
+
parent = _current_span.get()
|
|
325
|
+
|
|
326
|
+
span = ActiveSpan(
|
|
327
|
+
name="anthropic.messages.create",
|
|
328
|
+
kind=SpanKind.LLM,
|
|
329
|
+
parent_id=parent.span_id if parent else None,
|
|
330
|
+
trace_id=parent.trace_id if parent else None,
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
model = kwargs.get("model", "claude-3")
|
|
334
|
+
span.set_model(model, provider="anthropic")
|
|
335
|
+
span.set_attribute("llm.request.type", "chat")
|
|
336
|
+
|
|
337
|
+
if capture_content:
|
|
338
|
+
messages = kwargs.get("messages", [])
|
|
339
|
+
system = kwargs.get("system")
|
|
340
|
+
input_data = {"messages": messages}
|
|
341
|
+
if system:
|
|
342
|
+
input_data["system"] = system
|
|
343
|
+
span.set_input(input_data)
|
|
344
|
+
|
|
345
|
+
with span:
|
|
346
|
+
try:
|
|
347
|
+
response = original_messages_create(*args, **kwargs)
|
|
348
|
+
|
|
349
|
+
if capture_content and hasattr(response, "content") and response.content:
|
|
350
|
+
content = response.content[0]
|
|
351
|
+
if hasattr(content, "text"):
|
|
352
|
+
span.set_output({"content": content.text})
|
|
353
|
+
|
|
354
|
+
if hasattr(response, "usage"):
|
|
355
|
+
span.set_token_usage(
|
|
356
|
+
prompt_tokens=response.usage.input_tokens,
|
|
357
|
+
completion_tokens=response.usage.output_tokens,
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
if hasattr(response, "stop_reason"):
|
|
361
|
+
span.set_attribute("llm.response.finish_reason", response.stop_reason)
|
|
362
|
+
|
|
363
|
+
return response
|
|
364
|
+
|
|
365
|
+
except Exception as e:
|
|
366
|
+
span.set_error(e)
|
|
367
|
+
raise
|
|
368
|
+
|
|
369
|
+
client.messages.create = wrapped_messages_create
|
|
370
|
+
return client
|
|
371
|
+
|
|
372
|
+
except Exception as e:
|
|
373
|
+
logger.warning(f"Failed to wrap Anthropic client: {e}. Returning unwrapped client.")
|
|
374
|
+
return client
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def _wrap_anthropic_async(client: T, capture_content: bool) -> T:
|
|
378
|
+
"""Wrap async Anthropic client."""
|
|
379
|
+
try:
|
|
380
|
+
from agentreplay.decorators import ActiveSpan, SpanKind, _current_span
|
|
381
|
+
|
|
382
|
+
original_messages_create = client.messages.create
|
|
383
|
+
|
|
384
|
+
@functools.wraps(original_messages_create)
|
|
385
|
+
async def wrapped_messages_create(*args, **kwargs):
|
|
386
|
+
parent = _current_span.get()
|
|
387
|
+
|
|
388
|
+
span = ActiveSpan(
|
|
389
|
+
name="anthropic.messages.create",
|
|
390
|
+
kind=SpanKind.LLM,
|
|
391
|
+
parent_id=parent.span_id if parent else None,
|
|
392
|
+
trace_id=parent.trace_id if parent else None,
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
model = kwargs.get("model", "claude-3")
|
|
396
|
+
span.set_model(model, provider="anthropic")
|
|
397
|
+
span.set_attribute("llm.request.type", "chat")
|
|
398
|
+
|
|
399
|
+
if capture_content:
|
|
400
|
+
messages = kwargs.get("messages", [])
|
|
401
|
+
system = kwargs.get("system")
|
|
402
|
+
input_data = {"messages": messages}
|
|
403
|
+
if system:
|
|
404
|
+
input_data["system"] = system
|
|
405
|
+
span.set_input(input_data)
|
|
406
|
+
|
|
407
|
+
with span:
|
|
408
|
+
try:
|
|
409
|
+
response = await original_messages_create(*args, **kwargs)
|
|
410
|
+
|
|
411
|
+
if capture_content and hasattr(response, "content") and response.content:
|
|
412
|
+
content = response.content[0]
|
|
413
|
+
if hasattr(content, "text"):
|
|
414
|
+
span.set_output({"content": content.text})
|
|
415
|
+
|
|
416
|
+
if hasattr(response, "usage"):
|
|
417
|
+
span.set_token_usage(
|
|
418
|
+
prompt_tokens=response.usage.input_tokens,
|
|
419
|
+
completion_tokens=response.usage.output_tokens,
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
if hasattr(response, "stop_reason"):
|
|
423
|
+
span.set_attribute("llm.response.finish_reason", response.stop_reason)
|
|
424
|
+
|
|
425
|
+
return response
|
|
426
|
+
|
|
427
|
+
except Exception as e:
|
|
428
|
+
span.set_error(e)
|
|
429
|
+
raise
|
|
430
|
+
|
|
431
|
+
client.messages.create = wrapped_messages_create
|
|
432
|
+
return client
|
|
433
|
+
|
|
434
|
+
except Exception as e:
|
|
435
|
+
logger.warning(f"Failed to wrap async Anthropic client: {e}")
|
|
436
|
+
return client
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
# =============================================================================
|
|
440
|
+
# Generic Wrapper
|
|
441
|
+
# =============================================================================
|
|
442
|
+
|
|
443
|
+
def wrap_method(
|
|
444
|
+
obj: Any,
|
|
445
|
+
method_name: str,
|
|
446
|
+
*,
|
|
447
|
+
span_name: Optional[str] = None,
|
|
448
|
+
kind: str = "chain",
|
|
449
|
+
capture_input: bool = True,
|
|
450
|
+
capture_output: bool = True,
|
|
451
|
+
) -> None:
|
|
452
|
+
"""Wrap a specific method on an object for tracing.
|
|
453
|
+
|
|
454
|
+
Low-level utility for custom instrumentation.
|
|
455
|
+
|
|
456
|
+
Args:
|
|
457
|
+
obj: Object containing the method
|
|
458
|
+
method_name: Name of the method to wrap
|
|
459
|
+
span_name: Name for the span (default: method name)
|
|
460
|
+
kind: Span kind
|
|
461
|
+
capture_input: Whether to capture method args
|
|
462
|
+
capture_output: Whether to capture return value
|
|
463
|
+
|
|
464
|
+
Example:
|
|
465
|
+
>>> wrap_method(my_service, "call_api", span_name="api.call", kind="http")
|
|
466
|
+
"""
|
|
467
|
+
from agentreplay.decorators import ActiveSpan, _current_span
|
|
468
|
+
import inspect
|
|
469
|
+
|
|
470
|
+
original_method = getattr(obj, method_name)
|
|
471
|
+
name = span_name or method_name
|
|
472
|
+
|
|
473
|
+
if inspect.iscoroutinefunction(original_method):
|
|
474
|
+
@functools.wraps(original_method)
|
|
475
|
+
async def async_wrapped(*args, **kwargs):
|
|
476
|
+
parent = _current_span.get()
|
|
477
|
+
span = ActiveSpan(
|
|
478
|
+
name=name,
|
|
479
|
+
kind=kind,
|
|
480
|
+
parent_id=parent.span_id if parent else None,
|
|
481
|
+
trace_id=parent.trace_id if parent else None,
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
if capture_input:
|
|
485
|
+
span.set_input({"args": args, "kwargs": kwargs})
|
|
486
|
+
|
|
487
|
+
with span:
|
|
488
|
+
try:
|
|
489
|
+
result = await original_method(*args, **kwargs)
|
|
490
|
+
if capture_output:
|
|
491
|
+
span.set_output(result)
|
|
492
|
+
return result
|
|
493
|
+
except Exception as e:
|
|
494
|
+
span.set_error(e)
|
|
495
|
+
raise
|
|
496
|
+
|
|
497
|
+
setattr(obj, method_name, async_wrapped)
|
|
498
|
+
else:
|
|
499
|
+
@functools.wraps(original_method)
|
|
500
|
+
def sync_wrapped(*args, **kwargs):
|
|
501
|
+
parent = _current_span.get()
|
|
502
|
+
span = ActiveSpan(
|
|
503
|
+
name=name,
|
|
504
|
+
kind=kind,
|
|
505
|
+
parent_id=parent.span_id if parent else None,
|
|
506
|
+
trace_id=parent.trace_id if parent else None,
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
if capture_input:
|
|
510
|
+
span.set_input({"args": args, "kwargs": kwargs})
|
|
511
|
+
|
|
512
|
+
with span:
|
|
513
|
+
try:
|
|
514
|
+
result = original_method(*args, **kwargs)
|
|
515
|
+
if capture_output:
|
|
516
|
+
span.set_output(result)
|
|
517
|
+
return result
|
|
518
|
+
except Exception as e:
|
|
519
|
+
span.set_error(e)
|
|
520
|
+
raise
|
|
521
|
+
|
|
522
|
+
setattr(obj, method_name, sync_wrapped)
|