fix: PaddleOCR model pre-load at startup + 5min healthcheck grace
Some checks failed
Deploy to Coolify / deploy (push) Has been cancelled

Model wird beim Container-Start geladen (nicht erst beim ersten Request).
Health-Check start_period auf 300s erhoeht fuer initialen Download.
/health gibt "loading" zurueck bis Modell bereit ist.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-12 13:12:14 +01:00
parent 4ee38d6f0b
commit 2bc0f87325
2 changed files with 27 additions and 3 deletions

View File

@@ -164,8 +164,8 @@ services:
test: ["CMD", "curl", "-f", "http://127.0.0.1:8095/health"] test: ["CMD", "curl", "-f", "http://127.0.0.1:8095/health"]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
start_period: 120s start_period: 300s
retries: 3 retries: 5
restart: unless-stopped restart: unless-stopped
networks: networks:
- breakpilot-network - breakpilot-network

View File

@@ -1,15 +1,20 @@
"""PaddleOCR Remote Service — PP-OCRv5 Latin auf x86_64.""" """PaddleOCR Remote Service — PP-OCRv5 Latin auf x86_64."""
import io import io
import logging
import os import os
import numpy as np import numpy as np
from fastapi import FastAPI, File, Header, HTTPException, UploadFile from fastapi import FastAPI, File, Header, HTTPException, UploadFile
from PIL import Image from PIL import Image
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI(title="PaddleOCR Service") app = FastAPI(title="PaddleOCR Service")
_engine = None _engine = None
_ready = False
API_KEY = os.environ.get("PADDLEOCR_API_KEY", "") API_KEY = os.environ.get("PADDLEOCR_API_KEY", "")
@@ -18,6 +23,7 @@ def get_engine():
if _engine is None: if _engine is None:
from paddleocr import PaddleOCR from paddleocr import PaddleOCR
logger.info("Loading PaddleOCR model (first time may download)...")
_engine = PaddleOCR( _engine = PaddleOCR(
lang="en", lang="en",
text_recognition_model_name="latin_PP-OCRv5_mobile_rec", text_recognition_model_name="latin_PP-OCRv5_mobile_rec",
@@ -25,12 +31,27 @@ def get_engine():
use_doc_unwarping=False, use_doc_unwarping=False,
use_textline_orientation=False, use_textline_orientation=False,
) )
logger.info("PaddleOCR model loaded successfully")
return _engine return _engine
@app.on_event("startup")
def startup_load_model():
"""Pre-load model at startup so health check passes."""
global _ready
try:
get_engine()
_ready = True
logger.info("PaddleOCR ready to serve requests")
except Exception as e:
logger.error(f"Failed to load PaddleOCR model: {e}")
@app.get("/health") @app.get("/health")
def health(): def health():
return {"status": "ok", "model": "PP-OCRv5-latin"} if _ready:
return {"status": "ok", "model": "PP-OCRv5-latin"}
return {"status": "loading"}
@app.post("/ocr") @app.post("/ocr")
@@ -41,6 +62,9 @@ async def ocr(
if API_KEY and x_api_key != API_KEY: if API_KEY and x_api_key != API_KEY:
raise HTTPException(status_code=401, detail="Invalid API key") raise HTTPException(status_code=401, detail="Invalid API key")
if not _ready:
raise HTTPException(status_code=503, detail="Model still loading")
img_bytes = await file.read() img_bytes = await file.read()
img = Image.open(io.BytesIO(img_bytes)).convert("RGB") img = Image.open(io.BytesIO(img_bytes)).convert("RGB")
img_np = np.array(img) img_np = np.array(img)