langtrace-python-sdk 2.0.13__py3-none-any.whl → 2.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. examples/pinecone_example/basic.py +7 -2
  2. examples/weaviate_example/query_text.py +1737 -0
  3. langtrace_python_sdk/__init__.py +7 -1
  4. langtrace_python_sdk/constants/instrumentation/common.py +1 -0
  5. langtrace_python_sdk/constants/instrumentation/weaviate.py +44 -0
  6. langtrace_python_sdk/instrumentation/chroma/patch.py +191 -0
  7. langtrace_python_sdk/instrumentation/openai/patch.py +1 -0
  8. langtrace_python_sdk/instrumentation/pinecone/patch.py +1 -0
  9. langtrace_python_sdk/instrumentation/qdrant/patch.py +28 -4
  10. langtrace_python_sdk/instrumentation/weaviate/__init__.py +0 -0
  11. langtrace_python_sdk/instrumentation/weaviate/instrumentation.py +66 -0
  12. langtrace_python_sdk/instrumentation/weaviate/patch.py +166 -0
  13. langtrace_python_sdk/langtrace.py +4 -0
  14. langtrace_python_sdk/types/__init__.py +1 -0
  15. langtrace_python_sdk/utils/__init__.py +0 -2
  16. langtrace_python_sdk/utils/llm.py +0 -1
  17. langtrace_python_sdk/utils/misc.py +30 -0
  18. langtrace_python_sdk/utils/types.py +19 -0
  19. langtrace_python_sdk/utils/with_root_span.py +99 -4
  20. langtrace_python_sdk/version.py +1 -1
  21. {langtrace_python_sdk-2.0.13.dist-info → langtrace_python_sdk-2.1.0.dist-info}/METADATA +30 -15
  22. {langtrace_python_sdk-2.0.13.dist-info → langtrace_python_sdk-2.1.0.dist-info}/RECORD +28 -22
  23. tests/cohere/cassettes/test_cohere_chat.yaml +19 -21
  24. tests/cohere/cassettes/test_cohere_chat_streaming.yaml +73 -135
  25. tests/cohere/cassettes/test_cohere_embed.yaml +9 -9
  26. tests/cohere/cassettes/test_cohere_rerank.yaml +8 -8
  27. {langtrace_python_sdk-2.0.13.dist-info → langtrace_python_sdk-2.1.0.dist-info}/WHEEL +0 -0
  28. {langtrace_python_sdk-2.0.13.dist-info → langtrace_python_sdk-2.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -17,5 +17,11 @@ limitations under the License.
17
17
  from langtrace_python_sdk import langtrace
18
18
  from langtrace_python_sdk.utils.with_root_span import with_langtrace_root_span
19
19
  from langtrace_python_sdk.utils.prompt_registry import get_prompt_from_registry
20
+ from langtrace_python_sdk.utils.with_root_span import send_user_feedback
20
21
 
21
- __all__ = ["langtrace", "with_langtrace_root_span", "get_prompt_from_registry"]
22
+ __all__ = [
23
+ "langtrace",
24
+ "with_langtrace_root_span",
25
+ "get_prompt_from_registry",
26
+ "send_user_feedback",
27
+ ]
@@ -22,6 +22,7 @@ SERVICE_PROVIDERS = {
22
22
  "COHERE": "Cohere",
23
23
  "PPLX": "Perplexity",
24
24
  "QDRANT": "Qdrant",
25
+ "WEAVIATE": "Weaviate",
25
26
  }
26
27
 
27
28
  LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY = "langtrace_additional_attributes"
@@ -0,0 +1,44 @@
1
+ from langtrace.trace_attributes import WeaviateMethods
2
+
3
+ APIS = {
4
+ "weaviate.query.bm25": {
5
+ "MODULE": WeaviateMethods.QUERY_BM25.value,
6
+ "METHOD": "_BM25Query.bm25",
7
+ "OPERATION": "query",
8
+ },
9
+ "weaviate.query.fetch_object_by_id": {
10
+ "MODULE": WeaviateMethods.QUERY_FETCH_OBJECT_BY_ID.value,
11
+ "METHOD": "_FetchObjectByIDQuery.fetch_object_by_id",
12
+ "OPERATION": "query",
13
+ },
14
+ "weaviate.query.fetch_objects": {
15
+ "MODULE": WeaviateMethods.QUERY_FETCH_OBJECTS.value,
16
+ "METHOD": "_FetchObjectsQuery.fetch_objects",
17
+ "OPERATION": "query",
18
+ },
19
+ "weaviate.query.hybrid": {
20
+ "MODULE": WeaviateMethods.QUERY_HYBRID.value,
21
+ "METHOD": "_HybridQuery.hybrid",
22
+ "OPERATION": "query",
23
+ },
24
+ "weaviate.query.near_object": {
25
+ "MODULE": WeaviateMethods.QUERY_NEAR_OBJECT.value,
26
+ "METHOD": "_NearObjectQuery.near_object",
27
+ "OPERATION": "query",
28
+ },
29
+ "weaviate.query.near_text": {
30
+ "MODULE": WeaviateMethods.QUERY_NEAR_TEXT.value,
31
+ "METHOD": "_NearTextQuery.near_text",
32
+ "OPERATION": "query",
33
+ },
34
+ "weaviate.query.near_vector": {
35
+ "MODULE": WeaviateMethods.QUERY_NEAR_VECTOR.value,
36
+ "METHOD": "_NearVectorQuery.near_vector",
37
+ "OPERATION": "query",
38
+ },
39
+ "weaviate.collections.create": {
40
+ "MODULE": WeaviateMethods.COLLECTIONS_OPERATIONS.value,
41
+ "METHOD": "_Collections.create",
42
+ "OPERATION": "create",
43
+ },
44
+ }
@@ -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.llm import set_span_attributes
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
  LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
25
27
  SERVICE_PROVIDERS,
26
28
  )
29
+ import json
27
30
 
28
31
 
29
32
  def collection_patch(method, version, tracer):
@@ -44,6 +47,7 @@ def collection_patch(method, version, tracer):
44
47
  "langtrace.version": "1.0.0",
45
48
  "db.system": "chromadb",
46
49
  "db.operation": api["OPERATION"],
50
+ "db.query": json.dumps(kwargs.get("query")),
47
51
  **(extra_attributes if extra_attributes is not None else {}),
48
52
  }
49
53
 
@@ -57,8 +61,32 @@ def collection_patch(method, version, tracer):
57
61
  if value is not None:
58
62
  span.set_attribute(field, value)
59
63
  try:
64
+
65
+ operation = api["OPERATION"]
66
+ if operation == "add":
67
+ _set_chroma_add_attributes(span, kwargs)
68
+ elif operation == "get":
69
+ _set_chroma_get_attributes(span, kwargs)
70
+ elif operation == "query":
71
+ _set_chroma_query_attributes(span, kwargs)
72
+ elif operation == "peek":
73
+ _set_chroma_peek_attributes(span, kwargs)
74
+ elif operation == "update":
75
+ _set_chroma_update_attributes(span, kwargs)
76
+ elif operation == "upsert":
77
+ _set_chroma_upsert_attributes(span, kwargs)
78
+ elif operation == "modify":
79
+ _set_chroma_modify_attributes(span, kwargs)
80
+ elif operation == "delete":
81
+ _set_chroma_delete_attributes(span, kwargs)
60
82
  # Attempt to call the original method
61
83
  result = wrapped(*args, **kwargs)
84
+
85
+ if operation == "query":
86
+ events = _set_chroma_query_response(span, result)
87
+ for event in events:
88
+ span.add_event(name="db.chroma.query.result", attributes=event)
89
+
62
90
  span.set_status(StatusCode.OK)
63
91
  return result
64
92
  except Exception as err:
@@ -72,3 +100,166 @@ def collection_patch(method, version, tracer):
72
100
  raise
73
101
 
74
102
  return traced_method
103
+
104
+
105
+ def get_count_or_none(value):
106
+ return len(value) if value is not None else None
107
+
108
+
109
+ def handle_null_params(param):
110
+ return str(param) if param else None
111
+
112
+
113
+ @silently_fail
114
+ def _set_chroma_add_attributes(span, kwargs):
115
+ set_span_attributes(
116
+ span, "db.chroma.add.ids_count", get_count_or_none(kwargs.get("ids"))
117
+ )
118
+ set_span_attributes(
119
+ span,
120
+ "db.chroma.add.embeddings_count",
121
+ get_count_or_none(kwargs.get("embeddings")),
122
+ )
123
+ set_span_attributes(
124
+ span,
125
+ "db.chroma.add.metadatas_count",
126
+ get_count_or_none(kwargs.get("metadatas")),
127
+ )
128
+ set_span_attributes(
129
+ span,
130
+ "db.chroma.add.documents_count",
131
+ get_count_or_none(kwargs.get("documents")),
132
+ )
133
+
134
+
135
+ @silently_fail
136
+ def _set_chroma_get_attributes(span, kwargs):
137
+ set_span_attributes(
138
+ span, "db.chroma.get.ids_count", get_count_or_none(kwargs.get("ids"))
139
+ )
140
+ set_span_attributes(
141
+ span, "db.chroma.get.where", handle_null_params(kwargs.get("where"))
142
+ )
143
+ set_span_attributes(span, "db.chroma.get.limit", kwargs.get("limit"))
144
+ set_span_attributes(span, "db.chroma.get.offset", kwargs.get("offset"))
145
+ set_span_attributes(
146
+ span,
147
+ "db.chroma.get.where_document",
148
+ handle_null_params(kwargs.get("where_document")),
149
+ )
150
+ set_span_attributes(
151
+ span, "db.chroma.get.include", handle_null_params(kwargs.get("include"))
152
+ )
153
+
154
+
155
+ @silently_fail
156
+ def _set_chroma_query_attributes(span, kwargs):
157
+ set_span_attributes(
158
+ span,
159
+ "db.chroma.query.query_embeddings_count",
160
+ get_count_or_none(kwargs.get("query_embeddings")),
161
+ )
162
+ set_span_attributes(
163
+ span,
164
+ "db.chroma.query.query_texts_count",
165
+ get_count_or_none(kwargs.get("query_texts")),
166
+ )
167
+ set_span_attributes(span, "db.chroma.query.n_results", kwargs.get("n_results"))
168
+ set_span_attributes(
169
+ span, "db.chroma.query.where", handle_null_params(kwargs.get("where"))
170
+ )
171
+ set_span_attributes(
172
+ span,
173
+ "db.chroma.query.where_document",
174
+ handle_null_params(kwargs.get("where_document")),
175
+ )
176
+ set_span_attributes(
177
+ span, "db.chroma.query.include", handle_null_params(kwargs.get("include"))
178
+ )
179
+
180
+
181
+ @silently_fail
182
+ def _set_chroma_peek_attributes(span, kwargs):
183
+ set_span_attributes(span, "db.chroma.peek.limit", kwargs.get("limit"))
184
+
185
+
186
+ @silently_fail
187
+ def _set_chroma_update_attributes(span, kwargs):
188
+ set_span_attributes(
189
+ span, "db.chroma.update.ids_count", get_count_or_none(kwargs.get("ids"))
190
+ )
191
+ set_span_attributes(
192
+ span,
193
+ "db.chroma.update.embeddings_count",
194
+ get_count_or_none(kwargs.get("embeddings")),
195
+ )
196
+ set_span_attributes(
197
+ span,
198
+ "db.chroma.update.metadatas_count",
199
+ get_count_or_none(kwargs.get("metadatas")),
200
+ )
201
+ set_span_attributes(
202
+ span,
203
+ "db.chroma.update.documents_count",
204
+ get_count_or_none(kwargs.get("documents")),
205
+ )
206
+
207
+
208
+ @silently_fail
209
+ def _set_chroma_modify_attributes(span, kwargs):
210
+ set_span_attributes(span, "db.chroma.modify.name", kwargs.get("name"))
211
+ # TODO: Add metadata attribute
212
+
213
+
214
+ @silently_fail
215
+ def _set_chroma_upsert_attributes(span, kwargs):
216
+ set_span_attributes(
217
+ span,
218
+ "db.chroma.upsert.embeddings_count",
219
+ get_count_or_none(kwargs.get("embeddings")),
220
+ )
221
+ set_span_attributes(
222
+ span,
223
+ "db.chroma.upsert.metadatas_count",
224
+ get_count_or_none(kwargs.get("metadatas")),
225
+ )
226
+ set_span_attributes(
227
+ span,
228
+ "db.chroma.upsert.documents_count",
229
+ get_count_or_none(kwargs.get("documents")),
230
+ )
231
+
232
+
233
+ @silently_fail
234
+ def _set_chroma_delete_attributes(span, kwargs):
235
+ set_span_attributes(
236
+ span, "db.chroma.delete.ids_count", get_count_or_none(kwargs.get("ids"))
237
+ )
238
+ set_span_attributes(
239
+ span, "db.chroma.delete.where", handle_null_params(kwargs.get("where"))
240
+ )
241
+ set_span_attributes(
242
+ span,
243
+ "db.chroma.delete.where_document",
244
+ handle_null_params(kwargs.get("where_document")),
245
+ )
246
+
247
+
248
+ @silently_fail
249
+ def _set_chroma_query_response(span, result):
250
+
251
+ attributes = []
252
+ ids = result.get("ids")[0]
253
+ distances = result.get("distances")[0]
254
+ metadatas = result.get("metadatas")[0]
255
+ documents = result.get("documents")[0]
256
+
257
+ for idx, _ in enumerate(ids):
258
+ attribute = {
259
+ "id": ids[idx],
260
+ "distance": distances[idx],
261
+ "metadata": metadatas[idx],
262
+ "document": documents[idx],
263
+ }
264
+ attributes.append(attribute)
265
+ return attributes
@@ -616,6 +616,7 @@ def async_chat_completions_create(original_method, version, tracer):
616
616
  span.add_event(Event.STREAM_START.value)
617
617
  completion_tokens = 0
618
618
  try:
619
+ content = []
619
620
  async for chunk in result:
620
621
  if hasattr(chunk, "model") and chunk.model is not None:
621
622
  span.set_attribute("llm.model", chunk.model)
@@ -46,6 +46,7 @@ def generic_patch(original_method, method, version, tracer):
46
46
  "langtrace.version": "1.0.0",
47
47
  "db.system": "pinecone",
48
48
  "db.operation": api["OPERATION"],
49
+ "db.query": json.dumps(kwargs.get("query")),
49
50
  **(extra_attributes if extra_attributes is not None else {}),
50
51
  }
51
52
 
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  """
16
16
 
17
+ import json
17
18
  from langtrace.trace_attributes import DatabaseSpanAttributes
18
19
  from langtrace_python_sdk.utils.silently_fail import silently_fail
19
20
  from langtrace_python_sdk.utils.llm import set_span_attributes
@@ -46,6 +47,7 @@ def collection_patch(method, version, tracer):
46
47
  "langtrace.version": "1.0.0",
47
48
  "db.system": "qdrant",
48
49
  "db.operation": api["OPERATION"],
50
+ "db.query": json.dumps(kwargs.get("query")),
49
51
  **(extra_attributes if extra_attributes is not None else {}),
50
52
  }
51
53
 
@@ -55,12 +57,22 @@ def collection_patch(method, version, tracer):
55
57
  collection_name = kwargs.get("collection_name") or args[0]
56
58
  operation = api["OPERATION"]
57
59
  set_span_attributes(span, "db.collection.name", collection_name)
58
- if operation == "upsert":
59
- _set_upsert_attributes(span, args, kwargs)
60
- elif operation == "add":
60
+
61
+ if operation == "add":
61
62
  _set_upload_attributes(span, args, kwargs, "documents")
62
63
 
63
- # Todo: Add support for other operations here.
64
+ elif operation == "upsert":
65
+ _set_upsert_attributes(span, args, kwargs)
66
+
67
+ elif operation in ["query", "discover", "recommend", "retrieve", "search"]:
68
+ _set_search_attributes(span, args, kwargs)
69
+ elif operation in [
70
+ "query_batch",
71
+ "discover_batch",
72
+ "recommend_batch",
73
+ "search_batch",
74
+ ]:
75
+ _set_batch_search_attributes(span, args, kwargs, operation)
64
76
 
65
77
  for field, value in attributes.model_dump(by_alias=True).items():
66
78
  if value is not None:
@@ -104,3 +116,15 @@ def _set_upload_attributes(span, args, kwargs, field):
104
116
  length = len(docs.ids)
105
117
 
106
118
  set_span_attributes(span, f"db.upload.{field}_count", length)
119
+
120
+
121
+ @silently_fail
122
+ def _set_search_attributes(span, args, kwargs):
123
+ limit = kwargs.get("limit") or 10
124
+ set_span_attributes(span, "db.query.top_k", limit)
125
+
126
+
127
+ @silently_fail
128
+ def _set_batch_search_attributes(span, args, kwargs, method):
129
+ requests = kwargs.get("requests") or []
130
+ set_span_attributes(span, f"db.{method}.requests_count", len(requests))
@@ -0,0 +1,66 @@
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
+ import importlib.metadata
18
+ import logging
19
+ from typing import Collection
20
+
21
+ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
22
+ from opentelemetry.trace import get_tracer
23
+ from wrapt import wrap_function_wrapper
24
+
25
+ from langtrace_python_sdk.instrumentation.weaviate.patch import (
26
+ generic_collection_patch,
27
+ generic_query_patch,
28
+ )
29
+ from src.langtrace_python_sdk.constants.instrumentation.weaviate import APIS
30
+
31
+ logging.basicConfig(level=logging.FATAL)
32
+
33
+
34
+ class WeaviateInstrumentation(BaseInstrumentor):
35
+ """
36
+ The WeaviateInstrumentation class represents the instrumentation for the Weaviate SDK.
37
+ """
38
+
39
+ def instrumentation_dependencies(self) -> Collection[str]:
40
+ return ["weaviate-client >= 4.6.1", "trace-attributes >= 4.0.2"]
41
+
42
+ def _instrument(self, **kwargs):
43
+ tracer_provider = kwargs.get("tracer_provider")
44
+ tracer = get_tracer(__name__, "", tracer_provider)
45
+ version = importlib.metadata.version("weaviate-client")
46
+
47
+ for api_name, api_config in APIS.items():
48
+ module_path, function_name = api_name.rsplit(".", 1)
49
+ if api_config.get("OPERATION") == "query":
50
+ wrap_function_wrapper(
51
+ api_config["MODULE"],
52
+ api_config["METHOD"],
53
+ generic_query_patch(api_name, version, tracer),
54
+ )
55
+ elif api_config.get("OPERATION") == "create":
56
+ wrap_function_wrapper(
57
+ api_config["MODULE"],
58
+ api_config["METHOD"],
59
+ generic_collection_patch(api_name, version, tracer),
60
+ )
61
+
62
+ def _instrument_module(self, module_name):
63
+ pass
64
+
65
+ def _uninstrument(self, **kwargs):
66
+ pass
@@ -0,0 +1,166 @@
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
+ import json
18
+
19
+ from langtrace.trace_attributes import DatabaseSpanAttributes
20
+ from opentelemetry import baggage
21
+ from opentelemetry.trace import SpanKind
22
+ from opentelemetry.trace.status import Status, StatusCode
23
+
24
+ from langtrace_python_sdk.constants.instrumentation.common import (
25
+ LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
26
+ SERVICE_PROVIDERS,
27
+ )
28
+ from langtrace_python_sdk.constants.instrumentation.weaviate import APIS
29
+ from langtrace_python_sdk.utils.misc import extract_input_params, to_iso_format
30
+
31
+ # Predefined metadata response attributes
32
+ METADATA_ATTRIBUTES = [
33
+ "creation_time",
34
+ "last_update_time",
35
+ "distance",
36
+ "certainty",
37
+ "score",
38
+ "explain_score",
39
+ "is_consistent",
40
+ "rerank_score",
41
+ ]
42
+
43
+
44
+ def extract_metadata(metadata):
45
+ # Extraction response Query metadata
46
+ extracted_metadata = {
47
+ attr: (
48
+ to_iso_format(getattr(metadata, attr))
49
+ if "time" in attr
50
+ else getattr(metadata, attr)
51
+ )
52
+ for attr in METADATA_ATTRIBUTES
53
+ if hasattr(metadata, attr)
54
+ }
55
+
56
+ return {k: v for k, v in extracted_metadata.items() if v is not None}
57
+
58
+
59
+ def aggregate_responses(result):
60
+ all_responses = []
61
+
62
+ if hasattr(result, "objects") and result.objects is not None:
63
+ for each_obj in result.objects:
64
+ # Loop for multiple object responses
65
+ response_attributes = get_response_object_attributes(each_obj)
66
+ all_responses.append(response_attributes)
67
+ else:
68
+ # For single object responses
69
+ all_responses = get_response_object_attributes(result)
70
+
71
+ return json.dumps(all_responses)
72
+
73
+
74
+ def get_response_object_attributes(response_object):
75
+
76
+ response_attributes = {
77
+ **response_object.properties,
78
+ "uuid": str(response_object.uuid) if hasattr(response_object, "uuid") else None,
79
+ "collection": (
80
+ response_object.collection
81
+ if hasattr(response_object, "collection")
82
+ else None
83
+ ),
84
+ "vector": (
85
+ response_object.vector if hasattr(response_object, "vector") else None
86
+ ),
87
+ "references": (
88
+ response_object.references
89
+ if hasattr(response_object, "references")
90
+ else None
91
+ ),
92
+ "metadata": (
93
+ extract_metadata(response_object.metadata)
94
+ if hasattr(response_object, "metadata")
95
+ else None
96
+ ),
97
+ }
98
+ response_attributes = {
99
+ k: v for k, v in response_attributes.items() if v is not None
100
+ }
101
+ return response_attributes
102
+
103
+
104
+ def create_traced_method(method_name, version, tracer, get_collection_name=None):
105
+ def traced_method(wrapped, instance, args, kwargs):
106
+ api = APIS[method_name]
107
+ service_provider = SERVICE_PROVIDERS["WEAVIATE"]
108
+ extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
109
+
110
+ collection_name = (
111
+ get_collection_name(instance, kwargs)
112
+ if get_collection_name
113
+ else instance._name
114
+ )
115
+
116
+ span_attributes = {
117
+ "langtrace.sdk.name": "langtrace-python-sdk",
118
+ "langtrace.service.name": service_provider,
119
+ "langtrace.service.type": "vectordb",
120
+ "langtrace.service.version": version,
121
+ "langtrace.version": "1.0.0",
122
+ "db.system": "weaviate",
123
+ "db.operation": api["OPERATION"],
124
+ "db.collection.name": collection_name,
125
+ "db.query": json.dumps(extract_input_params(args, kwargs)),
126
+ **(extra_attributes if extra_attributes is not None else {}),
127
+ }
128
+
129
+ attributes = DatabaseSpanAttributes(**span_attributes)
130
+
131
+ with tracer.start_as_current_span(method_name, kind=SpanKind.CLIENT) as span:
132
+ for field, value in attributes.model_dump(by_alias=True).items():
133
+ if value is not None:
134
+ span.set_attribute(field, value)
135
+ try:
136
+ # Attempt to call the original method
137
+ result = wrapped(*args, **kwargs)
138
+ if api["OPERATION"] == "query":
139
+ span.add_event(
140
+ name="db.response",
141
+ attributes={"db.response": aggregate_responses(result)},
142
+ )
143
+ span.set_status(StatusCode.OK)
144
+ return result
145
+ except Exception as err:
146
+ # Record the exception in the span
147
+ span.record_exception(err)
148
+ # Set the span status to indicate an error
149
+ span.set_status(Status(StatusCode.ERROR, str(err)))
150
+ # Reraise the exception to ensure it's not swallowed
151
+ raise
152
+
153
+ return traced_method
154
+
155
+
156
+ def generic_query_patch(method_name, version, tracer):
157
+ return create_traced_method(method_name, version, tracer)
158
+
159
+
160
+ def generic_collection_patch(method_name, version, tracer):
161
+ return create_traced_method(
162
+ method_name,
163
+ version,
164
+ tracer,
165
+ get_collection_name=lambda instance, kwargs: kwargs.get("name"),
166
+ )
@@ -64,6 +64,9 @@ from langtrace_python_sdk.instrumentation.pinecone.instrumentation import (
64
64
  from langtrace_python_sdk.instrumentation.qdrant.instrumentation import (
65
65
  QdrantInstrumentation,
66
66
  )
67
+ from langtrace_python_sdk.instrumentation.weaviate.instrumentation import (
68
+ WeaviateInstrumentation,
69
+ )
67
70
 
68
71
 
69
72
  def init(
@@ -119,6 +122,7 @@ def init(
119
122
  "langgraph": LanggraphInstrumentation(),
120
123
  "anthropic": AnthropicInstrumentation(),
121
124
  "cohere": CohereInstrumentation(),
125
+ "weaviate": WeaviateInstrumentation(),
122
126
  }
123
127
 
124
128
  init_instrumentations(disable_instrumentations, all_instrumentations)
@@ -15,6 +15,7 @@ class InstrumentationType(Enum):
15
15
  LANGCHAIN_CORE = "langchain_core"
16
16
  LANGCHAIN_COMMUNITY = "langchain_community"
17
17
  LANGGRAPH = "langgraph"
18
+ WEAVIATE = "weaviate"
18
19
 
19
20
  @staticmethod
20
21
  def from_string(value: str):
@@ -3,5 +3,3 @@ def set_span_attribute(span, name, value):
3
3
  if value != "":
4
4
  span.set_attribute(name, value)
5
5
  return
6
-
7
-
@@ -65,4 +65,3 @@ def set_span_attributes(span, name, value):
65
65
  if value != "":
66
66
  span.set_attribute(name, value)
67
67
  return
68
-
@@ -0,0 +1,30 @@
1
+ from datetime import datetime
2
+ import json
3
+
4
+
5
+ def extract_input_params(args, kwargs):
6
+ extracted_params = {}
7
+ for key, value in kwargs.items():
8
+ if hasattr(value, "__dict__"):
9
+ extracted_params[key] = json.dumps(vars(value))
10
+ else:
11
+ extracted_params[key] = value
12
+ for i, value in enumerate(args):
13
+ if hasattr(value, "__dict__"):
14
+ extracted_params[f"arg{i}"] = json.dumps(vars(value))
15
+ else:
16
+ extracted_params[f"arg{i}"] = value
17
+ # Remove None values
18
+ return {k: v for k, v in extracted_params.items() if v is not None}
19
+
20
+
21
+ def to_iso_format(value):
22
+ return (
23
+ None
24
+ if value is None
25
+ else (
26
+ value.isoformat(timespec="microseconds") + "Z"
27
+ if isinstance(value, datetime)
28
+ else None
29
+ )
30
+ )