diff --git a/agentplatform/_genai/types/evals.py b/agentplatform/_genai/types/evals.py index d264a58c5e..59c59b8948 100644 --- a/agentplatform/_genai/types/evals.py +++ b/agentplatform/_genai/types/evals.py @@ -83,7 +83,7 @@ def _get_tool_declarations_from_agent(agent: Any) -> genai_types.ToolListUnion: The tool declarations of the agent. """ tool_declarations: genai_types.ToolListUnion = [] - for tool in agent.tools: + for tool in getattr(agent, "tools", None) or []: # ADK tools (e.g. AgentTool, VertexAiSearchTool) own their declaration # via _get_declaration(). A None result means the tool has no function # declaration (e.g. built-in retrieval tools). In both cases, skip the diff --git a/tests/unit/agentplatform/genai/test_evals.py b/tests/unit/agentplatform/genai/test_evals.py index aa27b91dc0..1b05cb4b0b 100644 --- a/tests/unit/agentplatform/genai/test_evals.py +++ b/tests/unit/agentplatform/genai/test_evals.py @@ -5979,6 +5979,39 @@ def run(self, query: "Optional[str]" = None): # noqa: F821 assert agent_info.agents["mock_agent"].tools == [] + @mock.patch.object(genai_types.FunctionDeclaration, "from_callable_with_api_option") + def test_load_from_agent_workflow_root_without_tools(self, mock_from_callable): + def my_search_tool(query: str) -> str: + """Searches for information.""" + return f"search result for {query}" + + mock_function_declaration = mock.Mock(spec=genai_types.FunctionDeclaration) + mock_from_callable.return_value = mock_function_declaration + + leaf_agent = mock.Mock() + leaf_agent.name = "leaf" + leaf_agent.instruction = "do a step" + leaf_agent.description = "leaf description" + leaf_agent.tools = [my_search_tool] + leaf_agent.sub_agents = [] + + root_agent = mock.Mock(spec=["name", "sub_agents"]) + root_agent.name = "pipeline" + root_agent.sub_agents = [leaf_agent] + assert not hasattr(root_agent, "tools") + + agent_info = agentplatform_genai_types.evals.AgentInfo.load_from_agent( + agent=root_agent, + ) + + assert agent_info.name == "pipeline" + assert agent_info.root_agent_id == "pipeline" + assert agent_info.agents["pipeline"].tools == [] + assert len(agent_info.agents["leaf"].tools) == 1 + assert agent_info.agents["leaf"].tools[0].function_declarations == [ + mock_function_declaration + ] + class TestValidateDatasetAgentData: """Unit tests for the _validate_dataset_agent_data function.""" diff --git a/vertexai/_genai/types/evals.py b/vertexai/_genai/types/evals.py index fefd7d3c0e..a7abbc9266 100644 --- a/vertexai/_genai/types/evals.py +++ b/vertexai/_genai/types/evals.py @@ -83,7 +83,7 @@ def _get_tool_declarations_from_agent(agent: Any) -> genai_types.ToolListUnion: The tool declarations of the agent. """ tool_declarations: genai_types.ToolListUnion = [] - for tool in agent.tools: + for tool in getattr(agent, "tools", None) or []: # ADK tools (e.g. AgentTool, VertexAiSearchTool) own their declaration # via _get_declaration(). A None result means the tool has no function # declaration (e.g. built-in retrieval tools). In both cases, skip the