langtrace-python-sdk 2.3.21__py3-none-any.whl → 2.3.22__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,89 @@
1
+ import dspy
2
+ from dotenv import find_dotenv, load_dotenv
3
+ from dspy.datasets import HotPotQA
4
+ from dspy.teleprompt import BootstrapFewShot
5
+
6
+ from langtrace_python_sdk import inject_additional_attributes, langtrace
7
+
8
+ _ = load_dotenv(find_dotenv())
9
+
10
+ langtrace.init()
11
+
12
+ turbo = dspy.LM('openai/gpt-4o-mini')
13
+ colbertv2_wiki17_abstracts = dspy.ColBERTv2(url='http://20.102.90.50:2017/wiki17_abstracts')
14
+
15
+ dspy.settings.configure(lm=turbo, rm=colbertv2_wiki17_abstracts)
16
+
17
+
18
+ # Load the dataset.
19
+ dataset = HotPotQA(train_seed=1, train_size=20, eval_seed=2023, dev_size=50, test_size=0)
20
+
21
+ # Tell DSPy that the 'question' field is the input. Any other fields are labels and/or metadata.
22
+ trainset = [x.with_inputs('question') for x in dataset.train]
23
+ devset = [x.with_inputs('question') for x in dataset.dev]
24
+
25
+
26
+ class GenerateAnswer(dspy.Signature):
27
+ """Answer questions with short factoid answers."""
28
+
29
+ context = dspy.InputField(desc="may contain relevant facts")
30
+ question = dspy.InputField()
31
+ answer = dspy.OutputField(desc="often between 1 and 5 words")
32
+
33
+
34
+ class RAG(dspy.Module):
35
+ def __init__(self, num_passages=3):
36
+ super().__init__()
37
+
38
+ self.retrieve = dspy.Retrieve(k=num_passages)
39
+ self.generate_answer = dspy.ChainOfThought(GenerateAnswer)
40
+
41
+ def forward(self, question):
42
+ context = self.retrieve(question).passages
43
+ prediction = self.generate_answer(context=context, question=question)
44
+ return dspy.Prediction(context=context, answer=prediction.answer)
45
+
46
+
47
+ # Validation logic: check that the predicted answer is correct.
48
+ # Also check that the retrieved context does actually contain that answer.
49
+ def validate_context_and_answer(example, prediction, trace=None):
50
+ answer_em = dspy.evaluate.answer_exact_match(example, prediction)
51
+ answer_pm = dspy.evaluate.answer_passage_match(example, prediction)
52
+ return answer_em and answer_pm
53
+
54
+
55
+ # Set up a basic optimizer, which will compile our RAG program.
56
+ optimizer = BootstrapFewShot(metric=validate_context_and_answer)
57
+
58
+ # Compile!
59
+ compiled_rag = optimizer.compile(RAG(), trainset=trainset)
60
+
61
+ # Ask any question you like to this simple RAG program.
62
+ my_question = "Who was the hero of the movie peraanmai?"
63
+
64
+ # Get the prediction. This contains `pred.context` and `pred.answer`.
65
+ # pred = compiled_rag(my_question)
66
+ pred = inject_additional_attributes(lambda: compiled_rag(my_question), {'experiment': 'experiment 6', 'description': 'trying additional stuff', 'run_id': 'run_1'})
67
+ # compiled_rag.save('compiled_rag_v1.json')
68
+
69
+ # Print the contexts and the answer.
70
+ print(f"Question: {my_question}")
71
+ print(f"Predicted Answer: {pred.answer}")
72
+ print(f"Retrieved Contexts (truncated): {[c[:200] + '...' for c in pred.context]}")
73
+
74
+ # print("Inspecting the history of the optimizer:")
75
+ # turbo.inspect_history(n=1)
76
+
77
+ from dspy.evaluate import Evaluate
78
+
79
+
80
+ def validate_answer(example, pred, trace=None):
81
+ return True
82
+
83
+
84
+ # Set up the evaluator, which can be used multiple times.
85
+ evaluate = Evaluate(devset=devset, metric=validate_answer, num_threads=4, display_progress=True, display_table=0)
86
+
87
+
88
+ # Evaluate our `optimized_cot` program.
89
+ evaluate(compiled_rag)
@@ -9,19 +9,19 @@ from langtrace_python_sdk.utils.with_root_span import (
9
9
 
10
10
  _ = load_dotenv(find_dotenv())
11
11
 
12
- langtrace.init(write_spans_to_console=True)
12
+ langtrace.init()
13
13
  client = OpenAI()
14
14
 
15
15
 
16
16
  def api():
17
17
  response = client.chat.completions.create(
18
- model="gpt-4",
18
+ model="o1-mini",
19
19
  messages=[
20
- {"role": "system", "content": "Talk like a pirate"},
21
- {"role": "user", "content": "Tell me a story in 3 sentences or less."},
20
+ # {"role": "system", "content": "Talk like a pirate"},
21
+ {"role": "user", "content": "How many r's are in strawberry?"},
22
22
  ],
23
- stream=True,
24
- # stream=False,
23
+ # stream=True,
24
+ stream=False,
25
25
  )
26
26
  return response
27
27
 
@@ -31,14 +31,17 @@ def chat_completion():
31
31
  response = api()
32
32
  # print(response)
33
33
  # Uncomment this for streaming
34
- result = []
35
- for chunk in response:
36
- if chunk.choices[0].delta.content is not None:
37
- content = [
38
- choice.delta.content if choice.delta and choice.delta.content else ""
39
- for choice in chunk.choices
40
- ]
41
- result.append(content[0] if len(content) > 0 else "")
42
-
43
- # print("".join(result))
34
+ # result = []
35
+ # for chunk in response:
36
+ # if chunk.choices[0].delta.content is not None:
37
+ # content = [
38
+ # choice.delta.content if choice.delta and choice.delta.content else ""
39
+ # for choice in chunk.choices
40
+ # ]
41
+ # result.append(content[0] if len(content) > 0 else "")
42
+
43
+ # # print("".join(result))
44
+ print(response)
44
45
  return response
46
+
47
+ chat_completion()
@@ -19,6 +19,7 @@ SERVICE_PROVIDERS = {
19
19
  "LANGCHAIN_COMMUNITY": "Langchain Community",
20
20
  "LANGCHAIN_CORE": "Langchain Core",
21
21
  "LANGGRAPH": "Langgraph",
22
+ "LITELLM": "Litellm",
22
23
  "LLAMAINDEX": "LlamaIndex",
23
24
  "OPENAI": "OpenAI",
24
25
  "PINECONE": "Pinecone",
@@ -0,0 +1,18 @@
1
+ APIS = {
2
+ "CHAT_COMPLETION": {
3
+ "METHOD": "chat.completions.create",
4
+ "ENDPOINT": "/chat/completions",
5
+ },
6
+ "IMAGES_GENERATION": {
7
+ "METHOD": "images.generate",
8
+ "ENDPOINT": "/images/generations",
9
+ },
10
+ "IMAGES_EDIT": {
11
+ "METHOD": "images.edit",
12
+ "ENDPOINT": "/images/edits",
13
+ },
14
+ "EMBEDDINGS_CREATE": {
15
+ "METHOD": "embeddings.create",
16
+ "ENDPOINT": "/embeddings",
17
+ },
18
+ }
@@ -19,6 +19,7 @@ from .vertexai import VertexAIInstrumentation
19
19
  from .gemini import GeminiInstrumentation
20
20
  from .mistral import MistralInstrumentation
21
21
  from .embedchain import EmbedchainInstrumentation
22
+ from .litellm import LiteLLMInstrumentation
22
23
 
23
24
  __all__ = [
24
25
  "AnthropicInstrumentation",
@@ -31,6 +32,7 @@ __all__ = [
31
32
  "LangchainCommunityInstrumentation",
32
33
  "LangchainCoreInstrumentation",
33
34
  "LanggraphInstrumentation",
35
+ "LiteLLMInstrumentation",
34
36
  "LlamaindexInstrumentation",
35
37
  "OpenAIInstrumentation",
36
38
  "PineconeInstrumentation",
@@ -1,6 +1,19 @@
1
1
  import json
2
+ import os
3
+
4
+ import ujson
5
+ from colorama import Fore
2
6
  from importlib_metadata import version as v
7
+ from langtrace.trace_attributes import FrameworkSpanAttributes
8
+ from opentelemetry import baggage
9
+ from opentelemetry.trace import SpanKind
10
+ from opentelemetry.trace.status import Status, StatusCode
11
+
3
12
  from langtrace_python_sdk.constants import LANGTRACE_SDK_NAME
13
+ from langtrace_python_sdk.constants.instrumentation.common import (
14
+ LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
15
+ SERVICE_PROVIDERS,
16
+ )
4
17
  from langtrace_python_sdk.utils import set_span_attribute
5
18
  from langtrace_python_sdk.utils.llm import (
6
19
  get_extra_attributes,
@@ -9,14 +22,6 @@ from langtrace_python_sdk.utils.llm import (
9
22
  set_span_attributes,
10
23
  )
11
24
  from langtrace_python_sdk.utils.silently_fail import silently_fail
12
- from langtrace_python_sdk.constants.instrumentation.common import (
13
- LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
14
- SERVICE_PROVIDERS,
15
- )
16
- from opentelemetry import baggage
17
- from langtrace.trace_attributes import FrameworkSpanAttributes
18
- from opentelemetry.trace import SpanKind
19
- from opentelemetry.trace.status import Status, StatusCode
20
25
 
21
26
 
22
27
  def patch_bootstrapfewshot_optimizer(operation_name, version, tracer):
@@ -115,6 +120,8 @@ def patch_signature(operation_name, version, tracer):
115
120
  **get_extra_attributes(),
116
121
  }
117
122
 
123
+ trace_checkpoint = os.environ.get("TRACE_DSPY_CHECKPOINT", "true").lower()
124
+
118
125
  if instance.__class__.__name__:
119
126
  span_attributes["dspy.signature.name"] = instance.__class__.__name__
120
127
  span_attributes["dspy.signature"] = str(instance.signature)
@@ -136,6 +143,9 @@ def patch_signature(operation_name, version, tracer):
136
143
  "dspy.signature.result",
137
144
  json.dumps(result.toDict()),
138
145
  )
146
+ if trace_checkpoint == "true":
147
+ print(Fore.RED + "Note: DSPy checkpoint tracing is enabled in Langtrace. To disable it, set the env var, TRACE_DSPY_CHECKPOINT to false" + Fore.RESET)
148
+ set_span_attribute(span, "dspy.checkpoint", ujson.dumps(instance.dump_state(False), indent=2))
139
149
  span.set_status(Status(StatusCode.OK))
140
150
 
141
151
  span.end()
@@ -0,0 +1,5 @@
1
+ from .instrumentation import LiteLLMInstrumentation
2
+
3
+ __all__ = [
4
+ "LiteLLMInstrumentation",
5
+ ]
@@ -0,0 +1,87 @@
1
+ """
2
+ Copyright (c) 2024 Scale3 Labs
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
12
+ """
13
+
14
+ from typing import Collection, Optional, Any
15
+ import importlib.metadata
16
+ import logging
17
+
18
+ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
19
+ from opentelemetry.trace import get_tracer, TracerProvider
20
+ from wrapt import wrap_function_wrapper
21
+
22
+ from langtrace_python_sdk.instrumentation.litellm.patch import (
23
+ async_chat_completions_create,
24
+ async_embeddings_create,
25
+ async_images_generate,
26
+ chat_completions_create,
27
+ embeddings_create,
28
+ images_generate,
29
+ )
30
+
31
+ logging.basicConfig(level=logging.FATAL)
32
+
33
+
34
+ class LiteLLMInstrumentation(BaseInstrumentor): # type: ignore
35
+
36
+ def instrumentation_dependencies(self) -> Collection[str]:
37
+ return ["litellm >= 1.48.0", "trace-attributes >= 4.0.5"]
38
+
39
+ def _instrument(self, **kwargs: Any) -> None:
40
+ tracer_provider: Optional[TracerProvider] = kwargs.get("tracer_provider")
41
+ tracer = get_tracer(__name__, "", tracer_provider)
42
+ version: str = importlib.metadata.version("openai")
43
+
44
+ wrap_function_wrapper(
45
+ "litellm",
46
+ "completion",
47
+ chat_completions_create(version, tracer),
48
+ )
49
+
50
+ wrap_function_wrapper(
51
+ "litellm",
52
+ "text_completion",
53
+ chat_completions_create(version, tracer),
54
+ )
55
+
56
+ wrap_function_wrapper(
57
+ "litellm.main",
58
+ "acompletion",
59
+ async_chat_completions_create(version, tracer),
60
+ )
61
+
62
+ wrap_function_wrapper(
63
+ "litellm.main",
64
+ "image_generation",
65
+ images_generate(version, tracer),
66
+ )
67
+
68
+ wrap_function_wrapper(
69
+ "litellm.main",
70
+ "aimage_generation",
71
+ async_images_generate(version, tracer),
72
+ )
73
+
74
+ wrap_function_wrapper(
75
+ "litellm.main",
76
+ "embedding",
77
+ embeddings_create(version, tracer),
78
+ )
79
+
80
+ wrap_function_wrapper(
81
+ "litellm.main",
82
+ "aembedding",
83
+ async_embeddings_create(version, tracer),
84
+ )
85
+
86
+ def _uninstrument(self, **kwargs: Any) -> None:
87
+ pass
@@ -0,0 +1,651 @@
1
+ import json
2
+ from typing import Any, Dict, List, Optional, Callable, Awaitable, Union
3
+ from langtrace.trace_attributes import (
4
+ LLMSpanAttributes,
5
+ SpanAttributes,
6
+ )
7
+ from langtrace_python_sdk.utils import set_span_attribute
8
+ from langtrace_python_sdk.utils.silently_fail import silently_fail
9
+ from opentelemetry import trace
10
+ from opentelemetry.trace import SpanKind, Tracer, Span
11
+ from opentelemetry.trace.status import Status, StatusCode
12
+ from opentelemetry.trace.propagation import set_span_in_context
13
+ from langtrace_python_sdk.constants.instrumentation.common import (
14
+ SERVICE_PROVIDERS,
15
+ )
16
+ from langtrace_python_sdk.constants.instrumentation.litellm import APIS
17
+ from langtrace_python_sdk.utils.llm import (
18
+ calculate_prompt_tokens,
19
+ get_base_url,
20
+ get_extra_attributes,
21
+ get_langtrace_attributes,
22
+ get_llm_request_attributes,
23
+ get_span_name,
24
+ get_tool_calls,
25
+ is_streaming,
26
+ set_event_completion,
27
+ StreamWrapper,
28
+ set_span_attributes,
29
+ )
30
+ from langtrace_python_sdk.types import NOT_GIVEN
31
+
32
+ from langtrace_python_sdk.instrumentation.openai.types import (
33
+ ImagesGenerateKwargs,
34
+ ChatCompletionsCreateKwargs,
35
+ EmbeddingsCreateKwargs,
36
+ ImagesEditKwargs,
37
+ ResultType,
38
+ ContentItem,
39
+ )
40
+
41
+
42
+ def filter_valid_attributes(attributes):
43
+ """Filter attributes where value is not None, not an empty string."""
44
+ return {
45
+ key: value
46
+ for key, value in attributes.items()
47
+ if value is not None and value != ""
48
+ }
49
+
50
+
51
+ def images_generate(version: str, tracer: Tracer) -> Callable:
52
+ """
53
+ Wrap the `generate` method of the `Images` class to trace it.
54
+ """
55
+
56
+ def traced_method(
57
+ wrapped: Callable, instance: Any, args: List[Any], kwargs: ImagesGenerateKwargs
58
+ ) -> Any:
59
+ service_provider = SERVICE_PROVIDERS["LITELLM"]
60
+ span_attributes = {
61
+ **get_langtrace_attributes(version, service_provider, vendor_type="llm"),
62
+ **get_llm_request_attributes(kwargs, operation_name="images_generate"),
63
+ SpanAttributes.LLM_URL: "not available",
64
+ SpanAttributes.LLM_PATH: APIS["IMAGES_GENERATION"]["ENDPOINT"],
65
+ **get_extra_attributes(), # type: ignore
66
+ }
67
+
68
+ attributes = LLMSpanAttributes(**filter_valid_attributes(span_attributes))
69
+
70
+ with tracer.start_as_current_span(
71
+ name=get_span_name(APIS["IMAGES_GENERATION"]["METHOD"]),
72
+ kind=SpanKind.CLIENT,
73
+ context=set_span_in_context(trace.get_current_span()),
74
+ ) as span:
75
+ set_span_attributes(span, attributes)
76
+ try:
77
+ # Attempt to call the original method
78
+ result = wrapped(*args, **kwargs)
79
+ if not is_streaming(kwargs):
80
+ data: Optional[ContentItem] = (
81
+ result.data[0]
82
+ if hasattr(result, "data") and len(result.data) > 0
83
+ else None
84
+ )
85
+ response = [
86
+ {
87
+ "role": "assistant",
88
+ "content": {
89
+ "url": getattr(data, "url", ""),
90
+ "revised_prompt": getattr(data, "revised_prompt", ""),
91
+ },
92
+ }
93
+ ]
94
+ set_event_completion(span, response)
95
+
96
+ span.set_status(StatusCode.OK)
97
+ return result
98
+ except Exception as err:
99
+ # Record the exception in the span
100
+ span.record_exception(err)
101
+
102
+ # Set the span status to indicate an error
103
+ span.set_status(Status(StatusCode.ERROR, str(err)))
104
+
105
+ # Reraise the exception to ensure it's not swallowed
106
+ raise
107
+
108
+ return traced_method
109
+
110
+
111
+ def async_images_generate(version: str, tracer: Tracer) -> Callable:
112
+ """
113
+ Wrap the `generate` method of the `Images` class to trace it.
114
+ """
115
+
116
+ async def traced_method(
117
+ wrapped: Callable, instance: Any, args: List[Any], kwargs: ImagesGenerateKwargs
118
+ ) -> Awaitable[Any]:
119
+ service_provider = SERVICE_PROVIDERS["LITELLM"]
120
+
121
+ span_attributes = {
122
+ **get_langtrace_attributes(version, service_provider, vendor_type="llm"),
123
+ **get_llm_request_attributes(kwargs, operation_name="images_generate"),
124
+ SpanAttributes.LLM_URL: "not available",
125
+ SpanAttributes.LLM_PATH: APIS["IMAGES_GENERATION"]["ENDPOINT"],
126
+ **get_extra_attributes(), # type: ignore
127
+ }
128
+
129
+ attributes = LLMSpanAttributes(**filter_valid_attributes(span_attributes))
130
+
131
+ with tracer.start_as_current_span(
132
+ name=get_span_name(APIS["IMAGES_GENERATION"]["METHOD"]),
133
+ kind=SpanKind.CLIENT,
134
+ context=set_span_in_context(trace.get_current_span()),
135
+ ) as span:
136
+ set_span_attributes(span, attributes)
137
+ try:
138
+ # Attempt to call the original method
139
+ result = await wrapped(*args, **kwargs)
140
+ if not is_streaming(kwargs):
141
+ data: Optional[ContentItem] = (
142
+ result.data[0]
143
+ if hasattr(result, "data") and len(result.data) > 0
144
+ else None
145
+ )
146
+ response = [
147
+ {
148
+ "role": "assistant",
149
+ "content": {
150
+ "url": getattr(data, "url", ""),
151
+ "revised_prompt": getattr(data, "revised_prompt", ""),
152
+ },
153
+ }
154
+ ]
155
+ set_event_completion(span, response)
156
+
157
+ span.set_status(StatusCode.OK)
158
+ return result
159
+ except Exception as err:
160
+ # Record the exception in the span
161
+ span.record_exception(err)
162
+
163
+ # Set the span status to indicate an error
164
+ span.set_status(Status(StatusCode.ERROR, str(err)))
165
+
166
+ # Reraise the exception to ensure it's not swallowed
167
+ raise
168
+
169
+ return traced_method
170
+
171
+
172
+ def images_edit(version: str, tracer: Tracer) -> Callable:
173
+ """
174
+ Wrap the `edit` method of the `Images` class to trace it.
175
+ """
176
+
177
+ def traced_method(
178
+ wrapped: Callable, instance: Any, args: List[Any], kwargs: ImagesEditKwargs
179
+ ) -> Any:
180
+ service_provider = SERVICE_PROVIDERS["LITELLM"]
181
+
182
+ span_attributes = {
183
+ **get_langtrace_attributes(version, service_provider, vendor_type="llm"),
184
+ **get_llm_request_attributes(kwargs, operation_name="images_edit"),
185
+ SpanAttributes.LLM_URL: "not available",
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(), # type: ignore
190
+ }
191
+
192
+ attributes = LLMSpanAttributes(**filter_valid_attributes(span_attributes))
193
+
194
+ with tracer.start_as_current_span(
195
+ name=APIS["IMAGES_EDIT"]["METHOD"],
196
+ kind=SpanKind.CLIENT,
197
+ context=set_span_in_context(trace.get_current_span()),
198
+ ) as span:
199
+ set_span_attributes(span, attributes)
200
+ try:
201
+ # Attempt to call the original method
202
+ result = wrapped(*args, **kwargs)
203
+
204
+ response = []
205
+ # Parse each image object
206
+ for each_data in result.data:
207
+ response.append(
208
+ {
209
+ "role": "assistant",
210
+ "content": {
211
+ "url": each_data.url,
212
+ "revised_prompt": each_data.revised_prompt,
213
+ "base64": each_data.b64_json,
214
+ },
215
+ }
216
+ )
217
+
218
+ set_event_completion(span, response)
219
+
220
+ span.set_status(StatusCode.OK)
221
+ return result
222
+ except Exception as err:
223
+ # Record the exception in the span
224
+ span.record_exception(err)
225
+
226
+ # Set the span status to indicate an error
227
+ span.set_status(Status(StatusCode.ERROR, str(err)))
228
+
229
+ # Reraise the exception to ensure it's not swallowed
230
+ raise
231
+
232
+ return traced_method
233
+
234
+
235
+ def chat_completions_create(version: str, tracer: Tracer) -> Callable:
236
+ """Wrap the `create` method of the `ChatCompletion` class to trace it."""
237
+
238
+ def traced_method(
239
+ wrapped: Callable,
240
+ instance: Any,
241
+ args: List[Any],
242
+ kwargs: ChatCompletionsCreateKwargs,
243
+ ) -> Any:
244
+ service_provider = SERVICE_PROVIDERS["LITELLM"]
245
+ if "perplexity" in get_base_url(instance):
246
+ service_provider = SERVICE_PROVIDERS["PPLX"]
247
+ elif "azure" in get_base_url(instance):
248
+ service_provider = SERVICE_PROVIDERS["AZURE"]
249
+ elif "groq" in get_base_url(instance):
250
+ service_provider = SERVICE_PROVIDERS["GROQ"]
251
+ llm_prompts = []
252
+ for item in kwargs.get("messages", []):
253
+ tools = get_tool_calls(item)
254
+ if tools is not None:
255
+ tool_calls = []
256
+ for tool_call in tools:
257
+ tool_call_dict = {
258
+ "id": getattr(tool_call, "id", ""),
259
+ "type": getattr(tool_call, "type", ""),
260
+ }
261
+ if hasattr(tool_call, "function"):
262
+ tool_call_dict["function"] = {
263
+ "name": getattr(tool_call.function, "name", ""),
264
+ "arguments": getattr(tool_call.function, "arguments", ""),
265
+ }
266
+ tool_calls.append(tool_call_dict)
267
+ llm_prompts.append(tool_calls)
268
+ else:
269
+ llm_prompts.append(item)
270
+
271
+ span_attributes = {
272
+ **get_langtrace_attributes(version, service_provider, vendor_type="llm"),
273
+ **get_llm_request_attributes(kwargs, prompts=llm_prompts),
274
+ SpanAttributes.LLM_URL: "not available",
275
+ SpanAttributes.LLM_PATH: APIS["CHAT_COMPLETION"]["ENDPOINT"],
276
+ **get_extra_attributes(), # type: ignore
277
+ }
278
+
279
+ attributes = LLMSpanAttributes(**filter_valid_attributes(span_attributes))
280
+
281
+ span = tracer.start_span(
282
+ name=get_span_name(APIS["CHAT_COMPLETION"]["METHOD"]),
283
+ kind=SpanKind.CLIENT,
284
+ context=set_span_in_context(trace.get_current_span()),
285
+ )
286
+ _set_input_attributes(span, kwargs, attributes)
287
+
288
+ try:
289
+ result = wrapped(*args, **kwargs)
290
+ if is_streaming(kwargs):
291
+ prompt_tokens = 0
292
+ for message in kwargs.get("messages", {}):
293
+ prompt_tokens += calculate_prompt_tokens(
294
+ json.dumps(str(message)), kwargs.get("model")
295
+ )
296
+ functions = kwargs.get("functions")
297
+ if functions is not None and functions != NOT_GIVEN:
298
+ for function in functions:
299
+ prompt_tokens += calculate_prompt_tokens(
300
+ json.dumps(function), kwargs.get("model")
301
+ )
302
+
303
+ return StreamWrapper(
304
+ result,
305
+ span,
306
+ prompt_tokens,
307
+ function_call=kwargs.get("functions") is not None,
308
+ tool_calls=kwargs.get("tools") is not None,
309
+ )
310
+ else:
311
+ _set_response_attributes(span, result)
312
+ span.set_status(StatusCode.OK)
313
+ span.end()
314
+ return result
315
+
316
+ except Exception as error:
317
+ span.record_exception(error)
318
+ span.set_status(Status(StatusCode.ERROR, str(error)))
319
+ span.end()
320
+ raise
321
+
322
+ return traced_method
323
+
324
+
325
+ def async_chat_completions_create(version: str, tracer: Tracer) -> Callable:
326
+ """Wrap the `create` method of the `ChatCompletion` class to trace it."""
327
+
328
+ async def traced_method(
329
+ wrapped: Callable,
330
+ instance: Any,
331
+ args: List[Any],
332
+ kwargs: ChatCompletionsCreateKwargs,
333
+ ) -> Awaitable[Any]:
334
+ service_provider = SERVICE_PROVIDERS["LITELLM"]
335
+ if "perplexity" in get_base_url(instance):
336
+ service_provider = SERVICE_PROVIDERS["PPLX"]
337
+ elif "azure" in get_base_url(instance):
338
+ service_provider = SERVICE_PROVIDERS["AZURE"]
339
+ llm_prompts = []
340
+ for item in kwargs.get("messages", []):
341
+ tools = get_tool_calls(item)
342
+ if tools is not None:
343
+ tool_calls = []
344
+ for tool_call in tools:
345
+ tool_call_dict = {
346
+ "id": getattr(tool_call, "id", ""),
347
+ "type": getattr(tool_call, "type", ""),
348
+ }
349
+ if hasattr(tool_call, "function"):
350
+ tool_call_dict["function"] = {
351
+ "name": getattr(tool_call.function, "name", ""),
352
+ "arguments": getattr(tool_call.function, "arguments", ""),
353
+ }
354
+ tool_calls.append(json.dumps(tool_call_dict))
355
+ llm_prompts.append(tool_calls)
356
+ else:
357
+ llm_prompts.append(item)
358
+
359
+ span_attributes = {
360
+ **get_langtrace_attributes(version, service_provider, vendor_type="llm"),
361
+ **get_llm_request_attributes(kwargs, prompts=llm_prompts),
362
+ SpanAttributes.LLM_URL: "not available",
363
+ SpanAttributes.LLM_PATH: APIS["CHAT_COMPLETION"]["ENDPOINT"],
364
+ **get_extra_attributes(), # type: ignore
365
+ }
366
+
367
+ attributes = LLMSpanAttributes(**filter_valid_attributes(span_attributes))
368
+
369
+ span = tracer.start_span(
370
+ name=get_span_name(APIS["CHAT_COMPLETION"]["METHOD"]),
371
+ kind=SpanKind.CLIENT,
372
+ context=set_span_in_context(trace.get_current_span()),
373
+ )
374
+ _set_input_attributes(span, kwargs, attributes)
375
+
376
+ try:
377
+ result = await wrapped(*args, **kwargs)
378
+ if is_streaming(kwargs):
379
+ prompt_tokens = 0
380
+ for message in kwargs.get("messages", {}):
381
+ prompt_tokens += calculate_prompt_tokens(
382
+ json.dumps((str(message))), kwargs.get("model")
383
+ )
384
+
385
+ functions = kwargs.get("functions")
386
+ if functions is not None and functions != NOT_GIVEN:
387
+ for function in functions:
388
+ prompt_tokens += calculate_prompt_tokens(
389
+ json.dumps(function), kwargs.get("model")
390
+ )
391
+
392
+ return StreamWrapper(
393
+ result,
394
+ span,
395
+ prompt_tokens,
396
+ function_call=kwargs.get("functions") is not None,
397
+ tool_calls=kwargs.get("tools") is not None,
398
+ ) # type: ignore
399
+ else:
400
+ _set_response_attributes(span, result)
401
+ span.set_status(StatusCode.OK)
402
+ span.end()
403
+ return result
404
+
405
+ except Exception as error:
406
+ span.record_exception(error)
407
+ span.set_status(Status(StatusCode.ERROR, str(error)))
408
+ span.end()
409
+ raise
410
+
411
+ return traced_method
412
+
413
+
414
+ def embeddings_create(version: str, tracer: Tracer) -> Callable:
415
+ """
416
+ Wrap the `create` method of the `Embeddings` class to trace it.
417
+ """
418
+
419
+ def traced_method(
420
+ wrapped: Callable,
421
+ instance: Any,
422
+ args: List[Any],
423
+ kwargs: EmbeddingsCreateKwargs,
424
+ ) -> Any:
425
+ service_provider = SERVICE_PROVIDERS["LITELLM"]
426
+
427
+ span_attributes = {
428
+ **get_langtrace_attributes(version, service_provider, vendor_type="llm"),
429
+ **get_llm_request_attributes(kwargs, operation_name="embed"),
430
+ SpanAttributes.LLM_URL: "not available",
431
+ SpanAttributes.LLM_PATH: APIS["EMBEDDINGS_CREATE"]["ENDPOINT"],
432
+ SpanAttributes.LLM_REQUEST_DIMENSIONS: kwargs.get("dimensions"),
433
+ **get_extra_attributes(), # type: ignore
434
+ }
435
+
436
+ encoding_format = kwargs.get("encoding_format")
437
+ if encoding_format is not None:
438
+ if not isinstance(encoding_format, list):
439
+ encoding_format = [encoding_format]
440
+ span_attributes[SpanAttributes.LLM_REQUEST_ENCODING_FORMATS] = (
441
+ encoding_format
442
+ )
443
+
444
+ if kwargs.get("input") is not None:
445
+ span_attributes[SpanAttributes.LLM_REQUEST_EMBEDDING_INPUTS] = json.dumps(
446
+ [kwargs.get("input", "")]
447
+ )
448
+
449
+ attributes = LLMSpanAttributes(**filter_valid_attributes(span_attributes))
450
+
451
+ with tracer.start_as_current_span(
452
+ name=get_span_name(APIS["EMBEDDINGS_CREATE"]["METHOD"]),
453
+ kind=SpanKind.CLIENT,
454
+ context=set_span_in_context(trace.get_current_span()),
455
+ ) as span:
456
+
457
+ set_span_attributes(span, attributes)
458
+ try:
459
+ # Attempt to call the original method
460
+ result = wrapped(*args, **kwargs)
461
+ span.set_status(StatusCode.OK)
462
+ return result
463
+ except Exception as err:
464
+ # Record the exception in the span
465
+ span.record_exception(err)
466
+
467
+ # Set the span status to indicate an error
468
+ span.set_status(Status(StatusCode.ERROR, str(err)))
469
+
470
+ # Reraise the exception to ensure it's not swallowed
471
+ raise
472
+
473
+ return traced_method
474
+
475
+
476
+ def async_embeddings_create(version: str, tracer: Tracer) -> Callable:
477
+ """
478
+ Wrap the `create` method of the `Embeddings` class to trace it.
479
+ """
480
+
481
+ async def traced_method(
482
+ wrapped: Callable,
483
+ instance: Any,
484
+ args: List[Any],
485
+ kwargs: EmbeddingsCreateKwargs,
486
+ ) -> Awaitable[Any]:
487
+
488
+ service_provider = SERVICE_PROVIDERS["LITELLM"]
489
+
490
+ span_attributes = {
491
+ **get_langtrace_attributes(version, service_provider, vendor_type="llm"),
492
+ **get_llm_request_attributes(kwargs, operation_name="embed"),
493
+ SpanAttributes.LLM_PATH: APIS["EMBEDDINGS_CREATE"]["ENDPOINT"],
494
+ SpanAttributes.LLM_REQUEST_DIMENSIONS: kwargs.get("dimensions"),
495
+ **get_extra_attributes(), # type: ignore
496
+ }
497
+
498
+ attributes = LLMSpanAttributes(**filter_valid_attributes(span_attributes))
499
+
500
+ encoding_format = kwargs.get("encoding_format")
501
+ if encoding_format is not None:
502
+ if not isinstance(encoding_format, list):
503
+ encoding_format = [encoding_format]
504
+ span_attributes[SpanAttributes.LLM_REQUEST_ENCODING_FORMATS] = (
505
+ encoding_format
506
+ )
507
+
508
+ if kwargs.get("input") is not None:
509
+ span_attributes[SpanAttributes.LLM_REQUEST_EMBEDDING_INPUTS] = json.dumps(
510
+ [kwargs.get("input", "")]
511
+ )
512
+
513
+ with tracer.start_as_current_span(
514
+ name=get_span_name(APIS["EMBEDDINGS_CREATE"]["METHOD"]),
515
+ kind=SpanKind.CLIENT,
516
+ context=set_span_in_context(trace.get_current_span()),
517
+ ) as span:
518
+
519
+ set_span_attributes(span, attributes)
520
+ try:
521
+ # Attempt to call the original method
522
+ result = await wrapped(*args, **kwargs)
523
+ span.set_status(StatusCode.OK)
524
+ return result
525
+ except Exception as err:
526
+ # Record the exception in the span
527
+ span.record_exception(err)
528
+
529
+ # Set the span status to indicate an error
530
+ span.set_status(Status(StatusCode.ERROR, str(err)))
531
+
532
+ # Reraise the exception to ensure it's not swallowed
533
+ raise
534
+
535
+ return traced_method
536
+
537
+
538
+ def extract_content(choice: Any) -> Union[str, List[Dict[str, Any]], Dict[str, Any]]:
539
+ # Check if choice.message exists and has a content attribute
540
+ if (
541
+ hasattr(choice, "message")
542
+ and hasattr(choice.message, "content")
543
+ and choice.message.content is not None
544
+ ):
545
+ return choice.message.content
546
+
547
+ # Check if choice.message has tool_calls and extract information accordingly
548
+ elif (
549
+ hasattr(choice, "message")
550
+ and hasattr(choice.message, "tool_calls")
551
+ and choice.message.tool_calls is not None
552
+ ):
553
+ result = [
554
+ {
555
+ "id": tool_call.id,
556
+ "type": tool_call.type,
557
+ "function": {
558
+ "name": tool_call.function.name,
559
+ "arguments": tool_call.function.arguments,
560
+ },
561
+ }
562
+ for tool_call in choice.message.tool_calls
563
+ ]
564
+ return result
565
+
566
+ # Check if choice.message has a function_call and extract information accordingly
567
+ elif (
568
+ hasattr(choice, "message")
569
+ and hasattr(choice.message, "function_call")
570
+ and choice.message.function_call is not None
571
+ ):
572
+ return {
573
+ "name": choice.message.function_call.name,
574
+ "arguments": choice.message.function_call.arguments,
575
+ }
576
+
577
+ # Return an empty string if none of the above conditions are met
578
+ else:
579
+ return ""
580
+
581
+
582
+ @silently_fail
583
+ def _set_input_attributes(
584
+ span: Span, kwargs: ChatCompletionsCreateKwargs, attributes: LLMSpanAttributes
585
+ ) -> None:
586
+ tools = []
587
+ for field, value in attributes.model_dump(by_alias=True).items():
588
+ set_span_attribute(span, field, value)
589
+ functions = kwargs.get("functions")
590
+ if functions is not None and functions != NOT_GIVEN:
591
+ for function in 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: Span, result: ResultType) -> None:
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 hasattr(choice, "content_filter_results")
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
+ )
@@ -0,0 +1,170 @@
1
+ """
2
+ Copyright (c) 2024 Scale3 Labs
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
12
+ """
13
+
14
+ from typing import Any, Dict, List, Union, Optional, TypedDict
15
+
16
+
17
+ class ContentItem:
18
+ url: str
19
+ revised_prompt: str
20
+ base64: Optional[str]
21
+
22
+ def __init__(
23
+ self,
24
+ url: str,
25
+ revised_prompt: str,
26
+ base64: Optional[str],
27
+ ):
28
+ self.url = url
29
+ self.revised_prompt = revised_prompt
30
+ self.base64 = base64
31
+
32
+
33
+ class ToolFunction:
34
+ name: str
35
+ arguments: str
36
+
37
+ def __init__(
38
+ self,
39
+ name: str,
40
+ arguments: str,
41
+ ):
42
+ self.name = name
43
+ self.arguments = arguments
44
+
45
+
46
+ class ToolCall:
47
+ id: str
48
+ type: str
49
+ function: ToolFunction
50
+
51
+ def __init__(
52
+ self,
53
+ id: str,
54
+ type: str,
55
+ function: ToolFunction,
56
+ ):
57
+ self.id = id
58
+ self.type = type
59
+ self.function = function
60
+
61
+
62
+ class Message:
63
+ role: str
64
+ content: Union[str, List[ContentItem], Dict[str, Any]]
65
+ tool_calls: Optional[List[ToolCall]]
66
+
67
+ def __init__(
68
+ self,
69
+ role: str,
70
+ content: Union[str, List[ContentItem], Dict[str, Any]],
71
+ content_filter_results: Optional[Any],
72
+ ):
73
+ self.role = role
74
+ self.content = content
75
+ self.content_filter_results = content_filter_results
76
+
77
+
78
+ class Usage:
79
+ prompt_tokens: int
80
+ completion_tokens: int
81
+ total_tokens: int
82
+
83
+ def __init__(
84
+ self,
85
+ prompt_tokens: int,
86
+ completion_tokens: int,
87
+ total_tokens: int,
88
+ ):
89
+ self.prompt_tokens = prompt_tokens
90
+ self.completion_tokens = completion_tokens
91
+ self.total_tokens = total_tokens
92
+
93
+
94
+ class Choice:
95
+ message: Message
96
+ content_filter_results: Optional[Any]
97
+
98
+ def __init__(
99
+ self,
100
+ message: Message,
101
+ content_filter_results: Optional[Any],
102
+ ):
103
+ self.message = message
104
+ self.content_filter_results = content_filter_results
105
+
106
+
107
+ class ResultType:
108
+ model: Optional[str]
109
+ content: List[ContentItem]
110
+ system_fingerprint: Optional[str]
111
+ usage: Optional[Usage]
112
+ choices: Optional[List[Choice]]
113
+ response_format: Optional[str]
114
+ size: Optional[str]
115
+ encoding_format: Optional[str]
116
+
117
+ def __init__(
118
+ self,
119
+ model: Optional[str],
120
+ role: Optional[str],
121
+ content: List[ContentItem],
122
+ system_fingerprint: Optional[str],
123
+ usage: Optional[Usage],
124
+ functions: Optional[List[ToolCall]],
125
+ tools: Optional[List[ToolCall]],
126
+ choices: Optional[List[Choice]],
127
+ response_format: Optional[str],
128
+ size: Optional[str],
129
+ encoding_format: Optional[str],
130
+ ):
131
+ self.model = model
132
+ self.role = role
133
+ self.content = content
134
+ self.system_fingerprint = system_fingerprint
135
+ self.usage = usage
136
+ self.functions = functions
137
+ self.tools = tools
138
+ self.choices = choices
139
+ self.response_format = response_format
140
+ self.size = size
141
+ self.encoding_format = encoding_format
142
+
143
+
144
+ class ImagesGenerateKwargs(TypedDict, total=False):
145
+ operation_name: str
146
+ model: Optional[str]
147
+ messages: Optional[List[Message]]
148
+ functions: Optional[List[ToolCall]]
149
+ tools: Optional[List[ToolCall]]
150
+ response_format: Optional[str]
151
+ size: Optional[str]
152
+ encoding_format: Optional[str]
153
+
154
+
155
+ class ImagesEditKwargs(TypedDict, total=False):
156
+ response_format: Optional[str]
157
+ size: Optional[str]
158
+
159
+
160
+ class ChatCompletionsCreateKwargs(TypedDict, total=False):
161
+ model: Optional[str]
162
+ messages: List[Message]
163
+ functions: Optional[List[ToolCall]]
164
+ tools: Optional[List[ToolCall]]
165
+
166
+
167
+ class EmbeddingsCreateKwargs(TypedDict, total=False):
168
+ dimensions: Optional[str]
169
+ input: Union[str, List[str], None]
170
+ encoding_format: Optional[Union[List[str], str]]
@@ -46,6 +46,7 @@ from langtrace_python_sdk.instrumentation import (
46
46
  LangchainCoreInstrumentation,
47
47
  LangchainInstrumentation,
48
48
  LanggraphInstrumentation,
49
+ LiteLLMInstrumentation,
49
50
  LlamaindexInstrumentation,
50
51
  MistralInstrumentation,
51
52
  OllamaInstrumentor,
@@ -137,6 +138,7 @@ def init(
137
138
  "langchain-core": LangchainCoreInstrumentation(),
138
139
  "langchain-community": LangchainCommunityInstrumentation(),
139
140
  "langgraph": LanggraphInstrumentation(),
141
+ "litellm": LiteLLMInstrumentation(),
140
142
  "anthropic": AnthropicInstrumentation(),
141
143
  "cohere": CohereInstrumentation(),
142
144
  "weaviate-client": WeaviateInstrumentation(),
@@ -1 +1 @@
1
- __version__ = "2.3.21"
1
+ __version__ = "2.3.22"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: langtrace-python-sdk
3
- Version: 2.3.21
3
+ Version: 2.3.22
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>
@@ -23,6 +23,7 @@ Requires-Dist: sqlalchemy
23
23
  Requires-Dist: tiktoken>=0.1.1
24
24
  Requires-Dist: trace-attributes==7.0.4
25
25
  Requires-Dist: transformers>=4.11.3
26
+ Requires-Dist: ujson>=5.10.0
26
27
  Provides-Extra: dev
27
28
  Requires-Dist: anthropic; extra == 'dev'
28
29
  Requires-Dist: chromadb; extra == 'dev'
@@ -34,6 +35,7 @@ Requires-Dist: groq; extra == 'dev'
34
35
  Requires-Dist: langchain; extra == 'dev'
35
36
  Requires-Dist: langchain-community; extra == 'dev'
36
37
  Requires-Dist: langchain-openai; extra == 'dev'
38
+ Requires-Dist: litellm; extra == 'dev'
37
39
  Requires-Dist: mistralai; extra == 'dev'
38
40
  Requires-Dist: ollama; extra == 'dev'
39
41
  Requires-Dist: openai==1.30.1; extra == 'dev'
@@ -287,6 +289,14 @@ By default, prompt and completion data are captured. If you would like to opt ou
287
289
 
288
290
  `TRACE_PROMPT_COMPLETION_DATA=false`
289
291
 
292
+ ### Enable/Disable checkpoint tracing for DSPy
293
+
294
+ By default, checkpoints are traced for DSPy pipelines. If you would like to disable it, set the following env var,
295
+
296
+ `TRACE_DSPY_CHECKPOINT=false`
297
+
298
+ Note: Checkpoint tracing will increase the latency of executions as the state is serialized. Please disable it in production.
299
+
290
300
  ## Supported integrations
291
301
 
292
302
  Langtrace automatically captures traces from the following vendors:
@@ -302,8 +312,9 @@ Langtrace automatically captures traces from the following vendors:
302
312
  | Gemini | LLM | :x: | :white_check_mark: |
303
313
  | Mistral | LLM | :x: | :white_check_mark: |
304
314
  | Langchain | Framework | :x: | :white_check_mark: |
305
- | LlamaIndex | Framework | :white_check_mark: | :white_check_mark: |
306
315
  | Langgraph | Framework | :x: | :white_check_mark: |
316
+ | LlamaIndex | Framework | :white_check_mark: | :white_check_mark: |
317
+ | LiteLLM | Framework | :x: | :white_check_mark: |
307
318
  | DSPy | Framework | :x: | :white_check_mark: |
308
319
  | CrewAI | Framework | :x: | :white_check_mark: |
309
320
  | Ollama | Framework | :x: | :white_check_mark: |
@@ -40,6 +40,7 @@ examples/dspy_example/math_problems_cot_parallel.py,sha256=5clw-IIVA0mWm0N0xWNDM
40
40
  examples/dspy_example/program_of_thought_basic.py,sha256=oEbtJdeKENMUbex25-zyStWwurRWW6OdP0KDs-jUkko,984
41
41
  examples/dspy_example/quiz_gen.py,sha256=OyGhepeX8meKOtLdmlYUjMD2ECk-ZQuQXUZif1hFQY4,3371
42
42
  examples/dspy_example/react.py,sha256=APAnHqgy9w-qY5jnPD_WbBx6bwo9C-DhPnUuhL-t7sg,1376
43
+ examples/dspy_example/optimizers/bootstrap_fewshot.py,sha256=IxJJIaPKowP0-iZSuviKQnhc0bLj0_46cO13O9vLAlc,3135
43
44
  examples/embedchain_example/simple.py,sha256=1lwnsh5wVjGjQ18OinID6aJ_itR-x0TOngtNU1E-Emc,373
44
45
  examples/fastapi_example/__init__.py,sha256=INIfvJP7zC_KkJCtulS1qbh61-MJTPAHnzAgzeKi0yU,87
45
46
  examples/fastapi_example/basic_route.py,sha256=_IRXjkOtJQ-bTIGa1WbvUF_2LF4bjghjyXt4YrHaRvw,1170
@@ -70,7 +71,7 @@ examples/ollama_example/basic.py,sha256=EPbsigOF4xBDBgLgAD0EzPo737ycVm7aXZr7F5Xt
70
71
  examples/openai_example/__init__.py,sha256=6faH7wTegSozKmS89sd1Tgv8AcEH0GfKkC7YaBWA8tg,849
71
72
  examples/openai_example/async_tool_calling_nonstreaming.py,sha256=H1-CrNfNDfqAkB5wEipITXlW2OsYL7XD5uQb6k3C6ps,3865
72
73
  examples/openai_example/async_tool_calling_streaming.py,sha256=LaSKmn_Unv55eTHXYdEmKjo39eNuB3ASOBV-m8U1HfU,7136
73
- examples/openai_example/chat_completion.py,sha256=HPFdM0lA01yo5VvZRRdgczyPSa-eurnALP723laOv6M,1192
74
+ examples/openai_example/chat_completion.py,sha256=lOp5BK-LXj0g1ErP3Ry6moUVMDkpbF-f6S4uqwNbhCo,1217
74
75
  examples/openai_example/chat_completion_tool_choice.py,sha256=rkOjbFnIJ5hWWHWg-aTSek41UN2PBfufGpdaFhkWYj8,2356
75
76
  examples/openai_example/embeddings_create.py,sha256=kcOZpl5nhHo_NC-3n2yKX5W8mAzNfut43mSy1BmQJUI,555
76
77
  examples/openai_example/function_calling.py,sha256=zz-JdCcpP7uCXG21EYXF1Y39IKj6gYt2fOP5N_ywpnc,2338
@@ -95,18 +96,19 @@ examples/vertexai_example/main.py,sha256=gndId5X5ksD-ycxnAWMdEqIDbLc3kz5Vt8vm4YP
95
96
  examples/weaviate_example/__init__.py,sha256=8JMDBsRSEV10HfTd-YC7xb4txBjD3la56snk-Bbg2Kw,618
96
97
  examples/weaviate_example/query_text.py,sha256=wPHQTc_58kPoKTZMygVjTj-2ZcdrIuaausJfMxNQnQc,127162
97
98
  langtrace_python_sdk/__init__.py,sha256=VZM6i71NR7pBQK6XvJWRelknuTYUhqwqE7PlicKa5Wg,1166
98
- langtrace_python_sdk/langtrace.py,sha256=vfDVtcWIDf8_01qp15zfRB92qbGEBUGu0daF9BlyspY,8863
99
- langtrace_python_sdk/version.py,sha256=TFqy6zAnse9cNqw1cAEbkzgx35wTL1grwvwajOcRgtE,23
99
+ langtrace_python_sdk/langtrace.py,sha256=YJUHbxNjhtlQvTbkmN-tqmGiKp3Yog70KR7GVlhIr7c,8936
100
+ langtrace_python_sdk/version.py,sha256=t_d6fGpbp-4uYw6IIW6eCTz39jbZes5f7hJjns8ZIZk,23
100
101
  langtrace_python_sdk/constants/__init__.py,sha256=3CNYkWMdd1DrkGqzLUgNZXjdAlM6UFMlf_F-odAToyc,146
101
102
  langtrace_python_sdk/constants/exporter/langtrace_exporter.py,sha256=5MNjnAOg-4am78J3gVMH6FSwq5N8TOj72ugkhsw4vi0,46
102
103
  langtrace_python_sdk/constants/instrumentation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
103
104
  langtrace_python_sdk/constants/instrumentation/anthropic.py,sha256=YX3llt3zwDY6XrYk3CB8WEVqgrzRXEw_ffyk56JoF3k,126
104
105
  langtrace_python_sdk/constants/instrumentation/chroma.py,sha256=hiPGYdHS0Yj4Kh3eaYBbuCAl_swqIygu80yFqkOgdak,955
105
106
  langtrace_python_sdk/constants/instrumentation/cohere.py,sha256=tf9sDfb5K3qOAHChEE5o8eYWPZ1io58VsOjZDCZPxfw,577
106
- langtrace_python_sdk/constants/instrumentation/common.py,sha256=bjst43qOtzIEAZMr-BS5Qzgv79dkt6GppkAG0_cLFzI,1019
107
+ langtrace_python_sdk/constants/instrumentation/common.py,sha256=yqSheP9Yx_otzrau3KgdMSNHMvBpWzt2ahifoDTbLCg,1045
107
108
  langtrace_python_sdk/constants/instrumentation/embedchain.py,sha256=HodCJvaFjILoOG50OwFObxfVxt_8VUaIAIqvgoN3tzo,278
108
109
  langtrace_python_sdk/constants/instrumentation/gemini.py,sha256=UAmfgg9FM7uNeOCdPfWlir6OIH-8BoxFGPRpdBd9ZZs,358
109
110
  langtrace_python_sdk/constants/instrumentation/groq.py,sha256=VFXmIl4aqGY_fS0PAmjPj_Qm7Tibxbx7Ur_e7rQpqXc,134
111
+ langtrace_python_sdk/constants/instrumentation/litellm.py,sha256=bMAlpY2scFe6Lql0Nl7euGNSO9QEV5Uzne12hnw3mSE,449
110
112
  langtrace_python_sdk/constants/instrumentation/mistral.py,sha256=9PlmcC5P5_BHJ-zsX1xekht6rSm7arTin58HAfdYvLk,730
111
113
  langtrace_python_sdk/constants/instrumentation/ollama.py,sha256=H_-S0xjqRsi5qSp7mAlK7Y9NlQ3BqOkG6ASogqqgdJY,212
112
114
  langtrace_python_sdk/constants/instrumentation/openai.py,sha256=uEOH5UXapU2DSf2AdgXTRhhJEHGWXUNFkUGD5QafflM,1164
@@ -117,7 +119,7 @@ langtrace_python_sdk/constants/instrumentation/weaviate.py,sha256=gtv-JBxvNGClEM
117
119
  langtrace_python_sdk/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
118
120
  langtrace_python_sdk/extensions/langtrace_exporter.py,sha256=UFupNL03zklVd5penpsfXjbWSb5qB39mEv2BY2wczSs,6307
119
121
  langtrace_python_sdk/extensions/langtrace_filesystem.py,sha256=34fZutG28EJ66l67OvTGsydAH3ZpXgikdE7hVLqBpG4,7863
120
- langtrace_python_sdk/instrumentation/__init__.py,sha256=2ZSUFvF1Lv15PQvPGk8a_RiukjFKWOM5esDot5ZOkbo,1622
122
+ langtrace_python_sdk/instrumentation/__init__.py,sha256=U2uQxrczJzPxZUFaRniN2iEK5ujRk7QadG7iM0sLDEc,1696
121
123
  langtrace_python_sdk/instrumentation/anthropic/__init__.py,sha256=donrurJAGYlxrSRA3BIf76jGeUcAx9Tq8CVpah68S0Y,101
122
124
  langtrace_python_sdk/instrumentation/anthropic/instrumentation.py,sha256=ndXdruI0BG7n75rsuEpKjfzePxrZxg40gZ39ONmD_v4,1845
123
125
  langtrace_python_sdk/instrumentation/anthropic/patch.py,sha256=ztPN4VZujoxYOKhTbFnup7Ibms9NAzYCPAJY43NUgKw,4935
@@ -136,7 +138,7 @@ langtrace_python_sdk/instrumentation/crewai/instrumentation.py,sha256=5Umzq8zjEn
136
138
  langtrace_python_sdk/instrumentation/crewai/patch.py,sha256=C2OKKPC-pzfzZWxPc74kHdYsKTX9yRhOgVY47WY9KN8,9109
137
139
  langtrace_python_sdk/instrumentation/dspy/__init__.py,sha256=tM1srfi_QgyCzrde4izojMrRq2Wm7Dj5QUvVQXIJzkk,84
138
140
  langtrace_python_sdk/instrumentation/dspy/instrumentation.py,sha256=o8URiDvCbZ8LL0I-4xKHkn_Ms2sETBRpn-gOliv3xzQ,2929
139
- langtrace_python_sdk/instrumentation/dspy/patch.py,sha256=8vDa3SAlo4Z0X-BOVO-wYxXPwHtISi4k7NwWwL-LOV4,9978
141
+ langtrace_python_sdk/instrumentation/dspy/patch.py,sha256=H7zF4PVdtepOSpzJuEcckKUjnZQYKlY7yhn3dk6xbpY,10458
140
142
  langtrace_python_sdk/instrumentation/embedchain/__init__.py,sha256=5L6n8-brMnRWZ0CMmHEuN1mrhIxrYLNtxRy0Ujc-hOY,103
141
143
  langtrace_python_sdk/instrumentation/embedchain/instrumentation.py,sha256=dShwm0duy25IvL7g9I_v-2oYuyh2fadeiJqXtXBay-8,1987
142
144
  langtrace_python_sdk/instrumentation/embedchain/patch.py,sha256=ovvBrtqUDwGSmSgK_S3pOOrDa4gkPSFG-HvmsxqmJE8,3627
@@ -158,6 +160,10 @@ langtrace_python_sdk/instrumentation/langchain_core/patch.py,sha256=CXEfbq6E88X_
158
160
  langtrace_python_sdk/instrumentation/langgraph/__init__.py,sha256=eitlHloY-aZ4ZuIEJx61AadEA3G7siyecP-V-lziAr8,101
159
161
  langtrace_python_sdk/instrumentation/langgraph/instrumentation.py,sha256=SUZZhWSIbcfsF1S5NtEqW8QzkRM_pKAuXB7pwk5tsOU,2526
160
162
  langtrace_python_sdk/instrumentation/langgraph/patch.py,sha256=PGe1ZywXctB_yYqnp8AtD8Xqj7EZ087-S5_2vLRYhEQ,4987
163
+ langtrace_python_sdk/instrumentation/litellm/__init__.py,sha256=8uziCc56rFSRiPkYcrcBRbtppOANkZ7uZssCKAl2MKk,97
164
+ langtrace_python_sdk/instrumentation/litellm/instrumentation.py,sha256=Km2q_yfZU6nSqPEXG2xbtTSjqv7xSS92Kxqzw-GtQno,2655
165
+ langtrace_python_sdk/instrumentation/litellm/patch.py,sha256=6ed50KrSC-2Upoh12BlcqfRVzZ1iXcTr8U9cVh9LhvU,24263
166
+ langtrace_python_sdk/instrumentation/litellm/types.py,sha256=aVkoa7tmAbYfyOhnyMrDaVjQuwhmRNLMthlNtKMtWX8,4311
161
167
  langtrace_python_sdk/instrumentation/llamaindex/__init__.py,sha256=rHvuqpuQKLj57Ow7vuKRqxAN5jT0b5NBeHwhXbbnRa4,103
162
168
  langtrace_python_sdk/instrumentation/llamaindex/instrumentation.py,sha256=8iAg-Oxwf2W4S60qRfO5mvzORYxublgq7FdGWqUB4q8,2965
163
169
  langtrace_python_sdk/instrumentation/llamaindex/patch.py,sha256=548hzPyT_k-2wmt9AArv4JzTT4j4AGKJq5Ar2bWv7o8,4615
@@ -235,8 +241,8 @@ tests/pinecone/cassettes/test_query.yaml,sha256=b5v9G3ssUy00oG63PlFUR3JErF2Js-5A
235
241
  tests/pinecone/cassettes/test_upsert.yaml,sha256=neWmQ1v3d03V8WoLl8FoFeeCYImb8pxlJBWnFd_lITU,38607
236
242
  tests/qdrant/conftest.py,sha256=9n0uHxxIjWk9fbYc4bx-uP8lSAgLBVx-cV9UjnsyCHM,381
237
243
  tests/qdrant/test_qdrant.py,sha256=pzjAjVY2kmsmGfrI2Gs2xrolfuaNHz7l1fqGQCjp5_o,3353
238
- langtrace_python_sdk-2.3.21.dist-info/METADATA,sha256=PpzO9NrNE901yxWYrCuy4eBWqxpo2tKPN-Yilv5_Rfg,15380
239
- langtrace_python_sdk-2.3.21.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
240
- langtrace_python_sdk-2.3.21.dist-info/entry_points.txt,sha256=1_b9-qvf2fE7uQNZcbUei9vLpFZBbbh9LrtGw95ssAo,70
241
- langtrace_python_sdk-2.3.21.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
242
- langtrace_python_sdk-2.3.21.dist-info/RECORD,,
244
+ langtrace_python_sdk-2.3.22.dist-info/METADATA,sha256=nVVecbmqTGT_8puoT55JzFEOe2zT9TI-9HJ9Eqd5S7U,15861
245
+ langtrace_python_sdk-2.3.22.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
246
+ langtrace_python_sdk-2.3.22.dist-info/entry_points.txt,sha256=1_b9-qvf2fE7uQNZcbUei9vLpFZBbbh9LrtGw95ssAo,70
247
+ langtrace_python_sdk-2.3.22.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
248
+ langtrace_python_sdk-2.3.22.dist-info/RECORD,,