import logging from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from app.config import settings from app.api import chat, ingestion, health logging.basicConfig( level=logging.INFO, format="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s", ) logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(_app: FastAPI): # Eagerly load the embedding model and vector store so the first visitor # doesn't pay the model-load latency. from app.services.embedding_service import get_embedding_service from app.services.vector_store import get_vector_store from app.services.llm_service import get_llm_service get_embedding_service() chunks = get_vector_store().count() get_llm_service() if chunks == 0: logger.warning("Vector store is EMPTY — run scripts/ingest.py to build the index") logger.info("Warmup complete — %d chunks indexed", chunks) yield app = FastAPI( title="Negoptim AI Backend", description="RAG-powered chatbot backend for Users Love IT (ULiT)", version="1.0.0", lifespan=lifespan, ) app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origin_list, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(health.router, prefix="/api", tags=["health"]) app.include_router(chat.router, prefix="/api", tags=["chat"]) app.include_router(ingestion.router, prefix="/api", tags=["ingestion"]) @app.get("/") async def root(): return {"service": "Negoptim AI", "docs": "/docs"}