Ghousted/CodeSage
0
1import logging2import os3from contextlib import asynccontextmanager4 5from dotenv import load_dotenv6load_dotenv()7 8from fastapi import FastAPI, Request9from fastapi.exceptions import RequestValidationError10from fastapi.middleware.cors import CORSMiddleware11from fastapi.responses import JSONResponse12 13from api.routes import router14 15logging.basicConfig(16 level=os.getenv("LOG_LEVEL", "INFO").upper(),17 format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",18)19logger = logging.getLogger(__name__)20 21REQUIRED_ENV_VARS = ("PINECONE_API_KEY", "HUGGINGFACE_API_KEY")22 23 24@asynccontextmanager25async def lifespan(app: FastAPI):26 missing = [v for v in REQUIRED_ENV_VARS if not os.environ.get(v)]27 if missing:28 logger.warning(29 "Missing required env vars: %s. Some endpoints will fail until they are set.",30 ", ".join(missing),31 )32 # Optionally pre-warm the embedding model so the first request isn't slow.33 if os.getenv("WARMUP_EMBEDDER", "1") == "1":34 try:35 from core.embedder import warmup36 warmup()37 logger.info("Embedding model pre-loaded.")38 except Exception as exc:39 logger.warning("Embedding model warmup failed: %s", exc)40 yield41 42 43app = FastAPI(44 title="CodeSage",45 description="AI-powered codebase analyzer with RAG",46 version="1.0.0",47 lifespan=lifespan,48)49 50# Allow override via env (comma-separated). Defaults to local dev.51_allowed = os.getenv(52 "ALLOWED_ORIGINS",53 "http://localhost:5173,http://127.0.0.1:5173",54).split(",")55 56app.add_middleware(57 CORSMiddleware,58 allow_origins=[o.strip() for o in _allowed if o.strip()],59 allow_methods=["*"],60 allow_headers=["*"],61)62 63app.include_router(router, prefix="/api")64 65 66@app.get("/health")67def health():68 missing = [v for v in REQUIRED_ENV_VARS if not os.environ.get(v)]69 return {70 "status": "ok" if not missing else "degraded",71 "missing_env": missing,72 }73 74 75@app.exception_handler(RequestValidationError)76async def _validation_exc(_: Request, exc: RequestValidationError):77 # Return user-friendly validation errors instead of FastAPI's verbose default78 return JSONResponse(79 status_code=422,80 content={"detail": exc.errors()[0]["msg"] if exc.errors() else "Validation error"},81 )82 83 84@app.exception_handler(Exception)85async def _global_exc(request: Request, exc: Exception):86 logger.exception("Unhandled error on %s %s", request.method, request.url.path)87 return JSONResponse(88 status_code=500,89 content={"detail": "Internal server error. Check server logs for details."},90 )91 