FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim ARG GBABELDOCUI_REF=main ARG BABELDOC_VERSION=0.6.3 ARG PYMUPDF_MIN_VERSION=1.26.7 ENV PDF2ZH_WEB_UI=1 \ PYTHONUNBUFFERED=1 \ GRADIO_SERVER_NAME=0.0.0.0 \ GRADIO_SERVER_PORT=7860 \ BABELDOC_VERSION=${BABELDOC_VERSION} \ PYMUPDF_MIN_VERSION=${PYMUPDF_MIN_VERSION} WORKDIR /app EXPOSE 7860 RUN apt-get update && \ apt-get install --no-install-recommends -y \ ca-certificates \ curl \ build-essential \ libgl1 \ libglib2.0-0 \ libxext6 \ libsm6 \ libxrender1 && \ rm -rf /var/lib/apt/lists/* # Keep the GBabelDocUI frontend, but rebuild it from source so the BabelDOC # runtime is not trapped inside the stale eaiu/gbabeldocwebui:latest image. RUN curl -fsSL "https://github.com/eaiu/GBabelDocUI/archive/${GBABELDOCUI_REF}.tar.gz" \ -o /tmp/gbabeldocui.tar.gz && \ tar -xzf /tmp/gbabeldocui.tar.gz --strip-components=1 -C /app && \ rm /tmp/gbabeldocui.tar.gz # GBabelDocUI still declares the old BabelDOC 0.5 line. Patch only the package # constraints, then let the normal project install build the same UI entrypoints. RUN python - <<'PY' from pathlib import Path import os pyproject = Path("pyproject.toml") text = pyproject.read_text() replacements = { '"pymupdf<1.25.3"': f'"pymupdf>={os.environ["PYMUPDF_MIN_VERSION"]}"', '"babeldoc>=0.5.20,<0.6.0"': f'"babeldoc=={os.environ["BABELDOC_VERSION"]}"', } for old, new in replacements.items(): if old not in text: raise SystemExit(f"Expected dependency constraint not found: {old}") text = text.replace(old, new) pyproject.write_text(text) PY RUN uv pip install --system --no-cache --compile-bytecode . # Build-time proof: fail the Space build if the deployed kernel is not the # requested BabelDOC line or if PyMuPDF is still on the old incompatible line. RUN python - <<'PY' from importlib.metadata import version, PackageNotFoundError import os def parse_version(value: str) -> tuple[int, ...]: parts = [] for token in value.replace('-', '.').split('.'): if token.isdigit(): parts.append(int(token)) else: digits = ''.join(ch for ch in token if ch.isdigit()) if digits: parts.append(int(digits)) break return tuple(parts) required = os.environ["BABELDOC_VERSION"] actual = version("babeldoc") if actual != required: raise SystemExit(f"BabelDOC mismatch: expected {required}, got {actual}") pymupdf_actual = version("pymupdf") pymupdf_min = os.environ["PYMUPDF_MIN_VERSION"] if parse_version(pymupdf_actual) < parse_version(pymupdf_min): raise SystemExit(f"PyMuPDF too old: expected >= {pymupdf_min}, got {pymupdf_actual}") for pkg in ["pdf2zh-gbabeldocui", "babeldoc", "pymupdf", "cryptography", "onnx"]: try: print(f"{pkg}=={version(pkg)}") except PackageNotFoundError: print(f"{pkg}=not-installed") PY RUN babeldoc --version && babeldoc --warmup && pdf2zh --version CMD ["pdf2zh", "--gui"]