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
@@ -12,8 +12,18 @@ from openlit.instrumentation.crewai.utils import (
|
|
12
12
|
set_server_address_and_port,
|
13
13
|
)
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
|
16
|
+
def async_general_wrap(
|
17
|
+
gen_ai_endpoint,
|
18
|
+
version,
|
19
|
+
environment,
|
20
|
+
application_name,
|
21
|
+
tracer,
|
22
|
+
pricing_info,
|
23
|
+
capture_message_content,
|
24
|
+
metrics,
|
25
|
+
disable_metrics,
|
26
|
+
):
|
17
27
|
"""
|
18
28
|
Modern async wrapper for CrewAI operations following Framework Instrumentation Guide patterns.
|
19
29
|
"""
|
@@ -34,7 +44,9 @@ def async_general_wrap(gen_ai_endpoint, version, environment, application_name,
|
|
34
44
|
operation_type = OPERATION_MAP.get(gen_ai_endpoint, "framework")
|
35
45
|
|
36
46
|
# Generate span name following {operation_type} {operation_name} pattern
|
37
|
-
span_name = _generate_span_name(
|
47
|
+
span_name = _generate_span_name(
|
48
|
+
operation_type, gen_ai_endpoint, instance, args, kwargs
|
49
|
+
)
|
38
50
|
|
39
51
|
with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
|
40
52
|
start_time = time.time()
|
@@ -43,10 +55,22 @@ def async_general_wrap(gen_ai_endpoint, version, environment, application_name,
|
|
43
55
|
try:
|
44
56
|
# Process response and generate comprehensive telemetry
|
45
57
|
response = process_crewai_response(
|
46
|
-
response,
|
47
|
-
|
48
|
-
|
49
|
-
|
58
|
+
response,
|
59
|
+
operation_type,
|
60
|
+
server_address,
|
61
|
+
server_port,
|
62
|
+
environment,
|
63
|
+
application_name,
|
64
|
+
metrics,
|
65
|
+
start_time,
|
66
|
+
span,
|
67
|
+
capture_message_content,
|
68
|
+
disable_metrics,
|
69
|
+
version,
|
70
|
+
instance,
|
71
|
+
args,
|
72
|
+
endpoint=gen_ai_endpoint,
|
73
|
+
**kwargs,
|
50
74
|
)
|
51
75
|
|
52
76
|
except Exception as e:
|
@@ -56,6 +80,7 @@ def async_general_wrap(gen_ai_endpoint, version, environment, application_name,
|
|
56
80
|
|
57
81
|
return wrapper
|
58
82
|
|
83
|
+
|
59
84
|
def _generate_span_name(operation_type, endpoint, instance, args, kwargs):
|
60
85
|
"""
|
61
86
|
Generate proper span names following {operation_type} {operation_name} convention.
|
@@ -12,8 +12,18 @@ from openlit.instrumentation.crewai.utils import (
|
|
12
12
|
set_server_address_and_port,
|
13
13
|
)
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
|
16
|
+
def general_wrap(
|
17
|
+
gen_ai_endpoint,
|
18
|
+
version,
|
19
|
+
environment,
|
20
|
+
application_name,
|
21
|
+
tracer,
|
22
|
+
pricing_info,
|
23
|
+
capture_message_content,
|
24
|
+
metrics,
|
25
|
+
disable_metrics,
|
26
|
+
):
|
17
27
|
"""
|
18
28
|
Modern wrapper for CrewAI operations following Framework Instrumentation Guide patterns.
|
19
29
|
"""
|
@@ -34,7 +44,9 @@ def general_wrap(gen_ai_endpoint, version, environment, application_name,
|
|
34
44
|
operation_type = OPERATION_MAP.get(gen_ai_endpoint, "framework")
|
35
45
|
|
36
46
|
# Generate span name following {operation_type} {operation_name} pattern
|
37
|
-
span_name = _generate_span_name(
|
47
|
+
span_name = _generate_span_name(
|
48
|
+
operation_type, gen_ai_endpoint, instance, args, kwargs
|
49
|
+
)
|
38
50
|
|
39
51
|
with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
|
40
52
|
start_time = time.time()
|
@@ -43,10 +55,22 @@ def general_wrap(gen_ai_endpoint, version, environment, application_name,
|
|
43
55
|
try:
|
44
56
|
# Process response and generate comprehensive telemetry
|
45
57
|
response = process_crewai_response(
|
46
|
-
response,
|
47
|
-
|
48
|
-
|
49
|
-
|
58
|
+
response,
|
59
|
+
operation_type,
|
60
|
+
server_address,
|
61
|
+
server_port,
|
62
|
+
environment,
|
63
|
+
application_name,
|
64
|
+
metrics,
|
65
|
+
start_time,
|
66
|
+
span,
|
67
|
+
capture_message_content,
|
68
|
+
disable_metrics,
|
69
|
+
version,
|
70
|
+
instance,
|
71
|
+
args,
|
72
|
+
endpoint=gen_ai_endpoint,
|
73
|
+
**kwargs,
|
50
74
|
)
|
51
75
|
|
52
76
|
except Exception as e:
|
@@ -56,6 +80,7 @@ def general_wrap(gen_ai_endpoint, version, environment, application_name,
|
|
56
80
|
|
57
81
|
return wrapper
|
58
82
|
|
83
|
+
|
59
84
|
def _generate_span_name(operation_type, endpoint, instance, args, kwargs):
|
60
85
|
"""
|
61
86
|
Generate proper span names following {operation_type} {operation_name} convention.
|
@@ -20,28 +20,25 @@ OPERATION_MAP = {
|
|
20
20
|
"crew_train": "invoke_agent",
|
21
21
|
"crew_replay": "invoke_agent",
|
22
22
|
"crew_test": "invoke_agent",
|
23
|
-
|
24
23
|
# Agent Operations (core agent functions)
|
25
24
|
"agent___init__": "create_agent",
|
26
25
|
"agent_execute_task": "invoke_agent",
|
27
26
|
"agent_backstory_property": "invoke_agent",
|
28
|
-
|
29
27
|
# Task Operations (task execution)
|
30
28
|
"task_execute": "invoke_agent",
|
31
29
|
"task_execute_async": "invoke_agent",
|
32
30
|
"task_execute_core": "invoke_agent",
|
33
|
-
|
34
31
|
# Tool Operations (tool execution)
|
35
32
|
"tool_run": "execute_tool",
|
36
33
|
"tool___call__": "execute_tool",
|
37
34
|
"tool_execute": "execute_tool",
|
38
|
-
|
39
35
|
# Memory Operations (knowledge management)
|
40
36
|
"memory_save": "invoke_agent",
|
41
37
|
"memory_search": "invoke_agent",
|
42
|
-
"memory_reset": "invoke_agent"
|
38
|
+
"memory_reset": "invoke_agent",
|
43
39
|
}
|
44
40
|
|
41
|
+
|
45
42
|
def set_server_address_and_port(instance):
|
46
43
|
"""
|
47
44
|
Extract server information from CrewAI instance.
|
@@ -57,13 +54,13 @@ def set_server_address_and_port(instance):
|
|
57
54
|
|
58
55
|
# Try to extract LLM endpoint information
|
59
56
|
try:
|
60
|
-
if hasattr(instance,
|
57
|
+
if hasattr(instance, "llm") and hasattr(instance.llm, "api_base"):
|
61
58
|
parsed = urlparse(instance.llm.api_base)
|
62
59
|
server_address = parsed.hostname or "localhost"
|
63
60
|
server_port = parsed.port or 443
|
64
|
-
elif hasattr(instance,
|
61
|
+
elif hasattr(instance, "agent") and hasattr(instance.agent, "llm"):
|
65
62
|
# For tasks that have an agent with LLM
|
66
|
-
if hasattr(instance.agent.llm,
|
63
|
+
if hasattr(instance.agent.llm, "api_base"):
|
67
64
|
parsed = urlparse(instance.agent.llm.api_base)
|
68
65
|
server_address = parsed.hostname or "localhost"
|
69
66
|
server_port = parsed.port or 443
|
@@ -73,10 +70,25 @@ def set_server_address_and_port(instance):
|
|
73
70
|
|
74
71
|
return server_address, server_port
|
75
72
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
73
|
+
|
74
|
+
def process_crewai_response(
|
75
|
+
response,
|
76
|
+
operation_type,
|
77
|
+
server_address,
|
78
|
+
server_port,
|
79
|
+
environment,
|
80
|
+
application_name,
|
81
|
+
metrics,
|
82
|
+
start_time,
|
83
|
+
span,
|
84
|
+
capture_message_content,
|
85
|
+
disable_metrics,
|
86
|
+
version,
|
87
|
+
instance,
|
88
|
+
args,
|
89
|
+
endpoint=None,
|
90
|
+
**kwargs,
|
91
|
+
):
|
80
92
|
"""
|
81
93
|
Process CrewAI response with comprehensive business intelligence.
|
82
94
|
OpenLIT's competitive advantage through superior observability.
|
@@ -100,16 +112,19 @@ def process_crewai_response(response, operation_type, server_address, server_por
|
|
100
112
|
llm = getattr(instance, "llm", None)
|
101
113
|
if llm:
|
102
114
|
# Try different model attribute names used by different LLM libraries
|
103
|
-
request_model = (
|
104
|
-
|
105
|
-
|
106
|
-
|
115
|
+
request_model = (
|
116
|
+
getattr(llm, "model_name", None)
|
117
|
+
or getattr(llm, "model", None)
|
118
|
+
or getattr(llm, "_model_name", None)
|
119
|
+
or "unknown"
|
120
|
+
)
|
107
121
|
if request_model != "unknown":
|
108
122
|
request_model = str(request_model)
|
109
123
|
|
110
124
|
# Create a wrapper instance that exposes model_name for common_framework_span_attributes
|
111
125
|
class ModelWrapper:
|
112
126
|
"""Wrapper class to expose model_name for framework span attributes."""
|
127
|
+
|
113
128
|
def __init__(self, original_instance, model_name):
|
114
129
|
self._original = original_instance
|
115
130
|
self.model_name = model_name
|
@@ -125,8 +140,15 @@ def process_crewai_response(response, operation_type, server_address, server_por
|
|
125
140
|
|
126
141
|
# Set common framework span attributes
|
127
142
|
common_framework_span_attributes(
|
128
|
-
scope,
|
129
|
-
|
143
|
+
scope,
|
144
|
+
SemanticConvention.GEN_AI_SYSTEM_CREWAI,
|
145
|
+
server_address,
|
146
|
+
server_port,
|
147
|
+
environment,
|
148
|
+
application_name,
|
149
|
+
version,
|
150
|
+
endpoint,
|
151
|
+
model_instance,
|
130
152
|
)
|
131
153
|
|
132
154
|
# Set span name following OpenTelemetry format
|
@@ -156,11 +178,14 @@ def process_crewai_response(response, operation_type, server_address, server_por
|
|
156
178
|
|
157
179
|
# === RECORD METRICS ===
|
158
180
|
if not disable_metrics and metrics:
|
159
|
-
_record_crewai_metrics(
|
181
|
+
_record_crewai_metrics(
|
182
|
+
metrics, standard_operation, duration_ms, environment, application_name
|
183
|
+
)
|
160
184
|
|
161
185
|
span.set_status(Status(StatusCode.OK))
|
162
186
|
return response
|
163
187
|
|
188
|
+
|
164
189
|
def _set_span_name(span, operation_type, instance, endpoint, args, kwargs):
|
165
190
|
"""Set span name following OpenTelemetry format: '{operation_type} {name}'"""
|
166
191
|
try:
|
@@ -175,11 +200,15 @@ def _set_span_name(span, operation_type, instance, endpoint, args, kwargs):
|
|
175
200
|
elif endpoint.startswith("agent_"):
|
176
201
|
if "create" in endpoint or endpoint == "agent___init__":
|
177
202
|
# Agent creation: "create_agent {agent_name}"
|
178
|
-
agent_name = getattr(instance, "name", None) or getattr(
|
203
|
+
agent_name = getattr(instance, "name", None) or getattr(
|
204
|
+
instance, "role", "agent"
|
205
|
+
)
|
179
206
|
span.update_name(f"create_agent {agent_name}")
|
180
207
|
else:
|
181
208
|
# Agent invocation: "invoke_agent {agent_name}"
|
182
|
-
agent_name = getattr(instance, "name", None) or getattr(
|
209
|
+
agent_name = getattr(instance, "name", None) or getattr(
|
210
|
+
instance, "role", "agent"
|
211
|
+
)
|
183
212
|
span.update_name(f"invoke_agent {agent_name}")
|
184
213
|
|
185
214
|
elif endpoint.startswith("task_"):
|
@@ -188,7 +217,10 @@ def _set_span_name(span, operation_type, instance, endpoint, args, kwargs):
|
|
188
217
|
|
189
218
|
elif endpoint.startswith("tool_"):
|
190
219
|
# Tool operations: "execute_tool {tool_name}"
|
191
|
-
tool_name =
|
220
|
+
tool_name = (
|
221
|
+
getattr(instance, "name", None)
|
222
|
+
or getattr(instance, "__class__", type(instance)).__name__
|
223
|
+
)
|
192
224
|
span.update_name(f"execute_tool {tool_name}")
|
193
225
|
|
194
226
|
elif endpoint.startswith("memory_"):
|
@@ -205,6 +237,7 @@ def _set_span_name(span, operation_type, instance, endpoint, args, kwargs):
|
|
205
237
|
# Fallback naming
|
206
238
|
span.update_name(f"invoke_agent {endpoint}")
|
207
239
|
|
240
|
+
|
208
241
|
def _set_agent_business_intelligence(span, instance, endpoint, args, kwargs):
|
209
242
|
"""Set agent business intelligence using standard OpenTelemetry semantic conventions"""
|
210
243
|
if not endpoint.startswith("agent_"):
|
@@ -232,32 +265,46 @@ def _set_agent_business_intelligence(span, instance, endpoint, args, kwargs):
|
|
232
265
|
# Enhanced Agent Configuration Tracking using SemanticConvention
|
233
266
|
max_retry_limit = getattr(instance, "max_retry_limit", None)
|
234
267
|
if max_retry_limit is not None:
|
235
|
-
span.set_attribute(
|
268
|
+
span.set_attribute(
|
269
|
+
SemanticConvention.GEN_AI_AGENT_MAX_RETRY_LIMIT, max_retry_limit
|
270
|
+
)
|
236
271
|
|
237
272
|
allow_delegation = getattr(instance, "allow_delegation", None)
|
238
273
|
if allow_delegation is not None:
|
239
|
-
span.set_attribute(
|
274
|
+
span.set_attribute(
|
275
|
+
SemanticConvention.GEN_AI_AGENT_ALLOW_DELEGATION, allow_delegation
|
276
|
+
)
|
240
277
|
|
241
278
|
allow_code_execution = getattr(instance, "allow_code_execution", None)
|
242
279
|
if allow_code_execution is not None:
|
243
|
-
span.set_attribute(
|
280
|
+
span.set_attribute(
|
281
|
+
SemanticConvention.GEN_AI_AGENT_ALLOW_CODE_EXECUTION,
|
282
|
+
allow_code_execution,
|
283
|
+
)
|
244
284
|
|
245
285
|
# Tools tracking using SemanticConvention
|
246
286
|
tools = getattr(instance, "tools", [])
|
247
287
|
if tools:
|
248
|
-
tool_names = [
|
288
|
+
tool_names = [
|
289
|
+
getattr(tool, "name", str(tool)) for tool in tools[:5]
|
290
|
+
] # Limit to first 5
|
249
291
|
if tool_names:
|
250
|
-
span.set_attribute(
|
292
|
+
span.set_attribute(
|
293
|
+
SemanticConvention.GEN_AI_AGENT_TOOLS, ", ".join(tool_names)
|
294
|
+
)
|
251
295
|
|
252
296
|
# === OpenAI Agent-specific Attributes ===
|
253
297
|
_set_openai_agent_attributes(span, instance, endpoint, args, kwargs)
|
254
298
|
|
255
299
|
# === Conversation and Data Source Tracking ===
|
256
|
-
_set_conversation_and_data_source_attributes(
|
300
|
+
_set_conversation_and_data_source_attributes(
|
301
|
+
span, instance, endpoint, args, kwargs
|
302
|
+
)
|
257
303
|
|
258
304
|
except Exception as e:
|
259
305
|
handle_exception(span, e)
|
260
306
|
|
307
|
+
|
261
308
|
def _set_openai_agent_attributes(span, instance, endpoint, args, kwargs):
|
262
309
|
"""Set OpenAI-specific agent attributes when using OpenAI models"""
|
263
310
|
try:
|
@@ -276,16 +323,27 @@ def _set_openai_agent_attributes(span, instance, endpoint, args, kwargs):
|
|
276
323
|
# OpenAI service tier if available
|
277
324
|
service_tier = getattr(llm, "service_tier", None)
|
278
325
|
if service_tier:
|
279
|
-
span.set_attribute(
|
326
|
+
span.set_attribute(
|
327
|
+
SemanticConvention.GEN_AI_OPENAI_REQUEST_SERVICE_TIER,
|
328
|
+
service_tier,
|
329
|
+
)
|
280
330
|
|
281
331
|
# OpenAI Assistant API attributes if available
|
282
|
-
assistant_id = getattr(instance, "assistant_id", None) or kwargs.get(
|
332
|
+
assistant_id = getattr(instance, "assistant_id", None) or kwargs.get(
|
333
|
+
"assistant_id"
|
334
|
+
)
|
283
335
|
if assistant_id:
|
284
|
-
span.set_attribute(
|
336
|
+
span.set_attribute(
|
337
|
+
SemanticConvention.GEN_AI_OPENAI_ASSISTANT_ID, assistant_id
|
338
|
+
)
|
285
339
|
|
286
|
-
thread_id = getattr(instance, "thread_id", None) or kwargs.get(
|
340
|
+
thread_id = getattr(instance, "thread_id", None) or kwargs.get(
|
341
|
+
"thread_id"
|
342
|
+
)
|
287
343
|
if thread_id:
|
288
|
-
span.set_attribute(
|
344
|
+
span.set_attribute(
|
345
|
+
SemanticConvention.GEN_AI_OPENAI_THREAD_ID, thread_id
|
346
|
+
)
|
289
347
|
|
290
348
|
run_id = getattr(instance, "run_id", None) or kwargs.get("run_id")
|
291
349
|
if run_id:
|
@@ -299,18 +357,23 @@ def _set_openai_agent_attributes(span, instance, endpoint, args, kwargs):
|
|
299
357
|
except Exception as e:
|
300
358
|
handle_exception(span, e)
|
301
359
|
|
302
|
-
|
360
|
+
|
361
|
+
def _set_conversation_and_data_source_attributes(
|
362
|
+
span, instance, endpoint, args, kwargs
|
363
|
+
):
|
303
364
|
"""Set conversation tracking and data source attributes"""
|
304
365
|
try:
|
305
366
|
# Conversation ID for multi-turn interactions
|
306
367
|
conversation_id = (
|
307
|
-
getattr(instance, "conversation_id", None)
|
308
|
-
getattr(instance, "session_id", None)
|
309
|
-
kwargs.get("conversation_id")
|
310
|
-
kwargs.get("session_id")
|
368
|
+
getattr(instance, "conversation_id", None)
|
369
|
+
or getattr(instance, "session_id", None)
|
370
|
+
or kwargs.get("conversation_id")
|
371
|
+
or kwargs.get("session_id")
|
311
372
|
)
|
312
373
|
if conversation_id:
|
313
|
-
span.set_attribute(
|
374
|
+
span.set_attribute(
|
375
|
+
SemanticConvention.GEN_AI_CONVERSATION_ID, str(conversation_id)
|
376
|
+
)
|
314
377
|
|
315
378
|
# Data source tracking for RAG operations
|
316
379
|
memory = getattr(instance, "memory", None)
|
@@ -319,25 +382,37 @@ def _set_conversation_and_data_source_attributes(span, instance, endpoint, args,
|
|
319
382
|
memory_provider = getattr(memory, "provider", None)
|
320
383
|
if memory_provider:
|
321
384
|
span.set_attribute(SemanticConvention.GEN_AI_DATA_SOURCE_TYPE, "memory")
|
322
|
-
span.set_attribute(
|
385
|
+
span.set_attribute(
|
386
|
+
SemanticConvention.GEN_AI_DATA_SOURCE_ID, str(memory_provider)
|
387
|
+
)
|
323
388
|
|
324
389
|
# Knowledge base or vector store detection
|
325
390
|
knowledge_source = getattr(instance, "knowledge_source", None)
|
326
391
|
if knowledge_source:
|
327
|
-
span.set_attribute(
|
328
|
-
|
392
|
+
span.set_attribute(
|
393
|
+
SemanticConvention.GEN_AI_DATA_SOURCE_TYPE, "knowledge_base"
|
394
|
+
)
|
395
|
+
span.set_attribute(
|
396
|
+
SemanticConvention.GEN_AI_DATA_SOURCE_ID, str(knowledge_source)
|
397
|
+
)
|
329
398
|
|
330
399
|
# Tool-based data sources
|
331
400
|
tools = getattr(instance, "tools", [])
|
332
401
|
for tool in tools:
|
333
402
|
tool_name = getattr(tool, "name", "").lower()
|
334
|
-
if any(
|
335
|
-
|
403
|
+
if any(
|
404
|
+
keyword in tool_name
|
405
|
+
for keyword in ["search", "retrieval", "database", "vector"]
|
406
|
+
):
|
407
|
+
span.set_attribute(
|
408
|
+
SemanticConvention.GEN_AI_DATA_SOURCE_TYPE, "external_tool"
|
409
|
+
)
|
336
410
|
break
|
337
411
|
|
338
412
|
except Exception as e:
|
339
413
|
handle_exception(span, e)
|
340
414
|
|
415
|
+
|
341
416
|
def _set_task_business_intelligence(span, instance, endpoint, args, kwargs):
|
342
417
|
"""Set task business intelligence using standard OpenTelemetry semantic conventions"""
|
343
418
|
if not endpoint.startswith("task_"):
|
@@ -352,16 +427,21 @@ def _set_task_business_intelligence(span, instance, endpoint, args, kwargs):
|
|
352
427
|
# Task description
|
353
428
|
task_description = getattr(instance, "description", "")
|
354
429
|
if task_description:
|
355
|
-
span.set_attribute(
|
430
|
+
span.set_attribute(
|
431
|
+
SemanticConvention.GEN_AI_TASK_DESCRIPTION, task_description
|
432
|
+
)
|
356
433
|
|
357
434
|
# Task expected output (keep only essential attributes that have semantic conventions or are critical)
|
358
435
|
expected_output = getattr(instance, "expected_output", "")
|
359
436
|
if expected_output:
|
360
|
-
span.set_attribute(
|
437
|
+
span.set_attribute(
|
438
|
+
SemanticConvention.GEN_AI_TASK_EXPECTED_OUTPUT, expected_output
|
439
|
+
)
|
361
440
|
|
362
441
|
except Exception as e:
|
363
442
|
handle_exception(span, e)
|
364
443
|
|
444
|
+
|
365
445
|
def _set_crew_business_intelligence(span, instance, endpoint, args, kwargs):
|
366
446
|
"""Set crew business intelligence using standard OpenTelemetry semantic conventions"""
|
367
447
|
if not endpoint.startswith("crew_"):
|
@@ -374,6 +454,7 @@ def _set_crew_business_intelligence(span, instance, endpoint, args, kwargs):
|
|
374
454
|
except Exception as e:
|
375
455
|
handle_exception(span, e)
|
376
456
|
|
457
|
+
|
377
458
|
def _set_tool_business_intelligence(span, instance, endpoint, args, kwargs):
|
378
459
|
"""Set tool business intelligence using standard OpenTelemetry semantic conventions"""
|
379
460
|
if not endpoint.startswith("tool_"):
|
@@ -381,14 +462,19 @@ def _set_tool_business_intelligence(span, instance, endpoint, args, kwargs):
|
|
381
462
|
|
382
463
|
try:
|
383
464
|
# Standard OpenTelemetry Gen AI Tool attributes
|
384
|
-
tool_name =
|
465
|
+
tool_name = (
|
466
|
+
getattr(instance, "name", None)
|
467
|
+
or getattr(instance, "__class__", type(instance)).__name__
|
468
|
+
)
|
385
469
|
if tool_name:
|
386
470
|
span.set_attribute(SemanticConvention.GEN_AI_TOOL_NAME, tool_name)
|
387
471
|
|
388
472
|
# Tool call ID if available (for tracking specific tool invocations)
|
389
473
|
tool_call_id = kwargs.get("call_id", None) or getattr(instance, "call_id", None)
|
390
474
|
if tool_call_id:
|
391
|
-
span.set_attribute(
|
475
|
+
span.set_attribute(
|
476
|
+
SemanticConvention.GEN_AI_TOOL_CALL_ID, str(tool_call_id)
|
477
|
+
)
|
392
478
|
|
393
479
|
# === OpenAI Function Calling Attributes ===
|
394
480
|
_set_openai_tool_attributes(span, instance, endpoint, args, kwargs)
|
@@ -396,6 +482,7 @@ def _set_tool_business_intelligence(span, instance, endpoint, args, kwargs):
|
|
396
482
|
except Exception as e:
|
397
483
|
handle_exception(span, e)
|
398
484
|
|
485
|
+
|
399
486
|
def _set_openai_tool_attributes(span, instance, endpoint, args, kwargs):
|
400
487
|
"""Set OpenAI function calling specific attributes using standard conventions"""
|
401
488
|
try:
|
@@ -409,7 +496,9 @@ def _set_openai_tool_attributes(span, instance, endpoint, args, kwargs):
|
|
409
496
|
tool_type = "api_client"
|
410
497
|
elif any(keyword in tool_class for keyword in ["database", "sql", "query"]):
|
411
498
|
tool_type = "database"
|
412
|
-
elif any(
|
499
|
+
elif any(
|
500
|
+
keyword in tool_class for keyword in ["vector", "embedding", "retrieval"]
|
501
|
+
):
|
413
502
|
tool_type = "vector_store"
|
414
503
|
else:
|
415
504
|
tool_type = "custom"
|
@@ -420,6 +509,7 @@ def _set_openai_tool_attributes(span, instance, endpoint, args, kwargs):
|
|
420
509
|
except Exception as e:
|
421
510
|
handle_exception(span, e)
|
422
511
|
|
512
|
+
|
423
513
|
def _capture_content(span, instance, response, endpoint):
|
424
514
|
"""Capture input/output content with MIME types"""
|
425
515
|
|
@@ -429,8 +519,10 @@ def _capture_content(span, instance, response, endpoint):
|
|
429
519
|
span.add_event(
|
430
520
|
name=SemanticConvention.GEN_AI_CONTENT_COMPLETION_EVENT,
|
431
521
|
attributes={
|
432
|
-
SemanticConvention.GEN_AI_CONTENT_COMPLETION: str(response)[
|
433
|
-
|
522
|
+
SemanticConvention.GEN_AI_CONTENT_COMPLETION: str(response)[
|
523
|
+
:1000
|
524
|
+
], # Limit size
|
525
|
+
},
|
434
526
|
)
|
435
527
|
|
436
528
|
# Capture input content based on operation type
|
@@ -440,14 +532,17 @@ def _capture_content(span, instance, response, endpoint):
|
|
440
532
|
span.add_event(
|
441
533
|
name=SemanticConvention.GEN_AI_CONTENT_PROMPT_EVENT,
|
442
534
|
attributes={
|
443
|
-
SemanticConvention.GEN_AI_CONTENT_PROMPT: task_description[
|
444
|
-
|
535
|
+
SemanticConvention.GEN_AI_CONTENT_PROMPT: task_description[
|
536
|
+
:1000
|
537
|
+
],
|
538
|
+
},
|
445
539
|
)
|
446
540
|
|
447
541
|
except Exception:
|
448
542
|
# Graceful degradation
|
449
543
|
pass
|
450
544
|
|
545
|
+
|
451
546
|
def _track_cost_and_tokens(span, instance, response, endpoint):
|
452
547
|
"""Track cost and token usage for business intelligence"""
|
453
548
|
|
@@ -462,7 +557,9 @@ def _track_cost_and_tokens(span, instance, response, endpoint):
|
|
462
557
|
response_length = len(str(response))
|
463
558
|
# Estimate token count (rough approximation: 4 chars per token)
|
464
559
|
estimated_tokens = response_length // 4
|
465
|
-
span.set_attribute(
|
560
|
+
span.set_attribute(
|
561
|
+
SemanticConvention.GEN_AI_CLIENT_TOKEN_USAGE, estimated_tokens
|
562
|
+
)
|
466
563
|
|
467
564
|
# Cost estimation would require pricing information
|
468
565
|
# This could be enhanced with actual cost tracking
|
@@ -471,7 +568,10 @@ def _track_cost_and_tokens(span, instance, response, endpoint):
|
|
471
568
|
# Graceful degradation
|
472
569
|
pass
|
473
570
|
|
474
|
-
|
571
|
+
|
572
|
+
def _record_crewai_metrics(
|
573
|
+
metrics, operation_type, duration_ms, environment, application_name
|
574
|
+
):
|
475
575
|
"""Record CrewAI-specific metrics"""
|
476
576
|
|
477
577
|
try:
|
@@ -484,7 +584,9 @@ def _record_crewai_metrics(metrics, operation_type, duration_ms, environment, ap
|
|
484
584
|
|
485
585
|
# Record operation duration
|
486
586
|
if "genai_client_operation_duration" in metrics:
|
487
|
-
metrics["genai_client_operation_duration"].record(
|
587
|
+
metrics["genai_client_operation_duration"].record(
|
588
|
+
duration_ms / 1000, attributes
|
589
|
+
)
|
488
590
|
|
489
591
|
# Record operation count
|
490
592
|
if "genai_requests" in metrics:
|
@@ -494,6 +596,7 @@ def _record_crewai_metrics(metrics, operation_type, duration_ms, environment, ap
|
|
494
596
|
# Graceful degradation
|
495
597
|
pass
|
496
598
|
|
599
|
+
|
497
600
|
def _parse_tools(tools):
|
498
601
|
"""Parse tools list into JSON format"""
|
499
602
|
|