From c9de7c5c7b2935716fc7bd921c600d1970473f47 Mon Sep 17 00:00:00 2001 From: Rishav C Date: Sun, 6 Sep 2026 20:51:49 -0400 Subject: [PATCH 1/5] feat(adk): make AgentEx client HTTP timeouts configurable by env var The AgentEx client's timeout comes from the SDK's DEFAULT_TIMEOUT, Timeout(connect=5.0, read=300, write=300, pool=300). Application code cannot change it: the ADK constructs its own clients internally at 14 sites (messages, tasks, events, tracing), all through create_async_agentex_client(), and none accepts a timeout from the caller. No environment variable controlled it either. The connect timeout is the one that matters. An AgentEx backend accepts connections serially, so connect latency grows with the number of concurrent callers. Measured against a local backend: concurrency 1 84 ms concurrency 20 423 ms concurrency 100 500 ms concurrency 200 1,010 ms An agent running 100 concurrent activities, each making ADK calls, pushes past the 5s budget and fails with httpcore.ConnectTimeout. One run produced 197 such failures in a single activity. A 5s connect against a 300s read is also internally inconsistent. Adds AGENTEX_CLIENT_{CONNECT,READ,WRITE,POOL}_TIMEOUT_SECONDS, following the existing EnvVarKeys and EnvironmentVariables pattern. Defaults equal the current DEFAULT_TIMEOUT, so an unconfigured process is unchanged. An explicit timeout= argument still wins, and a malformed value falls back to the SDK default with a warning rather than preventing client creation. --- src/agentex/lib/adk/utils/_modules/client.py | 28 +++++ src/agentex/lib/environment_variables.py | 13 +++ tests/lib/test_client_timeout_env.py | 102 +++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 tests/lib/test_client_timeout_env.py diff --git a/src/agentex/lib/adk/utils/_modules/client.py b/src/agentex/lib/adk/utils/_modules/client.py index 725289631..4907158e4 100644 --- a/src/agentex/lib/adk/utils/_modules/client.py +++ b/src/agentex/lib/adk/utils/_modules/client.py @@ -26,7 +26,35 @@ def auth_flow(self, request): yield request +def _timeout_from_env() -> httpx.Timeout: + """Build the client timeout from environment variables. + + Defaults match the SDK's DEFAULT_TIMEOUT, so an unconfigured process behaves + exactly as before. The connect timeout is the one worth raising: an AgentEx + backend accepts connections serially, so connect latency grows with the number + of callers and the 5s default is reached when a few hundred are in flight. + """ + env_vars = EnvironmentVariables.refresh() + return httpx.Timeout( + connect=env_vars.AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS, + read=env_vars.AGENTEX_CLIENT_READ_TIMEOUT_SECONDS, + write=env_vars.AGENTEX_CLIENT_WRITE_TIMEOUT_SECONDS, + pool=env_vars.AGENTEX_CLIENT_POOL_TIMEOUT_SECONDS, + ) + + def create_async_agentex_client(**kwargs) -> AsyncAgentex: + """Create an AsyncAgentex client. + + An explicit ``timeout=`` always wins; otherwise the timeout comes from the + AGENTEX_CLIENT_*_TIMEOUT_SECONDS environment variables. + """ + if "timeout" not in kwargs: + try: + kwargs["timeout"] = _timeout_from_env() + except Exception as exc: + # Never let timeout configuration stop a client being created. + logger.warning("Falling back to SDK default timeout: %r", exc) client = AsyncAgentex(**kwargs) client._client.auth = EnvAuth() return client diff --git a/src/agentex/lib/environment_variables.py b/src/agentex/lib/environment_variables.py index 7d893e462..70986511a 100644 --- a/src/agentex/lib/environment_variables.py +++ b/src/agentex/lib/environment_variables.py @@ -20,6 +20,11 @@ class EnvVarKeys(str, Enum): TEMPORAL_ADDRESS = "TEMPORAL_ADDRESS" REDIS_URL = "REDIS_URL" AGENTEX_BASE_URL = "AGENTEX_BASE_URL" + # AgentEx client HTTP timeouts (seconds) + AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS = "AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS" + AGENTEX_CLIENT_READ_TIMEOUT_SECONDS = "AGENTEX_CLIENT_READ_TIMEOUT_SECONDS" + AGENTEX_CLIENT_WRITE_TIMEOUT_SECONDS = "AGENTEX_CLIENT_WRITE_TIMEOUT_SECONDS" + AGENTEX_CLIENT_POOL_TIMEOUT_SECONDS = "AGENTEX_CLIENT_POOL_TIMEOUT_SECONDS" # Agent Identifiers AGENT_NAME = "AGENT_NAME" AGENT_DESCRIPTION = "AGENT_DESCRIPTION" @@ -61,6 +66,14 @@ class EnvironmentVariables(BaseModel): TEMPORAL_ADDRESS: str | None = "localhost:7233" REDIS_URL: str | None = None AGENTEX_BASE_URL: str | None = "http://localhost:5003" + # HTTP timeouts for the AgentEx client, in seconds. Defaults match the + # SDK's DEFAULT_TIMEOUT, so leaving these unset changes nothing. + # Raise the connect timeout when many concurrent activities share one + # backend: accepts queue, and 5s is reached at a few hundred in flight. + AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS: float = 5.0 + AGENTEX_CLIENT_READ_TIMEOUT_SECONDS: float = 300.0 + AGENTEX_CLIENT_WRITE_TIMEOUT_SECONDS: float = 300.0 + AGENTEX_CLIENT_POOL_TIMEOUT_SECONDS: float = 300.0 # Agent Identifiers AGENT_NAME: str AGENT_DESCRIPTION: str | None = None diff --git a/tests/lib/test_client_timeout_env.py b/tests/lib/test_client_timeout_env.py new file mode 100644 index 000000000..95b7671f6 --- /dev/null +++ b/tests/lib/test_client_timeout_env.py @@ -0,0 +1,102 @@ +"""Timeouts for the AgentEx client are configurable by environment variable. + +The connect timeout is the one that matters in practice. An AgentEx backend +accepts connections serially, so connect latency grows with the number of +concurrent callers, and the 5s default is reached once a few hundred are in +flight. Before this was configurable, the only way to change it was to pass +``timeout=`` at every construction site, which application code cannot do for +the client the ADK builds internally. +""" + +from __future__ import annotations + +import httpx +import pytest + +import agentex.lib.environment_variables as env_module +from agentex.lib.adk.utils._modules.client import ( + _timeout_from_env, + create_async_agentex_client, +) +from agentex.lib.environment_variables import EnvironmentVariables + + +@pytest.fixture(autouse=True) +def _clear_env_cache(): + """EnvironmentVariables.refresh() memoises into a module global.""" + env_module.refreshed_environment_variables = None + yield + env_module.refreshed_environment_variables = None + + +def _set_env(monkeypatch, **overrides: str) -> None: + # EnvironmentVariables has required fields; set them so construction succeeds. + monkeypatch.setenv("AGENT_NAME", "test-agent") + monkeypatch.setenv("ACP_URL", "http://localhost:8000") + for key, value in overrides.items(): + monkeypatch.setenv(key, value) + + +def test_defaults_match_the_sdk_default_timeout(monkeypatch): + """An unconfigured process must behave exactly as it did before.""" + _set_env(monkeypatch) + timeout = _timeout_from_env() + assert timeout.connect == 5.0 + assert timeout.read == 300.0 + assert timeout.write == 300.0 + assert timeout.pool == 300.0 + + +def test_connect_timeout_is_configurable(monkeypatch): + _set_env(monkeypatch, AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS="30") + timeout = _timeout_from_env() + assert timeout.connect == 30.0 + # the others are untouched + assert timeout.read == 300.0 + + +def test_all_four_are_configurable(monkeypatch): + _set_env( + monkeypatch, + AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS="30", + AGENTEX_CLIENT_READ_TIMEOUT_SECONDS="120", + AGENTEX_CLIENT_WRITE_TIMEOUT_SECONDS="90", + AGENTEX_CLIENT_POOL_TIMEOUT_SECONDS="60", + ) + timeout = _timeout_from_env() + assert (timeout.connect, timeout.read, timeout.write, timeout.pool) == ( + 30.0, + 120.0, + 90.0, + 60.0, + ) + + +def test_client_picks_up_the_env_timeout(monkeypatch): + _set_env(monkeypatch, AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS="30") + client = create_async_agentex_client(api_key="test", base_url="http://localhost:5003") + assert client.timeout.connect == 30.0 + + +def test_explicit_timeout_wins_over_the_environment(monkeypatch): + _set_env(monkeypatch, AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS="30") + client = create_async_agentex_client( + api_key="test", + base_url="http://localhost:5003", + timeout=httpx.Timeout(connect=7.0, read=8.0, write=9.0, pool=10.0), + ) + assert client.timeout.connect == 7.0 + + +def test_env_auth_is_still_attached(monkeypatch): + """The factory's original job must survive the change.""" + _set_env(monkeypatch) + client = create_async_agentex_client(api_key="test", base_url="http://localhost:5003") + assert client._client.auth is not None + + +def test_a_bad_value_does_not_prevent_client_creation(monkeypatch): + """Timeout configuration must never be the reason a client fails to build.""" + _set_env(monkeypatch, AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS="not-a-number") + client = create_async_agentex_client(api_key="test", base_url="http://localhost:5003") + assert client is not None From e02c23d4e5f951323152fd1a08772ff7ca73660f Mon Sep 17 00:00:00 2001 From: Rishav C Date: Sun, 6 Sep 2026 21:01:47 -0400 Subject: [PATCH 2/5] Remove an unused import from the timeout env var test The EnvironmentVariables symbol is reached through the env_module alias, so the direct import was dead and tripped ruff F401/I001 in CI. --- tests/lib/test_client_timeout_env.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/lib/test_client_timeout_env.py b/tests/lib/test_client_timeout_env.py index 95b7671f6..a96460e54 100644 --- a/tests/lib/test_client_timeout_env.py +++ b/tests/lib/test_client_timeout_env.py @@ -18,7 +18,6 @@ _timeout_from_env, create_async_agentex_client, ) -from agentex.lib.environment_variables import EnvironmentVariables @pytest.fixture(autouse=True) From 3db9365edb9e7dde134cd15438f6291d632354bb Mon Sep 17 00:00:00 2001 From: Rishav C Date: Sun, 6 Sep 2026 21:08:45 -0400 Subject: [PATCH 3/5] Address greptile: decouple the client timeouts from EnvironmentVariables Greptile flagged that adding the four timeout fields to the shared EnvironmentVariables model meant a malformed value broke far more than the client factory: refresh() is called from ~20 unguarded places, including AgentWorker startup and EnvAuth.auth_flow on every request. The try/except in create_async_agentex_client() did not contain that, it only made it look handled. Removing the try/except alone made it worse. agentex/lib/adk/utils/__init__.py constructs TemplatingModule() at module scope, which builds a client, so importing the ADK started requiring AGENT_NAME and ACP_URL to be set. The suppressed exception had been hiding that. Read the four values from os.environ in client.py instead. The shared model is untouched, so startup, auth and import are unaffected, and a malformed value raises a ValueError naming the variable at the point it is used rather than being swallowed. Adds a regression test asserting the timeout does not depend on the shared model. --- src/agentex/lib/adk/utils/_modules/client.py | 48 +++++++++----- src/agentex/lib/environment_variables.py | 20 ++---- tests/lib/test_client_timeout_env.py | 70 ++++++++++---------- 3 files changed, 73 insertions(+), 65 deletions(-) diff --git a/src/agentex/lib/adk/utils/_modules/client.py b/src/agentex/lib/adk/utils/_modules/client.py index 4907158e4..5312b7b6a 100644 --- a/src/agentex/lib/adk/utils/_modules/client.py +++ b/src/agentex/lib/adk/utils/_modules/client.py @@ -1,3 +1,4 @@ +import os from typing import override import httpx @@ -26,21 +27,40 @@ def auth_flow(self, request): yield request +# HTTP timeouts for the AgentEx client, in seconds. Defaults match the SDK's +# DEFAULT_TIMEOUT, so leaving these unset changes nothing. +_TIMEOUT_ENV_DEFAULTS = { + "connect": ("AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS", 5.0), + "read": ("AGENTEX_CLIENT_READ_TIMEOUT_SECONDS", 300.0), + "write": ("AGENTEX_CLIENT_WRITE_TIMEOUT_SECONDS", 300.0), + "pool": ("AGENTEX_CLIENT_POOL_TIMEOUT_SECONDS", 300.0), +} + + def _timeout_from_env() -> httpx.Timeout: """Build the client timeout from environment variables. - Defaults match the SDK's DEFAULT_TIMEOUT, so an unconfigured process behaves - exactly as before. The connect timeout is the one worth raising: an AgentEx - backend accepts connections serially, so connect latency grows with the number - of callers and the 5s default is reached when a few hundred are in flight. + Read from ``os.environ`` rather than from ``EnvironmentVariables``. That model + is loaded by worker startup and by ``EnvAuth.auth_flow`` on every request, and + ``agentex.lib.adk.utils`` builds a client at import time, so a field added + there would make a malformed timeout break all three. Reading here keeps the + blast radius to the one value that is actually wrong. + + The connect timeout is the one worth raising: an AgentEx backend accepts + connections serially, so connect latency grows with the number of callers and + the 5s default is reached when a few hundred are in flight. """ - env_vars = EnvironmentVariables.refresh() - return httpx.Timeout( - connect=env_vars.AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS, - read=env_vars.AGENTEX_CLIENT_READ_TIMEOUT_SECONDS, - write=env_vars.AGENTEX_CLIENT_WRITE_TIMEOUT_SECONDS, - pool=env_vars.AGENTEX_CLIENT_POOL_TIMEOUT_SECONDS, - ) + values = {} + for field, (env_var, default) in _TIMEOUT_ENV_DEFAULTS.items(): + raw = os.environ.get(env_var) + if raw is None or raw.strip() == "": + values[field] = default + continue + try: + values[field] = float(raw) + except ValueError as exc: + raise ValueError(f"{env_var} must be a number in seconds, got {raw!r}") from exc + return httpx.Timeout(**values) def create_async_agentex_client(**kwargs) -> AsyncAgentex: @@ -50,11 +70,7 @@ def create_async_agentex_client(**kwargs) -> AsyncAgentex: AGENTEX_CLIENT_*_TIMEOUT_SECONDS environment variables. """ if "timeout" not in kwargs: - try: - kwargs["timeout"] = _timeout_from_env() - except Exception as exc: - # Never let timeout configuration stop a client being created. - logger.warning("Falling back to SDK default timeout: %r", exc) + kwargs["timeout"] = _timeout_from_env() client = AsyncAgentex(**kwargs) client._client.auth = EnvAuth() return client diff --git a/src/agentex/lib/environment_variables.py b/src/agentex/lib/environment_variables.py index 70986511a..00dbbaada 100644 --- a/src/agentex/lib/environment_variables.py +++ b/src/agentex/lib/environment_variables.py @@ -20,16 +20,12 @@ class EnvVarKeys(str, Enum): TEMPORAL_ADDRESS = "TEMPORAL_ADDRESS" REDIS_URL = "REDIS_URL" AGENTEX_BASE_URL = "AGENTEX_BASE_URL" - # AgentEx client HTTP timeouts (seconds) - AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS = "AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS" - AGENTEX_CLIENT_READ_TIMEOUT_SECONDS = "AGENTEX_CLIENT_READ_TIMEOUT_SECONDS" - AGENTEX_CLIENT_WRITE_TIMEOUT_SECONDS = "AGENTEX_CLIENT_WRITE_TIMEOUT_SECONDS" - AGENTEX_CLIENT_POOL_TIMEOUT_SECONDS = "AGENTEX_CLIENT_POOL_TIMEOUT_SECONDS" # Agent Identifiers AGENT_NAME = "AGENT_NAME" AGENT_DESCRIPTION = "AGENT_DESCRIPTION" AGENT_ID = "AGENT_ID" AGENT_VERSION = "AGENT_VERSION" + AGENT_COMMIT_SHA = "AGENT_COMMIT_SHA" AGENT_API_KEY = "AGENT_API_KEY" # ACP Configuration ACP_URL = "ACP_URL" @@ -66,20 +62,18 @@ class EnvironmentVariables(BaseModel): TEMPORAL_ADDRESS: str | None = "localhost:7233" REDIS_URL: str | None = None AGENTEX_BASE_URL: str | None = "http://localhost:5003" - # HTTP timeouts for the AgentEx client, in seconds. Defaults match the - # SDK's DEFAULT_TIMEOUT, so leaving these unset changes nothing. - # Raise the connect timeout when many concurrent activities share one - # backend: accepts queue, and 5s is reached at a few hundred in flight. - AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS: float = 5.0 - AGENTEX_CLIENT_READ_TIMEOUT_SECONDS: float = 300.0 - AGENTEX_CLIENT_WRITE_TIMEOUT_SECONDS: float = 300.0 - AGENTEX_CLIENT_POOL_TIMEOUT_SECONDS: float = 300.0 # Agent Identifiers AGENT_NAME: str AGENT_DESCRIPTION: str | None = None AGENT_ID: str | None = None # Build/version discriminator (image tag or git sha), set by the deployment AGENT_VERSION: str | None = None + # The agent's source commit, baked into the image or set by the deployment. + # Unlike AGENT_VERSION this is expected to be a git SHA and nothing else, and + # it is OPT-IN: nothing is stamped unless the agent calls + # `adk.code_revision.enable()`, which also refuses a value that is not a git + # object name. See agentex.lib.core.tracing.code_revision. + AGENT_COMMIT_SHA: str | None = None AGENT_API_KEY: str | None = None ACP_TYPE: str | None = "async" AGENT_INPUT_TYPE: str | None = None diff --git a/tests/lib/test_client_timeout_env.py b/tests/lib/test_client_timeout_env.py index a96460e54..20e5be146 100644 --- a/tests/lib/test_client_timeout_env.py +++ b/tests/lib/test_client_timeout_env.py @@ -13,32 +13,14 @@ import httpx import pytest -import agentex.lib.environment_variables as env_module from agentex.lib.adk.utils._modules.client import ( _timeout_from_env, create_async_agentex_client, ) -@pytest.fixture(autouse=True) -def _clear_env_cache(): - """EnvironmentVariables.refresh() memoises into a module global.""" - env_module.refreshed_environment_variables = None - yield - env_module.refreshed_environment_variables = None - - -def _set_env(monkeypatch, **overrides: str) -> None: - # EnvironmentVariables has required fields; set them so construction succeeds. - monkeypatch.setenv("AGENT_NAME", "test-agent") - monkeypatch.setenv("ACP_URL", "http://localhost:8000") - for key, value in overrides.items(): - monkeypatch.setenv(key, value) - - -def test_defaults_match_the_sdk_default_timeout(monkeypatch): +def test_defaults_match_the_sdk_default_timeout(): """An unconfigured process must behave exactly as it did before.""" - _set_env(monkeypatch) timeout = _timeout_from_env() assert timeout.connect == 5.0 assert timeout.read == 300.0 @@ -47,7 +29,7 @@ def test_defaults_match_the_sdk_default_timeout(monkeypatch): def test_connect_timeout_is_configurable(monkeypatch): - _set_env(monkeypatch, AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS="30") + monkeypatch.setenv("AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS", "30") timeout = _timeout_from_env() assert timeout.connect == 30.0 # the others are untouched @@ -55,13 +37,10 @@ def test_connect_timeout_is_configurable(monkeypatch): def test_all_four_are_configurable(monkeypatch): - _set_env( - monkeypatch, - AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS="30", - AGENTEX_CLIENT_READ_TIMEOUT_SECONDS="120", - AGENTEX_CLIENT_WRITE_TIMEOUT_SECONDS="90", - AGENTEX_CLIENT_POOL_TIMEOUT_SECONDS="60", - ) + monkeypatch.setenv("AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS", "30") + monkeypatch.setenv("AGENTEX_CLIENT_READ_TIMEOUT_SECONDS", "120") + monkeypatch.setenv("AGENTEX_CLIENT_WRITE_TIMEOUT_SECONDS", "90") + monkeypatch.setenv("AGENTEX_CLIENT_POOL_TIMEOUT_SECONDS", "60") timeout = _timeout_from_env() assert (timeout.connect, timeout.read, timeout.write, timeout.pool) == ( 30.0, @@ -71,14 +50,21 @@ def test_all_four_are_configurable(monkeypatch): ) +def test_an_empty_value_falls_back_to_the_default(): + """An unset variable and one set to the empty string mean the same thing.""" + with pytest.MonkeyPatch.context() as mp: + mp.setenv("AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS", "") + assert _timeout_from_env().connect == 5.0 + + def test_client_picks_up_the_env_timeout(monkeypatch): - _set_env(monkeypatch, AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS="30") + monkeypatch.setenv("AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS", "30") client = create_async_agentex_client(api_key="test", base_url="http://localhost:5003") assert client.timeout.connect == 30.0 def test_explicit_timeout_wins_over_the_environment(monkeypatch): - _set_env(monkeypatch, AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS="30") + monkeypatch.setenv("AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS", "30") client = create_async_agentex_client( api_key="test", base_url="http://localhost:5003", @@ -87,15 +73,27 @@ def test_explicit_timeout_wins_over_the_environment(monkeypatch): assert client.timeout.connect == 7.0 -def test_env_auth_is_still_attached(monkeypatch): +def test_env_auth_is_still_attached(): """The factory's original job must survive the change.""" - _set_env(monkeypatch) client = create_async_agentex_client(api_key="test", base_url="http://localhost:5003") assert client._client.auth is not None -def test_a_bad_value_does_not_prevent_client_creation(monkeypatch): - """Timeout configuration must never be the reason a client fails to build.""" - _set_env(monkeypatch, AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS="not-a-number") - client = create_async_agentex_client(api_key="test", base_url="http://localhost:5003") - assert client is not None +def test_a_bad_value_names_the_variable(monkeypatch): + """A malformed value is a configuration error, so it must not be swallowed.""" + monkeypatch.setenv("AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS", "not-a-number") + with pytest.raises(ValueError, match="AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS"): + _timeout_from_env() + + +def test_the_timeout_does_not_depend_on_the_shared_environment_model(monkeypatch): + """Regression: these must not become EnvironmentVariables fields. + + That model has required fields, is loaded by worker startup and by + EnvAuth.auth_flow on every request, and agentex.lib.adk.utils builds a + client at import time. Routing timeouts through it makes all three depend + on a fully configured environment. + """ + monkeypatch.delenv("AGENT_NAME", raising=False) + monkeypatch.delenv("ACP_URL", raising=False) + assert _timeout_from_env().connect == 5.0 From cfd98bba04467646d7721a80495f6c04d08b6711 Mon Sep 17 00:00:00 2001 From: Rishav C Date: Sun, 6 Sep 2026 21:11:55 -0400 Subject: [PATCH 4/5] Narrow client.timeout before reading a component in tests AsyncAgentex.timeout is typed float | Timeout | None, so pyright rejected reading .connect off it directly. ruff was clean, which is why CI caught this and the local ruff run did not. --- tests/lib/test_client_timeout_env.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/lib/test_client_timeout_env.py b/tests/lib/test_client_timeout_env.py index 20e5be146..c0d2140a1 100644 --- a/tests/lib/test_client_timeout_env.py +++ b/tests/lib/test_client_timeout_env.py @@ -60,6 +60,8 @@ def test_an_empty_value_falls_back_to_the_default(): def test_client_picks_up_the_env_timeout(monkeypatch): monkeypatch.setenv("AGENTEX_CLIENT_CONNECT_TIMEOUT_SECONDS", "30") client = create_async_agentex_client(api_key="test", base_url="http://localhost:5003") + # client.timeout is float | Timeout | None; narrow before reading a component. + assert isinstance(client.timeout, httpx.Timeout) assert client.timeout.connect == 30.0 @@ -70,6 +72,7 @@ def test_explicit_timeout_wins_over_the_environment(monkeypatch): base_url="http://localhost:5003", timeout=httpx.Timeout(connect=7.0, read=8.0, write=9.0, pool=10.0), ) + assert isinstance(client.timeout, httpx.Timeout) assert client.timeout.connect == 7.0 From cb82208edacc5ac6e38198ef22a75ac2bc5cf908 Mon Sep 17 00:00:00 2001 From: Rishav C Date: Sun, 6 Sep 2026 21:12:37 -0400 Subject: [PATCH 5/5] Drop an unrelated file change from the branch Reverting environment_variables.py in the previous commit checked it out from origin/next rather than the merge base, which pulled AGENT_COMMIT_SHA from another commit into this diff. This PR now touches only client.py and its test. --- src/agentex/lib/environment_variables.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/agentex/lib/environment_variables.py b/src/agentex/lib/environment_variables.py index 00dbbaada..7d893e462 100644 --- a/src/agentex/lib/environment_variables.py +++ b/src/agentex/lib/environment_variables.py @@ -25,7 +25,6 @@ class EnvVarKeys(str, Enum): AGENT_DESCRIPTION = "AGENT_DESCRIPTION" AGENT_ID = "AGENT_ID" AGENT_VERSION = "AGENT_VERSION" - AGENT_COMMIT_SHA = "AGENT_COMMIT_SHA" AGENT_API_KEY = "AGENT_API_KEY" # ACP Configuration ACP_URL = "ACP_URL" @@ -68,12 +67,6 @@ class EnvironmentVariables(BaseModel): AGENT_ID: str | None = None # Build/version discriminator (image tag or git sha), set by the deployment AGENT_VERSION: str | None = None - # The agent's source commit, baked into the image or set by the deployment. - # Unlike AGENT_VERSION this is expected to be a git SHA and nothing else, and - # it is OPT-IN: nothing is stamped unless the agent calls - # `adk.code_revision.enable()`, which also refuses a value that is not a git - # object name. See agentex.lib.core.tracing.code_revision. - AGENT_COMMIT_SHA: str | None = None AGENT_API_KEY: str | None = None ACP_TYPE: str | None = "async" AGENT_INPUT_TYPE: str | None = None