Vizz17/context-aware-rag
0
1"""FastAPI application entrypoint."""2 3from __future__ import annotations4 5import logging6from contextlib import asynccontextmanager7from pathlib import Path8 9logging.basicConfig(10 level=logging.INFO,11 format="%(asctime)s %(levelname)-8s %(name)s — %(message)s",12 datefmt="%H:%M:%S",13)14 15from fastapi import FastAPI16from fastapi.middleware.cors import CORSMiddleware17from fastapi.responses import FileResponse, Response18from fastapi.staticfiles import StaticFiles19 20from app.api import chat, health, upload, auth21from app.core.config import settings22 23FRONTEND_DIR = Path(__file__).resolve().parent.parent / "frontend"24 25 26import asyncio27 28async def periodic_guest_cleanup():29 while True:30 try:31 await asyncio.sleep(3600) # Clean up every hour32 from app.services.auth import cleanup_old_guest_sessions33 cleanup_old_guest_sessions()34 except asyncio.CancelledError:35 break36 except Exception as e:37 logging.getLogger("app.main").error(f"Periodic guest cleanup failed: {e}")38 39@asynccontextmanager40async def lifespan(app: FastAPI):41 """Startup / shutdown lifecycle hook."""42 # ── Startup ──43 settings.ensure_dirs()44 45 # Initialize SQLite User DB and clean up old sessions46 from app.services.auth import init_db, cleanup_old_guest_sessions47 init_db()48 try:49 cleanup_old_guest_sessions()50 except Exception as e:51 logging.getLogger("app.main").error(f"Failed to clean up old guest sessions on start: {e}")52 53 # Eagerly initialize vector store to avoid cold start lag on first search54 from app.services.vector_store import get_vector_store55 get_vector_store()56 57 # Eagerly load cross-encoder reranker model to avoid cold start lag on first request58 if settings.enable_reranker:59 from app.services.reranker import _get_cross_encoder60 _get_cross_encoder()61 62 cleanup_task = asyncio.create_task(periodic_guest_cleanup())63 64 yield65 # ── Shutdown ──66 cleanup_task.cancel()67 try:68 await cleanup_task69 except asyncio.CancelledError:70 pass71 72 73app = FastAPI(74 title="Context-Aware RAG Engine",75 description="Semantic search & QA over PDF collections with source-grounded answers.",76 version="0.1.0",77 lifespan=lifespan,78)79 80# ── CORS ────────────────────────────────────────────────────81app.add_middleware(82 CORSMiddleware,83 allow_origins=settings.cors_origins,84 allow_credentials=True,85 allow_methods=["*"],86 allow_headers=["*"],87)88 89# ── Routes ──────────────────────────────────────────────────90app.include_router(health.router, tags=["Health"])91app.include_router(auth.router, prefix="/api", tags=["Auth"])92app.include_router(upload.router, prefix="/api", tags=["Documents"])93app.include_router(chat.router, prefix="/api", tags=["Chat"])94 95 96# ── Frontend ────────────────────────────────────────────────97@app.get("/", include_in_schema=False)98async def serve_root():99 """Serve the main chat page."""100 return FileResponse(FRONTEND_DIR / "index.html")101 102 103@app.get("/favicon.ico", include_in_schema=False)104async def favicon():105 """Return an empty response for browser favicon requests."""106 return Response(status_code=204)107 108 109app.mount("/static", StaticFiles(directory=str(FRONTEND_DIR)), name="static")110 