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.
@@ -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
- create_metrics_attributes,
15
- format_and_concatenate
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._llmresponse)
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
- # Set Span attributes (OTel Semconv)
39
- scope._span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
40
- scope._span.set_attribute(SemanticConvention.GEN_AI_OPERATION, SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT)
41
- scope._span.set_attribute(SemanticConvention.GEN_AI_SYSTEM, SemanticConvention.GEN_AI_SYSTEM_HUGGING_FACE)
42
- scope._span.set_attribute(SemanticConvention.GEN_AI_REQUEST_MODEL, request_model)
43
- scope._span.set_attribute(SemanticConvention.SERVER_PORT, scope._server_port)
44
-
45
- # List of attributes and their config keys
46
- attributes = [
47
- (SemanticConvention.GEN_AI_REQUEST_TEMPERATURE, "temperature"),
48
- (SemanticConvention.GEN_AI_REQUEST_TOP_K, "top_k"),
49
- (SemanticConvention.GEN_AI_REQUEST_TOP_P, "top_p"),
50
- (SemanticConvention.GEN_AI_REQUEST_MAX_TOKENS, "max_length"),
51
- ]
52
-
53
- # Set each attribute if the corresponding value exists and is not None
54
- for attribute, key in attributes:
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
- # To be removed one the change to span_attributes (from span events) is complete
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._llmresponse)
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._llmresponse,
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
- metrics_attributes = create_metrics_attributes(
94
- service_name=application_name,
95
- deployment_environment=environment,
96
- operation=SemanticConvention.GEN_AI_OPERATION_TYPE_CHAT,
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
- self = type("GenericScope", (), {})()
121
- response_dict = response_as_dict(response)
122
-
123
- # pylint: disable = no-member
124
- self._instance = instance
125
- self._start_time = start_time
126
- self._end_time = time.time()
127
- self._span = span
128
- self._timestamps = []
129
- self._ttft, self._tbt = self._end_time - self._start_time, 0
130
- self._server_address, self._server_port = server_address, server_port
131
- self._kwargs = kwargs
132
- self._args = args
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
- self._prompt = (
148
+ scope._prompt = (
138
149
  kwargs.get("text_inputs") or
139
150
  (kwargs.get("image") and kwargs.get("question") and
140
- ("image: " + kwargs.get("image") + " question:" + kwargs.get("question"))) or
151
+ ("image: " + kwargs.get("image") + " question:" + kwargs.get("question"))) or
141
152
  kwargs.get("fallback") or
142
153
  ""
143
154
  )
144
- self._prompt = format_and_concatenate(self._prompt)
145
-
146
- self._llmresponse = []
147
- if self._kwargs.get("task", "text-generation") == "text-generation":
148
- first_entry = response_dict[0]
149
-
150
- if isinstance(first_entry, dict) and isinstance(first_entry.get("generated_text"), list):
151
- last_element = first_entry.get("generated_text")[-1]
152
- self._llmresponse = last_element.get("content", last_element)
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
- def extract_text(entry):
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
- # Join all non-empty responses into a single string
169
- self._llmresponse = " ".join(filter(None, self._llmresponse))
177
+ elif task == "automatic-speech-recognition":
178
+ scope._completion = response.get("text", "") if isinstance(response, dict) else ""
170
179
 
171
- elif self._kwargs.get("task", "text-generation") == "automatic-speech-recognition":
172
- self._llmresponse = response_dict.get("text", "")
180
+ elif task == "image-classification":
181
+ scope._completion = str(response[0]) if isinstance(response, list) and len(response) > 0 else ""
173
182
 
174
- elif self._kwargs.get("task", "text-generation") == "image-classification":
175
- self._llmresponse = str(response_dict[0])
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
- elif self._kwargs.get("task", "text-generation") == "visual-question-answering":
178
- self._llmresponse = str(response_dict[0]).get("answer")
192
+ # Initialize timing attributes
193
+ scope._tbt = 0
194
+ scope._ttft = scope._end_time - scope._start_time
179
195
 
180
- common_chat_logic(self, pricing_info, environment, application_name, metrics,
181
- capture_message_content, disable_metrics, version, args, kwargs, is_stream=False)
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.13
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=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
@@ -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=9Ubss5nlumcypxprxff8Fv3sst7II27SsvCzqkBX9Kg,1457
129
- openlit/instrumentation/transformers/transformers.py,sha256=y--t7PXhUfPC81w-aEE7qowMah3os9gnKBQ5bN4QLGc,1980
130
- openlit/instrumentation/transformers/utils.py,sha256=3f-ewpUpduaBrTVIFJKaabACjz-6Vf8K7NEU0EzQ4Nk,8042
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.13.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
142
- openlit-1.34.13.dist-info/METADATA,sha256=4uHfQSKnuT-yfoNz7kj78yd53TBFDCDYVhOIsz7XF8k,23470
143
- openlit-1.34.13.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
144
- openlit-1.34.13.dist-info/RECORD,,
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,,