refactor(agent): require runtime for consolidation
This commit is contained in:
@@ -9,6 +9,10 @@ from nanobot.agent.autocompact import AutoCompact
|
||||
from nanobot.session.manager import Session, SessionManager
|
||||
|
||||
|
||||
def _runtime():
|
||||
return MagicMock(name="runtime")
|
||||
|
||||
|
||||
def _make_session(
|
||||
key: str = "cli:test",
|
||||
messages: list | None = None,
|
||||
@@ -193,7 +197,7 @@ class TestCheckExpired:
|
||||
mock_sm.list_sessions.return_value = []
|
||||
ac.sessions = mock_sm
|
||||
scheduler = MagicMock()
|
||||
ac.check_expired(scheduler)
|
||||
ac.check_expired(scheduler, _runtime)
|
||||
scheduler.assert_not_called()
|
||||
|
||||
def test_expired_session_schedules_background(self):
|
||||
@@ -213,10 +217,36 @@ class TestCheckExpired:
|
||||
scheduled.append(coro)
|
||||
coro.close()
|
||||
|
||||
ac.check_expired(scheduler)
|
||||
ac.check_expired(scheduler, _runtime)
|
||||
assert len(scheduled) == 1
|
||||
assert "cli:old" in ac._archiving
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_runtime_is_captured_before_background_starts(self):
|
||||
ac = _make_autocompact(ttl=15)
|
||||
old_dt = datetime.now() - timedelta(minutes=20)
|
||||
session = _make_session("cli:old", updated_at=old_dt)
|
||||
_add_turns(session, 5)
|
||||
ac.sessions.list_sessions.return_value = [
|
||||
{"key": "cli:old", "updated_at": old_dt.isoformat()}
|
||||
]
|
||||
ac.sessions.get_or_create.return_value = session
|
||||
admitted = _runtime()
|
||||
replacement = _runtime()
|
||||
resolve_runtime = MagicMock(return_value=admitted)
|
||||
scheduled = []
|
||||
|
||||
ac.check_expired(scheduled.append, resolve_runtime)
|
||||
resolve_runtime.return_value = replacement
|
||||
await scheduled[0]
|
||||
|
||||
resolve_runtime.assert_called_once_with()
|
||||
ac.consolidator.compact_idle_session.assert_awaited_once_with(
|
||||
"cli:old",
|
||||
runtime=admitted,
|
||||
max_suffix=ac._RECENT_SUFFIX_MESSAGES,
|
||||
)
|
||||
|
||||
def test_active_session_key_skips(self):
|
||||
"""Session in active_session_keys should be skipped."""
|
||||
ac = _make_autocompact(ttl=15)
|
||||
@@ -225,7 +255,7 @@ class TestCheckExpired:
|
||||
mock_sm.list_sessions.return_value = [{"key": "cli:busy", "updated_at": old_ts}]
|
||||
ac.sessions = mock_sm
|
||||
scheduler = MagicMock()
|
||||
ac.check_expired(scheduler, active_session_keys={"cli:busy"})
|
||||
ac.check_expired(scheduler, _runtime, active_session_keys={"cli:busy"})
|
||||
scheduler.assert_not_called()
|
||||
|
||||
def test_session_already_in_archiving_skips(self):
|
||||
@@ -237,7 +267,7 @@ class TestCheckExpired:
|
||||
ac.sessions = mock_sm
|
||||
ac._archiving.add("cli:dup")
|
||||
scheduler = MagicMock()
|
||||
ac.check_expired(scheduler)
|
||||
ac.check_expired(scheduler, _runtime)
|
||||
scheduler.assert_not_called()
|
||||
|
||||
def test_session_with_no_key_skips(self):
|
||||
@@ -247,7 +277,7 @@ class TestCheckExpired:
|
||||
mock_sm.list_sessions.return_value = [{"key": "", "updated_at": "old"}]
|
||||
ac.sessions = mock_sm
|
||||
scheduler = MagicMock()
|
||||
ac.check_expired(scheduler)
|
||||
ac.check_expired(scheduler, _runtime)
|
||||
scheduler.assert_not_called()
|
||||
|
||||
def test_session_with_missing_key_field_skips(self):
|
||||
@@ -257,7 +287,7 @@ class TestCheckExpired:
|
||||
mock_sm.list_sessions.return_value = [{"updated_at": "old"}]
|
||||
ac.sessions = mock_sm
|
||||
scheduler = MagicMock()
|
||||
ac.check_expired(scheduler)
|
||||
ac.check_expired(scheduler, _runtime)
|
||||
scheduler.assert_not_called()
|
||||
|
||||
def test_dream_session_skips(self):
|
||||
@@ -271,7 +301,7 @@ class TestCheckExpired:
|
||||
ac.sessions = mock_sm
|
||||
scheduler = MagicMock()
|
||||
|
||||
ac.check_expired(scheduler)
|
||||
ac.check_expired(scheduler, _runtime)
|
||||
|
||||
scheduler.assert_not_called()
|
||||
assert "dream:20260602-155256" not in ac._archiving
|
||||
@@ -290,7 +320,7 @@ class TestCheckExpired:
|
||||
ac.sessions = mock_sm
|
||||
|
||||
scheduler = MagicMock()
|
||||
ac.check_expired(scheduler)
|
||||
ac.check_expired(scheduler, _runtime)
|
||||
|
||||
scheduler.assert_not_called()
|
||||
|
||||
@@ -310,10 +340,13 @@ class TestArchiveDelegates:
|
||||
ac.sessions = mock_sm
|
||||
ac.consolidator.compact_idle_session = AsyncMock(return_value="Summary.")
|
||||
|
||||
await ac._archive("cli:test")
|
||||
runtime = _runtime()
|
||||
await ac._archive("cli:test", runtime=runtime)
|
||||
|
||||
ac.consolidator.compact_idle_session.assert_awaited_once_with(
|
||||
"cli:test", ac._RECENT_SUFFIX_MESSAGES,
|
||||
"cli:test",
|
||||
runtime=runtime,
|
||||
max_suffix=ac._RECENT_SUFFIX_MESSAGES,
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -322,7 +355,7 @@ class TestArchiveDelegates:
|
||||
ac.consolidator.compact_idle_session = AsyncMock(return_value="Summary.")
|
||||
ac._archiving.add("dream:20260602-155256")
|
||||
|
||||
await ac._archive("dream:20260602-155256")
|
||||
await ac._archive("dream:20260602-155256", runtime=_runtime())
|
||||
|
||||
ac.consolidator.compact_idle_session.assert_not_awaited()
|
||||
assert "dream:20260602-155256" not in ac._archiving
|
||||
@@ -338,7 +371,7 @@ class TestArchiveDelegates:
|
||||
ac.sessions = mock_sm
|
||||
ac.consolidator.compact_idle_session = AsyncMock(return_value="Hello.")
|
||||
|
||||
await ac._archive("cli:test")
|
||||
await ac._archive("cli:test", runtime=_runtime())
|
||||
|
||||
entry = ac._summaries.get("cli:test")
|
||||
assert entry is not None
|
||||
@@ -351,7 +384,7 @@ class TestArchiveDelegates:
|
||||
ac.sessions = mock_sm
|
||||
ac.consolidator.compact_idle_session = AsyncMock(return_value="")
|
||||
|
||||
await ac._archive("cli:test")
|
||||
await ac._archive("cli:test", runtime=_runtime())
|
||||
|
||||
assert "cli:test" not in ac._summaries
|
||||
|
||||
@@ -362,7 +395,7 @@ class TestArchiveDelegates:
|
||||
ac.sessions = mock_sm
|
||||
ac.consolidator.compact_idle_session = AsyncMock(return_value="(nothing)")
|
||||
|
||||
await ac._archive("cli:test")
|
||||
await ac._archive("cli:test", runtime=_runtime())
|
||||
|
||||
assert "cli:test" not in ac._summaries
|
||||
|
||||
@@ -374,7 +407,7 @@ class TestArchiveDelegates:
|
||||
ac.consolidator.compact_idle_session = AsyncMock(side_effect=RuntimeError("fail"))
|
||||
|
||||
ac._archiving.add("cli:test")
|
||||
await ac._archive("cli:test")
|
||||
await ac._archive("cli:test", runtime=_runtime())
|
||||
|
||||
assert "cli:test" not in ac._archiving
|
||||
|
||||
|
||||
Reference in New Issue
Block a user