Skip to content

Commit e8e0412

Browse files
vertex-sdk-botcopybara-github
authored andcommitted
feat: GenAI Client(evals) - Add loading agent info until function
PiperOrigin-RevId: 826638428
1 parent 65f8bba commit e8e0412

File tree

4 files changed

+113
-13
lines changed

4 files changed

+113
-13
lines changed

tests/unit/vertexai/genai/replays/test_create_evaluation_run.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def test_create_eval_run_data_source_evaluation_set(client):
7676
LLM_METRIC,
7777
],
7878
agent_info=types.evals.AgentInfo(
79-
agent="project/123/locations/us-central1/reasoningEngines/456",
79+
agent_resource_name="project/123/locations/us-central1/reasoningEngines/456",
8080
name="agent-1",
8181
instruction="agent-1 instruction",
8282
tool_declarations=[tool],

tests/unit/vertexai/genai/test_evals.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2782,6 +2782,40 @@ def test_agent_info_creation(self):
27822782
assert agent_info.description == "description1"
27832783
assert agent_info.tool_declarations == [tool]
27842784

2785+
@mock.patch.object(genai_types.FunctionDeclaration, "from_callable_with_api_option")
2786+
def test_load_from_agent(self, mock_from_callable):
2787+
def my_search_tool(query: str) -> str:
2788+
"""Searches for information."""
2789+
return f"search result for {query}"
2790+
2791+
mock_function_declaration = mock.Mock(spec=genai_types.FunctionDeclaration)
2792+
mock_from_callable.return_value = mock_function_declaration
2793+
2794+
mock_agent = mock.Mock()
2795+
mock_agent.name = "mock_agent"
2796+
mock_agent.instruction = "mock instruction"
2797+
mock_agent.description = "mock description"
2798+
mock_agent.tools = [my_search_tool]
2799+
2800+
agent_info = vertexai_genai_types.evals.AgentInfo.load_from_agent(
2801+
agent=mock_agent,
2802+
agent_resource_name="projects/123/locations/abc/reasoningEngines/456",
2803+
)
2804+
2805+
assert agent_info.name == "mock_agent"
2806+
assert agent_info.instruction == "mock instruction"
2807+
assert agent_info.description == "mock description"
2808+
assert (
2809+
agent_info.agent_resource_name
2810+
== "projects/123/locations/abc/reasoningEngines/456"
2811+
)
2812+
assert len(agent_info.tool_declarations) == 1
2813+
assert isinstance(agent_info.tool_declarations[0], genai_types.Tool)
2814+
assert agent_info.tool_declarations[0].function_declarations == [
2815+
mock_function_declaration
2816+
]
2817+
mock_from_callable.assert_called_once_with(callable=my_search_tool)
2818+
27852819

27862820
class TestEvent:
27872821
"""Unit tests for the Event class."""
@@ -4867,7 +4901,9 @@ def test_execute_evaluation_adds_creation_timestamp(
48674901
frozenset(["summarization_quality"]),
48684902
)
48694903
@mock.patch("time.sleep", return_value=None)
4870-
@mock.patch("vertexai._genai.evals.Evals._evaluate_instances")
4904+
@mock.patch(
4905+
"vertexai._genai.evals.Evals._evaluate_instances"
4906+
)
48714907
def test_predefined_metric_retry_on_resource_exhausted(
48724908
self,
48734909
mock_private_evaluate_instances,
@@ -4920,7 +4956,9 @@ def test_predefined_metric_retry_on_resource_exhausted(
49204956
frozenset(["summarization_quality"]),
49214957
)
49224958
@mock.patch("time.sleep", return_value=None)
4923-
@mock.patch("vertexai._genai.evals.Evals._evaluate_instances")
4959+
@mock.patch(
4960+
"vertexai._genai.evals.Evals._evaluate_instances"
4961+
)
49244962
def test_predefined_metric_retry_fail_on_resource_exhausted(
49254963
self,
49264964
mock_private_evaluate_instances,

vertexai/_genai/evals.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,6 +1397,15 @@ def create_evaluation_run(
13971397
)
13981398
inference_configs = {}
13991399
if agent_info:
1400+
if isinstance(agent_info, dict):
1401+
agent_info = types.evals.AgentInfo.model_validate(agent_info)
1402+
if (
1403+
not agent_info.agent_resource_name
1404+
or len(agent_info.agent_resource_name.split("reasoningEngines/")) != 2
1405+
):
1406+
raise ValueError(
1407+
"agent_info.agent_resource_name cannot be empty. Please provide a valid reasoning engine resource name in the format of projects/{project}/locations/{location}/reasoningEngines/{reasoning_engine}."
1408+
)
14001409
inference_configs[agent_info.name] = types.EvaluationRunInferenceConfig(
14011410
agent_config=types.EvaluationRunAgentConfig(
14021411
developer_instruction=genai_types.Content(
@@ -1405,11 +1414,11 @@ def create_evaluation_run(
14051414
tools=agent_info.tool_declarations,
14061415
)
14071416
)
1408-
if agent_info.agent:
1417+
if agent_info.agent_resource_name:
14091418
labels = labels or {}
1410-
labels["vertex-ai-evaluation-agent-engine-id"] = agent_info.agent.split(
1411-
"reasoningEngines/"
1412-
)[-1]
1419+
labels["vertex-ai-evaluation-agent-engine-id"] = (
1420+
agent_info.agent_resource_name.split("reasoningEngines/")[-1]
1421+
)
14131422
if not name:
14141423
name = f"evaluation_run_{uuid.uuid4()}"
14151424

@@ -2244,6 +2253,15 @@ async def create_evaluation_run(
22442253
)
22452254
inference_configs = {}
22462255
if agent_info:
2256+
if isinstance(agent_info, dict):
2257+
agent_info = types.evals.AgentInfo.model_validate(agent_info)
2258+
if (
2259+
not agent_info.agent_resource_name
2260+
or len(agent_info.agent_resource_name.split("reasoningEngines/")) != 2
2261+
):
2262+
raise ValueError(
2263+
"agent_info.agent_resource_name cannot be empty. Please provide a valid reasoning engine resource name in the format of projects/{project}/locations/{location}/reasoningEngines/{reasoning_engine}."
2264+
)
22472265
inference_configs[agent_info.name] = types.EvaluationRunInferenceConfig(
22482266
agent_config=types.EvaluationRunAgentConfig(
22492267
developer_instruction=genai_types.Content(
@@ -2252,11 +2270,11 @@ async def create_evaluation_run(
22522270
tools=agent_info.tool_declarations,
22532271
)
22542272
)
2255-
if agent_info.agent:
2273+
if agent_info.agent_resource_name:
22562274
labels = labels or {}
2257-
labels["vertex-ai-evaluation-agent-engine-id"] = agent_info.agent.split(
2258-
"reasoningEngines/"
2259-
)[-1]
2275+
labels["vertex-ai-evaluation-agent-engine-id"] = (
2276+
agent_info.agent_resource_name.split("reasoningEngines/")[-1]
2277+
)
22602278
if not name:
22612279
name = f"evaluation_run_{uuid.uuid4()}"
22622280

vertexai/_genai/types/evals.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class Importance(_common.CaseInSensitiveEnum):
3939
class AgentInfo(_common.BaseModel):
4040
"""The agent info of an agent, used for agent eval."""
4141

42-
agent: Optional[str] = Field(
42+
agent_resource_name: Optional[str] = Field(
4343
default=None,
4444
description="""The agent engine used to run agent. Agent engine resource name in str type, with format
4545
`projects/{project}/locations/{location}/reasoningEngines/{reasoning_engine_id}`.""",
@@ -57,11 +57,55 @@ class AgentInfo(_common.BaseModel):
5757
default=None, description="""List of tools used by the Agent."""
5858
)
5959

60+
@staticmethod
61+
def _get_tool_declarations_from_agent(agent: Any) -> genai_types.ToolListUnion:
62+
"""Get tool declarations from an agent.
63+
64+
Args:
65+
agent: The agent to get the tool declarations from. Data type is google.adk.agents.LLMAgent type, use Any to avoid dependency on ADK.
66+
67+
Returns:
68+
The tool declarations of the agent.
69+
"""
70+
tool_declarations: genai_types.ToolListUnion = []
71+
for tool in agent.tools:
72+
tool_declarations.append(
73+
{
74+
"function_declarations": [
75+
genai_types.FunctionDeclaration.from_callable_with_api_option(
76+
callable=tool
77+
)
78+
]
79+
}
80+
)
81+
return tool_declarations
82+
83+
@classmethod
84+
def load_from_agent(
85+
cls, agent: Any, agent_resource_name: Optional[str] = None
86+
) -> "AgentInfo":
87+
"""Load agent info from an agent.
88+
89+
Args:
90+
agent: The agent to get the agent info from, data type is google.adk.agents.LLMAgent type, use Any to avoid dependency on ADK.
91+
agent_resource_name: Optional. The agent engine resource name.
92+
93+
Returns:
94+
The agent info of the agent.
95+
"""
96+
return cls( # pytype: disable=missing-parameter
97+
name=agent.name,
98+
agent_resource_name=agent_resource_name,
99+
instruction=agent.instruction,
100+
description=agent.description,
101+
tool_declarations=AgentInfo._get_tool_declarations_from_agent(agent),
102+
)
103+
60104

61105
class AgentInfoDict(TypedDict, total=False):
62106
"""The agent info of an agent, used for agent eval."""
63107

64-
agent: Optional[str]
108+
agent_resource_name: Optional[str]
65109
"""The agent engine used to run agent. Agent engine resource name in str type, with format
66110
`projects/{project}/locations/{location}/reasoningEngines/{reasoning_engine_id}`."""
67111

0 commit comments

Comments
 (0)