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
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Azure AI Inference OpenTelemetry instrumentation utility functions
3
3
  """
4
+
4
5
  import time
5
6
 
6
7
  from opentelemetry.trace import Status, StatusCode
@@ -17,6 +18,7 @@ from openlit.__helpers import (
17
18
  )
18
19
  from openlit.semcov import SemanticConvention
19
20
 
21
+
20
22
  def format_content(messages):
21
23
  """
22
24
  Process a list of messages to extract content.
@@ -29,15 +31,17 @@ def format_content(messages):
29
31
 
30
32
  if isinstance(content, list):
31
33
  content_str = ", ".join(
32
- f'{item["type"]}: {item["text"] if "text" in item else item.get("image_url", "")}'
33
- if "type" in item else f'text: {item.get("text", "")}'
34
+ f"{item['type']}: {item['text'] if 'text' in item else item.get('image_url', '')}"
35
+ if "type" in item
36
+ else f"text: {item.get('text', '')}"
34
37
  for item in content
35
38
  )
36
- formatted_messages.append(f'{role}: {content_str}')
39
+ formatted_messages.append(f"{role}: {content_str}")
37
40
  else:
38
- formatted_messages.append(f'{role}: {content}')
41
+ formatted_messages.append(f"{role}: {content}")
42
+
43
+ return "\n".join(formatted_messages)
39
44
 
40
- return '\n'.join(formatted_messages)
41
45
 
42
46
  def process_chunk(scope, chunk):
43
47
  """
@@ -92,25 +96,44 @@ def process_chunk(scope, chunk):
92
96
  func = tool.get("function", {})
93
97
  scope._tools[idx] = {
94
98
  "id": tool["id"],
95
- "function": {"name": func.get("name", ""), "arguments": func.get("arguments", "")},
96
- "type": tool.get("type", "function")
99
+ "function": {
100
+ "name": func.get("name", ""),
101
+ "arguments": func.get("arguments", ""),
102
+ },
103
+ "type": tool.get("type", "function"),
97
104
  }
98
- elif scope._tools[idx] and "function" in tool: # Append args (id is None)
99
- scope._tools[idx]["function"]["arguments"] += tool["function"].get("arguments", "")
105
+ elif (
106
+ scope._tools[idx] and "function" in tool
107
+ ): # Append args (id is None)
108
+ scope._tools[idx]["function"]["arguments"] += tool["function"].get(
109
+ "arguments", ""
110
+ )
100
111
 
101
112
  # Handle usage information (typically only in final chunk)
102
113
  if chunked.get("usage"):
103
114
  scope._input_tokens = chunked.get("usage").get("prompt_tokens", 0)
104
115
  scope._output_tokens = chunked.get("usage").get("completion_tokens", 0)
105
116
  # Handle reasoning tokens if present (optional) - check nested structure
106
- completion_details = chunked.get("usage", {}).get("completion_tokens_details", {})
117
+ completion_details = chunked.get("usage", {}).get(
118
+ "completion_tokens_details", {}
119
+ )
107
120
  if "reasoning_tokens" in completion_details:
108
121
  scope._reasoning_tokens = completion_details.get("reasoning_tokens", 0)
109
122
  elif "reasoning_tokens" in chunked.get("usage", {}):
110
123
  scope._reasoning_tokens = chunked.get("usage").get("reasoning_tokens", 0)
111
124
 
112
- def common_chat_logic(scope, pricing_info, environment, application_name, metrics,
113
- capture_message_content, disable_metrics, version, is_stream):
125
+
126
+ def common_chat_logic(
127
+ scope,
128
+ pricing_info,
129
+ environment,
130
+ application_name,
131
+ metrics,
132
+ capture_message_content,
133
+ disable_metrics,
134
+ version,
135
+ is_stream,
136
+ ):
114
137
  """
115
138
  Process chat request and generate Telemetry
116
139
  """
@@ -121,65 +144,134 @@ def common_chat_logic(scope, pricing_info, environment, application_name, metric
121
144
  prompt = format_content(scope._kwargs.get("messages", []))
122
145
  request_model = scope._kwargs.get("model", "gpt-4o")
123
146
 
124
- cost = get_chat_model_cost(request_model, pricing_info, scope._input_tokens, scope._output_tokens)
147
+ cost = get_chat_model_cost(
148
+ request_model, pricing_info, scope._input_tokens, scope._output_tokens
149
+ )
125
150
 
126
151
  # Common Span Attributes
127
- common_span_attributes(scope,
128
- SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT, SemanticConvention.GEN_AI_SYSTEM_AZURE_AI_INFERENCE,
129
- scope._server_address, scope._server_port, request_model, scope._response_model,
130
- environment, application_name, is_stream, scope._tbt, scope._ttft, version)
152
+ common_span_attributes(
153
+ scope,
154
+ SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
155
+ SemanticConvention.GEN_AI_SYSTEM_AZURE_AI_INFERENCE,
156
+ scope._server_address,
157
+ scope._server_port,
158
+ request_model,
159
+ scope._response_model,
160
+ environment,
161
+ application_name,
162
+ is_stream,
163
+ scope._tbt,
164
+ scope._ttft,
165
+ version,
166
+ )
131
167
 
132
168
  # Span Attributes for Request parameters
133
- scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_FREQUENCY_PENALTY, scope._kwargs.get("frequency_penalty", 0.0))
134
- scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_MAX_TOKENS, scope._kwargs.get("max_tokens", -1))
135
- scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_PRESENCE_PENALTY, scope._kwargs.get("presence_penalty", 0.0))
136
- scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_STOP_SEQUENCES, scope._kwargs.get("stop", []))
137
- scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_TEMPERATURE, scope._kwargs.get("temperature", 1.0))
138
- scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_TOP_P, scope._kwargs.get("top_p", 1.0))
169
+ scope._span.set_attribute(
170
+ SemanticConvention.GEN_AI_REQUEST_FREQUENCY_PENALTY,
171
+ scope._kwargs.get("frequency_penalty", 0.0),
172
+ )
173
+ scope._span.set_attribute(
174
+ SemanticConvention.GEN_AI_REQUEST_MAX_TOKENS,
175
+ scope._kwargs.get("max_tokens", -1),
176
+ )
177
+ scope._span.set_attribute(
178
+ SemanticConvention.GEN_AI_REQUEST_PRESENCE_PENALTY,
179
+ scope._kwargs.get("presence_penalty", 0.0),
180
+ )
181
+ scope._span.set_attribute(
182
+ SemanticConvention.GEN_AI_REQUEST_STOP_SEQUENCES, scope._kwargs.get("stop", [])
183
+ )
184
+ scope._span.set_attribute(
185
+ SemanticConvention.GEN_AI_REQUEST_TEMPERATURE,
186
+ scope._kwargs.get("temperature", 1.0),
187
+ )
188
+ scope._span.set_attribute(
189
+ SemanticConvention.GEN_AI_REQUEST_TOP_P, scope._kwargs.get("top_p", 1.0)
190
+ )
139
191
 
140
192
  # Span Attributes for Response parameters
141
193
  scope._span.set_attribute(SemanticConvention.GEN_AI_RESPONSE_ID, scope._response_id)
142
- scope._span.set_attribute(SemanticConvention.GEN_AI_RESPONSE_FINISH_REASON, [scope._finish_reason])
143
- scope._span.set_attribute(SemanticConvention.GEN_AI_RESPONSE_SERVICE_TIER, scope._response_service_tier)
144
- scope._span.set_attribute(SemanticConvention.GEN_AI_RESPONSE_SYSTEM_FINGERPRINT, scope._response_service_tier)
145
- scope._span.set_attribute(SemanticConvention.GEN_AI_OUTPUT_TYPE, "text" if isinstance(scope._llmresponse, str) else "json")
194
+ scope._span.set_attribute(
195
+ SemanticConvention.GEN_AI_RESPONSE_FINISH_REASON, [scope._finish_reason]
196
+ )
197
+ scope._span.set_attribute(
198
+ SemanticConvention.GEN_AI_RESPONSE_SERVICE_TIER, scope._response_service_tier
199
+ )
200
+ scope._span.set_attribute(
201
+ SemanticConvention.GEN_AI_RESPONSE_SYSTEM_FINGERPRINT,
202
+ scope._response_service_tier,
203
+ )
204
+ scope._span.set_attribute(
205
+ SemanticConvention.GEN_AI_OUTPUT_TYPE,
206
+ "text" if isinstance(scope._llmresponse, str) else "json",
207
+ )
146
208
 
147
209
  # Span Attributes for Cost and Tokens
148
- scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS, scope._input_tokens)
149
- scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_OUTPUT_TOKENS, scope._output_tokens)
150
- scope._span.set_attribute(SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE, scope._input_tokens + scope._output_tokens)
210
+ scope._span.set_attribute(
211
+ SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS, scope._input_tokens
212
+ )
213
+ scope._span.set_attribute(
214
+ SemanticConvention.GEN_AI_USAGE_OUTPUT_TOKENS, scope._output_tokens
215
+ )
216
+ scope._span.set_attribute(
217
+ SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE,
218
+ scope._input_tokens + scope._output_tokens,
219
+ )
151
220
  scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_COST, cost)
152
221
 
153
222
  # Span Attributes for Reasoning (if present)
154
223
  if hasattr(scope, "_reasoning_tokens") and scope._reasoning_tokens > 0:
155
- scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_REASONING_TOKENS, scope._reasoning_tokens)
224
+ scope._span.set_attribute(
225
+ SemanticConvention.GEN_AI_USAGE_REASONING_TOKENS, scope._reasoning_tokens
226
+ )
156
227
  # Update total token usage to include reasoning tokens
157
- scope._span.set_attribute(SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE,
158
- scope._input_tokens + scope._output_tokens + scope._reasoning_tokens)
228
+ scope._span.set_attribute(
229
+ SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE,
230
+ scope._input_tokens + scope._output_tokens + scope._reasoning_tokens,
231
+ )
159
232
 
160
233
  # Span Attributes for Tools - optimized
161
234
  if scope._tools:
162
235
  tools = scope._tools if isinstance(scope._tools, list) else [scope._tools]
163
236
 
164
- names, ids, args = zip(*[
165
- (t.get("function", {}).get("name", ""),
166
- str(t.get("id", "")),
167
- str(t.get("function", {}).get("arguments", "")))
168
- for t in tools if isinstance(t, dict) and t
169
- ]) if tools else ([], [], [])
237
+ names, ids, args = (
238
+ zip(
239
+ *[
240
+ (
241
+ t.get("function", {}).get("name", ""),
242
+ str(t.get("id", "")),
243
+ str(t.get("function", {}).get("arguments", "")),
244
+ )
245
+ for t in tools
246
+ if isinstance(t, dict) and t
247
+ ]
248
+ )
249
+ if tools
250
+ else ([], [], [])
251
+ )
170
252
 
171
- scope._span.set_attribute(SemanticConvention.GEN_AI_TOOL_NAME, ", ".join(filter(None, names)))
172
- scope._span.set_attribute(SemanticConvention.GEN_AI_TOOL_CALL_ID, ", ".join(filter(None, ids)))
173
- scope._span.set_attribute(SemanticConvention.GEN_AI_TOOL_ARGS, ", ".join(filter(None, args)))
253
+ scope._span.set_attribute(
254
+ SemanticConvention.GEN_AI_TOOL_NAME, ", ".join(filter(None, names))
255
+ )
256
+ scope._span.set_attribute(
257
+ SemanticConvention.GEN_AI_TOOL_CALL_ID, ", ".join(filter(None, ids))
258
+ )
259
+ scope._span.set_attribute(
260
+ SemanticConvention.GEN_AI_TOOL_ARGS, ", ".join(filter(None, args))
261
+ )
174
262
 
175
263
  # Span Attributes for Content
176
264
  if capture_message_content:
177
265
  scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, prompt)
178
- scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_COMPLETION, scope._llmresponse)
266
+ scope._span.set_attribute(
267
+ SemanticConvention.GEN_AI_CONTENT_COMPLETION, scope._llmresponse
268
+ )
179
269
 
180
270
  # Add reasoning content if available
181
271
  if hasattr(scope, "_reasoning_content") and scope._reasoning_content:
182
- scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_REASONING, scope._reasoning_content)
272
+ scope._span.set_attribute(
273
+ SemanticConvention.GEN_AI_CONTENT_REASONING, scope._reasoning_content
274
+ )
183
275
 
184
276
  # To be removed once the change to span_attributes (from span events) is complete
185
277
  scope._span.add_event(
@@ -199,23 +291,69 @@ def common_chat_logic(scope, pricing_info, environment, application_name, metric
199
291
 
200
292
  # Metrics
201
293
  if not disable_metrics:
202
- record_completion_metrics(metrics, SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
203
- SemanticConvention.GEN_AI_SYSTEM_AZURE_AI_INFERENCE, scope._server_address, scope._server_port,
204
- request_model, scope._response_model, environment, application_name, scope._start_time, scope._end_time,
205
- scope._input_tokens, scope._output_tokens, cost, scope._tbt, scope._ttft)
294
+ record_completion_metrics(
295
+ metrics,
296
+ SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
297
+ SemanticConvention.GEN_AI_SYSTEM_AZURE_AI_INFERENCE,
298
+ scope._server_address,
299
+ scope._server_port,
300
+ request_model,
301
+ scope._response_model,
302
+ environment,
303
+ application_name,
304
+ scope._start_time,
305
+ scope._end_time,
306
+ scope._input_tokens,
307
+ scope._output_tokens,
308
+ cost,
309
+ scope._tbt,
310
+ scope._ttft,
311
+ )
312
+
206
313
 
207
- def process_streaming_chat_response(scope, pricing_info, environment, application_name, metrics,
208
- capture_message_content=False, disable_metrics=False, version=""):
314
+ def process_streaming_chat_response(
315
+ scope,
316
+ pricing_info,
317
+ environment,
318
+ application_name,
319
+ metrics,
320
+ capture_message_content=False,
321
+ disable_metrics=False,
322
+ version="",
323
+ ):
209
324
  """
210
325
  Process streaming chat request and generate Telemetry
211
326
  """
212
327
 
213
- common_chat_logic(scope, pricing_info, environment, application_name, metrics,
214
- capture_message_content, disable_metrics, version, is_stream=True)
328
+ common_chat_logic(
329
+ scope,
330
+ pricing_info,
331
+ environment,
332
+ application_name,
333
+ metrics,
334
+ capture_message_content,
335
+ disable_metrics,
336
+ version,
337
+ is_stream=True,
338
+ )
339
+
215
340
 
216
- def process_chat_response(response, request_model, pricing_info, server_port, server_address,
217
- environment, application_name, metrics, start_time, span, capture_message_content=False,
218
- disable_metrics=False, version="1.0.0", **kwargs):
341
+ def process_chat_response(
342
+ response,
343
+ request_model,
344
+ pricing_info,
345
+ server_port,
346
+ server_address,
347
+ environment,
348
+ application_name,
349
+ metrics,
350
+ start_time,
351
+ span,
352
+ capture_message_content=False,
353
+ disable_metrics=False,
354
+ version="1.0.0",
355
+ **kwargs,
356
+ ):
219
357
  """
220
358
  Process chat request and generate Telemetry
221
359
  """
@@ -232,14 +370,20 @@ def process_chat_response(response, request_model, pricing_info, server_port, se
232
370
  for choice in response_dict.get("choices", [])
233
371
  )
234
372
  # Handle reasoning content from non-streaming response
235
- reasoning_content = response_dict.get("choices", [{}])[0].get("message", {}).get("reasoning_content")
373
+ reasoning_content = (
374
+ response_dict.get("choices", [{}])[0]
375
+ .get("message", {})
376
+ .get("reasoning_content")
377
+ )
236
378
  if reasoning_content:
237
379
  scope._reasoning_content = reasoning_content
238
380
 
239
381
  scope._input_tokens = response_dict.get("usage", {}).get("prompt_tokens", 0)
240
382
  scope._output_tokens = response_dict.get("usage", {}).get("completion_tokens", 0)
241
383
  # Handle reasoning tokens if present (optional) - check nested structure
242
- completion_details = response_dict.get("usage", {}).get("completion_tokens_details", {})
384
+ completion_details = response_dict.get("usage", {}).get(
385
+ "completion_tokens_details", {}
386
+ )
243
387
  if "reasoning_tokens" in completion_details:
244
388
  scope._reasoning_tokens = completion_details.get("reasoning_tokens", 0)
245
389
  elif "reasoning_tokens" in response_dict.get("usage", {}):
@@ -248,7 +392,9 @@ def process_chat_response(response, request_model, pricing_info, server_port, se
248
392
  scope._reasoning_tokens = 0
249
393
  scope._response_id = response_dict.get("id")
250
394
  scope._response_model = response_dict.get("model")
251
- scope._finish_reason = str(response_dict.get("choices", [])[0].get("finish_reason", ""))
395
+ scope._finish_reason = str(
396
+ response_dict.get("choices", [])[0].get("finish_reason", "")
397
+ )
252
398
  scope._response_service_tier = str(response_dict.get("system_fingerprint", ""))
253
399
  scope._timestamps = []
254
400
  scope._ttft, scope._tbt = scope._end_time - scope._start_time, 0
@@ -257,17 +403,37 @@ def process_chat_response(response, request_model, pricing_info, server_port, se
257
403
 
258
404
  # Handle tool calls
259
405
  if scope._kwargs.get("tools"):
260
- scope._tools = response_dict.get("choices", [{}])[0].get("message", {}).get("tool_calls")
406
+ scope._tools = (
407
+ response_dict.get("choices", [{}])[0].get("message", {}).get("tool_calls")
408
+ )
261
409
  else:
262
410
  scope._tools = None
263
411
 
264
- common_chat_logic(scope, pricing_info, environment, application_name, metrics,
265
- capture_message_content, disable_metrics, version, is_stream=False)
412
+ common_chat_logic(
413
+ scope,
414
+ pricing_info,
415
+ environment,
416
+ application_name,
417
+ metrics,
418
+ capture_message_content,
419
+ disable_metrics,
420
+ version,
421
+ is_stream=False,
422
+ )
266
423
 
267
424
  return response
268
425
 
269
- def common_embedding_logic(scope, pricing_info, environment, application_name, metrics,
270
- capture_message_content, disable_metrics, version):
426
+
427
+ def common_embedding_logic(
428
+ scope,
429
+ pricing_info,
430
+ environment,
431
+ application_name,
432
+ metrics,
433
+ capture_message_content,
434
+ disable_metrics,
435
+ version,
436
+ ):
271
437
  """
272
438
  Process embedding request and generate Telemetry
273
439
  """
@@ -277,29 +443,54 @@ def common_embedding_logic(scope, pricing_info, environment, application_name, m
277
443
  cost = get_embed_model_cost(request_model, pricing_info, scope._input_tokens)
278
444
 
279
445
  # Common Span Attributes
280
- common_span_attributes(scope,
281
- SemanticConvention.GEN_AI_OPERATION_TYPE_EMBEDDING, SemanticConvention.GEN_AI_SYSTEM_AZURE_AI_INFERENCE,
282
- scope._server_address, scope._server_port, request_model, scope._response_model,
283
- environment, application_name, False, 0, scope._end_time - scope._start_time, version)
446
+ common_span_attributes(
447
+ scope,
448
+ SemanticConvention.GEN_AI_OPERATION_TYPE_EMBEDDING,
449
+ SemanticConvention.GEN_AI_SYSTEM_AZURE_AI_INFERENCE,
450
+ scope._server_address,
451
+ scope._server_port,
452
+ request_model,
453
+ scope._response_model,
454
+ environment,
455
+ application_name,
456
+ False,
457
+ 0,
458
+ scope._end_time - scope._start_time,
459
+ version,
460
+ )
284
461
 
285
462
  # Span Attributes for Request parameters
286
- scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_ENCODING_FORMATS, [scope._kwargs.get("encoding_format", "float")])
287
- scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_USER, scope._kwargs.get("user", ""))
463
+ scope._span.set_attribute(
464
+ SemanticConvention.GEN_AI_REQUEST_ENCODING_FORMATS,
465
+ [scope._kwargs.get("encoding_format", "float")],
466
+ )
467
+ scope._span.set_attribute(
468
+ SemanticConvention.GEN_AI_REQUEST_USER, scope._kwargs.get("user", "")
469
+ )
288
470
 
289
471
  # Span Attributes for Cost and Tokens
290
- scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS, scope._input_tokens)
291
- scope._span.set_attribute(SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE, scope._input_tokens)
472
+ scope._span.set_attribute(
473
+ SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS, scope._input_tokens
474
+ )
475
+ scope._span.set_attribute(
476
+ SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE, scope._input_tokens
477
+ )
292
478
  scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_COST, cost)
293
479
 
294
480
  # Span Attributes for Content
295
481
  if capture_message_content:
296
- scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, str(scope._kwargs.get("input", "")))
482
+ scope._span.set_attribute(
483
+ SemanticConvention.GEN_AI_CONTENT_PROMPT,
484
+ str(scope._kwargs.get("input", "")),
485
+ )
297
486
 
298
487
  # To be removed once the change to span_attributes (from span events) is complete
299
488
  scope._span.add_event(
300
489
  name=SemanticConvention.GEN_AI_CONTENT_PROMPT_EVENT,
301
490
  attributes={
302
- SemanticConvention.GEN_AI_CONTENT_PROMPT: str(scope._kwargs.get("input", "")),
491
+ SemanticConvention.GEN_AI_CONTENT_PROMPT: str(
492
+ scope._kwargs.get("input", "")
493
+ ),
303
494
  },
304
495
  )
305
496
 
@@ -307,14 +498,39 @@ def common_embedding_logic(scope, pricing_info, environment, application_name, m
307
498
 
308
499
  # Metrics
309
500
  if not disable_metrics:
310
- record_embedding_metrics(metrics, SemanticConvention.GEN_AI_OPERATION_TYPE_EMBEDDING,
311
- SemanticConvention.GEN_AI_SYSTEM_AZURE_AI_INFERENCE, scope._server_address, scope._server_port,
312
- request_model, scope._response_model, environment, application_name, scope._start_time, scope._end_time,
313
- scope._input_tokens, cost)
314
-
315
- def process_embedding_response(response, request_model, pricing_info, server_port, server_address,
316
- environment, application_name, metrics, start_time, span, capture_message_content=False,
317
- disable_metrics=False, version="1.0.0", **kwargs):
501
+ record_embedding_metrics(
502
+ metrics,
503
+ SemanticConvention.GEN_AI_OPERATION_TYPE_EMBEDDING,
504
+ SemanticConvention.GEN_AI_SYSTEM_AZURE_AI_INFERENCE,
505
+ scope._server_address,
506
+ scope._server_port,
507
+ request_model,
508
+ scope._response_model,
509
+ environment,
510
+ application_name,
511
+ scope._start_time,
512
+ scope._end_time,
513
+ scope._input_tokens,
514
+ cost,
515
+ )
516
+
517
+
518
+ def process_embedding_response(
519
+ response,
520
+ request_model,
521
+ pricing_info,
522
+ server_port,
523
+ server_address,
524
+ environment,
525
+ application_name,
526
+ metrics,
527
+ start_time,
528
+ span,
529
+ capture_message_content=False,
530
+ disable_metrics=False,
531
+ version="1.0.0",
532
+ **kwargs,
533
+ ):
318
534
  """
319
535
  Process embedding request and generate Telemetry
320
536
  """
@@ -331,7 +547,15 @@ def process_embedding_response(response, request_model, pricing_info, server_por
331
547
  scope._server_address, scope._server_port = server_address, server_port
332
548
  scope._kwargs = kwargs
333
549
 
334
- common_embedding_logic(scope, pricing_info, environment, application_name, metrics,
335
- capture_message_content, disable_metrics, version)
550
+ common_embedding_logic(
551
+ scope,
552
+ pricing_info,
553
+ environment,
554
+ application_name,
555
+ metrics,
556
+ capture_message_content,
557
+ disable_metrics,
558
+ version,
559
+ )
336
560
 
337
561
  return response
@@ -9,6 +9,7 @@ from openlit.instrumentation.bedrock.bedrock import converse, converse_stream
9
9
 
10
10
  _instruments = ("boto3 >= 1.34.138",)
11
11
 
12
+
12
13
  class BedrockInstrumentor(BaseInstrumentor):
13
14
  """
14
15
  An instrumentor for AWS Bedrock client library.
@@ -31,16 +32,32 @@ class BedrockInstrumentor(BaseInstrumentor):
31
32
  wrap_function_wrapper(
32
33
  "botocore.client",
33
34
  "ClientCreator.create_client",
34
- converse(version, environment, application_name, tracer, pricing_info,
35
- capture_message_content, metrics, disable_metrics),
35
+ converse(
36
+ version,
37
+ environment,
38
+ application_name,
39
+ tracer,
40
+ pricing_info,
41
+ capture_message_content,
42
+ metrics,
43
+ disable_metrics,
44
+ ),
36
45
  )
37
46
 
38
47
  # streaming
39
48
  wrap_function_wrapper(
40
49
  "botocore.client",
41
50
  "ClientCreator.create_client",
42
- converse_stream(version, environment, application_name, tracer, pricing_info,
43
- capture_message_content, metrics, disable_metrics),
51
+ converse_stream(
52
+ version,
53
+ environment,
54
+ application_name,
55
+ tracer,
56
+ pricing_info,
57
+ capture_message_content,
58
+ metrics,
59
+ disable_metrics,
60
+ ),
44
61
  )
45
62
 
46
63
  def _uninstrument(self, **kwargs):