Skip to content

fix(tools): support callables with positional-only arguments in FunctionTool - #7383

Open
Moashehri wants to merge 1 commit into
google:mainfrom
Moashehri:fix/function-tool-positional-only
Open

Moashehri wants to merge 1 commit into
google:mainfrom
Moashehri:fix/function-tool-positional-only

Conversation

@Moashehri

Copy link
Copy Markdown

Please ensure you have read the contribution guide before creating a pull request.

Link to Issue or Description of Change

1. Link to an existing issue (if applicable):

Problem:

FunctionTool currently supports positional-only parameters in its generated tool declaration schema (_function_tool_declarations.py), but runtime invocation in FunctionTool.run_async forwards model-generated arguments exclusively via keyword unpacking:

target(**args_to_call)

In Python, positional-only parameters cannot be supplied as keyword arguments. This causes valid callables such as:

def add(x: int, /, y: int) -> int:
    return x + y

and standard library built-ins such as:

math.pow

to raise TypeError during tool execution (TypeError: add() got some positional-only arguments passed as keyword arguments: 'x' or TypeError: math.pow() takes no keyword arguments). Because _invoke_callable only catches invocation TypeError when not self._spec.has_signature, this unhandled exception terminates the entire agent run.

Root Cause:

There is a mismatch between declaration/schema generation, which explicitly recognizes inspect.Parameter.POSITIONAL_ONLY parameters and exposes them by name in the LLM tool declaration schema, and runtime invocation, which treats every model argument as a keyword argument.

Solution:

Added _make_keyword_compatible(target, signature) in src/google/adk/tools/function_tool.py to adapt callables with positional-only parameters:

  • Zero overhead for standard callables: Callables without positional-only parameters are returned unchanged.
  • Positional binding: When positional-only parameters exist, matching arguments are extracted from kwargs in parameter declaration order and passed positionally, while remaining arguments are forwarded as keyword arguments.
  • Default values: Supports positional-only parameters with defaults even when optional arguments are omitted.
  • Full callable compatibility: Preserves synchronous functions, asynchronous coroutines, standard library built-ins, callable class instances (__call__), parameter injection (e.g. tool_context), confirmation predicates (require_confirmation), and thread-pool execution (_use_sync_callable_runner).

Testing Plan

Unit Tests:

  • I have added or updated unit tests for my change.
  • All unit tests pass locally.

Summary of passed test results:

  • 67/67 FunctionTool unit tests passed in tests/unittests/tools/test_function_tool.py (including 8 new regression tests covering sync, async, built-ins, defaults, thread-pool runner, callable objects, context injection, and confirmation predicates).
  • 231/231 relevant tests passed in tests/unittests/tools/test_function_tool*.py and tests/unittests/flows/llm_flows/tools/test_functions_thread_pool.py.
  • 2,715/2,715 tools tests passed in the broader tests/unittests/tools/ suite.
  • Full multi-version matrix execution (tox) across Python 3.10, 3.11, 3.12, 3.13, and 3.14:
    • Over 17,075 tests passed in each environment.
    • All 67 FunctionTool tests and all tool-related tests passed with 100% success on all 5 Python versions.
    • Zero failures occurred in any modified or tool-related paths.
    • The remaining test failures in tox (test_interactions_utils.py and test_import_loading.py) were verified to reproduce identically on clean upstream origin/main as pre-existing baseline/environmental issues.
  • Pre-commit: All 14 hooks passed cleanly on pre-commit run --all-files (pyink, isort, ruff, addlicense, compliance-checks, check-docs, etc.).

Manual End-to-End (E2E) Tests:

Built wheel with uv build and verified execution in an isolated virtual environment outside the repository:

FunctionTool(add_pos_only) result: 40
FunctionTool(math.pow) result: 1024.0
ALL CLEAN-WHEEL TESTS PASSED SUCCESSFULLY

Scope

Changes are strictly confined to:

  • src/google/adk/tools/function_tool.py
  • tests/unittests/tools/test_function_tool.py

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules.

…ionTool

When building Tool Declarations, FunctionTool explicitly includes
inspect.Parameter.POSITIONAL_ONLY parameters in the JSON schema.
However, during invocation, FunctionTool passes arguments as keyword
arguments (**args_to_call), which causes Python to raise a TypeError
when invoking functions or builtins that have positional-only arguments
(e.g., 'def add(x, /, y)' or 'math.pow').

This change introduces _make_keyword_compatible to adapt callables with
positional-only parameters so that matching keyword arguments from model
tool calls are bound to positional arguments at invocation time, while
preserving async execution, thread pool runners, and default arguments.
@google-cla

google-cla Bot commented Oct 2, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FunctionTool fails to invoke callables with positional-only arguments

2 participants