Spaces:
Running
Running
| """SQLModel engine and session dependency for the Postgres history store.""" | |
| from __future__ import annotations | |
| import logging | |
| import os | |
| from collections.abc import Iterator | |
| from pathlib import Path | |
| from sqlmodel import Session, SQLModel, create_engine | |
| logger = logging.getLogger("tracerag.database") | |
| # load .env so APP_DATABASE_URL is available; no-op if absent | |
| try: | |
| from dotenv import load_dotenv | |
| load_dotenv(Path(__file__).resolve().parent / ".env") | |
| except ImportError: # pragma: no cover | |
| pass | |
| _engine = None # lazily created | |
| def get_engine(): | |
| """Return the process-wide SQLModel engine, creating it on first use.""" | |
| global _engine | |
| if _engine is None: | |
| url = os.getenv("APP_DATABASE_URL") | |
| if not url: | |
| raise RuntimeError( | |
| "APP_DATABASE_URL is not set. Add your Neon connection string " | |
| "(postgresql://USER:PASSWORD@HOST/DB?sslmode=require) to .env." | |
| ) | |
| _engine = create_engine(url, echo=False, pool_pre_ping=True) | |
| return _engine | |
| def init_db() -> None: | |
| """Create the history tables if they don't exist (idempotent).""" | |
| # import models so their tables register before create | |
| from models import ChatSession, TraceLog, User # noqa: F401 | |
| from sqlalchemy.orm import configure_mappers | |
| # validate relationships at startup, not on first query | |
| configure_mappers() | |
| SQLModel.metadata.create_all(get_engine()) | |
| logger.info("Postgres history schema ready.") | |
| def get_session() -> Iterator[Session]: | |
| """FastAPI dependency yielding a session, closed afterwards.""" | |
| with Session(get_engine()) as session: | |
| yield session | |