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.
- openlit/__helpers.py +235 -86
- openlit/__init__.py +16 -13
- openlit/_instrumentors.py +2 -1
- openlit/evals/all.py +50 -21
- openlit/evals/bias_detection.py +47 -20
- openlit/evals/hallucination.py +53 -22
- openlit/evals/toxicity.py +50 -21
- openlit/evals/utils.py +54 -30
- openlit/guard/all.py +61 -19
- openlit/guard/prompt_injection.py +34 -14
- openlit/guard/restrict_topic.py +46 -15
- openlit/guard/sensitive_topic.py +34 -14
- openlit/guard/utils.py +58 -22
- openlit/instrumentation/ag2/__init__.py +24 -8
- openlit/instrumentation/ag2/ag2.py +34 -13
- openlit/instrumentation/ag2/async_ag2.py +34 -13
- openlit/instrumentation/ag2/utils.py +133 -30
- openlit/instrumentation/ai21/__init__.py +43 -14
- openlit/instrumentation/ai21/ai21.py +47 -21
- openlit/instrumentation/ai21/async_ai21.py +47 -21
- openlit/instrumentation/ai21/utils.py +299 -78
- openlit/instrumentation/anthropic/__init__.py +21 -4
- openlit/instrumentation/anthropic/anthropic.py +28 -17
- openlit/instrumentation/anthropic/async_anthropic.py +28 -17
- openlit/instrumentation/anthropic/utils.py +145 -35
- openlit/instrumentation/assemblyai/__init__.py +11 -2
- openlit/instrumentation/assemblyai/assemblyai.py +15 -4
- openlit/instrumentation/assemblyai/utils.py +120 -25
- openlit/instrumentation/astra/__init__.py +43 -10
- openlit/instrumentation/astra/astra.py +28 -5
- openlit/instrumentation/astra/async_astra.py +28 -5
- openlit/instrumentation/astra/utils.py +151 -55
- openlit/instrumentation/azure_ai_inference/__init__.py +43 -10
- openlit/instrumentation/azure_ai_inference/async_azure_ai_inference.py +53 -21
- openlit/instrumentation/azure_ai_inference/azure_ai_inference.py +53 -21
- openlit/instrumentation/azure_ai_inference/utils.py +307 -83
- openlit/instrumentation/bedrock/__init__.py +21 -4
- openlit/instrumentation/bedrock/bedrock.py +63 -25
- openlit/instrumentation/bedrock/utils.py +139 -30
- openlit/instrumentation/chroma/__init__.py +89 -16
- openlit/instrumentation/chroma/chroma.py +28 -6
- openlit/instrumentation/chroma/utils.py +167 -51
- openlit/instrumentation/cohere/__init__.py +63 -18
- openlit/instrumentation/cohere/async_cohere.py +63 -24
- openlit/instrumentation/cohere/cohere.py +63 -24
- openlit/instrumentation/cohere/utils.py +286 -73
- openlit/instrumentation/controlflow/__init__.py +35 -9
- openlit/instrumentation/controlflow/controlflow.py +66 -33
- openlit/instrumentation/crawl4ai/__init__.py +25 -10
- openlit/instrumentation/crawl4ai/async_crawl4ai.py +78 -31
- openlit/instrumentation/crawl4ai/crawl4ai.py +78 -31
- openlit/instrumentation/crewai/__init__.py +40 -15
- openlit/instrumentation/crewai/async_crewai.py +32 -7
- openlit/instrumentation/crewai/crewai.py +32 -7
- openlit/instrumentation/crewai/utils.py +159 -56
- openlit/instrumentation/dynamiq/__init__.py +46 -12
- openlit/instrumentation/dynamiq/dynamiq.py +74 -33
- openlit/instrumentation/elevenlabs/__init__.py +23 -4
- openlit/instrumentation/elevenlabs/async_elevenlabs.py +16 -4
- openlit/instrumentation/elevenlabs/elevenlabs.py +16 -4
- openlit/instrumentation/elevenlabs/utils.py +128 -25
- openlit/instrumentation/embedchain/__init__.py +11 -2
- openlit/instrumentation/embedchain/embedchain.py +68 -35
- openlit/instrumentation/firecrawl/__init__.py +24 -7
- openlit/instrumentation/firecrawl/firecrawl.py +46 -20
- openlit/instrumentation/google_ai_studio/__init__.py +45 -10
- openlit/instrumentation/google_ai_studio/async_google_ai_studio.py +67 -44
- openlit/instrumentation/google_ai_studio/google_ai_studio.py +67 -44
- openlit/instrumentation/google_ai_studio/utils.py +180 -67
- openlit/instrumentation/gpt4all/__init__.py +22 -7
- openlit/instrumentation/gpt4all/gpt4all.py +67 -29
- openlit/instrumentation/gpt4all/utils.py +285 -61
- openlit/instrumentation/gpu/__init__.py +128 -47
- openlit/instrumentation/groq/__init__.py +21 -4
- openlit/instrumentation/groq/async_groq.py +33 -21
- openlit/instrumentation/groq/groq.py +33 -21
- openlit/instrumentation/groq/utils.py +192 -55
- openlit/instrumentation/haystack/__init__.py +70 -24
- openlit/instrumentation/haystack/async_haystack.py +28 -6
- openlit/instrumentation/haystack/haystack.py +28 -6
- openlit/instrumentation/haystack/utils.py +196 -74
- openlit/instrumentation/julep/__init__.py +69 -19
- openlit/instrumentation/julep/async_julep.py +53 -27
- openlit/instrumentation/julep/julep.py +53 -28
- openlit/instrumentation/langchain/__init__.py +74 -63
- openlit/instrumentation/langchain/callback_handler.py +1100 -0
- openlit/instrumentation/langchain_community/__init__.py +13 -2
- openlit/instrumentation/langchain_community/async_langchain_community.py +23 -5
- openlit/instrumentation/langchain_community/langchain_community.py +23 -5
- openlit/instrumentation/langchain_community/utils.py +35 -9
- openlit/instrumentation/letta/__init__.py +68 -15
- openlit/instrumentation/letta/letta.py +99 -54
- openlit/instrumentation/litellm/__init__.py +43 -14
- openlit/instrumentation/litellm/async_litellm.py +51 -26
- openlit/instrumentation/litellm/litellm.py +51 -26
- openlit/instrumentation/litellm/utils.py +304 -102
- openlit/instrumentation/llamaindex/__init__.py +267 -90
- openlit/instrumentation/llamaindex/async_llamaindex.py +28 -6
- openlit/instrumentation/llamaindex/llamaindex.py +28 -6
- openlit/instrumentation/llamaindex/utils.py +204 -91
- openlit/instrumentation/mem0/__init__.py +11 -2
- openlit/instrumentation/mem0/mem0.py +50 -29
- openlit/instrumentation/milvus/__init__.py +10 -2
- openlit/instrumentation/milvus/milvus.py +31 -6
- openlit/instrumentation/milvus/utils.py +166 -67
- openlit/instrumentation/mistral/__init__.py +63 -18
- openlit/instrumentation/mistral/async_mistral.py +63 -24
- openlit/instrumentation/mistral/mistral.py +63 -24
- openlit/instrumentation/mistral/utils.py +277 -69
- openlit/instrumentation/multion/__init__.py +69 -19
- openlit/instrumentation/multion/async_multion.py +57 -26
- openlit/instrumentation/multion/multion.py +57 -26
- openlit/instrumentation/ollama/__init__.py +39 -18
- openlit/instrumentation/ollama/async_ollama.py +57 -26
- openlit/instrumentation/ollama/ollama.py +57 -26
- openlit/instrumentation/ollama/utils.py +226 -50
- openlit/instrumentation/openai/__init__.py +156 -32
- openlit/instrumentation/openai/async_openai.py +147 -67
- openlit/instrumentation/openai/openai.py +150 -67
- openlit/instrumentation/openai/utils.py +657 -185
- openlit/instrumentation/openai_agents/__init__.py +5 -1
- openlit/instrumentation/openai_agents/processor.py +110 -90
- openlit/instrumentation/phidata/__init__.py +13 -5
- openlit/instrumentation/phidata/phidata.py +67 -32
- openlit/instrumentation/pinecone/__init__.py +48 -9
- openlit/instrumentation/pinecone/async_pinecone.py +27 -5
- openlit/instrumentation/pinecone/pinecone.py +27 -5
- openlit/instrumentation/pinecone/utils.py +153 -47
- openlit/instrumentation/premai/__init__.py +22 -7
- openlit/instrumentation/premai/premai.py +51 -26
- openlit/instrumentation/premai/utils.py +246 -59
- openlit/instrumentation/pydantic_ai/__init__.py +49 -22
- openlit/instrumentation/pydantic_ai/pydantic_ai.py +69 -16
- openlit/instrumentation/pydantic_ai/utils.py +89 -24
- openlit/instrumentation/qdrant/__init__.py +19 -4
- openlit/instrumentation/qdrant/async_qdrant.py +33 -7
- openlit/instrumentation/qdrant/qdrant.py +33 -7
- openlit/instrumentation/qdrant/utils.py +228 -93
- openlit/instrumentation/reka/__init__.py +23 -10
- openlit/instrumentation/reka/async_reka.py +17 -11
- openlit/instrumentation/reka/reka.py +17 -11
- openlit/instrumentation/reka/utils.py +138 -36
- openlit/instrumentation/together/__init__.py +44 -12
- openlit/instrumentation/together/async_together.py +50 -27
- openlit/instrumentation/together/together.py +50 -27
- openlit/instrumentation/together/utils.py +301 -71
- openlit/instrumentation/transformers/__init__.py +2 -1
- openlit/instrumentation/transformers/transformers.py +13 -3
- openlit/instrumentation/transformers/utils.py +139 -36
- openlit/instrumentation/vertexai/__init__.py +81 -16
- openlit/instrumentation/vertexai/async_vertexai.py +33 -15
- openlit/instrumentation/vertexai/utils.py +123 -27
- openlit/instrumentation/vertexai/vertexai.py +33 -15
- openlit/instrumentation/vllm/__init__.py +12 -5
- openlit/instrumentation/vllm/utils.py +121 -31
- openlit/instrumentation/vllm/vllm.py +16 -10
- openlit/otel/events.py +35 -10
- openlit/otel/metrics.py +32 -24
- openlit/otel/tracing.py +24 -9
- openlit/semcov/__init__.py +72 -6
- {openlit-1.34.30.dist-info → openlit-1.34.31.dist-info}/METADATA +2 -1
- openlit-1.34.31.dist-info/RECORD +166 -0
- openlit/instrumentation/langchain/async_langchain.py +0 -102
- openlit/instrumentation/langchain/langchain.py +0 -102
- openlit/instrumentation/langchain/utils.py +0 -252
- openlit-1.34.30.dist-info/RECORD +0 -168
- {openlit-1.34.30.dist-info → openlit-1.34.31.dist-info}/LICENSE +0 -0
- {openlit-1.34.30.dist-info → openlit-1.34.31.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
|
33
|
-
if "type" in item
|
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
|
39
|
+
formatted_messages.append(f"{role}: {content_str}")
|
37
40
|
else:
|
38
|
-
formatted_messages.append(f
|
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": {
|
96
|
-
|
99
|
+
"function": {
|
100
|
+
"name": func.get("name", ""),
|
101
|
+
"arguments": func.get("arguments", ""),
|
102
|
+
},
|
103
|
+
"type": tool.get("type", "function"),
|
97
104
|
}
|
98
|
-
elif
|
99
|
-
scope._tools[idx]
|
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(
|
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
|
-
|
113
|
-
|
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(
|
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(
|
128
|
-
|
129
|
-
|
130
|
-
|
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(
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
scope._span.set_attribute(
|
138
|
-
|
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(
|
143
|
-
|
144
|
-
|
145
|
-
scope._span.set_attribute(
|
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(
|
149
|
-
|
150
|
-
|
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(
|
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(
|
158
|
-
|
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 =
|
165
|
-
(
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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(
|
172
|
-
|
173
|
-
|
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(
|
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(
|
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(
|
203
|
-
|
204
|
-
|
205
|
-
|
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(
|
208
|
-
|
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(
|
214
|
-
|
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(
|
217
|
-
|
218
|
-
|
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 =
|
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(
|
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(
|
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 =
|
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(
|
265
|
-
|
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
|
-
|
270
|
-
|
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(
|
281
|
-
|
282
|
-
|
283
|
-
|
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(
|
287
|
-
|
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(
|
291
|
-
|
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(
|
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(
|
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(
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
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(
|
335
|
-
|
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(
|
35
|
-
|
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(
|
43
|
-
|
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):
|