openlit 1.34.14__py3-none-any.whl → 1.34.16__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,288 @@
1
+ """
2
+ LiteLLM 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['role']
28
+ content = message['content']
29
+
30
+ if isinstance(content, list):
31
+ content_str = ", ".join(
32
+ f'{item["type"]}: {item["text"] if "text" in item else item["image_url"]}'
33
+ if "type" in item else f'text: {item["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 (len(chunked.get('choices', [])) > 0 and ('delta' in chunked.get('choices')[0] and
59
+ 'content' in chunked.get('choices')[0].get('delta', {}))):
60
+
61
+ content = chunked.get('choices')[0].get('delta').get('content')
62
+ if content:
63
+ scope._llmresponse += content
64
+
65
+ # Handle tool calls in streaming - optimized
66
+ delta_tools = chunked.get('choices', [{}])[0].get('delta', {}).get('tool_calls')
67
+ if delta_tools:
68
+ scope._tools = scope._tools or []
69
+
70
+ for tool in delta_tools:
71
+ idx = tool.get('index', 0)
72
+
73
+ # Extend list if needed
74
+ scope._tools.extend([{}] * (idx + 1 - len(scope._tools)))
75
+
76
+ if tool.get('id'): # New tool (id exists)
77
+ func = tool.get('function', {})
78
+ scope._tools[idx] = {
79
+ 'id': tool['id'],
80
+ 'function': {'name': func.get('name', ''), 'arguments': func.get('arguments', '')},
81
+ 'type': tool.get('type', 'function')
82
+ }
83
+ elif scope._tools[idx] and 'function' in tool: # Append args (id is None)
84
+ scope._tools[idx]['function']['arguments'] += tool['function'].get('arguments', '')
85
+
86
+ if chunked.get('usage'):
87
+ scope._input_tokens = chunked.get('usage').get('prompt_tokens', 0)
88
+ scope._output_tokens = chunked.get('usage').get('completion_tokens', 0)
89
+ scope._response_id = chunked.get('id')
90
+ scope._response_model = chunked.get('model')
91
+ scope._finish_reason = chunked.get('choices', [{}])[0].get('finish_reason')
92
+ scope._response_service_tier = str(chunked.get('system_fingerprint', ''))
93
+ scope._end_time = time.time()
94
+
95
+ def common_chat_logic(scope, pricing_info, environment, application_name, metrics,
96
+ capture_message_content, disable_metrics, version, is_stream):
97
+ """
98
+ Process chat request and generate Telemetry
99
+ """
100
+
101
+ if len(scope._timestamps) > 1:
102
+ scope._tbt = calculate_tbt(scope._timestamps)
103
+
104
+ prompt = format_content(scope._kwargs.get('messages', []))
105
+ request_model = scope._kwargs.get('model', 'openai/gpt-4o')
106
+
107
+ cost = get_chat_model_cost(request_model, pricing_info, scope._input_tokens, scope._output_tokens)
108
+
109
+ # Common Span Attributes
110
+ common_span_attributes(scope,
111
+ SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT, SemanticConvention.GEN_AI_SYSTEM_LITELLM,
112
+ scope._server_address, scope._server_port, request_model, scope._response_model,
113
+ environment, application_name, is_stream, scope._tbt, scope._ttft, version)
114
+
115
+ # Span Attributes for Request parameters
116
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_SEED, scope._kwargs.get('seed', ''))
117
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_FREQUENCY_PENALTY, scope._kwargs.get('frequency_penalty', 0.0))
118
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_MAX_TOKENS, scope._kwargs.get('max_tokens', -1))
119
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_PRESENCE_PENALTY, scope._kwargs.get('presence_penalty', 0.0))
120
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_STOP_SEQUENCES, scope._kwargs.get('stop', []))
121
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_TEMPERATURE, scope._kwargs.get('temperature', 1.0))
122
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_TOP_P, scope._kwargs.get('top_p', 1.0))
123
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_USER, scope._kwargs.get('user', ''))
124
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_SERVICE_TIER, scope._kwargs.get('service_tier', 'auto'))
125
+
126
+ # Span Attributes for Response parameters
127
+ scope._span.set_attribute(SemanticConvention.GEN_AI_RESPONSE_ID, scope._response_id)
128
+ scope._span.set_attribute(SemanticConvention.GEN_AI_RESPONSE_FINISH_REASON, [scope._finish_reason])
129
+ scope._span.set_attribute(SemanticConvention.GEN_AI_RESPONSE_SERVICE_TIER, scope._response_service_tier)
130
+ scope._span.set_attribute(SemanticConvention.GEN_AI_RESPONSE_SYSTEM_FINGERPRINT, scope._response_service_tier)
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_LITELLM,
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._input_tokens = response_dict.get('usage', {}).get('prompt_tokens', 0)
210
+ scope._output_tokens = response_dict.get('usage', {}).get('completion_tokens', 0)
211
+ scope._response_id = response_dict.get('id')
212
+ scope._response_model = response_dict.get('model')
213
+ scope._finish_reason = str(response_dict.get('choices', [])[0].get('finish_reason', ''))
214
+ scope._response_service_tier = str(response_dict.get('system_fingerprint', ''))
215
+ scope._timestamps = []
216
+ scope._ttft, scope._tbt = scope._end_time - scope._start_time, 0
217
+ scope._server_address, scope._server_port = server_address, server_port
218
+ scope._kwargs = kwargs
219
+
220
+ # Handle tool calls
221
+ if scope._kwargs.get("tools"):
222
+ scope._tools = response_dict.get("choices", [{}])[0].get("message", {}).get("tool_calls")
223
+ else:
224
+ scope._tools = None
225
+
226
+ common_chat_logic(scope, pricing_info, environment, application_name, metrics,
227
+ capture_message_content, disable_metrics, version, is_stream=False)
228
+
229
+ return response
230
+
231
+ def process_embedding_response(response, request_model, pricing_info, server_port, server_address,
232
+ environment, application_name, metrics, start_time, span, capture_message_content=False,
233
+ disable_metrics=False, version="1.0.0", **kwargs):
234
+ """
235
+ Process embedding request and generate Telemetry
236
+ """
237
+
238
+ # Create scope object
239
+ scope = type("GenericScope", (), {})()
240
+ response_dict = response_as_dict(response)
241
+
242
+ scope._start_time = start_time
243
+ scope._end_time = time.time()
244
+ scope._span = span
245
+ scope._input_tokens = response_dict.get('usage', {}).get('prompt_tokens', 0)
246
+ scope._response_model = response_dict.get('model')
247
+ scope._server_address, scope._server_port = server_address, server_port
248
+ scope._kwargs = kwargs
249
+
250
+ # Calculate cost of the operation
251
+ cost = get_embed_model_cost(request_model, pricing_info, scope._input_tokens)
252
+
253
+ # Common Span Attributes
254
+ common_span_attributes(scope,
255
+ SemanticConvention.GEN_AI_OPERATION_TYPE_EMBEDDING, SemanticConvention.GEN_AI_SYSTEM_LITELLM,
256
+ scope._server_address, scope._server_port, request_model, scope._response_model,
257
+ environment, application_name, False, 0, scope._end_time - scope._start_time, version)
258
+
259
+ # Span Attributes for Request parameters
260
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_ENCODING_FORMATS, [scope._kwargs.get('encoding_format', 'float')])
261
+ scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_USER, scope._kwargs.get('user', ''))
262
+
263
+ # Span Attributes for Cost and Tokens
264
+ scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS, scope._input_tokens)
265
+ scope._span.set_attribute(SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE, scope._input_tokens)
266
+ scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_COST, cost)
267
+
268
+ # Span Attributes for Content
269
+ if capture_message_content:
270
+ scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, str(scope._kwargs.get('input', '')))
271
+
272
+ # To be removed once the change to span_attributes (from span events) is complete
273
+ scope._span.add_event(
274
+ name=SemanticConvention.GEN_AI_CONTENT_PROMPT_EVENT,
275
+ attributes={
276
+ SemanticConvention.GEN_AI_CONTENT_PROMPT: str(scope._kwargs.get('input', '')),
277
+ },
278
+ )
279
+
280
+ scope._span.set_status(Status(StatusCode.OK))
281
+
282
+ # Metrics
283
+ if not disable_metrics:
284
+ record_embedding_metrics(metrics, SemanticConvention.GEN_AI_OPERATION_TYPE_EMBEDDING, SemanticConvention.GEN_AI_SYSTEM_LITELLM,
285
+ scope._server_address, scope._server_port, request_model, scope._response_model, environment,
286
+ application_name, scope._start_time, scope._end_time, scope._input_tokens, cost)
287
+
288
+ return response
@@ -166,6 +166,7 @@ class SemanticConvention:
166
166
  GEN_AI_CONTENT_COMPLETION_EVENT = "gen_ai.content.completion"
167
167
  GEN_AI_CONTENT_COMPLETION = "gen_ai.completion"
168
168
  GEN_AI_CONTENT_REVISED_PROMPT = "gen_ai.content.revised_prompt"
169
+ GEN_AI_CONTENT_REASONING = "gen_ai.content.reasoning"
169
170
 
170
171
  # GenAI Rag
171
172
  GEN_AI_RAG_MAX_SEGMENTS = "gen_ai.rag.max_segments"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: openlit
3
- Version: 1.34.14
3
+ Version: 1.34.16
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
@@ -29,10 +29,10 @@ openlit/instrumentation/astra/__init__.py,sha256=-JG3_YHQQaOQUr4XtFzqfaYiQKqviAA
29
29
  openlit/instrumentation/astra/astra.py,sha256=L_Yw980eEY0AzMqhNreKamlSplTlL8XiG5lx9Sj3D0c,1610
30
30
  openlit/instrumentation/astra/async_astra.py,sha256=87QFKnEQPHywuqMH0dOlnXZ2GqdYDZQgT4TfXB16fPI,1628
31
31
  openlit/instrumentation/astra/utils.py,sha256=qBNpugK0R6wQLFx39ZANY1fQvNAIc5PrpEazz-K69Yw,4976
32
- openlit/instrumentation/azure_ai_inference/__init__.py,sha256=ZoMAX_MUNCNMJqLZgl0A_kQ_lsgoz3VddkHiDT3pVF8,2032
33
- openlit/instrumentation/azure_ai_inference/async_azure_ai_inference.py,sha256=bRH9iJ9fE6RWibffWw6UfLHs2IhIUdcY3opyvPnQTHg,5058
34
- openlit/instrumentation/azure_ai_inference/azure_ai_inference.py,sha256=u--3wNMoVRa5pRdMys8osbsCubPESPJw70hdBzAA_tQ,4972
35
- openlit/instrumentation/azure_ai_inference/utils.py,sha256=JbIsmmi1Xw_gXLjPkhDkdTEJ4Bq8jVs3H4lmXE8Sxhs,11036
32
+ openlit/instrumentation/azure_ai_inference/__init__.py,sha256=_GuYy4ypF6_HICpAC8dNQ5-FBjkcNzPTPF4q3fTM10Q,2512
33
+ openlit/instrumentation/azure_ai_inference/async_azure_ai_inference.py,sha256=SFrniRWPqVBxFJVOpC8w1qNGSYZhCFXeKHVHu5pEdZI,5906
34
+ openlit/instrumentation/azure_ai_inference/azure_ai_inference.py,sha256=hRFuuvaXflctNNbk7N2GOfKaC_eCHbrBWf9_1sZcaGY,5808
35
+ openlit/instrumentation/azure_ai_inference/utils.py,sha256=JqKZgb6VppDbAQ2RdH_dQ2oWVnaqGIA1PCmMl1yrMtA,15491
36
36
  openlit/instrumentation/bedrock/__init__.py,sha256=Sfd0vm4Dfm1t-N7vBPRwU57GLTlZP2M4rVYRek_JHXY,1625
37
37
  openlit/instrumentation/bedrock/bedrock.py,sha256=kP9ESKzqhWu-dIWseyaeyendUyo6b7xJwjGo3LGi5Jc,2817
38
38
  openlit/instrumentation/bedrock/utils.py,sha256=_mTUIbioEg4jfoxocUbfc7RgGjhm9ACelbxIoFu4jbM,11636
@@ -80,9 +80,10 @@ openlit/instrumentation/langchain/async_langchain.py,sha256=rdk3INGcsxsfzZcoJo0y
80
80
  openlit/instrumentation/langchain/langchain.py,sha256=zgfzfOIDsaRoVgWl1T4XX2CLO7ttGOD15TagZtYQ-vE,17012
81
81
  openlit/instrumentation/letta/__init__.py,sha256=K8PtRKxuueyqEYE3LzxWJ74IieNKSI6dmk9sNRd8Mt0,3031
82
82
  openlit/instrumentation/letta/letta.py,sha256=SCIpJ4tdB1l1BmeQx4raaTS4MQO5X15pLvS4PepEKBE,8481
83
- openlit/instrumentation/litellm/__init__.py,sha256=qRqfwDMhP5adKGI2vRaelAkN12i0e8jtJrT31VFFM5A,2374
84
- openlit/instrumentation/litellm/async_litellm.py,sha256=DUUOmkyDAxwgET4euyLTjaYxgWGdg_aMyFGbH__qg1A,30502
85
- openlit/instrumentation/litellm/litellm.py,sha256=xQSqC1HZu7IUVnFAyD442rC6DdO6TSOV9hUTpyPSKW4,30408
83
+ openlit/instrumentation/litellm/__init__.py,sha256=D47yfDLLEKpkaRAy7_Yif70kj88AGqLQYZAABpTN4sE,2284
84
+ openlit/instrumentation/litellm/async_litellm.py,sha256=6cL_hv9t4tuXkcKZvpTdnb0wGTs54lSwGWCtdYZvyXg,6768
85
+ openlit/instrumentation/litellm/litellm.py,sha256=xLna3I_jcywTtIs1tBjHAQKyKjNM07T8GHX9pIqZcQ0,6664
86
+ openlit/instrumentation/litellm/utils.py,sha256=VMSnYkKn9yZtOphIh2ENNuqJtGjz1fXEYUKi5JGHC7A,13195
86
87
  openlit/instrumentation/llamaindex/__init__.py,sha256=2pmd9BKw3ab0OJ4yuJEg0-Jkn_haDbXvbUm5r2-rOCU,2007
87
88
  openlit/instrumentation/llamaindex/llamaindex.py,sha256=mdT2TvEWD0D9cEkFjXMeTculNoMWkuJ4mj7QWFnvcqY,4085
88
89
  openlit/instrumentation/mem0/__init__.py,sha256=IadP3bKgz2HCbnrh9S7AW24uDauGkzsIWeOQaGkOCc4,2447
@@ -137,8 +138,8 @@ openlit/instrumentation/vllm/vllm.py,sha256=VzazF2f4LLwjZDO_G8lIN_d622oSJM0fIO9w
137
138
  openlit/otel/events.py,sha256=VrMjTpvnLtYRBHCiFwJojTQqqNpRCxoD4yJYeQrtPsk,3560
138
139
  openlit/otel/metrics.py,sha256=GM2PDloBGRhBTkHHkYaqmOwIAQkY124ZhW4sEqW1Fgk,7086
139
140
  openlit/otel/tracing.py,sha256=tjV2bEbEDPUB1Z46gE-UsJsb04sRdFrfbhIDkxViZc0,3103
140
- openlit/semcov/__init__.py,sha256=ptyo37PY-FHDx_PShEvbdns71cD4YvvXw15bCRXKCKM,13461
141
- openlit-1.34.14.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
142
- openlit-1.34.14.dist-info/METADATA,sha256=qaOh__y9R5tT0z7qveai1LH4KWY7ampN3PzVhhm7D0M,23470
143
- openlit-1.34.14.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
144
- openlit-1.34.14.dist-info/RECORD,,
141
+ 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,,