From 2bc0f8732538b3286e995d8d9151ceae2609863d Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Thu, 12 Mar 2026 13:12:14 +0100 Subject: [PATCH] fix: PaddleOCR model pre-load at startup + 5min healthcheck grace 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 --- docker-compose.coolify.yml | 4 ++-- paddleocr-service/main.py | 26 +++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/docker-compose.coolify.yml b/docker-compose.coolify.yml index 9c9afc7..3c685a2 100644 --- a/docker-compose.coolify.yml +++ b/docker-compose.coolify.yml @@ -164,8 +164,8 @@ services: test: ["CMD", "curl", "-f", "http://127.0.0.1:8095/health"] interval: 30s timeout: 10s - start_period: 120s - retries: 3 + start_period: 300s + retries: 5 restart: unless-stopped networks: - breakpilot-network diff --git a/paddleocr-service/main.py b/paddleocr-service/main.py index 7c1d581..b025f87 100644 --- a/paddleocr-service/main.py +++ b/paddleocr-service/main.py @@ -1,15 +1,20 @@ """PaddleOCR Remote Service — PP-OCRv5 Latin auf x86_64.""" import io +import logging import os import numpy as np from fastapi import FastAPI, File, Header, HTTPException, UploadFile from PIL import Image +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + app = FastAPI(title="PaddleOCR Service") _engine = None +_ready = False API_KEY = os.environ.get("PADDLEOCR_API_KEY", "") @@ -18,6 +23,7 @@ def get_engine(): if _engine is None: from paddleocr import PaddleOCR + logger.info("Loading PaddleOCR model (first time may download)...") _engine = PaddleOCR( lang="en", text_recognition_model_name="latin_PP-OCRv5_mobile_rec", @@ -25,12 +31,27 @@ def get_engine(): use_doc_unwarping=False, use_textline_orientation=False, ) + logger.info("PaddleOCR model loaded successfully") 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") def health(): - return {"status": "ok", "model": "PP-OCRv5-latin"} + if _ready: + return {"status": "ok", "model": "PP-OCRv5-latin"} + return {"status": "loading"} @app.post("/ocr") @@ -41,6 +62,9 @@ async def ocr( if API_KEY and x_api_key != 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 = Image.open(io.BytesIO(img_bytes)).convert("RGB") img_np = np.array(img)