langtrace-python-sdk 1.3.1__py3-none-any.whl → 1.3.3__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 +1 -1
- examples/chroma_example/basic.py +1 -1
- examples/cohere_example/chat.py +1 -1
- examples/cohere_example/chat_stream.py +2 -1
- examples/cohere_example/embed_create.py +5 -4
- examples/fastapi_example/basic_route.py +4 -2
- examples/hiveagent_example/basic.py +23 -0
- examples/llamaindex_example/agent.py +86 -0
- examples/llamaindex_example/basic.py +1 -1
- examples/openai/chat_completion.py +1 -1
- examples/openai/function_calling.py +1 -1
- examples/perplexity_example/basic.py +5 -3
- examples/pinecone_example/basic.py +1 -1
- langtrace_python_sdk/extensions/langtrace_exporter.py +10 -2
- langtrace_python_sdk/instrumentation/cohere/patch.py +78 -51
- langtrace_python_sdk/instrumentation/llamaindex/instrumentation.py +8 -1
- langtrace_python_sdk/instrumentation/llamaindex/patch.py +46 -2
- langtrace_python_sdk/instrumentation/openai/instrumentation.py +25 -0
- langtrace_python_sdk/instrumentation/openai/patch.py +387 -6
- langtrace_python_sdk/langtrace.py +10 -7
- langtrace_python_sdk/version.py +1 -1
- {langtrace_python_sdk-1.3.1.dist-info → langtrace_python_sdk-1.3.3.dist-info}/METADATA +3 -2
- {langtrace_python_sdk-1.3.1.dist-info → langtrace_python_sdk-1.3.3.dist-info}/RECORD +25 -23
- {langtrace_python_sdk-1.3.1.dist-info → langtrace_python_sdk-1.3.3.dist-info}/WHEEL +1 -1
- {langtrace_python_sdk-1.3.1.dist-info → langtrace_python_sdk-1.3.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,9 +6,12 @@ from opentelemetry.trace import get_tracer
|
|
|
6
6
|
from wrapt import wrap_function_wrapper
|
|
7
7
|
|
|
8
8
|
from langtrace_python_sdk.instrumentation.openai.patch import (
|
|
9
|
+
async_embeddings_create,
|
|
10
|
+
async_images_generate,
|
|
9
11
|
chat_completions_create,
|
|
10
12
|
embeddings_create,
|
|
11
13
|
images_generate,
|
|
14
|
+
async_chat_completions_create,
|
|
12
15
|
)
|
|
13
16
|
|
|
14
17
|
import logging
|
|
@@ -25,21 +28,43 @@ class OpenAIInstrumentation(BaseInstrumentor):
|
|
|
25
28
|
tracer_provider = kwargs.get("tracer_provider")
|
|
26
29
|
tracer = get_tracer(__name__, "", tracer_provider)
|
|
27
30
|
version = importlib.metadata.version("openai")
|
|
31
|
+
|
|
28
32
|
wrap_function_wrapper(
|
|
29
33
|
"openai.resources.chat.completions",
|
|
30
34
|
"Completions.create",
|
|
31
35
|
chat_completions_create("openai.chat.completions.create", version, tracer),
|
|
32
36
|
)
|
|
37
|
+
|
|
38
|
+
wrap_function_wrapper(
|
|
39
|
+
"openai.resources.chat.completions",
|
|
40
|
+
"AsyncCompletions.create",
|
|
41
|
+
async_chat_completions_create(
|
|
42
|
+
"openai.chat.completions.create_stream", version, tracer
|
|
43
|
+
),
|
|
44
|
+
)
|
|
45
|
+
|
|
33
46
|
wrap_function_wrapper(
|
|
34
47
|
"openai.resources.images",
|
|
35
48
|
"Images.generate",
|
|
36
49
|
images_generate("openai.images.generate", version, tracer),
|
|
37
50
|
)
|
|
51
|
+
|
|
52
|
+
wrap_function_wrapper(
|
|
53
|
+
"openai.resources.images",
|
|
54
|
+
"AsyncImages.generate",
|
|
55
|
+
async_images_generate("openai.images.generate", version, tracer),
|
|
56
|
+
)
|
|
38
57
|
wrap_function_wrapper(
|
|
39
58
|
"openai.resources.embeddings",
|
|
40
59
|
"Embeddings.create",
|
|
41
60
|
embeddings_create("openai.embeddings.create", version, tracer),
|
|
42
61
|
)
|
|
43
62
|
|
|
63
|
+
wrap_function_wrapper(
|
|
64
|
+
"openai.resources.embeddings",
|
|
65
|
+
"AsyncEmbeddings.create",
|
|
66
|
+
async_embeddings_create("openai.embeddings.create", version, tracer),
|
|
67
|
+
)
|
|
68
|
+
|
|
44
69
|
def _uninstrument(self, **kwargs):
|
|
45
70
|
pass
|
|
@@ -9,10 +9,11 @@ from opentelemetry.trace import SpanKind
|
|
|
9
9
|
from opentelemetry.trace.status import Status, StatusCode
|
|
10
10
|
|
|
11
11
|
from langtrace_python_sdk.constants.instrumentation.common import (
|
|
12
|
-
LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
|
|
12
|
+
LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
|
|
13
|
+
SERVICE_PROVIDERS,
|
|
14
|
+
)
|
|
13
15
|
from langtrace_python_sdk.constants.instrumentation.openai import APIS
|
|
14
|
-
from langtrace_python_sdk.utils.llm import
|
|
15
|
-
estimate_tokens)
|
|
16
|
+
from langtrace_python_sdk.utils.llm import calculate_prompt_tokens, estimate_tokens
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
def images_generate(original_method, version, tracer):
|
|
@@ -40,7 +41,7 @@ def images_generate(original_method, version, tracer):
|
|
|
40
41
|
"llm.model": kwargs.get("model"),
|
|
41
42
|
"llm.stream": kwargs.get("stream"),
|
|
42
43
|
"llm.prompts": json.dumps([kwargs.get("prompt", [])]),
|
|
43
|
-
**(extra_attributes if extra_attributes is not None else {})
|
|
44
|
+
**(extra_attributes if extra_attributes is not None else {}),
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
attributes = LLMSpanAttributes(**span_attributes)
|
|
@@ -87,6 +88,78 @@ def images_generate(original_method, version, tracer):
|
|
|
87
88
|
return traced_method
|
|
88
89
|
|
|
89
90
|
|
|
91
|
+
def async_images_generate(original_method, version, tracer):
|
|
92
|
+
"""
|
|
93
|
+
Wrap the `generate` method of the `Images` class to trace it.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
async def traced_method(wrapped, instance, args, kwargs):
|
|
97
|
+
base_url = (
|
|
98
|
+
str(instance._client._base_url)
|
|
99
|
+
if hasattr(instance, "_client") and hasattr(instance._client, "_base_url")
|
|
100
|
+
else ""
|
|
101
|
+
)
|
|
102
|
+
service_provider = SERVICE_PROVIDERS["OPENAI"]
|
|
103
|
+
extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
|
|
104
|
+
|
|
105
|
+
span_attributes = {
|
|
106
|
+
"langtrace.sdk.name": "langtrace-python-sdk",
|
|
107
|
+
"langtrace.service.name": service_provider,
|
|
108
|
+
"langtrace.service.type": "llm",
|
|
109
|
+
"langtrace.service.version": version,
|
|
110
|
+
"langtrace.version": "1.0.0",
|
|
111
|
+
"url.full": base_url,
|
|
112
|
+
"llm.api": APIS["IMAGES_GENERATION"]["ENDPOINT"],
|
|
113
|
+
"llm.model": kwargs.get("model"),
|
|
114
|
+
"llm.stream": kwargs.get("stream"),
|
|
115
|
+
"llm.prompts": json.dumps([kwargs.get("prompt", [])]),
|
|
116
|
+
**(extra_attributes if extra_attributes is not None else {}),
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
attributes = LLMSpanAttributes(**span_attributes)
|
|
120
|
+
|
|
121
|
+
with tracer.start_as_current_span(
|
|
122
|
+
APIS["IMAGES_GENERATION"]["METHOD"], kind=SpanKind.CLIENT
|
|
123
|
+
) as span:
|
|
124
|
+
async for field, value in attributes.model_dump(by_alias=True).items():
|
|
125
|
+
if value is not None:
|
|
126
|
+
span.set_attribute(field, value)
|
|
127
|
+
try:
|
|
128
|
+
# Attempt to call the original method
|
|
129
|
+
result = await wrapped(*args, **kwargs)
|
|
130
|
+
if kwargs.get("stream") is False or kwargs.get("stream") is None:
|
|
131
|
+
data = (
|
|
132
|
+
result.data[0]
|
|
133
|
+
if hasattr(result, "data") and len(result.data) > 0
|
|
134
|
+
else {}
|
|
135
|
+
)
|
|
136
|
+
response = [
|
|
137
|
+
{
|
|
138
|
+
"url": data.url if hasattr(data, "url") else "",
|
|
139
|
+
"revised_prompt": (
|
|
140
|
+
data.revised_prompt
|
|
141
|
+
if hasattr(data, "revised_prompt")
|
|
142
|
+
else ""
|
|
143
|
+
),
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
span.set_attribute("llm.responses", json.dumps(response))
|
|
147
|
+
|
|
148
|
+
span.set_status(StatusCode.OK)
|
|
149
|
+
return result
|
|
150
|
+
except Exception as e:
|
|
151
|
+
# Record the exception in the span
|
|
152
|
+
span.record_exception(e)
|
|
153
|
+
|
|
154
|
+
# Set the span status to indicate an error
|
|
155
|
+
span.set_status(Status(StatusCode.ERROR, str(e)))
|
|
156
|
+
|
|
157
|
+
# Reraise the exception to ensure it's not swallowed
|
|
158
|
+
raise
|
|
159
|
+
|
|
160
|
+
return traced_method
|
|
161
|
+
|
|
162
|
+
|
|
90
163
|
def chat_completions_create(original_method, version, tracer):
|
|
91
164
|
"""Wrap the `create` method of the `ChatCompletion` class to trace it."""
|
|
92
165
|
|
|
@@ -105,6 +178,33 @@ def chat_completions_create(original_method, version, tracer):
|
|
|
105
178
|
|
|
106
179
|
extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
|
|
107
180
|
|
|
181
|
+
# handle tool calls in the kwargs
|
|
182
|
+
llm_prompts = []
|
|
183
|
+
for item in kwargs.get("messages", []):
|
|
184
|
+
if "tool_calls" in item:
|
|
185
|
+
tool_calls = []
|
|
186
|
+
for tool_call in item["tool_calls"]:
|
|
187
|
+
tool_call_dict = {
|
|
188
|
+
"id": tool_call.id if hasattr(tool_call, "id") else "",
|
|
189
|
+
"type": tool_call.type if hasattr(tool_call, "type") else "",
|
|
190
|
+
}
|
|
191
|
+
if hasattr(tool_call, "function"):
|
|
192
|
+
tool_call_dict["function"] = {
|
|
193
|
+
"name": (
|
|
194
|
+
tool_call.function.name
|
|
195
|
+
if hasattr(tool_call.function, "name")
|
|
196
|
+
else ""
|
|
197
|
+
),
|
|
198
|
+
"arguments": (
|
|
199
|
+
tool_call.function.arguments
|
|
200
|
+
if hasattr(tool_call.function, "arguments")
|
|
201
|
+
else ""
|
|
202
|
+
),
|
|
203
|
+
}
|
|
204
|
+
tool_calls.append(tool_call_dict)
|
|
205
|
+
item["tool_calls"] = tool_calls
|
|
206
|
+
llm_prompts.append(item)
|
|
207
|
+
|
|
108
208
|
span_attributes = {
|
|
109
209
|
"langtrace.sdk.name": "langtrace-python-sdk",
|
|
110
210
|
"langtrace.service.name": service_provider,
|
|
@@ -115,7 +215,7 @@ def chat_completions_create(original_method, version, tracer):
|
|
|
115
215
|
"llm.api": APIS["CHAT_COMPLETION"]["ENDPOINT"],
|
|
116
216
|
"llm.prompts": json.dumps(kwargs.get("messages", [])),
|
|
117
217
|
"llm.stream": kwargs.get("stream"),
|
|
118
|
-
**(extra_attributes if extra_attributes is not None else {})
|
|
218
|
+
**(extra_attributes if extra_attributes is not None else {}),
|
|
119
219
|
}
|
|
120
220
|
|
|
121
221
|
attributes = LLMSpanAttributes(**span_attributes)
|
|
@@ -272,7 +372,225 @@ def chat_completions_create(original_method, version, tracer):
|
|
|
272
372
|
result_content.append(content[0] if len(content) > 0 else "")
|
|
273
373
|
yield chunk
|
|
274
374
|
finally:
|
|
375
|
+
# Finalize span after processing all chunks
|
|
376
|
+
span.add_event(Event.STREAM_END.value)
|
|
377
|
+
span.set_attribute(
|
|
378
|
+
"llm.token.counts",
|
|
379
|
+
json.dumps(
|
|
380
|
+
{
|
|
381
|
+
"input_tokens": prompt_tokens,
|
|
382
|
+
"output_tokens": completion_tokens,
|
|
383
|
+
"total_tokens": prompt_tokens + completion_tokens,
|
|
384
|
+
}
|
|
385
|
+
),
|
|
386
|
+
)
|
|
387
|
+
span.set_attribute(
|
|
388
|
+
"llm.responses",
|
|
389
|
+
json.dumps(
|
|
390
|
+
[
|
|
391
|
+
{
|
|
392
|
+
"message": {
|
|
393
|
+
"role": "assistant",
|
|
394
|
+
"content": "".join(result_content),
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
]
|
|
398
|
+
),
|
|
399
|
+
)
|
|
400
|
+
span.set_status(StatusCode.OK)
|
|
401
|
+
span.end()
|
|
275
402
|
|
|
403
|
+
# return the wrapped method
|
|
404
|
+
return traced_method
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def async_chat_completions_create(original_method, version, tracer):
|
|
408
|
+
"""Wrap the `create` method of the `ChatCompletion` class to trace it."""
|
|
409
|
+
|
|
410
|
+
async def traced_method(wrapped, instance, args, kwargs):
|
|
411
|
+
base_url = (
|
|
412
|
+
str(instance._client._base_url)
|
|
413
|
+
if hasattr(instance, "_client") and hasattr(instance._client, "_base_url")
|
|
414
|
+
else ""
|
|
415
|
+
)
|
|
416
|
+
service_provider = SERVICE_PROVIDERS["OPENAI"]
|
|
417
|
+
# If base url contains perplexity or azure, set the service provider accordingly
|
|
418
|
+
if "perplexity" in base_url:
|
|
419
|
+
service_provider = SERVICE_PROVIDERS["PPLX"]
|
|
420
|
+
elif "azure" in base_url:
|
|
421
|
+
service_provider = SERVICE_PROVIDERS["AZURE"]
|
|
422
|
+
|
|
423
|
+
extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
|
|
424
|
+
|
|
425
|
+
span_attributes = {
|
|
426
|
+
"langtrace.sdk.name": "langtrace-python-sdk",
|
|
427
|
+
"langtrace.service.name": service_provider,
|
|
428
|
+
"langtrace.service.type": "llm",
|
|
429
|
+
"langtrace.service.version": version,
|
|
430
|
+
"langtrace.version": "1.0.0",
|
|
431
|
+
"url.full": base_url,
|
|
432
|
+
"llm.api": APIS["CHAT_COMPLETION"]["ENDPOINT"],
|
|
433
|
+
"llm.prompts": json.dumps(kwargs.get("messages", [])),
|
|
434
|
+
"llm.stream": kwargs.get("stream"),
|
|
435
|
+
**(extra_attributes if extra_attributes is not None else {}),
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
attributes = LLMSpanAttributes(**span_attributes)
|
|
439
|
+
|
|
440
|
+
if kwargs.get("temperature") is not None:
|
|
441
|
+
attributes.llm_temperature = kwargs.get("temperature")
|
|
442
|
+
if kwargs.get("top_p") is not None:
|
|
443
|
+
attributes.llm_top_p = kwargs.get("top_p")
|
|
444
|
+
if kwargs.get("user") is not None:
|
|
445
|
+
attributes.llm_user = kwargs.get("user")
|
|
446
|
+
if kwargs.get("functions") is not None:
|
|
447
|
+
attributes.llm_function_prompts = json.dumps(kwargs.get("functions"))
|
|
448
|
+
|
|
449
|
+
# TODO(Karthik): Gotta figure out how to handle streaming with context
|
|
450
|
+
# with tracer.start_as_current_span(APIS["CHAT_COMPLETION"]["METHOD"],
|
|
451
|
+
# kind=SpanKind.CLIENT) as span:
|
|
452
|
+
span = tracer.start_span(
|
|
453
|
+
APIS["CHAT_COMPLETION"]["METHOD"], kind=SpanKind.CLIENT
|
|
454
|
+
)
|
|
455
|
+
for field, value in attributes.model_dump(by_alias=True).items():
|
|
456
|
+
if value is not None:
|
|
457
|
+
span.set_attribute(field, value)
|
|
458
|
+
try:
|
|
459
|
+
# Attempt to call the original method
|
|
460
|
+
result = await wrapped(*args, **kwargs)
|
|
461
|
+
if kwargs.get("stream") is False or kwargs.get("stream") is None:
|
|
462
|
+
span.set_attribute("llm.model", result.model)
|
|
463
|
+
if hasattr(result, "choices") and result.choices is not None:
|
|
464
|
+
responses = [
|
|
465
|
+
{
|
|
466
|
+
"message": {
|
|
467
|
+
"role": (
|
|
468
|
+
choice.message.role
|
|
469
|
+
if choice.message and choice.message.role
|
|
470
|
+
else "assistant"
|
|
471
|
+
),
|
|
472
|
+
"content": (
|
|
473
|
+
choice.message.content
|
|
474
|
+
if choice.message and choice.message.content
|
|
475
|
+
else (
|
|
476
|
+
choice.message.function_call.arguments
|
|
477
|
+
if choice.message
|
|
478
|
+
and choice.message.function_call.arguments
|
|
479
|
+
else ""
|
|
480
|
+
)
|
|
481
|
+
),
|
|
482
|
+
**(
|
|
483
|
+
{
|
|
484
|
+
"content_filter_results": choice[
|
|
485
|
+
"content_filter_results"
|
|
486
|
+
]
|
|
487
|
+
}
|
|
488
|
+
if "content_filter_results" in choice
|
|
489
|
+
else {}
|
|
490
|
+
),
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
for choice in result.choices
|
|
494
|
+
]
|
|
495
|
+
span.set_attribute("llm.responses", json.dumps(responses))
|
|
496
|
+
else:
|
|
497
|
+
responses = []
|
|
498
|
+
span.set_attribute("llm.responses", json.dumps(responses))
|
|
499
|
+
if (
|
|
500
|
+
hasattr(result, "system_fingerprint")
|
|
501
|
+
and result.system_fingerprint is not None
|
|
502
|
+
):
|
|
503
|
+
span.set_attribute(
|
|
504
|
+
"llm.system.fingerprint", result.system_fingerprint
|
|
505
|
+
)
|
|
506
|
+
# Get the usage
|
|
507
|
+
if hasattr(result, "usage") and result.usage is not None:
|
|
508
|
+
usage = result.usage
|
|
509
|
+
if usage is not None:
|
|
510
|
+
usage_dict = {
|
|
511
|
+
"input_tokens": result.usage.prompt_tokens,
|
|
512
|
+
"output_tokens": usage.completion_tokens,
|
|
513
|
+
"total_tokens": usage.total_tokens,
|
|
514
|
+
}
|
|
515
|
+
span.set_attribute("llm.token.counts", json.dumps(usage_dict))
|
|
516
|
+
span.set_status(StatusCode.OK)
|
|
517
|
+
span.end()
|
|
518
|
+
return result
|
|
519
|
+
else:
|
|
520
|
+
# iterate over kwargs.get("messages", {}) and calculate the prompt tokens
|
|
521
|
+
prompt_tokens = 0
|
|
522
|
+
for message in kwargs.get("messages", {}):
|
|
523
|
+
prompt_tokens += calculate_prompt_tokens(
|
|
524
|
+
json.dumps(message), kwargs.get("model")
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
# iterate over kwargs.get("functions") and calculate the prompt tokens
|
|
528
|
+
if kwargs.get("functions") is not None:
|
|
529
|
+
for function in kwargs.get("functions"):
|
|
530
|
+
prompt_tokens += calculate_prompt_tokens(
|
|
531
|
+
json.dumps(function), kwargs.get("model")
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
return ahandle_streaming_response(
|
|
535
|
+
result,
|
|
536
|
+
span,
|
|
537
|
+
prompt_tokens,
|
|
538
|
+
function_call=kwargs.get("functions") is not None,
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
except Exception as error:
|
|
542
|
+
span.record_exception(error)
|
|
543
|
+
span.set_status(Status(StatusCode.ERROR, str(error)))
|
|
544
|
+
span.end()
|
|
545
|
+
raise
|
|
546
|
+
|
|
547
|
+
async def ahandle_streaming_response(
|
|
548
|
+
result, span, prompt_tokens, function_call=False
|
|
549
|
+
):
|
|
550
|
+
"""Process and yield streaming response chunks."""
|
|
551
|
+
result_content = []
|
|
552
|
+
span.add_event(Event.STREAM_START.value)
|
|
553
|
+
completion_tokens = 0
|
|
554
|
+
try:
|
|
555
|
+
async for chunk in result:
|
|
556
|
+
if hasattr(chunk, "model") and chunk.model is not None:
|
|
557
|
+
span.set_attribute("llm.model", chunk.model)
|
|
558
|
+
if hasattr(chunk, "choices") and chunk.choices is not None:
|
|
559
|
+
token_counts = [
|
|
560
|
+
(
|
|
561
|
+
estimate_tokens(choice.delta.content)
|
|
562
|
+
if choice.delta and choice.delta.content
|
|
563
|
+
else (
|
|
564
|
+
estimate_tokens(choice.delta.function_call.arguments)
|
|
565
|
+
if choice.delta.function_call
|
|
566
|
+
and choice.delta.function_call.arguments
|
|
567
|
+
else 0
|
|
568
|
+
)
|
|
569
|
+
)
|
|
570
|
+
for choice in chunk.choices
|
|
571
|
+
]
|
|
572
|
+
completion_tokens += sum(token_counts)
|
|
573
|
+
content = [
|
|
574
|
+
(
|
|
575
|
+
choice.delta.content
|
|
576
|
+
if choice.delta and choice.delta.content
|
|
577
|
+
else (
|
|
578
|
+
choice.delta.function_call.arguments
|
|
579
|
+
if choice.delta.function_call
|
|
580
|
+
and choice.delta.function_call.arguments
|
|
581
|
+
else ""
|
|
582
|
+
)
|
|
583
|
+
)
|
|
584
|
+
for choice in chunk.choices
|
|
585
|
+
]
|
|
586
|
+
else:
|
|
587
|
+
content = []
|
|
588
|
+
span.add_event(
|
|
589
|
+
Event.STREAM_OUTPUT.value, {"response": "".join(content)}
|
|
590
|
+
)
|
|
591
|
+
result_content.append(content[0] if len(content) > 0 else "")
|
|
592
|
+
yield chunk
|
|
593
|
+
finally:
|
|
276
594
|
# Finalize span after processing all chunks
|
|
277
595
|
span.add_event(Event.STREAM_END.value)
|
|
278
596
|
span.set_attribute(
|
|
@@ -330,7 +648,7 @@ def embeddings_create(original_method, version, tracer):
|
|
|
330
648
|
"llm.api": APIS["EMBEDDINGS_CREATE"]["ENDPOINT"],
|
|
331
649
|
"llm.model": kwargs.get("model"),
|
|
332
650
|
"llm.prompts": "",
|
|
333
|
-
**(extra_attributes if extra_attributes is not None else {})
|
|
651
|
+
**(extra_attributes if extra_attributes is not None else {}),
|
|
334
652
|
}
|
|
335
653
|
|
|
336
654
|
attributes = LLMSpanAttributes(**span_attributes)
|
|
@@ -366,3 +684,66 @@ def embeddings_create(original_method, version, tracer):
|
|
|
366
684
|
raise
|
|
367
685
|
|
|
368
686
|
return traced_method
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
def async_embeddings_create(original_method, version, tracer):
|
|
690
|
+
"""
|
|
691
|
+
Wrap the `create` method of the `Embeddings` class to trace it.
|
|
692
|
+
"""
|
|
693
|
+
|
|
694
|
+
async def traced_method(wrapped, instance, args, kwargs):
|
|
695
|
+
base_url = (
|
|
696
|
+
str(instance._client._base_url)
|
|
697
|
+
if hasattr(instance, "_client") and hasattr(instance._client, "_base_url")
|
|
698
|
+
else ""
|
|
699
|
+
)
|
|
700
|
+
|
|
701
|
+
service_provider = SERVICE_PROVIDERS["OPENAI"]
|
|
702
|
+
extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
|
|
703
|
+
|
|
704
|
+
span_attributes = {
|
|
705
|
+
"langtrace.sdk.name": "langtrace-python-sdk",
|
|
706
|
+
"langtrace.service.name": service_provider,
|
|
707
|
+
"langtrace.service.type": "llm",
|
|
708
|
+
"langtrace.service.version": version,
|
|
709
|
+
"langtrace.version": "1.0.0",
|
|
710
|
+
"url.full": base_url,
|
|
711
|
+
"llm.api": APIS["EMBEDDINGS_CREATE"]["ENDPOINT"],
|
|
712
|
+
"llm.model": kwargs.get("model"),
|
|
713
|
+
"llm.prompts": "",
|
|
714
|
+
**(extra_attributes if extra_attributes is not None else {}),
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
attributes = LLMSpanAttributes(**span_attributes)
|
|
718
|
+
kwargs.get("encoding_format")
|
|
719
|
+
|
|
720
|
+
if kwargs.get("encoding_format") is not None:
|
|
721
|
+
attributes.llm_encoding_format = kwargs.get("encoding_format")
|
|
722
|
+
if kwargs.get("dimensions") is not None:
|
|
723
|
+
attributes["llm.dimensions"] = kwargs.get("dimensions")
|
|
724
|
+
if kwargs.get("user") is not None:
|
|
725
|
+
attributes["llm.user"] = kwargs.get("user")
|
|
726
|
+
|
|
727
|
+
with tracer.start_as_current_span(
|
|
728
|
+
APIS["EMBEDDINGS_CREATE"]["METHOD"], kind=SpanKind.CLIENT
|
|
729
|
+
) as span:
|
|
730
|
+
|
|
731
|
+
async for field, value in attributes.model_dump(by_alias=True).items():
|
|
732
|
+
if value is not None:
|
|
733
|
+
span.set_attribute(field, value)
|
|
734
|
+
try:
|
|
735
|
+
# Attempt to call the original method
|
|
736
|
+
result = await wrapped(*args, **kwargs)
|
|
737
|
+
span.set_status(StatusCode.OK)
|
|
738
|
+
return result
|
|
739
|
+
except Exception as e:
|
|
740
|
+
# Record the exception in the span
|
|
741
|
+
span.record_exception(e)
|
|
742
|
+
|
|
743
|
+
# Set the span status to indicate an error
|
|
744
|
+
span.set_status(Status(StatusCode.ERROR, str(e)))
|
|
745
|
+
|
|
746
|
+
# Reraise the exception to ensure it's not swallowed
|
|
747
|
+
raise
|
|
748
|
+
|
|
749
|
+
return traced_method
|
|
@@ -41,7 +41,6 @@ def init(
|
|
|
41
41
|
api_key: str = None,
|
|
42
42
|
batch: bool = True,
|
|
43
43
|
write_to_langtrace_cloud: bool = True,
|
|
44
|
-
debug_log_to_console: bool = False,
|
|
45
44
|
custom_remote_exporter=None,
|
|
46
45
|
api_host: Optional[str] = None,
|
|
47
46
|
):
|
|
@@ -54,20 +53,24 @@ def init(
|
|
|
54
53
|
)
|
|
55
54
|
console_exporter = ConsoleSpanExporter()
|
|
56
55
|
batch_processor_remote = BatchSpanProcessor(remote_write_exporter)
|
|
56
|
+
simple_processor_remote = SimpleSpanProcessor(remote_write_exporter)
|
|
57
57
|
batch_processor_console = BatchSpanProcessor(console_exporter)
|
|
58
58
|
simple_processor_console = SimpleSpanProcessor(console_exporter)
|
|
59
59
|
|
|
60
|
-
if
|
|
60
|
+
if write_to_langtrace_cloud:
|
|
61
|
+
provider.add_span_processor(batch_processor_remote)
|
|
62
|
+
elif custom_remote_exporter is not None:
|
|
63
|
+
if batch:
|
|
64
|
+
provider.add_span_processor(batch_processor_remote)
|
|
65
|
+
else:
|
|
66
|
+
provider.add_span_processor(simple_processor_remote)
|
|
67
|
+
else:
|
|
61
68
|
if batch:
|
|
62
69
|
provider.add_span_processor(batch_processor_console)
|
|
63
70
|
else:
|
|
64
71
|
provider.add_span_processor(simple_processor_console)
|
|
65
72
|
|
|
66
|
-
|
|
67
|
-
if batch:
|
|
68
|
-
provider.add_span_processor(batch_processor_remote)
|
|
69
|
-
else:
|
|
70
|
-
raise ValueError("Batching is required for remote write")
|
|
73
|
+
|
|
71
74
|
|
|
72
75
|
# Initialize tracer
|
|
73
76
|
trace.set_tracer_provider(provider)
|
langtrace_python_sdk/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "1.3.
|
|
1
|
+
__version__ = "1.3.3"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: langtrace-python-sdk
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.3
|
|
4
4
|
Summary: Python SDK for LangTrace
|
|
5
5
|
Project-URL: Homepage, https://github.com/Scale3-Labs/langtrace-python-sdk
|
|
6
6
|
Author-email: Scale3 Labs <engineering@scale3labs.com>
|
|
@@ -19,6 +19,7 @@ Requires-Dist: trace-attributes
|
|
|
19
19
|
Provides-Extra: dev
|
|
20
20
|
Requires-Dist: anthropic; extra == 'dev'
|
|
21
21
|
Requires-Dist: chromadb; extra == 'dev'
|
|
22
|
+
Requires-Dist: cohere; extra == 'dev'
|
|
22
23
|
Requires-Dist: langchain; extra == 'dev'
|
|
23
24
|
Requires-Dist: langchain-openai; extra == 'dev'
|
|
24
25
|
Requires-Dist: llama-index; extra == 'dev'
|
|
@@ -144,7 +145,7 @@ pip install langtrace-python-sdk
|
|
|
144
145
|
|
|
145
146
|
``` python
|
|
146
147
|
from langtrace_python_sdk import langtrace # Must precede any llm module imports
|
|
147
|
-
langtrace.init(write_to_langtrace_cloud=False
|
|
148
|
+
langtrace.init(write_to_langtrace_cloud=False)
|
|
148
149
|
```
|
|
149
150
|
|
|
150
151
|
## Langtrace self hosted custom exporter
|
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
examples/anthropic_example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
examples/anthropic_example/completion.py,sha256=
|
|
3
|
+
examples/anthropic_example/completion.py,sha256=GpkfkBY-PaIqoWjpnVFB6QVqw1O6B_MYQPxn_JbkeOw,751
|
|
4
4
|
examples/chroma_example/__init__.py,sha256=Tq6pae7fHA7dwuDslabB5MTNedL21gu2RaZicpxSyLU,25
|
|
5
|
-
examples/chroma_example/basic.py,sha256=
|
|
5
|
+
examples/chroma_example/basic.py,sha256=RT4yi8mhN_bgTbeGZnHEscLT9z_VlFnHekIM9psefLk,870
|
|
6
6
|
examples/cohere_example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
examples/cohere_example/chat.py,sha256=
|
|
8
|
-
examples/cohere_example/chat_stream.py,sha256=
|
|
9
|
-
examples/cohere_example/embed_create.py,sha256=
|
|
10
|
-
examples/fastapi_example/basic_route.py,sha256=
|
|
7
|
+
examples/cohere_example/chat.py,sha256=L96GnhPm43l-iS5loyLBbP5f2J7ZiKzf9Yt-kwHn-5E,849
|
|
8
|
+
examples/cohere_example/chat_stream.py,sha256=wYmcTF232yfmiozmhGdXLsWX8sTFmRawSW8ApDaZmJY,641
|
|
9
|
+
examples/cohere_example/embed_create.py,sha256=MC1lmxIi_SRLxmiKyL17fq-M95NqWqBiAI1VH234E7E,514
|
|
10
|
+
examples/fastapi_example/basic_route.py,sha256=O0gaAdYgdbZ1LElV5JuJyq-FZMHcfJCysqQPpKa8fNI,1128
|
|
11
|
+
examples/hiveagent_example/basic.py,sha256=MuzPMHtrhkxv2T7Ke8Rz3TsSum_XZ0IyPc2ufOt0U1Y,444
|
|
11
12
|
examples/langchain_example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
13
|
examples/langchain_example/basic.py,sha256=mSVcE-Kx7jNS4U_zrm5WZI98xyw6WTrhd2GMVGgn9Cg,2653
|
|
13
14
|
examples/langchain_example/tool.py,sha256=8T8_IDbgA58XbsfyH5_xhA8ZKQfyfyFxF8wor-PsRjA,2556
|
|
14
15
|
examples/llamaindex_example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
examples/llamaindex_example/
|
|
16
|
+
examples/llamaindex_example/agent.py,sha256=_iIXy9lfDz6ySf6aTeeRqejlfGnXZ7msxLBjGiJth3E,2777
|
|
17
|
+
examples/llamaindex_example/basic.py,sha256=gvns3oDUy0c4I5ewnj9-B36_1La8y6qD3VQaq6v3syM,654
|
|
16
18
|
examples/llamaindex_example/data/abramov.txt,sha256=Ou-GyWZm5AjHLgxviBoRE9ikNv5MScsF0cd--0vVVhI,32667
|
|
17
19
|
examples/openai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
examples/openai/chat_completion.py,sha256=
|
|
20
|
+
examples/openai/chat_completion.py,sha256=1AiLIr0sWBKcqzhCbTJkspZyuGDZATxhbKBCKjzvM-E,1731
|
|
19
21
|
examples/openai/embeddings_create.py,sha256=AhDNAqg-WzRYLJAE_b2RKGjuVCh4aZSU7MxcZv2kCHQ,518
|
|
20
|
-
examples/openai/function_calling.py,sha256=
|
|
22
|
+
examples/openai/function_calling.py,sha256=qsbmlkWnOvdemekMqsB0roBhpn_GsNBWrR2td0vVEIs,2335
|
|
21
23
|
examples/openai/images_generate.py,sha256=ZioxTuHKE_yYlhpESqXKVzdkiwdegkmLVB7N8T2LU00,506
|
|
22
|
-
examples/perplexity_example/basic.py,sha256=
|
|
24
|
+
examples/perplexity_example/basic.py,sha256=oTLwEYlvpD4wEnqEUrUSlQ0SeQ0u50Jeab4ggkikQg0,671
|
|
23
25
|
examples/pinecone_example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
-
examples/pinecone_example/basic.py,sha256=
|
|
26
|
+
examples/pinecone_example/basic.py,sha256=OkYjN3J5kxw-kloOV3Q-iyI6opkbarWsMom-_AMP2ZA,893
|
|
25
27
|
langtrace_python_sdk/__init__.py,sha256=SlHg447-nQBbw8exRNJP_OyHUZ39Sldb7aaQ35hIRm8,262
|
|
26
|
-
langtrace_python_sdk/langtrace.py,sha256
|
|
27
|
-
langtrace_python_sdk/version.py,sha256
|
|
28
|
+
langtrace_python_sdk/langtrace.py,sha256=83-AkdASO7UF9FHR9BDZUSeYv9GFZkJJQD2YLKbqzo8,3562
|
|
29
|
+
langtrace_python_sdk/version.py,sha256=Vi6om3KImlKsS_Wg5CjUgYffoi2zx7T-SRPnnGL0G7M,22
|
|
28
30
|
langtrace_python_sdk/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
31
|
langtrace_python_sdk/constants/exporter/langtrace_exporter.py,sha256=5MNjnAOg-4am78J3gVMH6FSwq5N8TOj72ugkhsw4vi0,46
|
|
30
32
|
langtrace_python_sdk/constants/instrumentation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -35,7 +37,7 @@ langtrace_python_sdk/constants/instrumentation/common.py,sha256=iqoWV1laoFxzqqwz
|
|
|
35
37
|
langtrace_python_sdk/constants/instrumentation/openai.py,sha256=9VF6ic9Ed3bpSvdp6iNmrpx2Ppo6DPav3hoUcqSQSv0,1048
|
|
36
38
|
langtrace_python_sdk/constants/instrumentation/pinecone.py,sha256=Xaqqw-xBO0JJLGk75hiCUQGztNm0HiVaLQvjtYK7VJM,472
|
|
37
39
|
langtrace_python_sdk/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
langtrace_python_sdk/extensions/langtrace_exporter.py,sha256=
|
|
40
|
+
langtrace_python_sdk/extensions/langtrace_exporter.py,sha256=3KKeWSLECAxO-FMTndwF5Evv39t9-GG7p67DfYJpNEY,4194
|
|
39
41
|
langtrace_python_sdk/instrumentation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
42
|
langtrace_python_sdk/instrumentation/anthropic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
43
|
langtrace_python_sdk/instrumentation/anthropic/instrumentation.py,sha256=DTvz6fNMMxYc2z6oqOveBX6mIXHoOFeOoosyXB2HJ6Y,1159
|
|
@@ -45,7 +47,7 @@ langtrace_python_sdk/instrumentation/chroma/instrumentation.py,sha256=9k8KueizRJ
|
|
|
45
47
|
langtrace_python_sdk/instrumentation/chroma/patch.py,sha256=fddeGMP8obll2Tra04S8C0jEiJ97v_y1WC1QdO4bO4A,2303
|
|
46
48
|
langtrace_python_sdk/instrumentation/cohere/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
47
49
|
langtrace_python_sdk/instrumentation/cohere/instrumentation.py,sha256=-DWjAq4_H3Q8MCyH2qt3NiLndgrRy9BIUbrZZHsY0pY,1431
|
|
48
|
-
langtrace_python_sdk/instrumentation/cohere/patch.py,sha256=
|
|
50
|
+
langtrace_python_sdk/instrumentation/cohere/patch.py,sha256=ELur2aK2DakDWDf26pvas4r9vpawX7oxwgDiHLMj27U,16631
|
|
49
51
|
langtrace_python_sdk/instrumentation/langchain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
52
|
langtrace_python_sdk/instrumentation/langchain/instrumentation.py,sha256=UBUx31IErih1zDGkwez5ZFT2qGvCHxhdGHvJsEyhvEo,2938
|
|
51
53
|
langtrace_python_sdk/instrumentation/langchain/patch.py,sha256=ofageMjQZ_AGlnfknBI06dkaDsyNuqYRz-3EZhv9izY,3294
|
|
@@ -56,18 +58,18 @@ langtrace_python_sdk/instrumentation/langchain_core/__init__.py,sha256=47DEQpj8H
|
|
|
56
58
|
langtrace_python_sdk/instrumentation/langchain_core/instrumentation.py,sha256=1Y4q_F2nN79y-dap_if35vH9oowIuDi6mo0ELYIltNI,5417
|
|
57
59
|
langtrace_python_sdk/instrumentation/langchain_core/patch.py,sha256=L20qbFCChGJhYDAyftZ7snev7OElN-V3X4m3IZNEDYc,7959
|
|
58
60
|
langtrace_python_sdk/instrumentation/llamaindex/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
|
-
langtrace_python_sdk/instrumentation/llamaindex/instrumentation.py,sha256=
|
|
60
|
-
langtrace_python_sdk/instrumentation/llamaindex/patch.py,sha256=
|
|
61
|
+
langtrace_python_sdk/instrumentation/llamaindex/instrumentation.py,sha256=D7_HPvdipMeAA6F_sDUxdg7j_pC-MbS2b2O67cSmX7I,2580
|
|
62
|
+
langtrace_python_sdk/instrumentation/llamaindex/patch.py,sha256=8IM2dedF81w8_vVyA56JptyvlQl_bQO4UcB56sptuGs,3700
|
|
61
63
|
langtrace_python_sdk/instrumentation/openai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
62
|
-
langtrace_python_sdk/instrumentation/openai/instrumentation.py,sha256=
|
|
63
|
-
langtrace_python_sdk/instrumentation/openai/patch.py,sha256=
|
|
64
|
+
langtrace_python_sdk/instrumentation/openai/instrumentation.py,sha256=Pv4n4z_kSxvZGVxrj3AopBoWQSxIOtMKolkxHrchRdM,2162
|
|
65
|
+
langtrace_python_sdk/instrumentation/openai/patch.py,sha256=6hZ8ExTHHUnib8sU5k5OXt9zFKl3iTL2-PzsHJxW5ao,31587
|
|
64
66
|
langtrace_python_sdk/instrumentation/pinecone/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
65
67
|
langtrace_python_sdk/instrumentation/pinecone/instrumentation.py,sha256=o0EUd5jvHaDKOUTj4NjnL5UfDHDHxyXkWGlTW4oeRDk,1784
|
|
66
68
|
langtrace_python_sdk/instrumentation/pinecone/patch.py,sha256=5lF7hQmg2-U2EWtOC0w8_peRaNMysBomb0fjiNoS6eQ,2200
|
|
67
69
|
langtrace_python_sdk/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
68
70
|
langtrace_python_sdk/utils/llm.py,sha256=4z2e-md_ELXCEuOIRVWracR6qH2pmsOxCqpkuF9_3Nw,1589
|
|
69
71
|
langtrace_python_sdk/utils/with_root_span.py,sha256=N7ONrcF0myZbHBy5gpQffDbX-Kf63Crsz9szG0i3m08,1889
|
|
70
|
-
langtrace_python_sdk-1.3.
|
|
71
|
-
langtrace_python_sdk-1.3.
|
|
72
|
-
langtrace_python_sdk-1.3.
|
|
73
|
-
langtrace_python_sdk-1.3.
|
|
72
|
+
langtrace_python_sdk-1.3.3.dist-info/METADATA,sha256=SQOqeK4DcO0lKolsayInA_t-pr3Stk26sH742xYFDT0,9086
|
|
73
|
+
langtrace_python_sdk-1.3.3.dist-info/WHEEL,sha256=K0BPUNF1N3kQ9olb8aVEtkObePEjdr2JOLT1N83EVws,87
|
|
74
|
+
langtrace_python_sdk-1.3.3.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
|
|
75
|
+
langtrace_python_sdk-1.3.3.dist-info/RECORD,,
|