from __future__ import annotations import logging import os import time from contextlib import asynccontextmanager # Force offline mode globally to prevent HuggingFace hub HEAD loops # Must be set before any sentence-transformers imports os.environ["HF_HUB_OFFLINE"] = "1" os.environ["TRANSFORMERS_OFFLINE"] = "1" from fastapi import FastAPI from src.api.middleware.logging import RequestLoggingMiddleware from src.api.middleware.validation import InputValidationMiddleware from src.api.routes.health import init_health, set_index_size, set_model_loaded from src.api.routes.health import router as health_router from src.api.routes.ingest import router as ingest_router from src.api.routes.profiles import init_profiles from src.api.routes.profiles import router as profiles_router from src.api.routes.search import init_orchestrator from src.api.routes.search import router as search_router from src.core.config import DATA_DIR, get_settings logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): _start_time = time.time() init_health(index_size=0) logger.info("Starting India Runs — Intelligent Candidate Discovery") indexes_dir = DATA_DIR / "indexes" indexes_dir.mkdir(parents=True, exist_ok=True) faiss_path = indexes_dir / "faiss_index.bin" id_map_path = indexes_dir / "faiss_id_map.json" bm25_path = indexes_dir / "bm25_index.pkl" if not faiss_path.exists(): logger.warning( "No FAISS index found. Run 'python scripts/build_indexes.py --sample 50' first." ) init_health(index_size=0) yield return from src.core.config import build_orchestrator orchestrator, vector_search, profiles = build_orchestrator( faiss_path=faiss_path, id_map_path=id_map_path, bm25_path=bm25_path, cross_encoder_timeout_ms=get_settings().cross_encoder_timeout_ms, ) set_model_loaded("embedding", True) set_model_loaded("cross_encoder", True) # already handled inside build_orchestrator init_orchestrator(orchestrator) set_index_size(vector_search.size) init_profiles(profiles) logger.info("System initialized successfully (%.1fs)", time.time() - _start_time) yield logger.info("Shutting down") app = FastAPI( title="India Runs — Intelligent Candidate Discovery", description="Hybrid semantic search with agentic AI for candidate matching", version="0.1.0", lifespan=lifespan, ) app.add_middleware(RequestLoggingMiddleware) app.add_middleware(InputValidationMiddleware) app.include_router(search_router, prefix="/api/v1") app.include_router(profiles_router, prefix="/api/v1") app.include_router(ingest_router, prefix="/api/v1") app.include_router(health_router, prefix="/api/v1") if __name__ == "__main__": import uvicorn uvicorn.run("src.main:app", host="0.0.0.0", port=8000, log_level="info")