Skip to content

Commit 04c5e02

Browse files
vertex-sdk-botcopybara-github
authored andcommitted
fix: Fix NameError when evaluating ADK agents with AgentTool via GenAI Evals SDK
PiperOrigin-RevId: 900840088
1 parent 3e85f64 commit 04c5e02

2 files changed

Lines changed: 105 additions & 0 deletions

File tree

tests/unit/vertexai/genai/test_evals.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5404,6 +5404,101 @@ def my_search_tool(query: str) -> str:
54045404
]
54055405
mock_from_callable.assert_called_once_with(callable=my_search_tool)
54065406

5407+
def test_load_from_agent_with_get_declaration_tool(self):
5408+
"""Tests that tools with _get_declaration() use it instead of from_callable."""
5409+
mock_declaration = mock.Mock(spec=genai_types.FunctionDeclaration)
5410+
5411+
mock_tool = mock.Mock()
5412+
mock_tool._get_declaration = mock.Mock(return_value=mock_declaration)
5413+
5414+
mock_agent = mock.Mock()
5415+
mock_agent.name = "mock_agent"
5416+
mock_agent.instruction = "mock instruction"
5417+
mock_agent.description = "mock description"
5418+
mock_agent.tools = [mock_tool]
5419+
mock_agent.sub_agents = []
5420+
5421+
agent_info = vertexai_genai_types.evals.AgentInfo.load_from_agent(
5422+
agent=mock_agent,
5423+
)
5424+
5425+
assert agent_info.name == "mock_agent"
5426+
assert len(agent_info.agents["mock_agent"].tools) == 1
5427+
assert isinstance(agent_info.agents["mock_agent"].tools[0], genai_types.Tool)
5428+
assert agent_info.agents["mock_agent"].tools[0].function_declarations == [
5429+
mock_declaration
5430+
]
5431+
mock_tool._get_declaration.assert_called_once()
5432+
5433+
@mock.patch.object(genai_types.FunctionDeclaration, "from_callable_with_api_option")
5434+
def test_load_from_agent_with_mixed_tools(self, mock_from_callable):
5435+
"""Tests agents with both _get_declaration tools and plain callables."""
5436+
5437+
def my_plain_tool(query: str) -> str:
5438+
"""A plain callable tool."""
5439+
return query
5440+
5441+
mock_adk_declaration = mock.Mock(spec=genai_types.FunctionDeclaration)
5442+
mock_adk_tool = mock.Mock()
5443+
mock_adk_tool._get_declaration = mock.Mock(return_value=mock_adk_declaration)
5444+
5445+
mock_callable_declaration = mock.Mock(spec=genai_types.FunctionDeclaration)
5446+
mock_from_callable.return_value = mock_callable_declaration
5447+
5448+
mock_agent = mock.Mock()
5449+
mock_agent.name = "mock_agent"
5450+
mock_agent.instruction = "mock instruction"
5451+
mock_agent.description = "mock description"
5452+
mock_agent.tools = [mock_adk_tool, my_plain_tool]
5453+
mock_agent.sub_agents = []
5454+
5455+
agent_info = vertexai_genai_types.evals.AgentInfo.load_from_agent(
5456+
agent=mock_agent,
5457+
)
5458+
5459+
assert len(agent_info.agents["mock_agent"].tools) == 2
5460+
# First tool: ADK tool with _get_declaration
5461+
assert agent_info.agents["mock_agent"].tools[0].function_declarations == [
5462+
mock_adk_declaration
5463+
]
5464+
mock_adk_tool._get_declaration.assert_called_once()
5465+
# Second tool: plain callable via from_callable_with_api_option
5466+
assert agent_info.agents["mock_agent"].tools[1].function_declarations == [
5467+
mock_callable_declaration
5468+
]
5469+
mock_from_callable.assert_called_once_with(callable=my_plain_tool)
5470+
5471+
def test_load_from_agent_with_none_declaration_falls_back(self):
5472+
"""Tests that tools returning None from _get_declaration fall back to from_callable."""
5473+
mock_tool = mock.Mock()
5474+
mock_tool._get_declaration = mock.Mock(return_value=None)
5475+
mock_tool.__name__ = "mock_tool"
5476+
mock_tool.__doc__ = "A mock tool."
5477+
5478+
mock_agent = mock.Mock()
5479+
mock_agent.name = "mock_agent"
5480+
mock_agent.instruction = "mock instruction"
5481+
mock_agent.description = "mock description"
5482+
mock_agent.tools = [mock_tool]
5483+
mock_agent.sub_agents = []
5484+
5485+
with mock.patch.object(
5486+
genai_types.FunctionDeclaration, "from_callable_with_api_option"
5487+
) as mock_from_callable:
5488+
mock_callable_declaration = mock.Mock(spec=genai_types.FunctionDeclaration)
5489+
mock_from_callable.return_value = mock_callable_declaration
5490+
5491+
agent_info = vertexai_genai_types.evals.AgentInfo.load_from_agent(
5492+
agent=mock_agent,
5493+
)
5494+
5495+
assert len(agent_info.agents["mock_agent"].tools) == 1
5496+
assert agent_info.agents["mock_agent"].tools[0].function_declarations == [
5497+
mock_callable_declaration
5498+
]
5499+
mock_tool._get_declaration.assert_called_once()
5500+
mock_from_callable.assert_called_once_with(callable=mock_tool)
5501+
54075502

54085503
class TestValidateDatasetAgentData:
54095504
"""Unit tests for the _validate_dataset_agent_data function."""

vertexai/_genai/types/evals.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,16 @@ def _get_tool_declarations_from_agent(agent: Any) -> genai_types.ToolListUnion:
8484
"""
8585
tool_declarations: genai_types.ToolListUnion = []
8686
for tool in agent.tools:
87+
# ADK tools (e.g. AgentTool) provide their own declaration via
88+
# _get_declaration(). Use it when available to avoid calling
89+
# typing.get_type_hints() on tool instances whose classes use
90+
# `from __future__ import annotations`, which causes NameError.
91+
if hasattr(tool, "_get_declaration") and callable(tool._get_declaration):
92+
declaration = tool._get_declaration()
93+
if declaration is not None:
94+
tool_declarations.append({"function_declarations": [declaration]})
95+
continue
96+
8797
tool_declarations.append(
8898
{
8999
"function_declarations": [

0 commit comments

Comments
 (0)