fix(auxiliary_client): warn once + regression tests for bootstrap version skew

Follow-up on the salvaged fallback: silent degradation is how #64333 went
unnoticed (jobs dead on arrival, only errors.log knew). Warn once with a
resync hint, and cover both the skewed and healthy paths with tests.
This commit is contained in:
Teknium 2026-07-14 14:22:38 -07:00
parent f3ec79964e
commit 07be37d996
2 changed files with 69 additions and 8 deletions

View file

@ -155,6 +155,9 @@ def _resolve_aux_verify(base_url: Optional[str]) -> Any:
return True
_WARNED_KEEPALIVE_IMPORT_SKEW = False
def _openai_http_client_kwargs(
base_url: Optional[str],
*,
@ -169,15 +172,26 @@ def _openai_http_client_kwargs(
verify=_resolve_aux_verify(base_url),
)
except (ImportError, AttributeError):
# Fallback for older hermes-agent versions without build_keepalive_http_client.
# This can happen when Desktop users run with stale code (#64333).
# Without the keepalive client, auxiliary calls use the OpenAI SDK's default
# httpx client, which respects macOS system proxy (less predictable for env-only
# proxy use) and has no pool-level keepalive expiry (connections may live longer).
# This is a graceful degradation — functionality still works, just with slightly
# different proxy/keepalive behavior.
# Version-skewed installs (#64333): a process whose sys.path resolves
# an older agent/process_bootstrap.py without this helper — seen when
# the Desktop app's bundled runtime lags a git-installed source tree
# that newer callers (cron scheduler) were written against. Every cron
# job died on this ImportError before any agent logic ran. Degrade
# gracefully to the OpenAI SDK's default httpx client (respects macOS
# system proxy, no pool-level keepalive expiry) instead of failing the
# whole job, and say so once — silent version skew is how this bug
# went unnoticed until jobs were already dead on arrival.
global _WARNED_KEEPALIVE_IMPORT_SKEW
if not _WARNED_KEEPALIVE_IMPORT_SKEW:
_WARNED_KEEPALIVE_IMPORT_SKEW = True
logger.warning(
"agent.process_bootstrap.build_keepalive_http_client is "
"unavailable — mixed/stale install detected (#64333). Falling "
"back to the SDK default HTTP client. Run `hermes update` (or "
"reinstall the Desktop app) to resync the runtime."
)
client = None
if client is None:
return {}
return {"http_client": client}

View file

@ -0,0 +1,47 @@
"""Regression for #64333 — auxiliary client must survive a version-skewed
agent.process_bootstrap that lacks build_keepalive_http_client.
Desktop installs can run a runtime whose agent/process_bootstrap.py predates
the helper while newer callers (cron scheduler auxiliary_client) expect it.
Before the fix the module-level import made every cron job die with
ImportError before any agent logic ran.
"""
import builtins
import logging
import agent.auxiliary_client as aux
def test_missing_bootstrap_helper_degrades_instead_of_raising(monkeypatch, caplog):
"""ImportError from process_bootstrap → empty kwargs + one-time warning."""
real_import = builtins.__import__
def _fail_bootstrap(name, *args, **kwargs):
if name == "agent.process_bootstrap":
raise ImportError(
"cannot import name 'build_keepalive_http_client' "
"from 'agent.process_bootstrap'"
)
return real_import(name, *args, **kwargs)
monkeypatch.setattr(builtins, "__import__", _fail_bootstrap)
monkeypatch.setattr(aux, "_WARNED_KEEPALIVE_IMPORT_SKEW", False)
with caplog.at_level(logging.WARNING, logger="agent.auxiliary_client"):
result = aux._openai_http_client_kwargs("https://api.example.com/v1")
again = aux._openai_http_client_kwargs("https://api.example.com/v1")
assert result == {}
assert again == {}
skew_warnings = [
r for r in caplog.records if "mixed/stale install" in r.getMessage()
]
assert len(skew_warnings) == 1 # warned once, not per call
def test_healthy_bootstrap_still_injects_keepalive_client():
"""With the helper present, the keepalive http_client is injected."""
result = aux._openai_http_client_kwargs("https://api.example.com/v1")
assert "http_client" in result
assert result["http_client"] is not None