import aiosqlite from datetime import datetime from pathlib import Path from typing import List, Optional from .models import MemoryItem DB_PATH = Path("data/memory.db") async def init_db(): """Initialize database and create tables securely.""" Path("data").mkdir(exist_ok=True) async with aiosqlite.connect(DB_PATH) as db: await db.execute(""" CREATE TABLE IF NOT EXISTS memory_items ( item_id TEXT PRIMARY KEY, tier INTEGER NOT NULL DEFAULT 5, title TEXT NOT NULL, content TEXT NOT NULL, tags TEXT NOT NULL, confidence REAL NOT NULL, usage_count INTEGER NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, metadata TEXT NOT NULL ) """) await db.execute("CREATE INDEX IF NOT EXISTS idx_tags ON memory_items(tags)") await db.execute("CREATE INDEX IF NOT EXISTS idx_tier ON memory_items(tier)") await db.commit() class DatabaseMemoryStore: def __init__(self): self.db_path = DB_PATH async def add(self, item: MemoryItem) -> MemoryItem: async with aiosqlite.connect(self.db_path) as db: await db.execute(""" INSERT OR REPLACE INTO memory_items VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( item.item_id, item.tier.value if hasattr(item.tier, 'value') else (getattr(item, 'tier', 5) if hasattr(item, 'tier') else 5), item.title, item.content, ",".join(item.tags), item.confidence, item.usage_count, item.created_at.isoformat(), item.updated_at.isoformat(), str(item.metadata) )) await db.commit() return item async def get(self, item_id: str) -> Optional[MemoryItem]: async with aiosqlite.connect(self.db_path) as db: async with db.execute("SELECT * FROM memory_items WHERE item_id = ?", (item_id,)) as cursor: row = await cursor.fetchone() if row: return self._row_to_item(row) return None async def search(self, query: str, limit: int = 10) -> List[MemoryItem]: async with aiosqlite.connect(self.db_path) as db: async with db.execute(""" SELECT * FROM memory_items WHERE tags LIKE ? OR content LIKE ? ORDER BY tier DESC, usage_count DESC LIMIT ? """, (f"%{query}%", f"%{query}%", limit)) as cursor: rows = await cursor.fetchall() return [self._row_to_item(row) for row in rows] def _row_to_item(self, row) -> MemoryItem: return MemoryItem( item_id=row[0], tier=row[1], title=row[2], content=row[3], tags=row[4].split(",") if row[4] else [], confidence=row[5], usage_count=row[6], created_at=datetime.fromisoformat(row[7]), updated_at=datetime.fromisoformat(row[8]), metadata=eval(row[9]) if row[9] else {} )