Initial commit: AI Dungeon clone (FastAPI backend + React frontend)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KFsGHju9szibJJa2YJcdbg
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
"""Lightweight versioned schema migrations over SQLite's PRAGMA user_version.
|
||||
|
||||
How it works:
|
||||
- A fresh database is created by `Base.metadata.create_all()` (always current)
|
||||
and stamped with LATEST_VERSION.
|
||||
- An existing database runs every migration whose version is greater than its
|
||||
stored user_version, in order, then is stamped.
|
||||
|
||||
To change the schema: update models.py (keeps fresh DBs current) AND append a
|
||||
(version, sql) pair here (upgrades existing DBs). Keep migrations idempotent
|
||||
where cheap (IF NOT EXISTS etc.).
|
||||
"""
|
||||
|
||||
from sqlalchemy import inspect, text
|
||||
from sqlalchemy.engine import Engine
|
||||
|
||||
from .database import Base
|
||||
|
||||
# (version, SQL to run when upgrading past it) — append only, never reorder.
|
||||
MIGRATIONS: list[tuple[int, str]] = [
|
||||
# Phase 6: auto-summarization + memory bank (the `memories` table itself is
|
||||
# created by create_all, which runs for existing DBs too).
|
||||
(2, "ALTER TABLE adventures ADD COLUMN auto_summarize BOOLEAN NOT NULL DEFAULT 0"),
|
||||
(3, "ALTER TABLE adventures ADD COLUMN memory_bank_enabled BOOLEAN NOT NULL DEFAULT 0"),
|
||||
(4, "ALTER TABLE adventures ADD COLUMN memory_cursor INTEGER NOT NULL DEFAULT 0"),
|
||||
(5, "ALTER TABLE adventures ADD COLUMN summary_cursor INTEGER NOT NULL DEFAULT 0"),
|
||||
(6, "ALTER TABLE settings ADD COLUMN summary_model VARCHAR(200) NOT NULL DEFAULT ''"),
|
||||
(7, "ALTER TABLE settings ADD COLUMN embedding_model VARCHAR(200) NOT NULL DEFAULT ''"),
|
||||
(8, "ALTER TABLE settings ADD COLUMN memory_bank_capacity INTEGER NOT NULL DEFAULT 200"),
|
||||
(9, "ALTER TABLE settings ADD COLUMN memory_top_k INTEGER NOT NULL DEFAULT 5"),
|
||||
# Repair duplicate action indexes (player + AI actions of one turn used to
|
||||
# get the same index): renumber 0..n-1 per adventure, preserving order.
|
||||
# UPDATE..FROM: ranks are computed as a snapshot before any row is
|
||||
# rewritten (a correlated subquery would see partially-updated rows and
|
||||
# could produce duplicates again).
|
||||
(10, """
|
||||
UPDATE actions SET "index" = ranked.new_index
|
||||
FROM (
|
||||
SELECT id, ROW_NUMBER() OVER (
|
||||
PARTITION BY adventure_id ORDER BY "index", id
|
||||
) - 1 AS new_index
|
||||
FROM actions
|
||||
) AS ranked
|
||||
WHERE ranked.id = actions.id
|
||||
"""),
|
||||
# Reasoning-model support: separate thinking budget + stored reasoning text.
|
||||
(11, "ALTER TABLE settings ADD COLUMN reasoning_max_tokens INTEGER NOT NULL DEFAULT 0"),
|
||||
(12, "ALTER TABLE actions ADD COLUMN reasoning TEXT"),
|
||||
]
|
||||
|
||||
LATEST_VERSION = max((v for v, _ in MIGRATIONS), default=1)
|
||||
|
||||
|
||||
def bootstrap(engine: Engine) -> None:
|
||||
fresh = not inspect(engine).get_table_names()
|
||||
Base.metadata.create_all(bind=engine)
|
||||
with engine.begin() as conn:
|
||||
if fresh:
|
||||
conn.execute(text(f"PRAGMA user_version = {LATEST_VERSION}"))
|
||||
return
|
||||
current = conn.execute(text("PRAGMA user_version")).scalar() or 1
|
||||
for version, sql in MIGRATIONS:
|
||||
if version > current:
|
||||
conn.execute(text(sql))
|
||||
current = version
|
||||
conn.execute(text(f"PRAGMA user_version = {current}"))
|
||||
Reference in New Issue
Block a user