5151from ..session .session_manager import SessionManager
5252from ..telemetry .metrics import EventLoopMetrics
5353from ..telemetry .tracer import get_tracer , serialize
54+ from ..tools .caller import _ToolCaller
5455from ..tools .executors import ConcurrentToolExecutor
5556from ..tools .executors ._executor import ToolExecutor
5657from ..tools .registry import ToolRegistry
@@ -102,116 +103,6 @@ class Agent:
102103 6. Produces a final response
103104 """
104105
105- class ToolCaller :
106- """Call tool as a function."""
107-
108- def __init__ (self , agent : "Agent" ) -> None :
109- """Initialize instance.
110-
111- Args:
112- agent: Agent reference that will accept tool results.
113- """
114- # WARNING: Do not add any other member variables or methods as this could result in a name conflict with
115- # agent tools and thus break their execution.
116- self ._agent = agent
117-
118- def __getattr__ (self , name : str ) -> Callable [..., Any ]:
119- """Call tool as a function.
120-
121- This method enables the method-style interface (e.g., `agent.tool.tool_name(param="value")`).
122- It matches underscore-separated names to hyphenated tool names (e.g., 'some_thing' matches 'some-thing').
123-
124- Args:
125- name: The name of the attribute (tool) being accessed.
126-
127- Returns:
128- A function that when called will execute the named tool.
129-
130- Raises:
131- AttributeError: If no tool with the given name exists or if multiple tools match the given name.
132- """
133-
134- def caller (
135- user_message_override : Optional [str ] = None ,
136- record_direct_tool_call : Optional [bool ] = None ,
137- ** kwargs : Any ,
138- ) -> Any :
139- """Call a tool directly by name.
140-
141- Args:
142- user_message_override: Optional custom message to record instead of default
143- record_direct_tool_call: Whether to record direct tool calls in message history. Overrides class
144- attribute if provided.
145- **kwargs: Keyword arguments to pass to the tool.
146-
147- Returns:
148- The result returned by the tool.
149-
150- Raises:
151- AttributeError: If the tool doesn't exist.
152- """
153- if self ._agent ._interrupt_state .activated :
154- raise RuntimeError ("cannot directly call tool during interrupt" )
155-
156- normalized_name = self ._find_normalized_tool_name (name )
157-
158- # Create unique tool ID and set up the tool request
159- tool_id = f"tooluse_{ name } _{ random .randint (100000000 , 999999999 )} "
160- tool_use : ToolUse = {
161- "toolUseId" : tool_id ,
162- "name" : normalized_name ,
163- "input" : kwargs .copy (),
164- }
165- tool_results : list [ToolResult ] = []
166- invocation_state = kwargs
167-
168- async def acall () -> ToolResult :
169- async for event in ToolExecutor ._stream (self ._agent , tool_use , tool_results , invocation_state ):
170- if isinstance (event , ToolInterruptEvent ):
171- self ._agent ._interrupt_state .deactivate ()
172- raise RuntimeError ("cannot raise interrupt in direct tool call" )
173-
174- return tool_results [0 ]
175-
176- tool_result = run_async (acall )
177-
178- if record_direct_tool_call is not None :
179- should_record_direct_tool_call = record_direct_tool_call
180- else :
181- should_record_direct_tool_call = self ._agent .record_direct_tool_call
182-
183- if should_record_direct_tool_call :
184- # Create a record of this tool execution in the message history
185- self ._agent ._record_tool_execution (tool_use , tool_result , user_message_override )
186-
187- # Apply window management
188- self ._agent .conversation_manager .apply_management (self ._agent )
189-
190- return tool_result
191-
192- return caller
193-
194- def _find_normalized_tool_name (self , name : str ) -> str :
195- """Lookup the tool represented by name, replacing characters with underscores as necessary."""
196- tool_registry = self ._agent .tool_registry .registry
197-
198- if tool_registry .get (name , None ):
199- return name
200-
201- # If the desired name contains underscores, it might be a placeholder for characters that can't be
202- # represented as python identifiers but are valid as tool names, such as dashes. In that case, find
203- # all tools that can be represented with the normalized name
204- if "_" in name :
205- filtered_tools = [
206- tool_name for (tool_name , tool ) in tool_registry .items () if tool_name .replace ("-" , "_" ) == name
207- ]
208-
209- # The registry itself defends against similar names, so we can just take the first match
210- if filtered_tools :
211- return filtered_tools [0 ]
212-
213- raise AttributeError (f"Tool '{ name } ' not found" )
214-
215106 def __init__ (
216107 self ,
217108 model : Union [Model , str , None ] = None ,
@@ -349,7 +240,7 @@ def __init__(
349240 else :
350241 self .state = AgentState ()
351242
352- self .tool_caller = Agent . ToolCaller (self )
243+ self .tool_caller = _ToolCaller (self )
353244
354245 self .hooks = HookRegistry ()
355246
@@ -368,7 +259,7 @@ def __init__(
368259 self .hooks .invoke_callbacks (AgentInitializedEvent (agent = self ))
369260
370261 @property
371- def tool (self ) -> ToolCaller :
262+ def tool (self ) -> _ToolCaller :
372263 """Call tool as a function.
373264
374265 Returns:
0 commit comments