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.
- monocle_apptrace/instrumentation/common/constants.py +8 -0
- monocle_apptrace/instrumentation/common/span_handler.py +73 -23
- monocle_apptrace/instrumentation/common/utils.py +63 -6
- monocle_apptrace/instrumentation/common/wrapper.py +111 -42
- monocle_apptrace/instrumentation/common/wrapper_method.py +4 -2
- monocle_apptrace/instrumentation/metamodel/a2a/methods.py +1 -1
- monocle_apptrace/instrumentation/metamodel/adk/_helper.py +2 -1
- monocle_apptrace/instrumentation/metamodel/agents/_helper.py +3 -3
- monocle_apptrace/instrumentation/metamodel/agents/entities/inference.py +2 -0
- monocle_apptrace/instrumentation/metamodel/aiohttp/_helper.py +1 -1
- monocle_apptrace/instrumentation/metamodel/anthropic/entities/inference.py +1 -4
- monocle_apptrace/instrumentation/metamodel/azfunc/_helper.py +1 -1
- monocle_apptrace/instrumentation/metamodel/botocore/_helper.py +5 -0
- monocle_apptrace/instrumentation/metamodel/botocore/entities/inference.py +4 -0
- monocle_apptrace/instrumentation/metamodel/fastapi/_helper.py +4 -4
- monocle_apptrace/instrumentation/metamodel/fastapi/methods.py +4 -4
- monocle_apptrace/instrumentation/metamodel/flask/_helper.py +3 -3
- monocle_apptrace/instrumentation/metamodel/hugging_face/_helper.py +1 -1
- monocle_apptrace/instrumentation/metamodel/hugging_face/entities/inference.py +1 -4
- monocle_apptrace/instrumentation/metamodel/lambdafunc/_helper.py +1 -1
- monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +1 -4
- monocle_apptrace/instrumentation/metamodel/langgraph/_helper.py +30 -6
- monocle_apptrace/instrumentation/metamodel/langgraph/entities/inference.py +1 -1
- monocle_apptrace/instrumentation/metamodel/langgraph/langgraph_processor.py +88 -19
- monocle_apptrace/instrumentation/metamodel/langgraph/methods.py +22 -6
- monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +30 -10
- monocle_apptrace/instrumentation/metamodel/llamaindex/entities/agent.py +4 -3
- monocle_apptrace/instrumentation/metamodel/llamaindex/llamaindex_processor.py +15 -7
- monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +1 -8
- monocle_apptrace/instrumentation/metamodel/mcp/_helper.py +1 -1
- monocle_apptrace/instrumentation/metamodel/mistral/_helper.py +1 -1
- monocle_apptrace/instrumentation/metamodel/mistral/entities/inference.py +1 -4
- monocle_apptrace/instrumentation/metamodel/mistral/methods.py +0 -8
- monocle_apptrace/instrumentation/metamodel/openai/_helper.py +47 -7
- monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +20 -4
- monocle_apptrace/instrumentation/metamodel/openai/methods.py +1 -1
- monocle_apptrace/instrumentation/metamodel/strands/_helper.py +44 -0
- monocle_apptrace/instrumentation/metamodel/strands/entities/agent.py +179 -0
- monocle_apptrace/instrumentation/metamodel/strands/entities/tool.py +62 -0
- monocle_apptrace/instrumentation/metamodel/strands/methods.py +20 -0
- {monocle_apptrace-0.6.0.dist-info → monocle_apptrace-0.6.6.dist-info}/METADATA +15 -4
- {monocle_apptrace-0.6.0.dist-info → monocle_apptrace-0.6.6.dist-info}/RECORD +45 -41
- {monocle_apptrace-0.6.0.dist-info → monocle_apptrace-0.6.6.dist-info}/WHEEL +0 -0
- {monocle_apptrace-0.6.0.dist-info → monocle_apptrace-0.6.6.dist-info}/entry_points.txt +0 -0
- {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
|
-
|
|
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": "
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
255
|
-
|
|
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
|
|
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
|
},
|
|
@@ -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
|
+
]
|