fix(config): add stale timeout settings

This commit is contained in:
helix4u
2026-04-19 13:40:09 -06:00
committed by Teknium
parent 440764e013
commit 03e3c22e86
6 changed files with 267 additions and 31 deletions

View File

@@ -48,7 +48,10 @@ from hermes_constants import get_hermes_home
# Load .env from ~/.hermes/.env first, then project root as dev fallback.
# User-managed env files should override stale shell exports on restart.
from hermes_cli.env_loader import load_hermes_dotenv
from hermes_cli.timeouts import get_provider_request_timeout
from hermes_cli.timeouts import (
get_provider_request_timeout,
get_provider_stale_timeout,
)
_hermes_home = get_hermes_home()
_project_env = Path(__file__).parent / '.env'
@@ -2158,6 +2161,44 @@ class AIAgent:
return cfg
return float(os.getenv("HERMES_API_TIMEOUT", 1800.0))
def _resolved_api_call_stale_timeout_base(self) -> tuple[float, bool]:
"""Resolve the base non-stream stale timeout and whether it is implicit.
Priority:
1. ``providers.<id>.models.<model>.stale_timeout_seconds``
2. ``providers.<id>.stale_timeout_seconds``
3. ``HERMES_API_CALL_STALE_TIMEOUT`` env var
4. 300.0s default
Returns ``(timeout_seconds, uses_implicit_default)`` so the caller can
preserve legacy behaviors that only apply when the user has *not*
explicitly configured a stale timeout, such as auto-disabling the
detector for local endpoints.
"""
cfg = get_provider_stale_timeout(self.provider, self.model)
if cfg is not None:
return cfg, False
env_timeout = os.getenv("HERMES_API_CALL_STALE_TIMEOUT")
if env_timeout is not None:
return float(env_timeout), False
return 300.0, True
def _compute_non_stream_stale_timeout(self, messages: list[dict[str, Any]]) -> float:
"""Compute the effective non-stream stale timeout for this request."""
stale_base, uses_implicit_default = self._resolved_api_call_stale_timeout_base()
base_url = getattr(self, "_base_url", None) or self.base_url or ""
if uses_implicit_default and base_url and is_local_endpoint(base_url):
return float("inf")
est_tokens = sum(len(str(v)) for v in messages) // 4
if est_tokens > 100_000:
return max(stale_base, 600.0)
if est_tokens > 50_000:
return max(stale_base, 450.0)
return stale_base
def _is_openrouter_url(self) -> bool:
"""Return True when the base URL targets OpenRouter."""
return "openrouter" in self._base_url_lower
@@ -5594,18 +5635,9 @@ class AIAgent:
# httpx timeout (default 1800s) with zero feedback. The stale
# detector kills the connection early so the main retry loop can
# apply richer recovery (credential rotation, provider fallback).
_stale_base = float(os.getenv("HERMES_API_CALL_STALE_TIMEOUT", 300.0))
_base_url = getattr(self, "_base_url", None) or ""
if _stale_base == 300.0 and _base_url and is_local_endpoint(_base_url):
_stale_timeout = float("inf")
else:
_est_tokens = sum(len(str(v)) for v in api_kwargs.get("messages", [])) // 4
if _est_tokens > 100_000:
_stale_timeout = max(_stale_base, 600.0)
elif _est_tokens > 50_000:
_stale_timeout = max(_stale_base, 450.0)
else:
_stale_timeout = _stale_base
_stale_timeout = self._compute_non_stream_stale_timeout(
api_kwargs.get("messages", [])
)
_call_start = time.time()
self._touch_activity("waiting for non-streaming API response")