openlit 1.34.30__py3-none-any.whl → 1.34.32__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 +19 -14
  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 +113 -6
  15. openlit/instrumentation/ag2/ag2.py +459 -17
  16. openlit/instrumentation/ag2/async_ag2.py +459 -17
  17. openlit/instrumentation/ag2/utils.py +475 -31
  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 +82 -6
  161. {openlit-1.34.30.dist-info → openlit-1.34.32.dist-info}/METADATA +2 -1
  162. openlit-1.34.32.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.32.dist-info}/LICENSE +0 -0
  168. {openlit-1.34.30.dist-info → openlit-1.34.32.dist-info}/WHEEL +0 -0
@@ -4,18 +4,59 @@ Module for monitoring AG2 API calls.
4
4
 
5
5
  import time
6
6
  from opentelemetry.trace import SpanKind
7
- from openlit.__helpers import (
8
- handle_exception,
9
- set_server_address_and_port
10
- )
7
+ from openlit.__helpers import handle_exception, set_server_address_and_port
11
8
  from openlit.instrumentation.ag2.utils import (
12
9
  process_agent_creation,
13
10
  process_agent_run,
11
+ process_agent_generate_reply,
12
+ process_agent_receive,
13
+ process_agent_send,
14
+ process_groupchat_operation,
15
+ process_speaker_selection,
14
16
  )
15
17
  from openlit.semcov import SemanticConvention
16
18
 
17
- def conversable_agent(version, environment, application_name, tracer, pricing_info,
18
- capture_message_content, metrics, disable_metrics):
19
+
20
+ def extract_agent_name(instance, fallback="unknown_agent"):
21
+ """
22
+ Extract agent name from AG2 instance with intelligent fallbacks.
23
+
24
+ Args:
25
+ instance: AG2 instance (Agent, GroupChat, etc.)
26
+ fallback: Default name if no name can be extracted
27
+
28
+ Returns:
29
+ str: Agent name or meaningful fallback
30
+ """
31
+ # Try to get the name attribute first
32
+ agent_name = getattr(instance, "name", None)
33
+ if agent_name:
34
+ return agent_name
35
+
36
+ # Try to get from class name and make it meaningful
37
+ class_name = getattr(instance, "__class__", type(instance)).__name__.lower()
38
+
39
+ # Map common AG2 class names to meaningful names
40
+ class_name_map = {
41
+ "conversableagent": "conversable_agent",
42
+ "groupchat": "group_chat",
43
+ "groupchatmanager": "group_chat_manager",
44
+ "agent": "agent",
45
+ }
46
+
47
+ return class_name_map.get(class_name, fallback)
48
+
49
+
50
+ def conversable_agent(
51
+ version,
52
+ environment,
53
+ application_name,
54
+ tracer,
55
+ pricing_info,
56
+ capture_message_content,
57
+ metrics,
58
+ disable_metrics,
59
+ ):
19
60
  """
20
61
  Generates a telemetry wrapper for AG2 conversable agent creation.
21
62
  """
@@ -25,12 +66,16 @@ def conversable_agent(version, environment, application_name, tracer, pricing_in
25
66
  Wraps the AG2 conversable agent creation call.
26
67
  """
27
68
 
28
- server_address, server_port = set_server_address_and_port(instance, "127.0.0.1", 80)
29
- agent_name = kwargs.get("name", "NOT_FOUND")
69
+ server_address, server_port = set_server_address_and_port(
70
+ instance, "127.0.0.1", 80
71
+ )
72
+ agent_name = kwargs.get("name", "unknown_agent")
30
73
  llm_config = kwargs.get("llm_config", {})
31
74
  system_message = kwargs.get("system_message", "")
32
75
 
33
- span_name = f"{SemanticConvention.GEN_AI_OPERATION_TYPE_CREATE_AGENT} {agent_name}"
76
+ span_name = (
77
+ f"{SemanticConvention.GEN_AI_OPERATION_TYPE_CREATE_AGENT} {agent_name}"
78
+ )
34
79
 
35
80
  with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
36
81
  start_time = time.time()
@@ -51,7 +96,7 @@ def conversable_agent(version, environment, application_name, tracer, pricing_in
51
96
  span=span,
52
97
  capture_message_content=capture_message_content,
53
98
  disable_metrics=disable_metrics,
54
- version=version
99
+ version=version,
55
100
  )
56
101
 
57
102
  except Exception as e:
@@ -61,8 +106,17 @@ def conversable_agent(version, environment, application_name, tracer, pricing_in
61
106
 
62
107
  return wrapper
63
108
 
64
- def agent_run(version, environment, application_name, tracer, pricing_info,
65
- capture_message_content, metrics, disable_metrics):
109
+
110
+ def agent_run(
111
+ version,
112
+ environment,
113
+ application_name,
114
+ tracer,
115
+ pricing_info,
116
+ capture_message_content,
117
+ metrics,
118
+ disable_metrics,
119
+ ):
66
120
  """
67
121
  Generates a telemetry wrapper for AG2 agent run execution.
68
122
  """
@@ -72,15 +126,17 @@ def agent_run(version, environment, application_name, tracer, pricing_info,
72
126
  Wraps the AG2 agent run execution call.
73
127
  """
74
128
 
75
- server_address, server_port = set_server_address_and_port(instance, "127.0.0.1", 80)
129
+ server_address, server_port = set_server_address_and_port(
130
+ instance, "127.0.0.1", 80
131
+ )
76
132
 
77
133
  # Extract agent name from instance
78
- agent_name = getattr(instance, "name", "NOT_FOUND")
134
+ agent_name = extract_agent_name(instance)
79
135
 
80
136
  # Extract model from instance llm_config
81
- request_model = "gpt-4o"
137
+ request_model = "unknown"
82
138
  if hasattr(instance, "llm_config") and isinstance(instance.llm_config, dict):
83
- request_model = instance.llm_config.get("model", "gpt-4o")
139
+ request_model = instance.llm_config.get("model", "unknown")
84
140
 
85
141
  span_name = f"{SemanticConvention.GEN_AI_OPERATION_TYPE_EXECUTE_AGENT_TASK} {agent_name}"
86
142
 
@@ -103,7 +159,393 @@ def agent_run(version, environment, application_name, tracer, pricing_info,
103
159
  span=span,
104
160
  capture_message_content=capture_message_content,
105
161
  disable_metrics=disable_metrics,
106
- version=version
162
+ version=version,
163
+ )
164
+
165
+ except Exception as e:
166
+ handle_exception(span, e)
167
+
168
+ return response
169
+
170
+ return wrapper
171
+
172
+
173
+ def agent_generate_reply(
174
+ version,
175
+ environment,
176
+ application_name,
177
+ tracer,
178
+ pricing_info,
179
+ capture_message_content,
180
+ metrics,
181
+ disable_metrics,
182
+ ):
183
+ """
184
+ Generates a telemetry wrapper for AG2 ConversableAgent.generate_reply.
185
+ """
186
+
187
+ def wrapper(wrapped, instance, args, kwargs):
188
+ """
189
+ Wraps the AG2 ConversableAgent.generate_reply call.
190
+ """
191
+
192
+ server_address, server_port = set_server_address_and_port(
193
+ instance, "127.0.0.1", 80
194
+ )
195
+
196
+ # Extract agent name from instance
197
+ agent_name = extract_agent_name(instance)
198
+
199
+ # Extract model from instance llm_config
200
+ request_model = "unknown"
201
+ if hasattr(instance, "llm_config") and isinstance(instance.llm_config, dict):
202
+ request_model = instance.llm_config.get("model", "unknown")
203
+
204
+ span_name = f"{SemanticConvention.GEN_AI_OPERATION_TYPE_EXECUTE_AGENT_TASK} {agent_name}"
205
+
206
+ with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
207
+ start_time = time.time()
208
+ response = wrapped(*args, **kwargs)
209
+
210
+ try:
211
+ response = process_agent_generate_reply(
212
+ response=response,
213
+ agent_name=agent_name,
214
+ request_model=request_model,
215
+ messages=args[0] if args else kwargs.get("messages", []),
216
+ sender=args[1] if len(args) > 1 else kwargs.get("sender", None),
217
+ pricing_info=pricing_info,
218
+ server_port=server_port,
219
+ server_address=server_address,
220
+ environment=environment,
221
+ application_name=application_name,
222
+ metrics=metrics,
223
+ start_time=start_time,
224
+ span=span,
225
+ capture_message_content=capture_message_content,
226
+ disable_metrics=disable_metrics,
227
+ version=version,
228
+ )
229
+
230
+ except Exception as e:
231
+ handle_exception(span, e)
232
+
233
+ return response
234
+
235
+ return wrapper
236
+
237
+
238
+ def agent_receive(
239
+ version,
240
+ environment,
241
+ application_name,
242
+ tracer,
243
+ pricing_info,
244
+ capture_message_content,
245
+ metrics,
246
+ disable_metrics,
247
+ ):
248
+ """
249
+ Generates a telemetry wrapper for AG2 ConversableAgent.receive.
250
+ """
251
+
252
+ def wrapper(wrapped, instance, args, kwargs):
253
+ """
254
+ Wraps the AG2 ConversableAgent.receive call.
255
+ """
256
+
257
+ server_address, server_port = set_server_address_and_port(
258
+ instance, "127.0.0.1", 80
259
+ )
260
+
261
+ # Extract agent name from instance
262
+ agent_name = extract_agent_name(instance)
263
+
264
+ # Extract sender information
265
+ sender = args[0] if args else kwargs.get("sender", None)
266
+ sender_name = getattr(sender, "name", "Unknown") if sender else "Unknown"
267
+
268
+ # Extract message
269
+ message = args[1] if len(args) > 1 else kwargs.get("message", "")
270
+
271
+ span_name = f"{SemanticConvention.GEN_AI_OPERATION_TYPE_EXECUTE_AGENT_TASK} {agent_name}"
272
+
273
+ with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
274
+ start_time = time.time()
275
+ response = wrapped(*args, **kwargs)
276
+
277
+ try:
278
+ process_agent_receive(
279
+ message=message,
280
+ agent_name=agent_name,
281
+ sender_name=sender_name,
282
+ agent_instance=instance,
283
+ pricing_info=pricing_info,
284
+ server_port=server_port,
285
+ server_address=server_address,
286
+ environment=environment,
287
+ application_name=application_name,
288
+ metrics=metrics,
289
+ start_time=start_time,
290
+ span=span,
291
+ capture_message_content=capture_message_content,
292
+ disable_metrics=disable_metrics,
293
+ version=version,
294
+ )
295
+
296
+ except Exception as e:
297
+ handle_exception(span, e)
298
+
299
+ return response
300
+
301
+ return wrapper
302
+
303
+
304
+ def agent_send(
305
+ version,
306
+ environment,
307
+ application_name,
308
+ tracer,
309
+ pricing_info,
310
+ capture_message_content,
311
+ metrics,
312
+ disable_metrics,
313
+ ):
314
+ """
315
+ Generates a telemetry wrapper for AG2 ConversableAgent.send.
316
+ """
317
+
318
+ def wrapper(wrapped, instance, args, kwargs):
319
+ """
320
+ Wraps the AG2 ConversableAgent.send call.
321
+ """
322
+
323
+ server_address, server_port = set_server_address_and_port(
324
+ instance, "127.0.0.1", 80
325
+ )
326
+
327
+ # Extract agent name from instance
328
+ agent_name = extract_agent_name(instance)
329
+
330
+ # Extract recipient information
331
+ recipient = args[0] if args else kwargs.get("recipient", None)
332
+ recipient_name = (
333
+ getattr(recipient, "name", "Unknown") if recipient else "Unknown"
334
+ )
335
+
336
+ # Extract message
337
+ message = args[1] if len(args) > 1 else kwargs.get("message", "")
338
+
339
+ span_name = f"{SemanticConvention.GEN_AI_OPERATION_TYPE_EXECUTE_AGENT_TASK} {agent_name}"
340
+
341
+ with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
342
+ start_time = time.time()
343
+ response = wrapped(*args, **kwargs)
344
+
345
+ try:
346
+ process_agent_send(
347
+ message=message,
348
+ agent_name=agent_name,
349
+ recipient_name=recipient_name,
350
+ agent_instance=instance,
351
+ pricing_info=pricing_info,
352
+ server_port=server_port,
353
+ server_address=server_address,
354
+ environment=environment,
355
+ application_name=application_name,
356
+ metrics=metrics,
357
+ start_time=start_time,
358
+ span=span,
359
+ capture_message_content=capture_message_content,
360
+ disable_metrics=disable_metrics,
361
+ version=version,
362
+ )
363
+
364
+ except Exception as e:
365
+ handle_exception(span, e)
366
+
367
+ return response
368
+
369
+ return wrapper
370
+
371
+
372
+ def groupchat_manager_run_chat(
373
+ version,
374
+ environment,
375
+ application_name,
376
+ tracer,
377
+ pricing_info,
378
+ capture_message_content,
379
+ metrics,
380
+ disable_metrics,
381
+ ):
382
+ """
383
+ Generates a telemetry wrapper for AG2 GroupChatManager.run_chat.
384
+ """
385
+
386
+ def wrapper(wrapped, instance, args, kwargs):
387
+ """
388
+ Wraps the AG2 GroupChatManager.run_chat call.
389
+ """
390
+
391
+ server_address, server_port = set_server_address_and_port(
392
+ instance, "127.0.0.1", 80
393
+ )
394
+
395
+ # Extract groupchat information
396
+ groupchat = getattr(instance, "groupchat", None)
397
+ if groupchat:
398
+ participants = [agent.name for agent in groupchat.agents]
399
+ group_name = f"GroupChat_{len(participants)}_agents"
400
+ else:
401
+ participants = []
402
+ group_name = "UnknownGroupChat"
403
+
404
+ # Extract model information from GroupChatManager
405
+ request_model = "unknown" # Default fallback
406
+ if hasattr(instance, "llm_config") and isinstance(instance.llm_config, dict):
407
+ request_model = instance.llm_config.get("model", "unknown")
408
+
409
+ # Try to get more specific model from groupchat
410
+ if groupchat and hasattr(groupchat, "select_speaker_auto_llm_config"):
411
+ llm_config = groupchat.select_speaker_auto_llm_config
412
+ if isinstance(llm_config, dict):
413
+ request_model = llm_config.get("model", request_model)
414
+ elif hasattr(llm_config, "model"):
415
+ request_model = llm_config.model
416
+
417
+ # Extract sender information
418
+ sender = kwargs.get("sender", None)
419
+
420
+ # Extract messages
421
+ messages = args[0] if args else kwargs.get("messages", [])
422
+
423
+ span_name = f"{SemanticConvention.GEN_AI_OPERATION_TYPE_FRAMEWORK} {group_name}"
424
+
425
+ with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
426
+ start_time = time.time()
427
+ response = wrapped(*args, **kwargs)
428
+
429
+ try:
430
+ process_groupchat_operation(
431
+ group_name=group_name,
432
+ participants=participants,
433
+ messages=messages,
434
+ sender=sender,
435
+ max_turns=None, # Not available in new API
436
+ request_model=request_model,
437
+ pricing_info=pricing_info,
438
+ server_port=server_port,
439
+ server_address=server_address,
440
+ environment=environment,
441
+ application_name=application_name,
442
+ metrics=metrics,
443
+ start_time=start_time,
444
+ span=span,
445
+ capture_message_content=capture_message_content,
446
+ disable_metrics=disable_metrics,
447
+ version=version,
448
+ )
449
+
450
+ except Exception as e:
451
+ handle_exception(span, e)
452
+
453
+ return response
454
+
455
+ return wrapper
456
+
457
+
458
+ def groupchat_select_speaker(
459
+ version,
460
+ environment,
461
+ application_name,
462
+ tracer,
463
+ pricing_info,
464
+ capture_message_content,
465
+ metrics,
466
+ disable_metrics,
467
+ ):
468
+ """
469
+ Generates a telemetry wrapper for AG2 GroupChat.select_speaker.
470
+ """
471
+
472
+ def wrapper(wrapped, instance, args, kwargs):
473
+ """
474
+ Wraps the AG2 GroupChat.select_speaker call.
475
+ """
476
+
477
+ server_address, server_port = set_server_address_and_port(
478
+ instance, "127.0.0.1", 80
479
+ )
480
+
481
+ # Extract speaker information
482
+ last_speaker = args[0] if args else kwargs.get("last_speaker", None)
483
+ selector = args[1] if len(args) > 1 else kwargs.get("selector", None)
484
+
485
+ last_speaker_name = (
486
+ getattr(last_speaker, "name", "Unknown") if last_speaker else "Unknown"
487
+ )
488
+
489
+ # Extract agents list
490
+ agents = getattr(instance, "agents", [])
491
+
492
+ # Extract model information from GroupChat instance
493
+ request_model = "unknown" # Default fallback
494
+ # Check for speaker selection specific config
495
+ if hasattr(instance, "select_speaker_auto_llm_config"):
496
+ llm_config = instance.select_speaker_auto_llm_config
497
+ if isinstance(llm_config, dict):
498
+ request_model = llm_config.get("model", "unknown")
499
+ elif hasattr(llm_config, "model"):
500
+ request_model = llm_config.model
501
+
502
+ # Try to get model from selector if available
503
+ if (
504
+ selector
505
+ and hasattr(selector, "llm_config")
506
+ and isinstance(selector.llm_config, dict)
507
+ ):
508
+ request_model = selector.llm_config.get("model", request_model)
509
+
510
+ # Try to get model from agents if still unknown
511
+ if request_model == "unknown" and agents:
512
+ for agent in agents:
513
+ if hasattr(agent, "llm_config") and isinstance(agent.llm_config, dict):
514
+ model = agent.llm_config.get("model")
515
+ if model:
516
+ request_model = model
517
+ break
518
+
519
+ span_name = (
520
+ f"{SemanticConvention.GEN_AI_OPERATION_TYPE_AGENT} speaker_selection"
521
+ )
522
+
523
+ with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
524
+ start_time = time.time()
525
+ response = wrapped(*args, **kwargs)
526
+
527
+ try:
528
+ selected_speaker_name = (
529
+ getattr(response, "name", "Unknown") if response else "Unknown"
530
+ )
531
+
532
+ process_speaker_selection(
533
+ last_speaker=last_speaker_name,
534
+ selected_speaker=selected_speaker_name,
535
+ selector=selector,
536
+ agents=agents,
537
+ request_model=request_model,
538
+ pricing_info=pricing_info,
539
+ server_port=server_port,
540
+ server_address=server_address,
541
+ environment=environment,
542
+ application_name=application_name,
543
+ metrics=metrics,
544
+ start_time=start_time,
545
+ span=span,
546
+ capture_message_content=capture_message_content,
547
+ disable_metrics=disable_metrics,
548
+ version=version,
107
549
  )
108
550
 
109
551
  except Exception as e: