mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-21 16:18:55 +00:00
fix(plugins): rebuild Honcho client when timeout config changes
The Honcho client singleton cached the HTTP timeout at first build. In long-lived processes (gateway, dashboard), changing the timeout via config.yaml or HONCHO_TIMEOUT had no effect until restart. Track the resolved timeout alongside the cached client and compare on each get_honcho_client() call. When the timeout differs, reset the singleton so the next call rebuilds with the new value. Fixes #57347
This commit is contained in:
parent
73a4574ede
commit
e5bebe2cad
2 changed files with 76 additions and 2 deletions
|
|
@ -855,6 +855,28 @@ class HonchoClientConfig:
|
|||
|
||||
|
||||
_honcho_client_slot: SingletonSlot = SingletonSlot()
|
||||
_cached_timeout: float | None = None
|
||||
|
||||
|
||||
def _resolve_timeout_from_sources(config: HonchoClientConfig | None) -> float:
|
||||
"""Resolve the effective timeout from env, config.yaml, and the explicit config."""
|
||||
timeout = config.timeout if config is not None else None
|
||||
if timeout is None:
|
||||
timeout = _resolve_optional_float(os.environ.get("HONCHO_TIMEOUT"))
|
||||
if timeout is None:
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
|
||||
hermes_cfg = load_config()
|
||||
honcho_cfg = hermes_cfg.get("honcho", {})
|
||||
if isinstance(honcho_cfg, dict):
|
||||
timeout = _resolve_optional_float(
|
||||
honcho_cfg.get("timeout"),
|
||||
honcho_cfg.get("request_timeout"),
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
return timeout if timeout is not None else _DEFAULT_HTTP_TIMEOUT
|
||||
|
||||
|
||||
def _apply_fresh_oauth_token(config: HonchoClientConfig) -> None:
|
||||
|
|
@ -900,10 +922,20 @@ def get_honcho_client(config: HonchoClientConfig | None = None) -> Honcho:
|
|||
first calls (double-checked locking via ``SingletonSlot``), so racing
|
||||
threads can't each construct a client and leak the loser's connection.
|
||||
"""
|
||||
global _cached_timeout
|
||||
cached = _honcho_client_slot.peek()
|
||||
if cached is not None:
|
||||
_refresh_cached_oauth(cached, config)
|
||||
return cached
|
||||
# Detect timeout config changes in long-lived processes (gateway,
|
||||
# dashboard). If the user changed the timeout after the client was
|
||||
# built, rebuild with the new value.
|
||||
new_timeout = _resolve_timeout_from_sources(config)
|
||||
if new_timeout != _cached_timeout:
|
||||
_honcho_client_slot.reset()
|
||||
_cached_timeout = None
|
||||
cached = None
|
||||
else:
|
||||
_refresh_cached_oauth(cached, config)
|
||||
return cached
|
||||
|
||||
if config is None:
|
||||
config = HonchoClientConfig.from_global_config()
|
||||
|
|
@ -1014,6 +1046,8 @@ def get_honcho_client(config: HonchoClientConfig | None = None) -> Honcho:
|
|||
if resolved_timeout is not None:
|
||||
kwargs["timeout"] = resolved_timeout
|
||||
|
||||
global _cached_timeout
|
||||
_cached_timeout = resolved_timeout
|
||||
return Honcho(**kwargs)
|
||||
|
||||
return _honcho_client_slot.get(_build)
|
||||
|
|
@ -1021,4 +1055,6 @@ def get_honcho_client(config: HonchoClientConfig | None = None) -> Honcho:
|
|||
|
||||
def reset_honcho_client() -> None:
|
||||
"""Reset the Honcho client singleton (useful for testing)."""
|
||||
global _cached_timeout
|
||||
_honcho_client_slot.reset()
|
||||
_cached_timeout = None
|
||||
|
|
|
|||
|
|
@ -735,6 +735,44 @@ class TestGetHonchoClient:
|
|||
mock_honcho.assert_called_once()
|
||||
assert mock_honcho.call_args.kwargs["timeout"] == 77.5
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not importlib.util.find_spec("honcho"),
|
||||
reason="honcho SDK not installed"
|
||||
)
|
||||
def test_timeout_change_triggers_client_rebuild(self):
|
||||
"""Changing timeout config must rebuild the cached client."""
|
||||
fake_honcho_1 = MagicMock(name="Honcho_v1")
|
||||
fake_honcho_2 = MagicMock(name="Honcho_v2")
|
||||
cfg = HonchoClientConfig(
|
||||
api_key="test-key",
|
||||
workspace_id="hermes",
|
||||
environment="production",
|
||||
)
|
||||
|
||||
with patch("honcho.Honcho", return_value=fake_honcho_1) as mock_h1, \
|
||||
patch("hermes_cli.config.load_config", return_value={"honcho": {"timeout": 30}}):
|
||||
client1 = get_honcho_client(cfg)
|
||||
|
||||
assert client1 is fake_honcho_1
|
||||
assert mock_h1.call_args.kwargs["timeout"] == 30.0
|
||||
|
||||
# Same config — should return cached client (no rebuild)
|
||||
with patch("honcho.Honcho", return_value=fake_honcho_2) as mock_h2, \
|
||||
patch("hermes_cli.config.load_config", return_value={"honcho": {"timeout": 30}}):
|
||||
client2 = get_honcho_client(cfg)
|
||||
|
||||
assert client2 is fake_honcho_1 # still cached
|
||||
mock_h2.assert_not_called()
|
||||
|
||||
# Changed timeout — must rebuild
|
||||
with patch("honcho.Honcho", return_value=fake_honcho_2) as mock_h3, \
|
||||
patch("hermes_cli.config.load_config", return_value={"honcho": {"timeout": 300}}):
|
||||
client3 = get_honcho_client(cfg)
|
||||
|
||||
assert client3 is fake_honcho_2 # rebuilt
|
||||
mock_h3.assert_called_once()
|
||||
assert mock_h3.call_args.kwargs["timeout"] == 300.0
|
||||
|
||||
|
||||
class TestResolveSessionNameGatewayKey:
|
||||
"""Regression tests for gateway_session_key priority in resolve_session_name.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue