mseep-agentops 0.4.18__py3-none-any.whl → 0.4.22__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.
Files changed (153) hide show
  1. agentops/__init__.py +0 -0
  2. agentops/client/api/base.py +28 -30
  3. agentops/client/api/versions/v3.py +29 -25
  4. agentops/client/api/versions/v4.py +87 -46
  5. agentops/client/client.py +98 -29
  6. agentops/client/http/README.md +87 -0
  7. agentops/client/http/http_client.py +126 -172
  8. agentops/config.py +8 -2
  9. agentops/instrumentation/OpenTelemetry.md +133 -0
  10. agentops/instrumentation/README.md +167 -0
  11. agentops/instrumentation/__init__.py +13 -1
  12. agentops/instrumentation/agentic/ag2/__init__.py +18 -0
  13. agentops/instrumentation/agentic/ag2/instrumentor.py +922 -0
  14. agentops/instrumentation/agentic/agno/__init__.py +19 -0
  15. agentops/instrumentation/agentic/agno/attributes/__init__.py +20 -0
  16. agentops/instrumentation/agentic/agno/attributes/agent.py +250 -0
  17. agentops/instrumentation/agentic/agno/attributes/metrics.py +214 -0
  18. agentops/instrumentation/agentic/agno/attributes/storage.py +158 -0
  19. agentops/instrumentation/agentic/agno/attributes/team.py +195 -0
  20. agentops/instrumentation/agentic/agno/attributes/tool.py +210 -0
  21. agentops/instrumentation/agentic/agno/attributes/workflow.py +254 -0
  22. agentops/instrumentation/agentic/agno/instrumentor.py +1313 -0
  23. agentops/instrumentation/agentic/crewai/LICENSE +201 -0
  24. agentops/instrumentation/agentic/crewai/NOTICE.md +10 -0
  25. agentops/instrumentation/agentic/crewai/__init__.py +6 -0
  26. agentops/instrumentation/agentic/crewai/crewai_span_attributes.py +335 -0
  27. agentops/instrumentation/agentic/crewai/instrumentation.py +535 -0
  28. agentops/instrumentation/agentic/crewai/version.py +1 -0
  29. agentops/instrumentation/agentic/google_adk/__init__.py +19 -0
  30. agentops/instrumentation/agentic/google_adk/instrumentor.py +68 -0
  31. agentops/instrumentation/agentic/google_adk/patch.py +767 -0
  32. agentops/instrumentation/agentic/haystack/__init__.py +1 -0
  33. agentops/instrumentation/agentic/haystack/instrumentor.py +186 -0
  34. agentops/instrumentation/agentic/langgraph/__init__.py +3 -0
  35. agentops/instrumentation/agentic/langgraph/attributes.py +54 -0
  36. agentops/instrumentation/agentic/langgraph/instrumentation.py +598 -0
  37. agentops/instrumentation/agentic/langgraph/version.py +1 -0
  38. agentops/instrumentation/agentic/openai_agents/README.md +156 -0
  39. agentops/instrumentation/agentic/openai_agents/SPANS.md +145 -0
  40. agentops/instrumentation/agentic/openai_agents/TRACING_API.md +144 -0
  41. agentops/instrumentation/agentic/openai_agents/__init__.py +30 -0
  42. agentops/instrumentation/agentic/openai_agents/attributes/common.py +549 -0
  43. agentops/instrumentation/agentic/openai_agents/attributes/completion.py +172 -0
  44. agentops/instrumentation/agentic/openai_agents/attributes/model.py +58 -0
  45. agentops/instrumentation/agentic/openai_agents/attributes/tokens.py +275 -0
  46. agentops/instrumentation/agentic/openai_agents/exporter.py +469 -0
  47. agentops/instrumentation/agentic/openai_agents/instrumentor.py +107 -0
  48. agentops/instrumentation/agentic/openai_agents/processor.py +58 -0
  49. agentops/instrumentation/agentic/smolagents/README.md +88 -0
  50. agentops/instrumentation/agentic/smolagents/__init__.py +12 -0
  51. agentops/instrumentation/agentic/smolagents/attributes/agent.py +354 -0
  52. agentops/instrumentation/agentic/smolagents/attributes/model.py +205 -0
  53. agentops/instrumentation/agentic/smolagents/instrumentor.py +286 -0
  54. agentops/instrumentation/agentic/smolagents/stream_wrapper.py +258 -0
  55. agentops/instrumentation/agentic/xpander/__init__.py +15 -0
  56. agentops/instrumentation/agentic/xpander/context.py +112 -0
  57. agentops/instrumentation/agentic/xpander/instrumentor.py +877 -0
  58. agentops/instrumentation/agentic/xpander/trace_probe.py +86 -0
  59. agentops/instrumentation/agentic/xpander/version.py +3 -0
  60. agentops/instrumentation/common/README.md +65 -0
  61. agentops/instrumentation/common/attributes.py +1 -2
  62. agentops/instrumentation/providers/anthropic/__init__.py +24 -0
  63. agentops/instrumentation/providers/anthropic/attributes/__init__.py +23 -0
  64. agentops/instrumentation/providers/anthropic/attributes/common.py +64 -0
  65. agentops/instrumentation/providers/anthropic/attributes/message.py +541 -0
  66. agentops/instrumentation/providers/anthropic/attributes/tools.py +231 -0
  67. agentops/instrumentation/providers/anthropic/event_handler_wrapper.py +90 -0
  68. agentops/instrumentation/providers/anthropic/instrumentor.py +146 -0
  69. agentops/instrumentation/providers/anthropic/stream_wrapper.py +436 -0
  70. agentops/instrumentation/providers/google_genai/README.md +33 -0
  71. agentops/instrumentation/providers/google_genai/__init__.py +24 -0
  72. agentops/instrumentation/providers/google_genai/attributes/__init__.py +25 -0
  73. agentops/instrumentation/providers/google_genai/attributes/chat.py +125 -0
  74. agentops/instrumentation/providers/google_genai/attributes/common.py +88 -0
  75. agentops/instrumentation/providers/google_genai/attributes/model.py +284 -0
  76. agentops/instrumentation/providers/google_genai/instrumentor.py +170 -0
  77. agentops/instrumentation/providers/google_genai/stream_wrapper.py +238 -0
  78. agentops/instrumentation/providers/ibm_watsonx_ai/__init__.py +28 -0
  79. agentops/instrumentation/providers/ibm_watsonx_ai/attributes/__init__.py +27 -0
  80. agentops/instrumentation/providers/ibm_watsonx_ai/attributes/attributes.py +277 -0
  81. agentops/instrumentation/providers/ibm_watsonx_ai/attributes/common.py +104 -0
  82. agentops/instrumentation/providers/ibm_watsonx_ai/instrumentor.py +162 -0
  83. agentops/instrumentation/providers/ibm_watsonx_ai/stream_wrapper.py +302 -0
  84. agentops/instrumentation/providers/mem0/__init__.py +45 -0
  85. agentops/instrumentation/providers/mem0/common.py +377 -0
  86. agentops/instrumentation/providers/mem0/instrumentor.py +270 -0
  87. agentops/instrumentation/providers/mem0/memory.py +430 -0
  88. agentops/instrumentation/providers/openai/__init__.py +21 -0
  89. agentops/instrumentation/providers/openai/attributes/__init__.py +7 -0
  90. agentops/instrumentation/providers/openai/attributes/common.py +55 -0
  91. agentops/instrumentation/providers/openai/attributes/response.py +607 -0
  92. agentops/instrumentation/providers/openai/config.py +36 -0
  93. agentops/instrumentation/providers/openai/instrumentor.py +312 -0
  94. agentops/instrumentation/providers/openai/stream_wrapper.py +941 -0
  95. agentops/instrumentation/providers/openai/utils.py +44 -0
  96. agentops/instrumentation/providers/openai/v0.py +176 -0
  97. agentops/instrumentation/providers/openai/v0_wrappers.py +483 -0
  98. agentops/instrumentation/providers/openai/wrappers/__init__.py +30 -0
  99. agentops/instrumentation/providers/openai/wrappers/assistant.py +277 -0
  100. agentops/instrumentation/providers/openai/wrappers/chat.py +259 -0
  101. agentops/instrumentation/providers/openai/wrappers/completion.py +109 -0
  102. agentops/instrumentation/providers/openai/wrappers/embeddings.py +94 -0
  103. agentops/instrumentation/providers/openai/wrappers/image_gen.py +75 -0
  104. agentops/instrumentation/providers/openai/wrappers/responses.py +191 -0
  105. agentops/instrumentation/providers/openai/wrappers/shared.py +81 -0
  106. agentops/instrumentation/utilities/concurrent_futures/__init__.py +10 -0
  107. agentops/instrumentation/utilities/concurrent_futures/instrumentation.py +206 -0
  108. agentops/integration/callbacks/dspy/__init__.py +11 -0
  109. agentops/integration/callbacks/dspy/callback.py +471 -0
  110. agentops/integration/callbacks/langchain/README.md +59 -0
  111. agentops/integration/callbacks/langchain/__init__.py +15 -0
  112. agentops/integration/callbacks/langchain/callback.py +791 -0
  113. agentops/integration/callbacks/langchain/utils.py +54 -0
  114. agentops/legacy/crewai.md +121 -0
  115. agentops/logging/instrument_logging.py +4 -0
  116. agentops/sdk/README.md +220 -0
  117. agentops/sdk/core.py +75 -32
  118. agentops/sdk/descriptors/classproperty.py +28 -0
  119. agentops/sdk/exporters.py +152 -33
  120. agentops/semconv/README.md +125 -0
  121. agentops/semconv/span_kinds.py +0 -2
  122. agentops/validation.py +102 -63
  123. {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.22.dist-info}/METADATA +30 -40
  124. mseep_agentops-0.4.22.dist-info/RECORD +178 -0
  125. {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.22.dist-info}/WHEEL +1 -2
  126. mseep_agentops-0.4.18.dist-info/RECORD +0 -94
  127. mseep_agentops-0.4.18.dist-info/top_level.txt +0 -2
  128. tests/conftest.py +0 -10
  129. tests/unit/client/__init__.py +0 -1
  130. tests/unit/client/test_http_adapter.py +0 -221
  131. tests/unit/client/test_http_client.py +0 -206
  132. tests/unit/conftest.py +0 -54
  133. tests/unit/sdk/__init__.py +0 -1
  134. tests/unit/sdk/instrumentation_tester.py +0 -207
  135. tests/unit/sdk/test_attributes.py +0 -392
  136. tests/unit/sdk/test_concurrent_instrumentation.py +0 -468
  137. tests/unit/sdk/test_decorators.py +0 -763
  138. tests/unit/sdk/test_exporters.py +0 -241
  139. tests/unit/sdk/test_factory.py +0 -1188
  140. tests/unit/sdk/test_internal_span_processor.py +0 -397
  141. tests/unit/sdk/test_resource_attributes.py +0 -35
  142. tests/unit/test_config.py +0 -82
  143. tests/unit/test_context_manager.py +0 -777
  144. tests/unit/test_events.py +0 -27
  145. tests/unit/test_host_env.py +0 -54
  146. tests/unit/test_init_py.py +0 -501
  147. tests/unit/test_serialization.py +0 -433
  148. tests/unit/test_session.py +0 -676
  149. tests/unit/test_user_agent.py +0 -34
  150. tests/unit/test_validation.py +0 -405
  151. {tests → agentops/instrumentation/agentic/openai_agents/attributes}/__init__.py +0 -0
  152. /tests/unit/__init__.py → /agentops/instrumentation/providers/openai/attributes/tools.py +0 -0
  153. {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.22.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