// memory/src/store.rs // // SQLite Verbindung + Schema-Setup. // Eine DB-Datei pro Agent — saubere Trennung. use sqlx::SqlitePool; use anyhow::Result; pub struct MemoryStore { pub pool: SqlitePool, pub agent_id: String, } impl MemoryStore { /// Öffnet oder erstellt die SQLite DB für einen Agenten. /// `agent_id` → "sebas_tian" → "data/sebas_tian.db" pub async fn open(agent_id: &str) -> Result { // data/ Ordner anlegen falls nicht vorhanden tokio::fs::create_dir_all("data").await?; let path = format!("data/{}.db", agent_id); // SQLite URL — create_if_missing erstellt die Datei automatisch let url = format!("sqlite://{}?mode=rwc", path); let pool = SqlitePool::connect(&url).await?; let store = Self { pool, agent_id: agent_id.to_string() }; store.migrate().await?; Ok(store) } /// Erstellt alle Tabellen falls sie noch nicht existieren. /// Idempotent — kann mehrfach aufgerufen werden. async fn migrate(&self) -> Result<()> { sqlx::query( "CREATE TABLE IF NOT EXISTS conversations ( id INTEGER PRIMARY KEY AUTOINCREMENT, agent_id TEXT NOT NULL, date TEXT NOT NULL, summary TEXT, closed INTEGER NOT NULL DEFAULT 0, created_at INTEGER NOT NULL )" ) .execute(&self.pool) .await?; sqlx::query( "CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, conversation_id INTEGER NOT NULL, role TEXT NOT NULL, content TEXT NOT NULL, timestamp INTEGER NOT NULL, FOREIGN KEY (conversation_id) REFERENCES conversations(id) )" ) .execute(&self.pool) .await?; sqlx::query( "CREATE TABLE IF NOT EXISTS facts ( id INTEGER PRIMARY KEY AUTOINCREMENT, agent_id TEXT NOT NULL, category TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL, updated_at INTEGER NOT NULL, UNIQUE(agent_id, category, key) )" ) .execute(&self.pool) .await?; sqlx::query( "CREATE TABLE IF NOT EXISTS usage_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, agent_id TEXT NOT NULL, timestamp INTEGER NOT NULL, tokens_input INTEGER NOT NULL, tokens_output INTEGER NOT NULL, cost REAL, finish_reason TEXT )" ) .execute(&self.pool) .await?; Ok(()) } }