langtrace-python-sdk 2.0.6__py3-none-any.whl → 2.0.8__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.
@@ -2,19 +2,16 @@
2
2
  This example demonstrates how to use Pinecone with Langtrace.
3
3
  """
4
4
 
5
+ from langtrace_python_sdk import langtrace
5
6
  from dotenv import find_dotenv, load_dotenv
6
7
  from openai import OpenAI
7
8
  from pinecone import Pinecone
8
9
 
9
- from langtrace_python_sdk import langtrace
10
10
  from langtrace_python_sdk.utils.with_root_span import with_langtrace_root_span
11
11
 
12
12
  _ = load_dotenv(find_dotenv())
13
13
 
14
- langtrace.init(
15
- write_spans_to_console=True,
16
- disable_instrumentations={"all_except": ["pinecone", "openai"]},
17
- )
14
+ langtrace.init()
18
15
 
19
16
  client = OpenAI()
20
17
  pinecone = Pinecone()
@@ -30,10 +27,14 @@ def basic():
30
27
 
31
28
  embedding = result.data[0].embedding
32
29
 
33
- unique_id = "randomid"
30
+ unique_id = "unique_random_id"
34
31
  data_to_upsert = {"id": unique_id, "values": embedding}
35
32
 
36
33
  index = pinecone.Index("test-index")
37
- index.upsert(vectors=[data_to_upsert])
34
+ res = index.upsert(vectors=[data_to_upsert], namespace="test-namespace")
35
+ print("res", res)
38
36
 
39
- resp = index.query(vector=embedding, top_k=1)
37
+ resp = index.query(
38
+ vector=embedding, top_k=1, include_values=False, namespace="test-namespace"
39
+ )
40
+ print(resp)
@@ -16,5 +16,6 @@ limitations under the License.
16
16
 
17
17
  from langtrace_python_sdk import langtrace
18
18
  from langtrace_python_sdk.utils.with_root_span import with_langtrace_root_span
19
+ from langtrace_python_sdk.utils.prompt_registry import get_prompt_from_registry
19
20
 
20
- __all__ = ["langtrace", "with_langtrace_root_span"]
21
+ __all__ = ["langtrace", "with_langtrace_root_span", "get_prompt_from_registry"]
@@ -15,6 +15,8 @@ limitations under the License.
15
15
  """
16
16
 
17
17
  from langtrace.trace_attributes import DatabaseSpanAttributes
18
+ from langtrace_python_sdk.utils import set_span_attribute
19
+ from langtrace_python_sdk.utils.silently_fail import silently_fail
18
20
  from opentelemetry import baggage
19
21
  from opentelemetry.trace import SpanKind
20
22
  from opentelemetry.trace.status import Status, StatusCode
@@ -24,6 +26,7 @@ from langtrace_python_sdk.constants.instrumentation.common import (
24
26
  SERVICE_PROVIDERS,
25
27
  )
26
28
  from langtrace_python_sdk.constants.instrumentation.pinecone import APIS
29
+ import json
27
30
 
28
31
 
29
32
  def generic_patch(original_method, method, version, tracer):
@@ -49,12 +52,21 @@ def generic_patch(original_method, method, version, tracer):
49
52
  attributes = DatabaseSpanAttributes(**span_attributes)
50
53
 
51
54
  with tracer.start_as_current_span(api["METHOD"], kind=SpanKind.CLIENT) as span:
55
+
56
+ if span.is_recording():
57
+ set_span_attribute(span, "server.address", instance._config.host)
58
+ if method == "QUERY":
59
+ set_query_input_attributes(span, kwargs)
60
+
52
61
  for field, value in attributes.model_dump(by_alias=True).items():
53
62
  if value is not None:
54
63
  span.set_attribute(field, value)
55
64
  try:
56
65
  # Attempt to call the original method
57
66
  result = original_method(instance, *args, **kwargs)
67
+ if result:
68
+ if span.is_recording():
69
+ set_query_response_attributes(span, result)
58
70
  span.set_status(StatusCode.OK)
59
71
  return result
60
72
  except Exception as err:
@@ -67,4 +79,48 @@ def generic_patch(original_method, method, version, tracer):
67
79
  # Reraise the exception to ensure it's not swallowed
68
80
  raise
69
81
 
82
+ @silently_fail
83
+ def set_query_input_attributes(span, kwargs):
84
+ set_span_attribute(span, "db.query.top_k", kwargs.get("top_k"))
85
+ set_span_attribute(span, "db.query.namespace", kwargs.get("namespace"))
86
+ set_span_attribute(span, "db.query.id", kwargs.get("id"))
87
+ filter = (
88
+ json.dumps(kwargs.get("filter"))
89
+ if isinstance(kwargs.get("filter"), dict)
90
+ else kwargs.get("filter")
91
+ )
92
+ set_span_attribute(span, "db.query.filter", filter)
93
+ set_span_attribute(
94
+ span, "db.query.include_values", kwargs.get("include_values")
95
+ )
96
+ set_span_attribute(
97
+ span, "db.query.include_metadata", kwargs.get("include_metadata")
98
+ )
99
+
100
+ @silently_fail
101
+ def set_query_response_attributes(span, response):
102
+ matches = response.get("matches")
103
+
104
+ usage = response.get("usage")
105
+ for match in matches:
106
+ span.add_event(
107
+ name="db.query.match",
108
+ attributes={
109
+ "db.query.match.id": match.get("id"),
110
+ "db.query.match.score": match.get("score"),
111
+ "db.query.match.metadata": match.get("metadata"),
112
+ # "db.query.match.values": match.get("values"),
113
+ },
114
+ )
115
+
116
+ if "read_units" in usage:
117
+ set_span_attribute(
118
+ span, "db.query.usage.read_units", usage.get("read_units")
119
+ )
120
+
121
+ if "write_units" in usage:
122
+ set_span_attribute(
123
+ span, "db.query.usage.write_units", usage.get("write_units")
124
+ )
125
+
70
126
  return traced_method
@@ -15,6 +15,8 @@ limitations under the License.
15
15
  """
16
16
 
17
17
  from langtrace.trace_attributes import DatabaseSpanAttributes
18
+ from langtrace_python_sdk.utils.silently_fail import silently_fail
19
+ from langtrace_python_sdk.utils.llm import set_span_attributes
18
20
  from opentelemetry import baggage
19
21
  from opentelemetry.trace import SpanKind
20
22
  from opentelemetry.trace.status import Status, StatusCode
@@ -47,12 +49,19 @@ def collection_patch(method, version, tracer):
47
49
  **(extra_attributes if extra_attributes is not None else {}),
48
50
  }
49
51
 
50
- if hasattr(instance, "name") and instance.name is not None:
51
- span_attributes["db.collection.name"] = instance.name
52
-
53
52
  attributes = DatabaseSpanAttributes(**span_attributes)
54
53
 
55
54
  with tracer.start_as_current_span(api["METHOD"], kind=SpanKind.CLIENT) as span:
55
+ collection_name = kwargs.get("collection_name") or args[0]
56
+ operation = api["OPERATION"]
57
+ set_span_attributes(span, "db.collection.name", collection_name)
58
+ if operation == "upsert":
59
+ _set_upsert_attributes(span, args, kwargs)
60
+ elif operation == "add":
61
+ _set_upload_attributes(span, args, kwargs, "documents")
62
+
63
+ # Todo: Add support for other operations here.
64
+
56
65
  for field, value in attributes.model_dump(by_alias=True).items():
57
66
  if value is not None:
58
67
  span.set_attribute(field, value)
@@ -72,3 +81,26 @@ def collection_patch(method, version, tracer):
72
81
  raise
73
82
 
74
83
  return traced_method
84
+
85
+
86
+ @silently_fail
87
+ def _set_upsert_attributes(span, args, kwargs):
88
+ points = kwargs.get("points") or args[1]
89
+ if isinstance(points, list):
90
+ length = len(points)
91
+ else:
92
+ # In case of using Batch.
93
+ length = len(points.ids)
94
+ set_span_attributes(span, "db.upsert.points_count", length)
95
+
96
+
97
+ @silently_fail
98
+ def _set_upload_attributes(span, args, kwargs, field):
99
+ docs = kwargs.get(field) or args[0]
100
+ if isinstance(docs, list):
101
+ length = len(docs)
102
+ else:
103
+ # In case of using Batch.
104
+ length = len(docs.ids)
105
+
106
+ set_span_attributes(span, f"db.upload.{field}_count", length)
@@ -24,6 +24,8 @@ from opentelemetry.sdk.trace.export import (
24
24
  ConsoleSpanExporter,
25
25
  SimpleSpanProcessor,
26
26
  )
27
+ import sys
28
+ from opentelemetry.sdk.resources import Resource
27
29
 
28
30
  from langtrace_python_sdk.extensions.langtrace_exporter import LangTraceExporter
29
31
  from langtrace_python_sdk.instrumentation.anthropic.instrumentation import (
@@ -72,7 +74,7 @@ def init(
72
74
  api_host: Optional[str] = None,
73
75
  disable_instrumentations: Optional[DisableInstrumentations] = None,
74
76
  ):
75
- provider = TracerProvider()
77
+ provider = TracerProvider(resource=Resource.create({"service.name": sys.argv[0]}))
76
78
 
77
79
  remote_write_exporter = (
78
80
  LangTraceExporter(api_key=api_key, api_host=api_host)
@@ -128,24 +130,29 @@ def init_instrumentations(
128
130
  if disable_instrumentations is None:
129
131
  for _, v in all_instrumentations.items():
130
132
  v.instrument()
131
- return
132
- validate_instrumentations(disable_instrumentations)
133
-
134
- for key in disable_instrumentations:
135
- for vendor in disable_instrumentations[key]:
136
- if key == "only":
137
- filtered_dict = {
138
- k: v for k, v in all_instrumentations.items() if k != vendor.value
139
- }
140
- for _, v in filtered_dict.items():
141
- v.instrument()
142
- else:
143
- filtered_dict = {
144
- k: v for k, v in all_instrumentations.items() if k == vendor.value
145
- }
146
-
147
- for _, v in filtered_dict.items():
148
- v.instrument()
133
+ else:
134
+
135
+ validate_instrumentations(disable_instrumentations)
136
+
137
+ for key in disable_instrumentations:
138
+ for vendor in disable_instrumentations[key]:
139
+ if key == "only":
140
+ filtered_dict = {
141
+ k: v
142
+ for k, v in all_instrumentations.items()
143
+ if k != vendor.value
144
+ }
145
+ for _, v in filtered_dict.items():
146
+ v.instrument()
147
+ else:
148
+ filtered_dict = {
149
+ k: v
150
+ for k, v in all_instrumentations.items()
151
+ if k == vendor.value
152
+ }
153
+
154
+ for _, v in filtered_dict.items():
155
+ v.instrument()
149
156
 
150
157
 
151
158
  def validate_instrumentations(disable_instrumentations):
@@ -0,0 +1,7 @@
1
+ def set_span_attribute(span, name, value):
2
+ if value is not None:
3
+ if value != "":
4
+ span.set_attribute(name, value)
5
+ return
6
+
7
+
@@ -58,3 +58,11 @@ def calculate_price_from_usage(model, usage):
58
58
  + cost_table["output"] * usage["completion_tokens"]
59
59
  ) / 1000
60
60
  return 0
61
+
62
+
63
+ def set_span_attributes(span, name, value):
64
+ if value is not None:
65
+ if value != "":
66
+ span.set_attribute(name, value)
67
+ return
68
+
@@ -0,0 +1,82 @@
1
+ import os
2
+ from langtrace_python_sdk.constants.exporter.langtrace_exporter import (
3
+ LANGTRACE_REMOTE_URL,
4
+ )
5
+ import requests
6
+ from urllib.parse import urlencode
7
+ from typing import Optional, TypedDict, Dict, List
8
+
9
+
10
+ class LangtracePrompt(TypedDict):
11
+ id: str
12
+ value: str
13
+ variables: List[str]
14
+ model: str
15
+ modelSettings: Dict[str, object]
16
+ version: int
17
+ live: bool
18
+ tags: List[str]
19
+ note: str
20
+ promptsetId: str
21
+ createdAt: str
22
+ updatedAt: str
23
+
24
+
25
+ class FetchOptions(TypedDict, total=False):
26
+ prompt_version: int
27
+ variables: Dict[str, str]
28
+
29
+
30
+ def get_prompt_from_registry(
31
+ prompt_registry_id: str,
32
+ options: Optional[FetchOptions] = None,
33
+ ) -> LangtracePrompt:
34
+ """Fetches a prompt from the registry.
35
+
36
+ Args:
37
+ prompt_registry_id (str): The ID of the prompt registry.
38
+ options (dict, optional): Configuration options for fetching the prompt:
39
+ - prompt_version (int): Fetches the prompt with the specified version.
40
+ - variables (dict): Replaces variables in the prompt with provided values.
41
+
42
+ Returns:
43
+ dict: The fetched prompt with variables replaced as specified.
44
+
45
+ Raises:
46
+ Exception: If the fetch operation fails or returns an error.
47
+ """
48
+ try:
49
+ query_params = {"promptset_id": prompt_registry_id}
50
+ if options:
51
+ if "prompt_version" in options:
52
+ query_params["version"] = options["prompt_version"]
53
+ if "variables" in options:
54
+ for key, value in options["variables"].items():
55
+ query_params[f"variables.{key}"] = value
56
+ # Encode the query parameters
57
+ query_string = urlencode(query_params, doseq=True)
58
+ headers = {"x-api-key": os.environ["LANGTRACE_API_KEY"]}
59
+ print(query_params)
60
+ # Make the GET request to the API
61
+ response = requests.get(
62
+ f"{LANGTRACE_REMOTE_URL}/api/promptset?{query_string}",
63
+ headers=headers,
64
+ timeout=None,
65
+ )
66
+ response.raise_for_status()
67
+
68
+ # Extract the prompt data from the response
69
+ prompt_data = response.json()["prompts"][0]
70
+ return prompt_data
71
+
72
+ except requests.RequestException as err:
73
+ # Handle specific HTTP errors or general request exceptions
74
+ error_msg = str(err)
75
+ if err.response:
76
+ try:
77
+ # Try to extract server-provided error message
78
+ error_msg = err.response.json().get("error", error_msg)
79
+ except ValueError:
80
+ # Fallback if response is not JSON
81
+ error_msg = err.response.text
82
+ raise Exception(f"API error: {error_msg}") from err
@@ -0,0 +1,19 @@
1
+ import logging
2
+
3
+
4
+ def silently_fail(func):
5
+ """
6
+ A decorator that catches exceptions thrown by the decorated function and logs them as warnings.
7
+ """
8
+
9
+ logger = logging.getLogger(func.__module__)
10
+
11
+ def wrapper(*args, **kwargs):
12
+ try:
13
+ return func(*args, **kwargs)
14
+ except Exception as exception:
15
+ logger.warning(
16
+ "Failed to execute %s, error: %s", func.__name__, str(exception)
17
+ )
18
+
19
+ return wrapper
File without changes
@@ -1 +1 @@
1
- __version__ = "2.0.6"
1
+ __version__ = "2.0.8"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: langtrace-python-sdk
3
- Version: 2.0.6
3
+ Version: 2.0.8
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>
@@ -25,7 +25,9 @@ Requires-Dist: langchain-openai; extra == 'dev'
25
25
  Requires-Dist: llama-index; extra == 'dev'
26
26
  Requires-Dist: openai; extra == 'dev'
27
27
  Requires-Dist: python-dotenv; extra == 'dev'
28
+ Requires-Dist: qdrant-client; extra == 'dev'
28
29
  Provides-Extra: test
30
+ Requires-Dist: protobuf==3.20.0; extra == 'test'
29
31
  Requires-Dist: pytest; extra == 'test'
30
32
  Requires-Dist: pytest-asyncio; extra == 'test'
31
33
  Requires-Dist: pytest-vcr; extra == 'test'
@@ -217,6 +219,15 @@ def chat_completion():
217
219
  api_call2()
218
220
  ```
219
221
 
222
+ - `get_prompt_from_registry` - this function is designed to fetch the desired prompt from the `Prompt Registry`. You can pass two options for filtering `prompt_version` & `variables`.
223
+
224
+
225
+ ```python
226
+ from langtrace_python_sdk import get_prompt_from_registry
227
+
228
+ prompt = get_prompt_from_registry(<Registry ID>, options={"prompt_version": 1, "variables": {"foo": "bar"} })
229
+ ```
230
+
220
231
  ## Supported integrations
221
232
 
222
233
  Langtrace automatically captures traces from the following vendors:
@@ -32,12 +32,12 @@ examples/openai_example/tool_calling_nonstreaming.py,sha256=Yc848IooZRXNynHL6z0k
32
32
  examples/openai_example/tool_calling_streaming.py,sha256=mV1RbyAoVhumGRPpqPWQ6PMhnJyeifrlELd2-K1qJ_w,7015
33
33
  examples/perplexity_example/basic.py,sha256=bp7n27gaugJkaFVyt8pjaEfi66lYcqP6eFFjPewUShY,668
34
34
  examples/pinecone_example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- examples/pinecone_example/basic.py,sha256=9IWMoMLozMTpxjY8BFBg2EpUNY5dDjU1xocEG9XsXP0,966
35
+ examples/pinecone_example/basic.py,sha256=uwWu_UFuDtthgs-x8gOPnsUGOeBDCVPEoiz9uwSEjfg,1007
36
36
  examples/qdrant_example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
37
  examples/qdrant_example/basic.py,sha256=DCMjHSuBZKkhEjCkwy5d5La9WMyW0lCWqtcZWiFCEm4,1425
38
- langtrace_python_sdk/__init__.py,sha256=q2TAsxGIv4wFIQA7G-HGxlnzO89bhPU5TgI42sPHLj0,740
39
- langtrace_python_sdk/langtrace.py,sha256=z6CL_e2aQUXJlTUzel0IL2zBVAa5lxRR01b88GfaBuY,6576
40
- langtrace_python_sdk/version.py,sha256=4Yxft_MNOtwQG58mZpa8eoM0XBHa8VNkTPH9_i0gDaM,22
38
+ langtrace_python_sdk/__init__.py,sha256=_r2S-lLZFTFFvWjf0lWBX4nzkJN5R7FHX0Dnmr76CEk,848
39
+ langtrace_python_sdk/langtrace.py,sha256=o0xJInE_dwNBkAfIQRqMKPz6glCAcGdRJxEG8kasq8I,6839
40
+ langtrace_python_sdk/version.py,sha256=oVbl6ILP5dyBZtm2iUzM_QTUhSy3C325aGgDThF7P_4,22
41
41
  langtrace_python_sdk/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
42
  langtrace_python_sdk/constants/exporter/langtrace_exporter.py,sha256=5MNjnAOg-4am78J3gVMH6FSwq5N8TOj72ugkhsw4vi0,46
43
43
  langtrace_python_sdk/constants/instrumentation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -84,13 +84,16 @@ langtrace_python_sdk/instrumentation/openai/instrumentation.py,sha256=G2HSZfr6Du
84
84
  langtrace_python_sdk/instrumentation/openai/patch.py,sha256=kQxc_E1IlOgHZnlc0HbpGjM5rZCDR3YpNFChp2X9GbM,37263
85
85
  langtrace_python_sdk/instrumentation/pinecone/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
86
86
  langtrace_python_sdk/instrumentation/pinecone/instrumentation.py,sha256=52GbkfRK-sxXBXhHmHRRO2BGNd6zZVHDp8njLS1CQX0,2314
87
- langtrace_python_sdk/instrumentation/pinecone/patch.py,sha256=ebz57WgmlTnY-FDwQgJ-_eqsknEmDWfh-AiNow4G94s,2705
87
+ langtrace_python_sdk/instrumentation/pinecone/patch.py,sha256=hLgYWDKTytuvTORDd6dupMpKAp19vCgOAXDOdjvWme0,4832
88
88
  langtrace_python_sdk/instrumentation/qdrant/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
89
  langtrace_python_sdk/instrumentation/qdrant/instrumentation.py,sha256=vl2eKSP55aqDo1JiRlvOUBrr6kddvG9Z5dCYew2OG08,1816
90
- langtrace_python_sdk/instrumentation/qdrant/patch.py,sha256=skVziJbvoR7XYTI6KmsiQlclTmQEeLeJ5UrJLvi7fjw,2807
90
+ langtrace_python_sdk/instrumentation/qdrant/patch.py,sha256=x95PXaJe_GrOG51zZCDuchVA-bzuA7s5LHqOoWrqSA0,3883
91
91
  langtrace_python_sdk/types/__init__.py,sha256=QYK-kRkyfXDdzjjKmXVxi0vZ6Wpbxdf0CoZbTXO0JT8,787
92
- langtrace_python_sdk/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
- langtrace_python_sdk/utils/llm.py,sha256=Cl710-0rfr15g1xNkOpLpgCn84pNlfMflcQ4S5jSRu0,2010
92
+ langtrace_python_sdk/utils/__init__.py,sha256=is6gOLiHcvrN9kPgYnUwyzkgz9vInVQ4jGe7k0zRHrA,150
93
+ langtrace_python_sdk/utils/llm.py,sha256=gnmJ6YWzzdOCaVFg1C-iyNcMGDyiJ623qZeOwDYUfIQ,2162
94
+ langtrace_python_sdk/utils/prompt_registry.py,sha256=7FFB4Pj0414qgf02h5zL5vXBZgNBf74g4Iq7GdFaIO0,2689
95
+ langtrace_python_sdk/utils/silently_fail.py,sha256=F_9EteXCO9Cyq-8MA1OT2Zy_dx8n06nt31I7t7ui24E,478
96
+ langtrace_python_sdk/utils/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
94
97
  langtrace_python_sdk/utils/with_root_span.py,sha256=MXF-FmG47P0GtJQNffKM_3XGrJHVAC4hMB6GmwtnA4M,2454
95
98
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
96
99
  tests/conftest.py,sha256=0Jo6iCZTXbdvyJVhG9UpYGkLabL75378oauCzmt-Sa8,603
@@ -100,7 +103,8 @@ tests/anthropic/test_anthropic.py,sha256=p4WhcECJ_zctirtkAX6WejrlNfZzo90fSSxqQki
100
103
  tests/anthropic/cassettes/test_anthropic.yaml,sha256=z7YAqA_BBgI-hw7uyVMdLoIZdBYhKwl9cuTzUu9nAZs,2332
101
104
  tests/anthropic/cassettes/test_anthropic_streaming.yaml,sha256=D0w4-6dfsgrhbNLJEj8gZBV0yXfrAfA9u90Yu8Ac-TY,11675
102
105
  tests/anthropic/cassettes/test_async_anthropic_streaming.yaml,sha256=hQZPY2vwBaW3BWllLd0lcGQ73DjA8C3Ips1Hx9pA-ao,8373
103
- tests/chroma/test_chroma.py,sha256=bVWC_OwmarTgVzKxZy5lrXpJzu9v9Ko6uQdktxOVh74,2430
106
+ tests/chroma/conftest.py,sha256=kqb4VydCnlVpkvBX5Bu-pfnVS-ZZfju9cp6vq76tkJI,309
107
+ tests/chroma/test_chroma.py,sha256=-KJHunvvVi1OoKKOcKCeHO1s399Gm9vJfd-EzgllQmk,1220
104
108
  tests/cohere/conftest.py,sha256=jBbyg-tut-ZJN5_5D64sGmaPIhT_nQQQAiW43kl5Rz4,621
105
109
  tests/cohere/test_cohere_chat.py,sha256=MioEIXB5e-4t99Vt-KpLlxd4diPbp3S6eNfnPWjEDXM,4564
106
110
  tests/cohere/test_cohere_embed.py,sha256=bFKDzUnypMyLefAOHR-6dEgvt0yys2Yw7-F0zc0uQyc,1250
@@ -121,8 +125,13 @@ tests/openai/cassettes/test_async_image_generation.yaml,sha256=_LYZcrqxrnSqcWVQn
121
125
  tests/openai/cassettes/test_chat_completion.yaml,sha256=YkNFgK9VHAzNqGWuxFcTiE194GdEie8eDf1FSsffjd8,2944
122
126
  tests/openai/cassettes/test_chat_completion_streaming.yaml,sha256=nkx_TemQMYSZxUF_b-LCEFwCRDm0AkQHLf4sdJVuZBw,2592394
123
127
  tests/openai/cassettes/test_image_generation.yaml,sha256=gn5aSVp6V6_hb_rt2NnkAWd_idzDxo-7VzhZII0Wslw,3562
124
- tests/pinecone/test_pinecone.py,sha256=iFuAqzbfrxHzH_J2kDCbezqYv1m-yeQiHiO3rMKTuYc,2735
125
- langtrace_python_sdk-2.0.6.dist-info/METADATA,sha256=To2Jd-L4FM7sHtVWbVbSU19s0BQKynZm9LKboKRLrmQ,9582
126
- langtrace_python_sdk-2.0.6.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
127
- langtrace_python_sdk-2.0.6.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
128
- langtrace_python_sdk-2.0.6.dist-info/RECORD,,
128
+ tests/pinecone/conftest.py,sha256=EY1m5M6MKSOktbVEe__wYNjCW9A7H0xdC-aXhU8JEOg,916
129
+ tests/pinecone/test_pinecone.py,sha256=JMe3S15cfBWjTArC-FTCarRRYBFylkv4BwQgL2BmUfw,3614
130
+ tests/pinecone/cassettes/test_query.yaml,sha256=b5v9G3ssUy00oG63PlFUR3JErF2Js-5AIqHnMsvJM8w,103821
131
+ tests/pinecone/cassettes/test_upsert.yaml,sha256=neWmQ1v3d03V8WoLl8FoFeeCYImb8pxlJBWnFd_lITU,38607
132
+ tests/qdrant/conftest.py,sha256=9n0uHxxIjWk9fbYc4bx-uP8lSAgLBVx-cV9UjnsyCHM,381
133
+ tests/qdrant/test_qdrant.py,sha256=pzjAjVY2kmsmGfrI2Gs2xrolfuaNHz7l1fqGQCjp5_o,3353
134
+ langtrace_python_sdk-2.0.8.dist-info/METADATA,sha256=bUB2J9h4OXlPWUXjo-VhX2bqamXaImnC9_vbdxiH5Uw,10046
135
+ langtrace_python_sdk-2.0.8.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
136
+ langtrace_python_sdk-2.0.8.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
137
+ langtrace_python_sdk-2.0.8.dist-info/RECORD,,
@@ -0,0 +1,15 @@
1
+ import chromadb
2
+ import pytest
3
+ from langtrace_python_sdk.instrumentation.chroma.instrumentation import (
4
+ ChromaInstrumentation,
5
+ )
6
+
7
+
8
+ @pytest.fixture
9
+ def chroma_client():
10
+ return chromadb.Client()
11
+
12
+
13
+ @pytest.fixture(scope="session", autouse=True)
14
+ def instrument():
15
+ ChromaInstrumentation().instrument()
@@ -1,70 +1,31 @@
1
- from unittest.mock import MagicMock, patch, call
1
+ import importlib
2
+ from chromadb.utils import embedding_functions
2
3
  from langtrace_python_sdk.constants.instrumentation.common import SERVICE_PROVIDERS
3
- from langtrace_python_sdk.constants.instrumentation.openai import APIS
4
- from opentelemetry.trace.status import StatusCode, Status
5
- from langtrace_python_sdk.instrumentation.chroma.patch import collection_patch
6
- from opentelemetry.trace import SpanKind
7
- from tests.utils import common_setup
8
- import unittest
9
- import json
10
4
 
11
-
12
- class TestChromaPatch(unittest.TestCase):
13
- data = {
14
- "status": "success",
15
- }
16
-
17
- def setUp(self):
18
- self.chroma_mock, self.tracer, self.span = common_setup(
19
- self.data, "chromadb.Collection.add"
20
- )
21
- self.wrapped_method = MagicMock(return_value="mocked method result")
22
- self.instance = MagicMock()
23
- self.instance.name = "aa"
24
-
25
- def tearDown(self):
26
- self.chroma_mock.stop()
27
-
28
- def test_collection_patch_success(self):
29
- # Arrange
30
- traced_method = collection_patch("ADD", "1.2.3", self.tracer)
31
-
32
- # Act
33
- result = traced_method(self.wrapped_method, self.instance, (), {})
34
-
35
- # Assert
36
- # Assert the result of the original method is returned
37
- self.assertEqual(result, "mocked method result")
38
-
39
- # Assert the span is started with the correct parameters
40
- self.assertTrue(
41
- self.tracer.start_as_current_span.called_once_with(
42
- "chromadb.Collection.add", kind=SpanKind.CLIENT
43
- )
44
- )
45
-
46
- # Verify span attributes are set as expected
47
- expected_attributes = {
48
- "langtrace.sdk.name": "langtrace-python-sdk",
49
- "langtrace.service.name": "Chroma",
50
- "langtrace.service.type": "vectordb",
51
- "langtrace.service.version": "1.2.3",
52
- "langtrace.version": "1.0.0",
53
- "db.system": "chromadb",
54
- "db.operation": "add",
55
- "db.collection.name": "aa",
56
- }
57
- for key, value in expected_attributes.items():
58
- self.span.set_attribute.assert_has_calls([call(key, value)], any_order=True)
59
-
60
- actual_calls = self.span.set_attribute.call_args_list
61
-
62
- for key, value in expected_attributes.items():
63
- self.assertIn(call(key, value), actual_calls)
64
-
65
- # Assert the span status is set to OK
66
- self.span.set_status.assert_called_with(StatusCode.OK)
67
-
68
-
69
- if __name__ == "__main__":
70
- unittest.main()
5
+ COLLECTION_NAME = "test_collection"
6
+
7
+
8
+ def test_chroma_add(chroma_client, exporter):
9
+ embedder = embedding_functions.DefaultEmbeddingFunction()
10
+ collection = chroma_client.create_collection(
11
+ name=COLLECTION_NAME, embedding_function=embedder
12
+ )
13
+ collection.add(
14
+ documents=["This is a document", "This is another document"],
15
+ metadatas=[{"source": "my_source"}, {"source": "my_source"}],
16
+ ids=["id1", "id2"],
17
+ )
18
+ spans = exporter.get_finished_spans()
19
+ chroma_span = spans[-1]
20
+
21
+ attributes = chroma_span.attributes
22
+
23
+ assert attributes.get("db.collection.name") == COLLECTION_NAME
24
+ assert attributes.get("db.operation") == "add"
25
+ assert attributes.get("db.system") == "chromadb"
26
+ assert attributes.get("langtrace.sdk.name") == "langtrace-python-sdk"
27
+ assert attributes.get("langtrace.service.name") == SERVICE_PROVIDERS["CHROMA"]
28
+ assert attributes.get("langtrace.service.type") == "vectordb"
29
+ assert attributes.get("langtrace.service.version") == importlib.metadata.version(
30
+ "chromadb"
31
+ )