mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
fix(webapp): count a profile that is still starting as busy for idle exit
An auto-started gateway whose built ledgers were empty could idle-exit while hot-serve was still building a profile: the registry walk skips a reserved slot, so busy_reason saw nothing and the stop cancelled the start before that profile's queued admissions were recovered. busy_reason now reports a pending start (SessionAuthorities.starting_homes); the hold ends when the start is filled or fails.
This commit is contained in:
1 parent
422f2f8456
commit
2127cddbbd
3 files changed
+39
-2
No files matched your search
@@ -138,6 +138,15 @@ def _ledger_busy(runner) -> str | None:
|
||||
return None
|
||||
|
||||
|
||||
def _profile_start_busy(runner) -> str | None:
|
||||
"""A profile hot-serve is still building has no authority to walk yet, but its queued admissions
|
||||
are recovered once it starts; exiting now would cancel that start. A filled or failed start
|
||||
leaves the registry's starting set, so the hold ends with it."""
|
||||
registry = getattr(runner, "session_authorities", None)
|
||||
starting = registry.starting_homes() if registry is not None else ()
|
||||
return f"profile starting in {starting[0]}" if starting else None
|
||||
|
||||
|
||||
def _store_gates_busy(homes) -> str | None:
|
||||
from gateway.run_idle_gates import (
|
||||
profile_has_active_heartbeat, profile_has_active_loop, profile_has_pending_handoff)
|
||||
@@ -186,8 +195,8 @@ def busy_reason(runner, *, window: float, count_clients: bool = True) -> str | N
|
||||
if runner._scale_to_zero_has_live_background_work():
|
||||
return "background work in flight"
|
||||
homes = _served_homes(runner)
|
||||
return (_messaging_busy(runner) or _ledger_busy(runner) or _cron_busy(homes) or _kanban_busy()
|
||||
or _store_gates_busy(homes))
|
||||
return (_messaging_busy(runner) or _ledger_busy(runner) or _profile_start_busy(runner)
|
||||
or _cron_busy(homes) or _kanban_busy() or _store_gates_busy(homes))
|
||||
except Exception as exc:
|
||||
logger.debug("idle-exit probe failed; staying up", exc_info=True)
|
||||
return f"idle probe failed ({type(exc).__name__})"
|
||||
|
||||
@@ -75,6 +75,10 @@ class SessionAuthorities:
|
||||
return next(((key, started) for key, started in list(self._starting.items())
|
||||
if profile_matches_home(profile, Path(key))), None)
|
||||
|
||||
def starting_homes(self) -> list[str]:
|
||||
"""Keys of every slot whose authority is still being built (a snapshot, as in ``serving``)."""
|
||||
return list(self._starting)
|
||||
|
||||
def profile_name(self, authority):
|
||||
"""Served profile name for *authority*; None for the launch profile (its adapters are
|
||||
``runner.adapters``, its session keys the historical ``agent:main`` namespace)."""
|
||||
|
||||
@@ -108,6 +108,30 @@ def test_a_profile_still_starting_leaves_the_served_ledgers_readable(tmp_path, m
|
||||
db.close()
|
||||
|
||||
|
||||
def test_a_profile_still_starting_keeps_an_otherwise_idle_gateway_up(tmp_path, monkeypatch):
|
||||
"""With every built ledger empty, a profile hot-serve is still building holds the gateway: its
|
||||
queued admissions are recovered only once it starts. The hold ends with the start, filled or failed."""
|
||||
runner, _home, db, _stops = _runner(tmp_path, monkeypatch)
|
||||
registry = runner.session_authorities
|
||||
starting = tmp_path / "starting"
|
||||
starting.mkdir()
|
||||
try:
|
||||
assert run_idle_exit.busy_reason(runner, window=0.2) is None
|
||||
registry.add(starting, None, name="starting") # as serve_profile_runtime does
|
||||
assert "profile starting" in run_idle_exit.busy_reason(runner, window=0.2)
|
||||
registry.remove(starting) # the build failed
|
||||
assert run_idle_exit.busy_reason(runner, window=0.2) is None
|
||||
registry.add(starting, None, name="starting")
|
||||
starting_db = SessionDB(db_path=starting / "state.db")
|
||||
registry.replace(starting, SimpleNamespace(profile_id=str(starting), db=starting_db))
|
||||
try:
|
||||
assert run_idle_exit.busy_reason(runner, window=0.2) is None
|
||||
finally:
|
||||
starting_db.close()
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def test_secondary_local_session_adapter_does_not_count_as_messaging(tmp_path, monkeypatch):
|
||||
"""A secondary local session leaves an adapter map behind after the client disconnects."""
|
||||
runner, _home, db, _stops = _runner(tmp_path, monkeypatch)
|
||||
|
||||
Reference in new issue
Block a user