Files
ai-rpg/backend/app/migrations/init_db.py

104 lines
4.9 KiB
Python
Raw Normal View History

2026-06-19 11:28:04 +03:00
"""Database initialization: create all tables and seed defaults."""
from __future__ import annotations
import asyncio
import json
from pathlib import Path
from sqlalchemy import select, text
from app.db import AsyncSessionLocal, Base, engine
from app.models import GlossaryEntry, Preset, Setting, User
from app.config import settings
from app.logging_setup import get_logger, setup_logging
from app.core.security import hash_password
from app.prompts.fantasy_preset import FANTASY_PRESET_RU, FANTASY_PRESET_EN
log = get_logger("migrations")
DEFAULT_SETTINGS = [
("llm.base_url", settings.default_llm_base_url, "OpenAI-compatible base URL"),
("llm.api_key", settings.default_llm_api_key, "API key for LLM endpoint"),
("llm.model", settings.default_llm_model, "Default model name"),
("llm.temperature", 0.7, "Temperature for orchestrator"),
("llm.step_temperature", 0.85, "Temperature for narrative step writer"),
("llm.summary_temperature", 0.3, "Temperature for summarizer"),
("llm.max_tokens", 1024, "Max tokens per LLM response"),
("llm.request_timeout", 120, "LLM request timeout, seconds"),
("llm.streaming", True, "Whether to use streaming responses"),
("context.recent_messages", settings.default_recent_messages, "Guaranteed recent messages in prompt"),
("context.compress_threshold", settings.default_compress_threshold, "Trigger compression at this count"),
("context.summary_messages", settings.default_summary_messages, "Number of messages per summary block"),
("context.max_tokens_total", 6000, "Soft token budget for context window (small models)"),
("triggers.enabled", True, "Enable deferred trigger processing"),
("triggers.check_interval", 30, "Trigger checker interval, seconds"),
# Embeddings / RAG
("embedding.provider", settings.default_embedding_provider, "Embeddings provider: 'hash' (offline fallback) or 'openai' (real semantic embeddings)"),
("embedding.base_url", settings.default_embedding_base_url, "OpenAI-compatible embeddings base URL. Empty = reuse llm.base_url"),
("embedding.api_key", settings.default_embedding_api_key, "API key for embeddings endpoint. Empty = reuse llm.api_key"),
("embedding.model", settings.default_embedding_model, "Embedding model name (e.g. text-embedding-3-small, bge-m3, nomic-embed-text)"),
("embedding.dim", settings.default_embedding_dim, "Vector dimension. 0 = auto-probe from endpoint on first use"),
("embedding.request_timeout", settings.default_embedding_request_timeout, "Embeddings request timeout, seconds"),
]
async def init_db() -> None:
setup_logging()
log.info("creating_tables")
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
log.info("tables_ready")
async with AsyncSessionLocal() as session:
# Seed settings
result = await session.execute(select(Setting).limit(1))
if result.scalars().first() is None:
for key, value, desc in DEFAULT_SETTINGS:
session.add(Setting(key=key, value=value, description=desc))
await session.commit()
log.info("settings_seeded", count=len(DEFAULT_SETTINGS))
else:
log.info("settings_already_exist")
# Seed built-in Fantasy preset
result = await session.execute(select(Preset).where(Preset.is_builtin.is_(True)))
if result.scalars().first() is None:
for preset_def in (FANTASY_PRESET_RU, FANTASY_PRESET_EN):
session.add(Preset(
slug=preset_def["slug"],
title=preset_def["title"],
description=preset_def["description"],
language=preset_def["language"],
is_public=True,
is_builtin=True,
payload=preset_def["payload"],
))
await session.commit()
log.info("builtin_presets_seeded")
else:
log.info("builtin_presets_already_exist")
# Ensure admin_setup_token is set; if empty, generate and print
token = settings.admin_setup_token.strip()
if not token:
import secrets as _s
token = _s.token_urlsafe(24)
async with AsyncSessionLocal() as session:
existing = await session.execute(select(Setting).where(Setting.key == "admin.setup_token"))
existing_obj = existing.scalars().first()
if existing_obj is None:
session.add(Setting(key="admin.setup_token", value=token, description="One-time token for /admin/setup"))
await session.commit()
print("=" * 60)
print("ADMIN SETUP TOKEN (use at /admin/setup):")
print(token)
print("=" * 60)
log.info("admin_setup_token_generated")
else:
log.info("admin_setup_token_already_set")
if __name__ == "__main__":
asyncio.run(init_db())