fix(memory): prune encoded Dream sessions

This commit is contained in:
chengyongru
2026-07-13 12:07:29 +08:00
committed by Xubin Ren
parent 6d406d93c9
commit 791c7fd505
2 changed files with 49 additions and 17 deletions
+9 -7
View File
@@ -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
+40 -10
View File
@@ -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"