mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-08 13:12:08 +00:00
feat(pty): periodic reaper wired into dashboard lifespan
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
e10e4bca82
commit
c3d2be073a
3 changed files with 36 additions and 1 deletions
|
|
@ -126,6 +126,16 @@ class RegistryFull(Exception):
|
|||
pass
|
||||
|
||||
|
||||
async def run_reaper(registry: "PtySessionRegistry", *, interval: float = 60.0) -> None:
|
||||
"""Periodically reap idle/dead keep-alive sessions. Cancelled on shutdown."""
|
||||
while True:
|
||||
await asyncio.sleep(interval)
|
||||
try:
|
||||
await registry.reap_idle()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
class PtySessionRegistry:
|
||||
def __init__(self, *, ttl: float, max_sessions: int,
|
||||
buffer_cap: int, read_timeout: float) -> None:
|
||||
|
|
|
|||
|
|
@ -204,9 +204,14 @@ async def _lifespan(app: "FastAPI"):
|
|||
)
|
||||
cron_thread.start()
|
||||
|
||||
# Reap idle/dead keep-alive PTY sessions in the background (30-min TTL).
|
||||
pty_reaper_task = asyncio.create_task(run_reaper(PTY_REGISTRY))
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
pty_reaper_task.cancel()
|
||||
await PTY_REGISTRY.close_all()
|
||||
if cron_stop is not None:
|
||||
cron_stop.set()
|
||||
|
||||
|
|
@ -12543,7 +12548,7 @@ _PTY_READ_CHUNK_TIMEOUT = 0.2
|
|||
|
||||
# Keep-alive PTY sessions: a terminal connecting with ``?attach=<token>`` is
|
||||
# bound to a process that survives disconnect/refresh and is reattachable.
|
||||
from hermes_cli.pty_session import PtySessionRegistry, RegistryFull # noqa: E402
|
||||
from hermes_cli.pty_session import PtySessionRegistry, RegistryFull, run_reaper # noqa: E402
|
||||
|
||||
PTY_REGISTRY = PtySessionRegistry(
|
||||
ttl=30 * 60,
|
||||
|
|
|
|||
|
|
@ -160,3 +160,23 @@ async def test_new_key_at_capacity_raises_when_none_reapable():
|
|||
with pytest.raises(RegistryFull):
|
||||
await reg.attach_or_spawn("b", spawn=lambda: FakeBridge([]))
|
||||
await reg.close_all()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reaper_loop_invokes_reap(monkeypatch):
|
||||
from hermes_cli.pty_session import run_reaper
|
||||
reg = make_registry()
|
||||
calls = {"n": 0}
|
||||
|
||||
async def fake_reap(now=None):
|
||||
calls["n"] += 1
|
||||
|
||||
monkeypatch.setattr(reg, "reap_idle", fake_reap)
|
||||
task = asyncio.create_task(run_reaper(reg, interval=0.01))
|
||||
await asyncio.sleep(0.05)
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
assert calls["n"] >= 2
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue