Spaces:
Running
Running
| """SQLite-backed conversation persistence.""" | |
| from __future__ import annotations | |
| import json | |
| import sqlite3 | |
| from datetime import datetime, timezone | |
| from pathlib import Path | |
| class ConversationStore: | |
| """Store and retrieve conversation history keyed by session_id.""" | |
| def __init__(self, db_path: str = "data/conversations.db") -> None: | |
| self.db_path = Path(db_path) | |
| self.db_path.parent.mkdir(parents=True, exist_ok=True) | |
| self._init_db() | |
| def _init_db(self) -> None: | |
| with sqlite3.connect(self.db_path) as conn: | |
| conn.execute(""" | |
| CREATE TABLE IF NOT EXISTS conversations ( | |
| session_id TEXT NOT NULL, | |
| role TEXT NOT NULL, | |
| content TEXT NOT NULL, | |
| timestamp TEXT NOT NULL, | |
| metadata TEXT DEFAULT '{}' | |
| ) | |
| """) | |
| conn.execute(""" | |
| CREATE INDEX IF NOT EXISTS idx_session | |
| ON conversations(session_id) | |
| """) | |
| def append( | |
| self, | |
| session_id: str, | |
| role: str, | |
| content: str, | |
| metadata: dict | None = None, | |
| ) -> None: | |
| """Add a message to a session.""" | |
| with sqlite3.connect(self.db_path) as conn: | |
| conn.execute( | |
| "INSERT INTO conversations VALUES (?, ?, ?, ?, ?)", | |
| ( | |
| session_id, | |
| role, | |
| content, | |
| datetime.now(timezone.utc).isoformat(), | |
| json.dumps(metadata or {}), | |
| ), | |
| ) | |
| def get_history( | |
| self, session_id: str, max_turns: int = 10 | |
| ) -> list[dict]: | |
| """Get recent conversation history for a session. | |
| Returns up to max_turns * 2 messages (user + assistant pairs), | |
| ordered chronologically. | |
| """ | |
| with sqlite3.connect(self.db_path) as conn: | |
| rows = conn.execute( | |
| """SELECT role, content FROM conversations | |
| WHERE session_id = ? | |
| ORDER BY timestamp DESC LIMIT ?""", | |
| (session_id, max_turns * 2), | |
| ).fetchall() | |
| return [{"role": r, "content": c} for r, c in reversed(rows)] | |
| def list_sessions(self) -> list[str]: | |
| """List all session IDs.""" | |
| with sqlite3.connect(self.db_path) as conn: | |
| rows = conn.execute( | |
| "SELECT DISTINCT session_id FROM conversations" | |
| ).fetchall() | |
| return [r[0] for r in rows] | |
| def delete_session(self, session_id: str) -> None: | |
| """Delete all messages for a session.""" | |
| with sqlite3.connect(self.db_path) as conn: | |
| conn.execute( | |
| "DELETE FROM conversations WHERE session_id = ?", | |
| (session_id,), | |
| ) | |