From 029e910feaac40d56f59a01b2f590494c5886992 Mon Sep 17 00:00:00 2001 From: Kevin Pham <161087761+accupham@users.noreply.github.com> Date: Wed, 12 Feb 2025 22:03:45 -0600 Subject: [PATCH 1/9] Add method support for tool decorator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original tool decorator doesn't work on class methods because it unconditionally adds a self parameter treats method as if it were a plain function. That does not play nicely with Python’s bound-method behavior. This inspects the signature to prevent double-binding of self. --- src/smolagents/tools.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/smolagents/tools.py b/src/smolagents/tools.py index d73fcceae..bd75893bc 100644 --- a/src/smolagents/tools.py +++ b/src/smolagents/tools.py @@ -873,11 +873,23 @@ def __init__( function=tool_function, ) original_signature = inspect.signature(tool_function) - new_parameters = [inspect.Parameter("self", inspect.Parameter.POSITIONAL_ONLY)] + list( - original_signature.parameters.values() - ) - new_signature = original_signature.replace(parameters=new_parameters) + original_params = list(original_signature.parameters.values()) + + # check if the function already has a "self" or "cls" as the first parameter + if not original_params: + new_params = [inspect.Parameter("self", inspect.Parameter.POSITIONAL_ONLY)] + else: + first_param_name = original_params[0].name + if first_param_name in ("self", "cls"): + # already a method signature; don't add an extra self. + new_params = original_params + else: + # free function signature; prepend self. + new_params = [inspect.Parameter("self", inspect.Parameter.POSITIONAL_ONLY)] + original_params + + new_signature = original_signature.replace(parameters=new_params) simple_tool.forward.__signature__ = new_signature + return simple_tool From 164ba1dcb50e8a0d61f689aeb0a2f5d1c707eb6d Mon Sep 17 00:00:00 2001 From: Kevin Pham <161087761+accupham@users.noreply.github.com> Date: Wed, 12 Feb 2025 22:39:58 -0600 Subject: [PATCH 2/9] Update tools.py --- src/smolagents/tools.py | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/smolagents/tools.py b/src/smolagents/tools.py index bd75893bc..5c8220436 100644 --- a/src/smolagents/tools.py +++ b/src/smolagents/tools.py @@ -872,24 +872,33 @@ def __init__( output_type=tool_json_schema["return"]["type"], function=tool_function, ) - original_signature = inspect.signature(tool_function) - original_params = list(original_signature.parameters.values()) + original_sigature = inspect.signature(tool_function) + original_params = list(original_sig.parameters.values()) - # check if the function already has a "self" or "cls" as the first parameter - if not original_params: - new_params = [inspect.Parameter("self", inspect.Parameter.POSITIONAL_ONLY)] - else: - first_param_name = original_params[0].name - if first_param_name in ("self", "cls"): - # already a method signature; don't add an extra self. - new_params = original_params + is_method = ( + len(original_params) > 0 + and original_params[0].name in ("self", "cls") + ) + + @functools.wraps(tool_function) + def forward(*args, **kwargs): + if is_method: + return tool_function(*args, **kwargs) else: - # free function signature; prepend self. - new_params = [inspect.Parameter("self", inspect.Parameter.POSITIONAL_ONLY)] + original_params + # skip the 'self' argument. + return tool_function(*args[1:], **kwargs) + + if is_method: + new_sig = original_sig + else: + new_params = [ + inspect.Parameter("self", inspect.Parameter.POSITIONAL_ONLY) + ] + original_params + new_sig = original_signature.replace(parameters=new_params) - new_signature = original_signature.replace(parameters=new_params) - simple_tool.forward.__signature__ = new_signature + forward.__signature__ = new_sig + simple_tool.forward = forward return simple_tool From 163249e83a5cc0dacbab44bcaab9677324514aa3 Mon Sep 17 00:00:00 2001 From: Kevin Pham <161087761+accupham@users.noreply.github.com> Date: Thu, 13 Feb 2025 22:41:37 -0600 Subject: [PATCH 3/9] Update src/smolagents/tools.py Co-authored-by: Alex --- src/smolagents/tools.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/smolagents/tools.py b/src/smolagents/tools.py index 5c8220436..886df93ca 100644 --- a/src/smolagents/tools.py +++ b/src/smolagents/tools.py @@ -875,10 +875,7 @@ def __init__( original_sigature = inspect.signature(tool_function) original_params = list(original_sig.parameters.values()) - is_method = ( - len(original_params) > 0 - and original_params[0].name in ("self", "cls") - ) + is_method = inspect.ismethod(tool_function) @functools.wraps(tool_function) def forward(*args, **kwargs): From 3c1c9937d60938d2dae854fa4e16a47629b7c79a Mon Sep 17 00:00:00 2001 From: Kevin Pham Date: Mon, 17 Feb 2025 22:02:36 -0600 Subject: [PATCH 4/9] Add tests & documentation for tool decorators on class methods --- docs/source/en/guided_tour.md | 49 +++++- src/smolagents/_function_type_hints_utils.py | 17 +- src/smolagents/tools.py | 83 +++++++--- tests/test_tools.py | 161 +++++++++++++++++++ 4 files changed, 278 insertions(+), 32 deletions(-) diff --git a/docs/source/en/guided_tour.md b/docs/source/en/guided_tour.md index 5eca7fc21..d02cfa1ad 100644 --- a/docs/source/en/guided_tour.md +++ b/docs/source/en/guided_tour.md @@ -1,4 +1,4 @@ -