Skip to content

FunctionTool does not convert Union[Pydantic, Pydantic] at runtime (visible in ADK 2.0 GA) #5799

Closed

Description

@kkj333

🔴 Required Information

Describe the Bug:

FunctionTool._preprocess_args converts a JSON dict to a single Pydantic BaseModel and handles Optional[BaseModel], but it does not convert Union[BaseModel, BaseModel] arguments. When the LLM calls a tool with a dict matching one branch of the union, the dict is passed through unchanged. Tool code that uses isinstance(entity, UserProfile) then fails with Unexpected entity type: <class 'dict'>.

This is especially visible in ADK 2.0 GA: JSON_SCHEMA_FOR_FUNC_DECL changed from WIP / default OFF to EXPERIMENTAL / default ON (v2 transition, commit 1622793). Tool declaration generation now succeeds for union Pydantic params, but runtime conversion is still missing — so the agent can call the tool and still fail at execution time.

Steps to Reproduce:

  1. Install ADK 2.0.x (pip show google-adk)
  2. Run the minimal repro below (no API key needed for the tool execution path)
  3. Observe status: error for both user and company entity payloads

Expected Behavior:

entity: Union[UserProfile, CompanyProfile] should be converted from the LLM JSON dict to the matching Pydantic instance before the tool function runs (as documented in contributing/samples/tools/pydantic_argument/README.md).

Observed Behavior:

# Both return:
# {'status': 'error', 'message': "Unexpected entity type: <class 'dict'>"}

With JSON_SCHEMA_FOR_FUNC_DECL disabled, create_entity_profile also fails at declaration time:

ValueError: Failed to parse the parameter entity: Union[...UserProfile, ...CompanyProfile]

Environment Details:

  • ADK Library Version: 2.0.0 (also reproducible on main)
  • Desktop OS: macOS
  • Python Version: 3.13

Model Information:

  • LiteLLM: No
  • Model: gemini-2.5-pro on Vertex AI (live repro confirmed; local repro above does not require API)

🟡 Optional Information

Regression:

Not a new conversion implementation in 2.0 — Union[BaseModel, BaseModel] conversion appears never to have been implemented in FunctionTool (only Optional[T] with a single non-None type is handled in _preprocess_args).

What changed in 2.0 GA is that default-on JSON_SCHEMA_FOR_FUNC_DECL allows tool registration/schema generation to succeed, so the broken runtime path is now hit in normal use.

Minimal Reproduction Code:

import asyncio
from typing import Optional, Union
from unittest.mock import MagicMock
import pydantic
from google.adk.tools.function_tool import FunctionTool
from google.adk.tools.tool_context import ToolContext
from google.adk.agents.invocation_context import InvocationContext
from google.adk.sessions.session import Session

class UserProfile(pydantic.BaseModel):
    name: str
    age: int
    email: Optional[str] = None

class CompanyProfile(pydantic.BaseModel):
    company_name: str
    industry: str
    employee_count: int

def create_entity_profile(entity: Union[UserProfile, CompanyProfile]) -> dict:
    if isinstance(entity, UserProfile):
        return {"status": "user_profile_created", "entity_type": "user"}
    elif isinstance(entity, CompanyProfile):
        return {"status": "company_profile_created", "entity_type": "company"}
    return {"status": "error", "message": f"Unexpected entity type: {type(entity)}"}

async def main():
    tool = FunctionTool(create_entity_profile)
    ctx = MagicMock(spec=ToolContext)
    inv = MagicMock(spec=InvocationContext)
    inv.session = MagicMock(spec=Session)
    ctx.invocation_context = inv

    for args in [
        {"entity": {"name": "Diana", "age": 32, "email": "d@example.com"}},
        {"entity": {"company_name": "Acme Corp", "industry": "tech", "employee_count": 50}},
    ]:
        print(await tool.run_async(args=args, tool_context=ctx))

asyncio.run(main())

Additional Context:

How often has this issue occurred?:

  • Always (100%) for Union[BaseModel, BaseModel] tool parameters

Activity

  1. added
    tools[Component] This issue is related to tools
    on May 22, 2026
  2. added theissue type on May 22, 2026
  3. added 2 commits that reference this issue on May 22, 2026
    d56ee08
    41cd1f3
  4. added a commit that references this issue on May 22, 2026
    104edc8
  5. added a commit that references this issue on Aug 11, 2026
    5aefad4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

tools[Component] This issue is related to tools

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions