Skip to content

fix: resolve context annotations on callable tools - #7412

Closed
fhgffy wants to merge 2 commits into
google:mainfrom
fhgffy:fix/partial-tool-context
Closed

fhgffy wants to merge 2 commits into
google:mainfrom
fhgffy:fix/partial-tool-context

Conversation

@fhgffy

@fhgffy fhgffy commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Problem

A tool wrapped in functools.partial, or implemented as a callable instance, loses context injection when its context annotation is a string and the parameter is named ctx. For example:

def search(prefix: str, query: str, ctx: "ToolContext") -> str:
    return prefix + query

tool = FunctionTool(functools.partial(search, "found: "))

On main, the default JSON schema builder tries to expose ctx and raises PydanticSchemaGenerationError; calling run_async directly reports it as a missing argument. The context should be injected by ADK and omitted from the model's schema.

Solution

Keep typing.get_type_hints as the first path, then use the existing callable-aware resolver when it fails or returns no hints. Python 3.14 returns an empty dictionary for these callable forms instead of raising TypeError. This preserves wrappers with their own annotations or __wrapped__ metadata, and keeps parameter selection on the actual callable signature.

Testing plan

  • Added regression coverage for sync partials, async partials, callable instances, bound methods and update_wrapper instances. Before the fix, the first three cases failed. All five now pass.
  • The final patch passed 178 tests on Python 3.14 across FunctionTool, context utilities, callable utilities and FunctionNode. The Python 3.12 run including ToolNode passed 238 tests, with two Bash-execution tests failing on Windows; those two also fail with the unmodified main context utility.
  • Ran an offline Runner with a scripted model through tool declaration, function call, response and session state: sync partial, async partial and callable instance each returned found: hello; their declarations contained only query.
  • uv build passed. Formatting, lint, compliance and docs hooks passed. The new-file check was run directly with Python because its /bin/bash shebang is unavailable on Windows; there are no new production files.
  • Broader tools/utils run: 3272 passed, 2 skipped, 5 failed. Re-running those five with the unmodified context utility produced the same failures (Windows FIFO/path handling and web-page tests).
  • Full tox was attempted on Windows. Python 3.10/3.13 setup and 3.11/3.12 startup failed. The 3.14 full run had 17385 passed and 54 failed, including three new cases that exposed the empty-hints behavior. The follow-up handles that behavior; all five regression cases and the 178-test related suite pass on 3.14. The full matrix has not been rerun or shown to pass, and the remaining full-suite failures have not all been classified.

The legacy declaration builder with JSON_SCHEMA_FOR_FUNC_DECL=False still has its existing limitations for these callable forms; this change does not alter that builder.

@fhgffy

fhgffy commented Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

This is now covered by #7089 (56ac304). I ran this branch's five regression cases against main at f78149a on Python 3.12 and 3.14; all five pass on both versions. Closing this as superseded.

@fhgffy fhgffy closed this Oct 16, 2026
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.

2 participants