Skip to content

Commit 0dd813d

Browse files
vertex-sdk-botcopybara-github
authored andcommitted
fix: Standardize location resolution for A2aAgent and AdkApp
Fixes #6877 PiperOrigin-RevId: 931166805
1 parent 958e3eb commit 0dd813d

10 files changed

Lines changed: 274 additions & 18 deletions

File tree

agentplatform/agent_engines/templates/a2a.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,16 @@ def set_up(self):
320320
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "1"
321321
project = self._tmpl_attrs.get("project")
322322
os.environ["GOOGLE_CLOUD_PROJECT"] = project
323-
location = self._tmpl_attrs.get("location")
324-
os.environ["GOOGLE_CLOUD_LOCATION"] = location
323+
location = (
324+
os.getenv("GOOGLE_CLOUD_AGENT_ENGINE_LOCATION")
325+
or os.getenv("GOOGLE_CLOUD_LOCATION")
326+
or self._tmpl_attrs.get("location")
327+
)
328+
if location:
329+
if "GOOGLE_CLOUD_AGENT_ENGINE_LOCATION" not in os.environ:
330+
os.environ["GOOGLE_CLOUD_AGENT_ENGINE_LOCATION"] = location
331+
if "GOOGLE_CLOUD_LOCATION" not in os.environ:
332+
os.environ["GOOGLE_CLOUD_LOCATION"] = location
325333
agent_engine_id = os.getenv("GOOGLE_CLOUD_AGENT_ENGINE_ID", "test-agent-engine")
326334
version = "v1beta1"
327335

agentplatform/agent_engines/templates/adk.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -958,16 +958,17 @@ def set_up(self):
958958
project = self._tmpl_attrs.get("project")
959959
if project:
960960
os.environ["GOOGLE_CLOUD_PROJECT"] = project
961-
location = self._tmpl_attrs.get("location")
961+
location = (
962+
os.getenv("GOOGLE_CLOUD_AGENT_ENGINE_LOCATION")
963+
or os.getenv("GOOGLE_CLOUD_LOCATION")
964+
or self._tmpl_attrs.get("location")
965+
)
962966
if location:
963967
if "GOOGLE_CLOUD_AGENT_ENGINE_LOCATION" not in os.environ:
964968
os.environ["GOOGLE_CLOUD_AGENT_ENGINE_LOCATION"] = location
965969
if "GOOGLE_CLOUD_LOCATION" not in os.environ:
966970
os.environ["GOOGLE_CLOUD_LOCATION"] = location
967-
agent_engine_location = os.environ.get(
968-
"GOOGLE_CLOUD_AGENT_ENGINE_LOCATION", # the runtime env var (if set)
969-
location, # the location set in the AdkApp template
970-
)
971+
agent_engine_location = location
971972
express_mode_api_key = self._tmpl_attrs.get("express_mode_api_key")
972973
if express_mode_api_key and not project:
973974
os.environ["GOOGLE_API_KEY"] = express_mode_api_key

noxfile.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,9 @@ def unit_agentplatform_adk(session):
311311
# Install adk extras
312312
session.install("-e", ".[adk_testing]", "-c", constraints_path)
313313

314+
# Install A2A-specific testing dependencies
315+
session.install("a2a-sdk")
316+
314317
# Run py.test against the unit tests.
315318
session.run(
316319
"py.test",
@@ -324,6 +327,9 @@ def unit_agentplatform_adk(session):
324327
os.path.join(
325328
"tests", "unit", "agentplatform", "frameworks", "test_frameworks_adk.py"
326329
),
330+
os.path.join(
331+
"tests", "unit", "agentplatform", "frameworks", "test_frameworks_a2a.py"
332+
),
327333
*session.posargs,
328334
)
329335

setup.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,16 @@
175175
"aiohttp", # for ADK users to use aiohttp rather than httpx client
176176
]
177177

178+
a2a_testing_extra_require = [
179+
"a2a-sdk >= 1.0.0, < 2.0.0",
180+
]
181+
178182
adk_testing_extra_require = list(
179183
set(
180-
adk_extra_require + reasoning_engine_extra_require + ["absl-py", "pytest-xdist"]
184+
adk_extra_require
185+
+ a2a_testing_extra_require
186+
+ reasoning_engine_extra_require
187+
+ ["absl-py", "pytest-xdist"]
181188
)
182189
)
183190

@@ -356,6 +363,7 @@
356363
"ray_testing": ray_testing_extra_require,
357364
"adk": adk_extra_require,
358365
"adk_testing": adk_testing_extra_require,
366+
"a2a_testing": a2a_testing_extra_require,
359367
"reasoningengine": reasoning_engine_extra_require,
360368
"agent_engines": agent_engines_extra_require,
361369
"evaluation": evaluation_extra_require,
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import importlib
16+
import os
17+
from unittest import mock
18+
19+
import pytest
20+
import vertexai
21+
from google.cloud.aiplatform import initializer
22+
23+
# Skip A2A tests if a2a-sdk is not installed
24+
pytest.importorskip(
25+
"a2a.types", reason="a2a-sdk not installed, skipping A2A Agent tests"
26+
)
27+
28+
from a2a.types import AgentSkill
29+
30+
from vertexai.agent_engines.templates.a2a import A2aAgent as VertexA2aAgent
31+
from vertexai.agent_engines.templates.a2a import create_agent_card as vertex_create_card
32+
33+
from agentplatform.agent_engines.templates.a2a import A2aAgent as PlatformA2aAgent
34+
from agentplatform.agent_engines.templates.a2a import create_agent_card as platform_create_card
35+
36+
37+
_TEST_LOCATION = "us-central1"
38+
_TEST_PROJECT = "test-project"
39+
_TEST_SKILL = AgentSkill(
40+
id="hello_world",
41+
name="Returns hello world",
42+
description="just returns hello world",
43+
tags=["hello world"],
44+
examples=["hi", "hello world"],
45+
)
46+
47+
48+
@pytest.fixture
49+
def vertex_agent() -> VertexA2aAgent:
50+
card = vertex_create_card(agent_name="Test", description="Test", skills=[_TEST_SKILL])
51+
return VertexA2aAgent(agent_card=card)
52+
53+
54+
@pytest.fixture
55+
def platform_agent() -> PlatformA2aAgent:
56+
card = platform_create_card(agent_name="Test", description="Test", skills=[_TEST_SKILL])
57+
return PlatformA2aAgent(agent_card=card)
58+
59+
60+
class TestA2aLocationResolution:
61+
62+
@pytest.fixture(autouse=True)
63+
def patch_create_rest_routes(self, monkeypatch):
64+
"""Patch to avoid optional dependencies not actually used in these tests."""
65+
import sys
66+
from unittest.mock import MagicMock
67+
68+
mock_rest_routes = MagicMock()
69+
mock_rest_routes.create_rest_routes = MagicMock()
70+
monkeypatch.setitem(sys.modules, "a2a.server.routes.rest_routes", mock_rest_routes)
71+
72+
def setup_method(self):
73+
importlib.reload(initializer)
74+
from google.cloud import aiplatform
75+
importlib.reload(aiplatform)
76+
importlib.reload(vertexai)
77+
vertexai.init(project=_TEST_PROJECT, location=_TEST_LOCATION)
78+
79+
def teardown_method(self):
80+
initializer.global_pool.shutdown(wait=True)
81+
82+
@pytest.mark.parametrize(
83+
"agent_fixture",
84+
["vertex_agent", "platform_agent"],
85+
)
86+
def test_default_location_from_global_config(self, agent_fixture, request):
87+
agent = request.getfixturevalue(agent_fixture)
88+
with mock.patch.dict(os.environ, {}, clear=True):
89+
agent.set_up()
90+
assert os.environ.get("GOOGLE_CLOUD_LOCATION") == _TEST_LOCATION
91+
92+
# Check URL in agent card
93+
if hasattr(agent.agent_card, "url"):
94+
url = agent.agent_card.url
95+
else:
96+
url = agent.agent_card.supported_interfaces[0].url
97+
assert _TEST_LOCATION in url
98+
99+
@pytest.mark.parametrize(
100+
"agent_fixture",
101+
["vertex_agent", "platform_agent"],
102+
)
103+
def test_location_from_agent_engine_env_var(self, agent_fixture, request):
104+
agent = request.getfixturevalue(agent_fixture)
105+
with mock.patch.dict(
106+
os.environ,
107+
{"GOOGLE_CLOUD_AGENT_ENGINE_LOCATION": "us-east1"},
108+
clear=True,
109+
):
110+
agent.set_up()
111+
assert os.environ.get("GOOGLE_CLOUD_LOCATION") == "us-east1"
112+
113+
if hasattr(agent.agent_card, "url"):
114+
url = agent.agent_card.url
115+
else:
116+
url = agent.agent_card.supported_interfaces[0].url
117+
assert "us-east1" in url
118+
119+
@pytest.mark.parametrize(
120+
"agent_fixture",
121+
["vertex_agent", "platform_agent"],
122+
)
123+
def test_location_from_cloud_location_env_var(self, agent_fixture, request):
124+
agent = request.getfixturevalue(agent_fixture)
125+
with mock.patch.dict(
126+
os.environ,
127+
{"GOOGLE_CLOUD_LOCATION": "us-west1"},
128+
clear=True,
129+
):
130+
agent.set_up()
131+
assert os.environ.get("GOOGLE_CLOUD_LOCATION") == "us-west1"
132+
133+
if hasattr(agent.agent_card, "url"):
134+
url = agent.agent_card.url
135+
else:
136+
url = agent.agent_card.supported_interfaces[0].url
137+
assert "us-west1" in url
138+
139+
@pytest.mark.parametrize(
140+
"agent_fixture",
141+
["vertex_agent", "platform_agent"],
142+
)
143+
def test_location_env_var_precedence(self, agent_fixture, request):
144+
agent = request.getfixturevalue(agent_fixture)
145+
with mock.patch.dict(
146+
os.environ,
147+
{
148+
"GOOGLE_CLOUD_AGENT_ENGINE_LOCATION": "us-east1",
149+
"GOOGLE_CLOUD_LOCATION": "us-west1",
150+
},
151+
clear=True,
152+
):
153+
agent.set_up()
154+
# Should not overwrite existing GOOGLE_CLOUD_LOCATION
155+
assert os.environ.get("GOOGLE_CLOUD_LOCATION") == "us-west1"
156+
157+
if hasattr(agent.agent_card, "url"):
158+
url = agent.agent_card.url
159+
else:
160+
url = agent.agent_card.supported_interfaces[0].url
161+
assert "us-east1" in url

tests/unit/vertex_adk/test_agent_engine_templates_adk.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,57 @@ def test_dump_event_for_json():
10321032
# finally:
10331033
# initializer.global_pool.shutdown(wait=True)
10341034

1035+
@pytest.mark.usefixtures("google_auth_mock", "is_version_sufficient_mock")
1036+
class TestAdkLocationResolution:
1037+
def setup_method(self):
1038+
importlib.reload(initializer)
1039+
importlib.reload(vertexai)
1040+
vertexai.init(project=_TEST_PROJECT, location=_TEST_LOCATION)
1041+
1042+
def teardown_method(self):
1043+
initializer.global_pool.shutdown(wait=True)
1044+
1045+
@pytest.mark.parametrize(
1046+
"env_engine_loc, env_cloud_loc, expected_engine_loc, expected_cloud_loc",
1047+
[
1048+
(None, None, "us-central1", "us-central1"),
1049+
("us-east4", None, "us-east4", "us-east4"),
1050+
(None, "us-east4", "us-east4", "us-east4"),
1051+
("us-west1", "us-east4", "us-west1", "us-east4"),
1052+
],
1053+
)
1054+
def test_location_resolution(
1055+
self,
1056+
env_engine_loc,
1057+
env_cloud_loc,
1058+
expected_engine_loc,
1059+
expected_cloud_loc,
1060+
default_instrumentor_builder_mock,
1061+
get_project_id_mock,
1062+
):
1063+
env_patches = {}
1064+
if env_engine_loc is not None:
1065+
env_patches["GOOGLE_CLOUD_AGENT_ENGINE_LOCATION"] = env_engine_loc
1066+
if env_cloud_loc is not None:
1067+
env_patches["GOOGLE_CLOUD_LOCATION"] = env_cloud_loc
1068+
1069+
with mock.patch.dict(os.environ, env_patches, clear=False):
1070+
if env_engine_loc is None:
1071+
os.environ.pop("GOOGLE_CLOUD_AGENT_ENGINE_LOCATION", None)
1072+
if env_cloud_loc is None:
1073+
os.environ.pop("GOOGLE_CLOUD_LOCATION", None)
1074+
1075+
# Initialize AdkApp (which reads 'location' as 'us-central1' from global config)
1076+
app = agent_engines.AdkApp(agent=_TEST_AGENT)
1077+
assert app._tmpl_attrs.get("location") == "us-central1"
1078+
1079+
# Call set_up() to trigger location resolution
1080+
app.set_up()
1081+
1082+
# Assert that environment variables are correctly populated
1083+
assert os.environ.get("GOOGLE_CLOUD_AGENT_ENGINE_LOCATION") == expected_engine_loc
1084+
assert os.environ.get("GOOGLE_CLOUD_LOCATION") == expected_cloud_loc
1085+
10351086

10361087
@pytest.mark.usefixtures("is_version_sufficient_mock")
10371088
class TestAdkAppErrors:

vertexai/agent_engines/templates/a2a.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,16 @@ def set_up(self):
320320
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "1"
321321
project = self._tmpl_attrs.get("project")
322322
os.environ["GOOGLE_CLOUD_PROJECT"] = project
323-
location = self._tmpl_attrs.get("location")
324-
os.environ["GOOGLE_CLOUD_LOCATION"] = location
323+
location = (
324+
os.getenv("GOOGLE_CLOUD_AGENT_ENGINE_LOCATION")
325+
or os.getenv("GOOGLE_CLOUD_LOCATION")
326+
or self._tmpl_attrs.get("location")
327+
)
328+
if location:
329+
if "GOOGLE_CLOUD_AGENT_ENGINE_LOCATION" not in os.environ:
330+
os.environ["GOOGLE_CLOUD_AGENT_ENGINE_LOCATION"] = location
331+
if "GOOGLE_CLOUD_LOCATION" not in os.environ:
332+
os.environ["GOOGLE_CLOUD_LOCATION"] = location
325333
agent_engine_id = os.getenv("GOOGLE_CLOUD_AGENT_ENGINE_ID", "test-agent-engine")
326334
version = "v1beta1"
327335

vertexai/agent_engines/templates/adk.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -930,16 +930,17 @@ def set_up(self):
930930
project = self._tmpl_attrs.get("project")
931931
if project:
932932
os.environ["GOOGLE_CLOUD_PROJECT"] = project
933-
location = self._tmpl_attrs.get("location")
933+
location = (
934+
os.getenv("GOOGLE_CLOUD_AGENT_ENGINE_LOCATION")
935+
or os.getenv("GOOGLE_CLOUD_LOCATION")
936+
or self._tmpl_attrs.get("location")
937+
)
934938
if location:
935939
if "GOOGLE_CLOUD_AGENT_ENGINE_LOCATION" not in os.environ:
936940
os.environ["GOOGLE_CLOUD_AGENT_ENGINE_LOCATION"] = location
937941
if "GOOGLE_CLOUD_LOCATION" not in os.environ:
938942
os.environ["GOOGLE_CLOUD_LOCATION"] = location
939-
agent_engine_location = os.environ.get(
940-
"GOOGLE_CLOUD_AGENT_ENGINE_LOCATION", # the runtime env var (if set)
941-
location, # the location set in the AdkApp template
942-
)
943+
agent_engine_location = location
943944
express_mode_api_key = self._tmpl_attrs.get("express_mode_api_key")
944945
if express_mode_api_key and not project:
945946
os.environ["GOOGLE_API_KEY"] = express_mode_api_key

vertexai/preview/reasoning_engines/templates/a2a.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,16 @@ def set_up(self):
241241
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "1"
242242
project = self._tmpl_attrs.get("project")
243243
os.environ["GOOGLE_CLOUD_PROJECT"] = project
244-
location = self._tmpl_attrs.get("location")
245-
os.environ["GOOGLE_CLOUD_LOCATION"] = location
244+
location = (
245+
os.getenv("GOOGLE_CLOUD_AGENT_ENGINE_LOCATION")
246+
or os.getenv("GOOGLE_CLOUD_LOCATION")
247+
or self._tmpl_attrs.get("location")
248+
)
249+
if location:
250+
if "GOOGLE_CLOUD_AGENT_ENGINE_LOCATION" not in os.environ:
251+
os.environ["GOOGLE_CLOUD_AGENT_ENGINE_LOCATION"] = location
252+
if "GOOGLE_CLOUD_LOCATION" not in os.environ:
253+
os.environ["GOOGLE_CLOUD_LOCATION"] = location
246254
agent_engine_id = os.getenv("GOOGLE_CLOUD_AGENT_ENGINE_ID", "test-agent-engine")
247255
version = "v1beta1"
248256

vertexai/preview/reasoning_engines/templates/adk.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -739,7 +739,11 @@ def set_up(self):
739739
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "1"
740740
project = self._tmpl_attrs.get("project")
741741
os.environ["GOOGLE_CLOUD_PROJECT"] = project
742-
location = self._tmpl_attrs.get("location")
742+
location = (
743+
os.getenv("GOOGLE_CLOUD_AGENT_ENGINE_LOCATION")
744+
or os.getenv("GOOGLE_CLOUD_LOCATION")
745+
or self._tmpl_attrs.get("location")
746+
)
743747
if location:
744748
if "GOOGLE_CLOUD_AGENT_ENGINE_LOCATION" not in os.environ:
745749
os.environ["GOOGLE_CLOUD_AGENT_ENGINE_LOCATION"] = location

0 commit comments

Comments
 (0)