openlit 1.34.30__py3-none-any.whl → 1.34.31__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.
Files changed (168) hide show
  1. openlit/__helpers.py +235 -86
  2. openlit/__init__.py +16 -13
  3. openlit/_instrumentors.py +2 -1
  4. openlit/evals/all.py +50 -21
  5. openlit/evals/bias_detection.py +47 -20
  6. openlit/evals/hallucination.py +53 -22
  7. openlit/evals/toxicity.py +50 -21
  8. openlit/evals/utils.py +54 -30
  9. openlit/guard/all.py +61 -19
  10. openlit/guard/prompt_injection.py +34 -14
  11. openlit/guard/restrict_topic.py +46 -15
  12. openlit/guard/sensitive_topic.py +34 -14
  13. openlit/guard/utils.py +58 -22
  14. openlit/instrumentation/ag2/__init__.py +24 -8
  15. openlit/instrumentation/ag2/ag2.py +34 -13
  16. openlit/instrumentation/ag2/async_ag2.py +34 -13
  17. openlit/instrumentation/ag2/utils.py +133 -30
  18. openlit/instrumentation/ai21/__init__.py +43 -14
  19. openlit/instrumentation/ai21/ai21.py +47 -21
  20. openlit/instrumentation/ai21/async_ai21.py +47 -21
  21. openlit/instrumentation/ai21/utils.py +299 -78
  22. openlit/instrumentation/anthropic/__init__.py +21 -4
  23. openlit/instrumentation/anthropic/anthropic.py +28 -17
  24. openlit/instrumentation/anthropic/async_anthropic.py +28 -17
  25. openlit/instrumentation/anthropic/utils.py +145 -35
  26. openlit/instrumentation/assemblyai/__init__.py +11 -2
  27. openlit/instrumentation/assemblyai/assemblyai.py +15 -4
  28. openlit/instrumentation/assemblyai/utils.py +120 -25
  29. openlit/instrumentation/astra/__init__.py +43 -10
  30. openlit/instrumentation/astra/astra.py +28 -5
  31. openlit/instrumentation/astra/async_astra.py +28 -5
  32. openlit/instrumentation/astra/utils.py +151 -55
  33. openlit/instrumentation/azure_ai_inference/__init__.py +43 -10
  34. openlit/instrumentation/azure_ai_inference/async_azure_ai_inference.py +53 -21
  35. openlit/instrumentation/azure_ai_inference/azure_ai_inference.py +53 -21
  36. openlit/instrumentation/azure_ai_inference/utils.py +307 -83
  37. openlit/instrumentation/bedrock/__init__.py +21 -4
  38. openlit/instrumentation/bedrock/bedrock.py +63 -25
  39. openlit/instrumentation/bedrock/utils.py +139 -30
  40. openlit/instrumentation/chroma/__init__.py +89 -16
  41. openlit/instrumentation/chroma/chroma.py +28 -6
  42. openlit/instrumentation/chroma/utils.py +167 -51
  43. openlit/instrumentation/cohere/__init__.py +63 -18
  44. openlit/instrumentation/cohere/async_cohere.py +63 -24
  45. openlit/instrumentation/cohere/cohere.py +63 -24
  46. openlit/instrumentation/cohere/utils.py +286 -73
  47. openlit/instrumentation/controlflow/__init__.py +35 -9
  48. openlit/instrumentation/controlflow/controlflow.py +66 -33
  49. openlit/instrumentation/crawl4ai/__init__.py +25 -10
  50. openlit/instrumentation/crawl4ai/async_crawl4ai.py +78 -31
  51. openlit/instrumentation/crawl4ai/crawl4ai.py +78 -31
  52. openlit/instrumentation/crewai/__init__.py +40 -15
  53. openlit/instrumentation/crewai/async_crewai.py +32 -7
  54. openlit/instrumentation/crewai/crewai.py +32 -7
  55. openlit/instrumentation/crewai/utils.py +159 -56
  56. openlit/instrumentation/dynamiq/__init__.py +46 -12
  57. openlit/instrumentation/dynamiq/dynamiq.py +74 -33
  58. openlit/instrumentation/elevenlabs/__init__.py +23 -4
  59. openlit/instrumentation/elevenlabs/async_elevenlabs.py +16 -4
  60. openlit/instrumentation/elevenlabs/elevenlabs.py +16 -4
  61. openlit/instrumentation/elevenlabs/utils.py +128 -25
  62. openlit/instrumentation/embedchain/__init__.py +11 -2
  63. openlit/instrumentation/embedchain/embedchain.py +68 -35
  64. openlit/instrumentation/firecrawl/__init__.py +24 -7
  65. openlit/instrumentation/firecrawl/firecrawl.py +46 -20
  66. openlit/instrumentation/google_ai_studio/__init__.py +45 -10
  67. openlit/instrumentation/google_ai_studio/async_google_ai_studio.py +67 -44
  68. openlit/instrumentation/google_ai_studio/google_ai_studio.py +67 -44
  69. openlit/instrumentation/google_ai_studio/utils.py +180 -67
  70. openlit/instrumentation/gpt4all/__init__.py +22 -7
  71. openlit/instrumentation/gpt4all/gpt4all.py +67 -29
  72. openlit/instrumentation/gpt4all/utils.py +285 -61
  73. openlit/instrumentation/gpu/__init__.py +128 -47
  74. openlit/instrumentation/groq/__init__.py +21 -4
  75. openlit/instrumentation/groq/async_groq.py +33 -21
  76. openlit/instrumentation/groq/groq.py +33 -21
  77. openlit/instrumentation/groq/utils.py +192 -55
  78. openlit/instrumentation/haystack/__init__.py +70 -24
  79. openlit/instrumentation/haystack/async_haystack.py +28 -6
  80. openlit/instrumentation/haystack/haystack.py +28 -6
  81. openlit/instrumentation/haystack/utils.py +196 -74
  82. openlit/instrumentation/julep/__init__.py +69 -19
  83. openlit/instrumentation/julep/async_julep.py +53 -27
  84. openlit/instrumentation/julep/julep.py +53 -28
  85. openlit/instrumentation/langchain/__init__.py +74 -63
  86. openlit/instrumentation/langchain/callback_handler.py +1100 -0
  87. openlit/instrumentation/langchain_community/__init__.py +13 -2
  88. openlit/instrumentation/langchain_community/async_langchain_community.py +23 -5
  89. openlit/instrumentation/langchain_community/langchain_community.py +23 -5
  90. openlit/instrumentation/langchain_community/utils.py +35 -9
  91. openlit/instrumentation/letta/__init__.py +68 -15
  92. openlit/instrumentation/letta/letta.py +99 -54
  93. openlit/instrumentation/litellm/__init__.py +43 -14
  94. openlit/instrumentation/litellm/async_litellm.py +51 -26
  95. openlit/instrumentation/litellm/litellm.py +51 -26
  96. openlit/instrumentation/litellm/utils.py +304 -102
  97. openlit/instrumentation/llamaindex/__init__.py +267 -90
  98. openlit/instrumentation/llamaindex/async_llamaindex.py +28 -6
  99. openlit/instrumentation/llamaindex/llamaindex.py +28 -6
  100. openlit/instrumentation/llamaindex/utils.py +204 -91
  101. openlit/instrumentation/mem0/__init__.py +11 -2
  102. openlit/instrumentation/mem0/mem0.py +50 -29
  103. openlit/instrumentation/milvus/__init__.py +10 -2
  104. openlit/instrumentation/milvus/milvus.py +31 -6
  105. openlit/instrumentation/milvus/utils.py +166 -67
  106. openlit/instrumentation/mistral/__init__.py +63 -18
  107. openlit/instrumentation/mistral/async_mistral.py +63 -24
  108. openlit/instrumentation/mistral/mistral.py +63 -24
  109. openlit/instrumentation/mistral/utils.py +277 -69
  110. openlit/instrumentation/multion/__init__.py +69 -19
  111. openlit/instrumentation/multion/async_multion.py +57 -26
  112. openlit/instrumentation/multion/multion.py +57 -26
  113. openlit/instrumentation/ollama/__init__.py +39 -18
  114. openlit/instrumentation/ollama/async_ollama.py +57 -26
  115. openlit/instrumentation/ollama/ollama.py +57 -26
  116. openlit/instrumentation/ollama/utils.py +226 -50
  117. openlit/instrumentation/openai/__init__.py +156 -32
  118. openlit/instrumentation/openai/async_openai.py +147 -67
  119. openlit/instrumentation/openai/openai.py +150 -67
  120. openlit/instrumentation/openai/utils.py +657 -185
  121. openlit/instrumentation/openai_agents/__init__.py +5 -1
  122. openlit/instrumentation/openai_agents/processor.py +110 -90
  123. openlit/instrumentation/phidata/__init__.py +13 -5
  124. openlit/instrumentation/phidata/phidata.py +67 -32
  125. openlit/instrumentation/pinecone/__init__.py +48 -9
  126. openlit/instrumentation/pinecone/async_pinecone.py +27 -5
  127. openlit/instrumentation/pinecone/pinecone.py +27 -5
  128. openlit/instrumentation/pinecone/utils.py +153 -47
  129. openlit/instrumentation/premai/__init__.py +22 -7
  130. openlit/instrumentation/premai/premai.py +51 -26
  131. openlit/instrumentation/premai/utils.py +246 -59
  132. openlit/instrumentation/pydantic_ai/__init__.py +49 -22
  133. openlit/instrumentation/pydantic_ai/pydantic_ai.py +69 -16
  134. openlit/instrumentation/pydantic_ai/utils.py +89 -24
  135. openlit/instrumentation/qdrant/__init__.py +19 -4
  136. openlit/instrumentation/qdrant/async_qdrant.py +33 -7
  137. openlit/instrumentation/qdrant/qdrant.py +33 -7
  138. openlit/instrumentation/qdrant/utils.py +228 -93
  139. openlit/instrumentation/reka/__init__.py +23 -10
  140. openlit/instrumentation/reka/async_reka.py +17 -11
  141. openlit/instrumentation/reka/reka.py +17 -11
  142. openlit/instrumentation/reka/utils.py +138 -36
  143. openlit/instrumentation/together/__init__.py +44 -12
  144. openlit/instrumentation/together/async_together.py +50 -27
  145. openlit/instrumentation/together/together.py +50 -27
  146. openlit/instrumentation/together/utils.py +301 -71
  147. openlit/instrumentation/transformers/__init__.py +2 -1
  148. openlit/instrumentation/transformers/transformers.py +13 -3
  149. openlit/instrumentation/transformers/utils.py +139 -36
  150. openlit/instrumentation/vertexai/__init__.py +81 -16
  151. openlit/instrumentation/vertexai/async_vertexai.py +33 -15
  152. openlit/instrumentation/vertexai/utils.py +123 -27
  153. openlit/instrumentation/vertexai/vertexai.py +33 -15
  154. openlit/instrumentation/vllm/__init__.py +12 -5
  155. openlit/instrumentation/vllm/utils.py +121 -31
  156. openlit/instrumentation/vllm/vllm.py +16 -10
  157. openlit/otel/events.py +35 -10
  158. openlit/otel/metrics.py +32 -24
  159. openlit/otel/tracing.py +24 -9
  160. openlit/semcov/__init__.py +72 -6
  161. {openlit-1.34.30.dist-info → openlit-1.34.31.dist-info}/METADATA +2 -1
  162. openlit-1.34.31.dist-info/RECORD +166 -0
  163. openlit/instrumentation/langchain/async_langchain.py +0 -102
  164. openlit/instrumentation/langchain/langchain.py +0 -102
  165. openlit/instrumentation/langchain/utils.py +0 -252
  166. openlit-1.34.30.dist-info/RECORD +0 -168
  167. {openlit-1.34.30.dist-info → openlit-1.34.31.dist-info}/LICENSE +0 -0
  168. {openlit-1.34.30.dist-info → openlit-1.34.31.dist-info}/WHEEL +0 -0
@@ -12,8 +12,18 @@ from openlit.instrumentation.crewai.utils import (
12
12
  set_server_address_and_port,
13
13
  )
14
14
 
15
- def async_general_wrap(gen_ai_endpoint, version, environment, application_name,
16
- tracer, pricing_info, capture_message_content, metrics, disable_metrics):
15
+
16
+ def async_general_wrap(
17
+ gen_ai_endpoint,
18
+ version,
19
+ environment,
20
+ application_name,
21
+ tracer,
22
+ pricing_info,
23
+ capture_message_content,
24
+ metrics,
25
+ disable_metrics,
26
+ ):
17
27
  """
18
28
  Modern async wrapper for CrewAI operations following Framework Instrumentation Guide patterns.
19
29
  """
@@ -34,7 +44,9 @@ def async_general_wrap(gen_ai_endpoint, version, environment, application_name,
34
44
  operation_type = OPERATION_MAP.get(gen_ai_endpoint, "framework")
35
45
 
36
46
  # Generate span name following {operation_type} {operation_name} pattern
37
- span_name = _generate_span_name(operation_type, gen_ai_endpoint, instance, args, kwargs)
47
+ span_name = _generate_span_name(
48
+ operation_type, gen_ai_endpoint, instance, args, kwargs
49
+ )
38
50
 
39
51
  with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
40
52
  start_time = time.time()
@@ -43,10 +55,22 @@ def async_general_wrap(gen_ai_endpoint, version, environment, application_name,
43
55
  try:
44
56
  # Process response and generate comprehensive telemetry
45
57
  response = process_crewai_response(
46
- response, operation_type, server_address, server_port,
47
- environment, application_name, metrics, start_time, span,
48
- capture_message_content, disable_metrics, version,
49
- instance, args, endpoint=gen_ai_endpoint, **kwargs
58
+ response,
59
+ operation_type,
60
+ server_address,
61
+ server_port,
62
+ environment,
63
+ application_name,
64
+ metrics,
65
+ start_time,
66
+ span,
67
+ capture_message_content,
68
+ disable_metrics,
69
+ version,
70
+ instance,
71
+ args,
72
+ endpoint=gen_ai_endpoint,
73
+ **kwargs,
50
74
  )
51
75
 
52
76
  except Exception as e:
@@ -56,6 +80,7 @@ def async_general_wrap(gen_ai_endpoint, version, environment, application_name,
56
80
 
57
81
  return wrapper
58
82
 
83
+
59
84
  def _generate_span_name(operation_type, endpoint, instance, args, kwargs):
60
85
  """
61
86
  Generate proper span names following {operation_type} {operation_name} convention.
@@ -12,8 +12,18 @@ from openlit.instrumentation.crewai.utils import (
12
12
  set_server_address_and_port,
13
13
  )
14
14
 
15
- def general_wrap(gen_ai_endpoint, version, environment, application_name,
16
- tracer, pricing_info, capture_message_content, metrics, disable_metrics):
15
+
16
+ def general_wrap(
17
+ gen_ai_endpoint,
18
+ version,
19
+ environment,
20
+ application_name,
21
+ tracer,
22
+ pricing_info,
23
+ capture_message_content,
24
+ metrics,
25
+ disable_metrics,
26
+ ):
17
27
  """
18
28
  Modern wrapper for CrewAI operations following Framework Instrumentation Guide patterns.
19
29
  """
@@ -34,7 +44,9 @@ def general_wrap(gen_ai_endpoint, version, environment, application_name,
34
44
  operation_type = OPERATION_MAP.get(gen_ai_endpoint, "framework")
35
45
 
36
46
  # Generate span name following {operation_type} {operation_name} pattern
37
- span_name = _generate_span_name(operation_type, gen_ai_endpoint, instance, args, kwargs)
47
+ span_name = _generate_span_name(
48
+ operation_type, gen_ai_endpoint, instance, args, kwargs
49
+ )
38
50
 
39
51
  with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
40
52
  start_time = time.time()
@@ -43,10 +55,22 @@ def general_wrap(gen_ai_endpoint, version, environment, application_name,
43
55
  try:
44
56
  # Process response and generate comprehensive telemetry
45
57
  response = process_crewai_response(
46
- response, operation_type, server_address, server_port,
47
- environment, application_name, metrics, start_time, span,
48
- capture_message_content, disable_metrics, version,
49
- instance, args, endpoint=gen_ai_endpoint, **kwargs
58
+ response,
59
+ operation_type,
60
+ server_address,
61
+ server_port,
62
+ environment,
63
+ application_name,
64
+ metrics,
65
+ start_time,
66
+ span,
67
+ capture_message_content,
68
+ disable_metrics,
69
+ version,
70
+ instance,
71
+ args,
72
+ endpoint=gen_ai_endpoint,
73
+ **kwargs,
50
74
  )
51
75
 
52
76
  except Exception as e:
@@ -56,6 +80,7 @@ def general_wrap(gen_ai_endpoint, version, environment, application_name,
56
80
 
57
81
  return wrapper
58
82
 
83
+
59
84
  def _generate_span_name(operation_type, endpoint, instance, args, kwargs):
60
85
  """
61
86
  Generate proper span names following {operation_type} {operation_name} convention.
@@ -20,28 +20,25 @@ OPERATION_MAP = {
20
20
  "crew_train": "invoke_agent",
21
21
  "crew_replay": "invoke_agent",
22
22
  "crew_test": "invoke_agent",
23
-
24
23
  # Agent Operations (core agent functions)
25
24
  "agent___init__": "create_agent",
26
25
  "agent_execute_task": "invoke_agent",
27
26
  "agent_backstory_property": "invoke_agent",
28
-
29
27
  # Task Operations (task execution)
30
28
  "task_execute": "invoke_agent",
31
29
  "task_execute_async": "invoke_agent",
32
30
  "task_execute_core": "invoke_agent",
33
-
34
31
  # Tool Operations (tool execution)
35
32
  "tool_run": "execute_tool",
36
33
  "tool___call__": "execute_tool",
37
34
  "tool_execute": "execute_tool",
38
-
39
35
  # Memory Operations (knowledge management)
40
36
  "memory_save": "invoke_agent",
41
37
  "memory_search": "invoke_agent",
42
- "memory_reset": "invoke_agent"
38
+ "memory_reset": "invoke_agent",
43
39
  }
44
40
 
41
+
45
42
  def set_server_address_and_port(instance):
46
43
  """
47
44
  Extract server information from CrewAI instance.
@@ -57,13 +54,13 @@ def set_server_address_and_port(instance):
57
54
 
58
55
  # Try to extract LLM endpoint information
59
56
  try:
60
- if hasattr(instance, 'llm') and hasattr(instance.llm, 'api_base'):
57
+ if hasattr(instance, "llm") and hasattr(instance.llm, "api_base"):
61
58
  parsed = urlparse(instance.llm.api_base)
62
59
  server_address = parsed.hostname or "localhost"
63
60
  server_port = parsed.port or 443
64
- elif hasattr(instance, 'agent') and hasattr(instance.agent, 'llm'):
61
+ elif hasattr(instance, "agent") and hasattr(instance.agent, "llm"):
65
62
  # For tasks that have an agent with LLM
66
- if hasattr(instance.agent.llm, 'api_base'):
63
+ if hasattr(instance.agent.llm, "api_base"):
67
64
  parsed = urlparse(instance.agent.llm.api_base)
68
65
  server_address = parsed.hostname or "localhost"
69
66
  server_port = parsed.port or 443
@@ -73,10 +70,25 @@ def set_server_address_and_port(instance):
73
70
 
74
71
  return server_address, server_port
75
72
 
76
- def process_crewai_response(response, operation_type, server_address, server_port,
77
- environment, application_name, metrics, start_time, span,
78
- capture_message_content, disable_metrics, version,
79
- instance, args, endpoint=None, **kwargs):
73
+
74
+ def process_crewai_response(
75
+ response,
76
+ operation_type,
77
+ server_address,
78
+ server_port,
79
+ environment,
80
+ application_name,
81
+ metrics,
82
+ start_time,
83
+ span,
84
+ capture_message_content,
85
+ disable_metrics,
86
+ version,
87
+ instance,
88
+ args,
89
+ endpoint=None,
90
+ **kwargs,
91
+ ):
80
92
  """
81
93
  Process CrewAI response with comprehensive business intelligence.
82
94
  OpenLIT's competitive advantage through superior observability.
@@ -100,16 +112,19 @@ def process_crewai_response(response, operation_type, server_address, server_por
100
112
  llm = getattr(instance, "llm", None)
101
113
  if llm:
102
114
  # Try different model attribute names used by different LLM libraries
103
- request_model = (getattr(llm, "model_name", None) or
104
- getattr(llm, "model", None) or
105
- getattr(llm, "_model_name", None) or
106
- "unknown")
115
+ request_model = (
116
+ getattr(llm, "model_name", None)
117
+ or getattr(llm, "model", None)
118
+ or getattr(llm, "_model_name", None)
119
+ or "unknown"
120
+ )
107
121
  if request_model != "unknown":
108
122
  request_model = str(request_model)
109
123
 
110
124
  # Create a wrapper instance that exposes model_name for common_framework_span_attributes
111
125
  class ModelWrapper:
112
126
  """Wrapper class to expose model_name for framework span attributes."""
127
+
113
128
  def __init__(self, original_instance, model_name):
114
129
  self._original = original_instance
115
130
  self.model_name = model_name
@@ -125,8 +140,15 @@ def process_crewai_response(response, operation_type, server_address, server_por
125
140
 
126
141
  # Set common framework span attributes
127
142
  common_framework_span_attributes(
128
- scope, SemanticConvention.GEN_AI_SYSTEM_CREWAI, server_address, server_port,
129
- environment, application_name, version, endpoint, model_instance
143
+ scope,
144
+ SemanticConvention.GEN_AI_SYSTEM_CREWAI,
145
+ server_address,
146
+ server_port,
147
+ environment,
148
+ application_name,
149
+ version,
150
+ endpoint,
151
+ model_instance,
130
152
  )
131
153
 
132
154
  # Set span name following OpenTelemetry format
@@ -156,11 +178,14 @@ def process_crewai_response(response, operation_type, server_address, server_por
156
178
 
157
179
  # === RECORD METRICS ===
158
180
  if not disable_metrics and metrics:
159
- _record_crewai_metrics(metrics, standard_operation, duration_ms, environment, application_name)
181
+ _record_crewai_metrics(
182
+ metrics, standard_operation, duration_ms, environment, application_name
183
+ )
160
184
 
161
185
  span.set_status(Status(StatusCode.OK))
162
186
  return response
163
187
 
188
+
164
189
  def _set_span_name(span, operation_type, instance, endpoint, args, kwargs):
165
190
  """Set span name following OpenTelemetry format: '{operation_type} {name}'"""
166
191
  try:
@@ -175,11 +200,15 @@ def _set_span_name(span, operation_type, instance, endpoint, args, kwargs):
175
200
  elif endpoint.startswith("agent_"):
176
201
  if "create" in endpoint or endpoint == "agent___init__":
177
202
  # Agent creation: "create_agent {agent_name}"
178
- agent_name = getattr(instance, "name", None) or getattr(instance, "role", "agent")
203
+ agent_name = getattr(instance, "name", None) or getattr(
204
+ instance, "role", "agent"
205
+ )
179
206
  span.update_name(f"create_agent {agent_name}")
180
207
  else:
181
208
  # Agent invocation: "invoke_agent {agent_name}"
182
- agent_name = getattr(instance, "name", None) or getattr(instance, "role", "agent")
209
+ agent_name = getattr(instance, "name", None) or getattr(
210
+ instance, "role", "agent"
211
+ )
183
212
  span.update_name(f"invoke_agent {agent_name}")
184
213
 
185
214
  elif endpoint.startswith("task_"):
@@ -188,7 +217,10 @@ def _set_span_name(span, operation_type, instance, endpoint, args, kwargs):
188
217
 
189
218
  elif endpoint.startswith("tool_"):
190
219
  # Tool operations: "execute_tool {tool_name}"
191
- tool_name = getattr(instance, "name", None) or getattr(instance, "__class__", type(instance)).__name__
220
+ tool_name = (
221
+ getattr(instance, "name", None)
222
+ or getattr(instance, "__class__", type(instance)).__name__
223
+ )
192
224
  span.update_name(f"execute_tool {tool_name}")
193
225
 
194
226
  elif endpoint.startswith("memory_"):
@@ -205,6 +237,7 @@ def _set_span_name(span, operation_type, instance, endpoint, args, kwargs):
205
237
  # Fallback naming
206
238
  span.update_name(f"invoke_agent {endpoint}")
207
239
 
240
+
208
241
  def _set_agent_business_intelligence(span, instance, endpoint, args, kwargs):
209
242
  """Set agent business intelligence using standard OpenTelemetry semantic conventions"""
210
243
  if not endpoint.startswith("agent_"):
@@ -232,32 +265,46 @@ def _set_agent_business_intelligence(span, instance, endpoint, args, kwargs):
232
265
  # Enhanced Agent Configuration Tracking using SemanticConvention
233
266
  max_retry_limit = getattr(instance, "max_retry_limit", None)
234
267
  if max_retry_limit is not None:
235
- span.set_attribute(SemanticConvention.GEN_AI_AGENT_MAX_RETRY_LIMIT, max_retry_limit)
268
+ span.set_attribute(
269
+ SemanticConvention.GEN_AI_AGENT_MAX_RETRY_LIMIT, max_retry_limit
270
+ )
236
271
 
237
272
  allow_delegation = getattr(instance, "allow_delegation", None)
238
273
  if allow_delegation is not None:
239
- span.set_attribute(SemanticConvention.GEN_AI_AGENT_ALLOW_DELEGATION, allow_delegation)
274
+ span.set_attribute(
275
+ SemanticConvention.GEN_AI_AGENT_ALLOW_DELEGATION, allow_delegation
276
+ )
240
277
 
241
278
  allow_code_execution = getattr(instance, "allow_code_execution", None)
242
279
  if allow_code_execution is not None:
243
- span.set_attribute(SemanticConvention.GEN_AI_AGENT_ALLOW_CODE_EXECUTION, allow_code_execution)
280
+ span.set_attribute(
281
+ SemanticConvention.GEN_AI_AGENT_ALLOW_CODE_EXECUTION,
282
+ allow_code_execution,
283
+ )
244
284
 
245
285
  # Tools tracking using SemanticConvention
246
286
  tools = getattr(instance, "tools", [])
247
287
  if tools:
248
- tool_names = [getattr(tool, "name", str(tool)) for tool in tools[:5]] # Limit to first 5
288
+ tool_names = [
289
+ getattr(tool, "name", str(tool)) for tool in tools[:5]
290
+ ] # Limit to first 5
249
291
  if tool_names:
250
- span.set_attribute(SemanticConvention.GEN_AI_AGENT_TOOLS, ", ".join(tool_names))
292
+ span.set_attribute(
293
+ SemanticConvention.GEN_AI_AGENT_TOOLS, ", ".join(tool_names)
294
+ )
251
295
 
252
296
  # === OpenAI Agent-specific Attributes ===
253
297
  _set_openai_agent_attributes(span, instance, endpoint, args, kwargs)
254
298
 
255
299
  # === Conversation and Data Source Tracking ===
256
- _set_conversation_and_data_source_attributes(span, instance, endpoint, args, kwargs)
300
+ _set_conversation_and_data_source_attributes(
301
+ span, instance, endpoint, args, kwargs
302
+ )
257
303
 
258
304
  except Exception as e:
259
305
  handle_exception(span, e)
260
306
 
307
+
261
308
  def _set_openai_agent_attributes(span, instance, endpoint, args, kwargs):
262
309
  """Set OpenAI-specific agent attributes when using OpenAI models"""
263
310
  try:
@@ -276,16 +323,27 @@ def _set_openai_agent_attributes(span, instance, endpoint, args, kwargs):
276
323
  # OpenAI service tier if available
277
324
  service_tier = getattr(llm, "service_tier", None)
278
325
  if service_tier:
279
- span.set_attribute(SemanticConvention.GEN_AI_OPENAI_REQUEST_SERVICE_TIER, service_tier)
326
+ span.set_attribute(
327
+ SemanticConvention.GEN_AI_OPENAI_REQUEST_SERVICE_TIER,
328
+ service_tier,
329
+ )
280
330
 
281
331
  # OpenAI Assistant API attributes if available
282
- assistant_id = getattr(instance, "assistant_id", None) or kwargs.get("assistant_id")
332
+ assistant_id = getattr(instance, "assistant_id", None) or kwargs.get(
333
+ "assistant_id"
334
+ )
283
335
  if assistant_id:
284
- span.set_attribute(SemanticConvention.GEN_AI_OPENAI_ASSISTANT_ID, assistant_id)
336
+ span.set_attribute(
337
+ SemanticConvention.GEN_AI_OPENAI_ASSISTANT_ID, assistant_id
338
+ )
285
339
 
286
- thread_id = getattr(instance, "thread_id", None) or kwargs.get("thread_id")
340
+ thread_id = getattr(instance, "thread_id", None) or kwargs.get(
341
+ "thread_id"
342
+ )
287
343
  if thread_id:
288
- span.set_attribute(SemanticConvention.GEN_AI_OPENAI_THREAD_ID, thread_id)
344
+ span.set_attribute(
345
+ SemanticConvention.GEN_AI_OPENAI_THREAD_ID, thread_id
346
+ )
289
347
 
290
348
  run_id = getattr(instance, "run_id", None) or kwargs.get("run_id")
291
349
  if run_id:
@@ -299,18 +357,23 @@ def _set_openai_agent_attributes(span, instance, endpoint, args, kwargs):
299
357
  except Exception as e:
300
358
  handle_exception(span, e)
301
359
 
302
- def _set_conversation_and_data_source_attributes(span, instance, endpoint, args, kwargs):
360
+
361
+ def _set_conversation_and_data_source_attributes(
362
+ span, instance, endpoint, args, kwargs
363
+ ):
303
364
  """Set conversation tracking and data source attributes"""
304
365
  try:
305
366
  # Conversation ID for multi-turn interactions
306
367
  conversation_id = (
307
- getattr(instance, "conversation_id", None) or
308
- getattr(instance, "session_id", None) or
309
- kwargs.get("conversation_id") or
310
- kwargs.get("session_id")
368
+ getattr(instance, "conversation_id", None)
369
+ or getattr(instance, "session_id", None)
370
+ or kwargs.get("conversation_id")
371
+ or kwargs.get("session_id")
311
372
  )
312
373
  if conversation_id:
313
- span.set_attribute(SemanticConvention.GEN_AI_CONVERSATION_ID, str(conversation_id))
374
+ span.set_attribute(
375
+ SemanticConvention.GEN_AI_CONVERSATION_ID, str(conversation_id)
376
+ )
314
377
 
315
378
  # Data source tracking for RAG operations
316
379
  memory = getattr(instance, "memory", None)
@@ -319,25 +382,37 @@ def _set_conversation_and_data_source_attributes(span, instance, endpoint, args,
319
382
  memory_provider = getattr(memory, "provider", None)
320
383
  if memory_provider:
321
384
  span.set_attribute(SemanticConvention.GEN_AI_DATA_SOURCE_TYPE, "memory")
322
- span.set_attribute(SemanticConvention.GEN_AI_DATA_SOURCE_ID, str(memory_provider))
385
+ span.set_attribute(
386
+ SemanticConvention.GEN_AI_DATA_SOURCE_ID, str(memory_provider)
387
+ )
323
388
 
324
389
  # Knowledge base or vector store detection
325
390
  knowledge_source = getattr(instance, "knowledge_source", None)
326
391
  if knowledge_source:
327
- span.set_attribute(SemanticConvention.GEN_AI_DATA_SOURCE_TYPE, "knowledge_base")
328
- span.set_attribute(SemanticConvention.GEN_AI_DATA_SOURCE_ID, str(knowledge_source))
392
+ span.set_attribute(
393
+ SemanticConvention.GEN_AI_DATA_SOURCE_TYPE, "knowledge_base"
394
+ )
395
+ span.set_attribute(
396
+ SemanticConvention.GEN_AI_DATA_SOURCE_ID, str(knowledge_source)
397
+ )
329
398
 
330
399
  # Tool-based data sources
331
400
  tools = getattr(instance, "tools", [])
332
401
  for tool in tools:
333
402
  tool_name = getattr(tool, "name", "").lower()
334
- if any(keyword in tool_name for keyword in ["search", "retrieval", "database", "vector"]):
335
- span.set_attribute(SemanticConvention.GEN_AI_DATA_SOURCE_TYPE, "external_tool")
403
+ if any(
404
+ keyword in tool_name
405
+ for keyword in ["search", "retrieval", "database", "vector"]
406
+ ):
407
+ span.set_attribute(
408
+ SemanticConvention.GEN_AI_DATA_SOURCE_TYPE, "external_tool"
409
+ )
336
410
  break
337
411
 
338
412
  except Exception as e:
339
413
  handle_exception(span, e)
340
414
 
415
+
341
416
  def _set_task_business_intelligence(span, instance, endpoint, args, kwargs):
342
417
  """Set task business intelligence using standard OpenTelemetry semantic conventions"""
343
418
  if not endpoint.startswith("task_"):
@@ -352,16 +427,21 @@ def _set_task_business_intelligence(span, instance, endpoint, args, kwargs):
352
427
  # Task description
353
428
  task_description = getattr(instance, "description", "")
354
429
  if task_description:
355
- span.set_attribute(SemanticConvention.GEN_AI_TASK_DESCRIPTION, task_description)
430
+ span.set_attribute(
431
+ SemanticConvention.GEN_AI_TASK_DESCRIPTION, task_description
432
+ )
356
433
 
357
434
  # Task expected output (keep only essential attributes that have semantic conventions or are critical)
358
435
  expected_output = getattr(instance, "expected_output", "")
359
436
  if expected_output:
360
- span.set_attribute(SemanticConvention.GEN_AI_TASK_EXPECTED_OUTPUT, expected_output)
437
+ span.set_attribute(
438
+ SemanticConvention.GEN_AI_TASK_EXPECTED_OUTPUT, expected_output
439
+ )
361
440
 
362
441
  except Exception as e:
363
442
  handle_exception(span, e)
364
443
 
444
+
365
445
  def _set_crew_business_intelligence(span, instance, endpoint, args, kwargs):
366
446
  """Set crew business intelligence using standard OpenTelemetry semantic conventions"""
367
447
  if not endpoint.startswith("crew_"):
@@ -374,6 +454,7 @@ def _set_crew_business_intelligence(span, instance, endpoint, args, kwargs):
374
454
  except Exception as e:
375
455
  handle_exception(span, e)
376
456
 
457
+
377
458
  def _set_tool_business_intelligence(span, instance, endpoint, args, kwargs):
378
459
  """Set tool business intelligence using standard OpenTelemetry semantic conventions"""
379
460
  if not endpoint.startswith("tool_"):
@@ -381,14 +462,19 @@ def _set_tool_business_intelligence(span, instance, endpoint, args, kwargs):
381
462
 
382
463
  try:
383
464
  # Standard OpenTelemetry Gen AI Tool attributes
384
- tool_name = getattr(instance, "name", None) or getattr(instance, "__class__", type(instance)).__name__
465
+ tool_name = (
466
+ getattr(instance, "name", None)
467
+ or getattr(instance, "__class__", type(instance)).__name__
468
+ )
385
469
  if tool_name:
386
470
  span.set_attribute(SemanticConvention.GEN_AI_TOOL_NAME, tool_name)
387
471
 
388
472
  # Tool call ID if available (for tracking specific tool invocations)
389
473
  tool_call_id = kwargs.get("call_id", None) or getattr(instance, "call_id", None)
390
474
  if tool_call_id:
391
- span.set_attribute(SemanticConvention.GEN_AI_TOOL_CALL_ID, str(tool_call_id))
475
+ span.set_attribute(
476
+ SemanticConvention.GEN_AI_TOOL_CALL_ID, str(tool_call_id)
477
+ )
392
478
 
393
479
  # === OpenAI Function Calling Attributes ===
394
480
  _set_openai_tool_attributes(span, instance, endpoint, args, kwargs)
@@ -396,6 +482,7 @@ def _set_tool_business_intelligence(span, instance, endpoint, args, kwargs):
396
482
  except Exception as e:
397
483
  handle_exception(span, e)
398
484
 
485
+
399
486
  def _set_openai_tool_attributes(span, instance, endpoint, args, kwargs):
400
487
  """Set OpenAI function calling specific attributes using standard conventions"""
401
488
  try:
@@ -409,7 +496,9 @@ def _set_openai_tool_attributes(span, instance, endpoint, args, kwargs):
409
496
  tool_type = "api_client"
410
497
  elif any(keyword in tool_class for keyword in ["database", "sql", "query"]):
411
498
  tool_type = "database"
412
- elif any(keyword in tool_class for keyword in ["vector", "embedding", "retrieval"]):
499
+ elif any(
500
+ keyword in tool_class for keyword in ["vector", "embedding", "retrieval"]
501
+ ):
413
502
  tool_type = "vector_store"
414
503
  else:
415
504
  tool_type = "custom"
@@ -420,6 +509,7 @@ def _set_openai_tool_attributes(span, instance, endpoint, args, kwargs):
420
509
  except Exception as e:
421
510
  handle_exception(span, e)
422
511
 
512
+
423
513
  def _capture_content(span, instance, response, endpoint):
424
514
  """Capture input/output content with MIME types"""
425
515
 
@@ -429,8 +519,10 @@ def _capture_content(span, instance, response, endpoint):
429
519
  span.add_event(
430
520
  name=SemanticConvention.GEN_AI_CONTENT_COMPLETION_EVENT,
431
521
  attributes={
432
- SemanticConvention.GEN_AI_CONTENT_COMPLETION: str(response)[:1000], # Limit size
433
- }
522
+ SemanticConvention.GEN_AI_CONTENT_COMPLETION: str(response)[
523
+ :1000
524
+ ], # Limit size
525
+ },
434
526
  )
435
527
 
436
528
  # Capture input content based on operation type
@@ -440,14 +532,17 @@ def _capture_content(span, instance, response, endpoint):
440
532
  span.add_event(
441
533
  name=SemanticConvention.GEN_AI_CONTENT_PROMPT_EVENT,
442
534
  attributes={
443
- SemanticConvention.GEN_AI_CONTENT_PROMPT: task_description[:1000],
444
- }
535
+ SemanticConvention.GEN_AI_CONTENT_PROMPT: task_description[
536
+ :1000
537
+ ],
538
+ },
445
539
  )
446
540
 
447
541
  except Exception:
448
542
  # Graceful degradation
449
543
  pass
450
544
 
545
+
451
546
  def _track_cost_and_tokens(span, instance, response, endpoint):
452
547
  """Track cost and token usage for business intelligence"""
453
548
 
@@ -462,7 +557,9 @@ def _track_cost_and_tokens(span, instance, response, endpoint):
462
557
  response_length = len(str(response))
463
558
  # Estimate token count (rough approximation: 4 chars per token)
464
559
  estimated_tokens = response_length // 4
465
- span.set_attribute(SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE, estimated_tokens)
560
+ span.set_attribute(
561
+ SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE, estimated_tokens
562
+ )
466
563
 
467
564
  # Cost estimation would require pricing information
468
565
  # This could be enhanced with actual cost tracking
@@ -471,7 +568,10 @@ def _track_cost_and_tokens(span, instance, response, endpoint):
471
568
  # Graceful degradation
472
569
  pass
473
570
 
474
- def _record_crewai_metrics(metrics, operation_type, duration_ms, environment, application_name):
571
+
572
+ def _record_crewai_metrics(
573
+ metrics, operation_type, duration_ms, environment, application_name
574
+ ):
475
575
  """Record CrewAI-specific metrics"""
476
576
 
477
577
  try:
@@ -484,7 +584,9 @@ def _record_crewai_metrics(metrics, operation_type, duration_ms, environment, ap
484
584
 
485
585
  # Record operation duration
486
586
  if "genai_client_operation_duration" in metrics:
487
- metrics["genai_client_operation_duration"].record(duration_ms / 1000, attributes)
587
+ metrics["genai_client_operation_duration"].record(
588
+ duration_ms / 1000, attributes
589
+ )
488
590
 
489
591
  # Record operation count
490
592
  if "genai_requests" in metrics:
@@ -494,6 +596,7 @@ def _record_crewai_metrics(metrics, operation_type, duration_ms, environment, ap
494
596
  # Graceful degradation
495
597
  pass
496
598
 
599
+
497
600
  def _parse_tools(tools):
498
601
  """Parse tools list into JSON format"""
499
602