langtrace-python-sdk 2.1.28__py3-none-any.whl → 2.2.1__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.
- examples/cohere_example/chat.py +1 -0
- examples/cohere_example/chat_stream.py +3 -0
- examples/gemini_example/__init__.py +6 -0
- examples/gemini_example/function_tools.py +62 -0
- examples/gemini_example/main.py +91 -0
- examples/langchain_example/__init__.py +8 -0
- examples/langchain_example/groq_example.py +28 -15
- examples/ollama_example/basic.py +1 -0
- examples/openai_example/__init__.py +1 -0
- examples/openai_example/async_tool_calling_nonstreaming.py +1 -1
- examples/openai_example/chat_completion.py +1 -1
- examples/openai_example/embeddings_create.py +1 -0
- examples/openai_example/images_edit.py +2 -2
- examples/vertexai_example/__init__.py +6 -0
- examples/vertexai_example/main.py +214 -0
- langtrace_python_sdk/constants/instrumentation/common.py +2 -0
- langtrace_python_sdk/constants/instrumentation/gemini.py +12 -0
- langtrace_python_sdk/constants/instrumentation/vertexai.py +42 -0
- langtrace_python_sdk/instrumentation/__init__.py +4 -0
- langtrace_python_sdk/instrumentation/anthropic/patch.py +68 -96
- langtrace_python_sdk/instrumentation/chroma/patch.py +29 -29
- langtrace_python_sdk/instrumentation/cohere/patch.py +143 -242
- langtrace_python_sdk/instrumentation/gemini/__init__.py +3 -0
- langtrace_python_sdk/instrumentation/gemini/instrumentation.py +36 -0
- langtrace_python_sdk/instrumentation/gemini/patch.py +186 -0
- langtrace_python_sdk/instrumentation/groq/patch.py +82 -125
- langtrace_python_sdk/instrumentation/ollama/patch.py +62 -65
- langtrace_python_sdk/instrumentation/openai/patch.py +190 -494
- langtrace_python_sdk/instrumentation/qdrant/patch.py +6 -6
- langtrace_python_sdk/instrumentation/vertexai/__init__.py +3 -0
- langtrace_python_sdk/instrumentation/vertexai/instrumentation.py +33 -0
- langtrace_python_sdk/instrumentation/vertexai/patch.py +131 -0
- langtrace_python_sdk/langtrace.py +7 -1
- langtrace_python_sdk/utils/__init__.py +14 -3
- langtrace_python_sdk/utils/llm.py +311 -6
- langtrace_python_sdk/version.py +1 -1
- {langtrace_python_sdk-2.1.28.dist-info → langtrace_python_sdk-2.2.1.dist-info}/METADATA +26 -19
- {langtrace_python_sdk-2.1.28.dist-info → langtrace_python_sdk-2.2.1.dist-info}/RECORD +55 -36
- tests/anthropic/test_anthropic.py +28 -27
- tests/cohere/test_cohere_chat.py +36 -36
- tests/cohere/test_cohere_embed.py +12 -9
- tests/cohere/test_cohere_rerank.py +18 -11
- tests/groq/cassettes/test_async_chat_completion.yaml +113 -0
- tests/groq/cassettes/test_async_chat_completion_streaming.yaml +2232 -0
- tests/groq/cassettes/test_chat_completion.yaml +114 -0
- tests/groq/cassettes/test_chat_completion_streaming.yaml +2512 -0
- tests/groq/conftest.py +33 -0
- tests/groq/test_groq.py +142 -0
- tests/openai/cassettes/test_async_chat_completion_streaming.yaml +28 -28
- tests/openai/test_chat_completion.py +53 -67
- tests/openai/test_image_generation.py +47 -24
- tests/utils.py +40 -5
- {langtrace_python_sdk-2.1.28.dist-info → langtrace_python_sdk-2.2.1.dist-info}/WHEEL +0 -0
- {langtrace_python_sdk-2.1.28.dist-info → langtrace_python_sdk-2.2.1.dist-info}/entry_points.txt +0 -0
- {langtrace_python_sdk-2.1.28.dist-info → langtrace_python_sdk-2.2.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -16,22 +16,32 @@ limitations under the License.
|
|
|
16
16
|
|
|
17
17
|
import json
|
|
18
18
|
|
|
19
|
-
from
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
from langtrace.trace_attributes import (
|
|
20
|
+
LLMSpanAttributes,
|
|
21
|
+
SpanAttributes,
|
|
22
|
+
)
|
|
23
|
+
from langtrace_python_sdk.utils import set_span_attribute
|
|
24
|
+
from langtrace_python_sdk.utils.silently_fail import silently_fail
|
|
25
|
+
from opentelemetry import trace
|
|
22
26
|
from opentelemetry.trace import SpanKind
|
|
23
27
|
from opentelemetry.trace.status import Status, StatusCode
|
|
24
28
|
from opentelemetry.trace.propagation import set_span_in_context
|
|
25
|
-
from langtrace_python_sdk.constants import LANGTRACE_SDK_NAME
|
|
26
29
|
from langtrace_python_sdk.constants.instrumentation.common import (
|
|
27
|
-
LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
|
|
28
30
|
SERVICE_PROVIDERS,
|
|
29
31
|
)
|
|
30
32
|
from langtrace_python_sdk.constants.instrumentation.openai import APIS
|
|
31
33
|
from langtrace_python_sdk.utils.llm import (
|
|
32
34
|
calculate_prompt_tokens,
|
|
33
|
-
|
|
35
|
+
get_base_url,
|
|
36
|
+
get_extra_attributes,
|
|
37
|
+
get_langtrace_attributes,
|
|
38
|
+
get_llm_request_attributes,
|
|
39
|
+
get_llm_url,
|
|
34
40
|
get_tool_calls,
|
|
41
|
+
is_streaming,
|
|
42
|
+
set_event_completion,
|
|
43
|
+
StreamWrapper,
|
|
44
|
+
set_span_attributes,
|
|
35
45
|
)
|
|
36
46
|
from openai._types import NOT_GIVEN
|
|
37
47
|
|
|
@@ -42,44 +52,27 @@ def images_generate(original_method, version, tracer):
|
|
|
42
52
|
"""
|
|
43
53
|
|
|
44
54
|
def traced_method(wrapped, instance, args, kwargs):
|
|
45
|
-
base_url = (
|
|
46
|
-
str(instance._client._base_url)
|
|
47
|
-
if hasattr(instance, "_client") and hasattr(instance._client, "_base_url")
|
|
48
|
-
else ""
|
|
49
|
-
)
|
|
50
55
|
service_provider = SERVICE_PROVIDERS["OPENAI"]
|
|
51
|
-
extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
|
|
52
|
-
|
|
53
56
|
span_attributes = {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"url.full": base_url,
|
|
60
|
-
"llm.api": APIS["IMAGES_GENERATION"]["ENDPOINT"],
|
|
61
|
-
"llm.model": kwargs.get("model"),
|
|
62
|
-
"llm.stream": kwargs.get("stream"),
|
|
63
|
-
"llm.prompts": json.dumps(
|
|
64
|
-
[{"role": "user", "content": kwargs.get("prompt", [])}]
|
|
65
|
-
),
|
|
66
|
-
**(extra_attributes if extra_attributes is not None else {}),
|
|
57
|
+
**get_langtrace_attributes(version, service_provider, vendor_type="llm"),
|
|
58
|
+
**get_llm_request_attributes(kwargs),
|
|
59
|
+
**get_llm_url(instance),
|
|
60
|
+
SpanAttributes.LLM_PATH: APIS["IMAGES_GENERATION"]["ENDPOINT"],
|
|
61
|
+
**get_extra_attributes(),
|
|
67
62
|
}
|
|
68
63
|
|
|
69
64
|
attributes = LLMSpanAttributes(**span_attributes)
|
|
70
65
|
|
|
71
66
|
with tracer.start_as_current_span(
|
|
72
67
|
APIS["IMAGES_GENERATION"]["METHOD"],
|
|
73
|
-
kind=SpanKind.CLIENT,
|
|
68
|
+
kind=SpanKind.CLIENT.value,
|
|
74
69
|
context=set_span_in_context(trace.get_current_span()),
|
|
75
70
|
) as span:
|
|
76
|
-
|
|
77
|
-
if value is not None:
|
|
78
|
-
span.set_attribute(field, value)
|
|
71
|
+
set_span_attributes(span, attributes)
|
|
79
72
|
try:
|
|
80
73
|
# Attempt to call the original method
|
|
81
74
|
result = wrapped(*args, **kwargs)
|
|
82
|
-
if
|
|
75
|
+
if not is_streaming(kwargs):
|
|
83
76
|
data = (
|
|
84
77
|
result.data[0]
|
|
85
78
|
if hasattr(result, "data") and len(result.data) > 0
|
|
@@ -98,7 +91,7 @@ def images_generate(original_method, version, tracer):
|
|
|
98
91
|
},
|
|
99
92
|
}
|
|
100
93
|
]
|
|
101
|
-
span
|
|
94
|
+
set_event_completion(span, response)
|
|
102
95
|
|
|
103
96
|
span.set_status(StatusCode.OK)
|
|
104
97
|
return result
|
|
@@ -121,45 +114,28 @@ def async_images_generate(original_method, version, tracer):
|
|
|
121
114
|
"""
|
|
122
115
|
|
|
123
116
|
async def traced_method(wrapped, instance, args, kwargs):
|
|
124
|
-
base_url = (
|
|
125
|
-
str(instance._client._base_url)
|
|
126
|
-
if hasattr(instance, "_client") and hasattr(instance._client, "_base_url")
|
|
127
|
-
else ""
|
|
128
|
-
)
|
|
129
117
|
service_provider = SERVICE_PROVIDERS["OPENAI"]
|
|
130
|
-
extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
|
|
131
118
|
|
|
132
119
|
span_attributes = {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
"url.full": base_url,
|
|
139
|
-
"llm.api": APIS["IMAGES_GENERATION"]["ENDPOINT"],
|
|
140
|
-
"llm.model": kwargs.get("model"),
|
|
141
|
-
"llm.stream": kwargs.get("stream"),
|
|
142
|
-
"llm.prompts": json.dumps(
|
|
143
|
-
[{"role": "user", "content": kwargs.get("prompt", [])}]
|
|
144
|
-
),
|
|
145
|
-
**(extra_attributes if extra_attributes is not None else {}),
|
|
120
|
+
**get_langtrace_attributes(version, service_provider, vendor_type="llm"),
|
|
121
|
+
**get_llm_request_attributes(kwargs),
|
|
122
|
+
**get_llm_url(instance),
|
|
123
|
+
SpanAttributes.LLM_PATH: APIS["IMAGES_GENERATION"]["ENDPOINT"],
|
|
124
|
+
**get_extra_attributes(),
|
|
146
125
|
}
|
|
147
126
|
|
|
148
127
|
attributes = LLMSpanAttributes(**span_attributes)
|
|
149
128
|
|
|
150
129
|
with tracer.start_as_current_span(
|
|
151
130
|
APIS["IMAGES_GENERATION"]["METHOD"],
|
|
152
|
-
kind=SpanKind.CLIENT,
|
|
131
|
+
kind=SpanKind.CLIENT.value,
|
|
153
132
|
context=set_span_in_context(trace.get_current_span()),
|
|
154
133
|
) as span:
|
|
155
|
-
|
|
156
|
-
for field, value in items:
|
|
157
|
-
if value is not None:
|
|
158
|
-
span.set_attribute(field, value)
|
|
134
|
+
set_span_attributes(span, attributes)
|
|
159
135
|
try:
|
|
160
136
|
# Attempt to call the original method
|
|
161
137
|
result = await wrapped(*args, **kwargs)
|
|
162
|
-
if
|
|
138
|
+
if not is_streaming(kwargs):
|
|
163
139
|
data = (
|
|
164
140
|
result.data[0]
|
|
165
141
|
if hasattr(result, "data") and len(result.data) > 0
|
|
@@ -178,7 +154,7 @@ def async_images_generate(original_method, version, tracer):
|
|
|
178
154
|
},
|
|
179
155
|
}
|
|
180
156
|
]
|
|
181
|
-
span
|
|
157
|
+
set_event_completion(span, response)
|
|
182
158
|
|
|
183
159
|
span.set_status(StatusCode.OK)
|
|
184
160
|
return result
|
|
@@ -201,47 +177,26 @@ def images_edit(original_method, version, tracer):
|
|
|
201
177
|
"""
|
|
202
178
|
|
|
203
179
|
def traced_method(wrapped, instance, args, kwargs):
|
|
204
|
-
base_url = (
|
|
205
|
-
str(instance._client._base_url)
|
|
206
|
-
if hasattr(instance, "_client") and hasattr(instance._client, "_base_url")
|
|
207
|
-
else ""
|
|
208
|
-
)
|
|
209
180
|
service_provider = SERVICE_PROVIDERS["OPENAI"]
|
|
210
|
-
extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
|
|
211
181
|
|
|
212
182
|
span_attributes = {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
"llm.model": kwargs.get("model"),
|
|
221
|
-
"llm.response_format": kwargs.get("response_format"),
|
|
222
|
-
"llm.image.size": kwargs.get("size"),
|
|
223
|
-
"llm.prompts": json.dumps(
|
|
224
|
-
[
|
|
225
|
-
{
|
|
226
|
-
"role": kwargs.get("user", "user"),
|
|
227
|
-
"content": kwargs.get("prompt", []),
|
|
228
|
-
}
|
|
229
|
-
]
|
|
230
|
-
),
|
|
231
|
-
"llm.top_k": kwargs.get("n"),
|
|
232
|
-
**(extra_attributes if extra_attributes is not None else {}),
|
|
183
|
+
**get_langtrace_attributes(version, service_provider, vendor_type="llm"),
|
|
184
|
+
**get_llm_request_attributes(kwargs),
|
|
185
|
+
**get_llm_url(instance),
|
|
186
|
+
SpanAttributes.LLM_PATH: APIS["IMAGES_EDIT"]["ENDPOINT"],
|
|
187
|
+
SpanAttributes.LLM_RESPONSE_FORMAT: kwargs.get("response_format"),
|
|
188
|
+
SpanAttributes.LLM_IMAGE_SIZE: kwargs.get("size"),
|
|
189
|
+
**get_extra_attributes(),
|
|
233
190
|
}
|
|
234
191
|
|
|
235
192
|
attributes = LLMSpanAttributes(**span_attributes)
|
|
236
193
|
|
|
237
194
|
with tracer.start_as_current_span(
|
|
238
195
|
APIS["IMAGES_EDIT"]["METHOD"],
|
|
239
|
-
kind=SpanKind.CLIENT,
|
|
196
|
+
kind=SpanKind.CLIENT.value,
|
|
240
197
|
context=set_span_in_context(trace.get_current_span()),
|
|
241
198
|
) as span:
|
|
242
|
-
|
|
243
|
-
if value is not None:
|
|
244
|
-
span.set_attribute(field, value)
|
|
199
|
+
set_span_attributes(span, attributes)
|
|
245
200
|
try:
|
|
246
201
|
# Attempt to call the original method
|
|
247
202
|
result = wrapped(*args, **kwargs)
|
|
@@ -260,10 +215,7 @@ def images_edit(original_method, version, tracer):
|
|
|
260
215
|
}
|
|
261
216
|
)
|
|
262
217
|
|
|
263
|
-
span
|
|
264
|
-
name="response",
|
|
265
|
-
attributes={"llm.responses": json.dumps(response)},
|
|
266
|
-
)
|
|
218
|
+
set_event_completion(span, response)
|
|
267
219
|
|
|
268
220
|
span.set_status(StatusCode.OK)
|
|
269
221
|
return result
|
|
@@ -280,158 +232,17 @@ def images_edit(original_method, version, tracer):
|
|
|
280
232
|
return traced_method
|
|
281
233
|
|
|
282
234
|
|
|
283
|
-
class StreamWrapper:
|
|
284
|
-
def __init__(
|
|
285
|
-
self, stream, span, prompt_tokens, function_call=False, tool_calls=False
|
|
286
|
-
):
|
|
287
|
-
self.stream = stream
|
|
288
|
-
self.span = span
|
|
289
|
-
self.prompt_tokens = prompt_tokens
|
|
290
|
-
self.function_call = function_call
|
|
291
|
-
self.tool_calls = tool_calls
|
|
292
|
-
self.result_content = []
|
|
293
|
-
self.completion_tokens = 0
|
|
294
|
-
self._span_started = False
|
|
295
|
-
self._start_span()
|
|
296
|
-
|
|
297
|
-
def _start_span(self):
|
|
298
|
-
if not self._span_started:
|
|
299
|
-
self.span.add_event(Event.STREAM_START.value)
|
|
300
|
-
self._span_started = True
|
|
301
|
-
|
|
302
|
-
def _end_span(self):
|
|
303
|
-
if self._span_started:
|
|
304
|
-
self.span.add_event(Event.STREAM_END.value)
|
|
305
|
-
self.span.set_attribute(
|
|
306
|
-
"llm.token.counts",
|
|
307
|
-
json.dumps(
|
|
308
|
-
{
|
|
309
|
-
"input_tokens": self.prompt_tokens,
|
|
310
|
-
"output_tokens": self.completion_tokens,
|
|
311
|
-
"total_tokens": self.prompt_tokens + self.completion_tokens,
|
|
312
|
-
}
|
|
313
|
-
),
|
|
314
|
-
)
|
|
315
|
-
self.span.set_attribute(
|
|
316
|
-
"llm.responses",
|
|
317
|
-
json.dumps(
|
|
318
|
-
[
|
|
319
|
-
{
|
|
320
|
-
"role": "assistant",
|
|
321
|
-
"content": "".join(self.result_content),
|
|
322
|
-
}
|
|
323
|
-
]
|
|
324
|
-
),
|
|
325
|
-
)
|
|
326
|
-
self.span.set_status(StatusCode.OK)
|
|
327
|
-
self.span.end()
|
|
328
|
-
self._span_started = False
|
|
329
|
-
|
|
330
|
-
def __enter__(self):
|
|
331
|
-
self._start_span()
|
|
332
|
-
return self
|
|
333
|
-
|
|
334
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
335
|
-
self._end_span()
|
|
336
|
-
|
|
337
|
-
def __iter__(self):
|
|
338
|
-
self._start_span()
|
|
339
|
-
return self
|
|
340
|
-
|
|
341
|
-
def __aiter__(self):
|
|
342
|
-
self._start_span()
|
|
343
|
-
return self
|
|
344
|
-
|
|
345
|
-
async def __anext__(self):
|
|
346
|
-
try:
|
|
347
|
-
chunk = await self.stream.__anext__()
|
|
348
|
-
self.process_chunk(chunk)
|
|
349
|
-
return chunk
|
|
350
|
-
except StopIteration:
|
|
351
|
-
self._end_span()
|
|
352
|
-
raise
|
|
353
|
-
|
|
354
|
-
def __next__(self):
|
|
355
|
-
try:
|
|
356
|
-
chunk = next(self.stream)
|
|
357
|
-
self.process_chunk(chunk)
|
|
358
|
-
return chunk
|
|
359
|
-
except StopIteration:
|
|
360
|
-
self._end_span()
|
|
361
|
-
raise
|
|
362
|
-
|
|
363
|
-
def process_chunk(self, chunk):
|
|
364
|
-
if hasattr(chunk, "model") and chunk.model is not None:
|
|
365
|
-
self.span.set_attribute("llm.model", chunk.model)
|
|
366
|
-
if hasattr(chunk, "choices") and chunk.choices is not None:
|
|
367
|
-
content = []
|
|
368
|
-
if not self.function_call and not self.tool_calls:
|
|
369
|
-
for choice in chunk.choices:
|
|
370
|
-
if choice.delta and choice.delta.content is not None:
|
|
371
|
-
token_counts = estimate_tokens(choice.delta.content)
|
|
372
|
-
self.completion_tokens += token_counts
|
|
373
|
-
content = [choice.delta.content]
|
|
374
|
-
elif self.function_call:
|
|
375
|
-
for choice in chunk.choices:
|
|
376
|
-
if (
|
|
377
|
-
choice.delta
|
|
378
|
-
and choice.delta.function_call is not None
|
|
379
|
-
and choice.delta.function_call.arguments is not None
|
|
380
|
-
):
|
|
381
|
-
token_counts = estimate_tokens(
|
|
382
|
-
choice.delta.function_call.arguments
|
|
383
|
-
)
|
|
384
|
-
self.completion_tokens += token_counts
|
|
385
|
-
content = [choice.delta.function_call.arguments]
|
|
386
|
-
elif self.tool_calls:
|
|
387
|
-
for choice in chunk.choices:
|
|
388
|
-
if choice.delta and choice.delta.tool_calls is not None:
|
|
389
|
-
toolcalls = choice.delta.tool_calls
|
|
390
|
-
content = []
|
|
391
|
-
for tool_call in toolcalls:
|
|
392
|
-
if (
|
|
393
|
-
tool_call
|
|
394
|
-
and tool_call.function is not None
|
|
395
|
-
and tool_call.function.arguments is not None
|
|
396
|
-
):
|
|
397
|
-
token_counts = estimate_tokens(
|
|
398
|
-
tool_call.function.arguments
|
|
399
|
-
)
|
|
400
|
-
self.completion_tokens += token_counts
|
|
401
|
-
content.append(tool_call.function.arguments)
|
|
402
|
-
self.span.add_event(
|
|
403
|
-
Event.STREAM_OUTPUT.value,
|
|
404
|
-
{
|
|
405
|
-
"response": (
|
|
406
|
-
"".join(content)
|
|
407
|
-
if len(content) > 0 and content[0] is not None
|
|
408
|
-
else ""
|
|
409
|
-
)
|
|
410
|
-
},
|
|
411
|
-
)
|
|
412
|
-
if content:
|
|
413
|
-
self.result_content.append(content[0])
|
|
414
|
-
|
|
415
|
-
|
|
416
235
|
def chat_completions_create(original_method, version, tracer):
|
|
417
236
|
"""Wrap the `create` method of the `ChatCompletion` class to trace it."""
|
|
418
237
|
|
|
419
238
|
def traced_method(wrapped, instance, args, kwargs):
|
|
420
|
-
base_url = (
|
|
421
|
-
str(instance._client._base_url)
|
|
422
|
-
if hasattr(instance, "_client") and hasattr(instance._client, "_base_url")
|
|
423
|
-
else ""
|
|
424
|
-
)
|
|
425
239
|
service_provider = SERVICE_PROVIDERS["OPENAI"]
|
|
426
|
-
|
|
427
|
-
if "perplexity" in base_url:
|
|
240
|
+
if "perplexity" in get_base_url(instance):
|
|
428
241
|
service_provider = SERVICE_PROVIDERS["PPLX"]
|
|
429
|
-
elif "azure" in
|
|
242
|
+
elif "azure" in get_base_url(instance):
|
|
430
243
|
service_provider = SERVICE_PROVIDERS["AZURE"]
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
# handle tool calls in the kwargs
|
|
244
|
+
elif "groq" in get_base_url(instance):
|
|
245
|
+
service_provider = SERVICE_PROVIDERS["GROQ"]
|
|
435
246
|
llm_prompts = []
|
|
436
247
|
for item in kwargs.get("messages", []):
|
|
437
248
|
tools = get_tool_calls(item)
|
|
@@ -461,113 +272,31 @@ def chat_completions_create(original_method, version, tracer):
|
|
|
461
272
|
llm_prompts.append(item)
|
|
462
273
|
|
|
463
274
|
span_attributes = {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
"url.full": base_url,
|
|
470
|
-
"llm.api": APIS["CHAT_COMPLETION"]["ENDPOINT"],
|
|
471
|
-
"llm.prompts": json.dumps(llm_prompts),
|
|
472
|
-
"llm.stream": kwargs.get("stream"),
|
|
473
|
-
**(extra_attributes if extra_attributes is not None else {}),
|
|
275
|
+
**get_langtrace_attributes(version, service_provider, vendor_type="llm"),
|
|
276
|
+
**get_llm_request_attributes(kwargs, prompts=llm_prompts),
|
|
277
|
+
**get_llm_url(instance),
|
|
278
|
+
SpanAttributes.LLM_PATH: APIS["CHAT_COMPLETION"]["ENDPOINT"],
|
|
279
|
+
**get_extra_attributes(),
|
|
474
280
|
}
|
|
475
281
|
|
|
476
282
|
attributes = LLMSpanAttributes(**span_attributes)
|
|
477
283
|
|
|
478
|
-
tools = []
|
|
479
|
-
if (
|
|
480
|
-
kwargs.get("temperature") is not None
|
|
481
|
-
and kwargs.get("temperature") != NOT_GIVEN
|
|
482
|
-
):
|
|
483
|
-
attributes.llm_temperature = kwargs.get("temperature")
|
|
484
|
-
if kwargs.get("top_p") is not None and kwargs.get("top_p") != NOT_GIVEN:
|
|
485
|
-
attributes.llm_top_p = kwargs.get("top_p")
|
|
486
|
-
if kwargs.get("user") is not None and kwargs.get("user") != NOT_GIVEN:
|
|
487
|
-
attributes.llm_user = kwargs.get("user")
|
|
488
|
-
if kwargs.get("functions") is not None and kwargs.get("functions") != NOT_GIVEN:
|
|
489
|
-
for function in kwargs.get("functions"):
|
|
490
|
-
tools.append(json.dumps({"type": "function", "function": function}))
|
|
491
|
-
if kwargs.get("tools") is not None and kwargs.get("tools") != NOT_GIVEN:
|
|
492
|
-
tools.append(json.dumps(kwargs.get("tools")))
|
|
493
|
-
if len(tools) > 0:
|
|
494
|
-
attributes.llm_tools = json.dumps(tools)
|
|
495
|
-
|
|
496
|
-
# TODO(Karthik): Gotta figure out how to handle streaming with context
|
|
497
|
-
# with tracer.start_as_current_span(APIS["CHAT_COMPLETION"]["METHOD"],
|
|
498
|
-
# kind=SpanKind.CLIENT) as span:
|
|
499
284
|
span = tracer.start_span(
|
|
500
285
|
APIS["CHAT_COMPLETION"]["METHOD"],
|
|
501
|
-
kind=SpanKind.CLIENT,
|
|
286
|
+
kind=SpanKind.CLIENT.value,
|
|
502
287
|
context=set_span_in_context(trace.get_current_span()),
|
|
503
288
|
)
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
span.set_attribute(field, value)
|
|
289
|
+
_set_input_attributes(span, kwargs, attributes)
|
|
290
|
+
|
|
507
291
|
try:
|
|
508
|
-
# Attempt to call the original method
|
|
509
292
|
result = wrapped(*args, **kwargs)
|
|
510
|
-
if (
|
|
511
|
-
kwargs.get("stream") is False
|
|
512
|
-
or kwargs.get("stream") is None
|
|
513
|
-
or kwargs.get("stream") == NOT_GIVEN
|
|
514
|
-
):
|
|
515
|
-
span.set_attribute("llm.model", result.model)
|
|
516
|
-
if hasattr(result, "choices") and result.choices is not None:
|
|
517
|
-
responses = [
|
|
518
|
-
{
|
|
519
|
-
"role": (
|
|
520
|
-
choice.message.role
|
|
521
|
-
if choice.message and choice.message.role
|
|
522
|
-
else "assistant"
|
|
523
|
-
),
|
|
524
|
-
"content": extract_content(choice),
|
|
525
|
-
**(
|
|
526
|
-
{
|
|
527
|
-
"content_filter_results": choice[
|
|
528
|
-
"content_filter_results"
|
|
529
|
-
]
|
|
530
|
-
}
|
|
531
|
-
if "content_filter_results" in choice
|
|
532
|
-
else {}
|
|
533
|
-
),
|
|
534
|
-
}
|
|
535
|
-
for choice in result.choices
|
|
536
|
-
]
|
|
537
|
-
span.set_attribute("llm.responses", json.dumps(responses))
|
|
538
|
-
else:
|
|
539
|
-
responses = []
|
|
540
|
-
span.set_attribute("llm.responses", json.dumps(responses))
|
|
541
|
-
if (
|
|
542
|
-
hasattr(result, "system_fingerprint")
|
|
543
|
-
and result.system_fingerprint is not None
|
|
544
|
-
and result.system_fingerprint != NOT_GIVEN
|
|
545
|
-
):
|
|
546
|
-
span.set_attribute(
|
|
547
|
-
"llm.system.fingerprint", result.system_fingerprint
|
|
548
|
-
)
|
|
549
|
-
# Get the usage
|
|
550
|
-
if hasattr(result, "usage") and result.usage is not None:
|
|
551
|
-
usage = result.usage
|
|
552
|
-
if usage is not None:
|
|
553
|
-
usage_dict = {
|
|
554
|
-
"input_tokens": result.usage.prompt_tokens,
|
|
555
|
-
"output_tokens": usage.completion_tokens,
|
|
556
|
-
"total_tokens": usage.total_tokens,
|
|
557
|
-
}
|
|
558
|
-
span.set_attribute("llm.token.counts", json.dumps(usage_dict))
|
|
559
|
-
span.set_status(StatusCode.OK)
|
|
560
|
-
span.end()
|
|
561
|
-
return result
|
|
562
|
-
else:
|
|
563
|
-
# iterate over kwargs.get("messages", {}) and calculate the prompt tokens
|
|
293
|
+
if is_streaming(kwargs):
|
|
564
294
|
prompt_tokens = 0
|
|
565
295
|
for message in kwargs.get("messages", {}):
|
|
566
296
|
prompt_tokens += calculate_prompt_tokens(
|
|
567
297
|
json.dumps(message), kwargs.get("model")
|
|
568
298
|
)
|
|
569
299
|
|
|
570
|
-
# iterate over kwargs.get("functions") and calculate the prompt tokens
|
|
571
300
|
if (
|
|
572
301
|
kwargs.get("functions") is not None
|
|
573
302
|
and kwargs.get("functions") != NOT_GIVEN
|
|
@@ -584,6 +313,11 @@ def chat_completions_create(original_method, version, tracer):
|
|
|
584
313
|
function_call=kwargs.get("functions") is not None,
|
|
585
314
|
tool_calls=kwargs.get("tools") is not None,
|
|
586
315
|
)
|
|
316
|
+
else:
|
|
317
|
+
_set_response_attributes(span, kwargs, result)
|
|
318
|
+
span.set_status(StatusCode.OK)
|
|
319
|
+
span.end()
|
|
320
|
+
return result
|
|
587
321
|
|
|
588
322
|
except Exception as error:
|
|
589
323
|
span.record_exception(error)
|
|
@@ -591,7 +325,6 @@ def chat_completions_create(original_method, version, tracer):
|
|
|
591
325
|
span.end()
|
|
592
326
|
raise
|
|
593
327
|
|
|
594
|
-
# return the wrapped method
|
|
595
328
|
return traced_method
|
|
596
329
|
|
|
597
330
|
|
|
@@ -599,21 +332,11 @@ def async_chat_completions_create(original_method, version, tracer):
|
|
|
599
332
|
"""Wrap the `create` method of the `ChatCompletion` class to trace it."""
|
|
600
333
|
|
|
601
334
|
async def traced_method(wrapped, instance, args, kwargs):
|
|
602
|
-
base_url = (
|
|
603
|
-
str(instance._client._base_url)
|
|
604
|
-
if hasattr(instance, "_client") and hasattr(instance._client, "_base_url")
|
|
605
|
-
else ""
|
|
606
|
-
)
|
|
607
335
|
service_provider = SERVICE_PROVIDERS["OPENAI"]
|
|
608
|
-
|
|
609
|
-
if "perplexity" in base_url:
|
|
336
|
+
if "perplexity" in get_base_url(instance):
|
|
610
337
|
service_provider = SERVICE_PROVIDERS["PPLX"]
|
|
611
|
-
elif "azure" in
|
|
338
|
+
elif "azure" in get_base_url(instance):
|
|
612
339
|
service_provider = SERVICE_PROVIDERS["AZURE"]
|
|
613
|
-
|
|
614
|
-
extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
|
|
615
|
-
|
|
616
|
-
# handle tool calls in the kwargs
|
|
617
340
|
llm_prompts = []
|
|
618
341
|
for item in kwargs.get("messages", []):
|
|
619
342
|
tools = get_tool_calls(item)
|
|
@@ -637,117 +360,37 @@ def async_chat_completions_create(original_method, version, tracer):
|
|
|
637
360
|
else ""
|
|
638
361
|
),
|
|
639
362
|
}
|
|
640
|
-
tool_calls.append(tool_call_dict)
|
|
363
|
+
tool_calls.append(json.dumps(tool_call_dict))
|
|
641
364
|
llm_prompts.append(tool_calls)
|
|
642
365
|
else:
|
|
643
366
|
llm_prompts.append(item)
|
|
644
367
|
|
|
645
368
|
span_attributes = {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
"url.full": base_url,
|
|
652
|
-
"llm.api": APIS["CHAT_COMPLETION"]["ENDPOINT"],
|
|
653
|
-
"llm.prompts": json.dumps(llm_prompts),
|
|
654
|
-
"llm.stream": kwargs.get("stream"),
|
|
655
|
-
**(extra_attributes if extra_attributes is not None else {}),
|
|
369
|
+
**get_langtrace_attributes(version, service_provider, vendor_type="llm"),
|
|
370
|
+
**get_llm_request_attributes(kwargs, prompts=llm_prompts),
|
|
371
|
+
**get_llm_url(instance),
|
|
372
|
+
SpanAttributes.LLM_PATH: APIS["CHAT_COMPLETION"]["ENDPOINT"],
|
|
373
|
+
**get_extra_attributes(),
|
|
656
374
|
}
|
|
657
375
|
|
|
658
376
|
attributes = LLMSpanAttributes(**span_attributes)
|
|
659
377
|
|
|
660
|
-
tools = []
|
|
661
|
-
if (
|
|
662
|
-
kwargs.get("temperature") is not None
|
|
663
|
-
and kwargs.get("temperature") != NOT_GIVEN
|
|
664
|
-
):
|
|
665
|
-
attributes.llm_temperature = kwargs.get("temperature")
|
|
666
|
-
if kwargs.get("top_p") is not None and kwargs.get("top_p") != NOT_GIVEN:
|
|
667
|
-
attributes.llm_top_p = kwargs.get("top_p")
|
|
668
|
-
if kwargs.get("user") is not None and kwargs.get("user") != NOT_GIVEN:
|
|
669
|
-
attributes.llm_user = kwargs.get("user")
|
|
670
|
-
if kwargs.get("functions") is not None and kwargs.get("functions") != NOT_GIVEN:
|
|
671
|
-
for function in kwargs.get("functions"):
|
|
672
|
-
tools.append(json.dumps({"type": "function", "function": function}))
|
|
673
|
-
if kwargs.get("tools") is not None and kwargs.get("tools") != NOT_GIVEN:
|
|
674
|
-
tools.append(json.dumps(kwargs.get("tools")))
|
|
675
|
-
if len(tools) > 0:
|
|
676
|
-
attributes.llm_tools = json.dumps(tools)
|
|
677
|
-
|
|
678
|
-
# TODO(Karthik): Gotta figure out how to handle streaming with context
|
|
679
|
-
# with tracer.start_as_current_span(APIS["CHAT_COMPLETION"]["METHOD"],
|
|
680
|
-
# kind=SpanKind.CLIENT) as span:
|
|
681
378
|
span = tracer.start_span(
|
|
682
|
-
APIS["CHAT_COMPLETION"]["METHOD"],
|
|
379
|
+
APIS["CHAT_COMPLETION"]["METHOD"],
|
|
380
|
+
kind=SpanKind.CLIENT.value,
|
|
381
|
+
context=set_span_in_context(trace.get_current_span()),
|
|
683
382
|
)
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
span.set_attribute(field, value)
|
|
383
|
+
_set_input_attributes(span, kwargs, attributes)
|
|
384
|
+
|
|
687
385
|
try:
|
|
688
|
-
# Attempt to call the original method
|
|
689
386
|
result = await wrapped(*args, **kwargs)
|
|
690
|
-
if (
|
|
691
|
-
kwargs.get("stream") is False
|
|
692
|
-
or kwargs.get("stream") is None
|
|
693
|
-
or kwargs.get("stream") == NOT_GIVEN
|
|
694
|
-
):
|
|
695
|
-
span.set_attribute("llm.model", result.model)
|
|
696
|
-
if hasattr(result, "choices") and result.choices is not None:
|
|
697
|
-
responses = [
|
|
698
|
-
{
|
|
699
|
-
"role": (
|
|
700
|
-
choice.message.role
|
|
701
|
-
if choice.message and choice.message.role
|
|
702
|
-
else "assistant"
|
|
703
|
-
),
|
|
704
|
-
"content": extract_content(choice),
|
|
705
|
-
**(
|
|
706
|
-
{
|
|
707
|
-
"content_filter_results": choice[
|
|
708
|
-
"content_filter_results"
|
|
709
|
-
]
|
|
710
|
-
}
|
|
711
|
-
if "content_filter_results" in choice
|
|
712
|
-
else {}
|
|
713
|
-
),
|
|
714
|
-
}
|
|
715
|
-
for choice in result.choices
|
|
716
|
-
]
|
|
717
|
-
span.set_attribute("llm.responses", json.dumps(responses))
|
|
718
|
-
else:
|
|
719
|
-
responses = []
|
|
720
|
-
span.set_attribute("llm.responses", json.dumps(responses))
|
|
721
|
-
if (
|
|
722
|
-
hasattr(result, "system_fingerprint")
|
|
723
|
-
and result.system_fingerprint is not None
|
|
724
|
-
and result.system_fingerprint != NOT_GIVEN
|
|
725
|
-
):
|
|
726
|
-
span.set_attribute(
|
|
727
|
-
"llm.system.fingerprint", result.system_fingerprint
|
|
728
|
-
)
|
|
729
|
-
# Get the usage
|
|
730
|
-
if hasattr(result, "usage") and result.usage is not None:
|
|
731
|
-
usage = result.usage
|
|
732
|
-
if usage is not None:
|
|
733
|
-
usage_dict = {
|
|
734
|
-
"input_tokens": result.usage.prompt_tokens,
|
|
735
|
-
"output_tokens": usage.completion_tokens,
|
|
736
|
-
"total_tokens": usage.total_tokens,
|
|
737
|
-
}
|
|
738
|
-
span.set_attribute("llm.token.counts", json.dumps(usage_dict))
|
|
739
|
-
span.set_status(StatusCode.OK)
|
|
740
|
-
span.end()
|
|
741
|
-
return result
|
|
742
|
-
else:
|
|
743
|
-
# iterate over kwargs.get("messages", {}) and calculate the prompt tokens
|
|
387
|
+
if is_streaming(kwargs):
|
|
744
388
|
prompt_tokens = 0
|
|
745
389
|
for message in kwargs.get("messages", {}):
|
|
746
390
|
prompt_tokens += calculate_prompt_tokens(
|
|
747
391
|
json.dumps(message), kwargs.get("model")
|
|
748
392
|
)
|
|
749
393
|
|
|
750
|
-
# iterate over kwargs.get("functions") and calculate the prompt tokens
|
|
751
394
|
if (
|
|
752
395
|
kwargs.get("functions") is not None
|
|
753
396
|
and kwargs.get("functions") != NOT_GIVEN
|
|
@@ -764,6 +407,11 @@ def async_chat_completions_create(original_method, version, tracer):
|
|
|
764
407
|
function_call=kwargs.get("functions") is not None,
|
|
765
408
|
tool_calls=kwargs.get("tools") is not None,
|
|
766
409
|
)
|
|
410
|
+
else:
|
|
411
|
+
_set_response_attributes(span, kwargs, result)
|
|
412
|
+
span.set_status(StatusCode.OK)
|
|
413
|
+
span.end()
|
|
414
|
+
return result
|
|
767
415
|
|
|
768
416
|
except Exception as error:
|
|
769
417
|
span.record_exception(error)
|
|
@@ -771,7 +419,6 @@ def async_chat_completions_create(original_method, version, tracer):
|
|
|
771
419
|
span.end()
|
|
772
420
|
raise
|
|
773
421
|
|
|
774
|
-
# return the wrapped method
|
|
775
422
|
return traced_method
|
|
776
423
|
|
|
777
424
|
|
|
@@ -781,51 +428,39 @@ def embeddings_create(original_method, version, tracer):
|
|
|
781
428
|
"""
|
|
782
429
|
|
|
783
430
|
def traced_method(wrapped, instance, args, kwargs):
|
|
784
|
-
base_url = (
|
|
785
|
-
str(instance._client._base_url)
|
|
786
|
-
if hasattr(instance, "_client") and hasattr(instance._client, "_base_url")
|
|
787
|
-
else ""
|
|
788
|
-
)
|
|
789
|
-
|
|
790
431
|
service_provider = SERVICE_PROVIDERS["OPENAI"]
|
|
791
|
-
extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
|
|
792
432
|
|
|
793
433
|
span_attributes = {
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
"llm.api": APIS["EMBEDDINGS_CREATE"]["ENDPOINT"],
|
|
801
|
-
"llm.model": kwargs.get("model"),
|
|
802
|
-
"llm.prompts": "",
|
|
803
|
-
"llm.embedding_inputs": json.dumps([kwargs.get("input", "")]),
|
|
804
|
-
**(extra_attributes if extra_attributes is not None else {}),
|
|
434
|
+
**get_langtrace_attributes(version, service_provider, vendor_type="llm"),
|
|
435
|
+
**get_llm_request_attributes(kwargs),
|
|
436
|
+
**get_llm_url(instance),
|
|
437
|
+
SpanAttributes.LLM_PATH: APIS["EMBEDDINGS_CREATE"]["ENDPOINT"],
|
|
438
|
+
SpanAttributes.LLM_REQUEST_DIMENSIONS: kwargs.get("dimensions"),
|
|
439
|
+
**get_extra_attributes(),
|
|
805
440
|
}
|
|
806
441
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
442
|
+
encoding_format = kwargs.get("encoding_format")
|
|
443
|
+
if encoding_format is not None:
|
|
444
|
+
if not isinstance(encoding_format, list):
|
|
445
|
+
encoding_format = [encoding_format]
|
|
446
|
+
span_attributes[SpanAttributes.LLM_REQUEST_ENCODING_FORMATS] = (
|
|
447
|
+
encoding_format
|
|
810
448
|
)
|
|
811
449
|
|
|
812
|
-
|
|
813
|
-
|
|
450
|
+
if kwargs.get("input") is not None:
|
|
451
|
+
span_attributes[SpanAttributes.LLM_REQUEST_EMBEDDING_INPUTS] = json.dumps(
|
|
452
|
+
[kwargs.get("input", "")]
|
|
453
|
+
)
|
|
814
454
|
|
|
815
|
-
|
|
816
|
-
attributes["llm.dimensions"] = kwargs.get("dimensions")
|
|
817
|
-
if kwargs.get("user") is not None:
|
|
818
|
-
attributes["llm.user"] = kwargs.get("user")
|
|
455
|
+
attributes = LLMSpanAttributes(**span_attributes)
|
|
819
456
|
|
|
820
457
|
with tracer.start_as_current_span(
|
|
821
458
|
APIS["EMBEDDINGS_CREATE"]["METHOD"],
|
|
822
|
-
kind=SpanKind.CLIENT,
|
|
459
|
+
kind=SpanKind.CLIENT.value,
|
|
823
460
|
context=set_span_in_context(trace.get_current_span()),
|
|
824
461
|
) as span:
|
|
825
462
|
|
|
826
|
-
|
|
827
|
-
if value is not None:
|
|
828
|
-
span.set_attribute(field, value)
|
|
463
|
+
set_span_attributes(span, attributes)
|
|
829
464
|
try:
|
|
830
465
|
# Attempt to call the original method
|
|
831
466
|
result = wrapped(*args, **kwargs)
|
|
@@ -850,49 +485,39 @@ def async_embeddings_create(original_method, version, tracer):
|
|
|
850
485
|
"""
|
|
851
486
|
|
|
852
487
|
async def traced_method(wrapped, instance, args, kwargs):
|
|
853
|
-
base_url = (
|
|
854
|
-
str(instance._client._base_url)
|
|
855
|
-
if hasattr(instance, "_client") and hasattr(instance._client, "_base_url")
|
|
856
|
-
else ""
|
|
857
|
-
)
|
|
858
488
|
|
|
859
489
|
service_provider = SERVICE_PROVIDERS["OPENAI"]
|
|
860
|
-
extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
|
|
861
490
|
|
|
862
491
|
span_attributes = {
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
"url.full": base_url,
|
|
869
|
-
"llm.api": APIS["EMBEDDINGS_CREATE"]["ENDPOINT"],
|
|
870
|
-
"llm.model": kwargs.get("model"),
|
|
871
|
-
"llm.prompts": json.dumps(
|
|
872
|
-
[{"role": "user", "content": kwargs.get("input", "")}]
|
|
873
|
-
),
|
|
874
|
-
**(extra_attributes if extra_attributes is not None else {}),
|
|
492
|
+
**get_langtrace_attributes(version, service_provider, vendor_type="llm"),
|
|
493
|
+
**get_llm_request_attributes(kwargs),
|
|
494
|
+
SpanAttributes.LLM_PATH: APIS["EMBEDDINGS_CREATE"]["ENDPOINT"],
|
|
495
|
+
SpanAttributes.LLM_REQUEST_DIMENSIONS: kwargs.get("dimensions"),
|
|
496
|
+
**get_extra_attributes(),
|
|
875
497
|
}
|
|
876
498
|
|
|
877
499
|
attributes = LLMSpanAttributes(**span_attributes)
|
|
878
|
-
kwargs.get("encoding_format")
|
|
879
500
|
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
501
|
+
encoding_format = kwargs.get("encoding_format")
|
|
502
|
+
if encoding_format is not None:
|
|
503
|
+
if not isinstance(encoding_format, list):
|
|
504
|
+
encoding_format = [encoding_format]
|
|
505
|
+
span_attributes[SpanAttributes.LLM_REQUEST_ENCODING_FORMATS] = (
|
|
506
|
+
encoding_format
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
if kwargs.get("input") is not None:
|
|
510
|
+
span_attributes[SpanAttributes.LLM_REQUEST_EMBEDDING_INPUTS] = json.dumps(
|
|
511
|
+
[kwargs.get("input", "")]
|
|
512
|
+
)
|
|
886
513
|
|
|
887
514
|
with tracer.start_as_current_span(
|
|
888
515
|
APIS["EMBEDDINGS_CREATE"]["METHOD"],
|
|
889
|
-
kind=SpanKind.CLIENT,
|
|
516
|
+
kind=SpanKind.CLIENT.value,
|
|
890
517
|
context=set_span_in_context(trace.get_current_span()),
|
|
891
518
|
) as span:
|
|
892
519
|
|
|
893
|
-
|
|
894
|
-
if value is not None:
|
|
895
|
-
span.set_attribute(field, value)
|
|
520
|
+
set_span_attributes(span, attributes)
|
|
896
521
|
try:
|
|
897
522
|
# Attempt to call the original method
|
|
898
523
|
result = await wrapped(*args, **kwargs)
|
|
@@ -953,3 +578,74 @@ def extract_content(choice):
|
|
|
953
578
|
# Return an empty string if none of the above conditions are met
|
|
954
579
|
else:
|
|
955
580
|
return ""
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
@silently_fail
|
|
584
|
+
def _set_input_attributes(span, kwargs, attributes):
|
|
585
|
+
|
|
586
|
+
for field, value in attributes.model_dump(by_alias=True).items():
|
|
587
|
+
set_span_attribute(span, field, value)
|
|
588
|
+
|
|
589
|
+
if kwargs.get("functions") is not None and kwargs.get("functions") != NOT_GIVEN:
|
|
590
|
+
tools = []
|
|
591
|
+
for function in kwargs.get("functions"):
|
|
592
|
+
tools.append(json.dumps({"type": "function", "function": function}))
|
|
593
|
+
|
|
594
|
+
if kwargs.get("tools") is not None and kwargs.get("tools") != NOT_GIVEN:
|
|
595
|
+
tools.append(json.dumps(kwargs.get("tools")))
|
|
596
|
+
|
|
597
|
+
if tools:
|
|
598
|
+
set_span_attribute(span, SpanAttributes.LLM_TOOLS, json.dumps(tools))
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
@silently_fail
|
|
602
|
+
def _set_response_attributes(span, kwargs, result):
|
|
603
|
+
set_span_attribute(span, SpanAttributes.LLM_RESPONSE_MODEL, result.model)
|
|
604
|
+
if hasattr(result, "choices") and result.choices is not None:
|
|
605
|
+
responses = [
|
|
606
|
+
{
|
|
607
|
+
"role": (
|
|
608
|
+
choice.message.role
|
|
609
|
+
if choice.message and choice.message.role
|
|
610
|
+
else "assistant"
|
|
611
|
+
),
|
|
612
|
+
"content": extract_content(choice),
|
|
613
|
+
**(
|
|
614
|
+
{"content_filter_results": choice["content_filter_results"]}
|
|
615
|
+
if "content_filter_results" in choice
|
|
616
|
+
else {}
|
|
617
|
+
),
|
|
618
|
+
}
|
|
619
|
+
for choice in result.choices
|
|
620
|
+
]
|
|
621
|
+
set_event_completion(span, responses)
|
|
622
|
+
|
|
623
|
+
if (
|
|
624
|
+
hasattr(result, "system_fingerprint")
|
|
625
|
+
and result.system_fingerprint is not None
|
|
626
|
+
and result.system_fingerprint != NOT_GIVEN
|
|
627
|
+
):
|
|
628
|
+
set_span_attribute(
|
|
629
|
+
span,
|
|
630
|
+
SpanAttributes.LLM_SYSTEM_FINGERPRINT,
|
|
631
|
+
result.system_fingerprint,
|
|
632
|
+
)
|
|
633
|
+
# Get the usage
|
|
634
|
+
if hasattr(result, "usage") and result.usage is not None:
|
|
635
|
+
usage = result.usage
|
|
636
|
+
if usage is not None:
|
|
637
|
+
set_span_attribute(
|
|
638
|
+
span,
|
|
639
|
+
SpanAttributes.LLM_USAGE_PROMPT_TOKENS,
|
|
640
|
+
result.usage.prompt_tokens,
|
|
641
|
+
)
|
|
642
|
+
set_span_attribute(
|
|
643
|
+
span,
|
|
644
|
+
SpanAttributes.LLM_USAGE_COMPLETION_TOKENS,
|
|
645
|
+
result.usage.completion_tokens,
|
|
646
|
+
)
|
|
647
|
+
set_span_attribute(
|
|
648
|
+
span,
|
|
649
|
+
SpanAttributes.LLM_USAGE_TOTAL_TOKENS,
|
|
650
|
+
result.usage.total_tokens,
|
|
651
|
+
)
|