Skip to content

Commit a3e9a6d

Browse files
vertex-sdk-botcopybara-github
authored andcommitted
fix: Fix NameError when evaluating ADK agents with AgentTool via GenAI Evals SDK
PiperOrigin-RevId: 899737189
1 parent e73d4e7 commit a3e9a6d

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

tests/unit/vertexai/genai/test_evals.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5288,6 +5288,104 @@ def my_search_tool(query: str) -> str:
52885288
]
52895289
mock_from_callable.assert_called_once_with(callable=my_search_tool)
52905290

5291+
def test_load_from_agent_with_get_declaration_tool(self):
5292+
"""Tests that tools with _get_declaration() use it instead of from_callable."""
5293+
mock_declaration = mock.Mock(spec=genai_types.FunctionDeclaration)
5294+
5295+
mock_tool = mock.Mock()
5296+
mock_tool._get_declaration = mock.Mock(return_value=mock_declaration)
5297+
5298+
mock_agent = mock.Mock()
5299+
mock_agent.name = "mock_agent"
5300+
mock_agent.instruction = "mock instruction"
5301+
mock_agent.description = "mock description"
5302+
mock_agent.tools = [mock_tool]
5303+
mock_agent.sub_agents = []
5304+
5305+
agent_info = vertexai_genai_types.evals.AgentInfo.load_from_agent(
5306+
agent=mock_agent,
5307+
)
5308+
5309+
assert agent_info.name == "mock_agent"
5310+
assert len(agent_info.agents["mock_agent"].tools) == 1
5311+
assert isinstance(agent_info.agents["mock_agent"].tools[0], genai_types.Tool)
5312+
assert agent_info.agents["mock_agent"].tools[0].function_declarations == [
5313+
mock_declaration
5314+
]
5315+
mock_tool._get_declaration.assert_called_once()
5316+
5317+
@mock.patch.object(genai_types.FunctionDeclaration, "from_callable_with_api_option")
5318+
def test_load_from_agent_with_mixed_tools(self, mock_from_callable):
5319+
"""Tests agents with both _get_declaration tools and plain callables."""
5320+
def my_plain_tool(query: str) -> str:
5321+
"""A plain callable tool."""
5322+
return query
5323+
5324+
mock_adk_declaration = mock.Mock(spec=genai_types.FunctionDeclaration)
5325+
mock_adk_tool = mock.Mock()
5326+
mock_adk_tool._get_declaration = mock.Mock(
5327+
return_value=mock_adk_declaration
5328+
)
5329+
5330+
mock_callable_declaration = mock.Mock(spec=genai_types.FunctionDeclaration)
5331+
mock_from_callable.return_value = mock_callable_declaration
5332+
5333+
mock_agent = mock.Mock()
5334+
mock_agent.name = "mock_agent"
5335+
mock_agent.instruction = "mock instruction"
5336+
mock_agent.description = "mock description"
5337+
mock_agent.tools = [mock_adk_tool, my_plain_tool]
5338+
mock_agent.sub_agents = []
5339+
5340+
agent_info = vertexai_genai_types.evals.AgentInfo.load_from_agent(
5341+
agent=mock_agent,
5342+
)
5343+
5344+
assert len(agent_info.agents["mock_agent"].tools) == 2
5345+
# First tool: ADK tool with _get_declaration
5346+
assert agent_info.agents["mock_agent"].tools[0].function_declarations == [
5347+
mock_adk_declaration
5348+
]
5349+
mock_adk_tool._get_declaration.assert_called_once()
5350+
# Second tool: plain callable via from_callable_with_api_option
5351+
assert agent_info.agents["mock_agent"].tools[1].function_declarations == [
5352+
mock_callable_declaration
5353+
]
5354+
mock_from_callable.assert_called_once_with(callable=my_plain_tool)
5355+
5356+
def test_load_from_agent_with_none_declaration_falls_back(self):
5357+
"""Tests that tools returning None from _get_declaration fall back to from_callable."""
5358+
mock_tool = mock.Mock()
5359+
mock_tool._get_declaration = mock.Mock(return_value=None)
5360+
mock_tool.__name__ = "mock_tool"
5361+
mock_tool.__doc__ = "A mock tool."
5362+
5363+
mock_agent = mock.Mock()
5364+
mock_agent.name = "mock_agent"
5365+
mock_agent.instruction = "mock instruction"
5366+
mock_agent.description = "mock description"
5367+
mock_agent.tools = [mock_tool]
5368+
mock_agent.sub_agents = []
5369+
5370+
with mock.patch.object(
5371+
genai_types.FunctionDeclaration, "from_callable_with_api_option"
5372+
) as mock_from_callable:
5373+
mock_callable_declaration = mock.Mock(
5374+
spec=genai_types.FunctionDeclaration
5375+
)
5376+
mock_from_callable.return_value = mock_callable_declaration
5377+
5378+
agent_info = vertexai_genai_types.evals.AgentInfo.load_from_agent(
5379+
agent=mock_agent,
5380+
)
5381+
5382+
assert len(agent_info.agents["mock_agent"].tools) == 1
5383+
assert agent_info.agents["mock_agent"].tools[
5384+
0
5385+
].function_declarations == [mock_callable_declaration]
5386+
mock_tool._get_declaration.assert_called_once()
5387+
mock_from_callable.assert_called_once_with(callable=mock_tool)
5388+
52915389

52925390
class TestValidateDatasetAgentData:
52935391
"""Unit tests for the _validate_dataset_agent_data function."""

vertexai/_genai/types/evals.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ 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(
92+
tool._get_declaration
93+
):
94+
declaration = tool._get_declaration()
95+
if declaration is not None:
96+
tool_declarations.append(
97+
{"function_declarations": [declaration]}
98+
)
99+
continue
100+
87101
tool_declarations.append(
88102
{
89103
"function_declarations": [

0 commit comments

Comments
 (0)