Skip to content

Commit 5ca3977

Browse files
vertex-sdk-botcopybara-github
authored andcommitted
feat: Delegate agent resolution to server via geminiAgentConfig
FUTURE_COPYBARA_INTEGRATE_REVIEW=#6967 from googleapis:release-please--branches--main 4a0d1d2 PiperOrigin-RevId: 949000060
1 parent 3335750 commit 5ca3977

4 files changed

Lines changed: 44 additions & 67 deletions

File tree

agentplatform/_genai/_evals_common.py

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -988,33 +988,6 @@ def _agent_data_response_text(agent_data: types.evals.AgentData) -> Optional[str
988988
return "".join(text_parts) or None
989989

990990

991-
def _agent_resource_to_agent_info(
992-
agent: str, api_client: BaseApiClient
993-
) -> "types.evals.AgentInfo":
994-
"""Builds an `AgentInfo` from a Gemini Agents API agent resource name.
995-
996-
Fetches the agent through the SDK's `api_client` (so replay recording is
997-
preserved) via `_fetch_agent_config_dict` and derives a single-agent
998-
`AgentInfo`: the agent's short name is the agents-map key and
999-
`root_agent_id`.
1000-
1001-
Args:
1002-
agent: The Gemini Agents API agent resource name
1003-
(`projects/{p}/locations/{l}/agents/{name}`).
1004-
api_client: The API client used to fetch the agent.
1005-
1006-
Returns:
1007-
An `AgentInfo` describing the fetched agent.
1008-
"""
1009-
agent_config = _fetch_agent_config_dict(api_client, agent)
1010-
short_name = agent_config.agent_id
1011-
return types.evals.AgentInfo( # pytype: disable=missing-parameter
1012-
name=short_name,
1013-
agents={short_name: agent_config},
1014-
root_agent_id=short_name,
1015-
)
1016-
1017-
1018991
_INTERACTION_TERMINAL_STATES = frozenset(
1019992
["completed", "failed", "cancelled", "incomplete", "budget_exceeded"]
1020993
)

agentplatform/_genai/evals.py

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,11 @@ def _GenerateUserScenariosParameters_to_vertex(
725725
getv(from_object, ["allow_cross_region_model"]),
726726
)
727727

728+
if getv(from_object, ["gemini_agent_config"]) is not None:
729+
setv(
730+
to_object, ["geminiAgentConfig"], getv(from_object, ["gemini_agent_config"])
731+
)
732+
728733
return to_object
729734

730735

@@ -1518,6 +1523,7 @@ def _generate_user_scenarios(
15181523
] = None,
15191524
config: Optional[types.GenerateUserScenariosConfigOrDict] = None,
15201525
allow_cross_region_model: Optional[bool] = None,
1526+
gemini_agent_config: Optional[types.GeminiAgentConfigOrDict] = None,
15211527
) -> types.GenerateUserScenariosResponse:
15221528
"""
15231529
Generates user scenarios for agent evaluation.
@@ -1530,6 +1536,7 @@ def _generate_user_scenarios(
15301536
user_scenario_generation_config=user_scenario_generation_config,
15311537
config=config,
15321538
allow_cross_region_model=allow_cross_region_model,
1539+
gemini_agent_config=gemini_agent_config,
15331540
)
15341541

15351542
request_url_dict: Optional[dict[str, str]]
@@ -2967,21 +2974,23 @@ def generate_conversation_scenarios(
29672974
"`agent` must be a Gemini Agents API agent resource name of the"
29682975
" form projects/{project}/locations/{location}/agents/{agent}."
29692976
)
2970-
parsed_agent_info = _evals_common._agent_resource_to_agent_info(
2971-
agent, self._api_client
2977+
response = self._generate_user_scenarios(
2978+
gemini_agent_config=types.GeminiAgentConfig(gemini_agent=agent),
2979+
user_scenario_generation_config=config,
2980+
allow_cross_region_model=allow_cross_region_model,
29722981
)
29732982
else:
29742983
parsed_agent_info = (
29752984
evals_types.AgentInfo.model_validate(agent_info)
29762985
if isinstance(agent_info, dict)
29772986
else agent_info
29782987
)
2979-
response = self._generate_user_scenarios(
2980-
agents=parsed_agent_info.agents,
2981-
root_agent_id=parsed_agent_info.root_agent_id,
2982-
user_scenario_generation_config=config,
2983-
allow_cross_region_model=allow_cross_region_model,
2984-
)
2988+
response = self._generate_user_scenarios(
2989+
agents=parsed_agent_info.agents,
2990+
root_agent_id=parsed_agent_info.root_agent_id,
2991+
user_scenario_generation_config=config,
2992+
allow_cross_region_model=allow_cross_region_model,
2993+
)
29852994
return _evals_utils._postprocess_user_scenarios_response(response)
29862995

29872996
def generate_loss_clusters(
@@ -3683,6 +3692,7 @@ async def _generate_user_scenarios(
36833692
] = None,
36843693
config: Optional[types.GenerateUserScenariosConfigOrDict] = None,
36853694
allow_cross_region_model: Optional[bool] = None,
3695+
gemini_agent_config: Optional[types.GeminiAgentConfigOrDict] = None,
36863696
) -> types.GenerateUserScenariosResponse:
36873697
"""
36883698
Generates user scenarios for agent evaluation.
@@ -3695,6 +3705,7 @@ async def _generate_user_scenarios(
36953705
user_scenario_generation_config=user_scenario_generation_config,
36963706
config=config,
36973707
allow_cross_region_model=allow_cross_region_model,
3708+
gemini_agent_config=gemini_agent_config,
36983709
)
36993710

37003711
request_url_dict: Optional[dict[str, str]]
@@ -4758,21 +4769,23 @@ async def generate_conversation_scenarios(
47584769
"`agent` must be a Gemini Agents API agent resource name of the"
47594770
" form projects/{project}/locations/{location}/agents/{agent}."
47604771
)
4761-
parsed_agent_info = _evals_common._agent_resource_to_agent_info(
4762-
agent, self._api_client
4772+
response = await self._generate_user_scenarios(
4773+
gemini_agent_config=types.GeminiAgentConfig(gemini_agent=agent),
4774+
user_scenario_generation_config=config,
4775+
allow_cross_region_model=allow_cross_region_model,
47634776
)
47644777
else:
47654778
parsed_agent_info = (
47664779
evals_types.AgentInfo.model_validate(agent_info)
47674780
if isinstance(agent_info, dict)
47684781
else agent_info
47694782
)
4770-
response = await self._generate_user_scenarios(
4771-
agents=parsed_agent_info.agents,
4772-
root_agent_id=parsed_agent_info.root_agent_id,
4773-
user_scenario_generation_config=config,
4774-
allow_cross_region_model=allow_cross_region_model,
4775-
)
4783+
response = await self._generate_user_scenarios(
4784+
agents=parsed_agent_info.agents,
4785+
root_agent_id=parsed_agent_info.root_agent_id,
4786+
user_scenario_generation_config=config,
4787+
allow_cross_region_model=allow_cross_region_model,
4788+
)
47764789
return _evals_utils._postprocess_user_scenarios_response(response)
47774790

47784791
async def generate_loss_clusters(

agentplatform/_genai/types/common.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5501,6 +5501,11 @@ class _GenerateUserScenariosParameters(_common.BaseModel):
55015501
default=None,
55025502
description="""Opt-in flag to authorize cross-region routing for LLM models.""",
55035503
)
5504+
gemini_agent_config: Optional[GeminiAgentConfig] = Field(
5505+
default=None,
5506+
description="""If set, the server derives the agents map and root_agent_id
5507+
from the referenced Gemini Agent server-side.""",
5508+
)
55045509

55055510

55065511
class _GenerateUserScenariosParametersDict(TypedDict, total=False):
@@ -5524,6 +5529,10 @@ class _GenerateUserScenariosParametersDict(TypedDict, total=False):
55245529
allow_cross_region_model: Optional[bool]
55255530
"""Opt-in flag to authorize cross-region routing for LLM models."""
55265531

5532+
gemini_agent_config: Optional[GeminiAgentConfigDict]
5533+
"""If set, the server derives the agents map and root_agent_id
5534+
from the referenced Gemini Agent server-side."""
5535+
55275536

55285537
_GenerateUserScenariosParametersOrDict = Union[
55295538
_GenerateUserScenariosParameters, _GenerateUserScenariosParametersDict

tests/unit/agentplatform/genai/test_evals.py

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9213,19 +9213,9 @@ async def test_async_generate_conversation_scenarios(self):
92139213
request_body = call_args[0][2] # Third positional arg is the request dict
92149214
assert request_body.get("allowCrossRegionModel") is True
92159215

9216-
@mock.patch.object(_evals_common, "_fetch_agent_config_dict")
9217-
def test_generate_conversation_scenarios_from_gemini_agent(
9218-
self, mock_fetch_agent_config
9219-
):
9220-
mock_fetch_agent_config.return_value = (
9221-
agentplatform_genai_types.evals.AgentConfig(
9222-
agent_id="test-agent",
9223-
instruction="You are a helpful travel assistant.",
9224-
description="An agent that books flights.",
9225-
tools=[genai_types.Tool(google_search=genai_types.GoogleSearch())],
9226-
)
9227-
)
9228-
9216+
def test_generate_conversation_scenarios_from_gemini_agent(self):
9217+
"""When `agent` is a Gemini agent resource, gemini_agent_config is
9218+
forwarded to the server (no client-side synthesis)."""
92299219
evals_module = evals.Evals(api_client_=self.mock_api_client)
92309220

92319221
with mock.patch.object(
@@ -9237,18 +9227,10 @@ def test_generate_conversation_scenarios_from_gemini_agent(
92379227
config={"count": 2},
92389228
)
92399229

9240-
mock_fetch_agent_config.assert_called_once_with(
9241-
self.mock_api_client, _TEST_GEMINI_AGENT
9242-
)
92439230
call_kwargs = mock_generate_user_scenarios.call_args.kwargs
9244-
assert call_kwargs["root_agent_id"] == "test-agent"
9245-
agents = call_kwargs["agents"]
9246-
assert "test-agent" in agents
9247-
derived_config = agents["test-agent"]
9248-
assert derived_config.instruction == "You are a helpful travel assistant."
9249-
assert derived_config.description == "An agent that books flights."
9250-
assert derived_config.tools is not None
9251-
assert derived_config.tools[0].google_search is not None
9231+
assert call_kwargs["gemini_agent_config"].gemini_agent == _TEST_GEMINI_AGENT
9232+
assert call_kwargs.get("agents") is None
9233+
assert call_kwargs.get("root_agent_id") is None
92529234

92539235
def test_generate_conversation_scenarios_agent_and_agent_info_raises(self):
92549236
evals_module = evals.Evals(api_client_=self.mock_api_client)

0 commit comments

Comments
 (0)