feat: require offline PaddleOCR model assets

This commit is contained in:
2026-09-25 11:40:42 +08:00
parent 1dddceefc7
commit 1605ae6410
5 changed files with 34 additions and 5 deletions
+6 -1
View File
@@ -6,7 +6,7 @@ from docx import Document
from pptx import Presentation
from pptx.util import Inches
from wisegrade.parsing import parse_document
from wisegrade.parsing import _ocr_engine, parse_document
def test_pdf_pages_and_images(tmp_path):
@@ -20,6 +20,11 @@ def test_pdf_pages_and_images(tmp_path):
assert (tmp_path / "assets" / "page-1.png").exists()
def test_scanned_pdf_requires_offline_ocr_package(tmp_path):
with pytest.raises(RuntimeError, match="离线OCR模型目录"):
_ocr_engine(str(tmp_path / "missing-ocr"))
@pytest.mark.skipif(not shutil.which("libreoffice"), reason="LibreOffice is installed in the deployment image")
def test_docx_and_pptx_render(tmp_path):
word = Document()
+1
View File
@@ -19,6 +19,7 @@ class Settings(BaseSettings):
embedding_model_name: str = "BAAI/bge-m3"
embedding_model_path: Path = Path("/models/bge-m3")
embedding_model_version: str = "local"
ocr_model_dir: Path = Path("/models/ocr")
model_timeout_seconds: int = 120
model_concurrency: int = 2
model_retries: int = 2
+25 -3
View File
@@ -1,11 +1,18 @@
import subprocess
import tempfile
from functools import lru_cache
from pathlib import Path
from threading import Lock
import pymupdf as fitz
from docx import Document
from pptx import Presentation
from .config import get_settings
_ocr_lock = Lock()
def _render(page, output: Path):
output.parent.mkdir(parents=True, exist_ok=True)
@@ -13,15 +20,30 @@ def _render(page, output: Path):
return str(output)
def _ocr(image_path: str):
@lru_cache(maxsize=1)
def _ocr_engine(model_dir: str):
root = Path(model_dir)
detection, recognition = root / "detection", root / "recognition"
if not detection.is_dir() or not recognition.is_dir():
raise RuntimeError(f"扫描PDF需要离线OCR模型目录: {detection} 和 {recognition}")
try:
from paddleocr import PaddleOCR
except ImportError as exc:
raise RuntimeError("扫描PDF需要PaddleOCR;请安装OCR依赖或交由教师处理") from exc
try:
engine = PaddleOCR(lang="ch", use_textline_orientation=True)
return PaddleOCR(lang="ch", text_detection_model_dir=str(detection), text_recognition_model_dir=str(recognition),
use_textline_orientation=False, use_doc_orientation_classify=False, use_doc_unwarping=False)
except TypeError:
engine = PaddleOCR(use_angle_cls=True, lang="ch", show_log=False)
return PaddleOCR(lang="ch", det_model_dir=str(detection), rec_model_dir=str(recognition), use_angle_cls=False, show_log=False)
def _ocr(image_path: str):
engine = _ocr_engine(str(get_settings().ocr_model_dir))
with _ocr_lock:
return _ocr_unlocked(engine, image_path)
def _ocr_unlocked(engine, image_path: str):
if hasattr(engine, "predict"):
result = engine.predict(image_path)
lines = []