langtrace-python-sdk 2.1.16__py3-none-any.whl → 2.1.19__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.
Files changed (39) hide show
  1. examples/inspect_ai_example/basic_eval.py +24 -0
  2. examples/langchain_example/basic.py +23 -8
  3. examples/llamaindex_example/agent.py +1 -1
  4. examples/llamaindex_example/basic.py +5 -1
  5. examples/ollama_example/__init__.py +14 -0
  6. examples/ollama_example/basic.py +50 -0
  7. examples/openai_example/chat_completion.py +1 -1
  8. examples/pinecone_example/basic.py +1 -0
  9. langtrace_python_sdk/__init__.py +4 -5
  10. langtrace_python_sdk/constants/instrumentation/common.py +1 -0
  11. langtrace_python_sdk/constants/instrumentation/ollama.py +7 -0
  12. langtrace_python_sdk/constants/instrumentation/pinecone.py +1 -1
  13. langtrace_python_sdk/extensions/langtrace_filesystem.py +194 -0
  14. langtrace_python_sdk/instrumentation/__init__.py +2 -0
  15. langtrace_python_sdk/instrumentation/chroma/patch.py +7 -3
  16. langtrace_python_sdk/instrumentation/groq/patch.py +5 -2
  17. langtrace_python_sdk/instrumentation/langchain/patch.py +8 -2
  18. langtrace_python_sdk/instrumentation/langchain_community/patch.py +8 -2
  19. langtrace_python_sdk/instrumentation/langchain_core/patch.py +13 -3
  20. langtrace_python_sdk/instrumentation/langgraph/patch.py +7 -2
  21. langtrace_python_sdk/instrumentation/llamaindex/patch.py +7 -3
  22. langtrace_python_sdk/instrumentation/ollama/__init__.py +3 -0
  23. langtrace_python_sdk/instrumentation/ollama/instrumentation.py +58 -0
  24. langtrace_python_sdk/instrumentation/ollama/patch.py +215 -0
  25. langtrace_python_sdk/instrumentation/openai/patch.py +20 -8
  26. langtrace_python_sdk/instrumentation/pinecone/instrumentation.py +0 -1
  27. langtrace_python_sdk/instrumentation/pinecone/patch.py +7 -3
  28. langtrace_python_sdk/instrumentation/qdrant/patch.py +7 -2
  29. langtrace_python_sdk/instrumentation/weaviate/patch.py +7 -3
  30. langtrace_python_sdk/langtrace.py +14 -2
  31. langtrace_python_sdk/types/__init__.py +77 -1
  32. langtrace_python_sdk/utils/langtrace_sampler.py +56 -0
  33. langtrace_python_sdk/utils/with_root_span.py +11 -2
  34. langtrace_python_sdk/version.py +1 -1
  35. {langtrace_python_sdk-2.1.16.dist-info → langtrace_python_sdk-2.1.19.dist-info}/METADATA +3 -1
  36. {langtrace_python_sdk-2.1.16.dist-info → langtrace_python_sdk-2.1.19.dist-info}/RECORD +39 -29
  37. langtrace_python_sdk-2.1.19.dist-info/entry_points.txt +2 -0
  38. {langtrace_python_sdk-2.1.16.dist-info → langtrace_python_sdk-2.1.19.dist-info}/WHEEL +0 -0
  39. {langtrace_python_sdk-2.1.16.dist-info → langtrace_python_sdk-2.1.19.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,58 @@
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
+ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
18
+ from opentelemetry.trace import get_tracer
19
+ from wrapt import wrap_function_wrapper as _W
20
+ from typing import Collection
21
+ from importlib_metadata import version as v
22
+ from langtrace_python_sdk.constants.instrumentation.ollama import APIS
23
+ from .patch import generic_patch, ageneric_patch
24
+
25
+
26
+ class OllamaInstrumentor(BaseInstrumentor):
27
+ """
28
+ The OllamaInstrumentor class represents the Ollama instrumentation"""
29
+
30
+ def instrumentation_dependencies(self) -> Collection[str]:
31
+ return ["ollama >= 0.2.0, < 1"]
32
+
33
+ def _instrument(self, **kwargs):
34
+ tracer_provider = kwargs.get("tracer_provider")
35
+ tracer = get_tracer(__name__, "", tracer_provider)
36
+ version = v("ollama")
37
+ for operation_name, details in APIS.items():
38
+ operation = details["METHOD"]
39
+ # Dynamically creating the patching call
40
+ _W(
41
+ "ollama._client",
42
+ f"Client.{operation}",
43
+ generic_patch(operation_name, version, tracer),
44
+ )
45
+
46
+ _W(
47
+ "ollama._client",
48
+ f"AsyncClient.{operation}",
49
+ ageneric_patch(operation_name, version, tracer),
50
+ )
51
+ _W(
52
+ "ollama",
53
+ f"{operation}",
54
+ generic_patch(operation_name, version, tracer),
55
+ )
56
+
57
+ def _uninstrument(self, **kwargs):
58
+ pass
@@ -0,0 +1,215 @@
1
+ from langtrace_python_sdk.constants.instrumentation.ollama import APIS
2
+ from importlib_metadata import version as v
3
+ from langtrace_python_sdk.constants import LANGTRACE_SDK_NAME
4
+ from langtrace_python_sdk.utils import set_span_attribute
5
+ from langtrace_python_sdk.utils.silently_fail import silently_fail
6
+ from langtrace_python_sdk.constants.instrumentation.common import (
7
+ LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
8
+ SERVICE_PROVIDERS,
9
+ )
10
+ from opentelemetry import baggage
11
+ from langtrace.trace_attributes import LLMSpanAttributes, Event
12
+ from opentelemetry.trace import SpanKind
13
+ import json
14
+ from opentelemetry.trace.status import Status, StatusCode
15
+
16
+
17
+ def generic_patch(operation_name, version, tracer):
18
+ def traced_method(wrapped, instance, args, kwargs):
19
+ base_url = (
20
+ str(instance._client._base_url)
21
+ if hasattr(instance, "_client") and hasattr(instance._client, "_base_url")
22
+ else ""
23
+ )
24
+ api = APIS[operation_name]
25
+ service_provider = SERVICE_PROVIDERS["OLLAMA"]
26
+ extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
27
+ span_attributes = {
28
+ "langtrace.sdk.name": "langtrace-python-sdk",
29
+ "langtrace.service.name": service_provider,
30
+ "langtrace.service.type": "llm",
31
+ "langtrace.service.version": version,
32
+ "langtrace.version": v(LANGTRACE_SDK_NAME),
33
+ "llm.model": kwargs.get("model"),
34
+ "llm.stream": kwargs.get("stream"),
35
+ "url.full": base_url,
36
+ "llm.api": api["METHOD"],
37
+ **(extra_attributes if extra_attributes is not None else {}),
38
+ }
39
+
40
+ attributes = LLMSpanAttributes(**span_attributes)
41
+ with tracer.start_as_current_span(
42
+ f'ollama.{api["METHOD"]}', kind=SpanKind.CLIENT
43
+ ) as span:
44
+ _set_input_attributes(span, kwargs, attributes)
45
+
46
+ try:
47
+ result = wrapped(*args, **kwargs)
48
+ if result:
49
+ if span.is_recording():
50
+
51
+ if kwargs.get("stream"):
52
+ return _handle_streaming_response(
53
+ span, result, api["METHOD"]
54
+ )
55
+
56
+ _set_response_attributes(span, result)
57
+ span.set_status(Status(StatusCode.OK))
58
+
59
+ span.end()
60
+ return result
61
+
62
+ except Exception as err:
63
+ # Record the exception in the span
64
+ span.record_exception(err)
65
+
66
+ # Set the span status to indicate an error
67
+ span.set_status(Status(StatusCode.ERROR, str(err)))
68
+
69
+ # Reraise the exception to ensure it's not swallowed
70
+ raise
71
+
72
+ return traced_method
73
+
74
+
75
+ def ageneric_patch(operation_name, version, tracer):
76
+ async def traced_method(wrapped, instance, args, kwargs):
77
+ api = APIS[operation_name]
78
+ service_provider = SERVICE_PROVIDERS["OLLAMA"]
79
+ extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
80
+ span_attributes = {
81
+ "langtrace.sdk.name": "langtrace-python-sdk",
82
+ "langtrace.service.name": service_provider,
83
+ "url.full": "",
84
+ "llm.api": "",
85
+ "langtrace.service.type": "llm",
86
+ "langtrace.service.version": version,
87
+ "langtrace.version": v(LANGTRACE_SDK_NAME),
88
+ "llm.model": kwargs.get("model"),
89
+ "llm.stream": kwargs.get("stream"),
90
+ **(extra_attributes if extra_attributes is not None else {}),
91
+ }
92
+
93
+ attributes = LLMSpanAttributes(**span_attributes)
94
+ with tracer.start_as_current_span(api["METHOD"], kind=SpanKind.CLIENT) as span:
95
+ _set_input_attributes(span, kwargs, attributes)
96
+ try:
97
+ result = await wrapped(*args, **kwargs)
98
+ if result:
99
+ if span.is_recording():
100
+ if kwargs.get("stream"):
101
+ return _ahandle_streaming_response(
102
+ span, result, api["METHOD"]
103
+ )
104
+
105
+ _set_response_attributes(span, result)
106
+ span.set_status(Status(StatusCode.OK))
107
+ span.end()
108
+ return result
109
+ except Exception as err:
110
+ # Record the exception in the span
111
+ span.record_exception(err)
112
+
113
+ # Set the span status to indicate an error
114
+ span.set_status(Status(StatusCode.ERROR, str(err)))
115
+
116
+ # Reraise the exception to ensure it's not swallowed
117
+ raise
118
+
119
+ return traced_method
120
+
121
+
122
+ @silently_fail
123
+ def _set_response_attributes(span, response):
124
+
125
+ input_tokens = response.get("prompt_eval_count") or 0
126
+ output_tokens = response.get("eval_count") or 0
127
+ total_tokens = input_tokens + output_tokens
128
+ usage_dict = {
129
+ "input_tokens": input_tokens,
130
+ "output_tokens": output_tokens,
131
+ "total_tokens": total_tokens,
132
+ }
133
+
134
+ if total_tokens > 0:
135
+ set_span_attribute(span, "llm.token.counts", json.dumps(usage_dict))
136
+ set_span_attribute(span, "llm.finish_reason", response.get("done_reason"))
137
+
138
+ if "message" in response:
139
+ set_span_attribute(span, "llm.responses", json.dumps([response.get("message")]))
140
+
141
+ if "response" in response:
142
+ set_span_attribute(
143
+ span, "llm.responses", json.dumps([response.get("response")])
144
+ )
145
+
146
+
147
+ @silently_fail
148
+ def _set_input_attributes(span, kwargs, attributes):
149
+ for field, value in attributes.model_dump(by_alias=True).items():
150
+ set_span_attribute(span, field, value)
151
+
152
+ if "messages" in kwargs:
153
+ set_span_attribute(
154
+ span,
155
+ "llm.prompts",
156
+ json.dumps([kwargs.get("messages", [])]),
157
+ )
158
+ if "prompt" in kwargs:
159
+ set_span_attribute(
160
+ span,
161
+ "llm.prompts",
162
+ json.dumps([{"role": "user", "content": kwargs.get("prompt", [])}]),
163
+ )
164
+
165
+
166
+ def _handle_streaming_response(span, response, api):
167
+ accumulated_tokens = None
168
+ if api == "chat":
169
+ accumulated_tokens = {"message": {"content": "", "role": ""}}
170
+ if api == "completion":
171
+ accumulated_tokens = {"response": ""}
172
+
173
+ span.add_event(Event.STREAM_START.value)
174
+ try:
175
+ for chunk in response:
176
+ if api == "chat":
177
+ accumulated_tokens["message"]["content"] += chunk["message"]["content"]
178
+ accumulated_tokens["message"]["role"] = chunk["message"]["role"]
179
+ if api == "generate":
180
+ accumulated_tokens["response"] += chunk["response"]
181
+
182
+ _set_response_attributes(span, chunk | accumulated_tokens)
183
+ finally:
184
+ # Finalize span after processing all chunks
185
+ span.add_event(Event.STREAM_END.value)
186
+ span.set_status(StatusCode.OK)
187
+ span.end()
188
+
189
+ return response
190
+
191
+
192
+ async def _ahandle_streaming_response(span, response, api):
193
+ accumulated_tokens = None
194
+ if api == "chat":
195
+ accumulated_tokens = {"message": {"content": "", "role": ""}}
196
+ if api == "completion":
197
+ accumulated_tokens = {"response": ""}
198
+
199
+ span.add_event(Event.STREAM_START.value)
200
+ try:
201
+ async for chunk in response:
202
+ if api == "chat":
203
+ accumulated_tokens["message"]["content"] += chunk["message"]["content"]
204
+ accumulated_tokens["message"]["role"] = chunk["message"]["role"]
205
+ if api == "generate":
206
+ accumulated_tokens["response"] += chunk["response"]
207
+
208
+ _set_response_attributes(span, chunk | accumulated_tokens)
209
+ finally:
210
+ # Finalize span after processing all chunks
211
+ span.add_event(Event.STREAM_END.value)
212
+ span.set_status(StatusCode.OK)
213
+ span.end()
214
+
215
+ return response
@@ -18,10 +18,10 @@ import json
18
18
 
19
19
  from importlib_metadata import version as v
20
20
  from langtrace.trace_attributes import Event, LLMSpanAttributes
21
- from opentelemetry import baggage
21
+ from opentelemetry import baggage, trace
22
22
  from opentelemetry.trace import SpanKind
23
23
  from opentelemetry.trace.status import Status, StatusCode
24
-
24
+ from opentelemetry.trace.propagation import set_span_in_context
25
25
  from langtrace_python_sdk.constants import LANGTRACE_SDK_NAME
26
26
  from langtrace_python_sdk.constants.instrumentation.common import (
27
27
  LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
@@ -64,7 +64,9 @@ def images_generate(original_method, version, tracer):
64
64
  attributes = LLMSpanAttributes(**span_attributes)
65
65
 
66
66
  with tracer.start_as_current_span(
67
- APIS["IMAGES_GENERATION"]["METHOD"], kind=SpanKind.CLIENT
67
+ APIS["IMAGES_GENERATION"]["METHOD"],
68
+ kind=SpanKind.CLIENT,
69
+ context=set_span_in_context(trace.get_current_span()),
68
70
  ) as span:
69
71
  for field, value in attributes.model_dump(by_alias=True).items():
70
72
  if value is not None:
@@ -141,7 +143,9 @@ def async_images_generate(original_method, version, tracer):
141
143
  attributes = LLMSpanAttributes(**span_attributes)
142
144
 
143
145
  with tracer.start_as_current_span(
144
- APIS["IMAGES_GENERATION"]["METHOD"], kind=SpanKind.CLIENT
146
+ APIS["IMAGES_GENERATION"]["METHOD"],
147
+ kind=SpanKind.CLIENT,
148
+ context=set_span_in_context(trace.get_current_span()),
145
149
  ) as span:
146
150
  items = attributes.model_dump(by_alias=True).items()
147
151
  for field, value in items:
@@ -226,7 +230,9 @@ def images_edit(original_method, version, tracer):
226
230
  attributes = LLMSpanAttributes(**span_attributes)
227
231
 
228
232
  with tracer.start_as_current_span(
229
- APIS["IMAGES_EDIT"]["METHOD"], kind=SpanKind.CLIENT
233
+ APIS["IMAGES_EDIT"]["METHOD"],
234
+ kind=SpanKind.CLIENT,
235
+ context=set_span_in_context(trace.get_current_span()),
230
236
  ) as span:
231
237
  for field, value in attributes.model_dump(by_alias=True).items():
232
238
  if value is not None:
@@ -349,7 +355,9 @@ def chat_completions_create(original_method, version, tracer):
349
355
  # with tracer.start_as_current_span(APIS["CHAT_COMPLETION"]["METHOD"],
350
356
  # kind=SpanKind.CLIENT) as span:
351
357
  span = tracer.start_span(
352
- APIS["CHAT_COMPLETION"]["METHOD"], kind=SpanKind.CLIENT
358
+ APIS["CHAT_COMPLETION"]["METHOD"],
359
+ kind=SpanKind.CLIENT,
360
+ context=set_span_in_context(trace.get_current_span()),
353
361
  )
354
362
  for field, value in attributes.model_dump(by_alias=True).items():
355
363
  if value is not None:
@@ -833,7 +841,9 @@ def embeddings_create(original_method, version, tracer):
833
841
  attributes["llm.user"] = kwargs.get("user")
834
842
 
835
843
  with tracer.start_as_current_span(
836
- APIS["EMBEDDINGS_CREATE"]["METHOD"], kind=SpanKind.CLIENT
844
+ APIS["EMBEDDINGS_CREATE"]["METHOD"],
845
+ kind=SpanKind.CLIENT,
846
+ context=set_span_in_context(trace.get_current_span()),
837
847
  ) as span:
838
848
 
839
849
  for field, value in attributes.model_dump(by_alias=True).items():
@@ -898,7 +908,9 @@ def async_embeddings_create(original_method, version, tracer):
898
908
  attributes["llm.user"] = kwargs.get("user")
899
909
 
900
910
  with tracer.start_as_current_span(
901
- APIS["EMBEDDINGS_CREATE"]["METHOD"], kind=SpanKind.CLIENT
911
+ APIS["EMBEDDINGS_CREATE"]["METHOD"],
912
+ kind=SpanKind.CLIENT,
913
+ context=set_span_in_context(trace.get_current_span()),
902
914
  ) as span:
903
915
 
904
916
  async for field, value in attributes.model_dump(by_alias=True).items():
@@ -18,7 +18,6 @@ import importlib.metadata
18
18
  import logging
19
19
  from typing import Collection
20
20
 
21
- from langtrace.trace_attributes import PineconeMethods
22
21
  from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
23
22
  from opentelemetry.trace import get_tracer
24
23
  from wrapt import wrap_function_wrapper
@@ -17,10 +17,10 @@ limitations under the License.
17
17
  import json
18
18
 
19
19
  from langtrace.trace_attributes import DatabaseSpanAttributes
20
- from opentelemetry import baggage
20
+ from opentelemetry import baggage, trace
21
21
  from opentelemetry.trace import SpanKind
22
22
  from opentelemetry.trace.status import Status, StatusCode
23
-
23
+ from opentelemetry.trace.propagation import set_span_in_context
24
24
  from langtrace_python_sdk.constants.instrumentation.common import (
25
25
  LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
26
26
  SERVICE_PROVIDERS,
@@ -56,7 +56,11 @@ def generic_patch(operation_name, version, tracer):
56
56
 
57
57
  attributes = DatabaseSpanAttributes(**span_attributes)
58
58
 
59
- with tracer.start_as_current_span(api["METHOD"], kind=SpanKind.CLIENT) as span:
59
+ with tracer.start_as_current_span(
60
+ api["METHOD"],
61
+ kind=SpanKind.CLIENT,
62
+ context=set_span_in_context(trace.get_current_span()),
63
+ ) as span:
60
64
 
61
65
  if span.is_recording():
62
66
  set_span_attribute(span, "server.address", instance._config.host)
@@ -18,9 +18,10 @@ import json
18
18
  from langtrace.trace_attributes import DatabaseSpanAttributes
19
19
  from langtrace_python_sdk.utils.silently_fail import silently_fail
20
20
  from langtrace_python_sdk.utils.llm import set_span_attributes
21
- from opentelemetry import baggage
21
+ from opentelemetry import baggage, trace
22
22
  from opentelemetry.trace import SpanKind
23
23
  from opentelemetry.trace.status import Status, StatusCode
24
+ from opentelemetry.trace.propagation import set_span_in_context
24
25
 
25
26
  from langtrace_python_sdk.constants.instrumentation.common import (
26
27
  LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
@@ -56,7 +57,11 @@ def collection_patch(method, version, tracer):
56
57
 
57
58
  attributes = DatabaseSpanAttributes(**span_attributes)
58
59
 
59
- with tracer.start_as_current_span(api["METHOD"], kind=SpanKind.CLIENT) as span:
60
+ with tracer.start_as_current_span(
61
+ api["METHOD"],
62
+ kind=SpanKind.CLIENT,
63
+ context=set_span_in_context(trace.get_current_span()),
64
+ ) as span:
60
65
  collection_name = kwargs.get("collection_name") or args[0]
61
66
  operation = api["OPERATION"]
62
67
  set_span_attributes(span, "db.collection.name", collection_name)
@@ -17,10 +17,10 @@ limitations under the License.
17
17
  import json
18
18
 
19
19
  from langtrace.trace_attributes import DatabaseSpanAttributes
20
- from opentelemetry import baggage
20
+ from opentelemetry import baggage, trace
21
21
  from opentelemetry.trace import SpanKind
22
22
  from opentelemetry.trace.status import Status, StatusCode
23
-
23
+ from opentelemetry.trace.propagation import set_span_in_context
24
24
  from langtrace_python_sdk.constants.instrumentation.common import (
25
25
  LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
26
26
  SERVICE_PROVIDERS,
@@ -131,7 +131,11 @@ def create_traced_method(method_name, version, tracer, get_collection_name=None)
131
131
 
132
132
  attributes = DatabaseSpanAttributes(**span_attributes)
133
133
 
134
- with tracer.start_as_current_span(method_name, kind=SpanKind.CLIENT) as span:
134
+ with tracer.start_as_current_span(
135
+ method_name,
136
+ kind=SpanKind.CLIENT,
137
+ context=set_span_in_context(trace.get_current_span()),
138
+ ) as span:
135
139
  for field, value in attributes.model_dump(by_alias=True).items():
136
140
  if value is not None:
137
141
  span.set_attribute(field, value)
@@ -19,7 +19,12 @@ from typing import Optional
19
19
  from langtrace_python_sdk.constants.exporter.langtrace_exporter import (
20
20
  LANGTRACE_REMOTE_URL,
21
21
  )
22
- from langtrace_python_sdk.types import DisableInstrumentations, InstrumentationType
22
+ from langtrace_python_sdk.types import (
23
+ DisableInstrumentations,
24
+ InstrumentationType,
25
+ InstrumentationMethods,
26
+ )
27
+ from langtrace_python_sdk.utils.langtrace_sampler import LangtraceSampler
23
28
  from opentelemetry import trace
24
29
  from opentelemetry.sdk.trace import TracerProvider
25
30
  from opentelemetry.sdk.trace.export import (
@@ -45,6 +50,7 @@ from langtrace_python_sdk.instrumentation import (
45
50
  PineconeInstrumentation,
46
51
  QdrantInstrumentation,
47
52
  WeaviateInstrumentation,
53
+ OllamaInstrumentor,
48
54
  )
49
55
  from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
50
56
  from colorama import Fore
@@ -59,6 +65,7 @@ def init(
59
65
  custom_remote_exporter=None,
60
66
  api_host: Optional[str] = LANGTRACE_REMOTE_URL,
61
67
  disable_instrumentations: Optional[DisableInstrumentations] = None,
68
+ disable_tracing_for_functions: Optional[InstrumentationMethods] = None,
62
69
  ):
63
70
 
64
71
  host = (
@@ -66,7 +73,11 @@ def init(
66
73
  )
67
74
  check_if_sdk_is_outdated()
68
75
  print(Fore.GREEN + "Initializing Langtrace SDK.." + Fore.RESET)
69
- provider = TracerProvider(resource=Resource.create({"service.name": sys.argv[0]}))
76
+ sampler = LangtraceSampler(disabled_methods=disable_tracing_for_functions)
77
+ provider = TracerProvider(
78
+ resource=Resource.create({"service.name": sys.argv[0]}),
79
+ sampler=sampler,
80
+ )
70
81
 
71
82
  remote_write_exporter = (
72
83
  LangTraceExporter(api_key=api_key, api_host=host)
@@ -96,6 +107,7 @@ def init(
96
107
  "cohere": CohereInstrumentation(),
97
108
  "weaviate": WeaviateInstrumentation(),
98
109
  "sqlalchemy": SQLAlchemyInstrumentor(),
110
+ "ollama": OllamaInstrumentor(),
99
111
  }
100
112
 
101
113
  init_instrumentations(disable_instrumentations, all_instrumentations)
@@ -1,4 +1,4 @@
1
- from typing import List, TypedDict
1
+ from typing import List, Literal, TypedDict
2
2
  from enum import Enum
3
3
 
4
4
 
@@ -16,6 +16,7 @@ class InstrumentationType(Enum):
16
16
  LANGCHAIN_COMMUNITY = "langchain_community"
17
17
  LANGGRAPH = "langgraph"
18
18
  WEAVIATE = "weaviate"
19
+ OLLAMA = "ollama"
19
20
 
20
21
  @staticmethod
21
22
  def from_string(value: str):
@@ -28,3 +29,78 @@ class InstrumentationType(Enum):
28
29
  class DisableInstrumentations(TypedDict, total=False):
29
30
  all_except: List[InstrumentationType]
30
31
  only: List[InstrumentationType]
32
+
33
+
34
+ class VendorMethods(TypedDict):
35
+ PineconeMethods = Literal[
36
+ "pinecone.index.upsert", "pinecone.index.query", "pinecone.index.delete"
37
+ ]
38
+ AnthropicMethods = Literal["anthropic.messages.create"]
39
+ GroqMethods = Literal["groq.chat.completions.create"]
40
+ OpenaiMethods = Literal[
41
+ "openai.chat.completions.create",
42
+ "openai.embeddings.create",
43
+ "openai.images.generate",
44
+ "openai.images.edit",
45
+ ]
46
+
47
+ ChromadbMethods = Literal[
48
+ "chromadb.collection.add",
49
+ "chromadb.collection.query",
50
+ "chromadb.collection.delete",
51
+ "chromadb.collection.peek",
52
+ "chromadb.collection.update",
53
+ "chromadb.collection.upsert",
54
+ "chromadb.collection.modify",
55
+ "chromadb.collection.count",
56
+ ]
57
+ QdrantMethods = Literal[
58
+ "qdrantdb.add",
59
+ "qdrantdb.get_collection",
60
+ "qdrantdb.get_collections",
61
+ "qdrantdb.query",
62
+ "qdrantdb.query_batch",
63
+ "qdrantdb.delete",
64
+ "qdrantdb.discover",
65
+ "qdrantdb.discover_batch",
66
+ "qdrantdb.recommend",
67
+ "qdrantdb.recommend_batch",
68
+ "qdrantdb.retrieve",
69
+ "qdrantdb.search",
70
+ "qdrantdb.search_batch",
71
+ "qdrantdb.upsert",
72
+ "qdrantdb.count",
73
+ "qdrantdb.update_collection",
74
+ "qdrantdb.update_vectors",
75
+ ]
76
+
77
+ CohereMethods = Literal[
78
+ "cohere.client.chat",
79
+ "cohere.client.embed",
80
+ "cohere.client.rerank",
81
+ "cohere.client.chat_stream",
82
+ ]
83
+
84
+ LlamaIndexMethods = Literal[
85
+ "llamaindex.OpenAI.chat",
86
+ "llamaindex.RetrieverQueryEngine.query",
87
+ "llamaindex.VectorIndexRetriever.retrieve",
88
+ "llamaindex.SimpleVectorStore.query",
89
+ "llamaindex.RetrieverQueryEngine.retrieve",
90
+ ]
91
+
92
+
93
+ class InstrumentationMethods(TypedDict):
94
+ open_ai: List[VendorMethods.OpenaiMethods]
95
+ groq: List[VendorMethods.GroqMethods]
96
+ pinecone: List[VendorMethods.PineconeMethods]
97
+ llamaindex: List[VendorMethods.LlamaIndexMethods]
98
+ chromadb: List[VendorMethods.ChromadbMethods]
99
+ qdrant: List[VendorMethods.QdrantMethods]
100
+ langchain: List[str]
101
+ langchain_core: List[str]
102
+ langchain_community: List[str]
103
+ langgraph: List[str]
104
+ anthropic: List[VendorMethods.AnthropicMethods]
105
+ cohere: List[VendorMethods.CohereMethods]
106
+ weaviate: List[str]
@@ -0,0 +1,56 @@
1
+ from typing import Optional, Sequence
2
+ from opentelemetry.sdk.trace.sampling import (
3
+ Sampler,
4
+ Decision,
5
+ SamplingResult,
6
+ )
7
+ from opentelemetry.trace import SpanKind, Link, TraceState, TraceFlags
8
+ from opentelemetry.util.types import Attributes
9
+ from opentelemetry.context import Context
10
+ from opentelemetry import trace
11
+
12
+
13
+ class LangtraceSampler(Sampler):
14
+ _disabled_methods_names: set
15
+
16
+ def __init__(
17
+ self,
18
+ disabled_methods: dict,
19
+ ):
20
+ self._disabled_methods_names = set()
21
+ if disabled_methods:
22
+ for _, methods in disabled_methods.items():
23
+ for method in methods:
24
+ self._disabled_methods_names.add(method)
25
+
26
+ def should_sample(
27
+ self,
28
+ parent_context: Optional[Context],
29
+ trace_id: int,
30
+ name: str,
31
+ kind: Optional[SpanKind] = None,
32
+ attributes: Attributes = None,
33
+ links: Optional[Sequence[Link]] = None,
34
+ trace_state: Optional["TraceState"] = None,
35
+ ) -> SamplingResult:
36
+
37
+ parent_span = trace.get_current_span(parent_context)
38
+ parent_span_context = parent_span.get_span_context()
39
+
40
+ if not self._disabled_methods_names:
41
+ return SamplingResult(decision=Decision.RECORD_AND_SAMPLE)
42
+
43
+ if parent_context:
44
+ if (
45
+ parent_span_context.span_id != 0
46
+ and parent_span_context.trace_flags != TraceFlags.SAMPLED
47
+ ):
48
+ return SamplingResult(decision=Decision.DROP)
49
+
50
+ if name in self._disabled_methods_names:
51
+ return SamplingResult(decision=Decision.DROP)
52
+
53
+ return SamplingResult(decision=Decision.RECORD_AND_SAMPLE)
54
+
55
+ def get_description(self):
56
+ return "Langtrace Sampler"
@@ -23,6 +23,7 @@ from typing import Optional
23
23
  import requests
24
24
  from opentelemetry import baggage, context, trace
25
25
  from opentelemetry.trace import SpanKind
26
+ from opentelemetry.trace.propagation import set_span_in_context
26
27
 
27
28
  from langtrace_python_sdk.constants.instrumentation.common import (
28
29
  LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
@@ -48,7 +49,11 @@ def with_langtrace_root_span(
48
49
  span_id = None
49
50
  trace_id = None
50
51
 
51
- with tracer.start_as_current_span(operation_name, kind=kind) as span:
52
+ with tracer.start_as_current_span(
53
+ operation_name,
54
+ kind=kind,
55
+ context=set_span_in_context(trace.get_current_span()),
56
+ ) as span:
52
57
  span_id = str(span.get_span_context().span_id)
53
58
  trace_id = str(span.get_span_context().trace_id)
54
59
 
@@ -66,7 +71,11 @@ def with_langtrace_root_span(
66
71
  trace_id = None
67
72
  tracer = trace.get_tracer(__name__)
68
73
  operation_name = name if name else func.__name__
69
- with tracer.start_as_current_span(operation_name, kind=kind) as span:
74
+ with tracer.start_as_current_span(
75
+ operation_name,
76
+ kind=kind,
77
+ context=set_span_in_context(trace.get_current_span()),
78
+ ) as span:
70
79
  span_id = span.get_span_context().span_id
71
80
  trace_id = span.get_span_context().trace_id
72
81
  if (
@@ -1 +1 @@
1
- __version__ = "2.1.16"
1
+ __version__ = "2.1.19"