mseep-agentops 0.4.18__py3-none-any.whl → 0.4.23__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.
- agentops/__init__.py +0 -0
- agentops/client/api/base.py +28 -30
- agentops/client/api/versions/v3.py +29 -25
- agentops/client/api/versions/v4.py +87 -46
- agentops/client/client.py +98 -29
- agentops/client/http/README.md +87 -0
- agentops/client/http/http_client.py +126 -172
- agentops/config.py +8 -2
- agentops/instrumentation/OpenTelemetry.md +133 -0
- agentops/instrumentation/README.md +167 -0
- agentops/instrumentation/__init__.py +13 -1
- agentops/instrumentation/agentic/ag2/__init__.py +18 -0
- agentops/instrumentation/agentic/ag2/instrumentor.py +922 -0
- agentops/instrumentation/agentic/agno/__init__.py +19 -0
- agentops/instrumentation/agentic/agno/attributes/__init__.py +20 -0
- agentops/instrumentation/agentic/agno/attributes/agent.py +250 -0
- agentops/instrumentation/agentic/agno/attributes/metrics.py +214 -0
- agentops/instrumentation/agentic/agno/attributes/storage.py +158 -0
- agentops/instrumentation/agentic/agno/attributes/team.py +195 -0
- agentops/instrumentation/agentic/agno/attributes/tool.py +210 -0
- agentops/instrumentation/agentic/agno/attributes/workflow.py +254 -0
- agentops/instrumentation/agentic/agno/instrumentor.py +1313 -0
- agentops/instrumentation/agentic/crewai/LICENSE +201 -0
- agentops/instrumentation/agentic/crewai/NOTICE.md +10 -0
- agentops/instrumentation/agentic/crewai/__init__.py +6 -0
- agentops/instrumentation/agentic/crewai/crewai_span_attributes.py +335 -0
- agentops/instrumentation/agentic/crewai/instrumentation.py +535 -0
- agentops/instrumentation/agentic/crewai/version.py +1 -0
- agentops/instrumentation/agentic/google_adk/__init__.py +19 -0
- agentops/instrumentation/agentic/google_adk/instrumentor.py +68 -0
- agentops/instrumentation/agentic/google_adk/patch.py +767 -0
- agentops/instrumentation/agentic/haystack/__init__.py +1 -0
- agentops/instrumentation/agentic/haystack/instrumentor.py +186 -0
- agentops/instrumentation/agentic/langgraph/__init__.py +3 -0
- agentops/instrumentation/agentic/langgraph/attributes.py +54 -0
- agentops/instrumentation/agentic/langgraph/instrumentation.py +598 -0
- agentops/instrumentation/agentic/langgraph/version.py +1 -0
- agentops/instrumentation/agentic/openai_agents/README.md +156 -0
- agentops/instrumentation/agentic/openai_agents/SPANS.md +145 -0
- agentops/instrumentation/agentic/openai_agents/TRACING_API.md +144 -0
- agentops/instrumentation/agentic/openai_agents/__init__.py +30 -0
- agentops/instrumentation/agentic/openai_agents/attributes/common.py +549 -0
- agentops/instrumentation/agentic/openai_agents/attributes/completion.py +172 -0
- agentops/instrumentation/agentic/openai_agents/attributes/model.py +58 -0
- agentops/instrumentation/agentic/openai_agents/attributes/tokens.py +275 -0
- agentops/instrumentation/agentic/openai_agents/exporter.py +469 -0
- agentops/instrumentation/agentic/openai_agents/instrumentor.py +107 -0
- agentops/instrumentation/agentic/openai_agents/processor.py +58 -0
- agentops/instrumentation/agentic/smolagents/README.md +88 -0
- agentops/instrumentation/agentic/smolagents/__init__.py +12 -0
- agentops/instrumentation/agentic/smolagents/attributes/agent.py +354 -0
- agentops/instrumentation/agentic/smolagents/attributes/model.py +205 -0
- agentops/instrumentation/agentic/smolagents/instrumentor.py +286 -0
- agentops/instrumentation/agentic/smolagents/stream_wrapper.py +258 -0
- agentops/instrumentation/agentic/xpander/__init__.py +15 -0
- agentops/instrumentation/agentic/xpander/context.py +112 -0
- agentops/instrumentation/agentic/xpander/instrumentor.py +877 -0
- agentops/instrumentation/agentic/xpander/trace_probe.py +86 -0
- agentops/instrumentation/agentic/xpander/version.py +3 -0
- agentops/instrumentation/common/README.md +65 -0
- agentops/instrumentation/common/attributes.py +1 -2
- agentops/instrumentation/providers/anthropic/__init__.py +24 -0
- agentops/instrumentation/providers/anthropic/attributes/__init__.py +23 -0
- agentops/instrumentation/providers/anthropic/attributes/common.py +64 -0
- agentops/instrumentation/providers/anthropic/attributes/message.py +541 -0
- agentops/instrumentation/providers/anthropic/attributes/tools.py +231 -0
- agentops/instrumentation/providers/anthropic/event_handler_wrapper.py +90 -0
- agentops/instrumentation/providers/anthropic/instrumentor.py +146 -0
- agentops/instrumentation/providers/anthropic/stream_wrapper.py +436 -0
- agentops/instrumentation/providers/google_genai/README.md +33 -0
- agentops/instrumentation/providers/google_genai/__init__.py +24 -0
- agentops/instrumentation/providers/google_genai/attributes/__init__.py +25 -0
- agentops/instrumentation/providers/google_genai/attributes/chat.py +125 -0
- agentops/instrumentation/providers/google_genai/attributes/common.py +88 -0
- agentops/instrumentation/providers/google_genai/attributes/model.py +284 -0
- agentops/instrumentation/providers/google_genai/instrumentor.py +170 -0
- agentops/instrumentation/providers/google_genai/stream_wrapper.py +238 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/__init__.py +28 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/attributes/__init__.py +27 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/attributes/attributes.py +277 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/attributes/common.py +104 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/instrumentor.py +162 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/stream_wrapper.py +302 -0
- agentops/instrumentation/providers/mem0/__init__.py +45 -0
- agentops/instrumentation/providers/mem0/common.py +377 -0
- agentops/instrumentation/providers/mem0/instrumentor.py +270 -0
- agentops/instrumentation/providers/mem0/memory.py +430 -0
- agentops/instrumentation/providers/openai/__init__.py +21 -0
- agentops/instrumentation/providers/openai/attributes/__init__.py +7 -0
- agentops/instrumentation/providers/openai/attributes/common.py +55 -0
- agentops/instrumentation/providers/openai/attributes/response.py +607 -0
- agentops/instrumentation/providers/openai/config.py +36 -0
- agentops/instrumentation/providers/openai/instrumentor.py +312 -0
- agentops/instrumentation/providers/openai/stream_wrapper.py +941 -0
- agentops/instrumentation/providers/openai/utils.py +44 -0
- agentops/instrumentation/providers/openai/v0.py +176 -0
- agentops/instrumentation/providers/openai/v0_wrappers.py +483 -0
- agentops/instrumentation/providers/openai/wrappers/__init__.py +30 -0
- agentops/instrumentation/providers/openai/wrappers/assistant.py +277 -0
- agentops/instrumentation/providers/openai/wrappers/chat.py +259 -0
- agentops/instrumentation/providers/openai/wrappers/completion.py +109 -0
- agentops/instrumentation/providers/openai/wrappers/embeddings.py +94 -0
- agentops/instrumentation/providers/openai/wrappers/image_gen.py +75 -0
- agentops/instrumentation/providers/openai/wrappers/responses.py +191 -0
- agentops/instrumentation/providers/openai/wrappers/shared.py +81 -0
- agentops/instrumentation/utilities/concurrent_futures/__init__.py +10 -0
- agentops/instrumentation/utilities/concurrent_futures/instrumentation.py +206 -0
- agentops/integration/callbacks/dspy/__init__.py +11 -0
- agentops/integration/callbacks/dspy/callback.py +471 -0
- agentops/integration/callbacks/langchain/README.md +59 -0
- agentops/integration/callbacks/langchain/__init__.py +15 -0
- agentops/integration/callbacks/langchain/callback.py +791 -0
- agentops/integration/callbacks/langchain/utils.py +54 -0
- agentops/legacy/crewai.md +121 -0
- agentops/logging/instrument_logging.py +4 -0
- agentops/sdk/README.md +220 -0
- agentops/sdk/core.py +75 -32
- agentops/sdk/descriptors/classproperty.py +28 -0
- agentops/sdk/exporters.py +152 -33
- agentops/semconv/README.md +125 -0
- agentops/semconv/span_kinds.py +0 -2
- agentops/validation.py +102 -63
- {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/METADATA +30 -40
- mseep_agentops-0.4.23.dist-info/RECORD +178 -0
- {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/WHEEL +1 -2
- mseep_agentops-0.4.18.dist-info/RECORD +0 -94
- mseep_agentops-0.4.18.dist-info/top_level.txt +0 -2
- tests/conftest.py +0 -10
- tests/unit/client/__init__.py +0 -1
- tests/unit/client/test_http_adapter.py +0 -221
- tests/unit/client/test_http_client.py +0 -206
- tests/unit/conftest.py +0 -54
- tests/unit/sdk/__init__.py +0 -1
- tests/unit/sdk/instrumentation_tester.py +0 -207
- tests/unit/sdk/test_attributes.py +0 -392
- tests/unit/sdk/test_concurrent_instrumentation.py +0 -468
- tests/unit/sdk/test_decorators.py +0 -763
- tests/unit/sdk/test_exporters.py +0 -241
- tests/unit/sdk/test_factory.py +0 -1188
- tests/unit/sdk/test_internal_span_processor.py +0 -397
- tests/unit/sdk/test_resource_attributes.py +0 -35
- tests/unit/test_config.py +0 -82
- tests/unit/test_context_manager.py +0 -777
- tests/unit/test_events.py +0 -27
- tests/unit/test_host_env.py +0 -54
- tests/unit/test_init_py.py +0 -501
- tests/unit/test_serialization.py +0 -433
- tests/unit/test_session.py +0 -676
- tests/unit/test_user_agent.py +0 -34
- tests/unit/test_validation.py +0 -405
- {tests → agentops/instrumentation/agentic/openai_agents/attributes}/__init__.py +0 -0
- /tests/unit/__init__.py → /agentops/instrumentation/providers/openai/attributes/tools.py +0 -0
- {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
# SmoLAgents Instrumentation
|
2
|
+
|
3
|
+
This module provides OpenTelemetry instrumentation for the SmoLAgents framework. It captures telemetry data from model operations, agent executions, and tool usage.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- Model operation tracking
|
8
|
+
- Text generation
|
9
|
+
- Token usage
|
10
|
+
- Streaming responses
|
11
|
+
- Latency metrics
|
12
|
+
|
13
|
+
- Agent execution monitoring
|
14
|
+
- Step-by-step execution
|
15
|
+
- Planning phases
|
16
|
+
- Tool usage
|
17
|
+
- Execution time
|
18
|
+
|
19
|
+
- Tool usage analytics
|
20
|
+
- Tool call patterns
|
21
|
+
- Success/failure rates
|
22
|
+
- Execution time
|
23
|
+
- Error tracking
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
```python
|
28
|
+
from agentops import init
|
29
|
+
from agentops.instrumentation.smolagents import SmolagentsInstrumentor
|
30
|
+
|
31
|
+
# Initialize AgentOps with your API key
|
32
|
+
init(api_key="your-api-key")
|
33
|
+
|
34
|
+
# The instrumentation will be automatically activated
|
35
|
+
# All SmoLAgents operations will now be tracked
|
36
|
+
```
|
37
|
+
|
38
|
+
## Metrics Collected
|
39
|
+
|
40
|
+
1. Token Usage
|
41
|
+
- Input tokens
|
42
|
+
- Output tokens
|
43
|
+
- Total tokens per operation
|
44
|
+
|
45
|
+
2. Timing Metrics
|
46
|
+
- Operation duration
|
47
|
+
- Time to first token (streaming)
|
48
|
+
- Tool execution time
|
49
|
+
- Planning phase duration
|
50
|
+
|
51
|
+
3. Agent Metrics
|
52
|
+
- Step counts
|
53
|
+
- Planning steps
|
54
|
+
- Tools used
|
55
|
+
- Success/failure rates
|
56
|
+
|
57
|
+
4. Error Tracking
|
58
|
+
- Generation errors
|
59
|
+
- Tool execution errors
|
60
|
+
- Parsing errors
|
61
|
+
|
62
|
+
## Architecture
|
63
|
+
|
64
|
+
The instrumentation is built on OpenTelemetry and follows the same pattern as other AgentOps instrumentors:
|
65
|
+
|
66
|
+
1. Attribute Extractors
|
67
|
+
- Model attributes
|
68
|
+
- Agent attributes
|
69
|
+
- Tool call attributes
|
70
|
+
|
71
|
+
2. Wrappers
|
72
|
+
- Method wrappers for sync operations
|
73
|
+
- Stream wrappers for async operations
|
74
|
+
- Context propagation handling
|
75
|
+
|
76
|
+
3. Metrics
|
77
|
+
- Histograms for distributions
|
78
|
+
- Counters for events
|
79
|
+
- Custom attributes for filtering
|
80
|
+
|
81
|
+
## Contributing
|
82
|
+
|
83
|
+
When adding new features or modifying existing ones:
|
84
|
+
|
85
|
+
1. Follow the established pattern for attribute extraction
|
86
|
+
2. Maintain context propagation
|
87
|
+
3. Add appropriate error handling
|
88
|
+
4. Update tests and documentation
|
@@ -0,0 +1,12 @@
|
|
1
|
+
"""SmoLAgents instrumentation for AgentOps."""
|
2
|
+
|
3
|
+
from agentops.instrumentation.common import LibraryInfo
|
4
|
+
|
5
|
+
# Library information
|
6
|
+
_library_info = LibraryInfo(name="smolagents", default_version="1.16.0")
|
7
|
+
LIBRARY_NAME = _library_info.name
|
8
|
+
LIBRARY_VERSION = _library_info.version
|
9
|
+
|
10
|
+
from agentops.instrumentation.agentic.smolagents.instrumentor import SmolagentsInstrumentor # noqa: E402
|
11
|
+
|
12
|
+
__all__ = ["SmolagentsInstrumentor"]
|
@@ -0,0 +1,354 @@
|
|
1
|
+
"""Attribute extractors for SmoLAgents agent operations."""
|
2
|
+
|
3
|
+
from typing import Any, Dict, Optional, Tuple
|
4
|
+
import uuid
|
5
|
+
import json
|
6
|
+
|
7
|
+
from agentops.instrumentation.common.attributes import get_common_attributes
|
8
|
+
from agentops.semconv.agent import AgentAttributes
|
9
|
+
from agentops.semconv.tool import ToolAttributes
|
10
|
+
|
11
|
+
|
12
|
+
def get_agent_attributes(
|
13
|
+
args: Optional[Tuple] = None,
|
14
|
+
kwargs: Optional[Dict] = None,
|
15
|
+
return_value: Optional[Any] = None,
|
16
|
+
) -> Dict[str, Any]:
|
17
|
+
"""Extract attributes from an agent execution call.
|
18
|
+
|
19
|
+
Args:
|
20
|
+
args: Optional tuple of positional arguments
|
21
|
+
kwargs: Optional dict of keyword arguments
|
22
|
+
return_value: Optional return value from the function
|
23
|
+
|
24
|
+
Returns:
|
25
|
+
Dictionary of extracted attributes
|
26
|
+
"""
|
27
|
+
attributes = get_common_attributes()
|
28
|
+
|
29
|
+
try:
|
30
|
+
# Extract agent instance information
|
31
|
+
agent_instance = None
|
32
|
+
if args and len(args) > 0:
|
33
|
+
agent_instance = args[0]
|
34
|
+
elif kwargs and "self" in kwargs:
|
35
|
+
agent_instance = kwargs["self"]
|
36
|
+
|
37
|
+
if agent_instance:
|
38
|
+
# Extract agent name
|
39
|
+
agent_name = getattr(agent_instance, "name", agent_instance.__class__.__name__)
|
40
|
+
attributes[AgentAttributes.AGENT_NAME] = agent_name
|
41
|
+
|
42
|
+
# Generate agent ID if not present
|
43
|
+
agent_id = getattr(agent_instance, "id", str(uuid.uuid4()))
|
44
|
+
attributes[AgentAttributes.AGENT_ID] = agent_id
|
45
|
+
|
46
|
+
# Extract agent role/type
|
47
|
+
attributes[AgentAttributes.AGENT_ROLE] = "executor"
|
48
|
+
|
49
|
+
# Extract tools information
|
50
|
+
tools = getattr(agent_instance, "tools", [])
|
51
|
+
if tools:
|
52
|
+
tool_names = []
|
53
|
+
for tool in tools:
|
54
|
+
tool_name = getattr(tool, "name", str(tool))
|
55
|
+
tool_names.append(tool_name)
|
56
|
+
attributes[AgentAttributes.AGENT_TOOLS] = json.dumps(tool_names)
|
57
|
+
else:
|
58
|
+
attributes[AgentAttributes.AGENT_TOOLS] = "[]"
|
59
|
+
|
60
|
+
# Extract managed agents information
|
61
|
+
managed_agents = getattr(agent_instance, "managed_agents", [])
|
62
|
+
if managed_agents:
|
63
|
+
managed_agent_names = []
|
64
|
+
for managed_agent in managed_agents:
|
65
|
+
agent_name = getattr(managed_agent, "name", managed_agent.__class__.__name__)
|
66
|
+
managed_agent_names.append(agent_name)
|
67
|
+
attributes[AgentAttributes.AGENT_MANAGED_AGENTS] = json.dumps(managed_agent_names)
|
68
|
+
else:
|
69
|
+
attributes[AgentAttributes.AGENT_MANAGED_AGENTS] = "[]"
|
70
|
+
|
71
|
+
# Extract input/task from args or kwargs
|
72
|
+
task_input = None
|
73
|
+
if args and len(args) > 1:
|
74
|
+
task_input = args[1]
|
75
|
+
elif kwargs and "task" in kwargs:
|
76
|
+
task_input = kwargs["task"]
|
77
|
+
elif kwargs and "prompt" in kwargs:
|
78
|
+
task_input = kwargs["prompt"]
|
79
|
+
|
80
|
+
if task_input:
|
81
|
+
attributes["agent.task"] = str(task_input)
|
82
|
+
|
83
|
+
# Extract return value/output
|
84
|
+
if return_value is not None:
|
85
|
+
attributes["agentops.entity.output"] = str(return_value)
|
86
|
+
|
87
|
+
except Exception:
|
88
|
+
# If extraction fails, continue with basic attributes
|
89
|
+
pass
|
90
|
+
|
91
|
+
return attributes
|
92
|
+
|
93
|
+
|
94
|
+
def get_agent_stream_attributes(
|
95
|
+
args: Optional[Tuple] = None,
|
96
|
+
kwargs: Optional[Dict] = None,
|
97
|
+
return_value: Optional[Any] = None,
|
98
|
+
) -> Dict[str, Any]:
|
99
|
+
"""Extract attributes from an agent streaming call.
|
100
|
+
|
101
|
+
Args:
|
102
|
+
args: Optional tuple of positional arguments
|
103
|
+
kwargs: Optional dict of keyword arguments
|
104
|
+
return_value: Optional return value from the function
|
105
|
+
|
106
|
+
Returns:
|
107
|
+
Dictionary of extracted attributes
|
108
|
+
"""
|
109
|
+
attributes = get_common_attributes()
|
110
|
+
|
111
|
+
try:
|
112
|
+
# Extract reasoning/task information
|
113
|
+
if kwargs:
|
114
|
+
if "max_steps" in kwargs:
|
115
|
+
attributes["agent.max_steps"] = str(kwargs["max_steps"])
|
116
|
+
|
117
|
+
# Extract task/reasoning from various parameter names
|
118
|
+
task_info = None
|
119
|
+
for param_name in ["task", "prompt", "reasoning", "query"]:
|
120
|
+
if param_name in kwargs:
|
121
|
+
task_info = kwargs[param_name]
|
122
|
+
break
|
123
|
+
|
124
|
+
if task_info:
|
125
|
+
attributes["agent.reasoning"] = str(task_info)
|
126
|
+
|
127
|
+
# Extract from args
|
128
|
+
if args and len(args) > 1:
|
129
|
+
task_info = args[1]
|
130
|
+
attributes["agent.reasoning"] = str(task_info)
|
131
|
+
|
132
|
+
except Exception:
|
133
|
+
# If extraction fails, continue with basic attributes
|
134
|
+
pass
|
135
|
+
|
136
|
+
return attributes
|
137
|
+
|
138
|
+
|
139
|
+
def get_agent_step_attributes(
|
140
|
+
args: Optional[Tuple] = None,
|
141
|
+
kwargs: Optional[Dict] = None,
|
142
|
+
return_value: Optional[Any] = None,
|
143
|
+
) -> Dict[str, Any]:
|
144
|
+
"""Extract attributes from an agent step execution.
|
145
|
+
|
146
|
+
Args:
|
147
|
+
args: Optional tuple of positional arguments
|
148
|
+
kwargs: Optional dict of keyword arguments
|
149
|
+
return_value: Optional return value from the function
|
150
|
+
|
151
|
+
Returns:
|
152
|
+
Dictionary of extracted attributes
|
153
|
+
"""
|
154
|
+
attributes = get_common_attributes()
|
155
|
+
|
156
|
+
try:
|
157
|
+
# Try to extract step information
|
158
|
+
step_number = getattr(args[0] if args else None, "step_count", None)
|
159
|
+
if step_number is not None:
|
160
|
+
attributes["agent.step_number"] = str(step_number)
|
161
|
+
|
162
|
+
# Extract step name/type
|
163
|
+
step_name = "ActionStep" # Default for smolagents
|
164
|
+
attributes["agent.name"] = step_name
|
165
|
+
|
166
|
+
# Extract return value
|
167
|
+
if return_value is not None:
|
168
|
+
attributes["agentops.entity.output"] = str(return_value)
|
169
|
+
|
170
|
+
except Exception:
|
171
|
+
# If extraction fails, continue with basic attributes
|
172
|
+
pass
|
173
|
+
|
174
|
+
return attributes
|
175
|
+
|
176
|
+
|
177
|
+
def get_tool_call_attributes(
|
178
|
+
args: Optional[Tuple] = None,
|
179
|
+
kwargs: Optional[Dict] = None,
|
180
|
+
return_value: Optional[Any] = None,
|
181
|
+
) -> Dict[str, Any]:
|
182
|
+
"""Extract attributes from a tool call execution.
|
183
|
+
|
184
|
+
Args:
|
185
|
+
args: Optional tuple of positional arguments
|
186
|
+
kwargs: Optional dict of keyword arguments
|
187
|
+
return_value: Optional return value from the function
|
188
|
+
|
189
|
+
Returns:
|
190
|
+
Dictionary of extracted attributes
|
191
|
+
"""
|
192
|
+
attributes = get_common_attributes()
|
193
|
+
|
194
|
+
try:
|
195
|
+
# Generate tool execution ID
|
196
|
+
tool_id = str(uuid.uuid4())
|
197
|
+
attributes[ToolAttributes.TOOL_ID] = tool_id
|
198
|
+
|
199
|
+
# Extract tool information from various sources
|
200
|
+
tool_name = "unknown"
|
201
|
+
tool_description = "unknown"
|
202
|
+
tool_parameters = "{}"
|
203
|
+
|
204
|
+
# Try to extract from instance (first arg)
|
205
|
+
if args and len(args) > 0:
|
206
|
+
instance = args[0]
|
207
|
+
if hasattr(instance, "name"):
|
208
|
+
tool_name = instance.name
|
209
|
+
if hasattr(instance, "description"):
|
210
|
+
tool_description = instance.description
|
211
|
+
|
212
|
+
# Try to extract from kwargs
|
213
|
+
if kwargs:
|
214
|
+
if "tool_call" in kwargs:
|
215
|
+
tool_call = kwargs["tool_call"]
|
216
|
+
if hasattr(tool_call, "function"):
|
217
|
+
tool_name = tool_call.function.name
|
218
|
+
if hasattr(tool_call.function, "arguments"):
|
219
|
+
tool_parameters = tool_call.function.arguments
|
220
|
+
elif "name" in kwargs:
|
221
|
+
tool_name = kwargs["name"]
|
222
|
+
elif "function_name" in kwargs:
|
223
|
+
tool_name = kwargs["function_name"]
|
224
|
+
|
225
|
+
# Extract parameters
|
226
|
+
if "parameters" in kwargs:
|
227
|
+
tool_parameters = json.dumps(kwargs["parameters"])
|
228
|
+
elif "arguments" in kwargs:
|
229
|
+
tool_parameters = json.dumps(kwargs["arguments"])
|
230
|
+
elif "args" in kwargs:
|
231
|
+
tool_parameters = json.dumps(kwargs["args"])
|
232
|
+
|
233
|
+
# Set tool attributes
|
234
|
+
attributes[ToolAttributes.TOOL_NAME] = tool_name
|
235
|
+
attributes[ToolAttributes.TOOL_DESCRIPTION] = tool_description
|
236
|
+
attributes[ToolAttributes.TOOL_PARAMETERS] = tool_parameters
|
237
|
+
attributes[ToolAttributes.TOOL_STATUS] = "pending"
|
238
|
+
attributes[ToolAttributes.TOOL_OUTPUT_TYPE] = "unknown"
|
239
|
+
attributes[ToolAttributes.TOOL_INPUTS] = "{}"
|
240
|
+
|
241
|
+
# Extract return value
|
242
|
+
if return_value is not None:
|
243
|
+
attributes["tool.result"] = str(return_value)
|
244
|
+
attributes[ToolAttributes.TOOL_STATUS] = "success"
|
245
|
+
|
246
|
+
except Exception:
|
247
|
+
# If extraction fails, set basic attributes
|
248
|
+
attributes[ToolAttributes.TOOL_NAME] = "unknown"
|
249
|
+
attributes[ToolAttributes.TOOL_DESCRIPTION] = "unknown"
|
250
|
+
attributes[ToolAttributes.TOOL_ID] = str(uuid.uuid4())
|
251
|
+
attributes[ToolAttributes.TOOL_PARAMETERS] = "{}"
|
252
|
+
attributes[ToolAttributes.TOOL_STATUS] = "pending"
|
253
|
+
|
254
|
+
return attributes
|
255
|
+
|
256
|
+
|
257
|
+
def get_planning_step_attributes(
|
258
|
+
args: Optional[Tuple] = None,
|
259
|
+
kwargs: Optional[Dict] = None,
|
260
|
+
return_value: Optional[Any] = None,
|
261
|
+
) -> Dict[str, Any]:
|
262
|
+
"""Extract attributes from a planning step execution.
|
263
|
+
|
264
|
+
Args:
|
265
|
+
args: Optional tuple of positional arguments
|
266
|
+
kwargs: Optional dict of keyword arguments
|
267
|
+
return_value: Optional return value from the function
|
268
|
+
|
269
|
+
Returns:
|
270
|
+
Dictionary of extracted attributes
|
271
|
+
"""
|
272
|
+
attributes = get_common_attributes()
|
273
|
+
|
274
|
+
try:
|
275
|
+
# Extract planning information
|
276
|
+
if kwargs:
|
277
|
+
if "planning_step" in kwargs:
|
278
|
+
step = kwargs["planning_step"]
|
279
|
+
attributes["agent.planning.step"] = str(step)
|
280
|
+
if "reasoning" in kwargs:
|
281
|
+
attributes["agent.planning.reasoning"] = str(kwargs["reasoning"])
|
282
|
+
|
283
|
+
# Extract return value
|
284
|
+
if return_value is not None:
|
285
|
+
attributes["agentops.entity.output"] = str(return_value)
|
286
|
+
|
287
|
+
except Exception:
|
288
|
+
# If extraction fails, continue with basic attributes
|
289
|
+
pass
|
290
|
+
|
291
|
+
return attributes
|
292
|
+
|
293
|
+
|
294
|
+
def get_managed_agent_attributes(
|
295
|
+
args: Optional[Tuple] = None,
|
296
|
+
kwargs: Optional[Dict] = None,
|
297
|
+
return_value: Optional[Any] = None,
|
298
|
+
) -> Dict[str, Any]:
|
299
|
+
"""Extract attributes from a managed agent call.
|
300
|
+
|
301
|
+
Args:
|
302
|
+
args: Optional tuple of positional arguments
|
303
|
+
kwargs: Optional dict of keyword arguments
|
304
|
+
return_value: Optional return value from the function
|
305
|
+
|
306
|
+
Returns:
|
307
|
+
Dictionary of extracted attributes
|
308
|
+
"""
|
309
|
+
attributes = get_common_attributes()
|
310
|
+
|
311
|
+
try:
|
312
|
+
# Extract managed agent information
|
313
|
+
agent_instance = None
|
314
|
+
if args and len(args) > 0:
|
315
|
+
agent_instance = args[0]
|
316
|
+
elif kwargs and "agent" in kwargs:
|
317
|
+
agent_instance = kwargs["agent"]
|
318
|
+
|
319
|
+
if agent_instance:
|
320
|
+
# Extract agent details
|
321
|
+
agent_name = getattr(agent_instance, "name", agent_instance.__class__.__name__)
|
322
|
+
agent_id = getattr(agent_instance, "id", str(uuid.uuid4()))
|
323
|
+
agent_description = getattr(agent_instance, "description", "")
|
324
|
+
|
325
|
+
attributes[AgentAttributes.AGENT_NAME] = agent_name
|
326
|
+
attributes[AgentAttributes.AGENT_ID] = agent_id
|
327
|
+
attributes[AgentAttributes.AGENT_ROLE] = "managed"
|
328
|
+
attributes[AgentAttributes.AGENT_TYPE] = agent_instance.__class__.__name__
|
329
|
+
|
330
|
+
if agent_description:
|
331
|
+
attributes[AgentAttributes.AGENT_DESCRIPTION] = agent_description
|
332
|
+
|
333
|
+
# Check if this agent provides run summaries
|
334
|
+
attributes["agent.provide_run_summary"] = "false" # Default for smolagents
|
335
|
+
|
336
|
+
# Extract task information
|
337
|
+
task = None
|
338
|
+
if args and len(args) > 1:
|
339
|
+
task = args[1]
|
340
|
+
elif kwargs and "task" in kwargs:
|
341
|
+
task = kwargs["task"]
|
342
|
+
|
343
|
+
if task:
|
344
|
+
attributes["agent.task"] = str(task)
|
345
|
+
|
346
|
+
# Extract return value
|
347
|
+
if return_value is not None:
|
348
|
+
attributes["agentops.entity.output"] = str(return_value)
|
349
|
+
|
350
|
+
except Exception:
|
351
|
+
# If extraction fails, continue with basic attributes
|
352
|
+
pass
|
353
|
+
|
354
|
+
return attributes
|
@@ -0,0 +1,205 @@
|
|
1
|
+
"""Attribute extractors for SmoLAgents model operations."""
|
2
|
+
|
3
|
+
from typing import Any, Dict, Optional, Tuple
|
4
|
+
import json
|
5
|
+
|
6
|
+
from agentops.instrumentation.common.attributes import (
|
7
|
+
get_common_attributes,
|
8
|
+
)
|
9
|
+
from agentops.semconv.message import MessageAttributes
|
10
|
+
from agentops.semconv.span_attributes import SpanAttributes
|
11
|
+
|
12
|
+
|
13
|
+
def get_model_attributes(
|
14
|
+
args: Optional[Tuple] = None,
|
15
|
+
kwargs: Optional[Dict] = None,
|
16
|
+
return_value: Optional[Any] = None,
|
17
|
+
) -> Dict[str, Any]:
|
18
|
+
"""Extract attributes from a model generation call.
|
19
|
+
|
20
|
+
Args:
|
21
|
+
args: Optional tuple of positional arguments
|
22
|
+
kwargs: Optional dict of keyword arguments
|
23
|
+
return_value: Optional return value from the function
|
24
|
+
|
25
|
+
Returns:
|
26
|
+
Dictionary of extracted attributes
|
27
|
+
"""
|
28
|
+
attributes = get_common_attributes()
|
29
|
+
|
30
|
+
try:
|
31
|
+
# Extract model name from various sources
|
32
|
+
model_name = "unknown"
|
33
|
+
|
34
|
+
# Try to get from kwargs
|
35
|
+
if kwargs:
|
36
|
+
if "model" in kwargs:
|
37
|
+
model_name = kwargs["model"]
|
38
|
+
elif kwargs.get("self") and hasattr(kwargs["self"], "model_id"):
|
39
|
+
model_name = kwargs["self"].model_id
|
40
|
+
|
41
|
+
# Try to get from args (instance is usually first arg in methods)
|
42
|
+
if model_name == "unknown" and args and len(args) > 0:
|
43
|
+
instance = args[0]
|
44
|
+
if hasattr(instance, "model_id"):
|
45
|
+
model_name = instance.model_id
|
46
|
+
|
47
|
+
# Set model attributes
|
48
|
+
attributes[SpanAttributes.LLM_REQUEST_MODEL] = model_name
|
49
|
+
|
50
|
+
# Extract messages from kwargs
|
51
|
+
if kwargs and "messages" in kwargs:
|
52
|
+
messages = kwargs["messages"]
|
53
|
+
if isinstance(messages, list):
|
54
|
+
for i, message in enumerate(messages):
|
55
|
+
message_dict = message
|
56
|
+
if hasattr(message, "to_dict"):
|
57
|
+
message_dict = message.to_dict()
|
58
|
+
elif hasattr(message, "__dict__"):
|
59
|
+
message_dict = message.__dict__
|
60
|
+
|
61
|
+
if isinstance(message_dict, dict):
|
62
|
+
# Set role
|
63
|
+
role = message_dict.get("role", "user")
|
64
|
+
attributes[MessageAttributes.PROMPT_ROLE.format(i=i)] = role
|
65
|
+
|
66
|
+
# Set content
|
67
|
+
content = message_dict.get("content", "")
|
68
|
+
if content:
|
69
|
+
attributes[MessageAttributes.PROMPT_CONTENT.format(i=i)] = str(content)
|
70
|
+
|
71
|
+
# Extract tools from kwargs
|
72
|
+
if kwargs and "tools_to_call_from" in kwargs:
|
73
|
+
tools = kwargs["tools_to_call_from"]
|
74
|
+
if tools and isinstance(tools, list):
|
75
|
+
for i, tool in enumerate(tools):
|
76
|
+
tool_name = getattr(tool, "name", "unknown")
|
77
|
+
tool_description = getattr(tool, "description", "")
|
78
|
+
|
79
|
+
attributes[MessageAttributes.TOOL_CALL_NAME.format(i=i)] = tool_name
|
80
|
+
if tool_description:
|
81
|
+
attributes[MessageAttributes.TOOL_CALL_DESCRIPTION.format(i=i)] = tool_description
|
82
|
+
|
83
|
+
# Extract additional parameters
|
84
|
+
if kwargs:
|
85
|
+
if "temperature" in kwargs:
|
86
|
+
attributes[SpanAttributes.LLM_REQUEST_TEMPERATURE] = kwargs["temperature"]
|
87
|
+
if "max_tokens" in kwargs:
|
88
|
+
attributes[SpanAttributes.LLM_REQUEST_MAX_TOKENS] = kwargs["max_tokens"]
|
89
|
+
if "stop_sequences" in kwargs:
|
90
|
+
attributes[SpanAttributes.LLM_REQUEST_STOP_SEQUENCES] = json.dumps(kwargs["stop_sequences"])
|
91
|
+
|
92
|
+
# Extract response attributes
|
93
|
+
if return_value:
|
94
|
+
try:
|
95
|
+
# Handle ChatMessage response
|
96
|
+
if hasattr(return_value, "content"):
|
97
|
+
attributes[MessageAttributes.COMPLETION_CONTENT.format(i=0)] = str(return_value.content)
|
98
|
+
if hasattr(return_value, "role"):
|
99
|
+
attributes[MessageAttributes.COMPLETION_ROLE.format(i=0)] = return_value.role
|
100
|
+
|
101
|
+
# Handle tool calls in response
|
102
|
+
if hasattr(return_value, "tool_calls") and return_value.tool_calls:
|
103
|
+
for j, tool_call in enumerate(return_value.tool_calls):
|
104
|
+
if hasattr(tool_call, "function"):
|
105
|
+
attributes[MessageAttributes.COMPLETION_TOOL_CALL_NAME.format(i=0, j=j)] = (
|
106
|
+
tool_call.function.name
|
107
|
+
)
|
108
|
+
if hasattr(tool_call.function, "arguments"):
|
109
|
+
attributes[MessageAttributes.COMPLETION_TOOL_CALL_ARGUMENTS.format(i=0, j=j)] = (
|
110
|
+
tool_call.function.arguments
|
111
|
+
)
|
112
|
+
if hasattr(tool_call, "id"):
|
113
|
+
attributes[MessageAttributes.COMPLETION_TOOL_CALL_ID.format(i=0, j=j)] = tool_call.id
|
114
|
+
|
115
|
+
# Extract token usage
|
116
|
+
if hasattr(return_value, "token_usage") and return_value.token_usage:
|
117
|
+
token_usage = return_value.token_usage
|
118
|
+
if hasattr(token_usage, "input_tokens"):
|
119
|
+
attributes[SpanAttributes.LLM_USAGE_PROMPT_TOKENS] = token_usage.input_tokens
|
120
|
+
if hasattr(token_usage, "output_tokens"):
|
121
|
+
attributes[SpanAttributes.LLM_USAGE_COMPLETION_TOKENS] = token_usage.output_tokens
|
122
|
+
if hasattr(token_usage, "total_tokens"):
|
123
|
+
attributes[SpanAttributes.LLM_USAGE_TOTAL_TOKENS] = token_usage.total_tokens
|
124
|
+
|
125
|
+
# Extract response ID
|
126
|
+
if hasattr(return_value, "raw") and return_value.raw:
|
127
|
+
raw_response = return_value.raw
|
128
|
+
if hasattr(raw_response, "id"):
|
129
|
+
attributes[SpanAttributes.LLM_RESPONSE_ID] = raw_response.id
|
130
|
+
|
131
|
+
except Exception:
|
132
|
+
# If we can't extract response attributes, continue with what we have
|
133
|
+
pass
|
134
|
+
|
135
|
+
except Exception:
|
136
|
+
# If extraction fails, return basic attributes
|
137
|
+
pass
|
138
|
+
|
139
|
+
return attributes
|
140
|
+
|
141
|
+
|
142
|
+
def get_stream_attributes(
|
143
|
+
args: Optional[Tuple] = None,
|
144
|
+
kwargs: Optional[Dict] = None,
|
145
|
+
return_value: Optional[Any] = None,
|
146
|
+
) -> Dict[str, Any]:
|
147
|
+
"""Extract attributes from a streaming model generation call.
|
148
|
+
|
149
|
+
Args:
|
150
|
+
args: Optional tuple of positional arguments
|
151
|
+
kwargs: Optional dict of keyword arguments
|
152
|
+
return_value: Optional return value from the function
|
153
|
+
|
154
|
+
Returns:
|
155
|
+
Dictionary of extracted attributes
|
156
|
+
"""
|
157
|
+
attributes = get_common_attributes()
|
158
|
+
|
159
|
+
try:
|
160
|
+
# Extract model name
|
161
|
+
model_name = "unknown"
|
162
|
+
if kwargs and kwargs.get("self") and hasattr(kwargs["self"], "model_id"):
|
163
|
+
model_name = kwargs["self"].model_id
|
164
|
+
elif args and len(args) > 0 and hasattr(args[0], "model_id"):
|
165
|
+
model_name = args[0].model_id
|
166
|
+
|
167
|
+
attributes[SpanAttributes.LLM_REQUEST_MODEL] = model_name
|
168
|
+
attributes["gen_ai.request.streaming"] = True
|
169
|
+
|
170
|
+
# Extract messages for streaming
|
171
|
+
if kwargs and "messages" in kwargs:
|
172
|
+
messages = kwargs["messages"]
|
173
|
+
if isinstance(messages, list):
|
174
|
+
for i, message in enumerate(messages):
|
175
|
+
message_dict = message
|
176
|
+
if hasattr(message, "to_dict"):
|
177
|
+
message_dict = message.to_dict()
|
178
|
+
elif hasattr(message, "__dict__"):
|
179
|
+
message_dict = message.__dict__
|
180
|
+
|
181
|
+
if isinstance(message_dict, dict):
|
182
|
+
role = message_dict.get("role", "user")
|
183
|
+
attributes[MessageAttributes.PROMPT_ROLE.format(i=i)] = role
|
184
|
+
|
185
|
+
content = message_dict.get("content", "")
|
186
|
+
if content:
|
187
|
+
attributes[MessageAttributes.PROMPT_CONTENT.format(i=i)] = str(content)
|
188
|
+
|
189
|
+
# Extract tools for streaming
|
190
|
+
if kwargs and "tools_to_call_from" in kwargs:
|
191
|
+
tools = kwargs["tools_to_call_from"]
|
192
|
+
if tools and isinstance(tools, list):
|
193
|
+
for i, tool in enumerate(tools):
|
194
|
+
tool_name = getattr(tool, "name", "unknown")
|
195
|
+
tool_description = getattr(tool, "description", "")
|
196
|
+
|
197
|
+
attributes[MessageAttributes.TOOL_CALL_NAME.format(i=i)] = tool_name
|
198
|
+
if tool_description:
|
199
|
+
attributes[MessageAttributes.TOOL_CALL_DESCRIPTION.format(i=i)] = tool_description
|
200
|
+
|
201
|
+
except Exception:
|
202
|
+
# If extraction fails, return basic attributes
|
203
|
+
pass
|
204
|
+
|
205
|
+
return attributes
|