Skip to content

Commit 8b43add

Browse files
vertex-sdk-botcopybara-github
authored andcommitted
fix: GenAI Client(evals) - skip agent config fetch when agent and client locations differ
PiperOrigin-RevId: 956029497
1 parent 76b9616 commit 8b43add

2 files changed

Lines changed: 89 additions & 22 deletions

File tree

agentplatform/_genai/_evals_common.py

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -904,31 +904,56 @@ def _fetch_agent_config_dict(
904904
An AgentConfig with ``agent_id`` and, when available,
905905
``instruction``, ``description``, ``agent_type``, and ``tools``.
906906
"""
907-
agent_short_id = agent_resource_name.split("/")[-1] or "agent"
907+
parts = agent_resource_name.split("/")
908+
agent_location = None
909+
agent_short_id = "agent"
910+
911+
# Expected format: projects/{project}/locations/{location}/agents/{agent_id}
912+
if (
913+
len(parts) >= 6
914+
and parts[0] == "projects"
915+
and parts[2] == "locations"
916+
and parts[4] == "agents"
917+
):
918+
agent_location = parts[3]
919+
agent_short_id = parts[5]
920+
else:
921+
agent_short_id = parts[-1] or "agent"
908922

909923
instruction: Optional[str] = None
910924
description: Optional[str] = None
911925
agent_type: Optional[str] = None
912926
tools: Optional[list[genai_types.Tool]] = None
913927

914-
# TODO(b/539762376): This drops the location from `agent_resource_name` and
915-
# sends a relative path, so the client re-qualifies it with its own
916-
# location. Agents in `locations/global` are queried in the client's region
917-
# and fail with 400, silently leaving the returned AgentConfig empty.
918-
try:
919-
agent_resp = api_client.request("get", f"agents/{agent_short_id}", {}, None)
920-
if agent_resp.body:
921-
agent_dict = json.loads(agent_resp.body)
922-
instruction = agent_dict.get("system_instruction") or None
923-
description = agent_dict.get("description") or None
924-
agent_type = agent_dict.get("base_agent") or None
925-
tools = _agent_tools_to_config_tools(agent_dict.get("tools"))
926-
except Exception as e: # pylint: disable=broad-exception-caught
928+
client_location = _get_resolved_location(api_client)
929+
930+
if agent_location and client_location and agent_location != client_location:
927931
logger.warning(
928-
"Failed to fetch agent config for '%s' (continuing without it): %s",
932+
"Skipping agent config fetch for '%s' due to location mismatch. "
933+
"Agent location is '%s', but client location is '%s'. "
934+
"To fetch the agent config, configure a client with matching location.",
929935
agent_resource_name,
930-
e,
936+
agent_location,
937+
client_location,
931938
)
939+
else:
940+
try:
941+
request_path = (
942+
agent_resource_name if agent_location else f"agents/{agent_short_id}"
943+
)
944+
agent_resp = api_client.request("get", request_path, {}, None)
945+
if agent_resp.body:
946+
agent_dict = json.loads(agent_resp.body)
947+
instruction = agent_dict.get("system_instruction") or None
948+
description = agent_dict.get("description") or None
949+
agent_type = agent_dict.get("base_agent") or None
950+
tools = _agent_tools_to_config_tools(agent_dict.get("tools"))
951+
except Exception as e: # pylint: disable=broad-exception-caught
952+
logger.warning(
953+
"Failed to fetch agent config for '%s' (continuing without it): %s",
954+
agent_resource_name,
955+
e,
956+
)
932957

933958
return types.evals.AgentConfig( # pytype: disable=missing-parameter
934959
agent_id=agent_short_id,
@@ -941,7 +966,10 @@ def _fetch_agent_config_dict(
941966

942967
def _get_resolved_location(api_client: Any) -> Optional[str]:
943968
"""Returns the location configured on the API client."""
944-
return getattr(api_client, "location", None)
969+
loc = getattr(api_client, "location", None)
970+
if isinstance(loc, str):
971+
return loc
972+
return None
945973

946974

947975
class _InteractionsRestClient:

tests/unit/agentplatform/genai/test_evals.py

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10301,9 +10301,9 @@ def test_resolve_interactions_to_eval_cases_happy_path(self):
1030110301

1030210302
def mock_request(method, path, *args, **kwargs):
1030310303
resp = mock.MagicMock()
10304-
if path.startswith("interactions/"):
10304+
if path.endswith("interactions/i1"):
1030510305
resp.body = interaction_body
10306-
elif path.startswith("agents/"):
10306+
elif path.endswith("agents/my-agent"):
1030710307
resp.body = agent_body
1030810308
else:
1030910309
resp.body = None
@@ -10314,9 +10314,9 @@ def mock_request(method, path, *args, **kwargs):
1031410314
original_case = agentplatform_genai_types.EvalCase(
1031510315
interactions_data_source=agentplatform_genai_types.InteractionsDataSource(
1031610316
gemini_agent_config=agentplatform_genai_types.GeminiAgentConfig(
10317-
gemini_agent="projects/p/locations/l/agents/my-agent",
10317+
gemini_agent="projects/p/locations/us-central1/agents/my-agent",
1031810318
),
10319-
interaction="projects/p/locations/l/interactions/i1",
10319+
interaction="projects/p/locations/us-central1/interactions/i1",
1032010320
),
1032110321
)
1032210322

@@ -11119,7 +11119,12 @@ def mock_request(method, path, *args, **kwargs):
1111911119
# Verify the interaction and agent were fetched.
1112011120
assert mock_api_client.request.call_count == 2
1112111121
mock_api_client.request.assert_any_call("get", "interactions/test-id", {}, None)
11122-
mock_api_client.request.assert_any_call("get", "agents/my-agent", {}, None)
11122+
mock_api_client.request.assert_any_call(
11123+
"get",
11124+
"projects/p/locations/l/agents/my-agent",
11125+
{},
11126+
None,
11127+
)
1112311128

1112411129
def test_agents_map_populated_with_full_config(self):
1112511130
"""The agents dict includes instruction and tools from the Agent API."""
@@ -12072,6 +12077,40 @@ def test_mcp_server_kept_as_named_declaration(self):
1207212077
assert len(mcp_tool) == 1
1207312078
assert "my-mcp" in mcp_tool[0].function_declarations[0].description
1207412079

12080+
def test_skips_fetch_when_location_mismatches(self):
12081+
"""Skips agent config fetch and logs warning if locations mismatch."""
12082+
mock_api_client = mock.MagicMock()
12083+
mock_api_client.location = "us-central1"
12084+
mock_api_client.request.return_value = self._make_api_response({})
12085+
12086+
result = _evals_common._fetch_agent_config_dict(
12087+
mock_api_client,
12088+
"projects/p/locations/global/agents/my-agent",
12089+
)
12090+
assert result.agent_id == "my-agent"
12091+
assert result.instruction is None
12092+
mock_api_client.request.assert_not_called()
12093+
12094+
def test_uses_full_resource_name_when_location_matches(self):
12095+
"""Uses full resource name in GET request when client location matches."""
12096+
agent_json = {"system_instruction": "You are helpful."}
12097+
mock_api_client = mock.MagicMock()
12098+
mock_api_client.location = "us-central1"
12099+
mock_api_client.request.return_value = self._make_api_response(agent_json)
12100+
12101+
result = _evals_common._fetch_agent_config_dict(
12102+
mock_api_client,
12103+
"projects/p/locations/us-central1/agents/my-agent",
12104+
)
12105+
assert result.agent_id == "my-agent"
12106+
assert result.instruction == "You are helpful."
12107+
mock_api_client.request.assert_called_once_with(
12108+
"get",
12109+
"projects/p/locations/us-central1/agents/my-agent",
12110+
{},
12111+
None,
12112+
)
12113+
1207512114
def test_catalog_in_sync_with_server(self):
1207612115
"""SDK catalog keys and function names match the server-side catalog.
1207712116

0 commit comments

Comments
 (0)