"""Health/observability endpoints (ยง15).""" from __future__ import annotations from fastapi import APIRouter, Depends from sqlalchemy import text from sqlalchemy.ext.asyncio import AsyncSession import gogo from gogo.db import get_session router = APIRouter() @router.get("/health") async def health(): return {"status": "ok", "version": gogo.__version__} @router.get("/health/db") async def health_db(session: AsyncSession = Depends(get_session)): await session.execute(text("SELECT 1")) return {"status": "ok"} @router.get("/health/voice") async def health_voice(): """Voice pipeline liveness via its heartbeat file (shared volume).""" import os import time from gogo.voice.server import heartbeat_path path = heartbeat_path() try: age = time.time() - os.path.getmtime(path) except OSError: return {"status": "down", "detail": "no heartbeat file"} if age > 60: return {"status": "down", "detail": f"heartbeat {int(age)}s old"} return {"status": "ok", "heartbeat_age_s": int(age)}