Skip to content

Commit a4ffb67

Browse files
vertex-sdk-botcopybara-github
authored andcommitted
fix: Inject agents map from agent_info into agent_data for create_evaluation_run
PiperOrigin-RevId: 914468929
1 parent 2c844a8 commit a4ffb67

2 files changed

Lines changed: 144 additions & 3 deletions

File tree

tests/unit/vertexai/genai/test_evals.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8869,6 +8869,133 @@ def test_create_evaluation_set_with_agent_data(
88698869
assert candidate_response["candidate"] == "test-candidate"
88708870
assert candidate_response["agent_data"] == agent_data
88718871

8872+
@mock.patch.object(_evals_common, "evals")
8873+
@mock.patch.object(_evals_common, "_gcs_utils")
8874+
def test_create_evaluation_set_injects_agents_map_from_agent_info(
8875+
self, mock_gcs_utils, mock_evals_module
8876+
):
8877+
"""Tests that agents map is injected from agent_info when agent_data has no agents."""
8878+
agent_data = {
8879+
"turns": [
8880+
{
8881+
"turn_index": 0,
8882+
"turn_id": "turn_0",
8883+
"events": [
8884+
{
8885+
"author": "my_agent",
8886+
"content": {
8887+
"parts": [{"text": "hello"}],
8888+
"role": "model",
8889+
},
8890+
}
8891+
],
8892+
}
8893+
]
8894+
}
8895+
eval_df = pd.DataFrame([{"prompt": "test prompt", "agent_data": agent_data}])
8896+
8897+
agent_info = vertexai_genai_types.evals.AgentInfo(
8898+
name="my_agent",
8899+
agents={
8900+
"my_agent": vertexai_genai_types.evals.AgentConfig(
8901+
agent_id="my_agent",
8902+
instruction="You are a helpful agent.",
8903+
)
8904+
},
8905+
root_agent_id="my_agent",
8906+
)
8907+
8908+
mock_gcs_instance = mock_gcs_utils.GcsUtils.return_value
8909+
mock_gcs_instance.upload_json_to_prefix.return_value = (
8910+
"gs://bucket/path/request.json"
8911+
)
8912+
8913+
mock_evals_instance = mock_evals_module.Evals.return_value
8914+
mock_eval_item = mock.Mock()
8915+
mock_eval_item.name = "eval_item_1"
8916+
mock_evals_instance.create_evaluation_item.return_value = mock_eval_item
8917+
8918+
mock_eval_set = mock.Mock()
8919+
mock_evals_instance.create_evaluation_set.return_value = mock_eval_set
8920+
8921+
_evals_common._create_evaluation_set_from_dataframe(
8922+
api_client=self.mock_api_client,
8923+
gcs_dest_prefix="gs://bucket/prefix",
8924+
eval_df=eval_df,
8925+
candidate_name="test-candidate",
8926+
parsed_agent_info=agent_info,
8927+
)
8928+
8929+
call_args = mock_gcs_instance.upload_json_to_prefix.call_args
8930+
uploaded_data = call_args.kwargs["data"]
8931+
8932+
candidate_response = uploaded_data["candidate_responses"][0]
8933+
uploaded_agent_data = candidate_response["agent_data"]
8934+
assert "agents" in uploaded_agent_data
8935+
assert "my_agent" in uploaded_agent_data["agents"]
8936+
assert (
8937+
uploaded_agent_data["agents"]["my_agent"]["instruction"]
8938+
== "You are a helpful agent."
8939+
)
8940+
8941+
@mock.patch.object(_evals_common, "evals")
8942+
@mock.patch.object(_evals_common, "_gcs_utils")
8943+
def test_create_evaluation_set_preserves_existing_agents_map(
8944+
self, mock_gcs_utils, mock_evals_module
8945+
):
8946+
"""Tests that an existing agents map in agent_data is not overwritten."""
8947+
agent_data = {
8948+
"turns": [{"turn_id": "turn1", "events": []}],
8949+
"agents": {
8950+
"original_agent": {
8951+
"agent_id": "original_agent",
8952+
"instruction": "original instruction",
8953+
}
8954+
},
8955+
}
8956+
eval_df = pd.DataFrame([{"prompt": "test prompt", "agent_data": agent_data}])
8957+
8958+
agent_info = vertexai_genai_types.evals.AgentInfo(
8959+
name="different_agent",
8960+
agents={
8961+
"different_agent": vertexai_genai_types.evals.AgentConfig(
8962+
agent_id="different_agent",
8963+
instruction="different instruction",
8964+
)
8965+
},
8966+
root_agent_id="different_agent",
8967+
)
8968+
8969+
mock_gcs_instance = mock_gcs_utils.GcsUtils.return_value
8970+
mock_gcs_instance.upload_json_to_prefix.return_value = (
8971+
"gs://bucket/path/request.json"
8972+
)
8973+
8974+
mock_evals_instance = mock_evals_module.Evals.return_value
8975+
mock_eval_item = mock.Mock()
8976+
mock_eval_item.name = "eval_item_1"
8977+
mock_evals_instance.create_evaluation_item.return_value = mock_eval_item
8978+
8979+
mock_eval_set = mock.Mock()
8980+
mock_evals_instance.create_evaluation_set.return_value = mock_eval_set
8981+
8982+
_evals_common._create_evaluation_set_from_dataframe(
8983+
api_client=self.mock_api_client,
8984+
gcs_dest_prefix="gs://bucket/prefix",
8985+
eval_df=eval_df,
8986+
candidate_name="test-candidate",
8987+
parsed_agent_info=agent_info,
8988+
)
8989+
8990+
call_args = mock_gcs_instance.upload_json_to_prefix.call_args
8991+
uploaded_data = call_args.kwargs["data"]
8992+
8993+
candidate_response = uploaded_data["candidate_responses"][0]
8994+
uploaded_agent_data = candidate_response["agent_data"]
8995+
# Original agents map should be preserved, not overwritten
8996+
assert "original_agent" in uploaded_agent_data["agents"]
8997+
assert "different_agent" not in uploaded_agent_data["agents"]
8998+
88728999
@mock.patch.object(_evals_common, "evals")
88739000
@mock.patch.object(_evals_common, "_gcs_utils")
88749001
def test_create_evaluation_set_with_history_column(

vertexai/_genai/_evals_common.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ def _resolve_dataset(
438438
dest,
439439
eval_df,
440440
candidate_name,
441+
parsed_agent_info=parsed_agent_info,
441442
)
442443
dataset = types.EvaluationRunDataSource(evaluation_set=eval_set.name)
443444
return dataset
@@ -2845,6 +2846,7 @@ def _create_evaluation_set_from_dataframe(
28452846
gcs_dest_prefix: str,
28462847
eval_df: pd.DataFrame,
28472848
candidate_name: Optional[str] = None,
2849+
parsed_agent_info: Optional[types.evals.AgentInfo] = None,
28482850
) -> Union[types.EvaluationSet, Any]:
28492851
"""Converts a dataframe to an EvaluationSet."""
28502852
eval_item_requests = []
@@ -2877,16 +2879,28 @@ def _create_evaluation_set_from_dataframe(
28772879
elif isinstance(agent_data_val, types.evals.AgentData):
28782880
agent_data_obj = agent_data_val
28792881

2882+
# When agent_data exists but has no agents map (e.g. from remote
2883+
# agent_engine inference), inject the agents map from agent_info so
2884+
# the server-side autorater can access tool definitions and
2885+
# instructions.
2886+
if (
2887+
agent_data_obj
2888+
and not agent_data_obj.agents
2889+
and parsed_agent_info
2890+
and parsed_agent_info.agents
2891+
):
2892+
agent_data_obj.agents = parsed_agent_info.agents
2893+
28802894
candidate_responses = []
28812895
if _evals_constant.RESPONSE in row or agent_data_obj or intermediate_events:
28822896
# Resolve the oneof conflict: prioritize agent_data over flat text
28832897
response_text = row.get(_evals_constant.RESPONSE) or None
28842898

28852899
if agent_data_obj and response_text:
28862900
logger.info(
2887-
"Both 'response' and 'agent_data' columns found in the evaluation dataset. "
2888-
"Prioritizing 'agent_data' and omitting 'response' text to satisfy "
2889-
"CandidateResponse protobuf oneof constraints."
2901+
"Both 'response' and 'agent_data' columns found in the evaluation"
2902+
" dataset. Prioritizing 'agent_data' and omitting 'response' text"
2903+
" to satisfy CandidateResponse protobuf oneof constraints."
28902904
)
28912905
response_text = None
28922906

0 commit comments

Comments
 (0)