Files
pdf/gateway/app/routers/health.py
T

69 lines
2.1 KiB
Python

"""Liveness / readiness probes.
`/health` must work with **zero** engine dependencies — it is the signal load
balancers and orchestrators use to decide whether the process is up. It
deliberately does not import or touch the pybind11 bridge.
"""
from typing import Annotated
from fastapi import APIRouter, Depends
from pydantic import BaseModel
from app import __version__
from app.config import Settings, get_settings
from app.services import engine as engine_service
router = APIRouter(tags=["health"])
SettingsDep = Annotated[Settings, Depends(get_settings)]
class HealthResponse(BaseModel):
status: str
version: str
environment: str
engine_available: bool
# What this process can actually do. Every model path in the converter is
# fail-open, so a deploy that forgot the weights serves happily with
# Arabic recognition off and no page renderer. Without this an operator
# has to read startup logs to find that out — usually after a customer does.
capabilities: dict[str, bool] = {}
def _capabilities() -> dict[str, bool]:
"""Probe each optional capability, never raising: /health must always answer."""
caps: dict[str, bool] = {}
try:
from app.services import raster
caps["page_raster"] = raster.is_available()
except Exception:
caps["page_raster"] = False
try:
from app.services import ocr
caps["ocr"] = ocr.is_ocr_available()
caps["ocr_arabic"] = ocr.is_ocr_arabic_available()
except Exception:
caps["ocr"] = False
caps["ocr_arabic"] = False
try:
from app.services.convert.layout import ml_regions
caps["layout_ml"] = ml_regions.layout_ml_enabled() and ml_regions._weights_path().is_file()
except Exception:
caps["layout_ml"] = False
return caps
@router.get("/health", response_model=HealthResponse)
def health(settings: SettingsDep) -> HealthResponse:
return HealthResponse(
status="ok",
version=__version__,
environment=settings.environment,
engine_available=engine_service.is_available() or settings.engine_available,
capabilities=_capabilities(),
)