From acb6cabb574e7c08f1c67090b7d7c73c4425f8f7 Mon Sep 17 00:00:00 2001 From: A Vertex SDK engineer Date: Wed, 5 Nov 2025 00:55:17 -0800 Subject: [PATCH] feat: GenAI Client(evals) - Add loading agent info util function PiperOrigin-RevId: 828342302 --- .../replays/test_create_evaluation_run.py | 2 +- tests/unit/vertexai/genai/test_evals.py | 34 +++++++++++ vertexai/_genai/evals.py | 16 ++--- vertexai/_genai/types/evals.py | 60 ++++++++++++++++++- 4 files changed, 101 insertions(+), 11 deletions(-) diff --git a/tests/unit/vertexai/genai/replays/test_create_evaluation_run.py b/tests/unit/vertexai/genai/replays/test_create_evaluation_run.py index da9a537d23..d130bac190 100644 --- a/tests/unit/vertexai/genai/replays/test_create_evaluation_run.py +++ b/tests/unit/vertexai/genai/replays/test_create_evaluation_run.py @@ -76,7 +76,7 @@ def test_create_eval_run_data_source_evaluation_set(client): LLM_METRIC, ], agent_info=types.evals.AgentInfo( - agent="project/123/locations/us-central1/reasoningEngines/456", + agent_resource_name="project/123/locations/us-central1/reasoningEngines/456", name="agent-1", instruction="agent-1 instruction", tool_declarations=[tool], diff --git a/tests/unit/vertexai/genai/test_evals.py b/tests/unit/vertexai/genai/test_evals.py index bcfd8cc888..5ab9e4c417 100644 --- a/tests/unit/vertexai/genai/test_evals.py +++ b/tests/unit/vertexai/genai/test_evals.py @@ -2782,6 +2782,40 @@ def test_agent_info_creation(self): assert agent_info.description == "description1" assert agent_info.tool_declarations == [tool] + @mock.patch.object(genai_types.FunctionDeclaration, "from_callable_with_api_option") + def test_load_from_agent(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 + + mock_agent = mock.Mock() + mock_agent.name = "mock_agent" + mock_agent.instruction = "mock instruction" + mock_agent.description = "mock description" + mock_agent.tools = [my_search_tool] + + agent_info = vertexai_genai_types.evals.AgentInfo.load_from_agent( + agent=mock_agent, + agent_resource_name="projects/123/locations/abc/reasoningEngines/456", + ) + + assert agent_info.name == "mock_agent" + assert agent_info.instruction == "mock instruction" + assert agent_info.description == "mock description" + assert ( + agent_info.agent_resource_name + == "projects/123/locations/abc/reasoningEngines/456" + ) + assert len(agent_info.tool_declarations) == 1 + assert isinstance(agent_info.tool_declarations[0], genai_types.Tool) + assert agent_info.tool_declarations[0].function_declarations == [ + mock_function_declaration + ] + mock_from_callable.assert_called_once_with(callable=my_search_tool) + class TestEvent: """Unit tests for the Event class.""" diff --git a/vertexai/_genai/evals.py b/vertexai/_genai/evals.py index afc7868627..8a4bce18ad 100644 --- a/vertexai/_genai/evals.py +++ b/vertexai/_genai/evals.py @@ -1405,11 +1405,11 @@ def create_evaluation_run( tools=agent_info.tool_declarations, ) ) - if agent_info.agent: + if agent_info.agent_resource_name: labels = labels or {} - labels["vertex-ai-evaluation-agent-engine-id"] = agent_info.agent.split( - "reasoningEngines/" - )[-1] + labels["vertex-ai-evaluation-agent-engine-id"] = ( + agent_info.agent_resource_name.split("reasoningEngines/")[-1] + ) if not name: name = f"evaluation_run_{uuid.uuid4()}" @@ -2252,11 +2252,11 @@ async def create_evaluation_run( tools=agent_info.tool_declarations, ) ) - if agent_info.agent: + if agent_info.agent_resource_name: labels = labels or {} - labels["vertex-ai-evaluation-agent-engine-id"] = agent_info.agent.split( - "reasoningEngines/" - )[-1] + labels["vertex-ai-evaluation-agent-engine-id"] = ( + agent_info.agent_resource_name.split("reasoningEngines/")[-1] + ) if not name: name = f"evaluation_run_{uuid.uuid4()}" diff --git a/vertexai/_genai/types/evals.py b/vertexai/_genai/types/evals.py index 2c8aba057a..b301270b3c 100644 --- a/vertexai/_genai/types/evals.py +++ b/vertexai/_genai/types/evals.py @@ -39,7 +39,7 @@ class Importance(_common.CaseInSensitiveEnum): class AgentInfo(_common.BaseModel): """The agent info of an agent, used for agent eval.""" - agent: Optional[str] = Field( + agent_resource_name: Optional[str] = Field( default=None, description="""The agent engine used to run agent. Agent engine resource name in str type, with format `projects/{project}/locations/{location}/reasoningEngines/{reasoning_engine_id}`.""", @@ -57,11 +57,67 @@ class AgentInfo(_common.BaseModel): default=None, description="""List of tools used by the Agent.""" ) + @staticmethod + def _get_tool_declarations_from_agent(agent: Any) -> genai_types.ToolListUnion: + """Gets tool declarations from an agent. + + Args: + agent: The agent to get the tool declarations from. Data type is google.adk.agents.LLMAgent type, use Any to avoid dependency on ADK. + + Returns: + The tool declarations of the agent. + """ + tool_declarations: genai_types.ToolListUnion = [] + for tool in agent.tools: + tool_declarations.append( + { + "function_declarations": [ + genai_types.FunctionDeclaration.from_callable_with_api_option( + callable=tool + ) + ] + } + ) + return tool_declarations + + @classmethod + def load_from_agent( + cls, agent: Any, agent_resource_name: Optional[str] = None + ) -> "AgentInfo": + """Loads agent info from an agent. + + Args: + agent: The agent to get the agent info from, data type is google.adk.agents.LLMAgent type, use Any to avoid dependency on ADK. + agent_resource_name: Optional. The agent engine resource name. + + Returns: + The agent info of the agent. + + Example: + ``` + from vertexai._genai import types + + # Assuming 'my_agent' is an instance of google.adk.agents.LLMAgent + + agent_info = types.evals.AgentInfo.load_from_agent( + agent=my_agent, + agent_resource_name="projects/123/locations/us-central1/reasoningEngines/456" + ) + ``` + """ + return cls( # pytype: disable=missing-parameter + name=agent.name, + agent_resource_name=agent_resource_name, + instruction=agent.instruction, + description=agent.description, + tool_declarations=AgentInfo._get_tool_declarations_from_agent(agent), + ) + class AgentInfoDict(TypedDict, total=False): """The agent info of an agent, used for agent eval.""" - agent: Optional[str] + agent_resource_name: Optional[str] """The agent engine used to run agent. Agent engine resource name in str type, with format `projects/{project}/locations/{location}/reasoningEngines/{reasoning_engine_id}`."""