fix(memory): prune encoded Dream sessions
This commit is contained in:
@@ -16,7 +16,7 @@ from typing import TYPE_CHECKING, Any, Callable, Iterator
|
||||
from loguru import logger
|
||||
|
||||
from nanobot.runtime_context import public_history_messages
|
||||
from nanobot.session.manager import Session
|
||||
from nanobot.session.manager import Session, SessionManager
|
||||
from nanobot.utils.gitstore import GitStore
|
||||
from nanobot.utils.helpers import (
|
||||
ensure_dir,
|
||||
@@ -31,7 +31,6 @@ from nanobot.utils.helpers import (
|
||||
from nanobot.utils.prompt_templates import render_template
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nanobot.session.manager import SessionManager
|
||||
from nanobot.utils.llm_runtime import LLMRuntime
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -705,12 +704,15 @@ class MemoryStore:
|
||||
def prune_dream_sessions(sessions_dir: Path, *, keep: int = 10) -> None:
|
||||
"""Remove the oldest Dream session files, keeping only the N most recent.
|
||||
|
||||
Only files matching ``dream_*.jsonl`` are considered. Non-dream session
|
||||
files are never touched.
|
||||
Only current base64url-encoded Dream session keys are considered.
|
||||
Non-dream session files are never touched.
|
||||
"""
|
||||
dream_files = sorted(
|
||||
sessions_dir.glob("dream_*.jsonl"), key=lambda p: p.stat().st_mtime,
|
||||
)
|
||||
dream_files = []
|
||||
for path in sessions_dir.glob("*.jsonl"):
|
||||
decoded_key = SessionManager._decode_storage_key(path.stem)
|
||||
if decoded_key is not None and decoded_key.startswith("dream:"):
|
||||
dream_files.append(path)
|
||||
dream_files.sort(key=lambda p: p.stat().st_mtime)
|
||||
if len(dream_files) <= keep:
|
||||
return
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
"""Tests for Dream session key generation and rotation."""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
from nanobot.agent.memory import MemoryStore
|
||||
from nanobot.session.manager import SessionManager
|
||||
|
||||
|
||||
class TestDreamSessionKey:
|
||||
@@ -31,11 +33,11 @@ class TestPruneDreamSessions:
|
||||
sessions_dir.mkdir()
|
||||
|
||||
base_time = time.time() - 100
|
||||
dream_paths = []
|
||||
|
||||
for i in range(15):
|
||||
key = f"dream:20260528-{100000 + i:06d}"
|
||||
safe_key = key.replace(":", "_")
|
||||
path = sessions_dir / f"{safe_key}.jsonl"
|
||||
path = sessions_dir / f"{SessionManager._storage_key(key)}.jsonl"
|
||||
path.write_text(
|
||||
f'{{"_type": "metadata", "key": "{key}", '
|
||||
f'"created_at": "2026-05-28T10:00:{i:02d}", '
|
||||
@@ -43,29 +45,57 @@ class TestPruneDreamSessions:
|
||||
encoding="utf-8",
|
||||
)
|
||||
os.utime(path, (base_time + i, base_time + i))
|
||||
dream_paths.append(path)
|
||||
|
||||
normal_path = sessions_dir / "telegram_123.jsonl"
|
||||
normal_path.write_text('{"_type": "metadata"}\n', encoding="utf-8")
|
||||
|
||||
MemoryStore.prune_dream_sessions(sessions_dir, keep=10)
|
||||
|
||||
dream_files = sorted(sessions_dir.glob("dream_*.jsonl"))
|
||||
assert len(dream_files) == 10
|
||||
remaining_keys = [f.stem for f in dream_files]
|
||||
assert "dream_20260528-100000" not in remaining_keys
|
||||
assert "dream_20260528-100014" in remaining_keys
|
||||
assert [path.exists() for path in dream_paths] == [False] * 5 + [True] * 10
|
||||
assert normal_path.exists()
|
||||
|
||||
def test_ignores_legacy_dream_filenames(self, tmp_path):
|
||||
import os
|
||||
import time
|
||||
|
||||
sessions_dir = tmp_path / "sessions"
|
||||
sessions_dir.mkdir()
|
||||
base_time = time.time() - 100
|
||||
current_paths = []
|
||||
|
||||
for i in range(2):
|
||||
key = f"dream:20260713-{100000 + i:06d}"
|
||||
path = sessions_dir / f"{SessionManager._storage_key(key)}.jsonl"
|
||||
path.write_text(
|
||||
f'{{"_type": "metadata", "key": "{key}"}}\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
os.utime(path, (base_time + i, base_time + i))
|
||||
current_paths.append(path)
|
||||
|
||||
legacy_path = sessions_dir / "dream_20260713-095959.jsonl"
|
||||
legacy_path.write_text(
|
||||
'{"_type": "metadata", "key": "dream:20260713-095959"}\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
os.utime(legacy_path, (base_time - 1, base_time - 1))
|
||||
|
||||
MemoryStore.prune_dream_sessions(sessions_dir, keep=1)
|
||||
|
||||
assert [path.exists() for path in current_paths] == [False, True]
|
||||
assert legacy_path.exists()
|
||||
|
||||
def test_noop_when_under_limit(self, tmp_path):
|
||||
sessions_dir = tmp_path / "sessions"
|
||||
sessions_dir.mkdir()
|
||||
for i in range(3):
|
||||
key = f"dream:20260528-{100000 + i:06d}"
|
||||
safe_key = key.replace(":", "_")
|
||||
(sessions_dir / f"{safe_key}.jsonl").write_text("{}", encoding="utf-8")
|
||||
path = sessions_dir / f"{SessionManager._storage_key(key)}.jsonl"
|
||||
path.write_text("{}", encoding="utf-8")
|
||||
|
||||
MemoryStore.prune_dream_sessions(sessions_dir, keep=10)
|
||||
assert len(list(sessions_dir.glob("dream_*.jsonl"))) == 3
|
||||
assert len(list(sessions_dir.glob("*.jsonl"))) == 3
|
||||
|
||||
def test_empty_dir_noop(self, tmp_path):
|
||||
sessions_dir = tmp_path / "sessions"
|
||||
|
||||
Reference in New Issue
Block a user