mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-22 16:25:58 +00:00
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:
parent
f3ec79964e
commit
07be37d996
2 changed files with 69 additions and 8 deletions
|
|
@ -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}
|
||||
|
|
|
|||
47
tests/agent/test_auxiliary_client_bootstrap_skew.py
Normal file
47
tests/agent/test_auxiliary_client_bootstrap_skew.py
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue