-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathDockerfile
More file actions
194 lines (175 loc) · 10.2 KB
/
Copy pathDockerfile
File metadata and controls
194 lines (175 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# syntax=docker/dockerfile:1
#
# Example deployment image for docling.rs (the Rust docling port) with the full
# PDF ML pipeline — pdfium rasterization + RT-DETR layout + TableFormer table
# structure (hoisted-KV cached decoder, #97) + PP-OCRv3 — plus the picture
# classifier and the hybrid-chunker tokenizer. Builds a slim runtime with the
# `docling-rs` CLI, the native libs, and all models baked in, so a container can
# convert PDFs/images offline with no Python at runtime.
#
# Build from the repository root:
# docker build -f examples/Dockerfile -t docling-rs .
# Convert a document (Markdown to stdout):
# docker run --rm -v "$PWD:/data" docling-rs /data/input.pdf
#
# Optimizations baked in by default (see docs/PDF_CONFORMANCE.md for the measured
# numbers; every one is byte-exact on the snapshot corpus):
# * layout INT8 — static Conv-only quantization calibrated on the repo's PDF
# corpus, ~2.4x faster layout inference (`--build-arg INT8=0` for fp32;
# both precisions ship in the image, switchable per container via
# `-e DOCLING_LAYOUT_ONNX=/opt/models/layout_heron.onnx`)
# * TableFormer hoisted-KV decoder (fp32) — one-token-per-step decode with
# per-layer cross-attention tensors computed once per table; preferred
# over the quantized legacy decoder on every machine measured, so the
# legacy TF INT8 file is deliberately not built (see quantize_models.py)
# * codegen-units=1 on top of the workspace's lto="thin"
# * optional `--build-arg TARGET_CPU=x86-64-v3` (or `native` for same-host
# deploys): lets the compiler use AVX2+ in the image-processing hot paths;
# the default stays portable to any x86-64
#
# Stages:
# models — export the layout + TableFormer ONNX (torch, one-time), fetch
# the OCR model/dict, picture classifier, chunker tokenizer and
# pdfium, INT8-quantize the layout model (unless INT8=0)
# builder — compile the CLI (the `ort` crate downloads ONNX Runtime at build)
# runtime — slim image with just the binary, native libs and models
# ----------------------------------------------------------------------------
# Stage 1: ML models + native libs (Python only here, never at runtime)
# ----------------------------------------------------------------------------
FROM python:3.12-slim AS models
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl ca-certificates tar \
&& rm -rf /var/lib/apt/lists/*
# Install order matters and mirrors the (proven) publish-models workflow:
# the layout export needs transformers 5.x module names for its pos-embed
# fold-and-splice, while docling-ibm-models pins transformers <5 — so the
# layout model is exported BEFORE the TableFormer deps downgrade transformers.
RUN pip install --no-cache-dir --extra-index-url https://download.pytorch.org/whl/cpu \
torch "transformers>=5" "onnx>=1.16,<2"
WORKDIR /build
COPY scripts/install ./scripts/install
# Real corpus pages for INT8 calibration (build-stage only, not in the runtime).
COPY tests/data/pdf/sources ./tests/data/pdf/sources
COPY tests/data/scanned/sources ./tests/data/scanned/sources
# RT-DETR layout detector -> models/layout_heron.onnx. The trailing cache
# cleanup keeps the multi-GB HuggingFace downloads out of the layer.
RUN mkdir -p /opt/models/chunk /opt/pdfium \
&& python scripts/install/export_layout.py /opt/models/layout_heron.onnx \
&& rm -rf /root/.cache/huggingface
# TableFormer export + quantization/calibration deps (downgrades transformers
# to 4.x — the remaining exports don't import it).
RUN pip install --no-cache-dir --extra-index-url https://download.pytorch.org/whl/cpu \
docling-ibm-models onnxscript onnxruntime huggingface_hub \
opencv-python-headless sympy pypdfium2 pillow numpy
RUN true \
# TableFormer encoder/decoder/bbox. Exports both decoder generations: the
# #97 hoisted-KV step graph (decoder_kv.onnx — per-layer cross-attention
# K/V hoisted into the encoder, byte-exact vs docling and the fastest
# variant on every machine measured) and the legacy layer-output-cache
# decoder.onnx as a fallback. The artifacts dir auto-resolves (downloads
# docling-project/docling-models if absent).
&& python scripts/install/export_tableformer.py /opt/models/tableformer \
# PP-OCRv3 recognition model + dictionary (for scanned pages)
&& curl -fsSL -o /opt/models/ocr_rec.onnx \
"https://huggingface.co/SWHL/RapidOCR/resolve/main/PP-OCRv3/ch_PP-OCRv3_rec_infer.onnx" \
&& curl -fsSL -o /opt/models/ppocr_keys_v1.txt \
"https://raw.githubusercontent.com/PaddlePaddle/PaddleOCR/main/ppocr/utils/ppocr_keys_v1.txt" \
# DocumentFigureClassifier-v2.5 (--enrich-picture-classes)
&& curl -fsSL -o /opt/models/picture_classifier.onnx \
"https://huggingface.co/docling-project/DocumentFigureClassifier-v2.5/resolve/main/model.onnx" \
# all-MiniLM-L6-v2 tokenizer — the hybrid chunker works out of the box
&& curl -fsSL -o /opt/models/chunk/tokenizer.json \
"https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2/resolve/main/tokenizer.json" \
# pdfium prebuilt (page rasterization only)
&& curl -fsSL -o /tmp/pdfium.tgz \
"https://github.com/bblanchon/pdfium-binaries/releases/latest/download/pdfium-linux-x64.tgz" \
&& tar xzf /tmp/pdfium.tgz -C /opt/pdfium \
&& rm /tmp/pdfium.tgz \
&& rm -rf /root/.cache/huggingface
# INT8=1 (default): quantize the layout model (static, Conv-only, calibrated on
# the corpus copied above) and point the `layout_heron_active.onnx` name — which
# the runtime ENV references — at the int8 file. INT8=0 keeps it on fp32. Both
# precisions ship in the image either way.
#
# Only `layout` is quantized: the TableFormer decoder the runtime uses is the
# hoisted-KV fp32 graph, which outperforms the quantized legacy decoder AND is
# byte-exact — a freshly quantized TF decoder can also flip near-tie tokens on
# heavy tables (see quantize_models.py), so it has no place in this image.
ARG INT8=1
RUN if [ "$INT8" = "1" ]; then \
DOCLING_RS_MODELS_DIR=/opt/models python scripts/install/quantize_models.py layout \
&& ln -s layout_heron_int8.onnx /opt/models/layout_heron_active.onnx; \
else \
ln -s layout_heron.onnx /opt/models/layout_heron_active.onnx; \
fi
# ----------------------------------------------------------------------------
# Stage 2: Rust build
# ----------------------------------------------------------------------------
FROM rust:1-slim-trixie AS builder
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential cmake perl pkg-config ca-certificates \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# TARGET_CPU: empty (default) = portable x86-64 image; "x86-64-v3" = require
# AVX2 (2013+ CPUs, most cloud fleets); "native" = tune for the build host
# (only for images that run where they're built). Affects the Rust-side image
# processing hot paths (resize, normalization); ONNX Runtime ships its own
# runtime CPU dispatch either way.
ARG TARGET_CPU=""
ENV CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1
COPY Cargo.toml Cargo.lock ./
COPY crates ./crates
# --features serve compiles in the `docling-rs serve` subcommand (the HTTP
# conversion API), so one image covers both CLI and server use. The committed
# Cargo.lock pins the dependency graph (--locked), so the image builds the
# exact versions CI tested — release.sh keeps the lock in sync on version
# bumps.
RUN if [ -n "$TARGET_CPU" ]; then export RUSTFLAGS="-C target-cpu=$TARGET_CPU"; fi \
&& cargo build --release -p docling-cli --features serve --locked
# Collect the binary + the ONNX Runtime shared lib `ort` downloaded during build.
RUN mkdir -p /artifacts \
&& cp target/release/docling-rs /artifacts/ \
&& find target -name 'libonnxruntime*.so*' -exec cp -av {} /artifacts/ \; || true
# ----------------------------------------------------------------------------
# Stage 3: runtime (no Python, no toolchain)
# ----------------------------------------------------------------------------
FROM debian:trixie-slim AS runtime
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates libstdc++6 libgomp1 \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /artifacts/ /tmp/artifacts/
RUN cp /tmp/artifacts/docling-rs /usr/local/bin/ \
&& (cp /tmp/artifacts/libonnxruntime*.so* /usr/local/lib/ 2>/dev/null || true) \
&& rm -rf /tmp/artifacts \
&& ldconfig
COPY --from=models /opt/models /opt/models
COPY --from=models /opt/pdfium /opt/pdfium
# The picture classifier has no env override — it resolves models/… relative
# to the CWD, the binary's dir, or the binary's parent. The binary lives in
# /usr/local/bin, so this symlink makes /opt/models visible from any CWD.
RUN ln -s /opt/models /usr/local/models
# The pipeline finds its native lib + models via these env vars (set explicitly
# so the binary works from any working directory).
# * layout_heron_active.onnx is a symlink resolved by the models stage's INT8
# arg (int8 by default, fp32 with --build-arg INT8=0); both concrete files
# are present, so a container can pin either precision explicitly, e.g.
# `-e DOCLING_LAYOUT_ONNX=/opt/models/layout_heron.onnx` for fp32.
# * The TableFormer decoder is pinned to the hoisted-KV fp32 graph (#97);
# `-e DOCLING_TABLEFORMER_DECODER=/opt/models/tableformer/decoder.onnx`
# switches to the legacy graph (both are auto-detected by input names).
ENV PDFIUM_DYNAMIC_LIB_PATH=/opt/pdfium/lib \
DOCLING_LAYOUT_ONNX=/opt/models/layout_heron_active.onnx \
DOCLING_OCR_REC_ONNX=/opt/models/ocr_rec.onnx \
DOCLING_OCR_DICT=/opt/models/ppocr_keys_v1.txt \
DOCLING_TABLEFORMER_ENCODER=/opt/models/tableformer/encoder.onnx \
DOCLING_TABLEFORMER_DECODER=/opt/models/tableformer/decoder_kv.onnx \
DOCLING_TABLEFORMER_BBOX=/opt/models/tableformer/bbox.onnx \
DOCLING_CHUNK_TOKENIZER=/opt/models/chunk/tokenizer.json \
LD_LIBRARY_PATH=/usr/local/lib:/opt/pdfium/lib
# Default: read a file arg and print Markdown to stdout, e.g.
# docker run --rm -v "$PWD:/data" docling-rs /data/input.pdf --to json
# The HTTP conversion API is compiled in as well:
# docker run --rm -p 5001:5001 docling-rs serve --addr 0.0.0.0:5001
EXPOSE 5001
ENTRYPOINT ["docling-rs"]