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
openlit/guard/utils.py CHANGED
@@ -16,6 +16,7 @@ from openlit.semcov import SemanticConvention
16
16
  # Initialize logger for logging potential issues and operations
17
17
  logger = logging.getLogger(__name__)
18
18
 
19
+
19
20
  class JsonOutput(BaseModel):
20
21
  """
21
22
  A model representing the structure of JSON output for prompt injection detection.
@@ -34,13 +35,17 @@ class JsonOutput(BaseModel):
34
35
  classification: str
35
36
  explanation: str
36
37
 
37
- def setup_provider(provider: Optional[str], api_key: Optional[str],
38
- model: Optional[str],
39
- base_url: Optional[str]) -> Tuple[Optional[str], Optional[str], Optional[str]]:
38
+
39
+ def setup_provider(
40
+ provider: Optional[str],
41
+ api_key: Optional[str],
42
+ model: Optional[str],
43
+ base_url: Optional[str],
44
+ ) -> Tuple[Optional[str], Optional[str], Optional[str]]:
40
45
  """Function to setup LLM provider"""
41
46
  provider_configs = {
42
47
  "openai": {"env_var": "OPENAI_API_KEY"},
43
- "anthropic": {"env_var": "ANTHROPIC_API_KEY"}
48
+ "anthropic": {"env_var": "ANTHROPIC_API_KEY"},
44
49
  }
45
50
 
46
51
  if provider is None:
@@ -60,7 +65,9 @@ def setup_provider(provider: Optional[str], api_key: Optional[str],
60
65
 
61
66
  if not api_key:
62
67
  # pylint: disable=line-too-long
63
- raise ValueError(f"API key required via 'api_key' parameter or '{env_var}' environment variable")
68
+ raise ValueError(
69
+ f"API key required via 'api_key' parameter or '{env_var}' environment variable"
70
+ )
64
71
 
65
72
  return api_key, model, base_url
66
73
 
@@ -69,6 +76,7 @@ def format_prompt(system_prompt: str, text: str) -> str:
69
76
  """Function to format the prompt"""
70
77
  return system_prompt.replace("{{prompt}}", text)
71
78
 
79
+
72
80
  def llm_response(provider: str, prompt: str, model: str, base_url: str) -> str:
73
81
  """Function to get LLM response based on provider"""
74
82
  # pylint: disable=no-else-return
@@ -79,6 +87,7 @@ def llm_response(provider: str, prompt: str, model: str, base_url: str) -> str:
79
87
  else:
80
88
  raise ValueError(f"Unsupported provider: {provider}")
81
89
 
90
+
82
91
  def llm_response_openai(prompt: str, model: str, base_url: str) -> str:
83
92
  """Function to make LLM call to OpenAI"""
84
93
  client = OpenAI(base_url=base_url)
@@ -95,10 +104,11 @@ def llm_response_openai(prompt: str, model: str, base_url: str) -> str:
95
104
  {"role": "user", "content": prompt},
96
105
  ],
97
106
  temperature=0.0,
98
- response_format=JsonOutput
107
+ response_format=JsonOutput,
99
108
  )
100
109
  return response.choices[0].message.content
101
110
 
111
+
102
112
  def llm_response_anthropic(prompt: str, model: str) -> str:
103
113
  """Function to make LLM call to Anthropic"""
104
114
  client = Anthropic()
@@ -113,26 +123,42 @@ def llm_response_anthropic(prompt: str, model: str) -> str:
113
123
  "input_schema": {
114
124
  "type": "object",
115
125
  "properties": {
116
- "verdict": {"type": "string", "description": "Verdict of guardrail"},
126
+ "verdict": {
127
+ "type": "string",
128
+ "description": "Verdict of guardrail",
129
+ },
117
130
  "guard": {"type": "string", "description": "Type of guard"},
118
- "score": {"type": "number", "description": "Prompt score from Guard."},
119
- "classification": {"type": "string", "description": "Incorrect prompt type"},
120
- "explanation": {"type": "string", "description": "Reason for classification"}
131
+ "score": {
132
+ "type": "number",
133
+ "description": "Prompt score from Guard.",
134
+ },
135
+ "classification": {
136
+ "type": "string",
137
+ "description": "Incorrect prompt type",
138
+ },
139
+ "explanation": {
140
+ "type": "string",
141
+ "description": "Reason for classification",
142
+ },
121
143
  },
122
- "required": ["verdict", "guard", "score", "classification", "explanation"]
123
- }
144
+ "required": [
145
+ "verdict",
146
+ "guard",
147
+ "score",
148
+ "classification",
149
+ "explanation",
150
+ ],
151
+ },
124
152
  }
125
153
  ]
126
154
 
127
155
  response = client.messages.create(
128
156
  model=model,
129
- messages=[
130
- {"role": "user", "content": prompt}
131
- ],
157
+ messages=[{"role": "user", "content": prompt}],
132
158
  max_tokens=2000,
133
159
  temperature=0.0,
134
160
  tools=tools,
135
- stream=False
161
+ stream=False,
136
162
  )
137
163
 
138
164
  for content in response.content:
@@ -142,6 +168,7 @@ def llm_response_anthropic(prompt: str, model: str) -> str:
142
168
 
143
169
  return response
144
170
 
171
+
145
172
  def parse_llm_response(response) -> JsonOutput:
146
173
  """
147
174
  Parses the LLM response into a JsonOutput object.
@@ -163,8 +190,14 @@ def parse_llm_response(response) -> JsonOutput:
163
190
  return JsonOutput(**data)
164
191
  except (json.JSONDecodeError, TypeError) as e:
165
192
  logger.error("Error parsing LLM response: '%s'", e)
166
- return JsonOutput(score=0, classification="none", explanation="none",
167
- verdict="none", guard="none")
193
+ return JsonOutput(
194
+ score=0,
195
+ classification="none",
196
+ explanation="none",
197
+ verdict="none",
198
+ guard="none",
199
+ )
200
+
168
201
 
169
202
  def custom_rule_detection(text: str, custom_rules: list) -> JsonOutput:
170
203
  """
@@ -183,10 +216,12 @@ def custom_rule_detection(text: str, custom_rules: list) -> JsonOutput:
183
216
  guard=rule.get("guard", "prompt_injection"),
184
217
  score=rule.get("score", 0.5),
185
218
  classification=rule.get("classification", "custom"),
186
- explanation=rule.get("explanation", "Matched custom rule pattern.")
219
+ explanation=rule.get("explanation", "Matched custom rule pattern."),
187
220
  )
188
- return JsonOutput(score=0, classification="none", explanation="none",
189
- verdict="none", guard="none")
221
+ return JsonOutput(
222
+ score=0, classification="none", explanation="none", verdict="none", guard="none"
223
+ )
224
+
190
225
 
191
226
  def guard_metrics():
192
227
  """
@@ -204,11 +239,12 @@ def guard_metrics():
204
239
  guard_requests = meter.create_counter(
205
240
  name=SemanticConvention.GUARD_REQUESTS,
206
241
  description="Counter for Guard requests",
207
- unit="1"
242
+ unit="1",
208
243
  )
209
244
 
210
245
  return guard_requests
211
246
 
247
+
212
248
  def guard_metric_attributes(verdict, score, validator, classification, explanation):
213
249
  """
214
250
  Initializes OpenTelemetry attributes for metrics.
@@ -5,15 +5,15 @@ import importlib.metadata
5
5
  from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
6
6
  from wrapt import wrap_function_wrapper
7
7
 
8
- from openlit.instrumentation.ag2.ag2 import (
9
- conversable_agent, agent_run
10
- )
8
+ from openlit.instrumentation.ag2.ag2 import conversable_agent, agent_run
11
9
  from openlit.instrumentation.ag2.async_ag2 import (
12
- async_conversable_agent, async_agent_run
10
+ async_conversable_agent,
11
+ async_agent_run,
13
12
  )
14
13
 
15
14
  _instruments = ("ag2 >= 0.3.2",)
16
15
 
16
+
17
17
  class AG2Instrumentor(BaseInstrumentor):
18
18
  """
19
19
  An instrumentor for AG2 client library.
@@ -36,16 +36,32 @@ class AG2Instrumentor(BaseInstrumentor):
36
36
  wrap_function_wrapper(
37
37
  "autogen.agentchat.conversable_agent",
38
38
  "ConversableAgent.__init__",
39
- conversable_agent(version, environment, application_name,
40
- tracer, pricing_info, capture_message_content, metrics, disable_metrics),
39
+ conversable_agent(
40
+ version,
41
+ environment,
42
+ application_name,
43
+ tracer,
44
+ pricing_info,
45
+ capture_message_content,
46
+ metrics,
47
+ disable_metrics,
48
+ ),
41
49
  )
42
50
 
43
51
  # sync agent run
44
52
  wrap_function_wrapper(
45
53
  "autogen.agentchat.conversable_agent",
46
54
  "ConversableAgent.run",
47
- agent_run(version, environment, application_name,
48
- tracer, pricing_info, capture_message_content, metrics, disable_metrics),
55
+ agent_run(
56
+ version,
57
+ environment,
58
+ application_name,
59
+ tracer,
60
+ pricing_info,
61
+ capture_message_content,
62
+ metrics,
63
+ disable_metrics,
64
+ ),
49
65
  )
50
66
 
51
67
  def _uninstrument(self, **kwargs):
@@ -4,18 +4,24 @@ 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,
14
11
  )
15
12
  from openlit.semcov import SemanticConvention
16
13
 
17
- def conversable_agent(version, environment, application_name, tracer, pricing_info,
18
- capture_message_content, metrics, disable_metrics):
14
+
15
+ def conversable_agent(
16
+ version,
17
+ environment,
18
+ application_name,
19
+ tracer,
20
+ pricing_info,
21
+ capture_message_content,
22
+ metrics,
23
+ disable_metrics,
24
+ ):
19
25
  """
20
26
  Generates a telemetry wrapper for AG2 conversable agent creation.
21
27
  """
@@ -25,12 +31,16 @@ def conversable_agent(version, environment, application_name, tracer, pricing_in
25
31
  Wraps the AG2 conversable agent creation call.
26
32
  """
27
33
 
28
- server_address, server_port = set_server_address_and_port(instance, "127.0.0.1", 80)
34
+ server_address, server_port = set_server_address_and_port(
35
+ instance, "127.0.0.1", 80
36
+ )
29
37
  agent_name = kwargs.get("name", "NOT_FOUND")
30
38
  llm_config = kwargs.get("llm_config", {})
31
39
  system_message = kwargs.get("system_message", "")
32
40
 
33
- span_name = f"{SemanticConvention.GEN_AI_OPERATION_TYPE_CREATE_AGENT} {agent_name}"
41
+ span_name = (
42
+ f"{SemanticConvention.GEN_AI_OPERATION_TYPE_CREATE_AGENT} {agent_name}"
43
+ )
34
44
 
35
45
  with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
36
46
  start_time = time.time()
@@ -51,7 +61,7 @@ def conversable_agent(version, environment, application_name, tracer, pricing_in
51
61
  span=span,
52
62
  capture_message_content=capture_message_content,
53
63
  disable_metrics=disable_metrics,
54
- version=version
64
+ version=version,
55
65
  )
56
66
 
57
67
  except Exception as e:
@@ -61,8 +71,17 @@ def conversable_agent(version, environment, application_name, tracer, pricing_in
61
71
 
62
72
  return wrapper
63
73
 
64
- def agent_run(version, environment, application_name, tracer, pricing_info,
65
- capture_message_content, metrics, disable_metrics):
74
+
75
+ def agent_run(
76
+ version,
77
+ environment,
78
+ application_name,
79
+ tracer,
80
+ pricing_info,
81
+ capture_message_content,
82
+ metrics,
83
+ disable_metrics,
84
+ ):
66
85
  """
67
86
  Generates a telemetry wrapper for AG2 agent run execution.
68
87
  """
@@ -72,7 +91,9 @@ def agent_run(version, environment, application_name, tracer, pricing_info,
72
91
  Wraps the AG2 agent run execution call.
73
92
  """
74
93
 
75
- server_address, server_port = set_server_address_and_port(instance, "127.0.0.1", 80)
94
+ server_address, server_port = set_server_address_and_port(
95
+ instance, "127.0.0.1", 80
96
+ )
76
97
 
77
98
  # Extract agent name from instance
78
99
  agent_name = getattr(instance, "name", "NOT_FOUND")
@@ -103,7 +124,7 @@ def agent_run(version, environment, application_name, tracer, pricing_info,
103
124
  span=span,
104
125
  capture_message_content=capture_message_content,
105
126
  disable_metrics=disable_metrics,
106
- version=version
127
+ version=version,
107
128
  )
108
129
 
109
130
  except Exception as e:
@@ -4,18 +4,24 @@ Module for monitoring AG2 API calls (async version).
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,
14
11
  )
15
12
  from openlit.semcov import SemanticConvention
16
13
 
17
- def async_conversable_agent(version, environment, application_name, tracer, pricing_info,
18
- capture_message_content, metrics, disable_metrics):
14
+
15
+ def async_conversable_agent(
16
+ version,
17
+ environment,
18
+ application_name,
19
+ tracer,
20
+ pricing_info,
21
+ capture_message_content,
22
+ metrics,
23
+ disable_metrics,
24
+ ):
19
25
  """
20
26
  Generates a telemetry wrapper for AG2 async conversable agent creation.
21
27
  """
@@ -25,12 +31,16 @@ def async_conversable_agent(version, environment, application_name, tracer, pric
25
31
  Wraps the AG2 async conversable agent creation call.
26
32
  """
27
33
 
28
- server_address, server_port = set_server_address_and_port(instance, "127.0.0.1", 80)
34
+ server_address, server_port = set_server_address_and_port(
35
+ instance, "127.0.0.1", 80
36
+ )
29
37
  agent_name = kwargs.get("name", "NOT_FOUND")
30
38
  llm_config = kwargs.get("llm_config", {})
31
39
  system_message = kwargs.get("system_message", "")
32
40
 
33
- span_name = f"{SemanticConvention.GEN_AI_OPERATION_TYPE_CREATE_AGENT} {agent_name}"
41
+ span_name = (
42
+ f"{SemanticConvention.GEN_AI_OPERATION_TYPE_CREATE_AGENT} {agent_name}"
43
+ )
34
44
 
35
45
  with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
36
46
  start_time = time.time()
@@ -51,7 +61,7 @@ def async_conversable_agent(version, environment, application_name, tracer, pric
51
61
  span=span,
52
62
  capture_message_content=capture_message_content,
53
63
  disable_metrics=disable_metrics,
54
- version=version
64
+ version=version,
55
65
  )
56
66
 
57
67
  except Exception as e:
@@ -61,8 +71,17 @@ def async_conversable_agent(version, environment, application_name, tracer, pric
61
71
 
62
72
  return wrapper
63
73
 
64
- def async_agent_run(version, environment, application_name, tracer, pricing_info,
65
- capture_message_content, metrics, disable_metrics):
74
+
75
+ def async_agent_run(
76
+ version,
77
+ environment,
78
+ application_name,
79
+ tracer,
80
+ pricing_info,
81
+ capture_message_content,
82
+ metrics,
83
+ disable_metrics,
84
+ ):
66
85
  """
67
86
  Generates a telemetry wrapper for AG2 async agent run execution.
68
87
  """
@@ -72,7 +91,9 @@ def async_agent_run(version, environment, application_name, tracer, pricing_info
72
91
  Wraps the AG2 async agent run execution call.
73
92
  """
74
93
 
75
- server_address, server_port = set_server_address_and_port(instance, "127.0.0.1", 80)
94
+ server_address, server_port = set_server_address_and_port(
95
+ instance, "127.0.0.1", 80
96
+ )
76
97
 
77
98
  # Extract agent name from instance
78
99
  agent_name = getattr(instance, "name", "NOT_FOUND")
@@ -103,7 +124,7 @@ def async_agent_run(version, environment, application_name, tracer, pricing_info
103
124
  span=span,
104
125
  capture_message_content=capture_message_content,
105
126
  disable_metrics=disable_metrics,
106
- version=version
127
+ version=version,
107
128
  )
108
129
 
109
130
  except Exception as e:
@@ -1,6 +1,7 @@
1
1
  """
2
2
  AG2 OpenTelemetry instrumentation utility functions
3
3
  """
4
+
4
5
  import time
5
6
 
6
7
  from opentelemetry.trace import Status, StatusCode
@@ -12,6 +13,7 @@ from openlit.__helpers import (
12
13
  )
13
14
  from openlit.semcov import SemanticConvention
14
15
 
16
+
15
17
  def calculate_tokens_and_cost(response, request_model, pricing_info):
16
18
  """
17
19
  Calculate the input, output tokens, and their respective costs from AG2 response.
@@ -21,7 +23,9 @@ def calculate_tokens_and_cost(response, request_model, pricing_info):
21
23
 
22
24
  # Early return if response doesn't have cost data
23
25
  if not hasattr(response, "cost") or response.cost is None:
24
- cost = get_chat_model_cost(request_model, pricing_info, input_tokens, output_tokens)
26
+ cost = get_chat_model_cost(
27
+ request_model, pricing_info, input_tokens, output_tokens
28
+ )
25
29
  return input_tokens, output_tokens, cost
26
30
 
27
31
  try:
@@ -34,6 +38,7 @@ def calculate_tokens_and_cost(response, request_model, pricing_info):
34
38
  cost = get_chat_model_cost(request_model, pricing_info, input_tokens, output_tokens)
35
39
  return input_tokens, output_tokens, cost
36
40
 
41
+
37
42
  def _extract_tokens_from_cost(cost_data):
38
43
  """
39
44
  Extract input and output tokens from AG2 cost data structure.
@@ -52,6 +57,7 @@ def _extract_tokens_from_cost(cost_data):
52
57
 
53
58
  return input_tokens, output_tokens
54
59
 
60
+
55
61
  def format_content(chat_history):
56
62
  """
57
63
  Format the chat history into a string for span events.
@@ -67,32 +73,62 @@ def format_content(chat_history):
67
73
 
68
74
  return "\n".join(formatted_messages)
69
75
 
70
- def common_agent_logic(scope, pricing_info, environment, application_name, metrics,
71
- capture_message_content, disable_metrics, version, operation_type):
76
+
77
+ def common_agent_logic(
78
+ scope,
79
+ pricing_info,
80
+ environment,
81
+ application_name,
82
+ metrics,
83
+ capture_message_content,
84
+ disable_metrics,
85
+ version,
86
+ operation_type,
87
+ ):
72
88
  """
73
89
  Process agent request and generate Telemetry
74
90
  """
75
91
 
76
92
  # Common Span Attributes
77
- common_span_attributes(scope,
78
- operation_type, SemanticConvention.GEN_AI_SYSTEM_AG2,
79
- scope._server_address, scope._server_port, scope._request_model, scope._response_model,
80
- environment, application_name, False, 0, scope._end_time - scope._start_time, version)
93
+ common_span_attributes(
94
+ scope,
95
+ operation_type,
96
+ SemanticConvention.GEN_AI_SYSTEM_AG2,
97
+ scope._server_address,
98
+ scope._server_port,
99
+ scope._request_model,
100
+ scope._response_model,
101
+ environment,
102
+ application_name,
103
+ False,
104
+ 0,
105
+ scope._end_time - scope._start_time,
106
+ version,
107
+ )
81
108
 
82
109
  # Span Attributes for Agent-specific parameters
83
110
  scope._span.set_attribute(SemanticConvention.GEN_AI_AGENT_NAME, scope._agent_name)
84
111
 
85
112
  # Span Attributes for Response parameters
86
113
  if hasattr(scope, "_input_tokens"):
87
- scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS, scope._input_tokens)
88
- scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_OUTPUT_TOKENS, scope._output_tokens)
89
- scope._span.set_attribute(SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE, scope._input_tokens + scope._output_tokens)
114
+ scope._span.set_attribute(
115
+ SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS, scope._input_tokens
116
+ )
117
+ scope._span.set_attribute(
118
+ SemanticConvention.GEN_AI_USAGE_OUTPUT_TOKENS, scope._output_tokens
119
+ )
120
+ scope._span.set_attribute(
121
+ SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE,
122
+ scope._input_tokens + scope._output_tokens,
123
+ )
90
124
  scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_COST, scope._cost)
91
125
 
92
126
  # Span Attributes for Content
93
127
  if capture_message_content and hasattr(scope, "_chat_history"):
94
128
  chat_content = format_content(scope._chat_history)
95
- scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_COMPLETION, chat_content)
129
+ scope._span.set_attribute(
130
+ SemanticConvention.GEN_AI_CONTENT_COMPLETION, chat_content
131
+ )
96
132
 
97
133
  # To be removed once the change to span_attributes (from span events) is complete
98
134
  scope._span.add_event(
@@ -104,20 +140,51 @@ def common_agent_logic(scope, pricing_info, environment, application_name, metri
104
140
 
105
141
  # Set agent description for create agent operation
106
142
  if hasattr(scope, "_system_message"):
107
- scope._span.set_attribute(SemanticConvention.GEN_AI_AGENT_DESCRIPTION, scope._system_message)
143
+ scope._span.set_attribute(
144
+ SemanticConvention.GEN_AI_AGENT_DESCRIPTION, scope._system_message
145
+ )
108
146
 
109
147
  scope._span.set_status(Status(StatusCode.OK))
110
148
 
111
149
  # Metrics
112
150
  if not disable_metrics and hasattr(scope, "_input_tokens"):
113
- record_completion_metrics(metrics, operation_type, SemanticConvention.GEN_AI_SYSTEM_AG2,
114
- scope._server_address, scope._server_port, scope._request_model, scope._response_model, environment,
115
- application_name, scope._start_time, scope._end_time, scope._input_tokens, scope._output_tokens,
116
- scope._cost, 0, scope._end_time - scope._start_time)
117
-
118
- def process_agent_creation(agent_name, llm_config, system_message, pricing_info, server_port, server_address,
119
- environment, application_name, metrics, start_time, span, capture_message_content=False,
120
- disable_metrics=False, version="1.0.0", **kwargs):
151
+ record_completion_metrics(
152
+ metrics,
153
+ operation_type,
154
+ SemanticConvention.GEN_AI_SYSTEM_AG2,
155
+ scope._server_address,
156
+ scope._server_port,
157
+ scope._request_model,
158
+ scope._response_model,
159
+ environment,
160
+ application_name,
161
+ scope._start_time,
162
+ scope._end_time,
163
+ scope._input_tokens,
164
+ scope._output_tokens,
165
+ scope._cost,
166
+ 0,
167
+ scope._end_time - scope._start_time,
168
+ )
169
+
170
+
171
+ def process_agent_creation(
172
+ agent_name,
173
+ llm_config,
174
+ system_message,
175
+ pricing_info,
176
+ server_port,
177
+ server_address,
178
+ environment,
179
+ application_name,
180
+ metrics,
181
+ start_time,
182
+ span,
183
+ capture_message_content=False,
184
+ disable_metrics=False,
185
+ version="1.0.0",
186
+ **kwargs,
187
+ ):
121
188
  """
122
189
  Process agent creation and generate Telemetry
123
190
  """
@@ -134,12 +201,36 @@ def process_agent_creation(agent_name, llm_config, system_message, pricing_info,
134
201
  scope._system_message = system_message
135
202
  scope._server_address, scope._server_port = server_address, server_port
136
203
 
137
- common_agent_logic(scope, pricing_info, environment, application_name, metrics,
138
- capture_message_content, disable_metrics, version, SemanticConvention.GEN_AI_OPERATION_TYPE_CREATE_AGENT)
139
-
140
- def process_agent_run(response, agent_name, request_model, pricing_info, server_port, server_address,
141
- environment, application_name, metrics, start_time, span, capture_message_content=False,
142
- disable_metrics=False, version="1.0.0", **kwargs):
204
+ common_agent_logic(
205
+ scope,
206
+ pricing_info,
207
+ environment,
208
+ application_name,
209
+ metrics,
210
+ capture_message_content,
211
+ disable_metrics,
212
+ version,
213
+ SemanticConvention.GEN_AI_OPERATION_TYPE_CREATE_AGENT,
214
+ )
215
+
216
+
217
+ def process_agent_run(
218
+ response,
219
+ agent_name,
220
+ request_model,
221
+ pricing_info,
222
+ server_port,
223
+ server_address,
224
+ environment,
225
+ application_name,
226
+ metrics,
227
+ start_time,
228
+ span,
229
+ capture_message_content=False,
230
+ disable_metrics=False,
231
+ version="1.0.0",
232
+ **kwargs,
233
+ ):
143
234
  """
144
235
  Process agent run and generate Telemetry
145
236
  """
@@ -157,19 +248,31 @@ def process_agent_run(response, agent_name, request_model, pricing_info, server_
157
248
 
158
249
  # Calculate tokens and cost
159
250
  scope._input_tokens, scope._output_tokens, scope._cost = calculate_tokens_and_cost(
160
- response, request_model, pricing_info)
251
+ response, request_model, pricing_info
252
+ )
161
253
 
162
254
  # Extract response model from cost data
163
255
  try:
164
256
  if hasattr(response, "cost") and response.cost is not None:
165
257
  cost_data = response.cost.get("usage_including_cached_inference", {})
166
- scope._response_model = list(cost_data.keys())[1] if len(cost_data) > 1 else request_model
258
+ scope._response_model = (
259
+ list(cost_data.keys())[1] if len(cost_data) > 1 else request_model
260
+ )
167
261
  else:
168
262
  scope._response_model = request_model
169
263
  except (AttributeError, IndexError, KeyError, TypeError):
170
264
  scope._response_model = request_model
171
265
 
172
- common_agent_logic(scope, pricing_info, environment, application_name, metrics,
173
- capture_message_content, disable_metrics, version, SemanticConvention.GEN_AI_OPERATION_TYPE_EXECUTE_AGENT_TASK)
266
+ common_agent_logic(
267
+ scope,
268
+ pricing_info,
269
+ environment,
270
+ application_name,
271
+ metrics,
272
+ capture_message_content,
273
+ disable_metrics,
274
+ version,
275
+ SemanticConvention.GEN_AI_OPERATION_TYPE_EXECUTE_AGENT_TASK,
276
+ )
174
277
 
175
278
  return response