mindnet/app/main.py

134 lines
4.6 KiB
Python

"""
FILE: app/main.py
DESCRIPTION: Bootstrap der FastAPI Anwendung für WP-25a (Agentic MoE).
Orchestriert Lifespan-Events, globale Fehlerbehandlung und Routing.
Prüft beim Start die Integrität der Mixture of Experts Konfiguration.
VERSION: 1.1.0 (WP-25a: MoE Integrity Check)
STATUS: Active
DEPENDENCIES: app.config, app.routers.*, app.services.llm_service
"""
from __future__ import annotations
import logging
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from .config import get_settings
from .services.llm_service import LLMService
# Import der Router
from .routers.query import router as query_router
from .routers.graph import router as graph_router
from .routers.tools import router as tools_router
from .routers.feedback import router as feedback_router
from .routers.chat import router as chat_router
from .routers.ingest import router as ingest_router
try:
from .routers.admin import router as admin_router
except Exception:
admin_router = None
from .core.logging_setup import setup_logging
# Initialisierung des Loggings noch VOR create_app()
setup_logging()
logger = logging.getLogger(__name__)
# --- WP-25a: Lifespan Management mit MoE Integritäts-Prüfung ---
@asynccontextmanager
async def lifespan(app: FastAPI):
"""
Verwaltet den Lebenszyklus der Anwendung (Startup/Shutdown).
Verifiziert die Verfügbarkeit der MoE-Experten-Profile und Strategien.
"""
settings = get_settings()
logger.info("🚀 mindnet API: Starting up (WP-25a MoE Mode)...")
# 1. Startup: Integritäts-Check der MoE Konfiguration
# Wir prüfen die drei Säulen der Agentic-RAG Architektur.
decision_cfg = os.getenv("MINDNET_DECISION_CONFIG", "config/decision_engine.yaml")
profiles_cfg = getattr(settings, "LLM_PROFILES_PATH", "config/llm_profiles.yaml")
prompts_cfg = settings.PROMPTS_PATH
missing_files = []
if not os.path.exists(decision_cfg): missing_files.append(decision_cfg)
if not os.path.exists(profiles_cfg): missing_files.append(profiles_cfg)
if not os.path.exists(prompts_cfg): missing_files.append(prompts_cfg)
if missing_files:
logger.error(f"❌ CRITICAL: Missing MoE config files: {missing_files}")
else:
logger.info("✅ MoE Configuration files verified.")
yield
# 2. Shutdown: Ressourcen bereinigen
logger.info("🛑 mindnet API: Shutting down...")
try:
llm = LLMService()
await llm.close()
logger.info("✨ LLM resources cleaned up.")
except Exception as e:
logger.warning(f"⚠️ Error during LLMService cleanup: {e}")
logger.info("Goodbye.")
# --- App Factory ---
def create_app() -> FastAPI:
"""Initialisiert die FastAPI App mit WP-25a Erweiterungen."""
app = FastAPI(
title="mindnet API",
version="1.1.0", # WP-25a Milestone
lifespan=lifespan,
description="Digital Twin Knowledge Engine mit Mixture of Experts Orchestration."
)
s = get_settings()
# --- Globale Fehlerbehandlung (WP-25a Resilienz) ---
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
"""Fängt unerwartete Fehler in der MoE-Prozesskette ab."""
logger.error(f"❌ Unhandled Engine Error: {exc}", exc_info=True)
return JSONResponse(
status_code=500,
content={
"detail": "Ein interner Fehler ist in der MoE-Kette aufgetreten.",
"error_type": type(exc).__name__
}
)
# Healthcheck
@app.get("/healthz")
def healthz():
"""Bietet Statusinformationen über die Engine und Datenbank-Verbindung."""
return {
"status": "ok",
"version": "1.1.0",
"qdrant": s.QDRANT_URL,
"prefix": s.COLLECTION_PREFIX,
"moe_enabled": True
}
# Inkludieren der Router (100% Kompatibilität erhalten)
app.include_router(query_router, prefix="/query", tags=["query"])
app.include_router(graph_router, prefix="/graph", tags=["graph"])
app.include_router(tools_router, prefix="/tools", tags=["tools"])
app.include_router(feedback_router, prefix="/feedback", tags=["feedback"])
app.include_router(chat_router, prefix="/chat", tags=["chat"]) # WP-25a Agentic Chat
app.include_router(ingest_router, prefix="/ingest", tags=["ingest"])
if admin_router:
app.include_router(admin_router, prefix="/admin", tags=["admin"])
return app
# Instanziierung der App
app = create_app()