openlit 1.33.10__py3-none-any.whl → 1.33.12__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/__helpers.py +125 -88
- openlit/__init__.py +38 -11
- openlit/instrumentation/ag2/__init__.py +19 -20
- openlit/instrumentation/ag2/ag2.py +134 -69
- openlit/instrumentation/ai21/__init__.py +22 -21
- openlit/instrumentation/ai21/ai21.py +82 -546
- openlit/instrumentation/ai21/async_ai21.py +82 -546
- openlit/instrumentation/ai21/utils.py +409 -0
- openlit/instrumentation/anthropic/__init__.py +16 -16
- openlit/instrumentation/anthropic/anthropic.py +61 -353
- openlit/instrumentation/anthropic/async_anthropic.py +62 -354
- openlit/instrumentation/anthropic/utils.py +251 -0
- openlit/instrumentation/assemblyai/__init__.py +2 -2
- openlit/instrumentation/assemblyai/assemblyai.py +3 -3
- openlit/instrumentation/astra/__init__.py +25 -25
- openlit/instrumentation/astra/astra.py +2 -2
- openlit/instrumentation/astra/async_astra.py +2 -2
- openlit/instrumentation/azure_ai_inference/__init__.py +5 -5
- openlit/instrumentation/azure_ai_inference/async_azure_ai_inference.py +8 -8
- openlit/instrumentation/azure_ai_inference/azure_ai_inference.py +8 -8
- openlit/instrumentation/bedrock/__init__.py +2 -2
- openlit/instrumentation/bedrock/bedrock.py +3 -3
- openlit/instrumentation/chroma/__init__.py +9 -9
- openlit/instrumentation/chroma/chroma.py +2 -2
- openlit/instrumentation/cohere/__init__.py +7 -7
- openlit/instrumentation/cohere/async_cohere.py +9 -9
- openlit/instrumentation/cohere/cohere.py +9 -9
- openlit/instrumentation/controlflow/__init__.py +4 -4
- openlit/instrumentation/controlflow/controlflow.py +2 -2
- openlit/instrumentation/crawl4ai/__init__.py +3 -3
- openlit/instrumentation/crawl4ai/async_crawl4ai.py +2 -2
- openlit/instrumentation/crawl4ai/crawl4ai.py +2 -2
- openlit/instrumentation/crewai/__init__.py +3 -3
- openlit/instrumentation/crewai/crewai.py +2 -2
- openlit/instrumentation/dynamiq/__init__.py +5 -5
- openlit/instrumentation/dynamiq/dynamiq.py +2 -2
- openlit/instrumentation/elevenlabs/__init__.py +5 -5
- openlit/instrumentation/elevenlabs/async_elevenlabs.py +3 -3
- openlit/instrumentation/elevenlabs/elevenlabs.py +3 -3
- openlit/instrumentation/embedchain/__init__.py +2 -2
- openlit/instrumentation/embedchain/embedchain.py +4 -4
- openlit/instrumentation/firecrawl/__init__.py +3 -3
- openlit/instrumentation/firecrawl/firecrawl.py +2 -2
- openlit/instrumentation/google_ai_studio/__init__.py +3 -3
- openlit/instrumentation/google_ai_studio/async_google_ai_studio.py +3 -3
- openlit/instrumentation/google_ai_studio/google_ai_studio.py +3 -3
- openlit/instrumentation/gpt4all/__init__.py +3 -3
- openlit/instrumentation/gpt4all/gpt4all.py +7 -7
- openlit/instrumentation/groq/__init__.py +3 -3
- openlit/instrumentation/groq/async_groq.py +5 -5
- openlit/instrumentation/groq/groq.py +5 -5
- openlit/instrumentation/haystack/__init__.py +2 -2
- openlit/instrumentation/haystack/haystack.py +2 -2
- openlit/instrumentation/julep/__init__.py +7 -7
- openlit/instrumentation/julep/async_julep.py +3 -3
- openlit/instrumentation/julep/julep.py +3 -3
- openlit/instrumentation/langchain/__init__.py +2 -2
- openlit/instrumentation/langchain/async_langchain.py +13 -9
- openlit/instrumentation/langchain/langchain.py +13 -8
- openlit/instrumentation/letta/__init__.py +7 -7
- openlit/instrumentation/letta/letta.py +5 -5
- openlit/instrumentation/litellm/__init__.py +5 -5
- openlit/instrumentation/litellm/async_litellm.py +8 -8
- openlit/instrumentation/litellm/litellm.py +8 -8
- openlit/instrumentation/llamaindex/__init__.py +2 -2
- openlit/instrumentation/llamaindex/llamaindex.py +2 -2
- openlit/instrumentation/mem0/__init__.py +2 -2
- openlit/instrumentation/mem0/mem0.py +2 -2
- openlit/instrumentation/milvus/__init__.py +2 -2
- openlit/instrumentation/milvus/milvus.py +2 -2
- openlit/instrumentation/mistral/__init__.py +7 -7
- openlit/instrumentation/mistral/async_mistral.py +10 -10
- openlit/instrumentation/mistral/mistral.py +10 -10
- openlit/instrumentation/multion/__init__.py +7 -7
- openlit/instrumentation/multion/async_multion.py +5 -5
- openlit/instrumentation/multion/multion.py +5 -5
- openlit/instrumentation/ollama/__init__.py +11 -9
- openlit/instrumentation/ollama/async_ollama.py +71 -465
- openlit/instrumentation/ollama/ollama.py +71 -465
- openlit/instrumentation/ollama/utils.py +332 -0
- openlit/instrumentation/openai/__init__.py +11 -11
- openlit/instrumentation/openai/async_openai.py +18 -18
- openlit/instrumentation/openai/openai.py +18 -18
- openlit/instrumentation/phidata/__init__.py +2 -2
- openlit/instrumentation/phidata/phidata.py +2 -2
- openlit/instrumentation/pinecone/__init__.py +6 -6
- openlit/instrumentation/pinecone/pinecone.py +2 -2
- openlit/instrumentation/premai/__init__.py +3 -3
- openlit/instrumentation/premai/premai.py +7 -7
- openlit/instrumentation/qdrant/__init__.py +2 -2
- openlit/instrumentation/qdrant/async_qdrant.py +2 -2
- openlit/instrumentation/qdrant/qdrant.py +2 -2
- openlit/instrumentation/reka/__init__.py +3 -3
- openlit/instrumentation/reka/async_reka.py +3 -3
- openlit/instrumentation/reka/reka.py +3 -3
- openlit/instrumentation/together/__init__.py +5 -5
- openlit/instrumentation/together/async_together.py +8 -8
- openlit/instrumentation/together/together.py +8 -8
- openlit/instrumentation/transformers/__init__.py +2 -2
- openlit/instrumentation/transformers/transformers.py +4 -4
- openlit/instrumentation/vertexai/__init__.py +9 -9
- openlit/instrumentation/vertexai/async_vertexai.py +4 -4
- openlit/instrumentation/vertexai/vertexai.py +4 -4
- openlit/instrumentation/vllm/__init__.py +2 -2
- openlit/instrumentation/vllm/vllm.py +3 -3
- openlit/otel/events.py +85 -0
- openlit/otel/tracing.py +3 -13
- openlit/semcov/__init__.py +13 -1
- {openlit-1.33.10.dist-info → openlit-1.33.12.dist-info}/METADATA +2 -2
- openlit-1.33.12.dist-info/RECORD +126 -0
- openlit-1.33.10.dist-info/RECORD +0 -122
- {openlit-1.33.10.dist-info → openlit-1.33.12.dist-info}/LICENSE +0 -0
- {openlit-1.33.10.dist-info → openlit-1.33.12.dist-info}/WHEEL +0 -0
openlit/__helpers.py
CHANGED
@@ -11,6 +11,7 @@ import requests
|
|
11
11
|
import tiktoken
|
12
12
|
from opentelemetry.sdk.resources import SERVICE_NAME, TELEMETRY_SDK_NAME, DEPLOYMENT_ENVIRONMENT
|
13
13
|
from opentelemetry.trace import Status, StatusCode
|
14
|
+
from opentelemetry._events import Event
|
14
15
|
from openlit.semcov import SemanticConvetion
|
15
16
|
|
16
17
|
# Set up logging
|
@@ -20,12 +21,13 @@ def response_as_dict(response):
|
|
20
21
|
"""
|
21
22
|
Return parsed response as a dict
|
22
23
|
"""
|
24
|
+
|
23
25
|
# pylint: disable=no-else-return
|
24
26
|
if isinstance(response, dict):
|
25
27
|
return response
|
26
|
-
if hasattr(response,
|
28
|
+
if hasattr(response, 'model_dump'):
|
27
29
|
return response.model_dump()
|
28
|
-
elif hasattr(response,
|
30
|
+
elif hasattr(response, 'parse'):
|
29
31
|
return response_as_dict(response.parse())
|
30
32
|
else:
|
31
33
|
return response
|
@@ -33,8 +35,8 @@ def response_as_dict(response):
|
|
33
35
|
def get_env_variable(name, arg_value, error_message):
|
34
36
|
"""
|
35
37
|
Retrieve an environment variable if the argument is not provided
|
36
|
-
and raise an error if both are not set.
|
37
38
|
"""
|
39
|
+
|
38
40
|
if arg_value is not None:
|
39
41
|
return arg_value
|
40
42
|
value = os.getenv(name)
|
@@ -46,14 +48,8 @@ def get_env_variable(name, arg_value, error_message):
|
|
46
48
|
def openai_tokens(text, model):
|
47
49
|
"""
|
48
50
|
Calculate the number of tokens a given text would take up for a specified model.
|
49
|
-
|
50
|
-
Args:
|
51
|
-
text (str): The input text to be encoded.
|
52
|
-
model (str): The model identifier used for encoding.
|
53
|
-
|
54
|
-
Returns:
|
55
|
-
int: The number of tokens the text is encoded into.
|
56
51
|
"""
|
52
|
+
|
57
53
|
try:
|
58
54
|
encoding = tiktoken.encoding_for_model(model)
|
59
55
|
except:
|
@@ -65,16 +61,9 @@ def openai_tokens(text, model):
|
|
65
61
|
def general_tokens(text):
|
66
62
|
"""
|
67
63
|
Calculate the number of tokens a given text would take up.
|
68
|
-
|
69
|
-
Args:
|
70
|
-
text (str): The input text to be encoded.
|
71
|
-
model (str): The model identifier used for encoding.
|
72
|
-
|
73
|
-
Returns:
|
74
|
-
int: The number of tokens the text is encoded into.
|
75
64
|
"""
|
76
65
|
|
77
|
-
encoding = tiktoken.get_encoding(
|
66
|
+
encoding = tiktoken.get_encoding('gpt2')
|
78
67
|
|
79
68
|
num_tokens = len(encoding.encode(text))
|
80
69
|
return num_tokens
|
@@ -82,19 +71,11 @@ def general_tokens(text):
|
|
82
71
|
def get_chat_model_cost(model, pricing_info, prompt_tokens, completion_tokens):
|
83
72
|
"""
|
84
73
|
Retrieve the cost of processing for a given model based on prompt and tokens.
|
85
|
-
|
86
|
-
Args:
|
87
|
-
model (str): The model identifier.
|
88
|
-
pricing_info (dict): A dictionary containing pricing information for various models.
|
89
|
-
prompt_tokens (int): Number of tokens in the prompt.
|
90
|
-
completion_tokens (int): Number of tokens in the completion if applicable.
|
91
|
-
|
92
|
-
Returns:
|
93
|
-
float: The calculated cost for the operation.
|
94
74
|
"""
|
75
|
+
|
95
76
|
try:
|
96
|
-
cost = ((prompt_tokens / 1000) * pricing_info[
|
97
|
-
((completion_tokens / 1000) * pricing_info[
|
77
|
+
cost = ((prompt_tokens / 1000) * pricing_info['chat'][model]['promptPrice']) + \
|
78
|
+
((completion_tokens / 1000) * pricing_info['chat'][model]['completionPrice'])
|
98
79
|
except:
|
99
80
|
cost = 0
|
100
81
|
return cost
|
@@ -102,17 +83,10 @@ def get_chat_model_cost(model, pricing_info, prompt_tokens, completion_tokens):
|
|
102
83
|
def get_embed_model_cost(model, pricing_info, prompt_tokens):
|
103
84
|
"""
|
104
85
|
Retrieve the cost of processing for a given model based on prompt tokens.
|
105
|
-
|
106
|
-
Args:
|
107
|
-
model (str): The model identifier.
|
108
|
-
pricing_info (dict): A dictionary containing pricing information for various models.
|
109
|
-
prompt_tokens (int): Number of tokens in the prompt.
|
110
|
-
|
111
|
-
Returns:
|
112
|
-
float: The calculated cost for the operation.
|
113
86
|
"""
|
87
|
+
|
114
88
|
try:
|
115
|
-
cost = (prompt_tokens / 1000) * pricing_info[
|
89
|
+
cost = (prompt_tokens / 1000) * pricing_info['embeddings'][model]
|
116
90
|
except:
|
117
91
|
cost = 0
|
118
92
|
return cost
|
@@ -120,18 +94,10 @@ def get_embed_model_cost(model, pricing_info, prompt_tokens):
|
|
120
94
|
def get_image_model_cost(model, pricing_info, size, quality):
|
121
95
|
"""
|
122
96
|
Retrieve the cost of processing for a given model based on image size and quailty.
|
123
|
-
|
124
|
-
Args:
|
125
|
-
model (str): The model identifier.
|
126
|
-
pricing_info (dict): A dictionary containing pricing information for various models.
|
127
|
-
size (str): Size of the Image.
|
128
|
-
quality (int): Quality of the Image.
|
129
|
-
|
130
|
-
Returns:
|
131
|
-
float: The calculated cost for the operation.
|
132
97
|
"""
|
98
|
+
|
133
99
|
try:
|
134
|
-
cost = pricing_info[
|
100
|
+
cost = pricing_info['images'][model][quality][size]
|
135
101
|
except:
|
136
102
|
cost = 0
|
137
103
|
return cost
|
@@ -139,20 +105,13 @@ def get_image_model_cost(model, pricing_info, size, quality):
|
|
139
105
|
def get_audio_model_cost(model, pricing_info, prompt, duration=None):
|
140
106
|
"""
|
141
107
|
Retrieve the cost of processing for a given model based on prompt.
|
142
|
-
|
143
|
-
Args:
|
144
|
-
model (str): The model identifier.
|
145
|
-
pricing_info (dict): A dictionary containing pricing information for various models.
|
146
|
-
prompt (str): Prompt to the LLM Model
|
147
|
-
|
148
|
-
Returns:
|
149
|
-
float: The calculated cost for the operation.
|
150
108
|
"""
|
109
|
+
|
151
110
|
try:
|
152
111
|
if prompt:
|
153
|
-
cost = (len(prompt) / 1000) * pricing_info[
|
112
|
+
cost = (len(prompt) / 1000) * pricing_info['audio'][model]
|
154
113
|
else:
|
155
|
-
cost = duration * pricing_info[
|
114
|
+
cost = duration * pricing_info['audio'][model]
|
156
115
|
except:
|
157
116
|
cost = 0
|
158
117
|
return cost
|
@@ -160,15 +119,10 @@ def get_audio_model_cost(model, pricing_info, prompt, duration=None):
|
|
160
119
|
def fetch_pricing_info(pricing_json=None):
|
161
120
|
"""
|
162
121
|
Fetches pricing information from a specified URL or File Path.
|
163
|
-
|
164
|
-
Args:
|
165
|
-
pricing_json(str): path or url to the pricing json file
|
166
|
-
|
167
|
-
Returns:
|
168
|
-
dict: The pricing json
|
169
122
|
"""
|
123
|
+
|
170
124
|
if pricing_json:
|
171
|
-
is_url = urlparse(pricing_json).scheme !=
|
125
|
+
is_url = urlparse(pricing_json).scheme != ''
|
172
126
|
if is_url:
|
173
127
|
pricing_url = pricing_json
|
174
128
|
else:
|
@@ -176,39 +130,36 @@ def fetch_pricing_info(pricing_json=None):
|
|
176
130
|
with open(pricing_json, mode='r', encoding='utf-8') as f:
|
177
131
|
return json.load(f)
|
178
132
|
except FileNotFoundError:
|
179
|
-
logger.error(
|
133
|
+
logger.error('Pricing information file not found: %s', pricing_json)
|
180
134
|
except json.JSONDecodeError:
|
181
|
-
logger.error(
|
135
|
+
logger.error('Error decoding JSON from file: %s', pricing_json)
|
182
136
|
except Exception as file_err:
|
183
|
-
logger.error(
|
137
|
+
logger.error('Unexpected error occurred while reading file: %s', file_err)
|
184
138
|
return {}
|
185
139
|
else:
|
186
|
-
pricing_url =
|
140
|
+
pricing_url = 'https://raw.githubusercontent.com/openlit/openlit/main/assets/pricing.json'
|
187
141
|
try:
|
188
142
|
# Set a timeout of 10 seconds for both the connection and the read
|
189
143
|
response = requests.get(pricing_url, timeout=20)
|
190
144
|
response.raise_for_status()
|
191
145
|
return response.json()
|
192
146
|
except requests.HTTPError as http_err:
|
193
|
-
logger.error(
|
147
|
+
logger.error('HTTP error occured while fetching pricing info: %s', http_err)
|
194
148
|
except Exception as err:
|
195
|
-
logger.error(
|
149
|
+
logger.error('Unexpected error occurred while fetching pricing info: %s', err)
|
196
150
|
return {}
|
197
151
|
|
198
152
|
def handle_exception(span,e):
|
199
153
|
"""Handles Exception when LLM Function fails or trace creation fails."""
|
200
|
-
|
154
|
+
|
201
155
|
span.record_exception(e)
|
202
156
|
span.set_status(Status(StatusCode.ERROR))
|
203
157
|
|
204
158
|
def calculate_ttft(timestamps: List[float], start_time: float) -> float:
|
205
159
|
"""
|
206
160
|
Calculate the time to the first tokens.
|
207
|
-
|
208
|
-
:param timestamps: List of timestamps for received tokens
|
209
|
-
:param start_time: The start time of the streaming process
|
210
|
-
:return: Time to the first tokens
|
211
161
|
"""
|
162
|
+
|
212
163
|
if timestamps:
|
213
164
|
return timestamps[0] - start_time
|
214
165
|
return 0.0
|
@@ -216,10 +167,8 @@ def calculate_ttft(timestamps: List[float], start_time: float) -> float:
|
|
216
167
|
def calculate_tbt(timestamps: List[float]) -> float:
|
217
168
|
"""
|
218
169
|
Calculate the average time between tokens.
|
219
|
-
|
220
|
-
:param timestamps: List of timestamps for received tokens
|
221
|
-
:return: Average time between tokens
|
222
170
|
"""
|
171
|
+
|
223
172
|
if len(timestamps) > 1:
|
224
173
|
time_diffs = [timestamps[i] - timestamps[i - 1] for i in range(1, len(timestamps))]
|
225
174
|
return sum(time_diffs) / len(time_diffs)
|
@@ -238,8 +187,9 @@ def create_metrics_attributes(
|
|
238
187
|
"""
|
239
188
|
Returns OTel metrics attributes
|
240
189
|
"""
|
190
|
+
|
241
191
|
return {
|
242
|
-
TELEMETRY_SDK_NAME:
|
192
|
+
TELEMETRY_SDK_NAME: 'openlit',
|
243
193
|
SERVICE_NAME: service_name,
|
244
194
|
DEPLOYMENT_ENVIRONMENT: deployment_environment,
|
245
195
|
SemanticConvetion.GEN_AI_OPERATION: operation,
|
@@ -258,18 +208,18 @@ def set_server_address_and_port(client_instance: Any,
|
|
258
208
|
"""
|
259
209
|
|
260
210
|
# Try getting base_url from multiple potential attributes
|
261
|
-
base_client = getattr(client_instance,
|
262
|
-
base_url = getattr(base_client,
|
211
|
+
base_client = getattr(client_instance, '_client', None)
|
212
|
+
base_url = getattr(base_client, 'base_url', None)
|
263
213
|
|
264
214
|
if not base_url:
|
265
215
|
# Attempt to get endpoint from instance._config.endpoint if base_url is not set
|
266
|
-
config = getattr(client_instance,
|
267
|
-
base_url = getattr(config,
|
216
|
+
config = getattr(client_instance, '_config', None)
|
217
|
+
base_url = getattr(config, 'endpoint', None)
|
268
218
|
|
269
219
|
if not base_url:
|
270
220
|
# Attempt to get server_url from instance.sdk_configuration.server_url
|
271
|
-
config = getattr(client_instance,
|
272
|
-
base_url = getattr(config,
|
221
|
+
config = getattr(client_instance, 'sdk_configuration', None)
|
222
|
+
base_url = getattr(config, 'server_url', None)
|
273
223
|
|
274
224
|
if base_url:
|
275
225
|
if isinstance(base_url, str):
|
@@ -277,11 +227,98 @@ def set_server_address_and_port(client_instance: Any,
|
|
277
227
|
server_address = url.hostname or default_server_address
|
278
228
|
server_port = url.port if url.port is not None else default_server_port
|
279
229
|
else: # base_url might not be a str; handle as an object.
|
280
|
-
server_address = getattr(base_url,
|
281
|
-
port_attr = getattr(base_url,
|
230
|
+
server_address = getattr(base_url, 'host', None) or default_server_address
|
231
|
+
port_attr = getattr(base_url, 'port', None)
|
282
232
|
server_port = port_attr if port_attr is not None else default_server_port
|
283
233
|
else: # no base_url or endpoint provided; use defaults.
|
284
234
|
server_address = default_server_address
|
285
235
|
server_port = default_server_port
|
286
236
|
|
287
237
|
return server_address, server_port
|
238
|
+
|
239
|
+
def otel_event(name, attributes, body):
|
240
|
+
"""
|
241
|
+
Returns an OpenTelemetry Event object
|
242
|
+
"""
|
243
|
+
|
244
|
+
return Event(
|
245
|
+
name=name,
|
246
|
+
attributes=attributes,
|
247
|
+
body=body,
|
248
|
+
)
|
249
|
+
|
250
|
+
def extract_and_format_input(messages):
|
251
|
+
"""
|
252
|
+
Process a list of messages to extract content and categorize
|
253
|
+
them into fixed roles like 'user', 'assistant', 'system', 'tool'.
|
254
|
+
"""
|
255
|
+
|
256
|
+
fixed_roles = ['user', 'assistant', 'system', 'tool'] # Ensure these are your fixed keys
|
257
|
+
# Initialize the dictionary with fixed keys and empty structures
|
258
|
+
formatted_messages = {role_key: {'role': '', 'content': ''} for role_key in fixed_roles}
|
259
|
+
|
260
|
+
for message in messages:
|
261
|
+
# Normalize the message structure
|
262
|
+
message = response_as_dict(message)
|
263
|
+
|
264
|
+
# Extract role and content
|
265
|
+
role = message.get('role')
|
266
|
+
if role not in fixed_roles:
|
267
|
+
continue # Skip any role not in our predefined roles
|
268
|
+
|
269
|
+
content = message.get('content', '')
|
270
|
+
|
271
|
+
# Prepare content as a string
|
272
|
+
if isinstance(content, list):
|
273
|
+
content_str = ", ".join(
|
274
|
+
f'{item.get("type", "text")}: {extract_text_from_item(item)}'
|
275
|
+
for item in content
|
276
|
+
)
|
277
|
+
else:
|
278
|
+
content_str = content
|
279
|
+
|
280
|
+
# Set the role in the formatted message and concatenate content
|
281
|
+
if not formatted_messages[role]['role']:
|
282
|
+
formatted_messages[role]['role'] = role
|
283
|
+
|
284
|
+
if formatted_messages[role]['content']:
|
285
|
+
formatted_messages[role]['content'] += ' ' + content_str
|
286
|
+
else:
|
287
|
+
formatted_messages[role]['content'] = content_str
|
288
|
+
|
289
|
+
return formatted_messages
|
290
|
+
|
291
|
+
def extract_text_from_item(item):
|
292
|
+
"""
|
293
|
+
Extract text from inpit message
|
294
|
+
"""
|
295
|
+
|
296
|
+
#pylint: disable=no-else-return
|
297
|
+
if item.get('type') == 'text':
|
298
|
+
return item.get('text', '')
|
299
|
+
elif item.get('type') == 'image':
|
300
|
+
# Handle image content specifically checking for 'url' or 'base64'
|
301
|
+
source = item.get('source', {})
|
302
|
+
if isinstance(source, dict):
|
303
|
+
if source.get('type') == 'base64':
|
304
|
+
# Return the actual base64 data if present
|
305
|
+
return source.get('data', '[Missing base64 data]')
|
306
|
+
elif source.get('type') == 'url':
|
307
|
+
return source.get('url', '[Missing URL]')
|
308
|
+
elif item.get('type') == 'image_url':
|
309
|
+
# New format: Handle the 'image_url' type
|
310
|
+
image_url = item.get('image_url', {})
|
311
|
+
if isinstance(image_url, dict):
|
312
|
+
return image_url.get('url', '[Missing image URL]')
|
313
|
+
return ''
|
314
|
+
|
315
|
+
# To be removed one the change to log events (from span events) is complete
|
316
|
+
def concatenate_all_contents(formatted_messages):
|
317
|
+
"""
|
318
|
+
Concatenate all 'content' fields into a single strin
|
319
|
+
"""
|
320
|
+
return ' '.join(
|
321
|
+
message_data['content']
|
322
|
+
for message_data in formatted_messages.values()
|
323
|
+
if message_data['content']
|
324
|
+
)
|
openlit/__init__.py
CHANGED
@@ -21,9 +21,9 @@ from opentelemetry.sdk.resources import SERVICE_NAME, DEPLOYMENT_ENVIRONMENT
|
|
21
21
|
from openlit.semcov import SemanticConvetion
|
22
22
|
from openlit.otel.tracing import setup_tracing
|
23
23
|
from openlit.otel.metrics import setup_meter
|
24
|
+
from openlit.otel.events import setup_events
|
24
25
|
from openlit.__helpers import fetch_pricing_info, get_env_variable
|
25
26
|
|
26
|
-
|
27
27
|
# Instrumentors for various large language models.
|
28
28
|
from openlit.instrumentation.openai import OpenAIInstrumentor
|
29
29
|
from openlit.instrumentation.anthropic import AnthropicInstrumentor
|
@@ -85,10 +85,11 @@ class OpenlitConfig:
|
|
85
85
|
application_name (str): Name of the application using openLIT.
|
86
86
|
pricing_info (Dict[str, Any]): Pricing information.
|
87
87
|
tracer (Optional[Any]): Tracer instance for OpenTelemetry.
|
88
|
+
event_provider (Optional[Any]): Event logger provider for OpenTelemetry.
|
88
89
|
otlp_endpoint (Optional[str]): Endpoint for OTLP.
|
89
90
|
otlp_headers (Optional[Dict[str, str]]): Headers for OTLP.
|
90
91
|
disable_batch (bool): Flag to disable batch span processing in tracing.
|
91
|
-
|
92
|
+
capture_message_content (bool): Flag to enable or disable tracing of content.
|
92
93
|
"""
|
93
94
|
|
94
95
|
_instance = None
|
@@ -107,11 +108,12 @@ class OpenlitConfig:
|
|
107
108
|
cls.application_name = "default"
|
108
109
|
cls.pricing_info = {}
|
109
110
|
cls.tracer = None
|
111
|
+
cls.event_provider = None
|
110
112
|
cls.metrics_dict = {}
|
111
113
|
cls.otlp_endpoint = None
|
112
114
|
cls.otlp_headers = None
|
113
115
|
cls.disable_batch = False
|
114
|
-
cls.
|
116
|
+
cls.capture_message_content = True
|
115
117
|
cls.disable_metrics = False
|
116
118
|
|
117
119
|
@classmethod
|
@@ -120,10 +122,11 @@ class OpenlitConfig:
|
|
120
122
|
environment,
|
121
123
|
application_name,
|
122
124
|
tracer,
|
125
|
+
event_provider,
|
123
126
|
otlp_endpoint,
|
124
127
|
otlp_headers,
|
125
128
|
disable_batch,
|
126
|
-
|
129
|
+
capture_message_content,
|
127
130
|
metrics_dict,
|
128
131
|
disable_metrics,
|
129
132
|
pricing_json,
|
@@ -135,22 +138,26 @@ class OpenlitConfig:
|
|
135
138
|
environment (str): Deployment environment.
|
136
139
|
application_name (str): Application name.
|
137
140
|
tracer: Tracer instance.
|
141
|
+
event_provider: Event logger provider instance.
|
138
142
|
meter: Metric Instance
|
139
143
|
otlp_endpoint (str): OTLP endpoint.
|
140
144
|
otlp_headers (Dict[str, str]): OTLP headers.
|
141
145
|
disable_batch (bool): Disable batch span processing flag.
|
142
|
-
|
146
|
+
capture_message_content (bool): Enable or disable content tracing.
|
147
|
+
metrics_dict: Dictionary of metrics.
|
148
|
+
disable_metrics (bool): Flag to disable metrics.
|
143
149
|
pricing_json(str): path or url to the pricing json file
|
144
150
|
"""
|
145
151
|
cls.environment = environment
|
146
152
|
cls.application_name = application_name
|
147
153
|
cls.pricing_info = fetch_pricing_info(pricing_json)
|
148
154
|
cls.tracer = tracer
|
155
|
+
cls.event_provider = event_provider
|
149
156
|
cls.metrics_dict = metrics_dict
|
150
157
|
cls.otlp_endpoint = otlp_endpoint
|
151
158
|
cls.otlp_headers = otlp_headers
|
152
159
|
cls.disable_batch = disable_batch
|
153
|
-
cls.
|
160
|
+
cls.capture_message_content = capture_message_content
|
154
161
|
cls.disable_metrics = disable_metrics
|
155
162
|
|
156
163
|
|
@@ -187,8 +194,9 @@ def instrument_if_available(
|
|
187
194
|
environment=config.environment,
|
188
195
|
application_name=config.application_name,
|
189
196
|
tracer=config.tracer,
|
197
|
+
event_provider=config.event_provider,
|
190
198
|
pricing_info=config.pricing_info,
|
191
|
-
|
199
|
+
capture_message_content=config.capture_message_content,
|
192
200
|
metrics_dict=config.metrics_dict,
|
193
201
|
disable_metrics=config.disable_metrics,
|
194
202
|
)
|
@@ -207,10 +215,11 @@ def init(
|
|
207
215
|
environment="default",
|
208
216
|
application_name="default",
|
209
217
|
tracer=None,
|
218
|
+
event_logger=None,
|
210
219
|
otlp_endpoint=None,
|
211
220
|
otlp_headers=None,
|
212
221
|
disable_batch=False,
|
213
|
-
|
222
|
+
capture_message_content=True,
|
214
223
|
disabled_instrumentors=None,
|
215
224
|
meter=None,
|
216
225
|
disable_metrics=False,
|
@@ -227,11 +236,12 @@ def init(
|
|
227
236
|
environment (str): Deployment environment.
|
228
237
|
application_name (str): Application name.
|
229
238
|
tracer: Tracer instance (Optional).
|
239
|
+
event_logger: EventLoggerProvider instance (Optional).
|
230
240
|
meter: OpenTelemetry Metrics Instance (Optional).
|
231
241
|
otlp_endpoint (str): OTLP endpoint for exporter (Optional).
|
232
242
|
otlp_headers (Dict[str, str]): OTLP headers for exporter (Optional).
|
233
243
|
disable_batch (bool): Flag to disable batch span processing (Optional).
|
234
|
-
|
244
|
+
capture_message_content (bool): Flag to trace content (Optional).
|
235
245
|
disabled_instrumentors (List[str]): Optional. List of instrumentor names to disable.
|
236
246
|
disable_metrics (bool): Flag to disable metrics (Optional).
|
237
247
|
pricing_json(str): File path or url to the pricing json (Optional).
|
@@ -308,9 +318,22 @@ def init(
|
|
308
318
|
)
|
309
319
|
|
310
320
|
if not tracer:
|
311
|
-
logger.error("
|
321
|
+
logger.error("OpenLIT tracing setup failed. Tracing will not be available.")
|
312
322
|
return
|
313
323
|
|
324
|
+
# Setup events based on the provided or default configuration.
|
325
|
+
event_provider = setup_events(
|
326
|
+
application_name=application_name,
|
327
|
+
environment=environment,
|
328
|
+
event_logger=event_logger,
|
329
|
+
otlp_endpoint=None,
|
330
|
+
otlp_headers=None,
|
331
|
+
disable_batch=disable_batch,
|
332
|
+
)
|
333
|
+
|
334
|
+
if not event_provider:
|
335
|
+
logger.error("OpenLIT events setup failed. Events will not be available")
|
336
|
+
|
314
337
|
# Setup meter and receive metrics_dict instead of meter.
|
315
338
|
metrics_dict, err = setup_meter(
|
316
339
|
application_name=application_name,
|
@@ -326,15 +349,19 @@ def init(
|
|
326
349
|
)
|
327
350
|
return
|
328
351
|
|
352
|
+
if os.getenv("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", "").lower == "false":
|
353
|
+
capture_message_content=False
|
354
|
+
|
329
355
|
# Update global configuration with the provided settings.
|
330
356
|
config.update_config(
|
331
357
|
environment,
|
332
358
|
application_name,
|
333
359
|
tracer,
|
360
|
+
event_provider,
|
334
361
|
otlp_endpoint,
|
335
362
|
otlp_headers,
|
336
363
|
disable_batch,
|
337
|
-
|
364
|
+
capture_message_content,
|
338
365
|
metrics_dict,
|
339
366
|
disable_metrics,
|
340
367
|
pricing_json,
|
@@ -1,4 +1,3 @@
|
|
1
|
-
# pylint: disable=useless-return, bad-staticmethod-argument, disable=duplicate-code
|
2
1
|
"""Initializer of Auto Instrumentation of AG2 Functions"""
|
3
2
|
|
4
3
|
from typing import Collection
|
@@ -7,10 +6,10 @@ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
|
7
6
|
from wrapt import wrap_function_wrapper
|
8
7
|
|
9
8
|
from openlit.instrumentation.ag2.ag2 import (
|
10
|
-
|
9
|
+
conversable_agent, agent_run
|
11
10
|
)
|
12
11
|
|
13
|
-
_instruments = (
|
12
|
+
_instruments = ('ag2 >= 0.3.2',)
|
14
13
|
|
15
14
|
class AG2Instrumentor(BaseInstrumentor):
|
16
15
|
"""
|
@@ -21,30 +20,30 @@ class AG2Instrumentor(BaseInstrumentor):
|
|
21
20
|
return _instruments
|
22
21
|
|
23
22
|
def _instrument(self, **kwargs):
|
24
|
-
application_name = kwargs.get(
|
25
|
-
environment = kwargs.get(
|
26
|
-
tracer = kwargs.get(
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
23
|
+
application_name = kwargs.get('application_name', 'default_application')
|
24
|
+
environment = kwargs.get('environment', 'default_environment')
|
25
|
+
tracer = kwargs.get('tracer')
|
26
|
+
event_provider = kwargs.get('event_provider')
|
27
|
+
metrics = kwargs.get('metrics_dict')
|
28
|
+
pricing_info = kwargs.get('pricing_info', {})
|
29
|
+
capture_message_content = kwargs.get('capture_message_content', False)
|
30
|
+
disable_metrics = kwargs.get('disable_metrics')
|
31
|
+
version = importlib.metadata.version('ag2')
|
32
32
|
|
33
33
|
wrap_function_wrapper(
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
tracer, pricing_info,
|
34
|
+
'autogen.agentchat.conversable_agent',
|
35
|
+
'ConversableAgent.__init__',
|
36
|
+
conversable_agent(version, environment, application_name,
|
37
|
+
tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
|
38
38
|
)
|
39
39
|
|
40
40
|
wrap_function_wrapper(
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
tracer, pricing_info,
|
41
|
+
'autogen.agentchat.conversable_agent',
|
42
|
+
'ConversableAgent.run',
|
43
|
+
agent_run(version, environment, application_name,
|
44
|
+
tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
|
45
45
|
)
|
46
46
|
|
47
|
-
|
48
47
|
def _uninstrument(self, **kwargs):
|
49
48
|
# Proper uninstrumentation logic to revert patched methods
|
50
49
|
pass
|