Spaces:
Sleeping
Sleeping
| # main.py | |
| from contextlib import asynccontextmanager | |
| from fastapi import FastAPI | |
| from fastapi.responses import RedirectResponse | |
| from starlette.middleware.cors import CORSMiddleware | |
| from api.v1 import search, system | |
| from core.config import settings | |
| from core.exceptions import setup_exception_handlers | |
| from core.logger import setup_logger | |
| from models.embedder import TextEmbedder | |
| from models.reranker import TextReranker | |
| from services.search_service import HybridSearchService | |
| from storage.qdrant_client import QdrantStorage | |
| from storage.sqlite_client import SQLiteStorage | |
| logger = setup_logger("knowledge_engine") | |
| async def lifespan(app: FastAPI): | |
| """ | |
| FastAPI Lifespan function to manage startup and shutdown events. | |
| On startup, it initializes all necessary components (DB connections, models, services) and injects them into the app state. | |
| On shutdown, it ensures that all resources are properly cleaned up (e.g., closing DB connections). | |
| - This approach centralizes all initialization logic in one place, making it easier to manage dependencies and handle errors during startup. | |
| - If any critical error occurs during startup, it logs the error and prevents the server from starting in an unstable state. | |
| """ | |
| logger.info("π Starting Knowledge Engine API...") | |
| qdrant_client = None | |
| sqlite_client = None | |
| try: | |
| # 1. Infrastructure Connection (Database) | |
| qdrant_client = QdrantStorage(url=settings.QDRANT_URL, collection_name=settings.QDRANT_COLLECTION) | |
| sqlite_client = SQLiteStorage(db_path=settings.SQLITE_PATH) | |
| # 2. Load AI Model (Singleton) | |
| embedder = TextEmbedder(model_name=settings.EMBEDDER_NAME, use_fp16=True) | |
| reranker = TextReranker(model_name=settings.RERANKER_NAME) | |
| # 3. Business Service Orchestration (Instantiate the HybridSearchService with all dependencies) | |
| search_service = HybridSearchService( | |
| qdrant=qdrant_client, | |
| sqlite=sqlite_client, | |
| embedder=embedder, | |
| reranker=reranker | |
| ) | |
| # 4. Injecting services into FastAPI app state for global accessibility in routers | |
| app.state.search_service = search_service | |
| logger.info("β All services and models initialized successfully.") | |
| yield # --- From this point, the server starts receiving traffic --- | |
| except Exception as e: | |
| logger.critical(f"β Application failed to start: {e}", exc_info=True) | |
| raise e | |
| finally: | |
| logger.info("π Shutting down. Cleaning up resources...") | |
| # Safe termination of DB connections, etc. | |
| if qdrant_client is not None: qdrant_client.close() | |
| if sqlite_client is not None: sqlite_client.close() | |
| logger.info("Resources cleaned up.") | |
| # --------------------------- | |
| # FastAPI Instance Creation | |
| # --------------------------- | |
| app = FastAPI( | |
| title="Hybrid RAG Knowledge Engine API", | |
| description="Qdrant and BGE-M3-based high-performance hybrid search engine API", | |
| version="0.1.0", | |
| lifespan=lifespan | |
| ) | |
| # CORS Setup | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Mount static files (CSS, JS, etc.) if needed (e.g., for demo pages) | |
| # app.mount("/static", StaticFiles(directory="static"), name="static") | |
| # --------------------------- | |
| # Router Registration | |
| # --------------------------- | |
| app.include_router(system.router, prefix="/api/v1") | |
| app.include_router(search.router, prefix="/api/v1") | |
| async def root(): | |
| return RedirectResponse(url="/api/v1/search/demo") | |
| # ----------------------------------- | |
| # Register global exception handlers | |
| # ----------------------------------- | |
| setup_exception_handlers(app) |