openlit 1.33.9__py3-none-any.whl → 1.33.11__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 +78 -0
- openlit/__init__.py +41 -13
- openlit/instrumentation/ag2/__init__.py +9 -10
- openlit/instrumentation/ag2/ag2.py +134 -69
- openlit/instrumentation/ai21/__init__.py +6 -5
- openlit/instrumentation/ai21/ai21.py +71 -534
- openlit/instrumentation/ai21/async_ai21.py +71 -534
- openlit/instrumentation/ai21/utils.py +407 -0
- openlit/instrumentation/anthropic/__init__.py +3 -3
- openlit/instrumentation/anthropic/anthropic.py +5 -5
- openlit/instrumentation/anthropic/async_anthropic.py +5 -5
- 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 +7 -7
- openlit/instrumentation/astra/async_astra.py +7 -7
- openlit/instrumentation/azure_ai_inference/__init__.py +5 -5
- openlit/instrumentation/azure_ai_inference/async_azure_ai_inference.py +11 -11
- openlit/instrumentation/azure_ai_inference/azure_ai_inference.py +11 -11
- 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 +7 -7
- openlit/instrumentation/cohere/__init__.py +7 -7
- openlit/instrumentation/cohere/async_cohere.py +10 -10
- openlit/instrumentation/cohere/cohere.py +11 -11
- openlit/instrumentation/controlflow/__init__.py +4 -4
- openlit/instrumentation/controlflow/controlflow.py +5 -5
- openlit/instrumentation/crawl4ai/__init__.py +3 -3
- openlit/instrumentation/crawl4ai/async_crawl4ai.py +5 -5
- openlit/instrumentation/crawl4ai/crawl4ai.py +5 -5
- openlit/instrumentation/crewai/__init__.py +3 -3
- openlit/instrumentation/crewai/crewai.py +6 -4
- openlit/instrumentation/dynamiq/__init__.py +5 -5
- openlit/instrumentation/dynamiq/dynamiq.py +5 -5
- openlit/instrumentation/elevenlabs/__init__.py +5 -5
- openlit/instrumentation/elevenlabs/async_elevenlabs.py +4 -5
- openlit/instrumentation/elevenlabs/elevenlabs.py +4 -5
- openlit/instrumentation/embedchain/__init__.py +2 -2
- openlit/instrumentation/embedchain/embedchain.py +9 -9
- openlit/instrumentation/firecrawl/__init__.py +3 -3
- openlit/instrumentation/firecrawl/firecrawl.py +5 -5
- 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 +5 -5
- openlit/instrumentation/gpt4all/gpt4all.py +350 -225
- openlit/instrumentation/gpu/__init__.py +5 -5
- openlit/instrumentation/groq/__init__.py +5 -5
- openlit/instrumentation/groq/async_groq.py +359 -243
- openlit/instrumentation/groq/groq.py +359 -243
- openlit/instrumentation/haystack/__init__.py +2 -2
- openlit/instrumentation/haystack/haystack.py +5 -5
- openlit/instrumentation/julep/__init__.py +7 -7
- openlit/instrumentation/julep/async_julep.py +6 -6
- openlit/instrumentation/julep/julep.py +6 -6
- openlit/instrumentation/langchain/__init__.py +15 -9
- openlit/instrumentation/langchain/async_langchain.py +388 -0
- openlit/instrumentation/langchain/langchain.py +110 -497
- openlit/instrumentation/letta/__init__.py +7 -7
- openlit/instrumentation/letta/letta.py +10 -8
- openlit/instrumentation/litellm/__init__.py +9 -10
- openlit/instrumentation/litellm/async_litellm.py +321 -250
- openlit/instrumentation/litellm/litellm.py +319 -248
- openlit/instrumentation/llamaindex/__init__.py +2 -2
- openlit/instrumentation/llamaindex/llamaindex.py +5 -5
- openlit/instrumentation/mem0/__init__.py +2 -2
- openlit/instrumentation/mem0/mem0.py +5 -5
- openlit/instrumentation/milvus/__init__.py +2 -2
- openlit/instrumentation/milvus/milvus.py +7 -7
- openlit/instrumentation/mistral/__init__.py +13 -13
- openlit/instrumentation/mistral/async_mistral.py +426 -253
- openlit/instrumentation/mistral/mistral.py +424 -250
- openlit/instrumentation/multion/__init__.py +7 -7
- openlit/instrumentation/multion/async_multion.py +9 -7
- openlit/instrumentation/multion/multion.py +9 -7
- openlit/instrumentation/ollama/__init__.py +19 -39
- openlit/instrumentation/ollama/async_ollama.py +137 -563
- openlit/instrumentation/ollama/ollama.py +136 -563
- openlit/instrumentation/ollama/utils.py +333 -0
- openlit/instrumentation/openai/__init__.py +11 -11
- openlit/instrumentation/openai/async_openai.py +25 -27
- openlit/instrumentation/openai/openai.py +25 -27
- openlit/instrumentation/phidata/__init__.py +2 -2
- openlit/instrumentation/phidata/phidata.py +6 -4
- openlit/instrumentation/pinecone/__init__.py +6 -6
- openlit/instrumentation/pinecone/pinecone.py +7 -7
- openlit/instrumentation/premai/__init__.py +5 -5
- openlit/instrumentation/premai/premai.py +268 -219
- openlit/instrumentation/qdrant/__init__.py +2 -2
- openlit/instrumentation/qdrant/async_qdrant.py +7 -7
- openlit/instrumentation/qdrant/qdrant.py +7 -7
- openlit/instrumentation/reka/__init__.py +5 -5
- openlit/instrumentation/reka/async_reka.py +93 -55
- openlit/instrumentation/reka/reka.py +93 -55
- openlit/instrumentation/together/__init__.py +9 -9
- openlit/instrumentation/together/async_together.py +284 -242
- openlit/instrumentation/together/together.py +284 -242
- openlit/instrumentation/transformers/__init__.py +3 -3
- openlit/instrumentation/transformers/transformers.py +79 -48
- openlit/instrumentation/vertexai/__init__.py +19 -69
- openlit/instrumentation/vertexai/async_vertexai.py +333 -990
- openlit/instrumentation/vertexai/vertexai.py +333 -990
- openlit/instrumentation/vllm/__init__.py +3 -3
- openlit/instrumentation/vllm/vllm.py +65 -35
- openlit/otel/events.py +85 -0
- openlit/otel/tracing.py +3 -13
- openlit/semcov/__init__.py +16 -4
- {openlit-1.33.9.dist-info → openlit-1.33.11.dist-info}/METADATA +2 -2
- openlit-1.33.11.dist-info/RECORD +125 -0
- openlit-1.33.9.dist-info/RECORD +0 -121
- {openlit-1.33.9.dist-info → openlit-1.33.11.dist-info}/LICENSE +0 -0
- {openlit-1.33.9.dist-info → openlit-1.33.11.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
|
@@ -266,6 +267,11 @@ def set_server_address_and_port(client_instance: Any,
|
|
266
267
|
config = getattr(client_instance, "_config", None)
|
267
268
|
base_url = getattr(config, "endpoint", None)
|
268
269
|
|
270
|
+
if not base_url:
|
271
|
+
# Attempt to get server_url from instance.sdk_configuration.server_url
|
272
|
+
config = getattr(client_instance, "sdk_configuration", None)
|
273
|
+
base_url = getattr(config, "server_url", None)
|
274
|
+
|
269
275
|
if base_url:
|
270
276
|
if isinstance(base_url, str):
|
271
277
|
url = urlparse(base_url)
|
@@ -280,3 +286,75 @@ def set_server_address_and_port(client_instance: Any,
|
|
280
286
|
server_port = default_server_port
|
281
287
|
|
282
288
|
return server_address, server_port
|
289
|
+
|
290
|
+
def otel_event(name, attributes, body):
|
291
|
+
"""
|
292
|
+
Returns an OpenTelemetry Event object
|
293
|
+
"""
|
294
|
+
|
295
|
+
return Event(
|
296
|
+
name=name,
|
297
|
+
attributes=attributes,
|
298
|
+
body=body,
|
299
|
+
)
|
300
|
+
|
301
|
+
def extract_and_format_input(messages):
|
302
|
+
"""
|
303
|
+
Process a list of messages to extract content and categorize
|
304
|
+
them into fixed roles like 'user', 'assistant', 'system'.
|
305
|
+
"""
|
306
|
+
|
307
|
+
fixed_roles = ['user', 'assistant', 'system', 'tool'] # Ensure these are your fixed keys
|
308
|
+
# Initialize the dictionary with fixed keys and empty structures
|
309
|
+
formatted_messages = {role_key: {"role": "", "content": ""} for role_key in fixed_roles}
|
310
|
+
|
311
|
+
for message in messages:
|
312
|
+
# Normalize the message structure
|
313
|
+
message = response_as_dict(message)
|
314
|
+
|
315
|
+
# Extract role and content
|
316
|
+
role = message.get("role")
|
317
|
+
if role not in fixed_roles:
|
318
|
+
continue # Skip any role not in our predefined roles
|
319
|
+
|
320
|
+
content = message.get("content", "")
|
321
|
+
|
322
|
+
# Prepare content as a string
|
323
|
+
if isinstance(content, list):
|
324
|
+
content_str = ", ".join(
|
325
|
+
# pylint: disable=line-too-long
|
326
|
+
f'{item.get("type", "text")}: {item.get("text", item.get("image_url", "").get("url", "") if isinstance(item.get("image_url", ""), dict) else item.get("image_url", ""))}'
|
327
|
+
for item in content
|
328
|
+
)
|
329
|
+
else:
|
330
|
+
content_str = content
|
331
|
+
|
332
|
+
# Set the role in the formatted message and concatenate content
|
333
|
+
if not formatted_messages[role]["role"]:
|
334
|
+
formatted_messages[role]["role"] = role
|
335
|
+
|
336
|
+
if formatted_messages[role]["content"]:
|
337
|
+
formatted_messages[role]["content"] += " " + content_str
|
338
|
+
else:
|
339
|
+
formatted_messages[role]["content"] = content_str
|
340
|
+
|
341
|
+
return formatted_messages
|
342
|
+
|
343
|
+
# To be removed one the change to log events (from span events) is complete
|
344
|
+
def concatenate_all_contents(formatted_messages):
|
345
|
+
"""
|
346
|
+
Concatenate all 'content' fields from the formatted messages
|
347
|
+
dictionary into a single string.
|
348
|
+
|
349
|
+
Parameters:
|
350
|
+
- formatted_messages: Dictionary with roles as keys and corresponding
|
351
|
+
role and content as values.
|
352
|
+
|
353
|
+
Returns:
|
354
|
+
- A single string with all content concatenated.
|
355
|
+
"""
|
356
|
+
return " ".join(
|
357
|
+
message_data['content']
|
358
|
+
for message_data in formatted_messages.values()
|
359
|
+
if message_data['content']
|
360
|
+
)
|
openlit/__init__.py
CHANGED
@@ -17,12 +17,13 @@ import requests
|
|
17
17
|
# Import internal modules for setting up tracing and fetching pricing info.
|
18
18
|
from opentelemetry import trace as t
|
19
19
|
from opentelemetry.trace import SpanKind, Status, StatusCode, Span
|
20
|
+
from opentelemetry.sdk.resources import SERVICE_NAME, DEPLOYMENT_ENVIRONMENT
|
20
21
|
from openlit.semcov import SemanticConvetion
|
21
22
|
from openlit.otel.tracing import setup_tracing
|
22
23
|
from openlit.otel.metrics import setup_meter
|
24
|
+
from openlit.otel.events import setup_events
|
23
25
|
from openlit.__helpers import fetch_pricing_info, get_env_variable
|
24
26
|
|
25
|
-
|
26
27
|
# Instrumentors for various large language models.
|
27
28
|
from openlit.instrumentation.openai import OpenAIInstrumentor
|
28
29
|
from openlit.instrumentation.anthropic import AnthropicInstrumentor
|
@@ -84,10 +85,11 @@ class OpenlitConfig:
|
|
84
85
|
application_name (str): Name of the application using openLIT.
|
85
86
|
pricing_info (Dict[str, Any]): Pricing information.
|
86
87
|
tracer (Optional[Any]): Tracer instance for OpenTelemetry.
|
88
|
+
event_provider (Optional[Any]): Event logger provider for OpenTelemetry.
|
87
89
|
otlp_endpoint (Optional[str]): Endpoint for OTLP.
|
88
90
|
otlp_headers (Optional[Dict[str, str]]): Headers for OTLP.
|
89
91
|
disable_batch (bool): Flag to disable batch span processing in tracing.
|
90
|
-
|
92
|
+
capture_message_content (bool): Flag to enable or disable tracing of content.
|
91
93
|
"""
|
92
94
|
|
93
95
|
_instance = None
|
@@ -106,11 +108,12 @@ class OpenlitConfig:
|
|
106
108
|
cls.application_name = "default"
|
107
109
|
cls.pricing_info = {}
|
108
110
|
cls.tracer = None
|
111
|
+
cls.event_provider = None
|
109
112
|
cls.metrics_dict = {}
|
110
113
|
cls.otlp_endpoint = None
|
111
114
|
cls.otlp_headers = None
|
112
115
|
cls.disable_batch = False
|
113
|
-
cls.
|
116
|
+
cls.capture_message_content = True
|
114
117
|
cls.disable_metrics = False
|
115
118
|
|
116
119
|
@classmethod
|
@@ -119,10 +122,11 @@ class OpenlitConfig:
|
|
119
122
|
environment,
|
120
123
|
application_name,
|
121
124
|
tracer,
|
125
|
+
event_provider,
|
122
126
|
otlp_endpoint,
|
123
127
|
otlp_headers,
|
124
128
|
disable_batch,
|
125
|
-
|
129
|
+
capture_message_content,
|
126
130
|
metrics_dict,
|
127
131
|
disable_metrics,
|
128
132
|
pricing_json,
|
@@ -134,22 +138,26 @@ class OpenlitConfig:
|
|
134
138
|
environment (str): Deployment environment.
|
135
139
|
application_name (str): Application name.
|
136
140
|
tracer: Tracer instance.
|
141
|
+
event_provider: Event logger provider instance.
|
137
142
|
meter: Metric Instance
|
138
143
|
otlp_endpoint (str): OTLP endpoint.
|
139
144
|
otlp_headers (Dict[str, str]): OTLP headers.
|
140
145
|
disable_batch (bool): Disable batch span processing flag.
|
141
|
-
|
146
|
+
capture_message_content (bool): Enable or disable content tracing.
|
147
|
+
metrics_dict: Dictionary of metrics.
|
148
|
+
disable_metrics (bool): Flag to disable metrics.
|
142
149
|
pricing_json(str): path or url to the pricing json file
|
143
150
|
"""
|
144
151
|
cls.environment = environment
|
145
152
|
cls.application_name = application_name
|
146
153
|
cls.pricing_info = fetch_pricing_info(pricing_json)
|
147
154
|
cls.tracer = tracer
|
155
|
+
cls.event_provider = event_provider
|
148
156
|
cls.metrics_dict = metrics_dict
|
149
157
|
cls.otlp_endpoint = otlp_endpoint
|
150
158
|
cls.otlp_headers = otlp_headers
|
151
159
|
cls.disable_batch = disable_batch
|
152
|
-
cls.
|
160
|
+
cls.capture_message_content = capture_message_content
|
153
161
|
cls.disable_metrics = disable_metrics
|
154
162
|
|
155
163
|
|
@@ -186,8 +194,9 @@ def instrument_if_available(
|
|
186
194
|
environment=config.environment,
|
187
195
|
application_name=config.application_name,
|
188
196
|
tracer=config.tracer,
|
197
|
+
event_provider=config.event_provider,
|
189
198
|
pricing_info=config.pricing_info,
|
190
|
-
|
199
|
+
capture_message_content=config.capture_message_content,
|
191
200
|
metrics_dict=config.metrics_dict,
|
192
201
|
disable_metrics=config.disable_metrics,
|
193
202
|
)
|
@@ -206,10 +215,11 @@ def init(
|
|
206
215
|
environment="default",
|
207
216
|
application_name="default",
|
208
217
|
tracer=None,
|
218
|
+
event_logger=None,
|
209
219
|
otlp_endpoint=None,
|
210
220
|
otlp_headers=None,
|
211
221
|
disable_batch=False,
|
212
|
-
|
222
|
+
capture_message_content=True,
|
213
223
|
disabled_instrumentors=None,
|
214
224
|
meter=None,
|
215
225
|
disable_metrics=False,
|
@@ -226,11 +236,12 @@ def init(
|
|
226
236
|
environment (str): Deployment environment.
|
227
237
|
application_name (str): Application name.
|
228
238
|
tracer: Tracer instance (Optional).
|
239
|
+
event_logger: EventLoggerProvider instance (Optional).
|
229
240
|
meter: OpenTelemetry Metrics Instance (Optional).
|
230
241
|
otlp_endpoint (str): OTLP endpoint for exporter (Optional).
|
231
242
|
otlp_headers (Dict[str, str]): OTLP headers for exporter (Optional).
|
232
243
|
disable_batch (bool): Flag to disable batch span processing (Optional).
|
233
|
-
|
244
|
+
capture_message_content (bool): Flag to trace content (Optional).
|
234
245
|
disabled_instrumentors (List[str]): Optional. List of instrumentor names to disable.
|
235
246
|
disable_metrics (bool): Flag to disable metrics (Optional).
|
236
247
|
pricing_json(str): File path or url to the pricing json (Optional).
|
@@ -307,9 +318,22 @@ def init(
|
|
307
318
|
)
|
308
319
|
|
309
320
|
if not tracer:
|
310
|
-
logger.error("
|
321
|
+
logger.error("OpenLIT tracing setup failed. Tracing will not be available.")
|
311
322
|
return
|
312
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
|
+
|
313
337
|
# Setup meter and receive metrics_dict instead of meter.
|
314
338
|
metrics_dict, err = setup_meter(
|
315
339
|
application_name=application_name,
|
@@ -325,15 +349,19 @@ def init(
|
|
325
349
|
)
|
326
350
|
return
|
327
351
|
|
352
|
+
if os.getenv("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", "").lower == "false":
|
353
|
+
capture_message_content=False
|
354
|
+
|
328
355
|
# Update global configuration with the provided settings.
|
329
356
|
config.update_config(
|
330
357
|
environment,
|
331
358
|
application_name,
|
332
359
|
tracer,
|
360
|
+
event_provider,
|
333
361
|
otlp_endpoint,
|
334
362
|
otlp_headers,
|
335
363
|
disable_batch,
|
336
|
-
|
364
|
+
capture_message_content,
|
337
365
|
metrics_dict,
|
338
366
|
disable_metrics,
|
339
367
|
pricing_json,
|
@@ -555,11 +583,11 @@ def trace(wrapped):
|
|
555
583
|
span.set_attribute("function.args", str(args))
|
556
584
|
span.set_attribute("function.kwargs", str(kwargs))
|
557
585
|
span.set_attribute(
|
558
|
-
|
586
|
+
SERVICE_NAME,
|
559
587
|
OpenlitConfig.application_name,
|
560
588
|
)
|
561
589
|
span.set_attribute(
|
562
|
-
|
590
|
+
DEPLOYMENT_ENVIRONMENT, OpenlitConfig.environment
|
563
591
|
)
|
564
592
|
except Exception as meta_exception:
|
565
593
|
logging.error(
|
@@ -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,7 +6,7 @@ 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
12
|
_instruments = ("ag2 >= 0.3.2",)
|
@@ -24,27 +23,27 @@ class AG2Instrumentor(BaseInstrumentor):
|
|
24
23
|
application_name = kwargs.get("application_name", "default_application")
|
25
24
|
environment = kwargs.get("environment", "default_environment")
|
26
25
|
tracer = kwargs.get("tracer")
|
26
|
+
event_provider = kwargs.get("event_provider")
|
27
27
|
metrics = kwargs.get("metrics_dict")
|
28
28
|
pricing_info = kwargs.get("pricing_info", {})
|
29
|
-
|
29
|
+
capture_message_content = kwargs.get("capture_message_content", False)
|
30
30
|
disable_metrics = kwargs.get("disable_metrics")
|
31
31
|
version = importlib.metadata.version("ag2")
|
32
32
|
|
33
33
|
wrap_function_wrapper(
|
34
34
|
"autogen.agentchat.conversable_agent",
|
35
|
-
"ConversableAgent.
|
36
|
-
|
37
|
-
tracer, pricing_info,
|
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
41
|
"autogen.agentchat.conversable_agent",
|
42
|
-
"ConversableAgent.
|
43
|
-
|
44
|
-
tracer, pricing_info,
|
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
|
@@ -1,98 +1,163 @@
|
|
1
|
-
# pylint: disable=duplicate-code, broad-exception-caught, too-many-statements, unused-argument
|
2
1
|
"""
|
3
|
-
Module for monitoring AG2.
|
2
|
+
Module for monitoring AG2 API calls.
|
4
3
|
"""
|
5
4
|
|
6
5
|
import logging
|
6
|
+
import time
|
7
7
|
from opentelemetry.trace import SpanKind, Status, StatusCode
|
8
|
-
from opentelemetry.sdk.resources import TELEMETRY_SDK_NAME
|
9
|
-
from openlit.__helpers import
|
8
|
+
from opentelemetry.sdk.resources import SERVICE_NAME, TELEMETRY_SDK_NAME, DEPLOYMENT_ENVIRONMENT
|
9
|
+
from openlit.__helpers import (
|
10
|
+
handle_exception,
|
11
|
+
get_chat_model_cost,
|
12
|
+
otel_event,
|
13
|
+
)
|
10
14
|
from openlit.semcov import SemanticConvetion
|
11
15
|
|
12
16
|
# Initialize logger for logging potential issues and operations
|
13
17
|
logger = logging.getLogger(__name__)
|
14
18
|
|
15
|
-
|
16
|
-
|
19
|
+
AGENT_NAME = ''
|
20
|
+
REQUEST_MODEL = ''
|
21
|
+
SYSTEM_MESSAGE = ''
|
22
|
+
MODEL_AND_NAME_SET = False
|
23
|
+
|
24
|
+
def set_span_attributes(span, version, operation_name, environment,
|
25
|
+
application_name, server_address, server_port, request_model):
|
26
|
+
"""
|
27
|
+
Set common attributes for the span.
|
28
|
+
"""
|
29
|
+
|
30
|
+
# Set Span attributes (OTel Semconv)
|
31
|
+
span.set_attribute(TELEMETRY_SDK_NAME, "openlit")
|
32
|
+
span.set_attribute(SemanticConvetion.GEN_AI_OPERATION, operation_name)
|
33
|
+
span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM, SemanticConvetion.GEN_AI_SYSTEM_AG2)
|
34
|
+
span.set_attribute(SemanticConvetion.GEN_AI_AGENT_NAME, AGENT_NAME)
|
35
|
+
span.set_attribute(SemanticConvetion.SERVER_ADDRESS, server_address)
|
36
|
+
span.set_attribute(SemanticConvetion.SERVER_PORT, server_port)
|
37
|
+
span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MODEL, request_model)
|
38
|
+
|
39
|
+
# Set Span attributes (Extras)
|
40
|
+
span.set_attribute(DEPLOYMENT_ENVIRONMENT, environment)
|
41
|
+
span.set_attribute(SERVICE_NAME, application_name)
|
42
|
+
span.set_attribute(SemanticConvetion.GEN_AI_SDK_VERSION, version)
|
43
|
+
|
44
|
+
def calculate_tokens_and_cost(response, request_model, pricing_info):
|
17
45
|
"""
|
18
|
-
|
19
|
-
|
20
|
-
This function wraps any given function to measure its execution time,
|
21
|
-
log its operation, and trace its execution using OpenTelemetry.
|
22
|
-
|
23
|
-
Parameters:
|
24
|
-
- gen_ai_endpoint (str): A descriptor or name for the endpoint being traced.
|
25
|
-
- version (str): The version of the Langchain application.
|
26
|
-
- environment (str): The deployment environment (e.g., 'production', 'development').
|
27
|
-
- application_name (str): Name of the Langchain application.
|
28
|
-
- tracer (opentelemetry.trace.Tracer): The tracer object used for OpenTelemetry tracing.
|
29
|
-
- pricing_info (dict): Information about the pricing for internal metrics (currently not used).
|
30
|
-
- trace_content (bool): Flag indicating whether to trace the content of the response.
|
31
|
-
|
32
|
-
Returns:
|
33
|
-
- function: A higher-order function that takes a function 'wrapped' and returns
|
34
|
-
a new function that wraps 'wrapped' with additional tracing and logging.
|
46
|
+
Calculate the input, output tokens, and their respective costs.
|
35
47
|
"""
|
48
|
+
input_tokens = 0
|
49
|
+
output_tokens = 0
|
36
50
|
|
51
|
+
for usage_data in response.cost.values():
|
52
|
+
if isinstance(usage_data, dict):
|
53
|
+
for model_data in usage_data.values():
|
54
|
+
if isinstance(model_data, dict):
|
55
|
+
input_tokens += model_data.get('prompt_tokens', 0)
|
56
|
+
output_tokens += model_data.get('completion_tokens', 0)
|
57
|
+
|
58
|
+
cost = get_chat_model_cost(request_model, pricing_info, input_tokens, output_tokens)
|
59
|
+
return input_tokens, output_tokens, cost
|
60
|
+
|
61
|
+
def emit_events(response, event_provider, capture_message_content):
|
62
|
+
"""
|
63
|
+
Emit OpenTelemetry events for each chat history entry.
|
64
|
+
"""
|
65
|
+
for chat in response.chat_history:
|
66
|
+
event_type = (
|
67
|
+
SemanticConvetion.GEN_AI_CHOICE if chat['role'] == 'user'
|
68
|
+
else SemanticConvetion.GEN_AI_USER_MESSAGE
|
69
|
+
)
|
70
|
+
choice_event = otel_event(
|
71
|
+
name=event_type,
|
72
|
+
attributes={
|
73
|
+
SemanticConvetion.GEN_AI_SYSTEM: SemanticConvetion.GEN_AI_SYSTEM_AG2
|
74
|
+
},
|
75
|
+
body={
|
76
|
+
"index": response.chat_history.index(chat),
|
77
|
+
"message": {
|
78
|
+
**({"content": chat['content']} if capture_message_content else {}),
|
79
|
+
"role": 'assistant' if chat['role'] == 'user' else 'user'
|
80
|
+
}
|
81
|
+
}
|
82
|
+
)
|
83
|
+
event_provider.emit(choice_event)
|
84
|
+
|
85
|
+
def conversable_agent(version, environment, application_name,
|
86
|
+
tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics):
|
87
|
+
"""
|
88
|
+
Generates a telemetry wrapper for GenAI function call
|
89
|
+
"""
|
37
90
|
def wrapper(wrapped, instance, args, kwargs):
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
this may be None.
|
47
|
-
- args (tuple): Positional arguments passed to the wrapped function.
|
48
|
-
- kwargs (dict): Keyword arguments passed to the wrapped function.
|
49
|
-
|
50
|
-
Returns:
|
51
|
-
- The result of the wrapped function call.
|
52
|
-
|
53
|
-
The wrapper initiates a span with the provided tracer, sets various attributes
|
54
|
-
on the span based on the function's execution and response, and ensures
|
55
|
-
errors are handled and logged appropriately.
|
56
|
-
"""
|
57
|
-
|
58
|
-
with tracer.start_as_current_span(gen_ai_endpoint, kind= SpanKind.CLIENT) as span:
|
59
|
-
response = wrapped(*args, **kwargs)
|
60
|
-
|
61
|
-
if isinstance(instance.__dict__.get('llm_config'), dict):
|
62
|
-
llm_model = instance.__dict__['llm_config'].get('model', 'gpt-4')
|
63
|
-
else:
|
64
|
-
# Fallback to default if 'llm_config' is not a dictionary
|
65
|
-
llm_model = None
|
91
|
+
server_address, server_port = '127.0.0.1', 80
|
92
|
+
global AGENT_NAME, MODEL_AND_NAME_SET, REQUEST_MODEL, SYSTEM_MESSAGE
|
93
|
+
|
94
|
+
if not MODEL_AND_NAME_SET:
|
95
|
+
AGENT_NAME = kwargs.get("name", "NOT_FOUND")
|
96
|
+
REQUEST_MODEL = kwargs.get("llm_config", {}).get('model', 'gpt-4o')
|
97
|
+
SYSTEM_MESSAGE = kwargs.get('system_message', '')
|
98
|
+
MODEL_AND_NAME_SET = True
|
66
99
|
|
100
|
+
span_name = f"{SemanticConvetion.GEN_AI_OPERATION_TYPE_CREATE_AGENT} {AGENT_NAME}"
|
101
|
+
|
102
|
+
with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
|
67
103
|
try:
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
span.set_attribute(SemanticConvetion.GEN_AI_SYSTEM,
|
72
|
-
SemanticConvetion.GEN_AI_SYSTEM_AG2)
|
73
|
-
span.set_attribute(SemanticConvetion.GEN_AI_ENVIRONMENT,
|
74
|
-
environment)
|
75
|
-
span.set_attribute(SemanticConvetion.GEN_AI_APPLICATION_NAME,
|
76
|
-
application_name)
|
77
|
-
span.set_attribute(SemanticConvetion.GEN_AI_OPERATION,
|
78
|
-
SemanticConvetion.GEN_AI_OPERATION_TYPE_AGENT)
|
79
|
-
span.set_attribute(SemanticConvetion.GEN_AI_AGENT_ROLE,
|
80
|
-
instance.name)
|
81
|
-
if llm_model:
|
82
|
-
span.set_attribute(SemanticConvetion.GEN_AI_REQUEST_MODEL,
|
83
|
-
llm_model)
|
104
|
+
start_time = time.time()
|
105
|
+
response = wrapped(*args, **kwargs)
|
106
|
+
end_time = time.time()
|
84
107
|
|
108
|
+
set_span_attributes(span, version, SemanticConvetion.GEN_AI_OPERATION_TYPE_CREATE_AGENT,
|
109
|
+
environment, application_name, server_address, server_port, REQUEST_MODEL)
|
110
|
+
span.set_attribute(SemanticConvetion.GEN_AI_AGENT_DESCRIPTION, SYSTEM_MESSAGE)
|
111
|
+
span.set_attribute(SemanticConvetion.GEN_AI_RESPONSE_MODEL, REQUEST_MODEL)
|
112
|
+
span.set_attribute(SemanticConvetion.GEN_AI_SERVER_TTFT, end_time - start_time)
|
85
113
|
|
86
114
|
span.set_status(Status(StatusCode.OK))
|
87
115
|
|
88
|
-
# Return original response
|
89
116
|
return response
|
90
117
|
|
91
118
|
except Exception as e:
|
92
119
|
handle_exception(span, e)
|
93
120
|
logger.error("Error in trace creation: %s", e)
|
121
|
+
return response
|
122
|
+
|
123
|
+
return wrapper
|
124
|
+
|
125
|
+
def agent_run(version, environment, application_name,
|
126
|
+
tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics):
|
127
|
+
"""
|
128
|
+
Generates a telemetry wrapper for GenAI function call
|
129
|
+
"""
|
130
|
+
def wrapper(wrapped, instance, args, kwargs):
|
131
|
+
server_address, server_port = '127.0.0.1', 80
|
132
|
+
|
133
|
+
span_name = f"{SemanticConvetion.GEN_AI_OPERATION_TYPE_EXECUTE_AGENT_TASK} {AGENT_NAME}"
|
134
|
+
|
135
|
+
with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
|
136
|
+
try:
|
137
|
+
start_time = time.time()
|
138
|
+
response = wrapped(*args, **kwargs)
|
139
|
+
end_time = time.time()
|
140
|
+
|
141
|
+
input_tokens, output_tokens, cost = calculate_tokens_and_cost(response, REQUEST_MODEL, pricing_info)
|
142
|
+
response_model = list(response.cost.get('usage_including_cached_inference', {}).keys())[1]
|
143
|
+
|
144
|
+
set_span_attributes(span, version, SemanticConvetion.GEN_AI_OPERATION_TYPE_EXECUTE_AGENT_TASK,
|
145
|
+
environment, application_name, server_address, server_port, REQUEST_MODEL)
|
146
|
+
span.set_attribute(SemanticConvetion.GEN_AI_RESPONSE_MODEL, response_model)
|
147
|
+
span.set_attribute(SemanticConvetion.GEN_AI_USAGE_INPUT_TOKENS, input_tokens)
|
148
|
+
span.set_attribute(SemanticConvetion.GEN_AI_USAGE_OUTPUT_TOKENS, output_tokens)
|
149
|
+
span.set_attribute(SemanticConvetion.GEN_AI_CLIENT_TOKEN_USAGE, input_tokens + output_tokens)
|
150
|
+
span.set_attribute(SemanticConvetion.GEN_AI_USAGE_COST, cost)
|
151
|
+
span.set_attribute(SemanticConvetion.GEN_AI_SERVER_TTFT, end_time - start_time)
|
152
|
+
|
153
|
+
emit_events(response, event_provider, capture_message_content)
|
154
|
+
span.set_status(Status(StatusCode.OK))
|
155
|
+
|
156
|
+
return response
|
94
157
|
|
95
|
-
|
158
|
+
except Exception as e:
|
159
|
+
handle_exception(span, e)
|
160
|
+
logger.error("Error in trace creation: %s", e)
|
96
161
|
return response
|
97
162
|
|
98
163
|
return wrapper
|
@@ -27,9 +27,10 @@ class AI21Instrumentor(BaseInstrumentor):
|
|
27
27
|
application_name = kwargs.get("application_name", "default_application")
|
28
28
|
environment = kwargs.get("environment", "default_environment")
|
29
29
|
tracer = kwargs.get("tracer")
|
30
|
+
event_provider = kwargs.get("event_provider")
|
30
31
|
metrics = kwargs.get("metrics_dict")
|
31
32
|
pricing_info = kwargs.get("pricing_info", {})
|
32
|
-
|
33
|
+
capture_message_content = kwargs.get("capture_message_content", False)
|
33
34
|
disable_metrics = kwargs.get("disable_metrics")
|
34
35
|
version = importlib.metadata.version("ai21")
|
35
36
|
|
@@ -38,13 +39,13 @@ class AI21Instrumentor(BaseInstrumentor):
|
|
38
39
|
"ai21.clients.studio.resources.chat.chat_completions",
|
39
40
|
"ChatCompletions.create",
|
40
41
|
chat(version, environment, application_name,
|
41
|
-
tracer, pricing_info,
|
42
|
+
tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
|
42
43
|
)
|
43
44
|
wrap_function_wrapper(
|
44
45
|
"ai21.clients.studio.resources.studio_conversational_rag",
|
45
46
|
"StudioConversationalRag.create",
|
46
47
|
chat_rag(version, environment, application_name,
|
47
|
-
tracer, pricing_info,
|
48
|
+
tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
|
48
49
|
)
|
49
50
|
|
50
51
|
#Async
|
@@ -52,13 +53,13 @@ class AI21Instrumentor(BaseInstrumentor):
|
|
52
53
|
"ai21.clients.studio.resources.chat.async_chat_completions",
|
53
54
|
"AsyncChatCompletions.create",
|
54
55
|
async_chat(version, environment, application_name,
|
55
|
-
tracer, pricing_info,
|
56
|
+
tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
|
56
57
|
)
|
57
58
|
wrap_function_wrapper(
|
58
59
|
"ai21.clients.studio.resources.studio_conversational_rag",
|
59
60
|
"AsyncStudioConversationalRag.create",
|
60
61
|
async_chat_rag(version, environment, application_name,
|
61
|
-
tracer, pricing_info,
|
62
|
+
tracer, event_provider, pricing_info, capture_message_content, metrics, disable_metrics),
|
62
63
|
)
|
63
64
|
|
64
65
|
def _uninstrument(self, **kwargs):
|