@@ -396,11 +396,53 @@ class FunctionTool:
396396 )
397397 """Internal descriptor hook used for instance methods decorated with `@function_tool`."""
398398
399+ _staticmethod_tool_factory : Callable [[], FunctionTool ] | None = field (
400+ default = None ,
401+ kw_only = True ,
402+ repr = False ,
403+ )
404+ """Internal fallback for class-scoped tools wrapped in `staticmethod`."""
405+
406+ _method_tool_bound_to_class : bool = field (default = False , kw_only = True , repr = False )
407+ """Whether Python installed this tool directly on a class via `__set_name__`."""
408+
409+ def __set_name__ (self , owner : type [Any ], name : str ) -> None :
410+ if self ._staticmethod_tool_factory is not None :
411+ self ._method_tool_bound_to_class = True
412+
413+ def __getattribute__ (self , name : str ) -> Any :
414+ if not name .startswith ("_" ) and name not in {"__class__" , "__dict__" }:
415+ object .__getattribute__ (self , "_maybe_apply_staticmethod_tool" )()
416+ return object .__getattribute__ (self , name )
417+
399418 def __get__ (self , instance : Any , owner : type [Any ] | None = None ) -> FunctionTool :
400419 if instance is None or self ._method_tool_factory is None :
401420 return self
402421 return self ._method_tool_factory (instance )
403422
423+ def _maybe_apply_staticmethod_tool (self ) -> None :
424+ try :
425+ staticmethod_tool_factory = object .__getattribute__ (self , "_staticmethod_tool_factory" )
426+ method_tool_bound_to_class = object .__getattribute__ (
427+ self , "_method_tool_bound_to_class"
428+ )
429+ except AttributeError :
430+ return
431+
432+ if staticmethod_tool_factory is None or method_tool_bound_to_class :
433+ return
434+
435+ # `staticmethod` does not forward `__set_name__` to the wrapped FunctionTool.
436+ # Rebuild as a normal tool before exposing schema or invocation state.
437+ object .__setattr__ (self , "_staticmethod_tool_factory" , None )
438+ staticmethod_tool = staticmethod_tool_factory ()
439+ for tool_field in dataclasses .fields (FunctionTool ):
440+ object .__setattr__ (self , tool_field .name , getattr (staticmethod_tool , tool_field .name ))
441+
442+ bind_to_function_tool = getattr (self .on_invoke_tool , "__agents_bind_function_tool__" , None )
443+ if callable (bind_to_function_tool ):
444+ self .on_invoke_tool = bind_to_function_tool (self )
445+
404446 @property
405447 def qualified_name (self ) -> str :
406448 """Return the public qualified name used to identify this function tool."""
@@ -1860,9 +1902,15 @@ def _create_function_tool(
18601902 the_func : ToolFunction [...],
18611903 * ,
18621904 method_tool_instance : Any | None = None ,
1905+ treat_as_instance_method : bool | None = None ,
1906+ enable_method_binding : bool = True ,
18631907 ) -> FunctionTool :
18641908 is_sync_function_tool = not inspect .iscoroutinefunction (the_func )
1865- is_instance_method_tool = _is_instance_method_tool (the_func )
1909+ is_instance_method_tool = (
1910+ _is_instance_method_tool (the_func )
1911+ if treat_as_instance_method is None
1912+ else treat_as_instance_method
1913+ )
18661914 schema = function_schema (
18671915 func = the_func ,
18681916 name_override = name_override ,
@@ -1937,10 +1985,17 @@ async def _on_invoke_tool_impl(ctx: ToolContext[Any], input: str) -> Any:
19371985 defer_loading = defer_loading ,
19381986 sync_invoker = is_sync_function_tool ,
19391987 )
1940- if is_instance_method_tool and method_tool_instance is None :
1988+ if enable_method_binding and is_instance_method_tool and method_tool_instance is None :
19411989 function_tool ._method_tool_factory = lambda instance : _create_function_tool (
19421990 the_func ,
19431991 method_tool_instance = instance ,
1992+ treat_as_instance_method = True ,
1993+ enable_method_binding = False ,
1994+ )
1995+ function_tool ._staticmethod_tool_factory = lambda : _create_function_tool (
1996+ the_func ,
1997+ treat_as_instance_method = False ,
1998+ enable_method_binding = False ,
19441999 )
19452000 return function_tool
19462001
0 commit comments