Skip to content

Commit f9bcd6e

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 762079f commit f9bcd6e

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
@@ -2739,7 +2739,27 @@ def create_evaluation_run(
27392739
if isinstance(config, dict):
27402740
config = types.CreateEvaluationRunConfig.model_validate(config)
27412741

2742-
if agent_info and not inference_configs:
2742+
# Auto-construct inference_configs when agent_info is explicitly
2743+
# provided (existing behavior) OR an agent resource is provided
2744+
# (allows omitting agent_info for both Gemini Agents and Agent
2745+
# Engine). The server skips inference per-item when a
2746+
# CandidateResponse with a matching candidate name already exists,
2747+
# so it is safe to always send inference_configs.
2748+
_should_auto_infer = not inference_configs and (
2749+
agent_info or agent
2750+
)
2751+
if _should_auto_infer:
2752+
if not parsed_agent_info.name:
2753+
# Prefer the dataset's candidate_name (set by run_inference)
2754+
# so the inference_configs key matches the CandidateResponse
2755+
# and the server correctly skips already-completed items.
2756+
if (
2757+
isinstance(dataset, types.EvaluationDataset)
2758+
and dataset.candidate_name
2759+
):
2760+
parsed_agent_info.name = dataset.candidate_name
2761+
else:
2762+
parsed_agent_info.name = _evals_common._DEFAULT_CANDIDATE_NAME
27432763
parsed_user_simulator_config = (
27442764
evals_types.UserSimulatorConfig.model_validate(user_simulator_config)
27452765
if isinstance(user_simulator_config, dict)
@@ -2748,7 +2768,7 @@ def create_evaluation_run(
27482768
if getattr(parsed_user_simulator_config, "max_turn", None) is None:
27492769
parsed_user_simulator_config.max_turn = 5
27502770

2751-
candidate_name = parsed_agent_info.name or "candidate-1"
2771+
candidate_name = parsed_agent_info.name or _evals_common._DEFAULT_CANDIDATE_NAME
27522772
if agent and _evals_common._is_gemini_agent_resource(agent):
27532773
agent_run_config = types.AgentRunConfig(
27542774
gemini_agent_config=types.GeminiAgentConfig(gemini_agent=agent),
@@ -4523,7 +4543,27 @@ async def create_evaluation_run(
45234543
if isinstance(config, dict):
45244544
config = types.CreateEvaluationRunConfig.model_validate(config)
45254545

4526-
if agent_info and not inference_configs:
4546+
# Auto-construct inference_configs when agent_info is explicitly
4547+
# provided (existing behavior) OR an agent resource is provided
4548+
# (allows omitting agent_info for both Gemini Agents and Agent
4549+
# Engine). The server skips inference per-item when a
4550+
# CandidateResponse with a matching candidate name already exists,
4551+
# so it is safe to always send inference_configs.
4552+
_should_auto_infer = not inference_configs and (
4553+
agent_info or agent
4554+
)
4555+
if _should_auto_infer:
4556+
if not parsed_agent_info.name:
4557+
# Prefer the dataset's candidate_name (set by run_inference)
4558+
# so the inference_configs key matches the CandidateResponse
4559+
# and the server correctly skips already-completed items.
4560+
if (
4561+
isinstance(dataset, types.EvaluationDataset)
4562+
and dataset.candidate_name
4563+
):
4564+
parsed_agent_info.name = dataset.candidate_name
4565+
else:
4566+
parsed_agent_info.name = _evals_common._DEFAULT_CANDIDATE_NAME
45274567
parsed_user_simulator_config = (
45284568
evals_types.UserSimulatorConfig.model_validate(user_simulator_config)
45294569
if isinstance(user_simulator_config, dict)
@@ -4532,7 +4572,7 @@ async def create_evaluation_run(
45324572
if getattr(parsed_user_simulator_config, "max_turn", None) is None:
45334573
parsed_user_simulator_config.max_turn = 5
45344574

4535-
candidate_name = parsed_agent_info.name or "candidate-1"
4575+
candidate_name = parsed_agent_info.name or _evals_common._DEFAULT_CANDIDATE_NAME
45364576
if agent and _evals_common._is_gemini_agent_resource(agent):
45374577
agent_run_config = types.AgentRunConfig(
45384578
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
@@ -10779,6 +10779,10 @@ def test_create_evaluation_run_builds_gemini_agent_config(self):
1077910779
== _TEST_GEMINI_AGENT
1078010780
)
1078110781
assert "agent_engine" not in agent_run_config
10782+
# agent_info.name overrides the default candidate name.
10783+
inference_configs = request_body["inferenceConfigs"]
10784+
assert "gemini-agent" in inference_configs
10785+
assert _evals_common._DEFAULT_CANDIDATE_NAME not in inference_configs
1078210786

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

1081010948

1081110949
class TestResolveInteractionsForDisplay:

0 commit comments

Comments
 (0)