Spaces:
Running on Zero
Running on Zero
| """Memory manager — orchestrates short-term, long-term, and vector memory.""" | |
| import json | |
| from datetime import datetime | |
| from typing import Optional | |
| from config import config | |
| from database.repositories import MemoryRepo, UserFactRepo | |
| class MemoryManager: | |
| """Central memory system for Synapse V8.""" | |
| def __init__(self, user_id: str = "guest"): | |
| self.user_id = user_id | |
| self.short_term: list[dict] = [] | |
| self._short_term_limit = config.memory.short_term_limit | |
| async def add_short_term(self, role: str, content: str, | |
| metadata: dict = None) -> dict: | |
| entry = { | |
| "role": role, | |
| "content": content, | |
| "timestamp": datetime.utcnow().isoformat(), | |
| "metadata": metadata or {}, | |
| } | |
| self.short_term.append(entry) | |
| if len(self.short_term) > self._short_term_limit: | |
| await self._consolidate_oldest() | |
| return entry | |
| async def _consolidate_oldest(self): | |
| if not self.short_term: | |
| return | |
| oldest = self.short_term.pop(0) | |
| importance = self._calculate_importance(oldest["content"]) | |
| if importance >= config.memory.long_term_threshold: | |
| await MemoryRepo.create( | |
| user_id=self.user_id, | |
| content=oldest["content"], | |
| memory_type="consolidated", | |
| importance=importance, | |
| metadata=oldest.get("metadata"), | |
| ) | |
| def _calculate_importance(self, content: str) -> float: | |
| score = 0.3 | |
| important_markers = [ | |
| "remember", "important", "never forget", "always", | |
| "my name", "i prefer", "i like", "i don't like", | |
| "my birthday", "my email", "password", "secret", | |
| "goal", "project", "deadline", "meeting", | |
| ] | |
| lower = content.lower() | |
| for marker in important_markers: | |
| if marker in lower: | |
| score += 0.15 | |
| if len(content) > 200: | |
| score += 0.1 | |
| if "?" in content: | |
| score += 0.05 | |
| return min(score, 1.0) | |
| def get_short_term_context(self, max_messages: int = 20) -> list[dict]: | |
| return self.short_term[-max_messages:] | |
| async def search_long_term(self, query: str, limit: int = 5) -> list[dict]: | |
| results = await MemoryRepo.search(self.user_id, query) | |
| return results[:limit] | |
| async def get_pinned_memories(self) -> list[dict]: | |
| return await MemoryRepo.get_pinned(self.user_id) | |
| async def add_memory(self, content: str, memory_type: str = "long_term", | |
| importance: float = 0.5, tags: list = None, | |
| conversation_id: str = None) -> dict: | |
| return await MemoryRepo.create( | |
| user_id=self.user_id, | |
| content=content, | |
| memory_type=memory_type, | |
| importance=importance, | |
| tags=tags, | |
| conversation_id=conversation_id, | |
| ) | |
| async def add_user_fact(self, fact: str, category: str = "general", | |
| source: str = None) -> dict: | |
| return await UserFactRepo.create( | |
| user_id=self.user_id, | |
| fact=fact, | |
| category=category, | |
| source=source, | |
| ) | |
| async def get_user_facts(self, category: str = None) -> list[dict]: | |
| return await UserFactRepo.get_user_facts(self.user_id, category) | |
| async def get_all_memories(self, memory_type: str = None, | |
| limit: int = 50) -> list[dict]: | |
| return await MemoryRepo.get_user_memories( | |
| self.user_id, memory_type=memory_type, limit=limit | |
| ) | |
| async def toggle_pin_memory(self, memory_id: str) -> None: | |
| await MemoryRepo.toggle_pin(memory_id) | |
| async def delete_memory(self, memory_id: str) -> bool: | |
| return await MemoryRepo.delete(memory_id) | |
| async def build_memory_context(self) -> str: | |
| parts = [] | |
| pinned = await self.get_pinned_memories() | |
| if pinned: | |
| parts.append("Pinned memories:") | |
| for m in pinned[:10]: | |
| parts.append(f"- {m['content']}") | |
| facts = await self.get_user_facts() | |
| if facts: | |
| parts.append("\nKnown facts about user:") | |
| for f in facts[:10]: | |
| parts.append(f"- {f['fact']} ({f['category']})") | |
| recent_memories = await self.get_all_memories(limit=5) | |
| if recent_memories: | |
| parts.append("\nRecent memories:") | |
| for m in recent_memories: | |
| parts.append(f"- {m['content']}") | |
| return "\n".join(parts) if parts else "" | |
| async def consolidate(self, conversation_messages: list[dict]) -> None: | |
| if len(conversation_messages) < config.memory.consolidation_interval: | |
| return | |
| summary_parts = [] | |
| for msg in conversation_messages[-config.memory.consolidation_interval:]: | |
| summary_parts.append(f"{msg['role']}: {msg['content'][:200]}") | |
| summary = "\n".join(summary_parts) | |
| importance = self._calculate_importance(summary) | |
| if importance >= 0.4: | |
| await self.add_memory( | |
| content=f"Conversation summary: {summary[:500]}", | |
| memory_type="summary", | |
| importance=importance, | |
| ) | |