import aiosqlite import os DB_PATH = os.getenv("DB_PATH", "data/chat.db") async def init_db(): os.makedirs(os.path.dirname(DB_PATH), exist_ok=True) async with aiosqlite.connect(DB_PATH) as db: await db.executescript(""" CREATE TABLE IF NOT EXISTS sessions ( session_id TEXT PRIMARY KEY, persona_id TEXT DEFAULT 'default', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, title TEXT DEFAULT 'Новый чат', rpg_enabled INTEGER DEFAULT 0, facts_json TEXT DEFAULT '[]', global_plot TEXT DEFAULT '', status_quo TEXT DEFAULT '', plot_arc_json TEXT DEFAULT '{}', rng_seed INTEGER ); CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT NOT NULL, role TEXT NOT NULL, content TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (session_id) REFERENCES sessions(session_id) ); CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id); CREATE TABLE IF NOT EXISTS personas ( persona_id TEXT PRIMARY KEY, name TEXT NOT NULL, emoji TEXT DEFAULT '🤖', description TEXT DEFAULT '', prompt TEXT NOT NULL, custom INTEGER DEFAULT 1, sd_enabled INTEGER DEFAULT 0, lora_name TEXT DEFAULT '', lora_weight REAL DEFAULT 0.8, appearance_tags TEXT DEFAULT '', personality TEXT DEFAULT '', scenario TEXT DEFAULT '', first_mes TEXT DEFAULT '', mes_example TEXT DEFAULT '', lorebook_json TEXT DEFAULT '[]', avatar_path TEXT DEFAULT '' ); CREATE TABLE IF NOT EXISTS characters ( card_id TEXT PRIMARY KEY, name TEXT NOT NULL, description TEXT DEFAULT '', personality TEXT DEFAULT '', scenario TEXT DEFAULT '', first_mes TEXT DEFAULT '', mes_example TEXT DEFAULT '', raw_json TEXT NOT NULL, lora_name TEXT DEFAULT '', lora_weight REAL DEFAULT 0.8, appearance_tags TEXT DEFAULT '', lorebook_json TEXT DEFAULT '[]', avatar_path TEXT DEFAULT '', created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); """) await _migrate_messages_columns(db) await _migrate_personas_columns(db) await _migrate_sessions_columns(db) await _migrate_characters_columns(db) await _migrate_rpg_quests(db) await _migrate_action_resolutions(db) await db.commit() async def _migrate_messages_columns(db): async with db.execute("PRAGMA table_info(messages)") as cur: cols = {row[1] for row in await cur.fetchall()} if "image_prompt" not in cols: await db.execute("ALTER TABLE messages ADD COLUMN image_prompt TEXT") if "image_path" not in cols: await db.execute("ALTER TABLE messages ADD COLUMN image_path TEXT") async def _migrate_personas_columns(db): async with db.execute("PRAGMA table_info(personas)") as cur: cols = {row[1] for row in await cur.fetchall()} if "personality" not in cols: await db.execute("ALTER TABLE personas ADD COLUMN personality TEXT DEFAULT ''") if "scenario" not in cols: await db.execute("ALTER TABLE personas ADD COLUMN scenario TEXT DEFAULT ''") if "first_mes" not in cols: await db.execute("ALTER TABLE personas ADD COLUMN first_mes TEXT DEFAULT ''") if "mes_example" not in cols: await db.execute("ALTER TABLE personas ADD COLUMN mes_example TEXT DEFAULT ''") if "lorebook_json" not in cols: await db.execute("ALTER TABLE personas ADD COLUMN lorebook_json TEXT DEFAULT '[]'") if "avatar_path" not in cols: await db.execute("ALTER TABLE personas ADD COLUMN avatar_path TEXT DEFAULT ''") async def _migrate_sessions_columns(db): async with db.execute("PRAGMA table_info(sessions)") as cur: cols = {row[1] for row in await cur.fetchall()} if "rpg_enabled" not in cols: await db.execute("ALTER TABLE sessions ADD COLUMN rpg_enabled INTEGER DEFAULT 0") if "facts_json" not in cols: await db.execute("ALTER TABLE sessions ADD COLUMN facts_json TEXT DEFAULT '[]'") if "global_plot" not in cols: await db.execute("ALTER TABLE sessions ADD COLUMN global_plot TEXT DEFAULT ''") if "status_quo" not in cols: await db.execute("ALTER TABLE sessions ADD COLUMN status_quo TEXT DEFAULT ''") if "plot_arc_json" not in cols: await db.execute("ALTER TABLE sessions ADD COLUMN plot_arc_json TEXT DEFAULT '{}'") if "rng_seed" not in cols: await db.execute("ALTER TABLE sessions ADD COLUMN rng_seed INTEGER") if "affinity" not in cols: await db.execute("ALTER TABLE sessions ADD COLUMN affinity INTEGER DEFAULT 0") if "genre" not in cols: await db.execute("ALTER TABLE sessions ADD COLUMN genre TEXT DEFAULT 'adventure'") if "rpg_settings_json" not in cols: await db.execute("ALTER TABLE sessions ADD COLUMN rpg_settings_json TEXT DEFAULT '{}'") async def _migrate_rpg_quests(db): await db.executescript(""" CREATE TABLE IF NOT EXISTS rpg_quests ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT NOT NULL, title TEXT NOT NULL, status TEXT DEFAULT 'active', created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_rpg_quests_session ON rpg_quests(session_id); """) async def _migrate_action_resolutions(db): await db.executescript( """ CREATE TABLE IF NOT EXISTS action_resolutions ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT NOT NULL, message_id INTEGER, intent_text TEXT NOT NULL, roll INTEGER NOT NULL, outcome TEXT NOT NULL, resolution_text TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_action_resolutions_session ON action_resolutions(session_id); """ ) async def _migrate_characters_columns(db): async with db.execute("PRAGMA table_info(characters)") as cur: cols = {row[1] for row in await cur.fetchall()} if "avatar_path" not in cols: await db.execute("ALTER TABLE characters ADD COLUMN avatar_path TEXT DEFAULT ''")