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
|
OpenAI OpenTelemetry instrumentation utility functions
|
3
3
|
"""
|
4
|
+
|
4
5
|
import time
|
5
6
|
|
6
7
|
from opentelemetry.trace import Status, StatusCode
|
@@ -22,16 +23,18 @@ from openlit.__helpers import (
|
|
22
23
|
)
|
23
24
|
from openlit.semcov import SemanticConvention
|
24
25
|
|
26
|
+
|
25
27
|
def handle_not_given(value, default=None):
|
26
28
|
"""
|
27
29
|
Handle OpenAI's NotGiven values and None values by converting them to appropriate defaults.
|
28
30
|
"""
|
29
|
-
if hasattr(value,
|
31
|
+
if hasattr(value, "__class__") and value.__class__.__name__ == "NotGiven":
|
30
32
|
return default
|
31
33
|
if value is None:
|
32
34
|
return default
|
33
35
|
return value
|
34
36
|
|
37
|
+
|
35
38
|
def format_content(messages):
|
36
39
|
"""
|
37
40
|
Format the messages into a string for span events.
|
@@ -62,18 +65,19 @@ def format_content(messages):
|
|
62
65
|
for item in content:
|
63
66
|
# Chat completions format
|
64
67
|
if item.get("type") == "text":
|
65
|
-
content_str_list.append(f
|
66
|
-
elif
|
67
|
-
|
68
|
-
|
68
|
+
content_str_list.append(f"text: {item.get('text', '')}")
|
69
|
+
elif item.get("type") == "image_url" and not item.get(
|
70
|
+
"image_url", {}
|
71
|
+
).get("url", "").startswith("data:"):
|
72
|
+
content_str_list.append(f"image_url: {item['image_url']['url']}")
|
69
73
|
|
70
74
|
# Responses API format
|
71
75
|
elif item.get("type") == "input_text":
|
72
|
-
content_str_list.append(f
|
76
|
+
content_str_list.append(f"text: {item.get('text', '')}")
|
73
77
|
elif item.get("type") == "input_image":
|
74
78
|
image_url = item.get("image_url", "")
|
75
79
|
if image_url and not image_url.startswith("data:"):
|
76
|
-
content_str_list.append(f
|
80
|
+
content_str_list.append(f"image_url: {image_url}")
|
77
81
|
|
78
82
|
content_str = ", ".join(content_str_list)
|
79
83
|
formatted_messages.append(f"{role}: {content_str}")
|
@@ -82,6 +86,7 @@ def format_content(messages):
|
|
82
86
|
|
83
87
|
return "\n".join(formatted_messages)
|
84
88
|
|
89
|
+
|
85
90
|
def process_chat_chunk(scope, chunk):
|
86
91
|
"""
|
87
92
|
Process a chunk of chat response data and update state.
|
@@ -96,9 +101,7 @@ def process_chat_chunk(scope, chunk):
|
|
96
101
|
chunked = response_as_dict(chunk)
|
97
102
|
|
98
103
|
# Extract content from chat completions
|
99
|
-
if
|
100
|
-
"delta" in chunked.get("choices")[0]):
|
101
|
-
|
104
|
+
if len(chunked.get("choices", [])) > 0 and "delta" in chunked.get("choices")[0]:
|
102
105
|
delta = chunked.get("choices")[0]["delta"]
|
103
106
|
content = delta.get("content")
|
104
107
|
if content:
|
@@ -119,24 +122,36 @@ def process_chat_chunk(scope, chunk):
|
|
119
122
|
func = tool.get("function", {})
|
120
123
|
scope._tools[idx] = {
|
121
124
|
"id": tool["id"],
|
122
|
-
"function": {
|
123
|
-
|
125
|
+
"function": {
|
126
|
+
"name": func.get("name", ""),
|
127
|
+
"arguments": func.get("arguments", ""),
|
128
|
+
},
|
129
|
+
"type": tool.get("type", "function"),
|
124
130
|
}
|
125
|
-
elif
|
126
|
-
scope._tools[idx]
|
131
|
+
elif (
|
132
|
+
scope._tools[idx] and "function" in tool
|
133
|
+
): # Append args (id is None)
|
134
|
+
scope._tools[idx]["function"]["arguments"] += tool["function"].get(
|
135
|
+
"arguments", ""
|
136
|
+
)
|
127
137
|
|
128
138
|
# Extract metadata
|
129
139
|
scope._response_id = chunked.get("id") or scope._response_id
|
130
140
|
scope._response_model = chunked.get("model") or scope._response_model
|
131
141
|
|
132
142
|
try:
|
133
|
-
scope._finish_reason =
|
143
|
+
scope._finish_reason = (
|
144
|
+
chunked.get("choices", [])[0].get("finish_reason") or scope._finish_reason
|
145
|
+
)
|
134
146
|
except (IndexError, AttributeError, TypeError):
|
135
147
|
scope._finish_reason = "stop"
|
136
148
|
|
137
|
-
scope._system_fingerprint =
|
149
|
+
scope._system_fingerprint = (
|
150
|
+
chunked.get("system_fingerprint") or scope._system_fingerprint
|
151
|
+
)
|
138
152
|
scope._service_tier = chunked.get("service_tier") or scope._service_tier
|
139
153
|
|
154
|
+
|
140
155
|
def process_response_chunk(scope, chunk):
|
141
156
|
"""
|
142
157
|
Process a chunk of response API data and update state.
|
@@ -162,14 +177,16 @@ def process_response_chunk(scope, chunk):
|
|
162
177
|
|
163
178
|
item = chunked.get("item", {})
|
164
179
|
if item.get("type") == "function_call":
|
165
|
-
scope._response_tools.append(
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
180
|
+
scope._response_tools.append(
|
181
|
+
{
|
182
|
+
"id": item.get("id", ""),
|
183
|
+
"call_id": item.get("call_id", ""),
|
184
|
+
"name": item.get("name", ""),
|
185
|
+
"type": item.get("type", "function_call"),
|
186
|
+
"arguments": item.get("arguments", ""),
|
187
|
+
"status": item.get("status", "in_progress"),
|
188
|
+
}
|
189
|
+
)
|
173
190
|
|
174
191
|
elif chunked.get("type") == "response.function_call_arguments.delta":
|
175
192
|
# Tool arguments being streamed
|
@@ -204,12 +221,16 @@ def process_response_chunk(scope, chunk):
|
|
204
221
|
# Update the tool with final status and data
|
205
222
|
for tool in scope._response_tools:
|
206
223
|
if tool.get("id") == item_id:
|
207
|
-
tool.update(
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
224
|
+
tool.update(
|
225
|
+
{
|
226
|
+
"call_id": item.get("call_id", tool.get("call_id", "")),
|
227
|
+
"name": item.get("name", tool.get("name", "")),
|
228
|
+
"arguments": item.get(
|
229
|
+
"arguments", tool.get("arguments", "")
|
230
|
+
),
|
231
|
+
"status": item.get("status", "completed"),
|
232
|
+
}
|
233
|
+
)
|
213
234
|
break
|
214
235
|
|
215
236
|
elif chunked.get("type") == "response.completed":
|
@@ -226,8 +247,18 @@ def process_response_chunk(scope, chunk):
|
|
226
247
|
output_tokens_details = usage.get("output_tokens_details", {})
|
227
248
|
scope._reasoning_tokens = output_tokens_details.get("reasoning_tokens", 0)
|
228
249
|
|
229
|
-
|
230
|
-
|
250
|
+
|
251
|
+
def common_response_logic(
|
252
|
+
scope,
|
253
|
+
pricing_info,
|
254
|
+
environment,
|
255
|
+
application_name,
|
256
|
+
metrics,
|
257
|
+
capture_message_content,
|
258
|
+
disable_metrics,
|
259
|
+
version,
|
260
|
+
is_stream,
|
261
|
+
):
|
231
262
|
"""
|
232
263
|
Process responses API request and generate Telemetry
|
233
264
|
"""
|
@@ -252,66 +283,115 @@ def common_response_logic(scope, pricing_info, environment, application_name, me
|
|
252
283
|
cost = get_chat_model_cost(request_model, pricing_info, input_tokens, output_tokens)
|
253
284
|
|
254
285
|
# Common Span Attributes
|
255
|
-
common_span_attributes(
|
256
|
-
|
257
|
-
|
258
|
-
|
286
|
+
common_span_attributes(
|
287
|
+
scope,
|
288
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
289
|
+
SemanticConvention.GEN_AI_SYSTEM_OPENAI,
|
290
|
+
scope._server_address,
|
291
|
+
scope._server_port,
|
292
|
+
request_model,
|
293
|
+
scope._response_model,
|
294
|
+
environment,
|
295
|
+
application_name,
|
296
|
+
is_stream,
|
297
|
+
scope._tbt,
|
298
|
+
scope._ttft,
|
299
|
+
version,
|
300
|
+
)
|
259
301
|
|
260
302
|
# Span Attributes for Request parameters specific to responses API
|
261
|
-
scope._span.set_attribute(
|
303
|
+
scope._span.set_attribute(
|
304
|
+
SemanticConvention.GEN_AI_REQUEST_TEMPERATURE,
|
305
|
+
handle_not_given(scope._kwargs.get("temperature"), 1.0),
|
306
|
+
)
|
262
307
|
scope._span.set_attribute(
|
263
308
|
SemanticConvention.GEN_AI_REQUEST_TOP_P,
|
264
|
-
handle_not_given(scope._kwargs.get("top_p"), 1.0)
|
309
|
+
handle_not_given(scope._kwargs.get("top_p"), 1.0),
|
265
310
|
)
|
266
311
|
scope._span.set_attribute(
|
267
312
|
SemanticConvention.GEN_AI_REQUEST_MAX_TOKENS,
|
268
|
-
handle_not_given(scope._kwargs.get("max_output_tokens"), -1)
|
313
|
+
handle_not_given(scope._kwargs.get("max_output_tokens"), -1),
|
269
314
|
)
|
270
315
|
|
271
316
|
# Reasoning parameters
|
272
317
|
reasoning = scope._kwargs.get("reasoning", {})
|
273
318
|
if reasoning:
|
274
319
|
if reasoning.get("effort"):
|
275
|
-
scope._span.set_attribute(
|
320
|
+
scope._span.set_attribute(
|
321
|
+
"gen_ai.request.reasoning_effort", reasoning.get("effort")
|
322
|
+
)
|
276
323
|
|
277
324
|
# Responses API specific attributes
|
278
325
|
if hasattr(scope, "_service_tier"):
|
279
|
-
scope._span.set_attribute(
|
326
|
+
scope._span.set_attribute(
|
327
|
+
SemanticConvention.GEN_AI_REQUEST_SERVICE_TIER, scope._service_tier
|
328
|
+
)
|
280
329
|
|
281
330
|
# Span Attributes for Response parameters
|
282
331
|
scope._span.set_attribute(SemanticConvention.GEN_AI_RESPONSE_ID, scope._response_id)
|
283
|
-
scope._span.set_attribute(
|
332
|
+
scope._span.set_attribute(
|
333
|
+
SemanticConvention.GEN_AI_RESPONSE_FINISH_REASON, [scope._finish_reason]
|
334
|
+
)
|
284
335
|
scope._span.set_attribute(SemanticConvention.GEN_AI_OUTPUT_TYPE, "text")
|
285
336
|
|
286
337
|
# Span Attributes for Tools (responses API structure) - optimized
|
287
338
|
if hasattr(scope, "_response_tools") and scope._response_tools:
|
288
|
-
tools =
|
339
|
+
tools = (
|
340
|
+
scope._response_tools
|
341
|
+
if isinstance(scope._response_tools, list)
|
342
|
+
else [scope._response_tools]
|
343
|
+
)
|
289
344
|
|
290
|
-
names, ids, args =
|
291
|
-
(
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
345
|
+
names, ids, args = (
|
346
|
+
zip(
|
347
|
+
*[
|
348
|
+
(
|
349
|
+
t.get("name", ""),
|
350
|
+
str(t.get("call_id", "")), # Use call_id for responses API
|
351
|
+
str(t.get("arguments", "")),
|
352
|
+
)
|
353
|
+
for t in tools
|
354
|
+
if isinstance(t, dict) and t
|
355
|
+
]
|
356
|
+
)
|
357
|
+
if tools
|
358
|
+
else ([], [], [])
|
359
|
+
)
|
296
360
|
|
297
|
-
scope._span.set_attribute(
|
298
|
-
|
299
|
-
|
361
|
+
scope._span.set_attribute(
|
362
|
+
SemanticConvention.GEN_AI_TOOL_NAME, ", ".join(filter(None, names))
|
363
|
+
)
|
364
|
+
scope._span.set_attribute(
|
365
|
+
SemanticConvention.GEN_AI_TOOL_CALL_ID, ", ".join(filter(None, ids))
|
366
|
+
)
|
367
|
+
scope._span.set_attribute(
|
368
|
+
SemanticConvention.GEN_AI_TOOL_ARGS, ", ".join(filter(None, args))
|
369
|
+
)
|
300
370
|
|
301
371
|
# Span Attributes for Cost and Tokens
|
302
|
-
scope._span.set_attribute(
|
303
|
-
|
304
|
-
|
372
|
+
scope._span.set_attribute(
|
373
|
+
SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS, input_tokens
|
374
|
+
)
|
375
|
+
scope._span.set_attribute(
|
376
|
+
SemanticConvention.GEN_AI_USAGE_OUTPUT_TOKENS, output_tokens
|
377
|
+
)
|
378
|
+
scope._span.set_attribute(
|
379
|
+
SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE, input_tokens + output_tokens
|
380
|
+
)
|
305
381
|
scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_COST, cost)
|
306
382
|
|
307
383
|
# Reasoning tokens
|
308
384
|
if hasattr(scope, "_reasoning_tokens") and scope._reasoning_tokens > 0:
|
309
|
-
scope._span.set_attribute(
|
385
|
+
scope._span.set_attribute(
|
386
|
+
"gen_ai.usage.reasoning_tokens", scope._reasoning_tokens
|
387
|
+
)
|
310
388
|
|
311
389
|
# Span Attributes for Content
|
312
390
|
if capture_message_content:
|
313
391
|
scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, prompt)
|
314
|
-
scope._span.set_attribute(
|
392
|
+
scope._span.set_attribute(
|
393
|
+
SemanticConvention.GEN_AI_CONTENT_COMPLETION, scope._llmresponse
|
394
|
+
)
|
315
395
|
|
316
396
|
# To be removed once the change to span_attributes (from span events) is complete
|
317
397
|
scope._span.add_event(
|
@@ -331,23 +411,69 @@ def common_response_logic(scope, pricing_info, environment, application_name, me
|
|
331
411
|
|
332
412
|
# Record metrics
|
333
413
|
if not disable_metrics:
|
334
|
-
record_completion_metrics(
|
335
|
-
|
336
|
-
|
337
|
-
|
414
|
+
record_completion_metrics(
|
415
|
+
metrics,
|
416
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
417
|
+
SemanticConvention.GEN_AI_SYSTEM_OPENAI,
|
418
|
+
scope._server_address,
|
419
|
+
scope._server_port,
|
420
|
+
request_model,
|
421
|
+
scope._response_model,
|
422
|
+
environment,
|
423
|
+
application_name,
|
424
|
+
scope._start_time,
|
425
|
+
scope._end_time,
|
426
|
+
input_tokens,
|
427
|
+
output_tokens,
|
428
|
+
cost,
|
429
|
+
scope._tbt,
|
430
|
+
scope._ttft,
|
431
|
+
)
|
432
|
+
|
338
433
|
|
339
|
-
def process_streaming_response_response(
|
340
|
-
|
434
|
+
def process_streaming_response_response(
|
435
|
+
scope,
|
436
|
+
pricing_info,
|
437
|
+
environment,
|
438
|
+
application_name,
|
439
|
+
metrics,
|
440
|
+
capture_message_content=False,
|
441
|
+
disable_metrics=False,
|
442
|
+
version="",
|
443
|
+
):
|
341
444
|
"""
|
342
445
|
Process streaming responses API response and generate telemetry.
|
343
446
|
"""
|
344
447
|
|
345
|
-
common_response_logic(
|
346
|
-
|
448
|
+
common_response_logic(
|
449
|
+
scope,
|
450
|
+
pricing_info,
|
451
|
+
environment,
|
452
|
+
application_name,
|
453
|
+
metrics,
|
454
|
+
capture_message_content,
|
455
|
+
disable_metrics,
|
456
|
+
version,
|
457
|
+
is_stream=True,
|
458
|
+
)
|
459
|
+
|
347
460
|
|
348
|
-
def process_response_response(
|
349
|
-
|
350
|
-
|
461
|
+
def process_response_response(
|
462
|
+
response,
|
463
|
+
request_model,
|
464
|
+
pricing_info,
|
465
|
+
server_port,
|
466
|
+
server_address,
|
467
|
+
environment,
|
468
|
+
application_name,
|
469
|
+
metrics,
|
470
|
+
start_time,
|
471
|
+
span,
|
472
|
+
capture_message_content=False,
|
473
|
+
disable_metrics=False,
|
474
|
+
version="1.0.0",
|
475
|
+
**kwargs,
|
476
|
+
):
|
351
477
|
"""
|
352
478
|
Process non-streaming responses API response and generate telemetry.
|
353
479
|
"""
|
@@ -373,14 +499,16 @@ def process_response_response(response, request_model, pricing_info, server_port
|
|
373
499
|
break
|
374
500
|
if item.get("type") == "function_call":
|
375
501
|
# Handle tool call
|
376
|
-
scope._response_tools = [
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
502
|
+
scope._response_tools = [
|
503
|
+
{
|
504
|
+
"id": item.get("id", ""),
|
505
|
+
"call_id": item.get("call_id", ""),
|
506
|
+
"name": item.get("name", ""),
|
507
|
+
"type": item.get("type", "function_call"),
|
508
|
+
"arguments": item.get("arguments", ""),
|
509
|
+
"status": item.get("status", ""),
|
510
|
+
}
|
511
|
+
]
|
384
512
|
|
385
513
|
# Extract content from message item if found
|
386
514
|
if message_item:
|
@@ -406,13 +534,32 @@ def process_response_response(response, request_model, pricing_info, server_port
|
|
406
534
|
scope._service_tier = response_dict.get("service_tier", "default")
|
407
535
|
scope._finish_reason = response_dict.get("status", "completed")
|
408
536
|
|
409
|
-
common_response_logic(
|
410
|
-
|
537
|
+
common_response_logic(
|
538
|
+
scope,
|
539
|
+
pricing_info,
|
540
|
+
environment,
|
541
|
+
application_name,
|
542
|
+
metrics,
|
543
|
+
capture_message_content,
|
544
|
+
disable_metrics,
|
545
|
+
version,
|
546
|
+
is_stream=False,
|
547
|
+
)
|
411
548
|
|
412
549
|
return response
|
413
550
|
|
414
|
-
|
415
|
-
|
551
|
+
|
552
|
+
def common_chat_logic(
|
553
|
+
scope,
|
554
|
+
pricing_info,
|
555
|
+
environment,
|
556
|
+
application_name,
|
557
|
+
metrics,
|
558
|
+
capture_message_content,
|
559
|
+
disable_metrics,
|
560
|
+
version,
|
561
|
+
is_stream,
|
562
|
+
):
|
416
563
|
"""
|
417
564
|
Process chat request and generate Telemetry
|
418
565
|
"""
|
@@ -447,63 +594,121 @@ def common_chat_logic(scope, pricing_info, environment, application_name, metric
|
|
447
594
|
scope,
|
448
595
|
SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
449
596
|
SemanticConvention.GEN_AI_SYSTEM_OPENAI,
|
450
|
-
scope._server_address,
|
451
|
-
scope.
|
452
|
-
|
597
|
+
scope._server_address,
|
598
|
+
scope._server_port,
|
599
|
+
request_model,
|
600
|
+
scope._response_model,
|
601
|
+
environment,
|
602
|
+
application_name,
|
603
|
+
is_stream,
|
604
|
+
scope._tbt,
|
605
|
+
scope._ttft,
|
606
|
+
version,
|
453
607
|
)
|
454
608
|
|
455
609
|
# Span Attributes for Request parameters
|
456
|
-
scope._span.set_attribute(
|
610
|
+
scope._span.set_attribute(
|
611
|
+
SemanticConvention.GEN_AI_REQUEST_SEED,
|
612
|
+
str(handle_not_given(scope._kwargs.get("seed"), "")),
|
613
|
+
)
|
457
614
|
scope._span.set_attribute(
|
458
615
|
SemanticConvention.GEN_AI_REQUEST_FREQUENCY_PENALTY,
|
459
|
-
handle_not_given(scope._kwargs.get("frequency_penalty"), 0.0)
|
616
|
+
handle_not_given(scope._kwargs.get("frequency_penalty"), 0.0),
|
617
|
+
)
|
618
|
+
scope._span.set_attribute(
|
619
|
+
SemanticConvention.GEN_AI_REQUEST_MAX_TOKENS,
|
620
|
+
handle_not_given(scope._kwargs.get("max_tokens"), -1),
|
460
621
|
)
|
461
|
-
scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_MAX_TOKENS, handle_not_given(scope._kwargs.get("max_tokens"), -1))
|
462
622
|
scope._span.set_attribute(
|
463
623
|
SemanticConvention.GEN_AI_REQUEST_PRESENCE_PENALTY,
|
464
|
-
handle_not_given(scope._kwargs.get("presence_penalty"), 0.0)
|
624
|
+
handle_not_given(scope._kwargs.get("presence_penalty"), 0.0),
|
625
|
+
)
|
626
|
+
scope._span.set_attribute(
|
627
|
+
SemanticConvention.GEN_AI_REQUEST_STOP_SEQUENCES,
|
628
|
+
handle_not_given(scope._kwargs.get("stop"), []),
|
629
|
+
)
|
630
|
+
scope._span.set_attribute(
|
631
|
+
SemanticConvention.GEN_AI_REQUEST_TEMPERATURE,
|
632
|
+
handle_not_given(scope._kwargs.get("temperature"), 1.0),
|
633
|
+
)
|
634
|
+
scope._span.set_attribute(
|
635
|
+
SemanticConvention.GEN_AI_REQUEST_TOP_P,
|
636
|
+
handle_not_given(scope._kwargs.get("top_p"), 1.0),
|
637
|
+
)
|
638
|
+
scope._span.set_attribute(
|
639
|
+
SemanticConvention.GEN_AI_REQUEST_USER,
|
640
|
+
handle_not_given(scope._kwargs.get("user"), ""),
|
465
641
|
)
|
466
|
-
scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_STOP_SEQUENCES, handle_not_given(scope._kwargs.get("stop"), []))
|
467
|
-
scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_TEMPERATURE, handle_not_given(scope._kwargs.get("temperature"), 1.0))
|
468
|
-
scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_TOP_P, handle_not_given(scope._kwargs.get("top_p"), 1.0))
|
469
|
-
scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_USER, handle_not_given(scope._kwargs.get("user"), ""))
|
470
642
|
|
471
643
|
# Span Attributes for Response parameters
|
472
644
|
scope._span.set_attribute(SemanticConvention.GEN_AI_RESPONSE_ID, scope._response_id)
|
473
|
-
scope._span.set_attribute(
|
474
|
-
|
645
|
+
scope._span.set_attribute(
|
646
|
+
SemanticConvention.GEN_AI_RESPONSE_FINISH_REASON, [scope._finish_reason]
|
647
|
+
)
|
648
|
+
scope._span.set_attribute(
|
649
|
+
SemanticConvention.GEN_AI_OUTPUT_TYPE,
|
650
|
+
"text" if isinstance(scope._llmresponse, str) else "json",
|
651
|
+
)
|
475
652
|
|
476
653
|
# OpenAI-specific attributes
|
477
654
|
if hasattr(scope, "_system_fingerprint") and scope._system_fingerprint:
|
478
|
-
scope._span.set_attribute(
|
655
|
+
scope._span.set_attribute(
|
656
|
+
SemanticConvention.GEN_AI_RESPONSE_SYSTEM_FINGERPRINT,
|
657
|
+
scope._system_fingerprint,
|
658
|
+
)
|
479
659
|
if hasattr(scope, "_service_tier") and scope._service_tier:
|
480
|
-
scope._span.set_attribute(
|
660
|
+
scope._span.set_attribute(
|
661
|
+
SemanticConvention.GEN_AI_REQUEST_SERVICE_TIER, scope._service_tier
|
662
|
+
)
|
481
663
|
|
482
664
|
# Span Attributes for Tools - optimized
|
483
665
|
if hasattr(scope, "_tools") and scope._tools:
|
484
666
|
tools = scope._tools if isinstance(scope._tools, list) else [scope._tools]
|
485
667
|
|
486
|
-
names, ids, args =
|
487
|
-
(
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
668
|
+
names, ids, args = (
|
669
|
+
zip(
|
670
|
+
*[
|
671
|
+
(
|
672
|
+
t.get("function", {}).get("name", ""),
|
673
|
+
str(t.get("id", "")),
|
674
|
+
str(t.get("function", {}).get("arguments", "")),
|
675
|
+
)
|
676
|
+
for t in tools
|
677
|
+
if isinstance(t, dict) and t
|
678
|
+
]
|
679
|
+
)
|
680
|
+
if tools
|
681
|
+
else ([], [], [])
|
682
|
+
)
|
492
683
|
|
493
|
-
scope._span.set_attribute(
|
494
|
-
|
495
|
-
|
684
|
+
scope._span.set_attribute(
|
685
|
+
SemanticConvention.GEN_AI_TOOL_NAME, ", ".join(filter(None, names))
|
686
|
+
)
|
687
|
+
scope._span.set_attribute(
|
688
|
+
SemanticConvention.GEN_AI_TOOL_CALL_ID, ", ".join(filter(None, ids))
|
689
|
+
)
|
690
|
+
scope._span.set_attribute(
|
691
|
+
SemanticConvention.GEN_AI_TOOL_ARGS, ", ".join(filter(None, args))
|
692
|
+
)
|
496
693
|
|
497
694
|
# Span Attributes for Cost and Tokens
|
498
|
-
scope._span.set_attribute(
|
499
|
-
|
500
|
-
|
695
|
+
scope._span.set_attribute(
|
696
|
+
SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS, input_tokens
|
697
|
+
)
|
698
|
+
scope._span.set_attribute(
|
699
|
+
SemanticConvention.GEN_AI_USAGE_OUTPUT_TOKENS, output_tokens
|
700
|
+
)
|
701
|
+
scope._span.set_attribute(
|
702
|
+
SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE, input_tokens + output_tokens
|
703
|
+
)
|
501
704
|
scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_COST, cost)
|
502
705
|
|
503
706
|
# Span Attributes for Content
|
504
707
|
if capture_message_content:
|
505
708
|
scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, prompt)
|
506
|
-
scope._span.set_attribute(
|
709
|
+
scope._span.set_attribute(
|
710
|
+
SemanticConvention.GEN_AI_CONTENT_COMPLETION, scope._llmresponse
|
711
|
+
)
|
507
712
|
|
508
713
|
# To be removed once the change to span_attributes (from span events) is complete
|
509
714
|
scope._span.add_event(
|
@@ -523,23 +728,69 @@ def common_chat_logic(scope, pricing_info, environment, application_name, metric
|
|
523
728
|
|
524
729
|
# Record metrics
|
525
730
|
if not disable_metrics:
|
526
|
-
record_completion_metrics(
|
527
|
-
|
528
|
-
|
529
|
-
|
731
|
+
record_completion_metrics(
|
732
|
+
metrics,
|
733
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
734
|
+
SemanticConvention.GEN_AI_SYSTEM_OPENAI,
|
735
|
+
scope._server_address,
|
736
|
+
scope._server_port,
|
737
|
+
request_model,
|
738
|
+
scope._response_model,
|
739
|
+
environment,
|
740
|
+
application_name,
|
741
|
+
scope._start_time,
|
742
|
+
scope._end_time,
|
743
|
+
input_tokens,
|
744
|
+
output_tokens,
|
745
|
+
cost,
|
746
|
+
scope._tbt,
|
747
|
+
scope._ttft,
|
748
|
+
)
|
749
|
+
|
530
750
|
|
531
|
-
def process_streaming_chat_response(
|
532
|
-
|
751
|
+
def process_streaming_chat_response(
|
752
|
+
scope,
|
753
|
+
pricing_info,
|
754
|
+
environment,
|
755
|
+
application_name,
|
756
|
+
metrics,
|
757
|
+
capture_message_content=False,
|
758
|
+
disable_metrics=False,
|
759
|
+
version="",
|
760
|
+
):
|
533
761
|
"""
|
534
762
|
Process streaming chat response and generate telemetry.
|
535
763
|
"""
|
536
764
|
|
537
|
-
common_chat_logic(
|
538
|
-
|
765
|
+
common_chat_logic(
|
766
|
+
scope,
|
767
|
+
pricing_info,
|
768
|
+
environment,
|
769
|
+
application_name,
|
770
|
+
metrics,
|
771
|
+
capture_message_content,
|
772
|
+
disable_metrics,
|
773
|
+
version,
|
774
|
+
is_stream=True,
|
775
|
+
)
|
776
|
+
|
539
777
|
|
540
|
-
def process_chat_response(
|
541
|
-
|
542
|
-
|
778
|
+
def process_chat_response(
|
779
|
+
response,
|
780
|
+
request_model,
|
781
|
+
pricing_info,
|
782
|
+
server_port,
|
783
|
+
server_address,
|
784
|
+
environment,
|
785
|
+
application_name,
|
786
|
+
metrics,
|
787
|
+
start_time,
|
788
|
+
span,
|
789
|
+
capture_message_content=False,
|
790
|
+
disable_metrics=False,
|
791
|
+
version="1.0.0",
|
792
|
+
**kwargs,
|
793
|
+
):
|
543
794
|
"""
|
544
795
|
Process non-streaming chat response and generate telemetry.
|
545
796
|
"""
|
@@ -564,7 +815,11 @@ def process_chat_response(response, request_model, pricing_info, server_port, se
|
|
564
815
|
scope._kwargs = kwargs
|
565
816
|
scope._system_fingerprint = response_dict.get("system_fingerprint", "")
|
566
817
|
scope._service_tier = response_dict.get("service_tier", "auto")
|
567
|
-
scope._finish_reason =
|
818
|
+
scope._finish_reason = (
|
819
|
+
str(response_dict.get("choices", [])[0].get("finish_reason", ""))
|
820
|
+
if response_dict.get("choices")
|
821
|
+
else ""
|
822
|
+
)
|
568
823
|
|
569
824
|
# Handle operation type for responses API
|
570
825
|
if kwargs.get("_operation_type") == "responses":
|
@@ -572,17 +827,38 @@ def process_chat_response(response, request_model, pricing_info, server_port, se
|
|
572
827
|
|
573
828
|
# Handle tool calls
|
574
829
|
if kwargs.get("tools"):
|
575
|
-
scope._tools =
|
830
|
+
scope._tools = (
|
831
|
+
response_dict.get("choices", [{}])[0].get("message", {}).get("tool_calls")
|
832
|
+
)
|
576
833
|
else:
|
577
834
|
scope._tools = None
|
578
835
|
|
579
|
-
common_chat_logic(
|
580
|
-
|
836
|
+
common_chat_logic(
|
837
|
+
scope,
|
838
|
+
pricing_info,
|
839
|
+
environment,
|
840
|
+
application_name,
|
841
|
+
metrics,
|
842
|
+
capture_message_content,
|
843
|
+
disable_metrics,
|
844
|
+
version,
|
845
|
+
is_stream=False,
|
846
|
+
)
|
581
847
|
|
582
848
|
return response
|
583
849
|
|
584
|
-
|
585
|
-
|
850
|
+
|
851
|
+
def common_embedding_logic(
|
852
|
+
scope,
|
853
|
+
request_model,
|
854
|
+
pricing_info,
|
855
|
+
environment,
|
856
|
+
application_name,
|
857
|
+
metrics,
|
858
|
+
capture_message_content,
|
859
|
+
disable_metrics,
|
860
|
+
version,
|
861
|
+
):
|
586
862
|
"""
|
587
863
|
Common logic for processing embedding operations.
|
588
864
|
"""
|
@@ -591,61 +867,127 @@ def common_embedding_logic(scope, request_model, pricing_info, environment, appl
|
|
591
867
|
cost = get_embed_model_cost(request_model, pricing_info, scope._input_tokens)
|
592
868
|
|
593
869
|
# Common Span Attributes
|
594
|
-
common_span_attributes(
|
595
|
-
|
596
|
-
|
597
|
-
|
870
|
+
common_span_attributes(
|
871
|
+
scope,
|
872
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_EMBEDDING,
|
873
|
+
SemanticConvention.GEN_AI_SYSTEM_OPENAI,
|
874
|
+
scope._server_address,
|
875
|
+
scope._server_port,
|
876
|
+
request_model,
|
877
|
+
request_model,
|
878
|
+
environment,
|
879
|
+
application_name,
|
880
|
+
False,
|
881
|
+
scope._tbt,
|
882
|
+
scope._ttft,
|
883
|
+
version,
|
884
|
+
)
|
598
885
|
|
599
886
|
# Span Attributes for Request parameters
|
600
887
|
scope._span.set_attribute(
|
601
888
|
SemanticConvention.GEN_AI_REQUEST_ENCODING_FORMATS,
|
602
|
-
[handle_not_given(scope._kwargs.get("encoding_format"), "float")]
|
889
|
+
[handle_not_given(scope._kwargs.get("encoding_format"), "float")],
|
890
|
+
)
|
891
|
+
scope._span.set_attribute(
|
892
|
+
SemanticConvention.GEN_AI_REQUEST_USER,
|
893
|
+
handle_not_given(scope._kwargs.get("user"), ""),
|
603
894
|
)
|
604
|
-
scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_USER, handle_not_given(scope._kwargs.get("user"), ""))
|
605
895
|
|
606
896
|
# Span Attributes for Cost and Tokens
|
607
|
-
scope._span.set_attribute(
|
608
|
-
|
897
|
+
scope._span.set_attribute(
|
898
|
+
SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS, scope._input_tokens
|
899
|
+
)
|
900
|
+
scope._span.set_attribute(
|
901
|
+
SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE, scope._input_tokens
|
902
|
+
)
|
609
903
|
scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_COST, cost)
|
610
904
|
|
611
905
|
# Span Attributes for Content
|
612
906
|
if capture_message_content:
|
613
907
|
input_data = scope._kwargs.get("input", "")
|
614
|
-
formatted_content =
|
615
|
-
|
908
|
+
formatted_content = (
|
909
|
+
format_content(input_data)
|
910
|
+
if isinstance(input_data, (list, dict))
|
911
|
+
else str(input_data)
|
912
|
+
)
|
913
|
+
scope._span.set_attribute(
|
914
|
+
SemanticConvention.GEN_AI_CONTENT_PROMPT, formatted_content
|
915
|
+
)
|
616
916
|
|
617
917
|
scope._span.set_status(Status(StatusCode.OK))
|
618
918
|
|
619
919
|
# Record metrics
|
620
920
|
if not disable_metrics:
|
621
|
-
record_embedding_metrics(
|
622
|
-
|
623
|
-
|
921
|
+
record_embedding_metrics(
|
922
|
+
metrics,
|
923
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_EMBEDDING,
|
924
|
+
SemanticConvention.GEN_AI_SYSTEM_OPENAI,
|
925
|
+
scope._server_address,
|
926
|
+
scope._server_port,
|
927
|
+
request_model,
|
928
|
+
request_model,
|
929
|
+
environment,
|
930
|
+
application_name,
|
931
|
+
scope._start_time,
|
932
|
+
scope._end_time,
|
933
|
+
scope._input_tokens,
|
934
|
+
cost,
|
935
|
+
)
|
624
936
|
|
625
|
-
|
626
|
-
|
937
|
+
|
938
|
+
def common_image_logic(
|
939
|
+
scope,
|
940
|
+
request_model,
|
941
|
+
pricing_info,
|
942
|
+
environment,
|
943
|
+
application_name,
|
944
|
+
metrics,
|
945
|
+
capture_message_content,
|
946
|
+
disable_metrics,
|
947
|
+
version,
|
948
|
+
):
|
627
949
|
"""
|
628
950
|
Common logic for processing image operations.
|
629
951
|
"""
|
630
952
|
|
631
953
|
# Calculate cost
|
632
|
-
cost = get_image_model_cost(
|
633
|
-
|
634
|
-
|
954
|
+
cost = get_image_model_cost(
|
955
|
+
request_model,
|
956
|
+
pricing_info,
|
957
|
+
scope._kwargs.get("size", "1024x1024"),
|
958
|
+
scope._kwargs.get("quality", "standard"),
|
959
|
+
)
|
635
960
|
|
636
961
|
# Common Span Attributes
|
637
|
-
common_span_attributes(
|
638
|
-
|
639
|
-
|
640
|
-
|
962
|
+
common_span_attributes(
|
963
|
+
scope,
|
964
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_IMAGE,
|
965
|
+
SemanticConvention.GEN_AI_SYSTEM_OPENAI,
|
966
|
+
scope._server_address,
|
967
|
+
scope._server_port,
|
968
|
+
request_model,
|
969
|
+
request_model,
|
970
|
+
environment,
|
971
|
+
application_name,
|
972
|
+
False,
|
973
|
+
scope._tbt,
|
974
|
+
scope._ttft,
|
975
|
+
version,
|
976
|
+
)
|
641
977
|
|
642
978
|
# Span Attributes for Request parameters
|
643
|
-
scope._span.set_attribute(
|
979
|
+
scope._span.set_attribute(
|
980
|
+
SemanticConvention.GEN_AI_REQUEST_IMAGE_SIZE,
|
981
|
+
handle_not_given(scope._kwargs.get("size"), "1024x1024"),
|
982
|
+
)
|
644
983
|
scope._span.set_attribute(
|
645
984
|
SemanticConvention.GEN_AI_REQUEST_IMAGE_QUALITY,
|
646
|
-
handle_not_given(scope._kwargs.get("quality"), "standard")
|
985
|
+
handle_not_given(scope._kwargs.get("quality"), "standard"),
|
986
|
+
)
|
987
|
+
scope._span.set_attribute(
|
988
|
+
SemanticConvention.GEN_AI_REQUEST_USER,
|
989
|
+
handle_not_given(scope._kwargs.get("user"), ""),
|
647
990
|
)
|
648
|
-
scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_USER, handle_not_given(scope._kwargs.get("user"), ""))
|
649
991
|
|
650
992
|
# Extract response data
|
651
993
|
response_dict = scope._response_dict
|
@@ -657,7 +999,9 @@ def common_image_logic(scope, request_model, pricing_info, environment, applicat
|
|
657
999
|
|
658
1000
|
# Span Attributes for Response
|
659
1001
|
if response_created:
|
660
|
-
scope._span.set_attribute(
|
1002
|
+
scope._span.set_attribute(
|
1003
|
+
SemanticConvention.GEN_AI_RESPONSE_ID, str(response_created)
|
1004
|
+
)
|
661
1005
|
|
662
1006
|
# Process image data and collect URLs/base64 content
|
663
1007
|
if images_data:
|
@@ -675,7 +1019,9 @@ def common_image_logic(scope, request_model, pricing_info, environment, applicat
|
|
675
1019
|
|
676
1020
|
# Set image response data using semantic conventions
|
677
1021
|
if image_contents:
|
678
|
-
scope._span.set_attribute(
|
1022
|
+
scope._span.set_attribute(
|
1023
|
+
SemanticConvention.GEN_AI_RESPONSE_IMAGE, image_contents
|
1024
|
+
)
|
679
1025
|
|
680
1026
|
# Response-level attributes if different from request
|
681
1027
|
if response_size:
|
@@ -683,7 +1029,9 @@ def common_image_logic(scope, request_model, pricing_info, environment, applicat
|
|
683
1029
|
if response_quality:
|
684
1030
|
scope._span.set_attribute("gen_ai.response.image_quality", response_quality)
|
685
1031
|
if response_output_format:
|
686
|
-
scope._span.set_attribute(
|
1032
|
+
scope._span.set_attribute(
|
1033
|
+
"gen_ai.response.output_format", response_output_format
|
1034
|
+
)
|
687
1035
|
|
688
1036
|
# Span Attributes for Cost
|
689
1037
|
scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_COST, cost)
|
@@ -703,7 +1051,9 @@ def common_image_logic(scope, request_model, pricing_info, environment, applicat
|
|
703
1051
|
|
704
1052
|
# Set revised prompts as span attribute if any were found
|
705
1053
|
if revised_prompts:
|
706
|
-
scope._span.set_attribute(
|
1054
|
+
scope._span.set_attribute(
|
1055
|
+
SemanticConvention.GEN_AI_CONTENT_REVISED_PROMPT, revised_prompts
|
1056
|
+
)
|
707
1057
|
|
708
1058
|
# Add revised prompt events for detailed tracking
|
709
1059
|
for i, image in enumerate(images_data):
|
@@ -711,7 +1061,9 @@ def common_image_logic(scope, request_model, pricing_info, environment, applicat
|
|
711
1061
|
scope._span.add_event(
|
712
1062
|
name=SemanticConvention.GEN_AI_CONTENT_REVISED_PROMPT,
|
713
1063
|
attributes={
|
714
|
-
SemanticConvention.GEN_AI_CONTENT_REVISED_PROMPT: image[
|
1064
|
+
SemanticConvention.GEN_AI_CONTENT_REVISED_PROMPT: image[
|
1065
|
+
"revised_prompt"
|
1066
|
+
],
|
715
1067
|
"image_index": i,
|
716
1068
|
},
|
717
1069
|
)
|
@@ -720,12 +1072,33 @@ def common_image_logic(scope, request_model, pricing_info, environment, applicat
|
|
720
1072
|
|
721
1073
|
# Record metrics
|
722
1074
|
if not disable_metrics:
|
723
|
-
record_image_metrics(
|
724
|
-
|
725
|
-
|
1075
|
+
record_image_metrics(
|
1076
|
+
metrics,
|
1077
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_IMAGE,
|
1078
|
+
SemanticConvention.GEN_AI_SYSTEM_OPENAI,
|
1079
|
+
scope._server_address,
|
1080
|
+
scope._server_port,
|
1081
|
+
request_model,
|
1082
|
+
request_model,
|
1083
|
+
environment,
|
1084
|
+
application_name,
|
1085
|
+
scope._start_time,
|
1086
|
+
scope._end_time,
|
1087
|
+
cost,
|
1088
|
+
)
|
726
1089
|
|
727
|
-
|
728
|
-
|
1090
|
+
|
1091
|
+
def common_audio_logic(
|
1092
|
+
scope,
|
1093
|
+
request_model,
|
1094
|
+
pricing_info,
|
1095
|
+
environment,
|
1096
|
+
application_name,
|
1097
|
+
metrics,
|
1098
|
+
capture_message_content,
|
1099
|
+
disable_metrics,
|
1100
|
+
version,
|
1101
|
+
):
|
729
1102
|
"""
|
730
1103
|
Common logic for processing audio operations.
|
731
1104
|
"""
|
@@ -735,18 +1108,35 @@ def common_audio_logic(scope, request_model, pricing_info, environment, applicat
|
|
735
1108
|
cost = get_audio_model_cost(request_model, pricing_info, input_text)
|
736
1109
|
|
737
1110
|
# Common Span Attributes
|
738
|
-
common_span_attributes(
|
739
|
-
|
740
|
-
|
741
|
-
|
1111
|
+
common_span_attributes(
|
1112
|
+
scope,
|
1113
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_AUDIO,
|
1114
|
+
SemanticConvention.GEN_AI_SYSTEM_OPENAI,
|
1115
|
+
scope._server_address,
|
1116
|
+
scope._server_port,
|
1117
|
+
request_model,
|
1118
|
+
request_model,
|
1119
|
+
environment,
|
1120
|
+
application_name,
|
1121
|
+
False,
|
1122
|
+
scope._tbt,
|
1123
|
+
scope._ttft,
|
1124
|
+
version,
|
1125
|
+
)
|
742
1126
|
|
743
1127
|
# Span Attributes for Request parameters
|
744
|
-
scope._span.set_attribute(
|
1128
|
+
scope._span.set_attribute(
|
1129
|
+
SemanticConvention.GEN_AI_REQUEST_AUDIO_VOICE,
|
1130
|
+
handle_not_given(scope._kwargs.get("voice"), "alloy"),
|
1131
|
+
)
|
745
1132
|
scope._span.set_attribute(
|
746
1133
|
SemanticConvention.GEN_AI_REQUEST_AUDIO_RESPONSE_FORMAT,
|
747
|
-
handle_not_given(scope._kwargs.get("response_format"), "mp3")
|
1134
|
+
handle_not_given(scope._kwargs.get("response_format"), "mp3"),
|
1135
|
+
)
|
1136
|
+
scope._span.set_attribute(
|
1137
|
+
SemanticConvention.GEN_AI_REQUEST_AUDIO_SPEED,
|
1138
|
+
handle_not_given(scope._kwargs.get("speed"), 1.0),
|
748
1139
|
)
|
749
|
-
scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_AUDIO_SPEED, handle_not_given(scope._kwargs.get("speed"), 1.0))
|
750
1140
|
|
751
1141
|
# Span Attributes for Cost
|
752
1142
|
scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_COST, cost)
|
@@ -760,13 +1150,39 @@ def common_audio_logic(scope, request_model, pricing_info, environment, applicat
|
|
760
1150
|
|
761
1151
|
# Record metrics
|
762
1152
|
if not disable_metrics:
|
763
|
-
record_audio_metrics(
|
764
|
-
|
765
|
-
|
1153
|
+
record_audio_metrics(
|
1154
|
+
metrics,
|
1155
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_AUDIO,
|
1156
|
+
SemanticConvention.GEN_AI_SYSTEM_OPENAI,
|
1157
|
+
scope._server_address,
|
1158
|
+
scope._server_port,
|
1159
|
+
request_model,
|
1160
|
+
request_model,
|
1161
|
+
environment,
|
1162
|
+
application_name,
|
1163
|
+
scope._start_time,
|
1164
|
+
scope._end_time,
|
1165
|
+
cost,
|
1166
|
+
)
|
1167
|
+
|
766
1168
|
|
767
|
-
def process_audio_response(
|
768
|
-
|
769
|
-
|
1169
|
+
def process_audio_response(
|
1170
|
+
response,
|
1171
|
+
request_model,
|
1172
|
+
pricing_info,
|
1173
|
+
server_port,
|
1174
|
+
server_address,
|
1175
|
+
environment,
|
1176
|
+
application_name,
|
1177
|
+
metrics,
|
1178
|
+
start_time,
|
1179
|
+
end_time,
|
1180
|
+
span,
|
1181
|
+
capture_message_content=False,
|
1182
|
+
disable_metrics=False,
|
1183
|
+
version="1.0.0",
|
1184
|
+
**kwargs,
|
1185
|
+
):
|
770
1186
|
"""
|
771
1187
|
Process audio generation response and generate telemetry.
|
772
1188
|
"""
|
@@ -781,14 +1197,37 @@ def process_audio_response(response, request_model, pricing_info, server_port, s
|
|
781
1197
|
scope._server_address, scope._server_port = server_address, server_port
|
782
1198
|
scope._kwargs = kwargs
|
783
1199
|
|
784
|
-
common_audio_logic(
|
785
|
-
|
1200
|
+
common_audio_logic(
|
1201
|
+
scope,
|
1202
|
+
request_model,
|
1203
|
+
pricing_info,
|
1204
|
+
environment,
|
1205
|
+
application_name,
|
1206
|
+
metrics,
|
1207
|
+
capture_message_content,
|
1208
|
+
disable_metrics,
|
1209
|
+
version,
|
1210
|
+
)
|
786
1211
|
|
787
1212
|
return response
|
788
1213
|
|
789
|
-
|
790
|
-
|
791
|
-
|
1214
|
+
|
1215
|
+
def process_embedding_response(
|
1216
|
+
response,
|
1217
|
+
request_model,
|
1218
|
+
pricing_info,
|
1219
|
+
server_port,
|
1220
|
+
server_address,
|
1221
|
+
environment,
|
1222
|
+
application_name,
|
1223
|
+
metrics,
|
1224
|
+
start_time,
|
1225
|
+
span,
|
1226
|
+
capture_message_content=False,
|
1227
|
+
disable_metrics=False,
|
1228
|
+
version="1.0.0",
|
1229
|
+
**kwargs,
|
1230
|
+
):
|
792
1231
|
"""
|
793
1232
|
Process embedding response and generate telemetry.
|
794
1233
|
"""
|
@@ -805,14 +1244,38 @@ def process_embedding_response(response, request_model, pricing_info, server_por
|
|
805
1244
|
scope._server_address, scope._server_port = server_address, server_port
|
806
1245
|
scope._kwargs = kwargs
|
807
1246
|
|
808
|
-
common_embedding_logic(
|
809
|
-
|
1247
|
+
common_embedding_logic(
|
1248
|
+
scope,
|
1249
|
+
request_model,
|
1250
|
+
pricing_info,
|
1251
|
+
environment,
|
1252
|
+
application_name,
|
1253
|
+
metrics,
|
1254
|
+
capture_message_content,
|
1255
|
+
disable_metrics,
|
1256
|
+
version,
|
1257
|
+
)
|
810
1258
|
|
811
1259
|
return response
|
812
1260
|
|
813
|
-
|
814
|
-
|
815
|
-
|
1261
|
+
|
1262
|
+
def process_image_response(
|
1263
|
+
response,
|
1264
|
+
request_model,
|
1265
|
+
pricing_info,
|
1266
|
+
server_port,
|
1267
|
+
server_address,
|
1268
|
+
environment,
|
1269
|
+
application_name,
|
1270
|
+
metrics,
|
1271
|
+
start_time,
|
1272
|
+
end_time,
|
1273
|
+
span,
|
1274
|
+
capture_message_content=False,
|
1275
|
+
disable_metrics=False,
|
1276
|
+
version="1.0.0",
|
1277
|
+
**kwargs,
|
1278
|
+
):
|
816
1279
|
"""
|
817
1280
|
Process image generation response and generate telemetry.
|
818
1281
|
"""
|
@@ -829,7 +1292,16 @@ def process_image_response(response, request_model, pricing_info, server_port, s
|
|
829
1292
|
scope._kwargs = kwargs
|
830
1293
|
scope._response_dict = response_dict
|
831
1294
|
|
832
|
-
common_image_logic(
|
833
|
-
|
1295
|
+
common_image_logic(
|
1296
|
+
scope,
|
1297
|
+
request_model,
|
1298
|
+
pricing_info,
|
1299
|
+
environment,
|
1300
|
+
application_name,
|
1301
|
+
metrics,
|
1302
|
+
capture_message_content,
|
1303
|
+
disable_metrics,
|
1304
|
+
version,
|
1305
|
+
)
|
834
1306
|
|
835
1307
|
return response
|