import json import os from collections import deque from pathlib import Path from typing import Any, Dict, List, Optional from .config import WORKING_MEMORY_SIZE, MEMORY_DIR class MemorySystem: def __init__(self): self.working = deque(maxlen=WORKING_MEMORY_SIZE) self._long_term: List[Dict[str, Any]] = [] self._load_long_term() def _long_term_path(self) -> Path: return MEMORY_DIR / "long_term.jsonl" def _load_long_term(self): path = self._long_term_path() if path.exists(): with open(path) as f: for line in f: line = line.strip() if line: self._long_term.append(json.loads(line)) def _save_long_term(self): path = self._long_term_path() with open(path, "w") as f: for entry in self._long_term[-1000:]: f.write(json.dumps(entry) + "\n") def add_working(self, entry: Dict[str, Any]): self.working.append(entry) def get_working(self) -> List[Dict[str, Any]]: return list(self.working) def add_long_term(self, entry: Dict[str, Any]): self._long_term.append(entry) self._save_long_term() def query_long_term(self, key: str, top_k: int = 5) -> List[Dict[str, Any]]: matches = [] for entry in reversed(self._long_term): if key.lower() in str(entry).lower(): matches.append(entry) if len(matches) >= top_k: break return matches def get_context(self, query: str) -> str: working_summary = "\n".join( f"{e.get('role', 'system')}: {e.get('content', '')[:200]}" for e in self.working[-5:] ) lt_matches = self.query_long_term(query, top_k=3) lt_summary = "\n".join( f"[memory] {m.get('summary', str(m)[:200])}" for m in lt_matches ) parts = [] if working_summary: parts.append(f"[working memory]\n{working_summary}") if lt_summary: parts.append(f"[long-term memory]\n{lt_summary}") return "\n\n".join(parts) def summarize_to_long_term(self, role: str, content: str): summary = content[:300] if len(content) > 300 else content self.add_long_term({"role": role, "summary": summary, "content": content[:1000]})