Why: - Dashboard needs system health, Qdrant management, chunk preview, questions, and cost estimation endpoints Changes: - Add admin router with 11 endpoints (health, Qdrant CRUD, chunk preview, questions management, cost estimation) - Add delete_experiment to SQLite storage - Mount admin router and dashboard static files at /app Impact: - New /admin/* API routes available - Dashboard served at /app/ via StaticFiles
106 lines
3.2 KiB
Python
106 lines
3.2 KiB
Python
"""FastAPI app factory. Composes all domain routers and middleware."""
|
|
|
|
import logging
|
|
import os
|
|
import time
|
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
from starlette.requests import Request
|
|
from starlette.responses import Response
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.staticfiles import StaticFiles
|
|
|
|
from src.core.exceptions import (
|
|
ChunkingError,
|
|
BenchmarkError,
|
|
QueryError,
|
|
chunking_exception_handler,
|
|
benchmark_exception_handler,
|
|
)
|
|
from src.documents.routes import router as documents_router
|
|
from src.benchmarking.routes import router as benchmarking_router
|
|
from src.admin.routes import router as admin_router
|
|
from src.storage.sqlite import init_db
|
|
|
|
# Configure logging
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
# ── Request Logging Middleware ─────────────────────────────────────
|
|
|
|
class RequestLoggingMiddleware(BaseHTTPMiddleware):
|
|
"""Log request method, path, status code, and duration."""
|
|
|
|
async def dispatch(self, request: Request, call_next) -> Response:
|
|
start = time.time()
|
|
method = request.method
|
|
path = request.url.path
|
|
|
|
response = await call_next(request)
|
|
|
|
duration = (time.time() - start) * 1000 # ms
|
|
status = response.status_code
|
|
|
|
# Log at appropriate level
|
|
if status >= 500:
|
|
logger.error("%s %s -> %d (%.1fms)", method, path, status, duration)
|
|
elif status >= 400:
|
|
logger.warning("%s %s -> %d (%.1fms)", method, path, status, duration)
|
|
else:
|
|
logger.info("%s %s -> %d (%.1fms)", method, path, status, duration)
|
|
|
|
return response
|
|
|
|
|
|
def create_app() -> FastAPI:
|
|
"""Create and configure the FastAPI application.
|
|
|
|
Mounts domain routers and registers exception handlers.
|
|
"""
|
|
# Ensure SQLite tables exist at startup
|
|
init_db()
|
|
|
|
app = FastAPI(
|
|
title="RAG Chunking Benchmarker",
|
|
description="Benchmark five chunking strategies on regulatory documents. "
|
|
"Compare retrieval quality, answer faithfulness, and cost.",
|
|
version="0.1.0",
|
|
)
|
|
|
|
# CORS — allow all origins for development
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
# Request logging
|
|
app.add_middleware(RequestLoggingMiddleware)
|
|
|
|
# Register exception handlers
|
|
app.add_exception_handler(ChunkingError, chunking_exception_handler)
|
|
app.add_exception_handler(BenchmarkError, benchmark_exception_handler)
|
|
app.add_exception_handler(QueryError, benchmark_exception_handler)
|
|
|
|
# Mount domain routers
|
|
app.include_router(documents_router)
|
|
app.include_router(benchmarking_router)
|
|
app.include_router(admin_router)
|
|
|
|
# Mount dashboard at /app
|
|
static_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static")
|
|
app.mount("/app", StaticFiles(directory=static_dir, html=True), name="dashboard")
|
|
|
|
return app
|
|
|
|
|
|
app = create_app() |