openlit 1.34.13__py3-none-any.whl → 1.34.15__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.
- openlit/instrumentation/litellm/__init__.py +7 -6
- openlit/instrumentation/litellm/async_litellm.py +89 -493
- openlit/instrumentation/litellm/litellm.py +87 -491
- openlit/instrumentation/litellm/utils.py +288 -0
- openlit/instrumentation/transformers/__init__.py +12 -5
- openlit/instrumentation/transformers/transformers.py +21 -28
- openlit/instrumentation/transformers/utils.py +126 -110
- {openlit-1.34.13.dist-info → openlit-1.34.15.dist-info}/METADATA +1 -1
- {openlit-1.34.13.dist-info → openlit-1.34.15.dist-info}/RECORD +11 -10
- {openlit-1.34.13.dist-info → openlit-1.34.15.dist-info}/LICENSE +0 -0
- {openlit-1.34.13.dist-info → openlit-1.34.15.dist-info}/WHEEL +0 -0
@@ -3,19 +3,61 @@ HF Transformers OpenTelemetry instrumentation utility functions
|
|
3
3
|
"""
|
4
4
|
import time
|
5
5
|
|
6
|
-
from opentelemetry.sdk.resources import SERVICE_NAME, TELEMETRY_SDK_NAME, DEPLOYMENT_ENVIRONMENT
|
7
6
|
from opentelemetry.trace import Status, StatusCode
|
8
7
|
|
9
8
|
from openlit.__helpers import (
|
10
|
-
response_as_dict,
|
11
|
-
calculate_tbt,
|
12
9
|
general_tokens,
|
13
10
|
get_chat_model_cost,
|
14
|
-
|
15
|
-
|
11
|
+
common_span_attributes,
|
12
|
+
record_completion_metrics,
|
16
13
|
)
|
17
14
|
from openlit.semcov import SemanticConvention
|
18
15
|
|
16
|
+
def format_content(content):
|
17
|
+
"""
|
18
|
+
Format content to a consistent structure.
|
19
|
+
"""
|
20
|
+
if isinstance(content, str):
|
21
|
+
return content
|
22
|
+
elif isinstance(content, list):
|
23
|
+
# Check if its a list of chat messages (like in the test case)
|
24
|
+
if (len(content) > 0 and isinstance(content[0], dict) and
|
25
|
+
"role" in content[0] and "content" in content[0]):
|
26
|
+
# Handle chat message format like Groq
|
27
|
+
formatted_messages = []
|
28
|
+
for message in content:
|
29
|
+
role = message["role"]
|
30
|
+
msg_content = message["content"]
|
31
|
+
|
32
|
+
if isinstance(msg_content, list):
|
33
|
+
content_str = ", ".join(
|
34
|
+
f'{item["type"]}: {item["text"] if "text" in item else item.get("image_url", str(item))}'
|
35
|
+
if isinstance(item, dict) and "type" in item
|
36
|
+
else str(item)
|
37
|
+
for item in msg_content
|
38
|
+
)
|
39
|
+
formatted_messages.append(f"{role}: {content_str}")
|
40
|
+
else:
|
41
|
+
formatted_messages.append(f"{role}: {msg_content}")
|
42
|
+
return "\n".join(formatted_messages)
|
43
|
+
else:
|
44
|
+
# Handle other list formats (transformers responses)
|
45
|
+
formatted_content = []
|
46
|
+
for item in content:
|
47
|
+
if isinstance(item, str):
|
48
|
+
formatted_content.append(item)
|
49
|
+
elif isinstance(item, dict):
|
50
|
+
# Handle dict format for transformers
|
51
|
+
if "generated_text" in item:
|
52
|
+
formatted_content.append(str(item["generated_text"]))
|
53
|
+
else:
|
54
|
+
formatted_content.append(str(item))
|
55
|
+
else:
|
56
|
+
formatted_content.append(str(item))
|
57
|
+
return " ".join(formatted_content)
|
58
|
+
else:
|
59
|
+
return str(content)
|
60
|
+
|
19
61
|
def common_chat_logic(scope, pricing_info, environment, application_name, metrics,
|
20
62
|
capture_message_content, disable_metrics, version, args, kwargs, is_stream):
|
21
63
|
|
@@ -24,56 +66,42 @@ def common_chat_logic(scope, pricing_info, environment, application_name, metric
|
|
24
66
|
"""
|
25
67
|
|
26
68
|
scope._end_time = time.time()
|
27
|
-
if len(scope._timestamps) > 1:
|
28
|
-
scope._tbt = calculate_tbt(scope._timestamps)
|
29
|
-
|
30
69
|
forward_params = scope._instance._forward_params
|
31
70
|
request_model = scope._instance.model.config.name_or_path
|
32
71
|
|
33
72
|
input_tokens = general_tokens(scope._prompt)
|
34
|
-
output_tokens = general_tokens(scope.
|
73
|
+
output_tokens = general_tokens(scope._completion)
|
35
74
|
|
36
75
|
cost = get_chat_model_cost(request_model, pricing_info, input_tokens, output_tokens)
|
37
76
|
|
38
|
-
#
|
39
|
-
scope
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
(SemanticConvention.GEN_AI_REQUEST_TOP_K, "top_k")
|
49
|
-
|
50
|
-
(SemanticConvention.
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
value = forward_params.get(key)
|
56
|
-
if value is not None:
|
57
|
-
scope._span.set_attribute(attribute, value)
|
58
|
-
|
59
|
-
scope._span.set_attribute(SemanticConvention.GEN_AI_RESPONSE_MODEL, request_model)
|
77
|
+
# Common Span Attributes
|
78
|
+
common_span_attributes(scope,
|
79
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT, SemanticConvention.GEN_AI_SYSTEM_HUGGING_FACE,
|
80
|
+
scope._server_address, scope._server_port, request_model, request_model,
|
81
|
+
environment, application_name, is_stream, scope._tbt, scope._ttft, version)
|
82
|
+
|
83
|
+
# Set request parameters from forward_params
|
84
|
+
if forward_params.get("temperature") is not None:
|
85
|
+
scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_TEMPERATURE, forward_params["temperature"])
|
86
|
+
if forward_params.get("top_k") is not None:
|
87
|
+
scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_TOP_K, forward_params["top_k"])
|
88
|
+
if forward_params.get("top_p") is not None:
|
89
|
+
scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_TOP_P, forward_params["top_p"])
|
90
|
+
if forward_params.get("max_length") is not None:
|
91
|
+
scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_MAX_TOKENS, forward_params["max_length"])
|
92
|
+
|
93
|
+
# Set token usage and cost attributes
|
60
94
|
scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS, input_tokens)
|
61
95
|
scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_OUTPUT_TOKENS, output_tokens)
|
62
|
-
scope._span.set_attribute(SemanticConvention.SERVER_ADDRESS, scope._server_address)
|
63
|
-
scope._span.set_attribute(DEPLOYMENT_ENVIRONMENT, environment)
|
64
|
-
scope._span.set_attribute(SERVICE_NAME, application_name)
|
65
|
-
scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_IS_STREAM, is_stream)
|
66
96
|
scope._span.set_attribute(SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE, input_tokens + output_tokens)
|
67
97
|
scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_COST, cost)
|
68
|
-
scope._span.set_attribute(SemanticConvention.GEN_AI_SERVER_TBT, scope._tbt)
|
69
|
-
scope._span.set_attribute(SemanticConvention.GEN_AI_SERVER_TTFT, scope._ttft)
|
70
|
-
scope._span.set_attribute(SemanticConvention.GEN_AI_SDK_VERSION, version)
|
71
98
|
|
72
|
-
#
|
99
|
+
# Span Attributes for Content
|
73
100
|
if capture_message_content:
|
74
101
|
scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, scope._prompt)
|
75
|
-
scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_COMPLETION, scope.
|
102
|
+
scope._span.set_attribute(SemanticConvention.GEN_AI_CONTENT_COMPLETION, scope._completion)
|
76
103
|
|
104
|
+
# To be removed once the change to span_attributes (from span events) is complete
|
77
105
|
scope._span.add_event(
|
78
106
|
name=SemanticConvention.GEN_AI_CONTENT_PROMPT_EVENT,
|
79
107
|
attributes={
|
@@ -83,32 +111,18 @@ def common_chat_logic(scope, pricing_info, environment, application_name, metric
|
|
83
111
|
scope._span.add_event(
|
84
112
|
name=SemanticConvention.GEN_AI_CONTENT_COMPLETION_EVENT,
|
85
113
|
attributes={
|
86
|
-
SemanticConvention.GEN_AI_CONTENT_COMPLETION: scope.
|
114
|
+
SemanticConvention.GEN_AI_CONTENT_COMPLETION: scope._completion,
|
87
115
|
},
|
88
116
|
)
|
89
117
|
|
90
118
|
scope._span.set_status(Status(StatusCode.OK))
|
91
119
|
|
120
|
+
# Record metrics using the standardized helper function
|
92
121
|
if not disable_metrics:
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
system=SemanticConvention.GEN_AI_SYSTEM_HUGGING_FACE,
|
98
|
-
request_model=request_model,
|
99
|
-
server_address=scope._server_address,
|
100
|
-
server_port=scope._server_port,
|
101
|
-
response_model=request_model,
|
102
|
-
)
|
103
|
-
|
104
|
-
metrics["genai_client_usage_tokens"].record(input_tokens + output_tokens, metrics_attributes)
|
105
|
-
metrics["genai_client_operation_duration"].record(scope._end_time - scope._start_time, metrics_attributes)
|
106
|
-
metrics["genai_server_tbt"].record(scope._tbt, metrics_attributes)
|
107
|
-
metrics["genai_server_ttft"].record(scope._ttft, metrics_attributes)
|
108
|
-
metrics["genai_requests"].add(1, metrics_attributes)
|
109
|
-
metrics["genai_completion_tokens"].add(output_tokens, metrics_attributes)
|
110
|
-
metrics["genai_prompt_tokens"].add(input_tokens, metrics_attributes)
|
111
|
-
metrics["genai_cost"].record(cost, metrics_attributes)
|
122
|
+
record_completion_metrics(metrics, SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
|
123
|
+
SemanticConvention.GEN_AI_SYSTEM_HUGGING_FACE, scope._server_address, scope._server_port,
|
124
|
+
request_model, request_model, environment, application_name, scope._start_time, scope._end_time,
|
125
|
+
cost, input_tokens, output_tokens, scope._tbt, scope._ttft)
|
112
126
|
|
113
127
|
def process_chat_response(instance, response, request_model, pricing_info, server_port, server_address,
|
114
128
|
environment, application_name, metrics, start_time,
|
@@ -117,67 +131,69 @@ def process_chat_response(instance, response, request_model, pricing_info, serve
|
|
117
131
|
Process chat request and generate Telemetry
|
118
132
|
"""
|
119
133
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
if self._args and len(self._args) > 0:
|
135
|
-
self._prompt = args[0]
|
134
|
+
scope = type("GenericScope", (), {})()
|
135
|
+
scope._instance = instance
|
136
|
+
scope._start_time = start_time
|
137
|
+
scope._end_time = time.time()
|
138
|
+
scope._span = span
|
139
|
+
scope._server_address = server_address
|
140
|
+
scope._server_port = server_port
|
141
|
+
scope._kwargs = kwargs
|
142
|
+
scope._args = args
|
143
|
+
|
144
|
+
# Extract prompt from args or kwargs
|
145
|
+
if args and len(args) > 0:
|
146
|
+
scope._prompt = args[0]
|
136
147
|
else:
|
137
|
-
|
148
|
+
scope._prompt = (
|
138
149
|
kwargs.get("text_inputs") or
|
139
150
|
(kwargs.get("image") and kwargs.get("question") and
|
140
|
-
|
151
|
+
("image: " + kwargs.get("image") + " question:" + kwargs.get("question"))) or
|
141
152
|
kwargs.get("fallback") or
|
142
153
|
""
|
143
154
|
)
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
155
|
+
scope._prompt = format_content(scope._prompt)
|
156
|
+
|
157
|
+
# Process response based on task type
|
158
|
+
task = kwargs.get("task", "text-generation")
|
159
|
+
|
160
|
+
if task == "text-generation":
|
161
|
+
# Handle text generation responses
|
162
|
+
if isinstance(response, list) and len(response) > 0:
|
163
|
+
first_entry = response[0]
|
164
|
+
if isinstance(first_entry, dict):
|
165
|
+
if isinstance(first_entry.get("generated_text"), list):
|
166
|
+
# Handle nested list format
|
167
|
+
last_element = first_entry.get("generated_text")[-1]
|
168
|
+
scope._completion = last_element.get("content", str(last_element))
|
169
|
+
else:
|
170
|
+
# Handle standard format
|
171
|
+
scope._completion = first_entry.get("generated_text", "")
|
172
|
+
else:
|
173
|
+
scope._completion = str(first_entry)
|
153
174
|
else:
|
154
|
-
|
155
|
-
if isinstance(entry, dict):
|
156
|
-
return entry.get("generated_text")
|
157
|
-
if isinstance(entry, list):
|
158
|
-
return " ".join(
|
159
|
-
extract_text(sub_entry) for sub_entry in entry if isinstance(sub_entry, dict)
|
160
|
-
)
|
161
|
-
return ""
|
162
|
-
|
163
|
-
# Process and collect all generated texts
|
164
|
-
self._llmresponse = [
|
165
|
-
extract_text(entry) for entry in response_dict
|
166
|
-
]
|
175
|
+
scope._completion = ""
|
167
176
|
|
168
|
-
|
169
|
-
|
177
|
+
elif task == "automatic-speech-recognition":
|
178
|
+
scope._completion = response.get("text", "") if isinstance(response, dict) else ""
|
170
179
|
|
171
|
-
elif
|
172
|
-
|
180
|
+
elif task == "image-classification":
|
181
|
+
scope._completion = str(response[0]) if isinstance(response, list) and len(response) > 0 else ""
|
173
182
|
|
174
|
-
elif
|
175
|
-
|
183
|
+
elif task == "visual-question-answering":
|
184
|
+
if isinstance(response, list) and len(response) > 0 and isinstance(response[0], dict):
|
185
|
+
scope._completion = response[0].get("answer", "")
|
186
|
+
else:
|
187
|
+
scope._completion = ""
|
188
|
+
else:
|
189
|
+
# Default handling for other tasks
|
190
|
+
scope._completion = format_content(response)
|
176
191
|
|
177
|
-
|
178
|
-
|
192
|
+
# Initialize timing attributes
|
193
|
+
scope._tbt = 0
|
194
|
+
scope._ttft = scope._end_time - scope._start_time
|
179
195
|
|
180
|
-
common_chat_logic(
|
181
|
-
|
196
|
+
common_chat_logic(scope, pricing_info, environment, application_name, metrics,
|
197
|
+
capture_message_content, disable_metrics, version, args, kwargs, is_stream=False)
|
182
198
|
|
183
199
|
return response
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: openlit
|
3
|
-
Version: 1.34.
|
3
|
+
Version: 1.34.15
|
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
|
@@ -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=
|
84
|
-
openlit/instrumentation/litellm/async_litellm.py,sha256=
|
85
|
-
openlit/instrumentation/litellm/litellm.py,sha256=
|
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
|
@@ -125,9 +126,9 @@ openlit/instrumentation/together/__init__.py,sha256=0UmUqQtppyK3oopb4lTjX2LITgVC
|
|
125
126
|
openlit/instrumentation/together/async_together.py,sha256=0-h5fKw6rIwN_fvWVpGuvVqizIuM9xFCzz8Z4oGgOj0,6822
|
126
127
|
openlit/instrumentation/together/together.py,sha256=nY6mzHmHgoMbbnB_9eL0EBQjP0ltJVdkQj4pbamHAj0,6723
|
127
128
|
openlit/instrumentation/together/utils.py,sha256=n7r_pM_sqFnJEAkL7OhPydr0Uct0A74vXdcYELdbeW0,14368
|
128
|
-
openlit/instrumentation/transformers/__init__.py,sha256=
|
129
|
-
openlit/instrumentation/transformers/transformers.py,sha256=
|
130
|
-
openlit/instrumentation/transformers/utils.py,sha256=
|
129
|
+
openlit/instrumentation/transformers/__init__.py,sha256=hXq0WUZNl6Sz0Ihk29kA9i8Q1j0e1URFb7v7etnQpxI,1511
|
130
|
+
openlit/instrumentation/transformers/transformers.py,sha256=MHnHVo_6NP0gSIqxen6qQpCrZ0fs8Ec80EdZumMpVNo,1797
|
131
|
+
openlit/instrumentation/transformers/utils.py,sha256=MMy_SyRyDI4X-0mqbBwStac0xabmw0ZRvv_VWLA_Nkg,8426
|
131
132
|
openlit/instrumentation/vertexai/__init__.py,sha256=mT28WCBvQfRCkAWGL6bd0EjEPHvMjaNcz6T3jsLZh8k,3745
|
132
133
|
openlit/instrumentation/vertexai/async_vertexai.py,sha256=-kpg-eiL76O5_XopUPghCYwJHf0Nrxi00_Z5tCwq6zM,23086
|
133
134
|
openlit/instrumentation/vertexai/vertexai.py,sha256=5NB090aWlm9DnlccNNLRO6A97P_RN-JnHb5JS01tYyw,23000
|
@@ -138,7 +139,7 @@ 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
141
|
openlit/semcov/__init__.py,sha256=ptyo37PY-FHDx_PShEvbdns71cD4YvvXw15bCRXKCKM,13461
|
141
|
-
openlit-1.34.
|
142
|
-
openlit-1.34.
|
143
|
-
openlit-1.34.
|
144
|
-
openlit-1.34.
|
142
|
+
openlit-1.34.15.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
143
|
+
openlit-1.34.15.dist-info/METADATA,sha256=ySa6XC3OrkzTkTgO5r3UV-PCuefnpG6yoAzL2DvZ9aQ,23470
|
144
|
+
openlit-1.34.15.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
145
|
+
openlit-1.34.15.dist-info/RECORD,,
|
File without changes
|
File without changes
|