Skip to content

feat: Allow Union types in input/output schemas #2691

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

dylan-apex
Copy link

@dylan-apex dylan-apex commented Aug 22, 2025

This change allows the use of Union types in the input_schema and output_schema of LlmAgent and AgentTool. It also allows you to use Union types in classes that inherit from BaseModel.

According to googleapis/python-genai#447 this has been a supported feature since v1.6.0 and the minimum version for this repo is v1.21.0

Fixes #2670 , #2664

 class CustomOutput(BaseModel):
    custom_output: str

  class CustomOutput2(BaseModel):
    custom_output2: str

  class CustomType(BaseModel):
    custom = Union[CustomOutput, CustomOutput2]

  agent = Agent(
     ...,
     output_schema=CustomType
  )

  agent2 = Agent(
     ...,
    output_schema=Union[CustomOutput, CustomOutput2]
  )

This is achieved by:

  • Removing the restriction on anyOf in the function parameter parsing utility.
  • Using pydantic.TypeAdapter to validate and serialize the input and output when the schema is a Union.
  • Change llm_agent.input_schema = Optional[Any] = None and llm_agent.output_schema = Optional[Any]

New test cases have been added to cover these scenarios.

Real World Example
from __future__ import annotations
from pydantic import BaseModel, Field
from typing import List, Literal, Union

from google.adk.agents import Agent
from google.adk.tools.agent_tool import AgentTool
from pydantic import BaseModel, Field


class Cat(BaseModel):
    """Instruction for creating a new survey."""
    type: Literal["cat"] = Field(default="cat", description="The type of animal")
    colour_of_fur: str = Field(
        default=None,
        description="The color of the cat's fur",
    )


class Dog(BaseModel):
    """Instruction for opening an existing survey."""
    type: Literal["dog"] = Field(default="dog", description="The type of animal")
    wagginess_of_tail: str = Field(
        default=None,
        description="The wagging behavior of the dog's tail",
    )

Animal = Union[Dog, Cat]


class ResponseMessage(BaseModel):
    summary: str = Field(description="A brief summary of the animals.")
    do: List[Animal] = Field(description="A list of animals to be created.")

class DogList(BaseModel):
    dogs: List[Dog]  = Field(description="List of dogs")

class CatList(BaseModel):
    cats: List[Cat] = Field(description="List of cats")

model='gemini-2.5-flash'

dog_agent = Agent(
    name="dog_agent",
    model=model,
    description="Gets information about dogs.",
    instruction=f"""
    You're job is to provide some information about breeds of dogs.

    Dog 1 is a golden retriever
    Dog 2 is a pug
    Dog 3 is a beagle
    Dog 4 is a poodle
    Dog 5 is a german shepherd
    Dog 6 is a labrador
    """,
    output_schema=DogList,
)

cat_agent = Agent(
    name="cat_agent",
    model=model,
    description="Gets information about cats.",
    instruction=f"""
    You're job is to provide some information about pedigrees of cats.

    Cat 1 is a siamese with brown fur
    Cat 2 is a persian with white fur
    Cat 3 is a maine coon with black fur
    Cat 4 is a ragdoll with cream fur
    Cat 5 is a sphynx with pink fur
    Cat 6 is a bengal with spotted fur
    """,
    
    output_schema=CatList,
)

root_agent = Agent(
    name="root_agent",
    model=model,
    description="Frontline agent that greets user, identifies intent, and dispatches to appropriate handlers.",
    instruction=f"""
    You're job is to get information about animals, specifically dogs and cats.

    Use the appropriate tool based on the user's request.
    Return all of the cats and dogs you get from the tools in your response.
    """,
    tools=[
        AgentTool(agent=dog_agent), AgentTool(agent=cat_agent)
        ],
    output_schema=ResponseMessage,
)
Response that matches `ResponseMessage` schema
{"summary": "Here is the information I retrieved about cats and dogs.", "do": [{"type": "cat", "colour_of_fur": "brown"}, {"type": "cat", "colour_of_fur": "white"}, {"type": "cat", "colour_of_fur": "black"}, {"type": "cat", "colour_of_fur": "cream"}, {"type": "cat", "colour_of_fur": "pink"}, {"type": "cat", "colour_of_fur": "spotted"}, {"type": "dog", "wagginess_of_tail": "Very waggy, often a full-body wag when happy or excited."}, {"type": "dog", "wagginess_of_tail": "Moderate to wiggly, with a curled tail that shows excitement."}, {"type": "dog", "wagginess_of_tail": "Very waggy and expressive, often held high in a 'merry' wag."}, {"type": "dog", "wagginess_of_tail": "Moderate to very waggy, expressive of their mood."}, {"type": "dog", "wagginess_of_tail": "Expressive and waggy, can be very waggy when excited or greeting."}, {"type": "dog", "wagginess_of_tail": "Extremely waggy, often a powerful full-body 'otter tail' wag."}]}
Testing
================================================== 4146 passed, 2419 warnings in 31.50s ==================================================

This change allows the use of `Union` types in the `input_schema` and `output_schema` of `LlmAgent` and `AgentTool`.

This is achieved by:
- Removing the restriction on `anyOf` in the function parameter parsing utility.
- Using `pydantic.TypeAdapter` to validate and serialize the input and output when the schema is a `Union`.

New test cases have been added to cover these scenarios.
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @dylan-apex, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces support for Union types within the input_schema and output_schema of LlmAgent and AgentTool classes. This enhancement significantly increases the flexibility of defining LLM input and output structures, allowing for more complex and adaptive schema definitions. It resolves existing issues related to schema limitations by leveraging Pydantic's TypeAdapter for robust validation and serialization of union-typed data.

Highlights

  • Enabled Union Types: LlmAgent and AgentTool now support Union types for their input_schema and output_schema, allowing for more flexible data structures.
  • Pydantic TypeAdapter Integration: Utilized pydantic.TypeAdapter for dynamic validation and serialization of data against Union schemas, ensuring correct parsing of diverse input/output formats.
  • Relaxed Schema Constraints: The input_schema and output_schema attributes in LlmAgent have been broadened from Optional[type[BaseModel]] to Optional[Any], accommodating non-BaseModel types within Unions.
  • Removed anyOf Restriction: The previous limitation on anyOf (used by Pydantic for Union types) in function parameter parsing has been lifted, enabling full Union type support.
  • Comprehensive Test Coverage: New unit tests have been added to validate the correct handling of Union types in both input and output schemas for LlmAgent and AgentTool, including scenarios with BaseModel and Literal types.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@adk-bot adk-bot added bot triaged [Bot] This issue is triaged by ADK bot core [Component] This issue is related to the core interface and implementation labels Aug 22, 2025
@adk-bot adk-bot requested a review from Jacksunwei August 22, 2025 18:50
@adk-bot
Copy link
Collaborator

adk-bot commented Aug 22, 2025

Response from ADK Triaging Agent

Hello @dylan-apex, thank you for your contribution!

To help reviewers better understand the scope and impact of your changes, could you please add a testing plan section to your PR description? This section should detail how you've tested the new functionality.

You can find more information about our contribution guidelines here: https://github.com/google/adk-python/blob/main/CONTRIBUTING.md#requirement-for-prs

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request correctly implements support for Union types in input_schema and output_schema by leveraging pydantic.TypeAdapter and removing the restriction on anyOf. The changes are accompanied by a comprehensive set of new unit tests that cover various scenarios involving Union types.

I have identified a potential bug in the input serialization logic that could occur with Union types containing primitives, a typo in a new test case, and an opportunity to refactor some duplicated code to enhance maintainability. Please see my detailed comments below.

Copy link

Warning

Gemini encountered an error creating the review. You can try again by commenting /gemini review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bot triaged [Bot] This issue is triaged by ADK bot core [Component] This issue is related to the core interface and implementation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants