my_voice_assistant_v3_jamulix/app/main.py

63 lines
2.5 KiB
Python
Raw Normal View History

import time
from pathlib import Path
from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from app.metrics import metrics
from app.api.health import router as health_router
from app.api.chat import router as chat_router
from app.api.transcribe import router as transcribe_router
from app.api.speak import router as speak_router
from app.api.devices import router as devices_router
from app.api.sessions import router as sessions_router
from app.api.config import router as config_router
from app.api.admin import router as admin_router
from app.api.me import router as me_router
from app.api.metrics import router as metrics_router
from app.api.ws import router as ws_router
app = FastAPI(title="Voice Assistant Gateway")
@app.middleware("http")
async def record_metrics(request: Request, call_next):
start = time.perf_counter()
response = await call_next(request)
duration = time.perf_counter() - start
# Route-Template (z. B. /api/sessions/{session_id}/route) statt konkreter URL,
# um die Label-Kardinalitaet niedrig zu halten.
route = request.scope.get("route")
path = getattr(route, "path", request.url.path)
labels = {"method": request.method, "path": path}
metrics.inc("http_requests_total", {**labels, "status": response.status_code})
metrics.observe("http_request_duration_seconds", duration, labels)
return response
app.include_router(health_router)
app.include_router(chat_router, prefix="/api")
app.include_router(transcribe_router, prefix="/api")
app.include_router(speak_router, prefix="/api")
app.include_router(devices_router, prefix="/api")
app.include_router(sessions_router, prefix="/api")
app.include_router(config_router, prefix="/api")
app.include_router(admin_router, prefix="/api")
app.include_router(me_router, prefix="/api")
app.include_router(metrics_router, prefix="/api")
app.include_router(ws_router)
# Statische Web-UI (same-origin -> kein CORS). Muss NACH allen API-/WS-Routen
# gemountet werden, damit "/" nur die uebrigen Pfade abfaengt.
class _NoCacheStaticFiles(StaticFiles):
"""Liefert die UI ohne Caching aus -> Aenderungen sind sofort sichtbar (kein Safari-Cache)."""
def file_response(self, *args, **kwargs):
response = super().file_response(*args, **kwargs)
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
return response
_WEB_DIR = Path(__file__).resolve().parent / "web"
if _WEB_DIR.is_dir():
app.mount("/", _NoCacheStaticFiles(directory=str(_WEB_DIR), html=True), name="web")