Skip to content

Commit 78525d2

Browse files
vertex-sdk-botcopybara-github
authored andcommitted
fix: introduce timeout for outgoing A2A requests from AgentEngine
PiperOrigin-RevId: 891315397
1 parent ee9fbe1 commit 78525d2

File tree

2 files changed

+98
-1
lines changed

2 files changed

+98
-1
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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+
# pylint: disable=protected-access,bad-continuation,missing-function-docstring
16+
17+
from unittest import mock
18+
19+
from tests.unit.vertexai.genai.replays import pytest_helper
20+
from vertexai._genai import types
21+
from google.genai import _api_client
22+
import httpx
23+
import pytest
24+
25+
26+
pytest.importorskip(
27+
"a2a.client.errors", reason="a2a-sdk not installed, skipping Agent Engine A2A tests"
28+
)
29+
from a2a.client import ( # noqa: E402 # pylint: disable=g-import-not-at-top,g-bad-import-order
30+
errors as a2a_errors,
31+
)
32+
33+
pytest_plugins = ("pytest_asyncio",)
34+
35+
36+
@pytest.mark.asyncio
37+
async def test_timeout_is_set(client):
38+
agent_engine = client.agent_engines.get(
39+
name="projects/932854658080/locations/us-central1/reasoningEngines/857830725653626880",
40+
)
41+
assert isinstance(agent_engine, types.AgentEngine)
42+
43+
message_data = {
44+
"messageId": "msg-123",
45+
"role": "user",
46+
"parts": [
47+
{"kind": "text", "text": "Where will be the Super Bowl held in 2026?"}
48+
],
49+
}
50+
with mock.patch(
51+
"httpx.AsyncClient", spec=httpx.AsyncClient
52+
) as mock_async_client_factory:
53+
# Replay mode does not capture A2A calls so instead of relying on the
54+
# real service, we simulate a failed call.
55+
mock_response = httpx.Response(
56+
401,
57+
request=httpx.Request("POST", "url"),
58+
json={
59+
"error": {
60+
"code": "UNAUTHENTICATED",
61+
"message": "Authentication failed: Missing or invalid API key.",
62+
}
63+
},
64+
)
65+
mock_async_client_instance = mock_async_client_factory.return_value
66+
mock_async_client_instance.post.return_value = mock_response
67+
mock_async_client_instance.send.return_value = mock_response
68+
69+
# These credentials are missing in replay mode, so we need to set a fake
70+
# value. (This is not necessary in record mode.)
71+
class FakeCredentials:
72+
token = "fake-token"
73+
74+
agent_engine.api_client._api_client._credentials = FakeCredentials()
75+
76+
try:
77+
await agent_engine.on_message_send(**message_data)
78+
except a2a_errors.A2AClientHTTPError as e:
79+
# Make sure that the authentication error was successfully
80+
# propagated, otherwise the test is not valid.
81+
assert e.status_code == 401
82+
83+
mock_async_client_factory.assert_called_once()
84+
assert mock_async_client_factory.call_args.kwargs["timeout"] == 99.0
85+
86+
87+
pytestmark = pytest_helper.setup(
88+
file=__file__,
89+
globals_for_file=globals(),
90+
test_method="agent_engines.get",
91+
http_options=_api_client.HttpOptions(timeout=99000),
92+
)

vertexai/_genai/_agent_engines_utils.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1822,7 +1822,12 @@ async def _method(self, **kwargs) -> Any: # type: ignore[no-untyped-def]
18221822
"Authorization": (
18231823
f"Bearer {self.api_client._api_client._credentials.token}"
18241824
)
1825-
}
1825+
},
1826+
timeout=(
1827+
self.api_client._api_client._http_options.timeout / 1000.0
1828+
if self.api_client._api_client._http_options.timeout
1829+
else None
1830+
),
18261831
),
18271832
)
18281833
factory = ClientFactory(config)

0 commit comments

Comments
 (0)