openlit 1.29.0__py3-none-any.whl → 1.29.2__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.
@@ -31,10 +31,179 @@ def async_chat_completions(gen_ai_endpoint, version, environment, application_na
31
31
  A function that wraps the chat completions method to add telemetry.
32
32
  """
33
33
 
34
+ class TracedAsyncStream:
35
+ """
36
+ Wrapper for streaming responses to collect metrics and trace data.
37
+ Wraps the 'openai.AsyncStream' response to collect message IDs and aggregated response.
38
+
39
+ This class implements the '__aiter__' and '__anext__' methods that
40
+ handle asynchronous streaming responses.
41
+
42
+ This class also implements '__aenter__' and '__aexit__' methods that
43
+ handle asynchronous context management protocol.
44
+ """
45
+ def __init__(
46
+ self,
47
+ wrapped,
48
+ span,
49
+ *args,
50
+ **kwargs,
51
+ ):
52
+ self.__wrapped__ = wrapped
53
+ self._span = span
54
+ # Placeholder for aggregating streaming response
55
+ self._llmresponse = ""
56
+ self._response_id = ""
57
+
58
+ self._args = args
59
+ self._kwargs = kwargs
60
+
61
+ async def __aenter__(self):
62
+ await self.__wrapped__.__aenter__()
63
+ return self
64
+
65
+ async def __aexit__(self, exc_type, exc_value, traceback):
66
+ await self.__wrapped__.__aexit__(exc_type, exc_value, traceback)
67
+
68
+ def __aiter__(self):
69
+ return self
70
+
71
+ async def __anext__(self):
72
+ try:
73
+ chunk = await self.__wrapped__.__anext__()
74
+ # Collect message IDs and aggregated response from events
75
+ if len(chunk.choices) > 0:
76
+ # pylint: disable=line-too-long
77
+ if hasattr(chunk.choices[0], "delta") and hasattr(chunk.choices[0].delta, "content"):
78
+ content = chunk.choices[0].delta.content
79
+ if content:
80
+ self._llmresponse += content
81
+ self._response_id = chunk.id
82
+ return chunk
83
+ except StopAsyncIteration:
84
+ # Handling exception ensure observability without disrupting operation
85
+ try:
86
+ # Format 'messages' into a single string
87
+ message_prompt = self._kwargs.get("messages", "")
88
+ formatted_messages = []
89
+ for message in message_prompt:
90
+ role = message["role"]
91
+ content = message["content"]
92
+
93
+ if isinstance(content, list):
94
+ content_str = ", ".join(
95
+ # pylint: disable=line-too-long
96
+ f'{item["type"]}: {item["text"] if "text" in item else item["image_url"]}'
97
+ if "type" in item else f'text: {item["text"]}'
98
+ for item in content
99
+ )
100
+ formatted_messages.append(f"{role}: {content_str}")
101
+ else:
102
+ formatted_messages.append(f"{role}: {content}")
103
+ prompt = "\n".join(formatted_messages)
104
+
105
+ # Calculate tokens using input prompt and aggregated response
106
+ prompt_tokens = openai_tokens(prompt,
107
+ self._kwargs.get("model", "gpt-3.5-turbo"))
108
+ completion_tokens = openai_tokens(self._llmresponse,
109
+ self._kwargs.get("model", "gpt-3.5-turbo"))
110
+
111
+ # Calculate cost of the operation
112
+ cost = get_chat_model_cost(self._kwargs.get("model", "gpt-3.5-turbo"),
113
+ pricing_info, prompt_tokens,
114
+ completion_tokens)
115
+
116
+ # Set Span attributes
117
+ self._span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
118
+ self._span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
119
+ SemanticConvetion.GEN_AI_SYSTEM_OPENAI)
120
+ self._span.set_attribute(SemanticConvetion.GEN_AI_TYPE,
121
+ SemanticConvetion.GEN_AI_TYPE_CHAT)
122
+ self._span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
123
+ gen_ai_endpoint)
124
+ self._span.set_attribute(SemanticConvetion.GEN_AI_RESPONSE_ID,
125
+ self._response_id)
126
+ self._span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
127
+ environment)
128
+ self._span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
129
+ application_name)
130
+ self._span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MODEL,
131
+ self._kwargs.get("model", "gpt-3.5-turbo"))
132
+ self._span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_USER,
133
+ self._kwargs.get("user", ""))
134
+ self._span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TOP_P,
135
+ self._kwargs.get("top_p", 1.0))
136
+ self._span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MAX_TOKENS,
137
+ self._kwargs.get("max_tokens", -1))
138
+ self._span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TEMPERATURE,
139
+ self._kwargs.get("temperature", 1.0))
140
+ self._span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_PRESENCE_PENALTY,
141
+ self._kwargs.get("presence_penalty", 0.0))
142
+ self._span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_FREQUENCY_PENALTY,
143
+ self._kwargs.get("frequency_penalty", 0.0))
144
+ self._span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_SEED,
145
+ self._kwargs.get("seed", ""))
146
+ self._span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_IS_STREAM,
147
+ True)
148
+ self._span.set_attribute(SemanticConvetion.GEN_AI_USAGE_PROMPT_TOKENS,
149
+ prompt_tokens)
150
+ self._span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COMPLETION_TOKENS,
151
+ completion_tokens)
152
+ self._span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
153
+ prompt_tokens + completion_tokens)
154
+ self._span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
155
+ cost)
156
+ if trace_content:
157
+ self._span.add_event(
158
+ name=SemanticConvetion.GEN_AI_CONTENT_PROMPT_EVENT,
159
+ attributes={
160
+ SemanticConvetion.GEN_AI_CONTENT_PROMPT: prompt,
161
+ },
162
+ )
163
+ self._span.add_event(
164
+ name=SemanticConvetion.GEN_AI_CONTENT_COMPLETION_EVENT,
165
+ attributes={
166
+ SemanticConvetion.GEN_AI_CONTENT_COMPLETION: self._llmresponse,
167
+ },
168
+ )
169
+
170
+ self._span.set_status(Status(StatusCode.OK))
171
+
172
+ if disable_metrics is False:
173
+ attributes = {
174
+ TELEMETRY_SDK_NAME:
175
+ "openlit",
176
+ SemanticConvetion.GEN_AI_APPLICATION_NAME:
177
+ application_name,
178
+ SemanticConvetion.GEN_AI_SYSTEM:
179
+ SemanticConvetion.GEN_AI_SYSTEM_OPENAI,
180
+ SemanticConvetion.GEN_AI_ENVIRONMENT:
181
+ environment,
182
+ SemanticConvetion.GEN_AI_TYPE:
183
+ SemanticConvetion.GEN_AI_TYPE_CHAT,
184
+ SemanticConvetion.GEN_AI_REQUEST_MODEL:
185
+ self._kwargs.get("model", "gpt-3.5-turbo")
186
+ }
187
+
188
+ metrics["genai_requests"].add(1, attributes)
189
+ metrics["genai_total_tokens"].add(
190
+ prompt_tokens + completion_tokens, attributes
191
+ )
192
+ metrics["genai_completion_tokens"].add(completion_tokens, attributes)
193
+ metrics["genai_prompt_tokens"].add(prompt_tokens, attributes)
194
+ metrics["genai_cost"].record(cost, attributes)
195
+
196
+ except Exception as e:
197
+ handle_exception(self._span, e)
198
+ logger.error("Error in trace creation: %s", e)
199
+ finally:
200
+ self._span.end()
201
+ raise
202
+
34
203
  async def wrapper(wrapped, instance, args, kwargs):
35
204
  """
36
205
  Wraps the 'chat.completions' API call to add telemetry.
37
-
206
+
38
207
  This collects metrics such as execution time, cost, and token usage, and handles errors
39
208
  gracefully, adding details to the trace for observability.
40
209
 
@@ -54,140 +223,10 @@ def async_chat_completions(gen_ai_endpoint, version, environment, application_na
54
223
  # pylint: disable=no-else-return
55
224
  if streaming:
56
225
  # Special handling for streaming response to accommodate the nature of data flow
57
- async def stream_generator():
58
- with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
59
- # Placeholder for aggregating streaming response
60
- llmresponse = ""
61
-
62
- # Loop through streaming events capturing relevant details
63
- async for chunk in await wrapped(*args, **kwargs):
64
- # Collect message IDs and aggregated response from events
65
- if len(chunk.choices) > 0:
66
- # pylint: disable=line-too-long
67
- if hasattr(chunk.choices[0], "delta") and hasattr(chunk.choices[0].delta, "content"):
68
- content = chunk.choices[0].delta.content
69
- if content:
70
- llmresponse += content
71
- yield chunk
72
- response_id = chunk.id
73
-
74
- # Handling exception ensure observability without disrupting operation
75
- try:
76
- # Format 'messages' into a single string
77
- message_prompt = kwargs.get("messages", "")
78
- formatted_messages = []
79
- for message in message_prompt:
80
- role = message["role"]
81
- content = message["content"]
82
-
83
- if isinstance(content, list):
84
- content_str = ", ".join(
85
- # pylint: disable=line-too-long
86
- f'{item["type"]}: {item["text"] if "text" in item else item["image_url"]}'
87
- if "type" in item else f'text: {item["text"]}'
88
- for item in content
89
- )
90
- formatted_messages.append(f"{role}: {content_str}")
91
- else:
92
- formatted_messages.append(f"{role}: {content}")
93
- prompt = "\n".join(formatted_messages)
94
-
95
- # Calculate tokens using input prompt and aggregated response
96
- prompt_tokens = openai_tokens(prompt,
97
- kwargs.get("model", "gpt-3.5-turbo"))
98
- completion_tokens = openai_tokens(llmresponse,
99
- kwargs.get("model", "gpt-3.5-turbo"))
226
+ awaited_wrapped = await wrapped(*args, **kwargs)
227
+ span = tracer.start_span(gen_ai_endpoint, kind=SpanKind.CLIENT)
100
228
 
101
- # Calculate cost of the operation
102
- cost = get_chat_model_cost(kwargs.get("model", "gpt-3.5-turbo"),
103
- pricing_info, prompt_tokens,
104
- completion_tokens)
105
-
106
- # Set Span attributes
107
- span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
108
- span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
109
- SemanticConvetion.GEN_AI_SYSTEM_OPENAI)
110
- span.set_attribute(SemanticConvetion.GEN_AI_TYPE,
111
- SemanticConvetion.GEN_AI_TYPE_CHAT)
112
- span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
113
- gen_ai_endpoint)
114
- span.set_attribute(SemanticConvetion.GEN_AI_RESPONSE_ID,
115
- response_id)
116
- span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
117
- environment)
118
- span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
119
- application_name)
120
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MODEL,
121
- kwargs.get("model", "gpt-3.5-turbo"))
122
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_USER,
123
- kwargs.get("user", ""))
124
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TOP_P,
125
- kwargs.get("top_p", 1.0))
126
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MAX_TOKENS,
127
- kwargs.get("max_tokens", -1))
128
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_TEMPERATURE,
129
- kwargs.get("temperature", 1.0))
130
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_PRESENCE_PENALTY,
131
- kwargs.get("presence_penalty", 0.0))
132
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_FREQUENCY_PENALTY,
133
- kwargs.get("frequency_penalty", 0.0))
134
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_SEED,
135
- kwargs.get("seed", ""))
136
- span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_IS_STREAM,
137
- True)
138
- span.set_attribute(SemanticConvetion.GEN_AI_USAGE_PROMPT_TOKENS,
139
- prompt_tokens)
140
- span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COMPLETION_TOKENS,
141
- completion_tokens)
142
- span.set_attribute(SemanticConvetion.GEN_AI_USAGE_TOTAL_TOKENS,
143
- prompt_tokens + completion_tokens)
144
- span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST,
145
- cost)
146
- if trace_content:
147
- span.add_event(
148
- name=SemanticConvetion.GEN_AI_CONTENT_PROMPT_EVENT,
149
- attributes={
150
- SemanticConvetion.GEN_AI_CONTENT_PROMPT: prompt,
151
- },
152
- )
153
- span.add_event(
154
- name=SemanticConvetion.GEN_AI_CONTENT_COMPLETION_EVENT,
155
- attributes={
156
- SemanticConvetion.GEN_AI_CONTENT_COMPLETION: llmresponse,
157
- },
158
- )
159
-
160
- span.set_status(Status(StatusCode.OK))
161
-
162
- if disable_metrics is False:
163
- attributes = {
164
- TELEMETRY_SDK_NAME:
165
- "openlit",
166
- SemanticConvetion.GEN_AI_APPLICATION_NAME:
167
- application_name,
168
- SemanticConvetion.GEN_AI_SYSTEM:
169
- SemanticConvetion.GEN_AI_SYSTEM_OPENAI,
170
- SemanticConvetion.GEN_AI_ENVIRONMENT:
171
- environment,
172
- SemanticConvetion.GEN_AI_TYPE:
173
- SemanticConvetion.GEN_AI_TYPE_CHAT,
174
- SemanticConvetion.GEN_AI_REQUEST_MODEL:
175
- kwargs.get("model", "gpt-3.5-turbo")
176
- }
177
-
178
- metrics["genai_requests"].add(1, attributes)
179
- metrics["genai_total_tokens"].add(
180
- prompt_tokens + completion_tokens, attributes
181
- )
182
- metrics["genai_completion_tokens"].add(completion_tokens, attributes)
183
- metrics["genai_prompt_tokens"].add(prompt_tokens, attributes)
184
- metrics["genai_cost"].record(cost, attributes)
185
-
186
- except Exception as e:
187
- handle_exception(span, e)
188
- logger.error("Error in trace creation: %s", e)
189
-
190
- return stream_generator()
229
+ return TracedAsyncStream(awaited_wrapped, span)
191
230
 
192
231
  # Handling for non-streaming responses
193
232
  else:
@@ -363,7 +402,7 @@ def async_embedding(gen_ai_endpoint, version, environment, application_name,
363
402
  tracer, pricing_info, trace_content, metrics, disable_metrics):
364
403
  """
365
404
  Generates a telemetry wrapper for embeddings to collect metrics.
366
-
405
+
367
406
  Args:
368
407
  gen_ai_endpoint: Endpoint identifier for logging and tracing.
369
408
  version: Version of the monitoring package.
@@ -372,7 +411,7 @@ def async_embedding(gen_ai_endpoint, version, environment, application_name,
372
411
  tracer: OpenTelemetry tracer for creating spans.
373
412
  pricing_info: Information used for calculating the cost of OpenAI usage.
374
413
  trace_content: Flag indicating whether to trace the actual content.
375
-
414
+
376
415
  Returns:
377
416
  A function that wraps the embeddings method to add telemetry.
378
417
  """
@@ -475,7 +514,7 @@ def async_finetune(gen_ai_endpoint, version, environment, application_name,
475
514
  tracer, pricing_info, trace_content, metrics, disable_metrics):
476
515
  """
477
516
  Generates a telemetry wrapper for fine-tuning jobs to collect metrics.
478
-
517
+
479
518
  Args:
480
519
  gen_ai_endpoint: Endpoint identifier for logging and tracing.
481
520
  version: Version of the monitoring package.
@@ -484,7 +523,7 @@ def async_finetune(gen_ai_endpoint, version, environment, application_name,
484
523
  tracer: OpenTelemetry tracer for creating spans.
485
524
  pricing_info: Information used for calculating the cost of OpenAI usage.
486
525
  trace_content: Flag indicating whether to trace the actual content.
487
-
526
+
488
527
  Returns:
489
528
  A function that wraps the fine tuning creation method to add telemetry.
490
529
  """
@@ -564,7 +603,7 @@ def async_image_generate(gen_ai_endpoint, version, environment, application_name
564
603
  tracer, pricing_info, trace_content, metrics, disable_metrics):
565
604
  """
566
605
  Generates a telemetry wrapper for image generation to collect metrics.
567
-
606
+
568
607
  Args:
569
608
  gen_ai_endpoint: Endpoint identifier for logging and tracing.
570
609
  version: Version of the monitoring package.
@@ -573,7 +612,7 @@ def async_image_generate(gen_ai_endpoint, version, environment, application_name
573
612
  tracer: OpenTelemetry tracer for creating spans.
574
613
  pricing_info: Information used for calculating the cost of OpenAI image generation.
575
614
  trace_content: Flag indicating whether to trace the input prompt and generated images.
576
-
615
+
577
616
  Returns:
578
617
  A function that wraps the image generation method to add telemetry.
579
618
  """
@@ -694,7 +733,7 @@ def async_image_variatons(gen_ai_endpoint, version, environment, application_nam
694
733
  tracer, pricing_info, trace_content, metrics, disable_metrics):
695
734
  """
696
735
  Generates a telemetry wrapper for creating image variations to collect metrics.
697
-
736
+
698
737
  Args:
699
738
  gen_ai_endpoint: Endpoint identifier for logging and tracing.
700
739
  version: Version of the monitoring package.
@@ -703,7 +742,7 @@ def async_image_variatons(gen_ai_endpoint, version, environment, application_nam
703
742
  tracer: OpenTelemetry tracer for creating spans.
704
743
  pricing_info: Information used for calculating the cost of generating image variations.
705
744
  trace_content: Flag indicating whether to trace the input image and generated variations.
706
-
745
+
707
746
  Returns:
708
747
  A function that wraps the image variations creation method to add telemetry.
709
748
  """
@@ -813,7 +852,7 @@ def async_audio_create(gen_ai_endpoint, version, environment, application_name,
813
852
  tracer, pricing_info, trace_content, metrics, disable_metrics):
814
853
  """
815
854
  Generates a telemetry wrapper for creating speech audio to collect metrics.
816
-
855
+
817
856
  Args:
818
857
  gen_ai_endpoint: Endpoint identifier for logging and tracing.
819
858
  version: Version of the monitoring package.
@@ -822,7 +861,7 @@ def async_audio_create(gen_ai_endpoint, version, environment, application_name,
822
861
  tracer: OpenTelemetry tracer for creating spans.
823
862
  pricing_info: Information used for calculating the cost of generating speech audio.
824
863
  trace_content: Flag indicating whether to trace the input text and generated audio.
825
-
864
+
826
865
  Returns:
827
866
  A function that wraps the speech audio creation method to add telemetry.
828
867
  """
@@ -6,6 +6,7 @@ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
6
6
  from wrapt import wrap_function_wrapper
7
7
 
8
8
  from openlit.instrumentation.qdrant.qdrant import general_wrap
9
+ from openlit.instrumentation.qdrant.async_qdrant import async_general_wrap
9
10
 
10
11
  _instruments = ("qdrant-client >= 1.9.0",)
11
12
 
@@ -118,7 +119,146 @@ WRAPPED_METHODS = [
118
119
  "object": "QdrantClient.recommend",
119
120
  "endpoint": "qdrant.recommend",
120
121
  "wrapper": general_wrap,
121
- }
122
+ },
123
+ {
124
+
125
+ "package": "qdrant_client",
126
+ "object": "QdrantClient.create_payload_index",
127
+ "endpoint": "qdrant.create_payload_index",
128
+ "wrapper": general_wrap,
129
+ },
130
+ {
131
+
132
+ "package": "qdrant_client",
133
+ "object": "QdrantClient.query_points",
134
+ "endpoint": "qdrant.query_points",
135
+ "wrapper": general_wrap,
136
+ },
137
+
138
+ # Async Client
139
+ {
140
+ "package": "qdrant_client",
141
+ "object": "AsyncQdrantClient.create_collection",
142
+ "endpoint": "qdrant.create_collection",
143
+ "wrapper": async_general_wrap,
144
+ },
145
+ {
146
+ "package": "qdrant_client",
147
+ "object": "AsyncQdrantClient.delete_collection",
148
+ "endpoint": "qdrant.delete_collection",
149
+ "wrapper": async_general_wrap,
150
+ },
151
+ {
152
+ "package": "qdrant_client",
153
+ "object": "AsyncQdrantClient.update_collection",
154
+ "endpoint": "qdrant.update_collection",
155
+ "wrapper": async_general_wrap,
156
+ },
157
+ {
158
+ "package": "qdrant_client",
159
+ "object": "AsyncQdrantClient.upload_collection",
160
+ "endpoint": "qdrant.upload_collection",
161
+ "wrapper": async_general_wrap,
162
+ },
163
+ {
164
+ "package": "qdrant_client",
165
+ "object": "AsyncQdrantClient.upsert",
166
+ "endpoint": "qdrant.upsert",
167
+ "wrapper": async_general_wrap,
168
+ },
169
+ {
170
+ "package": "qdrant_client",
171
+ "object": "AsyncQdrantClient.set_payload",
172
+ "endpoint": "qdrant.set_payload",
173
+ "wrapper": async_general_wrap,
174
+ },
175
+ {
176
+ "package": "qdrant_client",
177
+ "object": "AsyncQdrantClient.overwrite_payload",
178
+ "endpoint": "qdrant.overwrite_payload",
179
+ "wrapper": async_general_wrap,
180
+ },
181
+ {
182
+ "package": "qdrant_client",
183
+ "object": "AsyncQdrantClient.clear_payload",
184
+ "endpoint": "qdrant.clear_payload",
185
+ "wrapper": async_general_wrap,
186
+ },
187
+ {
188
+ "package": "qdrant_client",
189
+ "object": "AsyncQdrantClient.delete_payload",
190
+ "endpoint": "qdrant.delete_payload",
191
+ "wrapper": async_general_wrap,
192
+ },
193
+ {
194
+ "package": "qdrant_client",
195
+ "object": "AsyncQdrantClient.upload_points",
196
+ "endpoint": "qdrant.upload_points",
197
+ "wrapper": async_general_wrap,
198
+ },
199
+ {
200
+ "package": "qdrant_client",
201
+ "object": "AsyncQdrantClient.update_vectors",
202
+ "endpoint": "qdrant.update_vectors",
203
+ "wrapper": async_general_wrap,
204
+ },
205
+ {
206
+ "package": "qdrant_client",
207
+ "object": "AsyncQdrantClient.delete_vectors",
208
+ "endpoint": "qdrant.delete_vectors",
209
+ "wrapper": async_general_wrap,
210
+ },
211
+ {
212
+ "package": "qdrant_client",
213
+ "object": "AsyncQdrantClient.delete",
214
+ "endpoint": "qdrant.delete",
215
+ "wrapper": async_general_wrap,
216
+ },
217
+ {
218
+ "package": "qdrant_client",
219
+ "object": "AsyncQdrantClient.retrieve",
220
+ "endpoint": "qdrant.retrieve",
221
+ "wrapper": async_general_wrap,
222
+ },
223
+ {
224
+ "package": "qdrant_client",
225
+ "object": "AsyncQdrantClient.scroll",
226
+ "endpoint": "qdrant.scroll",
227
+ "wrapper": async_general_wrap,
228
+ },
229
+ {
230
+ "package": "qdrant_client",
231
+ "object": "AsyncQdrantClient.search",
232
+ "endpoint": "qdrant.search",
233
+ "wrapper": async_general_wrap,
234
+ },
235
+ {
236
+ "package": "qdrant_client",
237
+ "object": "AsyncQdrantClient.search_groups",
238
+ "endpoint": "qdrant.search_groups",
239
+ "wrapper": async_general_wrap,
240
+ },
241
+ {
242
+
243
+ "package": "qdrant_client",
244
+ "object": "AsyncQdrantClient.recommend",
245
+ "endpoint": "qdrant.recommend",
246
+ "wrapper": async_general_wrap,
247
+ },
248
+ {
249
+
250
+ "package": "qdrant_client",
251
+ "object": "AsyncQdrantClient.create_payload_index",
252
+ "endpoint": "qdrant.create_payload_index",
253
+ "wrapper": async_general_wrap,
254
+ },
255
+ {
256
+
257
+ "package": "qdrant_client",
258
+ "object": "AsyncQdrantClient.query_points",
259
+ "endpoint": "qdrant.query_points",
260
+ "wrapper": async_general_wrap,
261
+ },
122
262
  ]
123
263
 
124
264
  class QdrantInstrumentor(BaseInstrumentor):
@@ -0,0 +1,267 @@
1
+ # pylint: disable=duplicate-code, broad-exception-caught, too-many-statements, unused-argument, possibly-used-before-assignment, too-many-branches
2
+ """
3
+ Module for monitoring Qdrant.
4
+ """
5
+
6
+ import logging
7
+ from opentelemetry.trace import SpanKind, Status, StatusCode
8
+ from opentelemetry.sdk.resources import TELEMETRY_SDK_NAME
9
+ from openlit.__helpers import handle_exception
10
+ from openlit.semcov import SemanticConvetion
11
+
12
+ # Initialize logger for logging potential issues and operations
13
+ logger = logging.getLogger(__name__)
14
+
15
+ def object_count(obj):
16
+ """
17
+ Counts Length of object if it exists, Else returns None
18
+ """
19
+ try:
20
+ cnt = len(obj)
21
+ # pylint: disable=bare-except
22
+ except:
23
+ cnt = 0
24
+
25
+ return cnt
26
+
27
+ def async_general_wrap(gen_ai_endpoint, version, environment, application_name,
28
+ tracer, pricing_info, trace_content, metrics, disable_metrics):
29
+ """
30
+ Creates a wrapper around a function call to trace and log its execution metrics.
31
+
32
+ This function wraps any given function to measure its execution time,
33
+ log its operation, and trace its execution using OpenTelemetry.
34
+
35
+ Parameters:
36
+ - gen_ai_endpoint (str): A descriptor or name for the endpoint being traced.
37
+ - version (str): The version of the Langchain application.
38
+ - environment (str): The deployment environment (e.g., 'production', 'development').
39
+ - application_name (str): Name of the Langchain application.
40
+ - tracer (opentelemetry.trace.Tracer): The tracer object used for OpenTelemetry tracing.
41
+ - pricing_info (dict): Information about the pricing for internal metrics (currently not used).
42
+ - trace_content (bool): Flag indicating whether to trace the content of the response.
43
+
44
+ Returns:
45
+ - function: A higher-order function that takes a function 'wrapped' and returns
46
+ a new function that wraps 'wrapped' with additional tracing and logging.
47
+ """
48
+
49
+ async def wrapper(wrapped, instance, args, kwargs):
50
+ """
51
+ An inner wrapper function that executes the wrapped function, measures execution
52
+ time, and records trace data using OpenTelemetry.
53
+
54
+ Parameters:
55
+ - wrapped (Callable): The original function that this wrapper will execute.
56
+ - instance (object): The instance to which the wrapped function belongs. This
57
+ is used for instance methods. For static and classmethods,
58
+ this may be None.
59
+ - args (tuple): Positional arguments passed to the wrapped function.
60
+ - kwargs (dict): Keyword arguments passed to the wrapped function.
61
+
62
+ Returns:
63
+ - The result of the wrapped function call.
64
+
65
+ The wrapper initiates a span with the provided tracer, sets various attributes
66
+ on the span based on the function's execution and response, and ensures
67
+ errors are handled and logged appropriately.
68
+ """
69
+ with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
70
+ response = await wrapped(*args, **kwargs)
71
+
72
+ try:
73
+ span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
74
+ span.set_attribute(SemanticConvetion.GEN_AI_ENDPOINT,
75
+ gen_ai_endpoint)
76
+ span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
77
+ environment)
78
+ span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
79
+ application_name)
80
+ span.set_attribute(SemanticConvetion.GEN_AI_TYPE,
81
+ SemanticConvetion.GEN_AI_TYPE_VECTORDB)
82
+ span.set_attribute(SemanticConvetion.DB_SYSTEM,
83
+ SemanticConvetion.DB_SYSTEM_QDRANT)
84
+
85
+ if gen_ai_endpoint == "qdrant.create_collection":
86
+ db_operation = SemanticConvetion.DB_OPERATION_CREATE_COLLECTION
87
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
88
+ SemanticConvetion.DB_OPERATION_CREATE_COLLECTION)
89
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
90
+ kwargs.get("collection_name", ""))
91
+
92
+ elif gen_ai_endpoint == "qdrant.upload_collection":
93
+ db_operation = SemanticConvetion.DB_OPERATION_CREATE_COLLECTION
94
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
95
+ SemanticConvetion.DB_OPERATION_CREATE_COLLECTION)
96
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
97
+ kwargs.get("collection_name", ""))
98
+
99
+ elif gen_ai_endpoint == "qdrant.delete_collection":
100
+ db_operation = SemanticConvetion.DB_OPERATION_DELETE_COLLECTION
101
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
102
+ SemanticConvetion.DB_OPERATION_DELETE_COLLECTION)
103
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
104
+ kwargs.get("collection_name", ""))
105
+
106
+ elif gen_ai_endpoint == "qdrant.update_collection":
107
+ db_operation = SemanticConvetion.DB_OPERATION_UPDATE_COLLECTION
108
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
109
+ SemanticConvetion.DB_OPERATION_UPDATE_COLLECTION)
110
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
111
+ kwargs.get("collection_name", ""))
112
+
113
+ elif gen_ai_endpoint == "qdrant.set_payload":
114
+ db_operation = SemanticConvetion.DB_OPERATION_ADD
115
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
116
+ SemanticConvetion.DB_OPERATION_ADD)
117
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
118
+ kwargs.get("collection_name", ""))
119
+ span.set_attribute(SemanticConvetion.DB_OPERATION_STATUS,
120
+ response.status)
121
+ span.set_attribute(SemanticConvetion.DB_VECTOR_COUNT,
122
+ object_count(kwargs.get("points", [])))
123
+ span.set_attribute(SemanticConvetion.DB_PAYLOAD_COUNT,
124
+ object_count(kwargs.get("payload", [])))
125
+
126
+ elif gen_ai_endpoint == "qdrant.retrieve":
127
+ db_operation = SemanticConvetion.DB_OPERATION_QUERY
128
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
129
+ SemanticConvetion.DB_OPERATION_QUERY)
130
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
131
+ kwargs.get("collection_name", ""))
132
+ span.set_attribute(SemanticConvetion.DB_STATEMENT,
133
+ str(kwargs.get("ids", "")))
134
+
135
+ elif gen_ai_endpoint == "qdrant.scroll":
136
+ db_operation = SemanticConvetion.DB_OPERATION_QUERY
137
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
138
+ SemanticConvetion.DB_OPERATION_QUERY)
139
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
140
+ kwargs.get("collection_name", ""))
141
+ span.set_attribute(SemanticConvetion.DB_STATEMENT,
142
+ str(kwargs.get("scroll_filter", "")))
143
+
144
+ elif gen_ai_endpoint in ["qdrant.search", "qdrant.search_groups"]:
145
+ db_operation = SemanticConvetion.DB_OPERATION_QUERY
146
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
147
+ SemanticConvetion.DB_OPERATION_QUERY)
148
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
149
+ kwargs.get("collection_name", ""))
150
+ span.set_attribute(SemanticConvetion.DB_STATEMENT,
151
+ str(kwargs.get("query_vector", "")))
152
+
153
+ elif gen_ai_endpoint == "qdrant.recommend":
154
+ db_operation = SemanticConvetion.DB_OPERATION_QUERY
155
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
156
+ SemanticConvetion.DB_OPERATION_QUERY)
157
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
158
+ kwargs.get("collection_name", ""))
159
+ span.set_attribute(SemanticConvetion.DB_STATEMENT,
160
+ "positive:" + str(kwargs.get("positive", "")) +
161
+ " negative:" + str(kwargs.get("negative", "")))
162
+
163
+ elif gen_ai_endpoint == "qdrant.upload_points":
164
+ db_operation = SemanticConvetion.DB_OPERATION_ADD
165
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
166
+ SemanticConvetion.DB_OPERATION_ADD)
167
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
168
+ kwargs.get("collection_name", ""))
169
+ span.set_attribute(SemanticConvetion.DB_VECTOR_COUNT,
170
+ object_count(kwargs.get("points")))
171
+
172
+ elif gen_ai_endpoint == "qdrant.update_vectors":
173
+ db_operation = SemanticConvetion.DB_OPERATION_UPDATE
174
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
175
+ SemanticConvetion.DB_OPERATION_UPDATE)
176
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
177
+ kwargs.get("collection_name", ""))
178
+ span.set_attribute(SemanticConvetion.DB_OPERATION_STATUS,
179
+ response.status)
180
+ span.set_attribute(SemanticConvetion.DB_VECTOR_COUNT,
181
+ object_count(kwargs.get("points")))
182
+
183
+ elif gen_ai_endpoint == "qdrant.overwrite_payload":
184
+ db_operation = SemanticConvetion.DB_OPERATION_UPDATE
185
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
186
+ SemanticConvetion.DB_OPERATION_UPDATE)
187
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
188
+ kwargs.get("collection_name", ""))
189
+ span.set_attribute(SemanticConvetion.DB_OPERATION_STATUS,
190
+ response.status)
191
+ span.set_attribute(SemanticConvetion.DB_VECTOR_COUNT,
192
+ object_count(kwargs.get("points")))
193
+ span.set_attribute(SemanticConvetion.DB_PAYLOAD_COUNT,
194
+ object_count(kwargs.get("payload")))
195
+
196
+ elif gen_ai_endpoint == "qdrant.upsert":
197
+ db_operation = SemanticConvetion.DB_OPERATION_UPSERT
198
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
199
+ kwargs.get("collection_name", ""))
200
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
201
+ SemanticConvetion.DB_OPERATION_UPSERT)
202
+ span.set_attribute(SemanticConvetion.DB_OPERATION_STATUS,
203
+ response.status)
204
+ span.set_attribute(SemanticConvetion.DB_VECTOR_COUNT,
205
+ object_count(kwargs.get("points")))
206
+
207
+ elif gen_ai_endpoint in ["qdrant.delete_payload", "qdrant.delete_vectors"]:
208
+ db_operation = SemanticConvetion.DB_OPERATION_DELETE
209
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
210
+ SemanticConvetion.DB_OPERATION_DELETE)
211
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
212
+ kwargs.get("collection_name", ""))
213
+ span.set_attribute(SemanticConvetion.DB_OPERATION_STATUS,
214
+ response.status)
215
+ span.set_attribute(SemanticConvetion.DB_VECTOR_COUNT,
216
+ object_count(kwargs.get("points")))
217
+
218
+ elif gen_ai_endpoint in ["qdrant.clear_payload", "qdrant.delete"]:
219
+ db_operation = SemanticConvetion.DB_OPERATION_DELETE
220
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
221
+ SemanticConvetion.DB_OPERATION_DELETE)
222
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
223
+ kwargs.get("collection_name", ""))
224
+ span.set_attribute(SemanticConvetion.DB_OPERATION_STATUS,
225
+ response.status)
226
+ span.set_attribute(SemanticConvetion.DB_VECTOR_COUNT,
227
+ object_count(kwargs.get("points_selector")))
228
+
229
+ elif gen_ai_endpoint == "qdrant.create_payload_index":
230
+ db_operation = SemanticConvetion.DB_OPERATION_CREATE_INDEX
231
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
232
+ SemanticConvetion.DB_OPERATION_CREATE_INDEX)
233
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
234
+ kwargs.get("collection_name", ""))
235
+ span.set_attribute(SemanticConvetion.DB_STATEMENT,
236
+ str(kwargs.get("query", "")))
237
+
238
+ span.set_status(Status(StatusCode.OK))
239
+
240
+ if disable_metrics is False:
241
+ attributes = {
242
+ TELEMETRY_SDK_NAME:
243
+ "openlit",
244
+ SemanticConvetion.GEN_AI_APPLICATION_NAME:
245
+ application_name,
246
+ SemanticConvetion.DB_SYSTEM:
247
+ SemanticConvetion.DB_SYSTEM_QDRANT,
248
+ SemanticConvetion.GEN_AI_ENVIRONMENT:
249
+ environment,
250
+ SemanticConvetion.GEN_AI_TYPE:
251
+ SemanticConvetion.GEN_AI_TYPE_VECTORDB,
252
+ SemanticConvetion.DB_OPERATION:
253
+ db_operation
254
+ }
255
+
256
+ metrics["db_requests"].add(1, attributes)
257
+
258
+ return response
259
+
260
+ except Exception as e:
261
+ handle_exception(span, e)
262
+ logger.error("Error in trace creation: %s", e)
263
+
264
+ # Return original response
265
+ return response
266
+
267
+ return wrapper
@@ -226,6 +226,22 @@ def general_wrap(gen_ai_endpoint, version, environment, application_name,
226
226
  span.set_attribute(SemanticConvetion.DB_VECTOR_COUNT,
227
227
  object_count(kwargs.get("points_selector")))
228
228
 
229
+ elif gen_ai_endpoint == "qdrant.create_payload_index":
230
+ db_operation = SemanticConvetion.DB_OPERATION_CREATE_INDEX
231
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
232
+ SemanticConvetion.DB_OPERATION_CREATE_INDEX)
233
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
234
+ kwargs.get("collection_name", ""))
235
+
236
+ elif gen_ai_endpoint == "qdrant.query_points":
237
+ db_operation = SemanticConvetion.DB_OPERATION_QUERY
238
+ span.set_attribute(SemanticConvetion.DB_OPERATION,
239
+ SemanticConvetion.DB_OPERATION_QUERY)
240
+ span.set_attribute(SemanticConvetion.DB_COLLECTION_NAME,
241
+ kwargs.get("collection_name", ""))
242
+ span.set_attribute(SemanticConvetion.DB_STATEMENT,
243
+ str(kwargs.get("query", "")))
244
+
229
245
  span.set_status(Status(StatusCode.OK))
230
246
 
231
247
  if disable_metrics is False:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: openlit
3
- Version: 1.29.0
3
+ Version: 1.29.2
4
4
  Summary: OpenTelemetry-native Auto instrumentation library for monitoring LLM Applications and GPUs, facilitating the integration of observability into your GenAI-driven projects
5
5
  Home-page: https://github.com/openlit/openlit/tree/main/openlit/python
6
6
  Keywords: OpenTelemetry,otel,otlp,llm,tracing,openai,anthropic,claude,cohere,llm monitoring,observability,monitoring,gpt,Generative AI,chatGPT,gpu
@@ -54,13 +54,14 @@ openlit/instrumentation/ollama/async_ollama.py,sha256=7lbikD-I9k8VL63idqj3VMEfiE
54
54
  openlit/instrumentation/ollama/ollama.py,sha256=lBt1d3rFnF1tFbfdOccwjEafHnmTAUGsiOKSHku6Fkw,31277
55
55
  openlit/instrumentation/openai/__init__.py,sha256=AZ2cPr3TMKkgGdMl_yXMeSi7bWhtmMqOW1iHdzHHGHA,16265
56
56
  openlit/instrumentation/openai/async_azure_openai.py,sha256=XbST1UE_zXzNL6RX2XwCsK_a6IhG9PHVTMKBjGrUcB0,48961
57
- openlit/instrumentation/openai/async_openai.py,sha256=RGNpKLsHYfJXjj1ImuWRJToVSs0wdvMNp2kyTBrBaDw,47578
57
+ openlit/instrumentation/openai/async_openai.py,sha256=WzGsROOC6IAlJ73OL48YYeXib1YzXODzhO8Z608vfhU,48491
58
58
  openlit/instrumentation/openai/azure_openai.py,sha256=dZUc5MtCwg_sZJWiruG6exYGhPAm-339sqs3sKZNRPU,48761
59
59
  openlit/instrumentation/openai/openai.py,sha256=pOeehRgd0GMITGMYUU6PUsjvPDUWmF-L_ER9ry1hL_c,48214
60
60
  openlit/instrumentation/pinecone/__init__.py,sha256=Mv9bElqNs07_JQkYyNnO0wOM3hdbprmw7sttdMeKC7g,2526
61
61
  openlit/instrumentation/pinecone/pinecone.py,sha256=0EhLmtOuvwWVvAKh3e56wyd8wzQq1oaLOmF15SVHxVE,8765
62
- openlit/instrumentation/qdrant/__init__.py,sha256=OJIg17-IGmBEvBYVKjCHcJ0hFXuEL7XV_jzUTqkolN8,4799
63
- openlit/instrumentation/qdrant/qdrant.py,sha256=4uHKYGvWQtRAEVLUWo3o4joJw7hFm2NxVuBu5YKZKiI,14456
62
+ openlit/instrumentation/qdrant/__init__.py,sha256=GMlZgRBKoQMgrL4cFbAKwytfdTHLzJEIuTQMxp0uZO0,8940
63
+ openlit/instrumentation/qdrant/async_qdrant.py,sha256=Xuyw2N75mRIjltrmY8wJes5DHal0Ku3A8VcUqfbsOl0,15071
64
+ openlit/instrumentation/qdrant/qdrant.py,sha256=K0cvEUbNx0hnk8AbEheYPSHcCgjFC482IZyHF9-P_b8,15488
64
65
  openlit/instrumentation/transformers/__init__.py,sha256=4GBtjzcJU4XiPexIUYEqF3pNZMeQw4Gm5B-cyumaFjs,1468
65
66
  openlit/instrumentation/transformers/transformers.py,sha256=KNAT2ROjziW6OAP6Y0Ec4oS2T2jx9y2mzpBgR_e78bI,7625
66
67
  openlit/instrumentation/vertexai/__init__.py,sha256=N3E9HtzefD-zC0fvmfGYiDmSqssoavp_i59wfuYLyMw,6079
@@ -71,7 +72,7 @@ openlit/instrumentation/vllm/vllm.py,sha256=lDzM7F5pgxvh8nKL0dcKB4TD0Mc9wXOWeXOs
71
72
  openlit/otel/metrics.py,sha256=FYAk4eBAmNtFKUIp4hbRbpdq4LME6MapyCQOIeuhmEg,4337
72
73
  openlit/otel/tracing.py,sha256=2kSj7n7uXSkRegcGFDC8IbnDOxqWTA8dGODs__Yn_yA,3719
73
74
  openlit/semcov/__init__.py,sha256=xPsw1aPonDSGYVuga-ZdoGt4yyA16wNFi5AEc7_xIrQ,8114
74
- openlit-1.29.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
75
- openlit-1.29.0.dist-info/METADATA,sha256=8uukJaCeePRMe0rFpdEQWACsNdD0SakudmVc6wPxkU0,20806
76
- openlit-1.29.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
77
- openlit-1.29.0.dist-info/RECORD,,
75
+ openlit-1.29.2.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
76
+ openlit-1.29.2.dist-info/METADATA,sha256=PYgpgLooRPP7jvQuo7MO5loiRNYmdBv84kNd44U40gM,20806
77
+ openlit-1.29.2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
78
+ openlit-1.29.2.dist-info/RECORD,,