openlit 1.34.30__py3-none-any.whl → 1.34.31__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 +235 -86
- openlit/__init__.py +16 -13
- openlit/_instrumentors.py +2 -1
- openlit/evals/all.py +50 -21
- openlit/evals/bias_detection.py +47 -20
- openlit/evals/hallucination.py +53 -22
- openlit/evals/toxicity.py +50 -21
- openlit/evals/utils.py +54 -30
- openlit/guard/all.py +61 -19
- openlit/guard/prompt_injection.py +34 -14
- openlit/guard/restrict_topic.py +46 -15
- openlit/guard/sensitive_topic.py +34 -14
- openlit/guard/utils.py +58 -22
- openlit/instrumentation/ag2/__init__.py +24 -8
- openlit/instrumentation/ag2/ag2.py +34 -13
- openlit/instrumentation/ag2/async_ag2.py +34 -13
- openlit/instrumentation/ag2/utils.py +133 -30
- openlit/instrumentation/ai21/__init__.py +43 -14
- openlit/instrumentation/ai21/ai21.py +47 -21
- openlit/instrumentation/ai21/async_ai21.py +47 -21
- openlit/instrumentation/ai21/utils.py +299 -78
- openlit/instrumentation/anthropic/__init__.py +21 -4
- openlit/instrumentation/anthropic/anthropic.py +28 -17
- openlit/instrumentation/anthropic/async_anthropic.py +28 -17
- openlit/instrumentation/anthropic/utils.py +145 -35
- openlit/instrumentation/assemblyai/__init__.py +11 -2
- openlit/instrumentation/assemblyai/assemblyai.py +15 -4
- openlit/instrumentation/assemblyai/utils.py +120 -25
- openlit/instrumentation/astra/__init__.py +43 -10
- openlit/instrumentation/astra/astra.py +28 -5
- openlit/instrumentation/astra/async_astra.py +28 -5
- openlit/instrumentation/astra/utils.py +151 -55
- openlit/instrumentation/azure_ai_inference/__init__.py +43 -10
- openlit/instrumentation/azure_ai_inference/async_azure_ai_inference.py +53 -21
- openlit/instrumentation/azure_ai_inference/azure_ai_inference.py +53 -21
- openlit/instrumentation/azure_ai_inference/utils.py +307 -83
- openlit/instrumentation/bedrock/__init__.py +21 -4
- openlit/instrumentation/bedrock/bedrock.py +63 -25
- openlit/instrumentation/bedrock/utils.py +139 -30
- openlit/instrumentation/chroma/__init__.py +89 -16
- openlit/instrumentation/chroma/chroma.py +28 -6
- openlit/instrumentation/chroma/utils.py +167 -51
- openlit/instrumentation/cohere/__init__.py +63 -18
- openlit/instrumentation/cohere/async_cohere.py +63 -24
- openlit/instrumentation/cohere/cohere.py +63 -24
- openlit/instrumentation/cohere/utils.py +286 -73
- openlit/instrumentation/controlflow/__init__.py +35 -9
- openlit/instrumentation/controlflow/controlflow.py +66 -33
- openlit/instrumentation/crawl4ai/__init__.py +25 -10
- openlit/instrumentation/crawl4ai/async_crawl4ai.py +78 -31
- openlit/instrumentation/crawl4ai/crawl4ai.py +78 -31
- openlit/instrumentation/crewai/__init__.py +40 -15
- openlit/instrumentation/crewai/async_crewai.py +32 -7
- openlit/instrumentation/crewai/crewai.py +32 -7
- openlit/instrumentation/crewai/utils.py +159 -56
- openlit/instrumentation/dynamiq/__init__.py +46 -12
- openlit/instrumentation/dynamiq/dynamiq.py +74 -33
- openlit/instrumentation/elevenlabs/__init__.py +23 -4
- openlit/instrumentation/elevenlabs/async_elevenlabs.py +16 -4
- openlit/instrumentation/elevenlabs/elevenlabs.py +16 -4
- openlit/instrumentation/elevenlabs/utils.py +128 -25
- openlit/instrumentation/embedchain/__init__.py +11 -2
- openlit/instrumentation/embedchain/embedchain.py +68 -35
- openlit/instrumentation/firecrawl/__init__.py +24 -7
- openlit/instrumentation/firecrawl/firecrawl.py +46 -20
- openlit/instrumentation/google_ai_studio/__init__.py +45 -10
- openlit/instrumentation/google_ai_studio/async_google_ai_studio.py +67 -44
- openlit/instrumentation/google_ai_studio/google_ai_studio.py +67 -44
- openlit/instrumentation/google_ai_studio/utils.py +180 -67
- openlit/instrumentation/gpt4all/__init__.py +22 -7
- openlit/instrumentation/gpt4all/gpt4all.py +67 -29
- openlit/instrumentation/gpt4all/utils.py +285 -61
- openlit/instrumentation/gpu/__init__.py +128 -47
- openlit/instrumentation/groq/__init__.py +21 -4
- openlit/instrumentation/groq/async_groq.py +33 -21
- openlit/instrumentation/groq/groq.py +33 -21
- openlit/instrumentation/groq/utils.py +192 -55
- openlit/instrumentation/haystack/__init__.py +70 -24
- openlit/instrumentation/haystack/async_haystack.py +28 -6
- openlit/instrumentation/haystack/haystack.py +28 -6
- openlit/instrumentation/haystack/utils.py +196 -74
- openlit/instrumentation/julep/__init__.py +69 -19
- openlit/instrumentation/julep/async_julep.py +53 -27
- openlit/instrumentation/julep/julep.py +53 -28
- openlit/instrumentation/langchain/__init__.py +74 -63
- openlit/instrumentation/langchain/callback_handler.py +1100 -0
- openlit/instrumentation/langchain_community/__init__.py +13 -2
- openlit/instrumentation/langchain_community/async_langchain_community.py +23 -5
- openlit/instrumentation/langchain_community/langchain_community.py +23 -5
- openlit/instrumentation/langchain_community/utils.py +35 -9
- openlit/instrumentation/letta/__init__.py +68 -15
- openlit/instrumentation/letta/letta.py +99 -54
- openlit/instrumentation/litellm/__init__.py +43 -14
- openlit/instrumentation/litellm/async_litellm.py +51 -26
- openlit/instrumentation/litellm/litellm.py +51 -26
- openlit/instrumentation/litellm/utils.py +304 -102
- openlit/instrumentation/llamaindex/__init__.py +267 -90
- openlit/instrumentation/llamaindex/async_llamaindex.py +28 -6
- openlit/instrumentation/llamaindex/llamaindex.py +28 -6
- openlit/instrumentation/llamaindex/utils.py +204 -91
- openlit/instrumentation/mem0/__init__.py +11 -2
- openlit/instrumentation/mem0/mem0.py +50 -29
- openlit/instrumentation/milvus/__init__.py +10 -2
- openlit/instrumentation/milvus/milvus.py +31 -6
- openlit/instrumentation/milvus/utils.py +166 -67
- openlit/instrumentation/mistral/__init__.py +63 -18
- openlit/instrumentation/mistral/async_mistral.py +63 -24
- openlit/instrumentation/mistral/mistral.py +63 -24
- openlit/instrumentation/mistral/utils.py +277 -69
- openlit/instrumentation/multion/__init__.py +69 -19
- openlit/instrumentation/multion/async_multion.py +57 -26
- openlit/instrumentation/multion/multion.py +57 -26
- openlit/instrumentation/ollama/__init__.py +39 -18
- openlit/instrumentation/ollama/async_ollama.py +57 -26
- openlit/instrumentation/ollama/ollama.py +57 -26
- openlit/instrumentation/ollama/utils.py +226 -50
- openlit/instrumentation/openai/__init__.py +156 -32
- openlit/instrumentation/openai/async_openai.py +147 -67
- openlit/instrumentation/openai/openai.py +150 -67
- openlit/instrumentation/openai/utils.py +657 -185
- openlit/instrumentation/openai_agents/__init__.py +5 -1
- openlit/instrumentation/openai_agents/processor.py +110 -90
- openlit/instrumentation/phidata/__init__.py +13 -5
- openlit/instrumentation/phidata/phidata.py +67 -32
- openlit/instrumentation/pinecone/__init__.py +48 -9
- openlit/instrumentation/pinecone/async_pinecone.py +27 -5
- openlit/instrumentation/pinecone/pinecone.py +27 -5
- openlit/instrumentation/pinecone/utils.py +153 -47
- openlit/instrumentation/premai/__init__.py +22 -7
- openlit/instrumentation/premai/premai.py +51 -26
- openlit/instrumentation/premai/utils.py +246 -59
- openlit/instrumentation/pydantic_ai/__init__.py +49 -22
- openlit/instrumentation/pydantic_ai/pydantic_ai.py +69 -16
- openlit/instrumentation/pydantic_ai/utils.py +89 -24
- openlit/instrumentation/qdrant/__init__.py +19 -4
- openlit/instrumentation/qdrant/async_qdrant.py +33 -7
- openlit/instrumentation/qdrant/qdrant.py +33 -7
- openlit/instrumentation/qdrant/utils.py +228 -93
- openlit/instrumentation/reka/__init__.py +23 -10
- openlit/instrumentation/reka/async_reka.py +17 -11
- openlit/instrumentation/reka/reka.py +17 -11
- openlit/instrumentation/reka/utils.py +138 -36
- openlit/instrumentation/together/__init__.py +44 -12
- openlit/instrumentation/together/async_together.py +50 -27
- openlit/instrumentation/together/together.py +50 -27
- openlit/instrumentation/together/utils.py +301 -71
- openlit/instrumentation/transformers/__init__.py +2 -1
- openlit/instrumentation/transformers/transformers.py +13 -3
- openlit/instrumentation/transformers/utils.py +139 -36
- openlit/instrumentation/vertexai/__init__.py +81 -16
- openlit/instrumentation/vertexai/async_vertexai.py +33 -15
- openlit/instrumentation/vertexai/utils.py +123 -27
- openlit/instrumentation/vertexai/vertexai.py +33 -15
- openlit/instrumentation/vllm/__init__.py +12 -5
- openlit/instrumentation/vllm/utils.py +121 -31
- openlit/instrumentation/vllm/vllm.py +16 -10
- openlit/otel/events.py +35 -10
- openlit/otel/metrics.py +32 -24
- openlit/otel/tracing.py +24 -9
- openlit/semcov/__init__.py +72 -6
- {openlit-1.34.30.dist-info → openlit-1.34.31.dist-info}/METADATA +2 -1
- openlit-1.34.31.dist-info/RECORD +166 -0
- openlit/instrumentation/langchain/async_langchain.py +0 -102
- openlit/instrumentation/langchain/langchain.py +0 -102
- openlit/instrumentation/langchain/utils.py +0 -252
- openlit-1.34.30.dist-info/RECORD +0 -168
- {openlit-1.34.30.dist-info → openlit-1.34.31.dist-info}/LICENSE +0 -0
- {openlit-1.34.30.dist-info → openlit-1.34.31.dist-info}/WHEEL +0 -0
openlit/guard/utils.py
CHANGED
@@ -16,6 +16,7 @@ from openlit.semcov import SemanticConvention
|
|
16
16
|
# Initialize logger for logging potential issues and operations
|
17
17
|
logger = logging.getLogger(__name__)
|
18
18
|
|
19
|
+
|
19
20
|
class JsonOutput(BaseModel):
|
20
21
|
"""
|
21
22
|
A model representing the structure of JSON output for prompt injection detection.
|
@@ -34,13 +35,17 @@ class JsonOutput(BaseModel):
|
|
34
35
|
classification: str
|
35
36
|
explanation: str
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
|
39
|
+
def setup_provider(
|
40
|
+
provider: Optional[str],
|
41
|
+
api_key: Optional[str],
|
42
|
+
model: Optional[str],
|
43
|
+
base_url: Optional[str],
|
44
|
+
) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
40
45
|
"""Function to setup LLM provider"""
|
41
46
|
provider_configs = {
|
42
47
|
"openai": {"env_var": "OPENAI_API_KEY"},
|
43
|
-
"anthropic": {"env_var": "ANTHROPIC_API_KEY"}
|
48
|
+
"anthropic": {"env_var": "ANTHROPIC_API_KEY"},
|
44
49
|
}
|
45
50
|
|
46
51
|
if provider is None:
|
@@ -60,7 +65,9 @@ def setup_provider(provider: Optional[str], api_key: Optional[str],
|
|
60
65
|
|
61
66
|
if not api_key:
|
62
67
|
# pylint: disable=line-too-long
|
63
|
-
raise ValueError(
|
68
|
+
raise ValueError(
|
69
|
+
f"API key required via 'api_key' parameter or '{env_var}' environment variable"
|
70
|
+
)
|
64
71
|
|
65
72
|
return api_key, model, base_url
|
66
73
|
|
@@ -69,6 +76,7 @@ def format_prompt(system_prompt: str, text: str) -> str:
|
|
69
76
|
"""Function to format the prompt"""
|
70
77
|
return system_prompt.replace("{{prompt}}", text)
|
71
78
|
|
79
|
+
|
72
80
|
def llm_response(provider: str, prompt: str, model: str, base_url: str) -> str:
|
73
81
|
"""Function to get LLM response based on provider"""
|
74
82
|
# pylint: disable=no-else-return
|
@@ -79,6 +87,7 @@ def llm_response(provider: str, prompt: str, model: str, base_url: str) -> str:
|
|
79
87
|
else:
|
80
88
|
raise ValueError(f"Unsupported provider: {provider}")
|
81
89
|
|
90
|
+
|
82
91
|
def llm_response_openai(prompt: str, model: str, base_url: str) -> str:
|
83
92
|
"""Function to make LLM call to OpenAI"""
|
84
93
|
client = OpenAI(base_url=base_url)
|
@@ -95,10 +104,11 @@ def llm_response_openai(prompt: str, model: str, base_url: str) -> str:
|
|
95
104
|
{"role": "user", "content": prompt},
|
96
105
|
],
|
97
106
|
temperature=0.0,
|
98
|
-
response_format=JsonOutput
|
107
|
+
response_format=JsonOutput,
|
99
108
|
)
|
100
109
|
return response.choices[0].message.content
|
101
110
|
|
111
|
+
|
102
112
|
def llm_response_anthropic(prompt: str, model: str) -> str:
|
103
113
|
"""Function to make LLM call to Anthropic"""
|
104
114
|
client = Anthropic()
|
@@ -113,26 +123,42 @@ def llm_response_anthropic(prompt: str, model: str) -> str:
|
|
113
123
|
"input_schema": {
|
114
124
|
"type": "object",
|
115
125
|
"properties": {
|
116
|
-
"verdict": {
|
126
|
+
"verdict": {
|
127
|
+
"type": "string",
|
128
|
+
"description": "Verdict of guardrail",
|
129
|
+
},
|
117
130
|
"guard": {"type": "string", "description": "Type of guard"},
|
118
|
-
"score": {
|
119
|
-
|
120
|
-
|
131
|
+
"score": {
|
132
|
+
"type": "number",
|
133
|
+
"description": "Prompt score from Guard.",
|
134
|
+
},
|
135
|
+
"classification": {
|
136
|
+
"type": "string",
|
137
|
+
"description": "Incorrect prompt type",
|
138
|
+
},
|
139
|
+
"explanation": {
|
140
|
+
"type": "string",
|
141
|
+
"description": "Reason for classification",
|
142
|
+
},
|
121
143
|
},
|
122
|
-
"required": [
|
123
|
-
|
144
|
+
"required": [
|
145
|
+
"verdict",
|
146
|
+
"guard",
|
147
|
+
"score",
|
148
|
+
"classification",
|
149
|
+
"explanation",
|
150
|
+
],
|
151
|
+
},
|
124
152
|
}
|
125
153
|
]
|
126
154
|
|
127
155
|
response = client.messages.create(
|
128
156
|
model=model,
|
129
|
-
messages=[
|
130
|
-
{"role": "user", "content": prompt}
|
131
|
-
],
|
157
|
+
messages=[{"role": "user", "content": prompt}],
|
132
158
|
max_tokens=2000,
|
133
159
|
temperature=0.0,
|
134
160
|
tools=tools,
|
135
|
-
stream=False
|
161
|
+
stream=False,
|
136
162
|
)
|
137
163
|
|
138
164
|
for content in response.content:
|
@@ -142,6 +168,7 @@ def llm_response_anthropic(prompt: str, model: str) -> str:
|
|
142
168
|
|
143
169
|
return response
|
144
170
|
|
171
|
+
|
145
172
|
def parse_llm_response(response) -> JsonOutput:
|
146
173
|
"""
|
147
174
|
Parses the LLM response into a JsonOutput object.
|
@@ -163,8 +190,14 @@ def parse_llm_response(response) -> JsonOutput:
|
|
163
190
|
return JsonOutput(**data)
|
164
191
|
except (json.JSONDecodeError, TypeError) as e:
|
165
192
|
logger.error("Error parsing LLM response: '%s'", e)
|
166
|
-
return JsonOutput(
|
167
|
-
|
193
|
+
return JsonOutput(
|
194
|
+
score=0,
|
195
|
+
classification="none",
|
196
|
+
explanation="none",
|
197
|
+
verdict="none",
|
198
|
+
guard="none",
|
199
|
+
)
|
200
|
+
|
168
201
|
|
169
202
|
def custom_rule_detection(text: str, custom_rules: list) -> JsonOutput:
|
170
203
|
"""
|
@@ -183,10 +216,12 @@ def custom_rule_detection(text: str, custom_rules: list) -> JsonOutput:
|
|
183
216
|
guard=rule.get("guard", "prompt_injection"),
|
184
217
|
score=rule.get("score", 0.5),
|
185
218
|
classification=rule.get("classification", "custom"),
|
186
|
-
explanation=rule.get("explanation", "Matched custom rule pattern.")
|
219
|
+
explanation=rule.get("explanation", "Matched custom rule pattern."),
|
187
220
|
)
|
188
|
-
return JsonOutput(
|
189
|
-
|
221
|
+
return JsonOutput(
|
222
|
+
score=0, classification="none", explanation="none", verdict="none", guard="none"
|
223
|
+
)
|
224
|
+
|
190
225
|
|
191
226
|
def guard_metrics():
|
192
227
|
"""
|
@@ -204,11 +239,12 @@ def guard_metrics():
|
|
204
239
|
guard_requests = meter.create_counter(
|
205
240
|
name=SemanticConvention.GUARD_REQUESTS,
|
206
241
|
description="Counter for Guard requests",
|
207
|
-
unit="1"
|
242
|
+
unit="1",
|
208
243
|
)
|
209
244
|
|
210
245
|
return guard_requests
|
211
246
|
|
247
|
+
|
212
248
|
def guard_metric_attributes(verdict, score, validator, classification, explanation):
|
213
249
|
"""
|
214
250
|
Initializes OpenTelemetry attributes for metrics.
|
@@ -5,15 +5,15 @@ import importlib.metadata
|
|
5
5
|
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
6
6
|
from wrapt import wrap_function_wrapper
|
7
7
|
|
8
|
-
from openlit.instrumentation.ag2.ag2 import
|
9
|
-
conversable_agent, agent_run
|
10
|
-
)
|
8
|
+
from openlit.instrumentation.ag2.ag2 import conversable_agent, agent_run
|
11
9
|
from openlit.instrumentation.ag2.async_ag2 import (
|
12
|
-
async_conversable_agent,
|
10
|
+
async_conversable_agent,
|
11
|
+
async_agent_run,
|
13
12
|
)
|
14
13
|
|
15
14
|
_instruments = ("ag2 >= 0.3.2",)
|
16
15
|
|
16
|
+
|
17
17
|
class AG2Instrumentor(BaseInstrumentor):
|
18
18
|
"""
|
19
19
|
An instrumentor for AG2 client library.
|
@@ -36,16 +36,32 @@ class AG2Instrumentor(BaseInstrumentor):
|
|
36
36
|
wrap_function_wrapper(
|
37
37
|
"autogen.agentchat.conversable_agent",
|
38
38
|
"ConversableAgent.__init__",
|
39
|
-
conversable_agent(
|
40
|
-
|
39
|
+
conversable_agent(
|
40
|
+
version,
|
41
|
+
environment,
|
42
|
+
application_name,
|
43
|
+
tracer,
|
44
|
+
pricing_info,
|
45
|
+
capture_message_content,
|
46
|
+
metrics,
|
47
|
+
disable_metrics,
|
48
|
+
),
|
41
49
|
)
|
42
50
|
|
43
51
|
# sync agent run
|
44
52
|
wrap_function_wrapper(
|
45
53
|
"autogen.agentchat.conversable_agent",
|
46
54
|
"ConversableAgent.run",
|
47
|
-
agent_run(
|
48
|
-
|
55
|
+
agent_run(
|
56
|
+
version,
|
57
|
+
environment,
|
58
|
+
application_name,
|
59
|
+
tracer,
|
60
|
+
pricing_info,
|
61
|
+
capture_message_content,
|
62
|
+
metrics,
|
63
|
+
disable_metrics,
|
64
|
+
),
|
49
65
|
)
|
50
66
|
|
51
67
|
def _uninstrument(self, **kwargs):
|
@@ -4,18 +4,24 @@ Module for monitoring AG2 API calls.
|
|
4
4
|
|
5
5
|
import time
|
6
6
|
from opentelemetry.trace import SpanKind
|
7
|
-
from openlit.__helpers import
|
8
|
-
handle_exception,
|
9
|
-
set_server_address_and_port
|
10
|
-
)
|
7
|
+
from openlit.__helpers import handle_exception, set_server_address_and_port
|
11
8
|
from openlit.instrumentation.ag2.utils import (
|
12
9
|
process_agent_creation,
|
13
10
|
process_agent_run,
|
14
11
|
)
|
15
12
|
from openlit.semcov import SemanticConvention
|
16
13
|
|
17
|
-
|
18
|
-
|
14
|
+
|
15
|
+
def conversable_agent(
|
16
|
+
version,
|
17
|
+
environment,
|
18
|
+
application_name,
|
19
|
+
tracer,
|
20
|
+
pricing_info,
|
21
|
+
capture_message_content,
|
22
|
+
metrics,
|
23
|
+
disable_metrics,
|
24
|
+
):
|
19
25
|
"""
|
20
26
|
Generates a telemetry wrapper for AG2 conversable agent creation.
|
21
27
|
"""
|
@@ -25,12 +31,16 @@ def conversable_agent(version, environment, application_name, tracer, pricing_in
|
|
25
31
|
Wraps the AG2 conversable agent creation call.
|
26
32
|
"""
|
27
33
|
|
28
|
-
server_address, server_port = set_server_address_and_port(
|
34
|
+
server_address, server_port = set_server_address_and_port(
|
35
|
+
instance, "127.0.0.1", 80
|
36
|
+
)
|
29
37
|
agent_name = kwargs.get("name", "NOT_FOUND")
|
30
38
|
llm_config = kwargs.get("llm_config", {})
|
31
39
|
system_message = kwargs.get("system_message", "")
|
32
40
|
|
33
|
-
span_name =
|
41
|
+
span_name = (
|
42
|
+
f"{SemanticConvention.GEN_AI_OPERATION_TYPE_CREATE_AGENT} {agent_name}"
|
43
|
+
)
|
34
44
|
|
35
45
|
with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
|
36
46
|
start_time = time.time()
|
@@ -51,7 +61,7 @@ def conversable_agent(version, environment, application_name, tracer, pricing_in
|
|
51
61
|
span=span,
|
52
62
|
capture_message_content=capture_message_content,
|
53
63
|
disable_metrics=disable_metrics,
|
54
|
-
version=version
|
64
|
+
version=version,
|
55
65
|
)
|
56
66
|
|
57
67
|
except Exception as e:
|
@@ -61,8 +71,17 @@ def conversable_agent(version, environment, application_name, tracer, pricing_in
|
|
61
71
|
|
62
72
|
return wrapper
|
63
73
|
|
64
|
-
|
65
|
-
|
74
|
+
|
75
|
+
def agent_run(
|
76
|
+
version,
|
77
|
+
environment,
|
78
|
+
application_name,
|
79
|
+
tracer,
|
80
|
+
pricing_info,
|
81
|
+
capture_message_content,
|
82
|
+
metrics,
|
83
|
+
disable_metrics,
|
84
|
+
):
|
66
85
|
"""
|
67
86
|
Generates a telemetry wrapper for AG2 agent run execution.
|
68
87
|
"""
|
@@ -72,7 +91,9 @@ def agent_run(version, environment, application_name, tracer, pricing_info,
|
|
72
91
|
Wraps the AG2 agent run execution call.
|
73
92
|
"""
|
74
93
|
|
75
|
-
server_address, server_port = set_server_address_and_port(
|
94
|
+
server_address, server_port = set_server_address_and_port(
|
95
|
+
instance, "127.0.0.1", 80
|
96
|
+
)
|
76
97
|
|
77
98
|
# Extract agent name from instance
|
78
99
|
agent_name = getattr(instance, "name", "NOT_FOUND")
|
@@ -103,7 +124,7 @@ def agent_run(version, environment, application_name, tracer, pricing_info,
|
|
103
124
|
span=span,
|
104
125
|
capture_message_content=capture_message_content,
|
105
126
|
disable_metrics=disable_metrics,
|
106
|
-
version=version
|
127
|
+
version=version,
|
107
128
|
)
|
108
129
|
|
109
130
|
except Exception as e:
|
@@ -4,18 +4,24 @@ Module for monitoring AG2 API calls (async version).
|
|
4
4
|
|
5
5
|
import time
|
6
6
|
from opentelemetry.trace import SpanKind
|
7
|
-
from openlit.__helpers import
|
8
|
-
handle_exception,
|
9
|
-
set_server_address_and_port
|
10
|
-
)
|
7
|
+
from openlit.__helpers import handle_exception, set_server_address_and_port
|
11
8
|
from openlit.instrumentation.ag2.utils import (
|
12
9
|
process_agent_creation,
|
13
10
|
process_agent_run,
|
14
11
|
)
|
15
12
|
from openlit.semcov import SemanticConvention
|
16
13
|
|
17
|
-
|
18
|
-
|
14
|
+
|
15
|
+
def async_conversable_agent(
|
16
|
+
version,
|
17
|
+
environment,
|
18
|
+
application_name,
|
19
|
+
tracer,
|
20
|
+
pricing_info,
|
21
|
+
capture_message_content,
|
22
|
+
metrics,
|
23
|
+
disable_metrics,
|
24
|
+
):
|
19
25
|
"""
|
20
26
|
Generates a telemetry wrapper for AG2 async conversable agent creation.
|
21
27
|
"""
|
@@ -25,12 +31,16 @@ def async_conversable_agent(version, environment, application_name, tracer, pric
|
|
25
31
|
Wraps the AG2 async conversable agent creation call.
|
26
32
|
"""
|
27
33
|
|
28
|
-
server_address, server_port = set_server_address_and_port(
|
34
|
+
server_address, server_port = set_server_address_and_port(
|
35
|
+
instance, "127.0.0.1", 80
|
36
|
+
)
|
29
37
|
agent_name = kwargs.get("name", "NOT_FOUND")
|
30
38
|
llm_config = kwargs.get("llm_config", {})
|
31
39
|
system_message = kwargs.get("system_message", "")
|
32
40
|
|
33
|
-
span_name =
|
41
|
+
span_name = (
|
42
|
+
f"{SemanticConvention.GEN_AI_OPERATION_TYPE_CREATE_AGENT} {agent_name}"
|
43
|
+
)
|
34
44
|
|
35
45
|
with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
|
36
46
|
start_time = time.time()
|
@@ -51,7 +61,7 @@ def async_conversable_agent(version, environment, application_name, tracer, pric
|
|
51
61
|
span=span,
|
52
62
|
capture_message_content=capture_message_content,
|
53
63
|
disable_metrics=disable_metrics,
|
54
|
-
version=version
|
64
|
+
version=version,
|
55
65
|
)
|
56
66
|
|
57
67
|
except Exception as e:
|
@@ -61,8 +71,17 @@ def async_conversable_agent(version, environment, application_name, tracer, pric
|
|
61
71
|
|
62
72
|
return wrapper
|
63
73
|
|
64
|
-
|
65
|
-
|
74
|
+
|
75
|
+
def async_agent_run(
|
76
|
+
version,
|
77
|
+
environment,
|
78
|
+
application_name,
|
79
|
+
tracer,
|
80
|
+
pricing_info,
|
81
|
+
capture_message_content,
|
82
|
+
metrics,
|
83
|
+
disable_metrics,
|
84
|
+
):
|
66
85
|
"""
|
67
86
|
Generates a telemetry wrapper for AG2 async agent run execution.
|
68
87
|
"""
|
@@ -72,7 +91,9 @@ def async_agent_run(version, environment, application_name, tracer, pricing_info
|
|
72
91
|
Wraps the AG2 async agent run execution call.
|
73
92
|
"""
|
74
93
|
|
75
|
-
server_address, server_port = set_server_address_and_port(
|
94
|
+
server_address, server_port = set_server_address_and_port(
|
95
|
+
instance, "127.0.0.1", 80
|
96
|
+
)
|
76
97
|
|
77
98
|
# Extract agent name from instance
|
78
99
|
agent_name = getattr(instance, "name", "NOT_FOUND")
|
@@ -103,7 +124,7 @@ def async_agent_run(version, environment, application_name, tracer, pricing_info
|
|
103
124
|
span=span,
|
104
125
|
capture_message_content=capture_message_content,
|
105
126
|
disable_metrics=disable_metrics,
|
106
|
-
version=version
|
127
|
+
version=version,
|
107
128
|
)
|
108
129
|
|
109
130
|
except Exception as e:
|
@@ -1,6 +1,7 @@
|
|
1
1
|
"""
|
2
2
|
AG2 OpenTelemetry instrumentation utility functions
|
3
3
|
"""
|
4
|
+
|
4
5
|
import time
|
5
6
|
|
6
7
|
from opentelemetry.trace import Status, StatusCode
|
@@ -12,6 +13,7 @@ from openlit.__helpers import (
|
|
12
13
|
)
|
13
14
|
from openlit.semcov import SemanticConvention
|
14
15
|
|
16
|
+
|
15
17
|
def calculate_tokens_and_cost(response, request_model, pricing_info):
|
16
18
|
"""
|
17
19
|
Calculate the input, output tokens, and their respective costs from AG2 response.
|
@@ -21,7 +23,9 @@ def calculate_tokens_and_cost(response, request_model, pricing_info):
|
|
21
23
|
|
22
24
|
# Early return if response doesn't have cost data
|
23
25
|
if not hasattr(response, "cost") or response.cost is None:
|
24
|
-
cost = get_chat_model_cost(
|
26
|
+
cost = get_chat_model_cost(
|
27
|
+
request_model, pricing_info, input_tokens, output_tokens
|
28
|
+
)
|
25
29
|
return input_tokens, output_tokens, cost
|
26
30
|
|
27
31
|
try:
|
@@ -34,6 +38,7 @@ def calculate_tokens_and_cost(response, request_model, pricing_info):
|
|
34
38
|
cost = get_chat_model_cost(request_model, pricing_info, input_tokens, output_tokens)
|
35
39
|
return input_tokens, output_tokens, cost
|
36
40
|
|
41
|
+
|
37
42
|
def _extract_tokens_from_cost(cost_data):
|
38
43
|
"""
|
39
44
|
Extract input and output tokens from AG2 cost data structure.
|
@@ -52,6 +57,7 @@ def _extract_tokens_from_cost(cost_data):
|
|
52
57
|
|
53
58
|
return input_tokens, output_tokens
|
54
59
|
|
60
|
+
|
55
61
|
def format_content(chat_history):
|
56
62
|
"""
|
57
63
|
Format the chat history into a string for span events.
|
@@ -67,32 +73,62 @@ def format_content(chat_history):
|
|
67
73
|
|
68
74
|
return "\n".join(formatted_messages)
|
69
75
|
|
70
|
-
|
71
|
-
|
76
|
+
|
77
|
+
def common_agent_logic(
|
78
|
+
scope,
|
79
|
+
pricing_info,
|
80
|
+
environment,
|
81
|
+
application_name,
|
82
|
+
metrics,
|
83
|
+
capture_message_content,
|
84
|
+
disable_metrics,
|
85
|
+
version,
|
86
|
+
operation_type,
|
87
|
+
):
|
72
88
|
"""
|
73
89
|
Process agent request and generate Telemetry
|
74
90
|
"""
|
75
91
|
|
76
92
|
# Common Span Attributes
|
77
|
-
common_span_attributes(
|
78
|
-
|
79
|
-
|
80
|
-
|
93
|
+
common_span_attributes(
|
94
|
+
scope,
|
95
|
+
operation_type,
|
96
|
+
SemanticConvention.GEN_AI_SYSTEM_AG2,
|
97
|
+
scope._server_address,
|
98
|
+
scope._server_port,
|
99
|
+
scope._request_model,
|
100
|
+
scope._response_model,
|
101
|
+
environment,
|
102
|
+
application_name,
|
103
|
+
False,
|
104
|
+
0,
|
105
|
+
scope._end_time - scope._start_time,
|
106
|
+
version,
|
107
|
+
)
|
81
108
|
|
82
109
|
# Span Attributes for Agent-specific parameters
|
83
110
|
scope._span.set_attribute(SemanticConvention.GEN_AI_AGENT_NAME, scope._agent_name)
|
84
111
|
|
85
112
|
# Span Attributes for Response parameters
|
86
113
|
if hasattr(scope, "_input_tokens"):
|
87
|
-
scope._span.set_attribute(
|
88
|
-
|
89
|
-
|
114
|
+
scope._span.set_attribute(
|
115
|
+
SemanticConvention.GEN_AI_USAGE_INPUT_TOKENS, scope._input_tokens
|
116
|
+
)
|
117
|
+
scope._span.set_attribute(
|
118
|
+
SemanticConvention.GEN_AI_USAGE_OUTPUT_TOKENS, scope._output_tokens
|
119
|
+
)
|
120
|
+
scope._span.set_attribute(
|
121
|
+
SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE,
|
122
|
+
scope._input_tokens + scope._output_tokens,
|
123
|
+
)
|
90
124
|
scope._span.set_attribute(SemanticConvention.GEN_AI_USAGE_COST, scope._cost)
|
91
125
|
|
92
126
|
# Span Attributes for Content
|
93
127
|
if capture_message_content and hasattr(scope, "_chat_history"):
|
94
128
|
chat_content = format_content(scope._chat_history)
|
95
|
-
scope._span.set_attribute(
|
129
|
+
scope._span.set_attribute(
|
130
|
+
SemanticConvention.GEN_AI_CONTENT_COMPLETION, chat_content
|
131
|
+
)
|
96
132
|
|
97
133
|
# To be removed once the change to span_attributes (from span events) is complete
|
98
134
|
scope._span.add_event(
|
@@ -104,20 +140,51 @@ def common_agent_logic(scope, pricing_info, environment, application_name, metri
|
|
104
140
|
|
105
141
|
# Set agent description for create agent operation
|
106
142
|
if hasattr(scope, "_system_message"):
|
107
|
-
scope._span.set_attribute(
|
143
|
+
scope._span.set_attribute(
|
144
|
+
SemanticConvention.GEN_AI_AGENT_DESCRIPTION, scope._system_message
|
145
|
+
)
|
108
146
|
|
109
147
|
scope._span.set_status(Status(StatusCode.OK))
|
110
148
|
|
111
149
|
# Metrics
|
112
150
|
if not disable_metrics and hasattr(scope, "_input_tokens"):
|
113
|
-
record_completion_metrics(
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
151
|
+
record_completion_metrics(
|
152
|
+
metrics,
|
153
|
+
operation_type,
|
154
|
+
SemanticConvention.GEN_AI_SYSTEM_AG2,
|
155
|
+
scope._server_address,
|
156
|
+
scope._server_port,
|
157
|
+
scope._request_model,
|
158
|
+
scope._response_model,
|
159
|
+
environment,
|
160
|
+
application_name,
|
161
|
+
scope._start_time,
|
162
|
+
scope._end_time,
|
163
|
+
scope._input_tokens,
|
164
|
+
scope._output_tokens,
|
165
|
+
scope._cost,
|
166
|
+
0,
|
167
|
+
scope._end_time - scope._start_time,
|
168
|
+
)
|
169
|
+
|
170
|
+
|
171
|
+
def process_agent_creation(
|
172
|
+
agent_name,
|
173
|
+
llm_config,
|
174
|
+
system_message,
|
175
|
+
pricing_info,
|
176
|
+
server_port,
|
177
|
+
server_address,
|
178
|
+
environment,
|
179
|
+
application_name,
|
180
|
+
metrics,
|
181
|
+
start_time,
|
182
|
+
span,
|
183
|
+
capture_message_content=False,
|
184
|
+
disable_metrics=False,
|
185
|
+
version="1.0.0",
|
186
|
+
**kwargs,
|
187
|
+
):
|
121
188
|
"""
|
122
189
|
Process agent creation and generate Telemetry
|
123
190
|
"""
|
@@ -134,12 +201,36 @@ def process_agent_creation(agent_name, llm_config, system_message, pricing_info,
|
|
134
201
|
scope._system_message = system_message
|
135
202
|
scope._server_address, scope._server_port = server_address, server_port
|
136
203
|
|
137
|
-
common_agent_logic(
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
204
|
+
common_agent_logic(
|
205
|
+
scope,
|
206
|
+
pricing_info,
|
207
|
+
environment,
|
208
|
+
application_name,
|
209
|
+
metrics,
|
210
|
+
capture_message_content,
|
211
|
+
disable_metrics,
|
212
|
+
version,
|
213
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_CREATE_AGENT,
|
214
|
+
)
|
215
|
+
|
216
|
+
|
217
|
+
def process_agent_run(
|
218
|
+
response,
|
219
|
+
agent_name,
|
220
|
+
request_model,
|
221
|
+
pricing_info,
|
222
|
+
server_port,
|
223
|
+
server_address,
|
224
|
+
environment,
|
225
|
+
application_name,
|
226
|
+
metrics,
|
227
|
+
start_time,
|
228
|
+
span,
|
229
|
+
capture_message_content=False,
|
230
|
+
disable_metrics=False,
|
231
|
+
version="1.0.0",
|
232
|
+
**kwargs,
|
233
|
+
):
|
143
234
|
"""
|
144
235
|
Process agent run and generate Telemetry
|
145
236
|
"""
|
@@ -157,19 +248,31 @@ def process_agent_run(response, agent_name, request_model, pricing_info, server_
|
|
157
248
|
|
158
249
|
# Calculate tokens and cost
|
159
250
|
scope._input_tokens, scope._output_tokens, scope._cost = calculate_tokens_and_cost(
|
160
|
-
response, request_model, pricing_info
|
251
|
+
response, request_model, pricing_info
|
252
|
+
)
|
161
253
|
|
162
254
|
# Extract response model from cost data
|
163
255
|
try:
|
164
256
|
if hasattr(response, "cost") and response.cost is not None:
|
165
257
|
cost_data = response.cost.get("usage_including_cached_inference", {})
|
166
|
-
scope._response_model =
|
258
|
+
scope._response_model = (
|
259
|
+
list(cost_data.keys())[1] if len(cost_data) > 1 else request_model
|
260
|
+
)
|
167
261
|
else:
|
168
262
|
scope._response_model = request_model
|
169
263
|
except (AttributeError, IndexError, KeyError, TypeError):
|
170
264
|
scope._response_model = request_model
|
171
265
|
|
172
|
-
common_agent_logic(
|
173
|
-
|
266
|
+
common_agent_logic(
|
267
|
+
scope,
|
268
|
+
pricing_info,
|
269
|
+
environment,
|
270
|
+
application_name,
|
271
|
+
metrics,
|
272
|
+
capture_message_content,
|
273
|
+
disable_metrics,
|
274
|
+
version,
|
275
|
+
SemanticConvention.GEN_AI_OPERATION_TYPE_EXECUTE_AGENT_TASK,
|
276
|
+
)
|
174
277
|
|
175
278
|
return response
|