monocle-apptrace 0.6.0__py3-none-any.whl → 0.6.6__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.

Potentially problematic release.


This version of monocle-apptrace might be problematic. Click here for more details.

Files changed (45) hide show
  1. monocle_apptrace/instrumentation/common/constants.py +8 -0
  2. monocle_apptrace/instrumentation/common/span_handler.py +73 -23
  3. monocle_apptrace/instrumentation/common/utils.py +63 -6
  4. monocle_apptrace/instrumentation/common/wrapper.py +111 -42
  5. monocle_apptrace/instrumentation/common/wrapper_method.py +4 -2
  6. monocle_apptrace/instrumentation/metamodel/a2a/methods.py +1 -1
  7. monocle_apptrace/instrumentation/metamodel/adk/_helper.py +2 -1
  8. monocle_apptrace/instrumentation/metamodel/agents/_helper.py +3 -3
  9. monocle_apptrace/instrumentation/metamodel/agents/entities/inference.py +2 -0
  10. monocle_apptrace/instrumentation/metamodel/aiohttp/_helper.py +1 -1
  11. monocle_apptrace/instrumentation/metamodel/anthropic/entities/inference.py +1 -4
  12. monocle_apptrace/instrumentation/metamodel/azfunc/_helper.py +1 -1
  13. monocle_apptrace/instrumentation/metamodel/botocore/_helper.py +5 -0
  14. monocle_apptrace/instrumentation/metamodel/botocore/entities/inference.py +4 -0
  15. monocle_apptrace/instrumentation/metamodel/fastapi/_helper.py +4 -4
  16. monocle_apptrace/instrumentation/metamodel/fastapi/methods.py +4 -4
  17. monocle_apptrace/instrumentation/metamodel/flask/_helper.py +3 -3
  18. monocle_apptrace/instrumentation/metamodel/hugging_face/_helper.py +1 -1
  19. monocle_apptrace/instrumentation/metamodel/hugging_face/entities/inference.py +1 -4
  20. monocle_apptrace/instrumentation/metamodel/lambdafunc/_helper.py +1 -1
  21. monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +1 -4
  22. monocle_apptrace/instrumentation/metamodel/langgraph/_helper.py +30 -6
  23. monocle_apptrace/instrumentation/metamodel/langgraph/entities/inference.py +1 -1
  24. monocle_apptrace/instrumentation/metamodel/langgraph/langgraph_processor.py +88 -19
  25. monocle_apptrace/instrumentation/metamodel/langgraph/methods.py +22 -6
  26. monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +30 -10
  27. monocle_apptrace/instrumentation/metamodel/llamaindex/entities/agent.py +4 -3
  28. monocle_apptrace/instrumentation/metamodel/llamaindex/llamaindex_processor.py +15 -7
  29. monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +1 -8
  30. monocle_apptrace/instrumentation/metamodel/mcp/_helper.py +1 -1
  31. monocle_apptrace/instrumentation/metamodel/mistral/_helper.py +1 -1
  32. monocle_apptrace/instrumentation/metamodel/mistral/entities/inference.py +1 -4
  33. monocle_apptrace/instrumentation/metamodel/mistral/methods.py +0 -8
  34. monocle_apptrace/instrumentation/metamodel/openai/_helper.py +47 -7
  35. monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +20 -4
  36. monocle_apptrace/instrumentation/metamodel/openai/methods.py +1 -1
  37. monocle_apptrace/instrumentation/metamodel/strands/_helper.py +44 -0
  38. monocle_apptrace/instrumentation/metamodel/strands/entities/agent.py +179 -0
  39. monocle_apptrace/instrumentation/metamodel/strands/entities/tool.py +62 -0
  40. monocle_apptrace/instrumentation/metamodel/strands/methods.py +20 -0
  41. {monocle_apptrace-0.6.0.dist-info → monocle_apptrace-0.6.6.dist-info}/METADATA +15 -4
  42. {monocle_apptrace-0.6.0.dist-info → monocle_apptrace-0.6.6.dist-info}/RECORD +45 -41
  43. {monocle_apptrace-0.6.0.dist-info → monocle_apptrace-0.6.6.dist-info}/WHEEL +0 -0
  44. {monocle_apptrace-0.6.0.dist-info → monocle_apptrace-0.6.6.dist-info}/entry_points.txt +0 -0
  45. {monocle_apptrace-0.6.0.dist-info → monocle_apptrace-0.6.6.dist-info}/licenses/LICENSE +0 -0
@@ -2,7 +2,7 @@ from opentelemetry.context import attach, detach, get_current, get_value, set_va
2
2
  from monocle_apptrace.instrumentation.common.constants import AGENT_PREFIX_KEY
3
3
  from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
4
4
  from monocle_apptrace.instrumentation.metamodel.llamaindex._helper import (
5
- is_delegation_tool, LLAMAINDEX_AGENT_NAME_KEY, get_agent_name
5
+ is_delegation_tool, LLAMAINDEX_AGENT_NAME_KEY, get_agent_name, get_name, set_current_agent
6
6
  )
7
7
  from monocle_apptrace.instrumentation.metamodel.llamaindex.entities.agent import (
8
8
  AGENT_DELEGATION
@@ -13,14 +13,14 @@ TOOL_INVOCATION_STARTED:str = "llamaindex.tool_invocation_started"
13
13
  class DelegationHandler(SpanHandler):
14
14
  # LlamaIndex uses an internal tool to initate delegation to other agents. The method is tool invoke() with tool name as `transfer_to_<agent_name>`.
15
15
  # Hence we usea different output processor for tool invoke() to format the span as agentic.delegation.
16
- def hydrate_span(self, to_wrap, wrapped, instance, args, kwargs, result, span, parent_span = None, ex:Exception = None) -> bool:
16
+ def hydrate_span(self, to_wrap, wrapped, instance, args, kwargs, result, span, parent_span = None, ex:Exception = None, is_post_exec:bool= False) -> bool:
17
17
  if is_delegation_tool(args, instance):
18
18
  agent_request_wrapper = to_wrap.copy()
19
19
  agent_request_wrapper["output_processor"] = AGENT_DELEGATION
20
20
  else:
21
21
  agent_request_wrapper = to_wrap
22
22
 
23
- return super().hydrate_span(agent_request_wrapper, wrapped, instance, args, kwargs, result, span, parent_span, ex)
23
+ return super().hydrate_span(agent_request_wrapper, wrapped, instance, args, kwargs, result, span, parent_span, ex, is_post_exec)
24
24
 
25
25
  # There are two different APIs for tool calling FunctionTool.call() and AgentWorkflow.tool_call(). In case of single agent calling tool, only the FunctionTool.call() is used. In case of multi agent case,
26
26
  # the AgentWorkflow.tool_call() is used which inturn calls FunctionTool.call(). We can't entirely rely on the FunctionTool.call() to extract tool span details, especially the agent delegation details are not available there.
@@ -29,7 +29,10 @@ class LlamaIndexToolHandler(DelegationHandler):
29
29
  def pre_tracing(self, to_wrap, wrapped, instance, args, kwargs):
30
30
  cur_context = get_current()
31
31
  cur_context = set_value(TOOL_INVOCATION_STARTED, True, cur_context)
32
- return attach(cur_context)
32
+ current_agent = get_value(LLAMAINDEX_AGENT_NAME_KEY)
33
+ if current_agent is not None:
34
+ cur_context = set_value(LLAMAINDEX_AGENT_NAME_KEY, current_agent, cur_context)
35
+ return attach(cur_context), None
33
36
 
34
37
  def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value, token=None):
35
38
  if token:
@@ -44,8 +47,13 @@ class LlamaIndexSingleAgenttToolHandlerWrapper(DelegationHandler):
44
47
  class LlamaIndexAgentHandler(SpanHandler):
45
48
  def pre_tracing(self, to_wrap, wrapped, instance, args, kwargs):
46
49
  cur_context = get_current()
50
+ agent_name = get_name(instance)
51
+
52
+ # Set both OpenTelemetry context and thread-local storage
53
+ set_current_agent(agent_name)
54
+ cur_context = set_value(LLAMAINDEX_AGENT_NAME_KEY, agent_name, cur_context)
47
55
  cur_context = set_value(AGENT_PREFIX_KEY, "handoff", cur_context)
48
- return attach(cur_context)
56
+ return attach(cur_context), None
49
57
 
50
58
  def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value, token=None):
51
59
  if token:
@@ -53,8 +61,8 @@ class LlamaIndexAgentHandler(SpanHandler):
53
61
 
54
62
  # LlamaIndex uses direct OpenAI call for agent inferences. Given that the workflow type is set to llamaindex, the openAI inference does not record the input/output events.
55
63
  # To avoid this, we set the workflow type to generic for agent inference spans so we can capture the prompts and responses.
56
- def hydrate_span(self, to_wrap, wrapped, instance, args, kwargs, result, span, parent_span = None, ex:Exception = None) -> bool:
57
- retval = super().hydrate_span(to_wrap, wrapped, instance, args, kwargs, result, span, parent_span, ex)
64
+ def hydrate_span(self, to_wrap, wrapped, instance, args, kwargs, result, span, parent_span = None, ex:Exception = None, is_post_exec:bool= False) -> bool:
65
+ retval = super().hydrate_span(to_wrap, wrapped, instance, args, kwargs, result, span, parent_span, ex, is_post_exec)
58
66
  if SpanHandler.is_root_span(parent_span):
59
67
  span.set_attribute(LLAMAINDEX_AGENT_NAME_KEY, "")
60
68
  else:
@@ -89,17 +89,10 @@ LLAMAINDEX_METHODS = [
89
89
  {
90
90
  "package": "llama_index.core.agent",
91
91
  "object": "ReActAgent",
92
- "method": "chat",
92
+ "method": "run",
93
93
  "wrapper_method": task_wrapper,
94
94
  "output_processor": AGENT
95
95
  },
96
- {
97
- "package": "llama_index.core.agent",
98
- "object": "ReActAgent",
99
- "method": "achat",
100
- "wrapper_method": atask_wrapper,
101
- "output_processor": AGENT
102
- },
103
96
  {
104
97
  "package": "llama_index.core.agent.workflow.function_agent",
105
98
  "object": "FunctionAgent",
@@ -37,7 +37,7 @@ def get_output_text(arguments):
37
37
 
38
38
  def get_name(arguments):
39
39
  """Get the name of the tool from the instance."""
40
- if 'parent_span' in arguments:
40
+ if 'parent_span' in arguments and arguments['parent_span']:
41
41
  arguments['parent_span'].set_attribute("is_mcp", True)
42
42
  args = arguments["args"]
43
43
  if (
@@ -146,7 +146,7 @@ def update_span_from_llm_response(result, include_token_counts=False):
146
146
  "total_tokens": getattr(result, "total_tokens", 0),
147
147
  } if include_token_counts else {}
148
148
  # Add other metadata fields like finish_reason, etc.
149
- return {**tokens, "inference_sub_type": "turn_end"}
149
+ return {**tokens}
150
150
 
151
151
 
152
152
  def extract_finish_reason(arguments):
@@ -4,6 +4,7 @@ from monocle_apptrace.instrumentation.common.utils import get_error_message, res
4
4
 
5
5
  MISTRAL_INFERENCE = {
6
6
  "type": SPAN_TYPES.INFERENCE,
7
+ "subtype": lambda arguments: _helper.agent_inference_type(arguments),
7
8
  "attributes": [
8
9
  [
9
10
  {
@@ -83,10 +84,6 @@ MISTRAL_INFERENCE = {
83
84
  "_comment": "finish type mapped from finish reason",
84
85
  "attribute": "finish_type",
85
86
  "accessor": lambda arguments: _helper.map_finish_reason_to_finish_type(_helper.extract_finish_reason(arguments))
86
- },
87
- {
88
- "attribute": "inference_sub_type",
89
- "accessor": lambda arguments: _helper.agent_inference_type(arguments)
90
87
  }
91
88
  ]
92
89
  }
@@ -43,14 +43,6 @@ MISTRAL_METHODS = [
43
43
  "wrapper_method": task_wrapper,
44
44
  "output_processor": MISTRAL_RETRIEVAL
45
45
  },
46
- {
47
- "package": "mistralai.embeddings", # where Embeddings is defined
48
- "object": "AsyncEmbeddings", # async embeddings client
49
- "method": "create", # async create
50
- "span_handler": "non_framework_handler",
51
- "wrapper_method": atask_wrapper,
52
- "output_processor": MISTRAL_RETRIEVAL
53
- }
54
46
  ]
55
47
 
56
48
 
@@ -5,6 +5,7 @@ and assistant messages from various input formats.
5
5
 
6
6
  import json
7
7
  import logging
8
+ from urllib.parse import urlparse
8
9
  from opentelemetry.context import get_value
9
10
  from monocle_apptrace.instrumentation.common.utils import (
10
11
  Option,
@@ -23,6 +24,12 @@ from monocle_apptrace.instrumentation.common.constants import AGENT_PREFIX_KEY,
23
24
 
24
25
  logger = logging.getLogger(__name__)
25
26
 
27
+ # Mapping of URL substrings to provider names
28
+ URL_MAP = {
29
+ "deepseek.com": "deepseek",
30
+ # add more providers here as needed
31
+ }
32
+
26
33
  def extract_messages(kwargs):
27
34
  """Extract system and user messages"""
28
35
  try:
@@ -184,8 +191,19 @@ def extract_assistant_message(arguments):
184
191
 
185
192
 
186
193
  def extract_provider_name(instance):
194
+ # Try to get host from base_url if it's a parsed object
187
195
  provider_url: Option[str] = try_option(getattr, instance._client.base_url, 'host')
188
- return provider_url.unwrap_or(None)
196
+ if provider_url.unwrap_or(None) is not None:
197
+ return provider_url.unwrap_or(None)
198
+
199
+ # If base_url is just a string (e.g., "https://api.deepseek.com")
200
+ base_url = getattr(instance._client, "base_url", None)
201
+ if isinstance(base_url, str):
202
+ parsed = urlparse(base_url)
203
+ if parsed.hostname:
204
+ return parsed.hostname
205
+
206
+ return None
189
207
 
190
208
 
191
209
  def extract_inference_endpoint(instance):
@@ -248,29 +266,49 @@ def extract_vector_output(vector_output):
248
266
  return ""
249
267
 
250
268
  def get_inference_type(instance):
269
+ # Check if it's Azure OpenAI first
251
270
  inference_type: Option[str] = try_option(getattr, instance._client, '_api_version')
252
271
  if inference_type.unwrap_or(None):
253
272
  return 'azure_openai'
254
- else:
255
- return 'openai'
273
+
274
+ # Check based on base_url using the mapping
275
+ base_url = getattr(instance, "base_url", None) or getattr(instance._client, "base_url", None)
276
+
277
+ if base_url:
278
+ base_url_str = str(base_url).lower()
279
+ for key, name in URL_MAP.items():
280
+ if key in base_url_str:
281
+ return name
282
+
283
+ # fallback default
284
+ return "openai"
256
285
 
257
286
  class OpenAISpanHandler(NonFrameworkSpanHandler):
258
287
  def is_teams_span_in_progress(self) -> bool:
259
288
  return self.is_framework_span_in_progress() and self.get_workflow_name_in_progress() == WORKFLOW_TYPE_MAP["teams.ai"]
289
+
290
+ def is_llamaindex_span_in_progress(self) -> bool:
291
+ return self.is_framework_span_in_progress() and self.get_workflow_name_in_progress() == WORKFLOW_TYPE_MAP["llama_index"]
260
292
 
261
- # If openAI is being called by Teams AI SDK, then retain the metadata part of the span events
293
+ # If openAI is being called by Teams AI SDK or LlamaIndex, customize event processing
262
294
  def skip_processor(self, to_wrap, wrapped, instance, span, args, kwargs) -> list[str]:
263
295
  if self.is_teams_span_in_progress():
264
296
  return ["attributes", "events.data.input", "events.data.output"]
297
+ elif self.is_llamaindex_span_in_progress():
298
+ # For LlamaIndex, we want to keep all inference span attributes and events
299
+ return []
265
300
  else:
266
301
  return super().skip_processor(to_wrap, wrapped, instance, span, args, kwargs)
267
302
 
268
- def hydrate_events(self, to_wrap, wrapped, instance, args, kwargs, ret_result, span, parent_span=None, ex:Exception=None) -> bool:
303
+ def hydrate_events(self, to_wrap, wrapped, instance, args, kwargs, ret_result, span, parent_span=None, ex:Exception=None, is_post_exec:bool=False) -> bool:
269
304
  # If openAI is being called by Teams AI SDK, then copy parent
270
305
  if self.is_teams_span_in_progress() and ex is None:
271
- return super().hydrate_events(to_wrap, wrapped, instance, args, kwargs, ret_result, span=parent_span, parent_span=None, ex=ex)
306
+ return super().hydrate_events(to_wrap, wrapped, instance, args, kwargs, ret_result, span=parent_span, parent_span=None, ex=ex, is_post_exec=is_post_exec)
307
+ # For LlamaIndex, process events normally on the inference span
308
+ elif self.is_llamaindex_span_in_progress():
309
+ return super().hydrate_events(to_wrap, wrapped, instance, args, kwargs, ret_result, span, parent_span=parent_span, ex=ex, is_post_exec=is_post_exec)
272
310
 
273
- return super().hydrate_events(to_wrap, wrapped, instance, args, kwargs, ret_result, span, parent_span=parent_span, ex=ex)
311
+ return super().hydrate_events(to_wrap, wrapped, instance, args, kwargs, ret_result, span, parent_span=parent_span, ex=ex, is_post_exec=is_post_exec)
274
312
 
275
313
  def post_task_processing(self, to_wrap, wrapped, instance, args, kwargs, result, ex, span, parent_span):
276
314
  # TeamsAI doesn't capture the status and other metadata from underlying OpenAI SDK.
@@ -316,3 +354,5 @@ def agent_inference_type(arguments):
316
354
  return INFERENCE_AGENT_DELEGATION
317
355
  return INFERENCE_TOOL_CALL
318
356
  return INFERENCE_TURN_END
357
+
358
+
@@ -11,6 +11,7 @@ from monocle_apptrace.instrumentation.common.utils import (
11
11
  patch_instance_method,
12
12
  resolve_from_alias,
13
13
  )
14
+ from monocle_apptrace.instrumentation.common.constants import PROVIDER_BASE_URLS
14
15
 
15
16
  logger = logging.getLogger(__name__)
16
17
 
@@ -120,6 +121,24 @@ def _create_span_result(state, stream_start_time):
120
121
  finish_reason=state["finish_reason"],
121
122
  )
122
123
 
124
+ # Registry mapping client detection functions → entity_type
125
+ CLIENT_ENTITY_MAP = {
126
+ "deepseek": "inference.deepseek",
127
+ # add more clients in future
128
+ }
129
+
130
+ def get_entity_type(response, helper=None):
131
+ for client_name, entity in CLIENT_ENTITY_MAP.items():
132
+ check_fn = globals().get(f"is_{client_name}_client")
133
+ if check_fn and check_fn(response):
134
+ return entity
135
+
136
+ # fallback to helper if available
137
+ if helper and hasattr(helper, "get_inference_type"):
138
+ return "inference." + helper.get_inference_type(response)
139
+
140
+ # default fallback
141
+ return "inference.openai"
123
142
 
124
143
  def process_stream(to_wrap, response, span_processor):
125
144
  stream_start_time = time.time_ns()
@@ -167,6 +186,7 @@ def process_stream(to_wrap, response, span_processor):
167
186
 
168
187
  INFERENCE = {
169
188
  "type": SPAN_TYPES.INFERENCE,
189
+ "subtype": lambda arguments: _helper.agent_inference_type(arguments),
170
190
  "is_auto_close": lambda kwargs: kwargs.get("stream", False) is False,
171
191
  "response_processor": process_stream,
172
192
  "attributes": [
@@ -276,10 +296,6 @@ INFERENCE = {
276
296
  "accessor": lambda arguments: _helper.map_finish_reason_to_finish_type(
277
297
  _helper.extract_finish_reason(arguments)
278
298
  ),
279
- },
280
- {
281
- "attribute": "inference_sub_type",
282
- "accessor": lambda arguments: _helper.agent_inference_type(arguments)
283
299
  }
284
300
  ],
285
301
  },
@@ -56,4 +56,4 @@ OPENAI_METHODS = [
56
56
  "output_processor": INFERENCE
57
57
  }
58
58
 
59
- ]
59
+ ]
@@ -0,0 +1,44 @@
1
+
2
+ from monocle_apptrace.instrumentation.common.constants import SPAN_TYPES
3
+
4
+
5
+ def get_agent_name(instance):
6
+ return instance.name
7
+
8
+ def get_agent_description(instance):
9
+ return instance.description
10
+
11
+ def extract_agent_input(arguments):
12
+ return arguments['args'][0]
13
+
14
+ def extract_agent_response(result):
15
+ return result.message['content'][0]['text']
16
+
17
+ def get_tool_type(arguments):
18
+ ## TODO: check for MCP type
19
+ return "tool.strands"
20
+
21
+ def get_tool_name(arguments):
22
+ return arguments.get('args')[1][0]['name']
23
+
24
+ def get_tool_description(arguments):
25
+ return arguments.get('args')[1][0]['description']
26
+
27
+ def get_source_agent(arguments):
28
+ return arguments.get('args')[0].name
29
+
30
+ def extract_tool_input(arguments):
31
+ return str(arguments.get('args')[1][0]['input'])
32
+
33
+ def extract_tool_response(result):
34
+ return result.tool_result['content'][0]['text']
35
+
36
+ def should_skip_delegation(arguments):
37
+ if arguments.get('parent_span') and arguments.get('parent_span').attributes.get("span.type") != SPAN_TYPES.AGENTIC_TOOL_INVOCATION:
38
+ return True
39
+ return False
40
+
41
+ def should_skip_request(arguments):
42
+ if arguments.get('parent_span') and arguments.get('parent_span').attributes.get("span.type") in [SPAN_TYPES.AGENTIC_TOOL_INVOCATION, SPAN_TYPES.AGENTIC_REQUEST]:
43
+ return True
44
+ return False
@@ -0,0 +1,179 @@
1
+ from monocle_apptrace.instrumentation.common.constants import AGENT_REQUEST_SPAN_NAME, SPAN_SUBTYPES, SPAN_TYPES
2
+ from monocle_apptrace.instrumentation.metamodel.strands import (
3
+ _helper
4
+ )
5
+ from monocle_apptrace.instrumentation.common.utils import get_error_message
6
+
7
+ AGENT = {
8
+ "type": SPAN_TYPES.AGENTIC_INVOCATION,
9
+ "subtype": SPAN_SUBTYPES.CONTENT_PROCESSING,
10
+ "attributes": [
11
+ [
12
+ {
13
+ "_comment": "agent type",
14
+ "attribute": "type",
15
+ "accessor": lambda arguments:'agent.strands'
16
+ },
17
+ {
18
+ "_comment": "name of the agent",
19
+ "attribute": "name",
20
+ "accessor": lambda arguments: _helper.get_agent_name(arguments['instance'])
21
+ },
22
+ {
23
+ "_comment": "agent description",
24
+ "attribute": "description",
25
+ "accessor": lambda arguments: _helper.get_agent_description(arguments['instance'])
26
+ }
27
+ ]
28
+ ],
29
+ "events": [
30
+ {
31
+ "name":"data.input",
32
+ "attributes": [
33
+ {
34
+ "_comment": "this is Agent input",
35
+ "attribute": "input",
36
+ "accessor": lambda arguments: _helper.extract_agent_input(arguments)
37
+ }
38
+ ]
39
+ },
40
+ {
41
+ "name":"data.output",
42
+ "attributes": [
43
+ {
44
+ "attribute": "error_code",
45
+ "accessor": lambda arguments: get_error_message(arguments)
46
+ },
47
+ {
48
+ "_comment": "this is response from LLM",
49
+ "attribute": "response",
50
+ "accessor": lambda arguments: _helper.extract_agent_response(arguments['result'])
51
+ }
52
+ ]
53
+ }
54
+ ]
55
+ }
56
+
57
+ AGENT_REQUEST = {
58
+ "type": SPAN_TYPES.AGENTIC_REQUEST,
59
+ "subtype": SPAN_SUBTYPES.PLANNING,
60
+ "should_skip": lambda arguments: _helper.should_skip_request(arguments),
61
+ "attributes": [
62
+ [
63
+ {
64
+ "_comment": "agent type",
65
+ "attribute": "type",
66
+ "accessor": lambda arguments:'agent.strands'
67
+ }
68
+ ],
69
+ ],
70
+ "events": [
71
+ {
72
+ "name":"data.input",
73
+ "attributes": [
74
+ {
75
+ "_comment": "this is Agent input",
76
+ "attribute": "input",
77
+ "accessor": lambda arguments: _helper.extract_agent_input(arguments)
78
+ }
79
+ ]
80
+ },
81
+ {
82
+ "name":"data.output",
83
+ "attributes": [
84
+ {
85
+ "attribute": "error_code",
86
+ "accessor": lambda arguments: get_error_message(arguments)
87
+ },
88
+ {
89
+ "_comment": "this is response from LLM",
90
+ "attribute": "response",
91
+ "accessor": lambda arguments: _helper.extract_agent_response(arguments['result'])
92
+ }
93
+ ]
94
+ }
95
+ ]
96
+ }
97
+
98
+ TOOLS = {
99
+ "type": SPAN_TYPES.AGENTIC_TOOL_INVOCATION,
100
+ "subtype": SPAN_SUBTYPES.ROUTING,
101
+ "attributes": [
102
+ [
103
+ {
104
+ "_comment": "tool type",
105
+ "attribute": "type",
106
+ "accessor": lambda arguments: _helper.get_tool_type(arguments['span'])
107
+ },
108
+ {
109
+ "_comment": "name of the tool",
110
+ "attribute": "name",
111
+ "accessor": lambda arguments: _helper.get_tool_name(arguments['instance'])
112
+ },
113
+ {
114
+ "_comment": "tool description",
115
+ "attribute": "description",
116
+ "accessor": lambda arguments: _helper.get_tool_description(arguments['instance'])
117
+ }
118
+ ],
119
+ [
120
+ {
121
+ "_comment": "name of the agent",
122
+ "attribute": "name",
123
+ "accessor": lambda arguments: _helper.get_source_agent()
124
+ },
125
+ {
126
+ "_comment": "agent type",
127
+ "attribute": "type",
128
+ "accessor": lambda arguments:'agent.strands'
129
+ }
130
+ ]
131
+ ],
132
+ "events": [
133
+ {
134
+ "name":"data.input",
135
+ "attributes": [
136
+ {
137
+ "_comment": "this is Tool input",
138
+ "attribute": "input",
139
+ "accessor": lambda arguments: _helper.extract_tool_input(arguments)
140
+ },
141
+ ]
142
+ },
143
+ {
144
+ "name":"data.output",
145
+ "attributes": [
146
+ {
147
+ "_comment": "this is response from Tool",
148
+ "attribute": "response",
149
+ "accessor": lambda arguments: _helper.extract_tool_response(arguments['result'])
150
+ }
151
+ ]
152
+ }
153
+ ]
154
+ }
155
+
156
+ AGENT_DELEGATION = {
157
+ "type": SPAN_TYPES.AGENTIC_DELEGATION,
158
+ "subtype": SPAN_SUBTYPES.ROUTING,
159
+ "should_skip": lambda arguments: _helper.should_skip_delegation(arguments),
160
+ "attributes": [
161
+ [
162
+ {
163
+ "_comment": "agent type",
164
+ "attribute": "type",
165
+ "accessor": lambda arguments:'agent.strands'
166
+ },
167
+ {
168
+ "_comment": "name of the agent",
169
+ "attribute": "from_agent",
170
+ "accessor": lambda arguments: _helper.get_source_agent()
171
+ },
172
+ {
173
+ "_comment": "name of the agent called",
174
+ "attribute": "to_agent",
175
+ "accessor": lambda arguments: _helper.get_target_agent(arguments['instance'])
176
+ }
177
+ ]
178
+ ]
179
+ }
@@ -0,0 +1,62 @@
1
+ from monocle_apptrace.instrumentation.common.constants import AGENT_REQUEST_SPAN_NAME, SPAN_SUBTYPES, SPAN_TYPES
2
+ from monocle_apptrace.instrumentation.metamodel.strands import (
3
+ _helper
4
+ )
5
+ from monocle_apptrace.instrumentation.common.utils import get_error_message
6
+ TOOL = {
7
+ "type": SPAN_TYPES.AGENTIC_TOOL_INVOCATION,
8
+ "subtype": SPAN_SUBTYPES.CONTENT_GENERATION,
9
+ "attributes": [
10
+ [
11
+ {
12
+ "_comment": "tool type",
13
+ "attribute": "type",
14
+ "accessor": lambda arguments: _helper.get_tool_type(arguments)
15
+ },
16
+ {
17
+ "_comment": "name of the tool",
18
+ "attribute": "name",
19
+ "accessor": lambda arguments: _helper.get_tool_name(arguments)
20
+ },
21
+ {
22
+ "_comment": "tool description",
23
+ "attribute": "description",
24
+ "accessor": lambda arguments: _helper.get_tool_description(arguments)
25
+ }
26
+ ],
27
+ [
28
+ {
29
+ "_comment": "name of the agent",
30
+ "attribute": "name",
31
+ "accessor": lambda arguments: _helper.get_source_agent(arguments)
32
+ },
33
+ {
34
+ "_comment": "agent type",
35
+ "attribute": "type",
36
+ "accessor": lambda arguments:'agent.stands'
37
+ }
38
+ ]
39
+ ],
40
+ "events": [
41
+ {
42
+ "name":"data.input",
43
+ "attributes": [
44
+ {
45
+ "_comment": "this is Tool input",
46
+ "attribute": "input",
47
+ "accessor": lambda arguments: _helper.extract_tool_input(arguments)
48
+ },
49
+ ]
50
+ },
51
+ {
52
+ "name":"data.output",
53
+ "attributes": [
54
+ {
55
+ "_comment": "this is response from Tool",
56
+ "attribute": "response",
57
+ "accessor": lambda arguments: _helper.extract_tool_response(arguments['result'])
58
+ }
59
+ ]
60
+ }
61
+ ]
62
+ }
@@ -0,0 +1,20 @@
1
+ from monocle_apptrace.instrumentation.common.wrapper import task_wrapper, atask_wrapper, atask_iter_wrapper
2
+ from monocle_apptrace.instrumentation.metamodel.strands.entities.agent import AGENT, AGENT_REQUEST, AGENT_DELEGATION
3
+ from monocle_apptrace.instrumentation.metamodel.strands.entities.tool import TOOL
4
+
5
+ STRAND_METHODS = [
6
+ {
7
+ "package": "strands.agent.agent",
8
+ "object": "Agent",
9
+ "method": "__call__",
10
+ "wrapper_method": task_wrapper,
11
+ "output_processor_list": [AGENT_REQUEST, AGENT_DELEGATION, AGENT]
12
+ },
13
+ {
14
+ "package": "strands.tools.executors.concurrent",
15
+ "object": "ConcurrentToolExecutor",
16
+ "method": "_execute",
17
+ "wrapper_method": atask_iter_wrapper,
18
+ "output_processor": TOOL,
19
+ }
20
+ ]