mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix: add activity heartbeats to prevent false gateway inactivity timeouts (#10501)
Multiple gaps in activity tracking could cause the gateway's inactivity timeout to fire while the agent is actively working: 1. Streaming wait loop had no periodic heartbeat — the outer thread only touched activity when the stale-stream detector fired (180-300s), and for local providers (Ollama) the stale timeout was infinity, meaning zero heartbeats. Now touches activity every 30s. 2. Concurrent tool execution never set the activity callback on worker threads (threading.local invisible across threads) and never set _current_tool. Workers now set the callback, and the concurrent wait uses a polling loop with 30s heartbeats. 3. Modal backend's execute() override had its own polling loop without any activity callback. Now matches _wait_for_process cadence (10s).
This commit is contained in:
parent
0d25e1c146
commit
a418ddbd8b
3 changed files with 73 additions and 3 deletions
|
|
@ -105,6 +105,10 @@ class BaseModalExecutionEnvironment(BaseEnvironment):
|
|||
if self._client_timeout_grace_seconds is not None:
|
||||
deadline = time.monotonic() + prepared.timeout + self._client_timeout_grace_seconds
|
||||
|
||||
_last_activity_touch = time.monotonic()
|
||||
_modal_exec_start = time.monotonic()
|
||||
_ACTIVITY_INTERVAL = 10.0 # match _wait_for_process cadence
|
||||
|
||||
while True:
|
||||
if is_interrupted():
|
||||
try:
|
||||
|
|
@ -128,6 +132,22 @@ class BaseModalExecutionEnvironment(BaseEnvironment):
|
|||
pass
|
||||
return self._timeout_result_for_modal(prepared.timeout)
|
||||
|
||||
# Periodic activity touch so the gateway knows we're alive
|
||||
_now = time.monotonic()
|
||||
if _now - _last_activity_touch >= _ACTIVITY_INTERVAL:
|
||||
_last_activity_touch = _now
|
||||
try:
|
||||
from tools.environments.base import _get_activity_callback
|
||||
_cb = _get_activity_callback()
|
||||
except Exception:
|
||||
_cb = None
|
||||
if _cb:
|
||||
try:
|
||||
_elapsed = int(_now - _modal_exec_start)
|
||||
_cb(f"modal command running ({_elapsed}s elapsed)")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
time.sleep(self._poll_interval_seconds)
|
||||
|
||||
def _before_execute(self) -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue