2828
2929
3030def _patch_tool_execution () -> None :
31+ if hasattr (ToolManager , "execute_tool_call" ):
32+ _patch_execute_tool_call ()
33+
34+ elif hasattr (ToolManager , "_call_tool" ):
35+ # older versions
36+ _patch_call_tool ()
37+
38+
39+ def _patch_execute_tool_call () -> None :
40+ original_execute_tool_call = ToolManager .execute_tool_call
41+
42+ @wraps (original_execute_tool_call )
43+ async def wrapped_execute_tool_call (
44+ self : "Any" , validated : "Any" , * args : "Any" , ** kwargs : "Any"
45+ ) -> "Any" :
46+ # Extract tool info before calling original
47+ call = validated .call
48+ name = call .tool_name
49+ tool = self .tools .get (name ) if self .tools else None
50+
51+ # Determine tool type by checking tool.toolset
52+ tool_type = "function" # default
53+ if tool and HAS_MCP and isinstance (tool .toolset , MCPServer ):
54+ tool_type = "mcp"
55+
56+ # Get agent from contextvar
57+ agent = get_current_agent ()
58+
59+ if agent and tool :
60+ try :
61+ args_dict = call .args_as_dict ()
62+ except Exception :
63+ args_dict = call .args if isinstance (call .args , dict ) else {}
64+
65+ # Create execute_tool span
66+ # Nesting is handled by isolation_scope() to ensure proper parent-child relationships
67+ with sentry_sdk .isolation_scope ():
68+ with execute_tool_span (
69+ name ,
70+ args_dict ,
71+ agent ,
72+ tool_type = tool_type ,
73+ ) as span :
74+ try :
75+ result = await original_execute_tool_call (
76+ self ,
77+ validated ,
78+ * args ,
79+ ** kwargs ,
80+ )
81+ update_execute_tool_span (span , result )
82+ return result
83+ except ToolRetryError as exc :
84+ exc_info = sys .exc_info ()
85+ with capture_internal_exceptions ():
86+ # Avoid circular import due to multi-file integration structure
87+ from sentry_sdk .integrations .pydantic_ai import (
88+ PydanticAIIntegration ,
89+ )
90+
91+ integration = sentry_sdk .get_client ().get_integration (
92+ PydanticAIIntegration
93+ )
94+ if (
95+ integration is not None
96+ and integration .handled_tool_call_exceptions
97+ ):
98+ _capture_exception (exc , handled = True )
99+ reraise (* exc_info )
100+
101+ return await original_execute_tool_call (self , validated , * args , ** kwargs )
102+
103+ ToolManager .execute_tool_call = wrapped_execute_tool_call
104+
105+
106+ def _patch_call_tool () -> None :
31107 """
32108 Patch ToolManager._call_tool to create execute_tool spans.
33109
@@ -39,7 +115,6 @@ def _patch_tool_execution() -> None:
39115 - Dealing with signature mismatches from instrumented MCP servers
40116 - Complex nested toolset handling
41117 """
42-
43118 original_call_tool = ToolManager ._call_tool
44119
45120 @wraps (original_call_tool )
0 commit comments