fix: Add response schema for agent tool function declaration even when it's return None

PiperOrigin-RevId: 784216811
This commit is contained in:
Xiang (Sean) Zhou
2025-07-17 09:51:34 -07:00
committed by Copybara-Service
parent 33ac8380ad
commit 377b5a9b78
2 changed files with 158 additions and 0 deletions
+12
View File
@@ -61,6 +61,7 @@ class AgentTool(BaseTool):
@override @override
def _get_declaration(self) -> types.FunctionDeclaration: def _get_declaration(self) -> types.FunctionDeclaration:
from ..agents.llm_agent import LlmAgent from ..agents.llm_agent import LlmAgent
from ..utils.variant_utils import GoogleLLMVariant
if isinstance(self.agent, LlmAgent) and self.agent.input_schema: if isinstance(self.agent, LlmAgent) and self.agent.input_schema:
result = _automatic_function_calling_util.build_function_declaration( result = _automatic_function_calling_util.build_function_declaration(
@@ -80,6 +81,17 @@ class AgentTool(BaseTool):
description=self.agent.description, description=self.agent.description,
name=self.name, name=self.name,
) )
# Set response schema for non-GEMINI_API variants
if self._api_variant != GoogleLLMVariant.GEMINI_API:
# Determine response type based on agent's output schema
if isinstance(self.agent, LlmAgent) and self.agent.output_schema:
# Agent has structured output schema - response is an object
result.response = types.Schema(type=types.Type.OBJECT)
else:
# Agent returns text - response is a string
result.response = types.Schema(type=types.Type.STRING)
result.name = self.name result.name = self.name
return result return result
+146
View File
@@ -16,6 +16,8 @@ from google.adk.agents import Agent
from google.adk.agents import SequentialAgent from google.adk.agents import SequentialAgent
from google.adk.agents.callback_context import CallbackContext from google.adk.agents.callback_context import CallbackContext
from google.adk.tools.agent_tool import AgentTool from google.adk.tools.agent_tool import AgentTool
from google.adk.utils.variant_utils import GoogleLLMVariant
from google.genai import types
from google.genai.types import Part from google.genai.types import Part
from pydantic import BaseModel from pydantic import BaseModel
from pytest import mark from pytest import mark
@@ -209,3 +211,147 @@ def test_custom_schema():
# The second request is the tool agent request. # The second request is the tool agent request.
assert mock_model.requests[1].config.response_schema == CustomOutput assert mock_model.requests[1].config.response_schema == CustomOutput
assert mock_model.requests[1].config.response_mime_type == 'application/json' assert mock_model.requests[1].config.response_mime_type == 'application/json'
@mark.parametrize(
'env_variables',
[
'VERTEX', # Test VERTEX_AI variant
],
indirect=True,
)
def test_agent_tool_response_schema_no_output_schema_vertex_ai():
"""Test AgentTool with no output schema has string response schema for VERTEX_AI."""
tool_agent = Agent(
name='tool_agent',
model=testing_utils.MockModel.create(responses=['test response']),
)
agent_tool = AgentTool(agent=tool_agent)
declaration = agent_tool._get_declaration()
assert declaration.name == 'tool_agent'
assert declaration.parameters.type == 'OBJECT'
assert declaration.parameters.properties['request'].type == 'STRING'
# Should have string response schema for VERTEX_AI
assert declaration.response is not None
assert declaration.response.type == types.Type.STRING
@mark.parametrize(
'env_variables',
[
'VERTEX', # Test VERTEX_AI variant
],
indirect=True,
)
def test_agent_tool_response_schema_with_output_schema_vertex_ai():
"""Test AgentTool with output schema has object response schema for VERTEX_AI."""
class CustomOutput(BaseModel):
custom_output: str
tool_agent = Agent(
name='tool_agent',
model=testing_utils.MockModel.create(responses=['test response']),
output_schema=CustomOutput,
)
agent_tool = AgentTool(agent=tool_agent)
declaration = agent_tool._get_declaration()
assert declaration.name == 'tool_agent'
# Should have object response schema for VERTEX_AI when output_schema exists
assert declaration.response is not None
assert declaration.response.type == types.Type.OBJECT
@mark.parametrize(
'env_variables',
[
'GOOGLE_AI', # Test GEMINI_API variant
],
indirect=True,
)
def test_agent_tool_response_schema_gemini_api():
"""Test AgentTool with GEMINI_API variant has no response schema."""
class CustomOutput(BaseModel):
custom_output: str
tool_agent = Agent(
name='tool_agent',
model=testing_utils.MockModel.create(responses=['test response']),
output_schema=CustomOutput,
)
agent_tool = AgentTool(agent=tool_agent)
declaration = agent_tool._get_declaration()
assert declaration.name == 'tool_agent'
# GEMINI_API should not have response schema
assert declaration.response is None
@mark.parametrize(
'env_variables',
[
'VERTEX', # Test VERTEX_AI variant
],
indirect=True,
)
def test_agent_tool_response_schema_with_input_schema_vertex_ai():
"""Test AgentTool with input and output schemas for VERTEX_AI."""
class CustomInput(BaseModel):
custom_input: str
class CustomOutput(BaseModel):
custom_output: str
tool_agent = Agent(
name='tool_agent',
model=testing_utils.MockModel.create(responses=['test response']),
input_schema=CustomInput,
output_schema=CustomOutput,
)
agent_tool = AgentTool(agent=tool_agent)
declaration = agent_tool._get_declaration()
assert declaration.name == 'tool_agent'
assert declaration.parameters.type == 'OBJECT'
assert declaration.parameters.properties['custom_input'].type == 'STRING'
# Should have object response schema for VERTEX_AI when output_schema exists
assert declaration.response is not None
assert declaration.response.type == types.Type.OBJECT
@mark.parametrize(
'env_variables',
[
'VERTEX', # Test VERTEX_AI variant
],
indirect=True,
)
def test_agent_tool_response_schema_with_input_schema_no_output_vertex_ai():
"""Test AgentTool with input schema but no output schema for VERTEX_AI."""
class CustomInput(BaseModel):
custom_input: str
tool_agent = Agent(
name='tool_agent',
model=testing_utils.MockModel.create(responses=['test response']),
input_schema=CustomInput,
)
agent_tool = AgentTool(agent=tool_agent)
declaration = agent_tool._get_declaration()
assert declaration.name == 'tool_agent'
assert declaration.parameters.type == 'OBJECT'
assert declaration.parameters.properties['custom_input'].type == 'STRING'
# Should have string response schema for VERTEX_AI when no output_schema
assert declaration.response is not None
assert declaration.response.type == types.Type.STRING