pagepiper/app/main.py
pyr0ball dca993691a Merge branch 'feat/colbert-retriever' into freeze/pr-12-14-15-integration
# Conflicts:
#	README.md
#	app/api/library.py
#	pyproject.toml
2026-07-10 19:11:52 -07:00

78 lines
2.7 KiB
Python

# app/main.py
"""FastAPI application factory for pagepiper."""
from __future__ import annotations
import logging
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI
from app.config import DB_PATH, VEC_DB_PATH, VEC_DIMENSIONS
from app.services.bm25_index import BM25Index
from app.services.colbert_index import ColBERTIndex
logger = logging.getLogger("pagepiper")
# Per-user BM25 registry — keyed by user_id; "__local__" for single-user mode
_bm25_map: dict[str, BM25Index] = {}
# Per-user ColBERT registry — keyed by user_id; index files live under
# <user_dir>/colbert_index/ (see _get_colbert_for)
_colbert_map: dict[str, ColBERTIndex] = {}
def _get_bm25_for(user_id: str) -> BM25Index:
if user_id not in _bm25_map:
_bm25_map[user_id] = BM25Index()
return _bm25_map[user_id]
def _get_colbert_for(user_id: str, user_dir) -> ColBERTIndex:
if user_id not in _colbert_map:
index_dir = str(user_dir / "colbert_index")
_colbert_map[user_id] = ColBERTIndex(index_dir=index_dir)
return _colbert_map[user_id]
@asynccontextmanager
async def lifespan(app: FastAPI):
from app.cloud_session import CLOUD_MODE
from app.config import LOCAL_USER_ID
from app.startup import apply_migrations, check_and_rebuild_vec_schema
embed_model = os.environ.get("PAGEPIPER_EMBED_MODEL", "nomic-embed-text")
logger.info("Pagepiper starting — embed model: %s, dims: %d", embed_model, VEC_DIMENSIONS)
if CLOUD_MODE:
from app.startup import warn_if_unencrypted
from app.config import DATA_DIR
warn_if_unencrypted(str(DATA_DIR))
else:
# In cloud mode, per-user migration and vec schema check run on first request (deps.py).
from app.config import DATA_DIR
apply_migrations(DB_PATH)
check_and_rebuild_vec_schema(VEC_DB_PATH, VEC_DIMENSIONS, DB_PATH)
_get_bm25_for(LOCAL_USER_ID).mark_dirty()
_get_colbert_for(LOCAL_USER_ID, DATA_DIR).mark_dirty()
yield
app = FastAPI(title="Pagepiper", lifespan=lifespan)
# Register routers
from app.api.library import router as library_router # noqa: E402
from app.api.shelve import router as shelve_router # noqa: E402
from app.api.search import router as search_router # noqa: E402
from app.api.chat import router as chat_router # noqa: E402
from app.api.feedback import router as feedback_router # noqa: E402
from app.api.feedback_attach import router as feedback_attach_router # noqa: E402
app.include_router(library_router)
app.include_router(shelve_router)
app.include_router(search_router)
app.include_router(chat_router)
app.include_router(feedback_router, prefix="/api/v1/feedback")
app.include_router(feedback_attach_router, prefix="/api/v1/feedback")