langtrace-python-sdk 1.3.6__py3-none-any.whl → 2.0.0__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/anthropic_example/completion.py +6 -6
- examples/cohere_example/chat.py +5 -4
- examples/cohere_example/chat_stream.py +2 -4
- examples/cohere_example/{embed_create.py → embed.py} +4 -3
- examples/cohere_example/rerank.py +31 -0
- examples/cohere_example/tools.py +40 -0
- examples/openai_example/chat_completion.py +41 -0
- examples/{openai → openai_example}/embeddings_create.py +3 -2
- examples/{openai → openai_example}/function_calling.py +3 -5
- examples/{openai → openai_example}/images_generate.py +1 -1
- examples/openai_example/tool_calling.py +67 -0
- examples/{openai → openai_example}/tool_calling_nonstreaming.py +2 -1
- langtrace_python_sdk/__init__.py +14 -1
- langtrace_python_sdk/constants/instrumentation/cohere.py +6 -1
- langtrace_python_sdk/instrumentation/anthropic/instrumentation.py +16 -4
- langtrace_python_sdk/instrumentation/anthropic/patch.py +26 -6
- langtrace_python_sdk/instrumentation/chroma/instrumentation.py +14 -2
- langtrace_python_sdk/instrumentation/chroma/patch.py +16 -4
- langtrace_python_sdk/instrumentation/cohere/instrumentation.py +24 -7
- langtrace_python_sdk/instrumentation/cohere/patch.py +295 -95
- langtrace_python_sdk/instrumentation/langchain/instrumentation.py +14 -3
- langtrace_python_sdk/instrumentation/langchain/patch.py +16 -4
- langtrace_python_sdk/instrumentation/langchain_community/instrumentation.py +15 -2
- langtrace_python_sdk/instrumentation/langchain_community/patch.py +20 -3
- langtrace_python_sdk/instrumentation/langchain_core/instrumentation.py +14 -4
- langtrace_python_sdk/instrumentation/langchain_core/patch.py +19 -7
- langtrace_python_sdk/instrumentation/llamaindex/instrumentation.py +15 -11
- langtrace_python_sdk/instrumentation/llamaindex/patch.py +20 -10
- langtrace_python_sdk/instrumentation/openai/instrumentation.py +20 -9
- langtrace_python_sdk/instrumentation/openai/patch.py +112 -78
- langtrace_python_sdk/instrumentation/pinecone/instrumentation.py +14 -3
- langtrace_python_sdk/instrumentation/pinecone/patch.py +17 -4
- langtrace_python_sdk/langtrace.py +40 -35
- langtrace_python_sdk/utils/llm.py +17 -4
- langtrace_python_sdk/utils/with_root_span.py +21 -5
- langtrace_python_sdk/version.py +1 -1
- {langtrace_python_sdk-1.3.6.dist-info → langtrace_python_sdk-2.0.0.dist-info}/METADATA +2 -2
- {langtrace_python_sdk-1.3.6.dist-info → langtrace_python_sdk-2.0.0.dist-info}/RECORD +53 -45
- tests/anthropic/cassettes/test_anthropic.yaml +85 -0
- tests/anthropic/cassettes/test_anthropic_streaming.yaml +456 -0
- tests/anthropic/cassettes/test_async_anthropic_streaming.yaml +328 -0
- tests/anthropic/conftest.py +38 -0
- tests/anthropic/test_anthropic.py +108 -72
- tests/conftest.py +17 -0
- tests/openai/conftest.py +5 -13
- tests/openai/test_chat_completion.py +21 -0
- tests/openai/test_image_generation.py +20 -8
- examples/openai/chat_completion.py +0 -58
- /examples/{openai → openai_example}/__init__.py +0 -0
- /examples/{openai → openai_example}/async_tool_calling_nonstreaming.py +0 -0
- /examples/{openai → openai_example}/async_tool_calling_streaming.py +0 -0
- /examples/{openai → openai_example}/tool_calling_streaming.py +0 -0
- {langtrace_python_sdk-1.3.6.dist-info → langtrace_python_sdk-2.0.0.dist-info}/WHEEL +0 -0
- {langtrace_python_sdk-1.3.6.dist-info → langtrace_python_sdk-2.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Copyright (c) 2024 Scale3 Labs
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
"""
|
|
3
16
|
|
|
4
17
|
import json
|
|
5
18
|
|
|
@@ -10,14 +23,11 @@ from opentelemetry.trace.status import Status, StatusCode
|
|
|
10
23
|
|
|
11
24
|
from langtrace_python_sdk.constants.instrumentation.cohere import APIS
|
|
12
25
|
from langtrace_python_sdk.constants.instrumentation.common import (
|
|
13
|
-
LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
|
|
14
|
-
SERVICE_PROVIDERS,
|
|
15
|
-
)
|
|
16
|
-
from langtrace_python_sdk.utils.llm import estimate_tokens
|
|
26
|
+
LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY, SERVICE_PROVIDERS)
|
|
17
27
|
|
|
18
28
|
|
|
19
|
-
def
|
|
20
|
-
"""Wrap the `
|
|
29
|
+
def rerank(original_method, version, tracer):
|
|
30
|
+
"""Wrap the `rerank` method."""
|
|
21
31
|
|
|
22
32
|
def traced_method(wrapped, instance, args, kwargs):
|
|
23
33
|
service_provider = SERVICE_PROVIDERS["COHERE"]
|
|
@@ -29,10 +39,105 @@ def embed_create(original_method, version, tracer):
|
|
|
29
39
|
"langtrace.service.type": "llm",
|
|
30
40
|
"langtrace.service.version": version,
|
|
31
41
|
"langtrace.version": "1.0.0",
|
|
32
|
-
"url.full": APIS["
|
|
33
|
-
"llm.api": APIS["
|
|
42
|
+
"url.full": APIS["RERANK"]["URL"],
|
|
43
|
+
"llm.api": APIS["RERANK"]["ENDPOINT"],
|
|
34
44
|
"llm.model": kwargs.get("model"),
|
|
35
45
|
"llm.prompts": "",
|
|
46
|
+
"llm.documents": json.dumps(kwargs.get("documents")),
|
|
47
|
+
"llm.retrieval.query": kwargs.get("query"),
|
|
48
|
+
**(extra_attributes if extra_attributes is not None else {}),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
attributes = LLMSpanAttributes(**span_attributes)
|
|
52
|
+
|
|
53
|
+
if kwargs.get("top_n") is not None:
|
|
54
|
+
attributes.llm_top_k = kwargs.get("top_n")
|
|
55
|
+
|
|
56
|
+
if kwargs.get("user") is not None:
|
|
57
|
+
attributes.llm_user = kwargs.get("user")
|
|
58
|
+
|
|
59
|
+
span = tracer.start_span(APIS["RERANK"]["METHOD"], kind=SpanKind.CLIENT)
|
|
60
|
+
for field, value in attributes.model_dump(by_alias=True).items():
|
|
61
|
+
if value is not None:
|
|
62
|
+
span.set_attribute(field, value)
|
|
63
|
+
try:
|
|
64
|
+
# Attempt to call the original method
|
|
65
|
+
result = wrapped(*args, **kwargs)
|
|
66
|
+
|
|
67
|
+
if hasattr(result, "results") and result.results is not None:
|
|
68
|
+
results = []
|
|
69
|
+
for _, doc in enumerate(result.results):
|
|
70
|
+
results.append(doc.json())
|
|
71
|
+
span.set_attribute("llm.retrieval.results", json.dumps(results))
|
|
72
|
+
|
|
73
|
+
if (hasattr(result, "response_id")) and (result.response_id is not None):
|
|
74
|
+
span.set_attribute("llm.response_id", result.response_id)
|
|
75
|
+
|
|
76
|
+
if hasattr(result, "meta") and result.meta is not None:
|
|
77
|
+
if (
|
|
78
|
+
hasattr(result.meta, "billed_units")
|
|
79
|
+
and result.meta.billed_units is not None
|
|
80
|
+
):
|
|
81
|
+
usage = result.meta.billed_units
|
|
82
|
+
if usage is not None:
|
|
83
|
+
usage_dict = {
|
|
84
|
+
"input_tokens": (
|
|
85
|
+
usage.input_tokens
|
|
86
|
+
if usage.input_tokens is not None
|
|
87
|
+
else 0
|
|
88
|
+
),
|
|
89
|
+
"output_tokens": (
|
|
90
|
+
usage.output_tokens
|
|
91
|
+
if usage.output_tokens is not None
|
|
92
|
+
else 0
|
|
93
|
+
),
|
|
94
|
+
"total_tokens": (
|
|
95
|
+
usage.input_tokens + usage.output_tokens
|
|
96
|
+
if usage.input_tokens is not None
|
|
97
|
+
and usage.output_tokens is not None
|
|
98
|
+
else 0
|
|
99
|
+
),
|
|
100
|
+
"search_units": (
|
|
101
|
+
usage.search_units
|
|
102
|
+
if usage.search_units is not None
|
|
103
|
+
else 0
|
|
104
|
+
),
|
|
105
|
+
}
|
|
106
|
+
span.set_attribute(
|
|
107
|
+
"llm.token.counts", json.dumps(usage_dict)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
span.set_status(StatusCode.OK)
|
|
111
|
+
span.end()
|
|
112
|
+
return result
|
|
113
|
+
|
|
114
|
+
except Exception as error:
|
|
115
|
+
span.record_exception(error)
|
|
116
|
+
span.set_status(Status(StatusCode.ERROR, str(error)))
|
|
117
|
+
span.end()
|
|
118
|
+
raise
|
|
119
|
+
|
|
120
|
+
return traced_method
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def embed(original_method, version, tracer):
|
|
124
|
+
"""Wrap the `embed` method."""
|
|
125
|
+
|
|
126
|
+
def traced_method(wrapped, instance, args, kwargs):
|
|
127
|
+
service_provider = SERVICE_PROVIDERS["COHERE"]
|
|
128
|
+
extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
|
|
129
|
+
|
|
130
|
+
span_attributes = {
|
|
131
|
+
"langtrace.sdk.name": "langtrace-python-sdk",
|
|
132
|
+
"langtrace.service.name": service_provider,
|
|
133
|
+
"langtrace.service.type": "llm",
|
|
134
|
+
"langtrace.service.version": version,
|
|
135
|
+
"langtrace.version": "1.0.0",
|
|
136
|
+
"url.full": APIS["EMBED"]["URL"],
|
|
137
|
+
"llm.api": APIS["EMBED"]["ENDPOINT"],
|
|
138
|
+
"llm.model": kwargs.get("model"),
|
|
139
|
+
"llm.prompts": "",
|
|
140
|
+
"llm.embedding_inputs": json.dumps(kwargs.get("texts")),
|
|
36
141
|
"llm.embedding_dataset_id": kwargs.get("dataset_id"),
|
|
37
142
|
"llm.embedding_input_type": kwargs.get("input_type"),
|
|
38
143
|
"llm.embedding_job_name": kwargs.get("name"),
|
|
@@ -44,13 +149,48 @@ def embed_create(original_method, version, tracer):
|
|
|
44
149
|
if kwargs.get("user") is not None:
|
|
45
150
|
attributes.llm_user = kwargs.get("user")
|
|
46
151
|
|
|
47
|
-
span = tracer.start_span(APIS["
|
|
152
|
+
span = tracer.start_span(APIS["EMBED"]["METHOD"], kind=SpanKind.CLIENT)
|
|
48
153
|
for field, value in attributes.model_dump(by_alias=True).items():
|
|
49
154
|
if value is not None:
|
|
50
155
|
span.set_attribute(field, value)
|
|
51
156
|
try:
|
|
52
157
|
# Attempt to call the original method
|
|
53
158
|
result = wrapped(*args, **kwargs)
|
|
159
|
+
|
|
160
|
+
if hasattr(result, "meta") and result.meta is not None:
|
|
161
|
+
if (
|
|
162
|
+
hasattr(result.meta, "billed_units")
|
|
163
|
+
and result.meta.billed_units is not None
|
|
164
|
+
):
|
|
165
|
+
usage = result.meta.billed_units
|
|
166
|
+
if usage is not None:
|
|
167
|
+
usage_dict = {
|
|
168
|
+
"input_tokens": (
|
|
169
|
+
usage.input_tokens
|
|
170
|
+
if usage.input_tokens is not None
|
|
171
|
+
else 0
|
|
172
|
+
),
|
|
173
|
+
"output_tokens": (
|
|
174
|
+
usage.output_tokens
|
|
175
|
+
if usage.output_tokens is not None
|
|
176
|
+
else 0
|
|
177
|
+
),
|
|
178
|
+
"total_tokens": (
|
|
179
|
+
usage.input_tokens + usage.output_tokens
|
|
180
|
+
if usage.input_tokens is not None
|
|
181
|
+
and usage.output_tokens is not None
|
|
182
|
+
else 0
|
|
183
|
+
),
|
|
184
|
+
"search_units": (
|
|
185
|
+
usage.search_units
|
|
186
|
+
if usage.search_units is not None
|
|
187
|
+
else 0
|
|
188
|
+
),
|
|
189
|
+
}
|
|
190
|
+
span.set_attribute(
|
|
191
|
+
"llm.token.counts", json.dumps(usage_dict)
|
|
192
|
+
)
|
|
193
|
+
|
|
54
194
|
span.set_status(StatusCode.OK)
|
|
55
195
|
span.end()
|
|
56
196
|
return result
|
|
@@ -71,32 +211,33 @@ def chat_create(original_method, version, tracer):
|
|
|
71
211
|
service_provider = SERVICE_PROVIDERS["COHERE"]
|
|
72
212
|
|
|
73
213
|
message = kwargs.get("message", "")
|
|
74
|
-
prompts =
|
|
214
|
+
prompts = [{"role": "USER", "content": message}]
|
|
215
|
+
system_prompts = []
|
|
216
|
+
history = []
|
|
75
217
|
preamble = kwargs.get("preamble")
|
|
76
218
|
if preamble:
|
|
77
|
-
|
|
78
|
-
[{"role": "system", "content": preamble}]
|
|
79
|
-
+ [{"role": "USER", "content": message}]
|
|
80
|
-
)
|
|
219
|
+
system_prompts = [{"role": "system", "content": preamble}]
|
|
81
220
|
|
|
82
221
|
chat_history = kwargs.get("chat_history")
|
|
83
222
|
if chat_history:
|
|
84
223
|
history = [
|
|
85
224
|
{
|
|
86
|
-
"
|
|
87
|
-
"role"
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
"
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
),
|
|
95
|
-
}
|
|
225
|
+
"role": (
|
|
226
|
+
item.get("role") if item.get("role") is not None else "USER"
|
|
227
|
+
),
|
|
228
|
+
"content": (
|
|
229
|
+
item.get("message")
|
|
230
|
+
if item.get("message") is not None
|
|
231
|
+
else ""
|
|
232
|
+
),
|
|
96
233
|
}
|
|
97
234
|
for item in chat_history
|
|
98
235
|
]
|
|
99
|
-
|
|
236
|
+
if len(history) > 0:
|
|
237
|
+
prompts = history + prompts
|
|
238
|
+
if len(system_prompts) > 0:
|
|
239
|
+
prompts = system_prompts + prompts
|
|
240
|
+
prompts = json.dumps(prompts)
|
|
100
241
|
|
|
101
242
|
extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
|
|
102
243
|
|
|
@@ -121,13 +262,13 @@ def chat_create(original_method, version, tracer):
|
|
|
121
262
|
if kwargs.get("temperature") is not None:
|
|
122
263
|
attributes.llm_temperature = kwargs.get("temperature")
|
|
123
264
|
if kwargs.get("max_tokens") is not None:
|
|
124
|
-
attributes.
|
|
265
|
+
attributes.llm_max_tokens = str(kwargs.get("max_tokens"))
|
|
125
266
|
if kwargs.get("max_input_tokens") is not None:
|
|
126
|
-
attributes.
|
|
267
|
+
attributes.llm_max_input_tokens = str(kwargs.get("max_input_tokens"))
|
|
127
268
|
if kwargs.get("p") is not None:
|
|
128
269
|
attributes.llm_top_p = kwargs.get("p")
|
|
129
270
|
if kwargs.get("k") is not None:
|
|
130
|
-
attributes.
|
|
271
|
+
attributes.llm_top_k = kwargs.get("k")
|
|
131
272
|
if kwargs.get("user") is not None:
|
|
132
273
|
attributes.llm_user = kwargs.get("user")
|
|
133
274
|
if kwargs.get("conversation_id") is not None:
|
|
@@ -171,36 +312,40 @@ def chat_create(original_method, version, tracer):
|
|
|
171
312
|
span.set_attribute("llm.is_search_required", result.is_search_required)
|
|
172
313
|
|
|
173
314
|
if kwargs.get("stream") is False or kwargs.get("stream") is None:
|
|
174
|
-
if hasattr(result, "text") and result.text is not None:
|
|
315
|
+
if hasattr(result, "text") and result.text is not None and result.text != "":
|
|
175
316
|
if (
|
|
176
317
|
hasattr(result, "chat_history")
|
|
177
318
|
and result.chat_history is not None
|
|
178
319
|
):
|
|
179
320
|
responses = [
|
|
180
321
|
{
|
|
181
|
-
"
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
),
|
|
194
|
-
}
|
|
322
|
+
"role": (
|
|
323
|
+
item.role
|
|
324
|
+
if hasattr(item, "role")
|
|
325
|
+
and item.role is not None
|
|
326
|
+
else "USER"
|
|
327
|
+
),
|
|
328
|
+
"content": (
|
|
329
|
+
item.message
|
|
330
|
+
if hasattr(item, "message")
|
|
331
|
+
and item.message is not None
|
|
332
|
+
else ""
|
|
333
|
+
),
|
|
195
334
|
}
|
|
196
335
|
for item in result.chat_history
|
|
197
336
|
]
|
|
198
337
|
span.set_attribute("llm.responses", json.dumps(responses))
|
|
199
338
|
else:
|
|
200
339
|
responses = [
|
|
201
|
-
{"
|
|
340
|
+
{"role": "CHATBOT", "content": result.text}
|
|
202
341
|
]
|
|
203
342
|
span.set_attribute("llm.responses", json.dumps(responses))
|
|
343
|
+
elif hasattr(result, "tool_calls") and result.tool_calls is not None:
|
|
344
|
+
tool_calls = []
|
|
345
|
+
for tool_call in result.tool_calls:
|
|
346
|
+
tool_calls.append(tool_call.json())
|
|
347
|
+
span.set_attribute("llm.tool_calls", json.dumps(tool_calls))
|
|
348
|
+
span.set_attribute("llm.responses", json.dumps([]))
|
|
204
349
|
else:
|
|
205
350
|
responses = []
|
|
206
351
|
span.set_attribute("llm.responses", json.dumps(responses))
|
|
@@ -230,6 +375,11 @@ def chat_create(original_method, version, tracer):
|
|
|
230
375
|
and usage.output_tokens is not None
|
|
231
376
|
else 0
|
|
232
377
|
),
|
|
378
|
+
"search_units": (
|
|
379
|
+
usage.search_units
|
|
380
|
+
if usage.search_units is not None
|
|
381
|
+
else 0
|
|
382
|
+
),
|
|
233
383
|
}
|
|
234
384
|
span.set_attribute(
|
|
235
385
|
"llm.token.counts", json.dumps(usage_dict)
|
|
@@ -257,33 +407,33 @@ def chat_stream(original_method, version, tracer):
|
|
|
257
407
|
service_provider = SERVICE_PROVIDERS["COHERE"]
|
|
258
408
|
|
|
259
409
|
message = kwargs.get("message", "")
|
|
260
|
-
|
|
261
|
-
|
|
410
|
+
prompts = [{"role": "USER", "content": message}]
|
|
411
|
+
system_prompts = []
|
|
412
|
+
history = []
|
|
262
413
|
preamble = kwargs.get("preamble")
|
|
263
414
|
if preamble:
|
|
264
|
-
|
|
265
|
-
[{"role": "system", "content": preamble}]
|
|
266
|
-
+ [{"role": "USER", "content": message}]
|
|
267
|
-
)
|
|
415
|
+
system_prompts = [{"role": "system", "content": preamble}]
|
|
268
416
|
|
|
269
417
|
chat_history = kwargs.get("chat_history")
|
|
270
418
|
if chat_history:
|
|
271
419
|
history = [
|
|
272
420
|
{
|
|
273
|
-
"
|
|
274
|
-
"role"
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
"
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
),
|
|
282
|
-
}
|
|
421
|
+
"role": (
|
|
422
|
+
item.get("role") if item.get("role") is not None else "USER"
|
|
423
|
+
),
|
|
424
|
+
"content": (
|
|
425
|
+
item.get("message")
|
|
426
|
+
if item.get("message") is not None
|
|
427
|
+
else ""
|
|
428
|
+
),
|
|
283
429
|
}
|
|
284
430
|
for item in chat_history
|
|
285
431
|
]
|
|
286
|
-
|
|
432
|
+
if len(history) > 0:
|
|
433
|
+
prompts = history + prompts
|
|
434
|
+
if len(system_prompts) > 0:
|
|
435
|
+
prompts = system_prompts + prompts
|
|
436
|
+
prompts = json.dumps(prompts)
|
|
287
437
|
|
|
288
438
|
extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
|
|
289
439
|
|
|
@@ -308,13 +458,13 @@ def chat_stream(original_method, version, tracer):
|
|
|
308
458
|
if kwargs.get("temperature") is not None:
|
|
309
459
|
attributes.llm_temperature = kwargs.get("temperature")
|
|
310
460
|
if kwargs.get("max_tokens") is not None:
|
|
311
|
-
attributes.
|
|
461
|
+
attributes.llm_max_tokens = str(kwargs.get("max_tokens"))
|
|
312
462
|
if kwargs.get("max_input_tokens") is not None:
|
|
313
|
-
attributes.
|
|
463
|
+
attributes.llm_max_input_tokens = str(kwargs.get("max_input_tokens"))
|
|
314
464
|
if kwargs.get("p") is not None:
|
|
315
465
|
attributes.llm_top_p = kwargs.get("p")
|
|
316
466
|
if kwargs.get("k") is not None:
|
|
317
|
-
attributes.
|
|
467
|
+
attributes.llm_top_k = kwargs.get("k")
|
|
318
468
|
if kwargs.get("user") is not None:
|
|
319
469
|
attributes.llm_user = kwargs.get("user")
|
|
320
470
|
if kwargs.get("conversation_id") is not None:
|
|
@@ -342,49 +492,99 @@ def chat_stream(original_method, version, tracer):
|
|
|
342
492
|
try:
|
|
343
493
|
# Attempt to call the original method
|
|
344
494
|
result = wrapped(*args, **kwargs)
|
|
345
|
-
|
|
346
|
-
result_content = []
|
|
347
495
|
span.add_event(Event.STREAM_START.value)
|
|
348
|
-
completion_tokens = 0
|
|
349
496
|
try:
|
|
350
497
|
for event in result:
|
|
351
498
|
if hasattr(event, "text") and event.text is not None:
|
|
352
|
-
completion_tokens += estimate_tokens(event.text)
|
|
353
499
|
content = event.text
|
|
354
500
|
else:
|
|
355
501
|
content = ""
|
|
356
502
|
span.add_event(
|
|
357
503
|
Event.STREAM_OUTPUT.value, {"response": "".join(content)}
|
|
358
504
|
)
|
|
359
|
-
|
|
505
|
+
|
|
506
|
+
if hasattr(event, "finish_reason") and event.finish_reason == "COMPLETE":
|
|
507
|
+
response = event.response
|
|
508
|
+
|
|
509
|
+
if (hasattr(response, "generation_id")) and (
|
|
510
|
+
response.generation_id is not None
|
|
511
|
+
):
|
|
512
|
+
span.set_attribute("llm.generation_id", response.generation_id)
|
|
513
|
+
if (hasattr(response, "response_id")) and (response.response_id is not None):
|
|
514
|
+
span.set_attribute("llm.response_id", response.response_id)
|
|
515
|
+
if (hasattr(response, "is_search_required")) and (
|
|
516
|
+
response.is_search_required is not None
|
|
517
|
+
):
|
|
518
|
+
span.set_attribute("llm.is_search_required", response.is_search_required)
|
|
519
|
+
|
|
520
|
+
# Set the response attributes
|
|
521
|
+
if hasattr(response, "text") and response.text is not None:
|
|
522
|
+
if (
|
|
523
|
+
hasattr(response, "chat_history")
|
|
524
|
+
and response.chat_history is not None
|
|
525
|
+
):
|
|
526
|
+
responses = [
|
|
527
|
+
{
|
|
528
|
+
"role": (
|
|
529
|
+
item.role
|
|
530
|
+
if hasattr(item, "role")
|
|
531
|
+
and item.role is not None
|
|
532
|
+
else "USER"
|
|
533
|
+
),
|
|
534
|
+
"content": (
|
|
535
|
+
item.message
|
|
536
|
+
if hasattr(item, "message")
|
|
537
|
+
and item.message is not None
|
|
538
|
+
else ""
|
|
539
|
+
),
|
|
540
|
+
}
|
|
541
|
+
for item in response.chat_history
|
|
542
|
+
]
|
|
543
|
+
span.set_attribute("llm.responses", json.dumps(responses))
|
|
544
|
+
else:
|
|
545
|
+
responses = [
|
|
546
|
+
{"role": "CHATBOT", "content": response.text}
|
|
547
|
+
]
|
|
548
|
+
span.set_attribute("llm.responses", json.dumps(responses))
|
|
549
|
+
|
|
550
|
+
# Get the usage
|
|
551
|
+
if hasattr(response, "meta") and response.meta is not None:
|
|
552
|
+
if (
|
|
553
|
+
hasattr(response.meta, "billed_units")
|
|
554
|
+
and response.meta.billed_units is not None
|
|
555
|
+
):
|
|
556
|
+
usage = response.meta.billed_units
|
|
557
|
+
if usage is not None:
|
|
558
|
+
usage_dict = {
|
|
559
|
+
"input_tokens": (
|
|
560
|
+
usage.input_tokens
|
|
561
|
+
if usage.input_tokens is not None
|
|
562
|
+
else 0
|
|
563
|
+
),
|
|
564
|
+
"output_tokens": (
|
|
565
|
+
usage.output_tokens
|
|
566
|
+
if usage.output_tokens is not None
|
|
567
|
+
else 0
|
|
568
|
+
),
|
|
569
|
+
"total_tokens": (
|
|
570
|
+
usage.input_tokens + usage.output_tokens
|
|
571
|
+
if usage.input_tokens is not None
|
|
572
|
+
and usage.output_tokens is not None
|
|
573
|
+
else 0
|
|
574
|
+
),
|
|
575
|
+
"search_units": (
|
|
576
|
+
usage.search_units
|
|
577
|
+
if usage.search_units is not None
|
|
578
|
+
else 0
|
|
579
|
+
),
|
|
580
|
+
}
|
|
581
|
+
span.set_attribute(
|
|
582
|
+
"llm.token.counts", json.dumps(usage_dict)
|
|
583
|
+
)
|
|
584
|
+
|
|
360
585
|
yield event
|
|
361
586
|
finally:
|
|
362
|
-
|
|
363
|
-
# Finalize span after processing all chunks
|
|
364
587
|
span.add_event(Event.STREAM_END.value)
|
|
365
|
-
span.set_attribute(
|
|
366
|
-
"llm.token.counts",
|
|
367
|
-
json.dumps(
|
|
368
|
-
{
|
|
369
|
-
"input_tokens": prompt_tokens,
|
|
370
|
-
"output_tokens": completion_tokens,
|
|
371
|
-
"total_tokens": prompt_tokens + completion_tokens,
|
|
372
|
-
}
|
|
373
|
-
),
|
|
374
|
-
)
|
|
375
|
-
span.set_attribute(
|
|
376
|
-
"llm.responses",
|
|
377
|
-
json.dumps(
|
|
378
|
-
[
|
|
379
|
-
{
|
|
380
|
-
"message": {
|
|
381
|
-
"role": "CHATBOT",
|
|
382
|
-
"content": "".join(result_content),
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
]
|
|
386
|
-
),
|
|
387
|
-
)
|
|
388
588
|
span.set_status(StatusCode.OK)
|
|
389
589
|
span.end()
|
|
390
590
|
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Copyright (c) 2024 Scale3 Labs
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
3
15
|
"""
|
|
4
16
|
|
|
5
17
|
import importlib.metadata
|
|
6
18
|
import inspect
|
|
19
|
+
import logging
|
|
7
20
|
from typing import Collection
|
|
8
21
|
|
|
9
22
|
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
|
@@ -12,8 +25,6 @@ from wrapt import wrap_function_wrapper
|
|
|
12
25
|
|
|
13
26
|
from langtrace_python_sdk.instrumentation.langchain.patch import generic_patch
|
|
14
27
|
|
|
15
|
-
import logging
|
|
16
|
-
|
|
17
28
|
logging.basicConfig(level=logging.FATAL)
|
|
18
29
|
|
|
19
30
|
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Copyright (c) 2024 Scale3 Labs
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
3
15
|
"""
|
|
4
16
|
|
|
5
17
|
import json
|
|
@@ -51,12 +63,12 @@ def generic_patch(
|
|
|
51
63
|
|
|
52
64
|
span.set_status(StatusCode.OK)
|
|
53
65
|
return result
|
|
54
|
-
except Exception as
|
|
66
|
+
except Exception as err:
|
|
55
67
|
# Record the exception in the span
|
|
56
|
-
span.record_exception(
|
|
68
|
+
span.record_exception(err)
|
|
57
69
|
|
|
58
70
|
# Set the span status to indicate an error
|
|
59
|
-
span.set_status(Status(StatusCode.ERROR, str(
|
|
71
|
+
span.set_status(Status(StatusCode.ERROR, str(err)))
|
|
60
72
|
|
|
61
73
|
# Reraise the exception to ensure it's not swallowed
|
|
62
74
|
raise
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Copyright (c) 2024 Scale3 Labs
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
3
15
|
"""
|
|
4
16
|
|
|
5
17
|
import importlib.metadata
|
|
@@ -10,7 +22,8 @@ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
|
|
10
22
|
from opentelemetry.trace import get_tracer
|
|
11
23
|
from wrapt import wrap_function_wrapper
|
|
12
24
|
|
|
13
|
-
from langtrace_python_sdk.instrumentation.langchain_community.patch import
|
|
25
|
+
from langtrace_python_sdk.instrumentation.langchain_community.patch import \
|
|
26
|
+
generic_patch
|
|
14
27
|
|
|
15
28
|
|
|
16
29
|
def patch_module_classes(
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (c) 2024 Scale3 Labs
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
|
|
1
18
|
import json
|
|
2
19
|
|
|
3
20
|
from langtrace.trace_attributes import FrameworkSpanAttributes
|
|
@@ -43,12 +60,12 @@ def generic_patch(
|
|
|
43
60
|
|
|
44
61
|
span.set_status(StatusCode.OK)
|
|
45
62
|
return result
|
|
46
|
-
except Exception as
|
|
63
|
+
except Exception as err:
|
|
47
64
|
# Record the exception in the span
|
|
48
|
-
span.record_exception(
|
|
65
|
+
span.record_exception(err)
|
|
49
66
|
|
|
50
67
|
# Set the span status to indicate an error
|
|
51
|
-
span.set_status(Status(StatusCode.ERROR, str(
|
|
68
|
+
span.set_status(Status(StatusCode.ERROR, str(err)))
|
|
52
69
|
|
|
53
70
|
# Reraise the exception to ensure it's not swallowed
|
|
54
71
|
raise
|