Skip to content

Commit 7c2cbd6

Browse files
vertex-sdk-botcopybara-github
authored andcommitted
feat: Make agent_info optional for running inference with create_evaluation_run
PiperOrigin-RevId: 951079941
1 parent ab757c7 commit 7c2cbd6

4 files changed

Lines changed: 188 additions & 8 deletions

File tree

agentplatform/_genai/_evals_common.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
PARTS = _evals_constant.PARTS
8989
USER_AUTHOR = _evals_constant.USER_AUTHOR
9090
AGENT_DATA = _evals_constant.AGENT_DATA
91+
_DEFAULT_CANDIDATE_NAME = _evals_constant.DEFAULT_CANDIDATE_NAME
9192

9293

9394
@contextlib.contextmanager
@@ -528,11 +529,11 @@ def _resolve_inference_configs(
528529
if inference_configs is None:
529530
inference_configs = {}
530531

531-
# We might have used "candidate-1" as a placeholder key in the caller,
532-
# let's migrate it to the agent name, or if it doesn't exist, just create it.
533-
if "candidate-1" in inference_configs:
532+
# We might have used the default candidate name as a placeholder key
533+
# in the caller; migrate it to the agent name.
534+
if _DEFAULT_CANDIDATE_NAME in inference_configs:
534535
inference_configs[parsed_agent_info.name] = inference_configs.pop(
535-
"candidate-1"
536+
_DEFAULT_CANDIDATE_NAME
536537
)
537538

538539
if parsed_agent_info.name not in inference_configs:

agentplatform/_genai/_evals_constant.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
CONVERSATION_PLAN = "conversation_plan"
6464
HISTORY = "history"
6565
CONVERSATION_HISTORY = "conversation_history"
66+
DEFAULT_CANDIDATE_NAME = "candidate-1"
6667

6768
COMMON_DATASET_COLUMNS = frozenset(
6869
{

agentplatform/_genai/evals.py

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2746,7 +2746,25 @@ def create_evaluation_run(
27462746
if isinstance(config, dict):
27472747
config = types.CreateEvaluationRunConfig.model_validate(config)
27482748

2749-
if agent_info and not inference_configs:
2749+
# Auto-construct inference_configs when agent_info is explicitly
2750+
# provided (existing behavior) OR an agent resource is provided
2751+
# (allows omitting agent_info for both Gemini Agents and Agent
2752+
# Engine). The server skips inference per-item when a
2753+
# CandidateResponse with a matching candidate name already exists,
2754+
# so it is safe to always send inference_configs.
2755+
_should_auto_infer = not inference_configs and (agent_info or agent)
2756+
if _should_auto_infer:
2757+
if not parsed_agent_info.name:
2758+
# Prefer the dataset's candidate_name (set by run_inference)
2759+
# so the inference_configs key matches the CandidateResponse
2760+
# and the server correctly skips already-completed items.
2761+
if (
2762+
isinstance(dataset, types.EvaluationDataset)
2763+
and dataset.candidate_name
2764+
):
2765+
parsed_agent_info.name = dataset.candidate_name
2766+
else:
2767+
parsed_agent_info.name = _evals_common._DEFAULT_CANDIDATE_NAME
27502768
parsed_user_simulator_config = (
27512769
evals_types.UserSimulatorConfig.model_validate(user_simulator_config)
27522770
if isinstance(user_simulator_config, dict)
@@ -2755,7 +2773,9 @@ def create_evaluation_run(
27552773
if getattr(parsed_user_simulator_config, "max_turn", None) is None:
27562774
parsed_user_simulator_config.max_turn = 5
27572775

2758-
candidate_name = parsed_agent_info.name or "candidate-1"
2776+
candidate_name = (
2777+
parsed_agent_info.name or _evals_common._DEFAULT_CANDIDATE_NAME
2778+
)
27592779
if agent and _evals_common._is_gemini_agent_resource(agent):
27602780
agent_run_config = types.AgentRunConfig(
27612781
gemini_agent_config=types.GeminiAgentConfig(gemini_agent=agent),
@@ -4534,7 +4554,25 @@ async def create_evaluation_run(
45344554
if isinstance(config, dict):
45354555
config = types.CreateEvaluationRunConfig.model_validate(config)
45364556

4537-
if agent_info and not inference_configs:
4557+
# Auto-construct inference_configs when agent_info is explicitly
4558+
# provided (existing behavior) OR an agent resource is provided
4559+
# (allows omitting agent_info for both Gemini Agents and Agent
4560+
# Engine). The server skips inference per-item when a
4561+
# CandidateResponse with a matching candidate name already exists,
4562+
# so it is safe to always send inference_configs.
4563+
_should_auto_infer = not inference_configs and (agent_info or agent)
4564+
if _should_auto_infer:
4565+
if not parsed_agent_info.name:
4566+
# Prefer the dataset's candidate_name (set by run_inference)
4567+
# so the inference_configs key matches the CandidateResponse
4568+
# and the server correctly skips already-completed items.
4569+
if (
4570+
isinstance(dataset, types.EvaluationDataset)
4571+
and dataset.candidate_name
4572+
):
4573+
parsed_agent_info.name = dataset.candidate_name
4574+
else:
4575+
parsed_agent_info.name = _evals_common._DEFAULT_CANDIDATE_NAME
45384576
parsed_user_simulator_config = (
45394577
evals_types.UserSimulatorConfig.model_validate(user_simulator_config)
45404578
if isinstance(user_simulator_config, dict)
@@ -4543,7 +4581,9 @@ async def create_evaluation_run(
45434581
if getattr(parsed_user_simulator_config, "max_turn", None) is None:
45444582
parsed_user_simulator_config.max_turn = 5
45454583

4546-
candidate_name = parsed_agent_info.name or "candidate-1"
4584+
candidate_name = (
4585+
parsed_agent_info.name or _evals_common._DEFAULT_CANDIDATE_NAME
4586+
)
45474587
if agent and _evals_common._is_gemini_agent_resource(agent):
45484588
agent_run_config = types.AgentRunConfig(
45494589
gemini_agent_config=types.GeminiAgentConfig(gemini_agent=agent),

tests/unit/agentplatform/genai/test_evals.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10761,6 +10761,10 @@ def test_create_evaluation_run_builds_gemini_agent_config(self):
1076110761
== _TEST_GEMINI_AGENT
1076210762
)
1076310763
assert "agent_engine" not in agent_run_config
10764+
# agent_info.name overrides the default candidate name.
10765+
inference_configs = request_body["inferenceConfigs"]
10766+
assert "gemini-agent" in inference_configs
10767+
assert _evals_common._DEFAULT_CANDIDATE_NAME not in inference_configs
1076410768

1076510769
def test_create_evaluation_run_agent_engine_does_not_set_gemini(self):
1076610770
evals_module = evals.Evals(api_client_=self.mock_api_client)
@@ -10788,6 +10792,140 @@ def test_create_evaluation_run_agent_engine_does_not_set_gemini(self):
1078810792
agent_run_config = self._agent_run_config(request_body)
1078910793
assert "gemini_agent_config" not in agent_run_config
1079010794
assert agent_run_config["agent_engine"] == _TEST_AGENT_ENGINE
10795+
# agent_info.name overrides the default candidate name.
10796+
inference_configs = request_body["inferenceConfigs"]
10797+
assert "ae-agent" in inference_configs
10798+
assert _evals_common._DEFAULT_CANDIDATE_NAME not in inference_configs
10799+
10800+
def test_create_evaluation_run_gemini_agent_without_agent_info(self):
10801+
"""Gemini agent resource alone triggers inference_configs auto-construction."""
10802+
evals_module = evals.Evals(api_client_=self.mock_api_client)
10803+
10804+
evals_module.create_evaluation_run(
10805+
dataset=agentplatform_genai_types.EvaluationRunDataSource(
10806+
evaluation_set="projects/123/locations/us-central1/evaluationSets/789"
10807+
),
10808+
metrics=[
10809+
agentplatform_genai_types.EvaluationRunMetric(
10810+
metric="multi_turn_task_success_v1",
10811+
metric_config=agentplatform_genai_types.UnifiedMetric(
10812+
predefined_metric_spec=genai_types.PredefinedMetricSpec(
10813+
metric_spec_name="multi_turn_task_success_v1",
10814+
)
10815+
),
10816+
)
10817+
],
10818+
dest="gs://test-bucket/output",
10819+
agent=_TEST_GEMINI_AGENT,
10820+
# No agent_info provided.
10821+
)
10822+
10823+
request_body = self._get_create_run_body()
10824+
agent_run_config = self._agent_run_config(request_body)
10825+
assert (
10826+
agent_run_config["gemini_agent_config"]["gemini_agent"]
10827+
== _TEST_GEMINI_AGENT
10828+
)
10829+
assert "agent_engine" not in agent_run_config
10830+
# Default candidate name should match the constant.
10831+
inference_configs = request_body["inferenceConfigs"]
10832+
assert _evals_common._DEFAULT_CANDIDATE_NAME in inference_configs
10833+
10834+
def test_create_evaluation_run_no_agent_no_agent_info_no_inference(self):
10835+
"""Without agent or agent_info, no inference_configs are auto-constructed."""
10836+
evals_module = evals.Evals(api_client_=self.mock_api_client)
10837+
10838+
evals_module.create_evaluation_run(
10839+
dataset=agentplatform_genai_types.EvaluationRunDataSource(
10840+
evaluation_set="projects/123/locations/us-central1/evaluationSets/789"
10841+
),
10842+
metrics=[
10843+
agentplatform_genai_types.EvaluationRunMetric(
10844+
metric="multi_turn_task_success_v1",
10845+
metric_config=agentplatform_genai_types.UnifiedMetric(
10846+
predefined_metric_spec=genai_types.PredefinedMetricSpec(
10847+
metric_spec_name="multi_turn_task_success_v1",
10848+
)
10849+
),
10850+
)
10851+
],
10852+
dest="gs://test-bucket/output",
10853+
# No agent, no agent_info.
10854+
)
10855+
10856+
request_body = self._get_create_run_body()
10857+
assert "inferenceConfigs" not in request_body or not request_body.get(
10858+
"inferenceConfigs"
10859+
)
10860+
10861+
def test_create_evaluation_run_agent_engine_without_agent_info(self):
10862+
"""Agent Engine resource alone triggers inference_configs auto-construction."""
10863+
evals_module = evals.Evals(api_client_=self.mock_api_client)
10864+
10865+
evals_module.create_evaluation_run(
10866+
dataset=agentplatform_genai_types.EvaluationRunDataSource(
10867+
evaluation_set="projects/123/locations/us-central1/evaluationSets/789"
10868+
),
10869+
metrics=[
10870+
agentplatform_genai_types.EvaluationRunMetric(
10871+
metric="multi_turn_task_success_v1",
10872+
metric_config=agentplatform_genai_types.UnifiedMetric(
10873+
predefined_metric_spec=genai_types.PredefinedMetricSpec(
10874+
metric_spec_name="multi_turn_task_success_v1",
10875+
)
10876+
),
10877+
)
10878+
],
10879+
dest="gs://test-bucket/output",
10880+
agent=_TEST_AGENT_ENGINE,
10881+
# No agent_info provided.
10882+
)
10883+
10884+
request_body = self._get_create_run_body()
10885+
agent_run_config = self._agent_run_config(request_body)
10886+
assert agent_run_config["agent_engine"] == _TEST_AGENT_ENGINE
10887+
assert "gemini_agent_config" not in agent_run_config
10888+
# Default candidate name should match the constant.
10889+
inference_configs = request_body["inferenceConfigs"]
10890+
assert _evals_common._DEFAULT_CANDIDATE_NAME in inference_configs
10891+
10892+
@mock.patch.object(_evals_common, "_resolve_dataset")
10893+
def test_create_evaluation_run_uses_dataset_candidate_name(
10894+
self, mock_resolve_dataset
10895+
):
10896+
"""When dataset.candidate_name is set (e.g. from run_inference), the
10897+
inference_configs key should match it instead of using the default."""
10898+
mock_resolve_dataset.return_value = (
10899+
agentplatform_genai_types.EvaluationRunDataSource(
10900+
evaluation_set="projects/123/locations/us-central1/evaluationSets/789"
10901+
)
10902+
)
10903+
evals_module = evals.Evals(api_client_=self.mock_api_client)
10904+
10905+
evals_module.create_evaluation_run(
10906+
dataset=agentplatform_genai_types.EvaluationDataset(
10907+
eval_dataset_df=pd.DataFrame({"prompt": ["hello"]}),
10908+
candidate_name="my-agent-v2",
10909+
),
10910+
metrics=[
10911+
agentplatform_genai_types.EvaluationRunMetric(
10912+
metric="multi_turn_task_success_v1",
10913+
metric_config=agentplatform_genai_types.UnifiedMetric(
10914+
predefined_metric_spec=genai_types.PredefinedMetricSpec(
10915+
metric_spec_name="multi_turn_task_success_v1",
10916+
)
10917+
),
10918+
)
10919+
],
10920+
dest="gs://test-bucket/output",
10921+
agent=_TEST_GEMINI_AGENT,
10922+
# No agent_info -- candidate name should come from dataset.
10923+
)
10924+
10925+
request_body = self._get_create_run_body()
10926+
inference_configs = request_body["inferenceConfigs"]
10927+
assert "my-agent-v2" in inference_configs
10928+
assert _evals_common._DEFAULT_CANDIDATE_NAME not in inference_configs
1079110929

1079210930

1079310931
class TestResolveInteractionsForDisplay:

0 commit comments

Comments
 (0)