langtrace-python-sdk 3.8.3__py3-none-any.whl → 3.8.4__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.
@@ -24,6 +24,7 @@ from .milvus import MilvusInstrumentation
24
24
  from .mistral import MistralInstrumentation
25
25
  from .ollama import OllamaInstrumentor
26
26
  from .openai import OpenAIInstrumentation
27
+ from .openai_agents import OpenAIAgentsInstrumentation
27
28
  from .phidata import PhiDataInstrumentation
28
29
  from .pinecone import PineconeInstrumentation
29
30
  from .pymongo import PyMongoInstrumentation
@@ -64,4 +65,5 @@ __all__ = [
64
65
  "PhiDataInstrumentation",
65
66
  "AgnoInstrumentation",
66
67
  "CleanLabInstrumentation",
68
+ "OpenAIAgentsInstrumentation",
67
69
  ]
@@ -1,5 +1,5 @@
1
1
  """
2
- Copyright (c) 2024 Scale3 Labs
2
+ Copyright (c) 2025 Scale3 Labs
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -14,13 +14,16 @@ See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  """
16
16
 
17
+ from typing import Collection
18
+
19
+ from importlib_metadata import version as v
17
20
  from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
18
21
  from opentelemetry.trace import get_tracer
19
22
  from wrapt import wrap_function_wrapper as _W
20
- from typing import Collection
21
- from importlib_metadata import version as v
23
+
22
24
  from .patch import patch_agent, patch_memory
23
25
 
26
+
24
27
  class AgnoInstrumentation(BaseInstrumentor):
25
28
  def instrumentation_dependencies(self) -> Collection[str]:
26
29
  return ["agno >= 1.1.4"]
@@ -0,0 +1,5 @@
1
+ from .instrumentation import OpenAIAgentsInstrumentation
2
+
3
+ __all__ = [
4
+ "OpenAIAgentsInstrumentation",
5
+ ]
@@ -0,0 +1,52 @@
1
+ """
2
+ Copyright (c) 2025 Scale3 Labs
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
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
12
+ """
13
+
14
+ import importlib.metadata
15
+ import logging
16
+ from typing import Any, Collection, Optional
17
+
18
+ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
19
+ from opentelemetry.trace import TracerProvider, get_tracer
20
+ from wrapt import wrap_function_wrapper
21
+
22
+ from langtrace_python_sdk.instrumentation.openai_agents.patch import \
23
+ get_new_response
24
+
25
+ logging.basicConfig(level=logging.FATAL)
26
+
27
+
28
+ class OpenAIAgentsInstrumentation(BaseInstrumentor): # type: ignore
29
+
30
+ def instrumentation_dependencies(self) -> Collection[str]:
31
+ return ["openai-agents >= 0.0.3", "trace-attributes >= 4.0.5"]
32
+
33
+ def _instrument(self, **kwargs: Any) -> None:
34
+ tracer_provider: Optional[TracerProvider] = kwargs.get("tracer_provider")
35
+ tracer = get_tracer(__name__, "", tracer_provider)
36
+ version: str = importlib.metadata.version("openai")
37
+
38
+ # TODO(Karthik): This is adding a lot of noise to the trace.
39
+ # wrap_function_wrapper(
40
+ # "agents.run",
41
+ # "Runner._get_handoffs",
42
+ # get_handoffs(version, tracer),
43
+ # )
44
+
45
+ wrap_function_wrapper(
46
+ "agents.run",
47
+ "Runner._get_new_response",
48
+ get_new_response(version, tracer),
49
+ )
50
+
51
+ def _uninstrument(self, **kwargs: Any) -> None:
52
+ pass
@@ -0,0 +1,533 @@
1
+ import json
2
+ from typing import Any, Callable, List
3
+
4
+ from agents.exceptions import (InputGuardrailTripwireTriggered,
5
+ OutputGuardrailTripwireTriggered)
6
+ from agents.run import Runner
7
+ from importlib_metadata import version as v
8
+ from langtrace.trace_attributes import FrameworkSpanAttributes, SpanAttributes
9
+ from opentelemetry import baggage, trace
10
+ from opentelemetry.trace import SpanKind, Tracer
11
+ from opentelemetry.trace.status import Status, StatusCode
12
+
13
+ from langtrace_python_sdk.constants import LANGTRACE_SDK_NAME
14
+ from langtrace_python_sdk.constants.instrumentation.common import (
15
+ LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY, SERVICE_PROVIDERS)
16
+ from langtrace_python_sdk.utils.llm import (set_event_completion,
17
+ set_span_attributes,
18
+ set_usage_attributes)
19
+
20
+
21
+ def extract_agent_details(agent_or_handoff):
22
+ """Extract relevant details from an agent/handoff and its handoffs."""
23
+ try:
24
+ if agent_or_handoff is None:
25
+ return None
26
+
27
+ # Handle both Agent and Handoff types
28
+ if hasattr(agent_or_handoff, 'agent'): # This is a Handoff
29
+ agent = agent_or_handoff.agent
30
+ else: # This is an Agent
31
+ agent = agent_or_handoff
32
+
33
+ if agent is None:
34
+ return None
35
+
36
+ agent_details = {
37
+ "name": getattr(agent, 'name', None),
38
+ "instructions": getattr(agent, 'instructions', None),
39
+ "handoff_description": getattr(agent, 'handoff_description', None),
40
+ "handoffs": []
41
+ }
42
+
43
+ if hasattr(agent, 'handoffs') and agent.handoffs:
44
+ for handoff_item in agent.handoffs:
45
+ handoff_details = extract_agent_details(handoff_item)
46
+ if handoff_details:
47
+ agent_details["handoffs"].append(handoff_details)
48
+
49
+ return agent_details
50
+ except Exception: # Catch all exceptions and fail silently
51
+ return None
52
+
53
+
54
+ def extract_handoff_details(handoff):
55
+ """Extract relevant details from a Handoff object."""
56
+ try:
57
+ if handoff is None:
58
+ return None
59
+
60
+ return {
61
+ "tool_name": getattr(handoff, 'tool_name', None),
62
+ "tool_description": getattr(handoff, 'tool_description', None),
63
+ "agent_name": getattr(handoff, 'agent_name', None),
64
+ "input_json_schema": getattr(handoff, 'input_json_schema', {}),
65
+ "strict_json_schema": getattr(handoff, 'strict_json_schema', False)
66
+ }
67
+ except Exception: # Catch all exceptions and fail silently
68
+ return None
69
+
70
+
71
+ def get_handoffs(version: str, tracer: Tracer) -> Callable:
72
+ """Wrap the `prompt` method of the `TLM` class to trace it."""
73
+
74
+ def traced_method(
75
+ wrapped: Callable,
76
+ instance: Any,
77
+ args: List[Any],
78
+ kwargs: Any,
79
+ ) -> Any:
80
+ try:
81
+ service_provider = SERVICE_PROVIDERS["OPENAI"]
82
+ extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
83
+ span_attributes = {
84
+ "langtrace.sdk.name": "langtrace-python-sdk",
85
+ "langtrace.service.name": service_provider,
86
+ "langtrace.service.type": "framework",
87
+ "langtrace.service.version": version,
88
+ "langtrace.version": v(LANGTRACE_SDK_NAME),
89
+ **(extra_attributes if extra_attributes is not None else {}),
90
+ }
91
+
92
+ # Process agents from args
93
+ agents_list = []
94
+ if args:
95
+ for arg in args:
96
+ try:
97
+ if arg is not None:
98
+ if hasattr(arg, 'name') or hasattr(arg, 'agent'):
99
+ agent_details = extract_agent_details(arg)
100
+ if agent_details:
101
+ agents_list.append(agent_details)
102
+ elif isinstance(arg, (list, tuple)):
103
+ for item in arg:
104
+ if item is not None and (hasattr(item, 'name') or hasattr(item, 'agent')):
105
+ agent_details = extract_agent_details(item)
106
+ if agent_details:
107
+ agents_list.append(agent_details)
108
+ except Exception:
109
+ continue # Skip any errors in processing individual arguments
110
+
111
+ if agents_list:
112
+ try:
113
+ span_attributes["openai_agents.agents"] = json.dumps(agents_list)
114
+ except Exception:
115
+ pass # Silently fail if JSON serialization fails
116
+
117
+ attributes = FrameworkSpanAttributes(**span_attributes)
118
+
119
+ with tracer.start_as_current_span(
120
+ name=f"openai_agents.available_handoffs", kind=SpanKind.CLIENT
121
+ ) as span:
122
+ try:
123
+ set_span_attributes(span, attributes)
124
+ result = wrapped(*args, **kwargs)
125
+
126
+ # Process handoff results
127
+ if result is not None:
128
+ handoffs_list = []
129
+ try:
130
+ if isinstance(result, (list, tuple)):
131
+ for handoff in result:
132
+ if handoff is not None and hasattr(handoff, 'tool_name'):
133
+ handoff_details = extract_handoff_details(handoff)
134
+ if handoff_details:
135
+ handoffs_list.append(handoff_details)
136
+ elif hasattr(result, 'tool_name'):
137
+ handoff_details = extract_handoff_details(result)
138
+ if handoff_details:
139
+ handoffs_list.append(handoff_details)
140
+
141
+ if handoffs_list:
142
+ try:
143
+ span.set_attribute("openai_agents.handoffs", json.dumps(handoffs_list))
144
+ span.set_status(Status(StatusCode.OK))
145
+ except Exception:
146
+ pass # Silently fail if JSON serialization fails
147
+ except Exception:
148
+ pass # Silently fail if handoff processing fails
149
+
150
+ return result
151
+
152
+ except Exception as err:
153
+ try:
154
+ span.record_exception(err)
155
+ span.set_status(Status(StatusCode.ERROR, str(err)))
156
+ except Exception:
157
+ pass # Silently fail if error recording fails
158
+ raise # Re-raise the original error since it's from the wrapped function
159
+
160
+ except Exception as outer_err:
161
+ # If anything fails in our instrumentation wrapper, catch it and return control to the wrapped function
162
+ try:
163
+ return wrapped(*args, **kwargs)
164
+ except Exception as wrapped_err:
165
+ raise wrapped_err # Only raise errors from the wrapped function
166
+
167
+ return traced_method
168
+
169
+
170
+ def extract_response_input_details(input_item):
171
+ """Extract relevant details from a response input item."""
172
+ try:
173
+ if input_item is None:
174
+ return None
175
+
176
+ details = {
177
+ "type": input_item.__class__.__name__,
178
+ }
179
+
180
+ # Extract common attributes that might be present
181
+ for attr in ['content', 'role', 'name', 'tool_name', 'tool_call_id']:
182
+ if hasattr(input_item, attr):
183
+ value = getattr(input_item, attr)
184
+ if value is not None:
185
+ details[attr] = value
186
+
187
+ return details
188
+ except Exception:
189
+ return None
190
+
191
+
192
+ def extract_model_response(response):
193
+ """Extract relevant details from a ModelResponse."""
194
+ try:
195
+ if response is None:
196
+ return None
197
+
198
+ response_dict = {
199
+ "referenceable_id": getattr(response, "referenceable_id", None),
200
+ "usage": {
201
+ "requests": getattr(response.usage, "requests", 0),
202
+ "input_tokens": getattr(response.usage, "input_tokens", 0),
203
+ "output_tokens": getattr(response.usage, "output_tokens", 0),
204
+ "total_tokens": getattr(response.usage, "total_tokens", 0)
205
+ },
206
+ "output": []
207
+ }
208
+
209
+ # Extract output messages or function calls
210
+ if response.output:
211
+ for output_item in response.output:
212
+ if hasattr(output_item, 'type'):
213
+ if output_item.type == 'function_call':
214
+ # Handle function call
215
+ function_call = {
216
+ "id": getattr(output_item, "id", None),
217
+ "type": "function_call",
218
+ "status": getattr(output_item, "status", None),
219
+ "call_id": getattr(output_item, "call_id", None),
220
+ "name": getattr(output_item, "name", None),
221
+ "arguments": getattr(output_item, "arguments", "{}")
222
+ }
223
+ response_dict["output"].append(function_call)
224
+ # Set response type as function_call and capture the function name
225
+ response_dict["response_type"] = "function_call"
226
+ response_dict["function"] = getattr(output_item, "name", None)
227
+ elif output_item.type == 'message':
228
+ # Handle regular message
229
+ message = {
230
+ "id": getattr(output_item, "id", None),
231
+ "role": getattr(output_item, "role", None),
232
+ "status": getattr(output_item, "status", None),
233
+ "type": "message",
234
+ "content": []
235
+ }
236
+
237
+ # Extract content (text, annotations, etc.)
238
+ if hasattr(output_item, 'content') and output_item.content:
239
+ for content_item in output_item.content:
240
+ content_dict = {
241
+ "type": getattr(content_item, "type", None),
242
+ "text": getattr(content_item, "text", None),
243
+ "annotations": getattr(content_item, "annotations", [])
244
+ }
245
+ message["content"].append(content_dict)
246
+
247
+ response_dict["output"].append(message)
248
+ # Set response type as response for messages
249
+ if "response_type" not in response_dict:
250
+ response_dict["response_type"] = "response"
251
+
252
+ return response_dict
253
+ except Exception:
254
+ return None
255
+
256
+
257
+ def extract_message_history(messages):
258
+ """Extract relevant details from message history."""
259
+ try:
260
+ if not messages or not isinstance(messages, list):
261
+ return []
262
+
263
+ history = []
264
+ for msg in messages:
265
+ if not isinstance(msg, dict):
266
+ continue
267
+
268
+ message = {}
269
+ # Extract common fields
270
+ for key in ['content', 'role', 'id', 'type', 'status']:
271
+ if key in msg:
272
+ message[key] = msg[key]
273
+
274
+ # Extract function call specific fields
275
+ if msg.get('type') == 'function_call':
276
+ for key in ['arguments', 'call_id', 'name']:
277
+ if key in msg:
278
+ message[key] = msg[key]
279
+
280
+ # Extract function output
281
+ if msg.get('type') == 'function_call_output':
282
+ for key in ['call_id', 'output']:
283
+ if key in msg:
284
+ message[key] = msg[key]
285
+
286
+ if message:
287
+ history.append(message)
288
+
289
+ return history
290
+ except Exception:
291
+ return []
292
+
293
+
294
+ def extract_run_context(context):
295
+ """Extract relevant details from RunContextWrapper."""
296
+ try:
297
+ if context is None:
298
+ return None
299
+
300
+ return {
301
+ "usage": {
302
+ "requests": getattr(context.usage, "requests", 0),
303
+ "input_tokens": getattr(context.usage, "input_tokens", 0),
304
+ "output_tokens": getattr(context.usage, "output_tokens", 0),
305
+ "total_tokens": getattr(context.usage, "total_tokens", 0)
306
+ }
307
+ }
308
+ except Exception:
309
+ return None
310
+
311
+
312
+ def extract_run_config(config):
313
+ """Extract relevant details from RunConfig."""
314
+ try:
315
+ if config is None:
316
+ return None
317
+
318
+ return {
319
+ "workflow_name": getattr(config, "workflow_name", None),
320
+ "trace_id": getattr(config, "trace_id", None),
321
+ "group_id": getattr(config, "group_id", None),
322
+ "tracing_disabled": getattr(config, "tracing_disabled", False),
323
+ "trace_include_sensitive_data": getattr(config, "trace_include_sensitive_data", True)
324
+ }
325
+ except Exception:
326
+ return None
327
+
328
+
329
+ def get_new_response(version: str, tracer: Tracer) -> Callable:
330
+ """Wrap the _get_new_response method to trace inputs and outputs."""
331
+
332
+ async def traced_method(
333
+ wrapped: Callable,
334
+ instance: Any,
335
+ args: List[Any],
336
+ kwargs: Any,
337
+ ) -> Any:
338
+ try:
339
+ service_provider = SERVICE_PROVIDERS["OPENAI"]
340
+ extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
341
+ span_attributes = {
342
+ "langtrace.sdk.name": "langtrace-python-sdk",
343
+ "langtrace.service.name": service_provider,
344
+ "langtrace.service.type": "framework",
345
+ "langtrace.service.version": version,
346
+ "langtrace.version": v(LANGTRACE_SDK_NAME),
347
+ **(extra_attributes if extra_attributes is not None else {}),
348
+ }
349
+
350
+ # Process input arguments
351
+ try:
352
+ if args and len(args) >= 7: # Check if we have all expected arguments
353
+ agent = args[0]
354
+ agent_name = getattr(agent, 'name', None) if agent else None
355
+ input_details = {
356
+ "agent": extract_agent_details(agent),
357
+ "instructions": args[1],
358
+ "message_history": extract_message_history(args[2]),
359
+ "run_context": extract_run_context(args[5]),
360
+ "run_config": extract_run_config(args[6])
361
+ }
362
+ span_attributes["openai_agents.inputs"] = json.dumps(input_details)
363
+
364
+ # Set standard LLM prompts attribute
365
+ if args[2]: # message_history exists
366
+ messages = []
367
+ for msg in args[2]:
368
+ if isinstance(msg, dict):
369
+ messages.append({
370
+ "role": msg.get("role", "user"),
371
+ "content": msg.get("content", "")
372
+ })
373
+ if messages:
374
+ span_attributes[SpanAttributes.LLM_PROMPTS] = json.dumps(messages)
375
+ except Exception:
376
+ pass # Silently fail if input processing fails
377
+
378
+ attributes = FrameworkSpanAttributes(**span_attributes)
379
+ # Determine span name based on agent name
380
+ agent_name = getattr(args[0], 'name', None) if args and len(args) > 0 else None
381
+ span_name = (f"openai_agents.{agent_name}" if agent_name
382
+ else "openai_agents.agent_response")
383
+
384
+ with tracer.start_as_current_span(
385
+ name=span_name,
386
+ kind=SpanKind.CLIENT
387
+ ) as span:
388
+ try:
389
+ set_span_attributes(span, attributes)
390
+
391
+ # Get the model from _get_model
392
+ agent = args[0] if args and len(args) > 0 else None
393
+ run_config = args[6] if args and len(args) > 6 else None
394
+ if agent and run_config:
395
+ try:
396
+ model = Runner._get_model(agent, run_config)
397
+ if hasattr(model, 'model'):
398
+ model_name = None
399
+ if isinstance(model.model, str):
400
+ model_name = model.model
401
+ elif hasattr(model.model, 'model_name'):
402
+ model_name = model.model.model_name
403
+ if model_name:
404
+ span.set_attribute(SpanAttributes.LLM_REQUEST_MODEL, model_name)
405
+ except Exception:
406
+ pass # Silently fail if model extraction fails
407
+
408
+ result = await wrapped(*args, **kwargs)
409
+
410
+ # Process response output
411
+ if result is not None:
412
+ try:
413
+ response_dict = extract_model_response(result)
414
+ if response_dict:
415
+ # Set the main response output
416
+ span.set_attribute("openai_agents.outputs", json.dumps(response_dict))
417
+
418
+ # Set response type and function details as separate attributes for easier querying
419
+ span.set_attribute("openai_agents.response_type", response_dict.get("response_type", "response"))
420
+ if response_dict.get("response_type") == "function_call" and "function" in response_dict:
421
+ span.set_attribute("openai_agents.function", response_dict["function"])
422
+
423
+ # Set usage attributes using the llm utility function
424
+ if "usage" in response_dict:
425
+ set_usage_attributes(span, response_dict["usage"])
426
+
427
+ # Set standard LLM completion event
428
+ if "output" in response_dict:
429
+ completions = []
430
+ for output in response_dict["output"]:
431
+ if output["type"] == "message":
432
+ content = " ".join([item.get("text", "") for item in output.get("content", [])])
433
+ completions.append({
434
+ "role": output.get("role", "assistant"),
435
+ "content": content
436
+ })
437
+ elif output["type"] == "function_call":
438
+ completions.append({
439
+ "role": "assistant",
440
+ "function_call": {
441
+ "name": output.get("name"),
442
+ "arguments": output.get("arguments", "{}")
443
+ }
444
+ })
445
+ if completions:
446
+ set_event_completion(span, completions)
447
+
448
+ span.set_status(Status(StatusCode.OK))
449
+ except Exception:
450
+ pass # Silently fail if response processing fails
451
+
452
+ return result
453
+
454
+ except InputGuardrailTripwireTriggered as err:
455
+ # Handle guardrail tripwire specifically
456
+ guardrail_name = err.guardrail_result.guardrail.__class__.__name__
457
+ error_msg = f"Input guardrail {guardrail_name} triggered tripwire"
458
+
459
+ # Set error attributes and status on current span
460
+ span.set_attribute("error.type", "input_guardrail_tripwire")
461
+ span.set_attribute("error.guardrail", guardrail_name)
462
+ span.record_exception(err)
463
+ span.set_status(Status(StatusCode.ERROR, error_msg))
464
+
465
+ # Get the current context and root span
466
+ ctx = trace.get_current_span().get_span_context()
467
+ if ctx:
468
+ root_span = trace.get_tracer(__name__).start_span(
469
+ "root_span",
470
+ context=ctx,
471
+ kind=SpanKind.CLIENT
472
+ )
473
+ root_span.set_attribute("error.type", "input_guardrail_tripwire")
474
+ root_span.set_attribute("error.guardrail", guardrail_name)
475
+ root_span.record_exception(err)
476
+ root_span.set_status(Status(StatusCode.ERROR, error_msg))
477
+ root_span.end()
478
+
479
+ raise
480
+ except OutputGuardrailTripwireTriggered as err:
481
+ # Handle guardrail tripwire specifically
482
+ guardrail_name = err.guardrail_result.guardrail.__class__.__name__
483
+ error_msg = f"Output guardrail {guardrail_name} triggered tripwire"
484
+
485
+ # Set error attributes and status on current span
486
+ span.set_attribute("error.type", "output_guardrail_tripwire")
487
+ span.set_attribute("error.guardrail", guardrail_name)
488
+ span.record_exception(err)
489
+ span.set_status(Status(StatusCode.ERROR, error_msg))
490
+
491
+ # Get the current context and root span
492
+ ctx = trace.get_current_span().get_span_context()
493
+ if ctx:
494
+ root_span = trace.get_tracer(__name__).start_span(
495
+ "root_span",
496
+ context=ctx,
497
+ kind=SpanKind.CLIENT
498
+ )
499
+ root_span.set_attribute("error.type", "output_guardrail_tripwire")
500
+ root_span.set_attribute("error.guardrail", guardrail_name)
501
+ root_span.record_exception(err)
502
+ root_span.set_status(Status(StatusCode.ERROR, error_msg))
503
+ root_span.end()
504
+
505
+ raise
506
+ except Exception as err:
507
+ error_msg = str(err)
508
+
509
+ # Set error status on current span
510
+ span.record_exception(err)
511
+ span.set_status(Status(StatusCode.ERROR, error_msg))
512
+
513
+ # Get the current context and root span
514
+ ctx = trace.get_current_span().get_span_context()
515
+ if ctx:
516
+ root_span = trace.get_tracer(__name__).start_span(
517
+ "root_span",
518
+ context=ctx,
519
+ kind=SpanKind.CLIENT
520
+ )
521
+ root_span.record_exception(err)
522
+ root_span.set_status(Status(StatusCode.ERROR, error_msg))
523
+ root_span.end()
524
+
525
+ raise
526
+
527
+ except Exception as outer_err:
528
+ try:
529
+ return await wrapped(*args, **kwargs)
530
+ except Exception as wrapped_err:
531
+ raise wrapped_err
532
+
533
+ return traced_method
@@ -1,5 +1,5 @@
1
1
  """
2
- Copyright (c) 2024 Scale3 Labs
2
+ Copyright (c) 2025 Scale3 Labs
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -14,13 +14,16 @@ See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  """
16
16
 
17
+ from typing import Collection
18
+
19
+ from importlib_metadata import version as v
17
20
  from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
18
21
  from opentelemetry.trace import get_tracer
19
22
  from wrapt import wrap_function_wrapper as _W
20
- from typing import Collection
21
- from importlib_metadata import version as v
23
+
22
24
  from .patch import patch_agent, patch_memory
23
25
 
26
+
24
27
  class PhiDataInstrumentation(BaseInstrumentor):
25
28
  def instrumentation_dependencies(self) -> Collection[str]:
26
29
  return ["phidata >= 2.7.10"] # Adjust version as needed
@@ -50,9 +50,9 @@ from langtrace_python_sdk.instrumentation import (
50
50
  LangchainCoreInstrumentation, LangchainInstrumentation,
51
51
  LanggraphInstrumentation, LiteLLMInstrumentation,
52
52
  LlamaindexInstrumentation, MilvusInstrumentation, MistralInstrumentation,
53
- OllamaInstrumentor, OpenAIInstrumentation, PhiDataInstrumentation,
54
- PineconeInstrumentation, PyMongoInstrumentation, QdrantInstrumentation,
55
- VertexAIInstrumentation, WeaviateInstrumentation)
53
+ OllamaInstrumentor, OpenAIAgentsInstrumentation, OpenAIInstrumentation,
54
+ PhiDataInstrumentation, PineconeInstrumentation, PyMongoInstrumentation,
55
+ QdrantInstrumentation, VertexAIInstrumentation, WeaviateInstrumentation)
56
56
  from langtrace_python_sdk.types import (DisableInstrumentations,
57
57
  InstrumentationMethods)
58
58
  from langtrace_python_sdk.utils import (check_if_sdk_is_outdated,
@@ -291,6 +291,7 @@ def init(
291
291
  "pymilvus": MilvusInstrumentation(),
292
292
  "crewai-tools": CrewaiToolsInstrumentation(),
293
293
  "cleanlab-tlm": CleanLabInstrumentation(),
294
+ "openai-agents": OpenAIAgentsInstrumentation(),
294
295
  }
295
296
 
296
297
  init_instrumentations(config.disable_instrumentations, all_instrumentations)
@@ -1 +1 @@
1
- __version__ = "3.8.3"
1
+ __version__ = "3.8.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langtrace-python-sdk
3
- Version: 3.8.3
3
+ Version: 3.8.4
4
4
  Summary: Python SDK for LangTrace
5
5
  Project-URL: Homepage, https://github.com/Scale3-Labs/langtrace-python-sdk
6
6
  Author-email: Scale3 Labs <engineering@scale3labs.com>
@@ -41,7 +41,8 @@ Requires-Dist: langchain-openai; extra == 'dev'
41
41
  Requires-Dist: litellm==1.48.7; extra == 'dev'
42
42
  Requires-Dist: mistralai; extra == 'dev'
43
43
  Requires-Dist: ollama; extra == 'dev'
44
- Requires-Dist: openai==1.60.0; extra == 'dev'
44
+ Requires-Dist: openai-agents>=0.0.3; extra == 'dev'
45
+ Requires-Dist: openai>=1.60.0; extra == 'dev'
45
46
  Requires-Dist: phidata; extra == 'dev'
46
47
  Requires-Dist: pinecone; extra == 'dev'
47
48
  Requires-Dist: python-dotenv; extra == 'dev'
@@ -115,8 +115,8 @@ examples/vertexai_example/main.py,sha256=gndId5X5ksD-ycxnAWMdEqIDbLc3kz5Vt8vm4YP
115
115
  examples/weaviate_example/__init__.py,sha256=8JMDBsRSEV10HfTd-YC7xb4txBjD3la56snk-Bbg2Kw,618
116
116
  examples/weaviate_example/query_text.py,sha256=wPHQTc_58kPoKTZMygVjTj-2ZcdrIuaausJfMxNQnQc,127162
117
117
  langtrace_python_sdk/__init__.py,sha256=VZM6i71NR7pBQK6XvJWRelknuTYUhqwqE7PlicKa5Wg,1166
118
- langtrace_python_sdk/langtrace.py,sha256=L0ktJXsGjvKS0dc6kWGCOUoNsGTERLEmd-1QfzDppq8,13706
119
- langtrace_python_sdk/version.py,sha256=suroMOhipFiXYE6cgp5XFXSXtW2pHyZibLMFSNkHUec,22
118
+ langtrace_python_sdk/langtrace.py,sha256=T-DsDrwWaL4gAUK1lkTRRpmvoO7F2WtO5hQZdyrVAxE,13791
119
+ langtrace_python_sdk/version.py,sha256=9rvKBbG_yfSTrG3aEj6qW6gdB6C0X_Ay-m4XltC5N30,22
120
120
  langtrace_python_sdk/constants/__init__.py,sha256=3CNYkWMdd1DrkGqzLUgNZXjdAlM6UFMlf_F-odAToyc,146
121
121
  langtrace_python_sdk/constants/exporter/langtrace_exporter.py,sha256=EVCrouYCpY98f0KSaKr4PzNxPULTZZO6dSA_crEOyJU,106
122
122
  langtrace_python_sdk/constants/instrumentation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -141,9 +141,9 @@ langtrace_python_sdk/constants/instrumentation/weaviate.py,sha256=gtv-JBxvNGClEM
141
141
  langtrace_python_sdk/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
142
142
  langtrace_python_sdk/extensions/langtrace_exporter.py,sha256=ckd8dMmY6h2oxE04p1JFLwUB5PSJX_Cy4eDFEM6aj4Y,6605
143
143
  langtrace_python_sdk/extensions/langtrace_filesystem.py,sha256=34fZutG28EJ66l67OvTGsydAH3ZpXgikdE7hVLqBpG4,7863
144
- langtrace_python_sdk/instrumentation/__init__.py,sha256=UOtU6nT-ZT-ZvHzOgPNhG_IooMe5jU8lzgdoDCHpeBc,2469
144
+ langtrace_python_sdk/instrumentation/__init__.py,sha256=DC96oELorZedDf5zooQ6HGi-dmieXwUephgeB_LzfaU,2559
145
145
  langtrace_python_sdk/instrumentation/agno/__init__.py,sha256=95fn4oA-CHB0mxc6KnVB20KSbXGl_ZZr9n99EEaXzrY,91
146
- langtrace_python_sdk/instrumentation/agno/instrumentation.py,sha256=CrwHzkzLpdo2m2nBHE69d8MIFMxZuvTsT1LTxqlTWV0,2664
146
+ langtrace_python_sdk/instrumentation/agno/instrumentation.py,sha256=XUnfvqpp13IgdF03xGKasq7kGjeaN1sXLIwCf-Nt_Nc,2667
147
147
  langtrace_python_sdk/instrumentation/agno/patch.py,sha256=tbMscAxsZuo4MAEUQ9tUPiluyj8RRcksF7sYU-v2knM,13399
148
148
  langtrace_python_sdk/instrumentation/anthropic/__init__.py,sha256=donrurJAGYlxrSRA3BIf76jGeUcAx9Tq8CVpah68S0Y,101
149
149
  langtrace_python_sdk/instrumentation/anthropic/instrumentation.py,sha256=ndXdruI0BG7n75rsuEpKjfzePxrZxg40gZ39ONmD_v4,1845
@@ -225,8 +225,11 @@ langtrace_python_sdk/instrumentation/openai/__init__.py,sha256=VPHRNCQEdkizIVP2d
225
225
  langtrace_python_sdk/instrumentation/openai/instrumentation.py,sha256=PZxI0qfoud1VsKGmJu49YDp0Z9z9TzCR8qxR3uznOMA,2810
226
226
  langtrace_python_sdk/instrumentation/openai/patch.py,sha256=mRE150sHGL2dq4oOKTYWek1vup7HuRW_sqP6nNOd3N8,27862
227
227
  langtrace_python_sdk/instrumentation/openai/types.py,sha256=aVkoa7tmAbYfyOhnyMrDaVjQuwhmRNLMthlNtKMtWX8,4311
228
+ langtrace_python_sdk/instrumentation/openai_agents/__init__.py,sha256=ElRfFIQYXD2-eRyL3fZnjIsDJLTrDolh5cZHPnZv0q8,107
229
+ langtrace_python_sdk/instrumentation/openai_agents/instrumentation.py,sha256=6M4FHXfem7pazrNgsimebrEfMb2FxI8lHrdEMbVf75Y,1860
230
+ langtrace_python_sdk/instrumentation/openai_agents/patch.py,sha256=zXuIjqnPvWNEu8nCDTRxxvTAi8PwAOsBJEtsFn3PXaI,24012
228
231
  langtrace_python_sdk/instrumentation/phidata/__init__.py,sha256=q2v6luvqp9gFf1AJX6YrBvuyMC_q6cEnB5syl2HNPlU,97
229
- langtrace_python_sdk/instrumentation/phidata/instrumentation.py,sha256=E_u0WUtBukmWjKjkgtJgeCS6ab_2xhcamLOH9cERNCM,2694
232
+ langtrace_python_sdk/instrumentation/phidata/instrumentation.py,sha256=S639XMVOGmpIK9jug9NWrUBLqs1G5ywBZiIhVuCkwGk,2697
230
233
  langtrace_python_sdk/instrumentation/phidata/patch.py,sha256=-Jf_20wLLRGRM6sY3RreS-ocXjdq5m33-gxNtl_eUdQ,12133
231
234
  langtrace_python_sdk/instrumentation/pinecone/__init__.py,sha256=DzXyGh9_MGWveJvXULkFwdkf7PbG2s3bAWtT1Dmz7Ok,99
232
235
  langtrace_python_sdk/instrumentation/pinecone/instrumentation.py,sha256=2jhxGmXPrdSsrETRR_e58Rdc_uoKfRunvsCc660v6QA,1831
@@ -297,8 +300,8 @@ tests/pinecone/cassettes/test_query.yaml,sha256=b5v9G3ssUy00oG63PlFUR3JErF2Js-5A
297
300
  tests/pinecone/cassettes/test_upsert.yaml,sha256=neWmQ1v3d03V8WoLl8FoFeeCYImb8pxlJBWnFd_lITU,38607
298
301
  tests/qdrant/conftest.py,sha256=9n0uHxxIjWk9fbYc4bx-uP8lSAgLBVx-cV9UjnsyCHM,381
299
302
  tests/qdrant/test_qdrant.py,sha256=pzjAjVY2kmsmGfrI2Gs2xrolfuaNHz7l1fqGQCjp5_o,3353
300
- langtrace_python_sdk-3.8.3.dist-info/METADATA,sha256=Etxr0XXT7aO2v4RKQduS0KHOe5qKWY9Ozid_Ciu3efs,15792
301
- langtrace_python_sdk-3.8.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
302
- langtrace_python_sdk-3.8.3.dist-info/entry_points.txt,sha256=1_b9-qvf2fE7uQNZcbUei9vLpFZBbbh9LrtGw95ssAo,70
303
- langtrace_python_sdk-3.8.3.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
304
- langtrace_python_sdk-3.8.3.dist-info/RECORD,,
303
+ langtrace_python_sdk-3.8.4.dist-info/METADATA,sha256=Ck-Z7OW7aUVpudx-_jyLp187Muy4X0Gltx-sddQPJ0g,15844
304
+ langtrace_python_sdk-3.8.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
305
+ langtrace_python_sdk-3.8.4.dist-info/entry_points.txt,sha256=1_b9-qvf2fE7uQNZcbUei9vLpFZBbbh9LrtGw95ssAo,70
306
+ langtrace_python_sdk-3.8.4.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
307
+ langtrace_python_sdk-3.8.4.dist-info/RECORD,,