openlit 1.34.16__py3-none-any.whl → 1.34.18__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.
@@ -0,0 +1,298 @@
1
+ """
2
+ Mistral OpenTelemetry instrumentation utility functions
3
+ """
4
+ import time
5
+
6
+ from opentelemetry.trace import Status, StatusCode
7
+
8
+ from openlit.__helpers import (
9
+ calculate_ttft,
10
+ response_as_dict,
11
+ calculate_tbt,
12
+ get_chat_model_cost,
13
+ get_embed_model_cost,
14
+ common_span_attributes,
15
+ record_completion_metrics,
16
+ record_embedding_metrics,
17
+ )
18
+ from openlit.semcov import SemanticConvention
19
+
20
+ def format_content(messages):
21
+ """
22
+ Process a list of messages to extract content.
23
+ """
24
+
25
+ formatted_messages = []
26
+ for message in messages:
27
+ role = message.get("role", "user")
28
+ content = message.get("content", "")
29
+
30
+ if isinstance(content, list):
31
+ content_str = ", ".join(
32
+ f'{item["type"]}: {item["text"] if "text" in item else item.get("image_url", "")}'
33
+ if "type" in item else f'text: {item.get("text", "")}'
34
+ for item in content
35
+ )
36
+ formatted_messages.append(f"{role}: {content_str}")
37
+ else:
38
+ formatted_messages.append(f"{role}: {content}")
39
+
40
+ return "\n".join(formatted_messages)
41
+
42
+ def process_chunk(scope, chunk):
43
+ """
44
+ Process a chunk of response data and update state.
45
+ """
46
+
47
+ end_time = time.time()
48
+ # Record the timestamp for the current chunk
49
+ scope._timestamps.append(end_time)
50
+
51
+ if len(scope._timestamps) == 1:
52
+ # Calculate time to first chunk
53
+ scope._ttft = calculate_ttft(scope._timestamps, scope._start_time)
54
+
55
+ chunked = response_as_dict(chunk)
56
+
57
+ # Collect message IDs and aggregated response from events
58
+ if chunked.get("data"):
59
+ data = chunked.get("data")
60
+ choices = data.get("choices", [])
61
+
62
+ if choices and "delta" in choices[0]:
63
+ delta = choices[0]["delta"]
64
+ content = delta.get("content")
65
+ if content:
66
+ scope._llmresponse += content
67
+
68
+ # Handle tool calls in streaming - optimized
69
+ delta_tools = delta.get("tool_calls")
70
+ if delta_tools:
71
+ scope._tools = scope._tools or []
72
+
73
+ for tool in delta_tools:
74
+ idx = tool.get("index", 0)
75
+
76
+ # Extend list if needed
77
+ scope._tools.extend([{}] * (idx + 1 - len(scope._tools)))
78
+
79
+ if tool.get("id"): # New tool (id exists)
80
+ func = tool.get("function", {})
81
+ scope._tools[idx] = {
82
+ "id": tool["id"],
83
+ "function": {"name": func.get("name", ""), "arguments": func.get("arguments", "")},
84
+ "type": tool.get("type", "function")
85
+ }
86
+ elif scope._tools[idx] and "function" in tool: # Append args (id is None)
87
+ scope._tools[idx]["function"]["arguments"] += tool["function"].get("arguments", "")
88
+
89
+ # Handle usage information (typically only in final chunk)
90
+ if data.get("usage"):
91
+ scope._input_tokens = data.get("usage").get("prompt_tokens", 0)
92
+ scope._output_tokens = data.get("usage").get("completion_tokens", 0)
93
+ scope._response_id = data.get("id")
94
+ scope._response_model = data.get("model")
95
+ scope._finish_reason = choices[0].get("finish_reason", "") if choices else ""
96
+ scope._end_time = time.time()
97
+
98
+ def common_chat_logic(scope, pricing_info, environment, application_name, metrics,
99
+ capture_message_content, disable_metrics, version, is_stream):
100
+ """
101
+ Process chat request and generate Telemetry
102
+ """
103
+
104
+ if len(scope._timestamps) > 1:
105
+ scope._tbt = calculate_tbt(scope._timestamps)
106
+
107
+ prompt = format_content(scope._kwargs.get("messages", []))
108
+ request_model = scope._kwargs.get("model", "mistral-small-latest")
109
+
110
+ cost = get_chat_model_cost(request_model, pricing_info, scope._input_tokens, scope._output_tokens)
111
+
112
+ # Common Span Attributes
113
+ common_span_attributes(scope,
114
+ SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT, SemanticConvention.GEN_AI_SYSTEM_MISTRAL,
115
+ scope._server_address, scope._server_port, request_model, scope._response_model,
116
+ environment, application_name, is_stream, scope._tbt, scope._ttft, version)
117
+
118
+ # Span Attributes for Request parameters
119
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_SEED, scope._kwargs.get("seed", ""))
120
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_FREQUENCY_PENALTY, scope._kwargs.get("frequency_penalty", 0.0))
121
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_MAX_TOKENS, scope._kwargs.get("max_tokens", -1))
122
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_PRESENCE_PENALTY, scope._kwargs.get("presence_penalty", 0.0))
123
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_STOP_SEQUENCES, scope._kwargs.get("stop_sequences", []))
124
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_TEMPERATURE, scope._kwargs.get("temperature", 0.3))
125
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_TOP_K, scope._kwargs.get("k", 1.0))
126
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_TOP_P, scope._kwargs.get("p", 1.0))
127
+
128
+ # Span Attributes for Response parameters
129
+ scope._span.set_attribute(SemanticConvention.GEN_AI_RESPONSE_ID, scope._response_id)
130
+ scope._span.set_attribute(SemanticConvention.GEN_AI_RESPONSE_FINISH_REASON, [scope._finish_reason])
131
+ scope._span.set_attribute(SemanticConvention.GEN_AI_OUTPUT_TYPE, "text" if isinstance(scope._llmresponse, str) else "json")
132
+
133
+ # Span Attributes for Cost and Tokens
134
+ scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS, scope._input_tokens)
135
+ scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_OUTPUT_TOKENS, scope._output_tokens)
136
+ scope._span.set_attribute(SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE, scope._input_tokens + scope._output_tokens)
137
+ scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_COST, cost)
138
+
139
+ # Span Attributes for Tools - optimized
140
+ if scope._tools:
141
+ tools = scope._tools if isinstance(scope._tools, list) else [scope._tools]
142
+
143
+ names, ids, args = zip(*[
144
+ (t.get("function", {}).get("name", ""),
145
+ str(t.get("id", "")),
146
+ str(t.get("function", {}).get("arguments", "")))
147
+ for t in tools if isinstance(t, dict) and t
148
+ ]) if tools else ([], [], [])
149
+
150
+ scope._span.set_attribute(SemanticConvention.GEN_AI_TOOL_NAME, ", ".join(filter(None, names)))
151
+ scope._span.set_attribute(SemanticConvention.GEN_AI_TOOL_CALL_ID, ", ".join(filter(None, ids)))
152
+ scope._span.set_attribute(SemanticConvention.GEN_AI_TOOL_ARGS, ", ".join(filter(None, args)))
153
+
154
+ # Span Attributes for Content
155
+ if capture_message_content:
156
+ scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, prompt)
157
+ scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_COMPLETION, scope._llmresponse)
158
+
159
+ # To be removed once the change to span_attributes (from span events) is complete
160
+ scope._span.add_event(
161
+ name=SemanticConvention.GEN_AI_CONTENT_PROMPT_EVENT,
162
+ attributes={
163
+ SemanticConvention.GEN_AI_CONTENT_PROMPT: prompt,
164
+ },
165
+ )
166
+ scope._span.add_event(
167
+ name=SemanticConvention.GEN_AI_CONTENT_COMPLETION_EVENT,
168
+ attributes={
169
+ SemanticConvention.GEN_AI_CONTENT_COMPLETION: scope._llmresponse,
170
+ },
171
+ )
172
+
173
+ scope._span.set_status(Status(StatusCode.OK))
174
+
175
+ # Metrics
176
+ if not disable_metrics:
177
+ record_completion_metrics(metrics, SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT, SemanticConvention.GEN_AI_SYSTEM_MISTRAL,
178
+ scope._server_address, scope._server_port, request_model, scope._response_model, environment,
179
+ application_name, scope._start_time, scope._end_time, scope._input_tokens, scope._output_tokens,
180
+ cost, scope._tbt, scope._ttft)
181
+
182
+ def process_streaming_chat_response(scope, pricing_info, environment, application_name, metrics,
183
+ capture_message_content=False, disable_metrics=False, version=""):
184
+ """
185
+ Process streaming chat request and generate Telemetry
186
+ """
187
+
188
+ common_chat_logic(scope, pricing_info, environment, application_name, metrics,
189
+ capture_message_content, disable_metrics, version, is_stream=True)
190
+
191
+ def process_chat_response(response, request_model, pricing_info, server_port, server_address,
192
+ environment, application_name, metrics, start_time, span, capture_message_content=False,
193
+ disable_metrics=False, version="1.0.0", **kwargs):
194
+ """
195
+ Process chat request and generate Telemetry
196
+ """
197
+
198
+ # Create scope object
199
+ scope = type("GenericScope", (), {})()
200
+ response_dict = response_as_dict(response)
201
+
202
+ scope._start_time = start_time
203
+ scope._end_time = time.time()
204
+ scope._span = span
205
+ scope._llmresponse = " ".join(
206
+ (choice.get("message", {}).get("content") or "")
207
+ for choice in response_dict.get("choices", [])
208
+ )
209
+ scope._response_id = response_dict.get("id")
210
+ scope._response_model = response_dict.get("model")
211
+ scope._input_tokens = response_dict.get("usage", {}).get("prompt_tokens", 0)
212
+ scope._output_tokens = response_dict.get("usage", {}).get("completion_tokens", 0)
213
+ scope._timestamps = []
214
+ scope._ttft, scope._tbt = scope._end_time - scope._start_time, 0
215
+ scope._server_address, scope._server_port = server_address, server_port
216
+ scope._kwargs = kwargs
217
+ scope._finish_reason = str(response_dict.get("choices", [])[0].get("finish_reason", "")) if response_dict.get("choices") else ""
218
+
219
+ # Handle tool calls
220
+ if kwargs.get("tools"):
221
+ scope._tools = response_dict.get("choices", [{}])[0].get("message", {}).get("tool_calls")
222
+ else:
223
+ scope._tools = None
224
+
225
+ common_chat_logic(scope, pricing_info, environment, application_name, metrics,
226
+ capture_message_content, disable_metrics, version, is_stream=False)
227
+
228
+ return response
229
+
230
+ def common_embedding_logic(scope, pricing_info, environment, application_name, metrics,
231
+ capture_message_content, disable_metrics, version):
232
+ """
233
+ Process embedding request and generate Telemetry
234
+ """
235
+
236
+ request_model = scope._kwargs.get("model", "mistral-embed")
237
+ inputs = scope._kwargs.get("inputs", [])
238
+
239
+ cost = get_embed_model_cost(request_model, pricing_info, scope._input_tokens)
240
+
241
+ # Common Span Attributes
242
+ common_span_attributes(scope,
243
+ SemanticConvention.GEN_AI_OPERATION_TYPE_EMBEDDING, SemanticConvention.GEN_AI_SYSTEM_MISTRAL,
244
+ scope._server_address, scope._server_port, request_model, scope._response_model,
245
+ environment, application_name, False, 0, scope._ttft, version)
246
+
247
+ # Span Attributes for Request parameters
248
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_ENCODING_FORMATS, [scope._kwargs.get("encoding_format", "float")])
249
+
250
+ # Span Attributes for Cost and Tokens
251
+ scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS, scope._input_tokens)
252
+ scope._span.set_attribute(SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE, scope._input_tokens)
253
+ scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_COST, cost)
254
+
255
+ # Span Attributes for Content
256
+ if capture_message_content:
257
+ scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, str(inputs))
258
+
259
+ # To be removed once the change to span_attributes (from span events) is complete
260
+ scope._span.add_event(
261
+ name=SemanticConvention.GEN_AI_CONTENT_PROMPT_EVENT,
262
+ attributes={
263
+ SemanticConvention.GEN_AI_CONTENT_PROMPT: str(inputs),
264
+ },
265
+ )
266
+
267
+ scope._span.set_status(Status(StatusCode.OK))
268
+
269
+ # Metrics
270
+ if not disable_metrics:
271
+ record_embedding_metrics(metrics, SemanticConvention.GEN_AI_OPERATION_TYPE_EMBEDDING, SemanticConvention.GEN_AI_SYSTEM_MISTRAL,
272
+ scope._server_address, scope._server_port, request_model, scope._response_model, environment,
273
+ application_name, scope._start_time, scope._end_time, scope._input_tokens, cost)
274
+
275
+ def process_embedding_response(response, request_model, pricing_info, server_port, server_address,
276
+ environment, application_name, metrics, start_time, span, capture_message_content=False,
277
+ disable_metrics=False, version="1.0.0", **kwargs):
278
+ """
279
+ Process embedding request and generate Telemetry
280
+ """
281
+
282
+ # Create scope object
283
+ scope = type("GenericScope", (), {})()
284
+ response_dict = response_as_dict(response)
285
+
286
+ scope._start_time = start_time
287
+ scope._end_time = time.time()
288
+ scope._span = span
289
+ scope._response_model = response_dict.get("model")
290
+ scope._input_tokens = response_dict.get("usage", {}).get("prompt_tokens", 0)
291
+ scope._ttft = scope._end_time - scope._start_time
292
+ scope._server_address, scope._server_port = server_address, server_port
293
+ scope._kwargs = kwargs
294
+
295
+ common_embedding_logic(scope, pricing_info, environment, application_name, metrics,
296
+ capture_message_content, disable_metrics, version)
297
+
298
+ return response
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: openlit
3
- Version: 1.34.16
3
+ Version: 1.34.18
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
  License: Apache-2.0
6
6
  Keywords: OpenTelemetry,otel,otlp,llm,tracing,openai,anthropic,claude,cohere,llm monitoring,observability,monitoring,gpt,Generative AI,chatGPT,gpu
@@ -38,9 +38,10 @@ openlit/instrumentation/bedrock/bedrock.py,sha256=kP9ESKzqhWu-dIWseyaeyendUyo6b7
38
38
  openlit/instrumentation/bedrock/utils.py,sha256=_mTUIbioEg4jfoxocUbfc7RgGjhm9ACelbxIoFu4jbM,11636
39
39
  openlit/instrumentation/chroma/__init__.py,sha256=4ZeHY1OInRKQbb4qg8BVvGJtWN1XdzW6mosqi7-6ruE,3353
40
40
  openlit/instrumentation/chroma/chroma.py,sha256=fPIX6vgcDFkBgKGxrkXZWDaBtZa5y-PV3jJjKsFqjDg,10640
41
- openlit/instrumentation/cohere/__init__.py,sha256=TIRq1obu-zqBji0HhMbFGfI2q5m-zw0nWbToKeZqpg4,2905
42
- openlit/instrumentation/cohere/async_cohere.py,sha256=cFIrQ-maKmM8JKRitX-mgD7Jm_1UDyO_kOp4X_0MQww,30745
43
- openlit/instrumentation/cohere/cohere.py,sha256=11QGsBTLiTfF-oH_fIQ9IBbIgtS2vf7M-VvbG8MuO_Q,30628
41
+ openlit/instrumentation/cohere/__init__.py,sha256=FIJ_QokUZcN9UsVPWckRfHiLVJ5zj3jtcOjQmjQA5f0,2978
42
+ openlit/instrumentation/cohere/async_cohere.py,sha256=wgctJa-BcKEkVguLorR7mYRShv7GiioF1_zxevvzXTw,6990
43
+ openlit/instrumentation/cohere/cohere.py,sha256=7YAj6kwJ1MDLFY3WtbPRehZFIseDVIbvPQJZxG8Qc6A,6814
44
+ openlit/instrumentation/cohere/utils.py,sha256=5l3Av-t0GzawPd0j5L9jcpdJDGOvLq2YCMpo3usS4OY,14533
44
45
  openlit/instrumentation/controlflow/__init__.py,sha256=Y5xSj6bwkGufG989YtRGujzj8qrV4T5kXr4hzGb2nGk,2168
45
46
  openlit/instrumentation/controlflow/controlflow.py,sha256=NNovai9DwRCSaa-shFxAyUIYiRaEBgXkyuXFk3lQlPU,5573
46
47
  openlit/instrumentation/crawl4ai/__init__.py,sha256=TTFFeZkTgPZJAD1Tlr2LK-CTH2A7NkZK-5SHKFfFOtU,1947
@@ -90,9 +91,10 @@ openlit/instrumentation/mem0/__init__.py,sha256=IadP3bKgz2HCbnrh9S7AW24uDauGkzsI
90
91
  openlit/instrumentation/mem0/mem0.py,sha256=Y3Y_OWH9fW4vrus5vwPoWyy7sWXB9IR3k5tt6idMJ9E,5348
91
92
  openlit/instrumentation/milvus/__init__.py,sha256=qQqI-mOXEIsXGYwLyUGLEECcIHCm_6_KQZq0vFL90LY,2991
92
93
  openlit/instrumentation/milvus/milvus.py,sha256=G_wUAvwKmm7Jghd-X8xdwZphRjnJSKctU1424pQmJ3U,9220
93
- openlit/instrumentation/mistral/__init__.py,sha256=5hh5s2-kudMbfkWD6Lhe5IQvrlZ5qA-Sv3laeXwyaPA,3060
94
- openlit/instrumentation/mistral/async_mistral.py,sha256=XgT7ZQ2Dx_UscoljjgKvWb6Fe5FuDs7Yrwau96KpSKQ,31187
95
- openlit/instrumentation/mistral/mistral.py,sha256=_2qM8v4RCL-S0Mm1vbW77m5vUm8aPDyiijbP75yHZaY,31083
94
+ openlit/instrumentation/mistral/__init__.py,sha256=D4CLrx9KSSxAPA1m00743Og0Tl8BS47nsgp4qG5-qh8,2977
95
+ openlit/instrumentation/mistral/async_mistral.py,sha256=LWq8tYahbA7NOPDdk6DWJAKuxR4GHOC49w6L1QuNtBw,6946
96
+ openlit/instrumentation/mistral/mistral.py,sha256=lyXyPZuxVACcZoOz85G4FFH4KP77uGf3aOiFDdHLQFI,6771
97
+ openlit/instrumentation/mistral/utils.py,sha256=B_sdUXXYPq6w3-qPX6lei0eKU6OxcgXOKG40TOZWDUQ,13200
96
98
  openlit/instrumentation/multion/__init__.py,sha256=Wr3lcDyG_YbOLkCUzBFhraAedF6E113tce8eSWlcz10,3149
97
99
  openlit/instrumentation/multion/async_multion.py,sha256=XutZnayCJOZ_NA9bvE1NUoej41KOGR7FRn2tpoGKMEU,6092
98
100
  openlit/instrumentation/multion/multion.py,sha256=-WqRAcu5qiEMY9XDmlJTQHuQiWfdwms9JDn127QCNb8,6074
@@ -139,7 +141,7 @@ openlit/otel/events.py,sha256=VrMjTpvnLtYRBHCiFwJojTQqqNpRCxoD4yJYeQrtPsk,3560
139
141
  openlit/otel/metrics.py,sha256=GM2PDloBGRhBTkHHkYaqmOwIAQkY124ZhW4sEqW1Fgk,7086
140
142
  openlit/otel/tracing.py,sha256=tjV2bEbEDPUB1Z46gE-UsJsb04sRdFrfbhIDkxViZc0,3103
141
143
  openlit/semcov/__init__.py,sha256=8oIh2VC667NDh8FA3M-ESusHmeus1sgDUD8binx_nAc,13519
142
- openlit-1.34.16.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
143
- openlit-1.34.16.dist-info/METADATA,sha256=mdKEEE4FgRuUOe_Pl1Crh0S89-A8wCrBchSFJ7cqBRI,23470
144
- openlit-1.34.16.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
145
- openlit-1.34.16.dist-info/RECORD,,
144
+ openlit-1.34.18.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
145
+ openlit-1.34.18.dist-info/METADATA,sha256=H34xsI30squSeUxI--7xhbuKEcOc_fEl7ru4HsnOJZU,23470
146
+ openlit-1.34.18.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
147
+ openlit-1.34.18.dist-info/RECORD,,