"""aiosqlite DB — 요청/응답 로깅 + 대화 영구화.""" from time import time import aiosqlite from config import settings SCHEMA = """ CREATE TABLE IF NOT EXISTS request_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, job_id TEXT NOT NULL, message TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'queued', model TEXT NOT NULL, response_chars INTEGER DEFAULT 0, latency_ms REAL DEFAULT 0, created_at REAL NOT NULL, completed_at REAL, rewrite_model TEXT, reasoning_model TEXT, rewritten_message TEXT, rewrite_latency_ms REAL DEFAULT 0 ); CREATE INDEX IF NOT EXISTS idx_logs_job ON request_logs(job_id); CREATE INDEX IF NOT EXISTS idx_logs_created ON request_logs(created_at); CREATE TABLE IF NOT EXISTS conversation_messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, role TEXT NOT NULL, content TEXT NOT NULL, created_at REAL NOT NULL ); CREATE INDEX IF NOT EXISTS idx_conv_user ON conversation_messages(user_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_conv_created ON conversation_messages(created_at); """ # Phase 1 → Phase 2 마이그레이션 (이미 존재하면 무시) MIGRATIONS = [ "ALTER TABLE request_logs ADD COLUMN rewrite_model TEXT", "ALTER TABLE request_logs ADD COLUMN reasoning_model TEXT", "ALTER TABLE request_logs ADD COLUMN rewritten_message TEXT", "ALTER TABLE request_logs ADD COLUMN rewrite_latency_ms REAL DEFAULT 0", ] async def init_db(): async with aiosqlite.connect(settings.db_path) as db: await db.execute("PRAGMA journal_mode=WAL") await db.executescript(SCHEMA) for migration in MIGRATIONS: try: await db.execute(migration) except Exception: pass # 이미 존재하는 컬럼 await db.commit() async def log_request(job_id: str, message: str, model: str, created_at: float): async with aiosqlite.connect(settings.db_path) as db: await db.execute( "INSERT INTO request_logs (job_id, message, model, created_at) VALUES (?, ?, ?, ?)", (job_id, message, model, created_at), ) await db.commit() async def save_conversation_message(user_id: str, role: str, content: str, created_at: float): """대화 메시지 저장.""" async with aiosqlite.connect(settings.db_path) as db: await db.execute( "INSERT INTO conversation_messages (user_id, role, content, created_at) VALUES (?, ?, ?, ?)", (user_id, role, content, created_at), ) await db.commit() async def load_conversation_messages(user_id: str, since: float, limit: int = 40) -> list[dict]: """user_id의 최근 메시지 로드 (오래된 순).""" async with aiosqlite.connect(settings.db_path) as db: db.row_factory = aiosqlite.Row cursor = await db.execute( "SELECT role, content, created_at FROM conversation_messages " "WHERE user_id=? AND created_at >= ? ORDER BY created_at DESC LIMIT ?", (user_id, since, limit), ) rows = await cursor.fetchall() # 오래된 순으로 뒤집기 return [{"role": r["role"], "content": r["content"], "created_at": r["created_at"]} for r in reversed(rows)] async def cleanup_old_conversations(days: int = 7) -> int: """N일 이상 오래된 대화 삭제. 삭제된 행 수 반환.""" cutoff = time() - (days * 86400) async with aiosqlite.connect(settings.db_path) as db: cursor = await db.execute("DELETE FROM conversation_messages WHERE created_at < ?", (cutoff,)) await db.commit() return cursor.rowcount async def log_completion( job_id: str, status: str, response_chars: int, latency_ms: float, completed_at: float, *, rewrite_model: str | None = None, reasoning_model: str | None = None, rewritten_message: str | None = None, rewrite_latency_ms: float = 0, ): async with aiosqlite.connect(settings.db_path) as db: await db.execute( """UPDATE request_logs SET status=?, response_chars=?, latency_ms=?, completed_at=?, rewrite_model=?, reasoning_model=?, rewritten_message=?, rewrite_latency_ms=? WHERE job_id=?""", (status, response_chars, latency_ms, completed_at, rewrite_model, reasoning_model, rewritten_message, rewrite_latency_ms, job_id), ) await db.commit()