Skip to content

Commit c58b43d

Browse files
committed
feat(node,pdf): replace installDependencies() with scripts/download_dependencies.sh
installDependencies() duplicated what a plain bash script does better: fetch pdfium + the ONNX models straight into ./models and ./.pdfium, which both the Rust CLI and the Node bindings already default to relative to CWD. Removes the JS download machinery entirely (deps.js keeps only path resolution + status checks), deletes the now-pointless setup_nodejs_dependencies.sh wrapper, and adds the CWD-relative .pdfium/lib fallback to pdfium_backend.rs so the Rust side needs no env vars either. publish-models.yml now also re-hosts pdfium + the OCR model (previously only layout/TableFormer), so download_dependencies.sh talks to a single release. TableFormer assets are published under their bare names (encoder.onnx, decoder.onnx, bbox.onnx) instead of a tableformer- prefix, matching the script's fixed target paths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DofkqhMuAJbL9arnnuVisL
1 parent 326095b commit c58b43d

12 files changed

Lines changed: 284 additions & 471 deletions

File tree

.github/workflows/publish-models.yml

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
# Export the RT-DETR layout model + TableFormer to ONNX and publish them as
2-
# GitHub Release assets, so `installDependencies()` in the `fleischwolf` npm
3-
# package (crates/fleischwolf-node/deps.js) can fetch them with zero
4-
# configuration instead of every user needing a local torch toolchain.
1+
# Export the RT-DETR layout model + TableFormer to ONNX, re-host pdfium + the
2+
# OCR model, and publish everything as GitHub Release assets, so
3+
# `scripts/download_dependencies.sh` can fetch the whole PDF/image ML stack
4+
# from a single host (this repo's releases) with zero configuration.
55
#
6-
# Both are PyTorch→ONNX *format conversions* of docling-project's own models
7-
# (Apache-2.0 / CDLA-Permissive-2.0 — see MODELS_NOTICE.md); no weights are
8-
# retrained or modified here.
6+
# The layout model and TableFormer are PyTorch→ONNX *format conversions* of
7+
# docling-project's own models (Apache-2.0 / CDLA-Permissive-2.0 — see
8+
# MODELS_NOTICE.md); no weights are retrained or modified here. pdfium and the
9+
# OCR model are re-hosted third-party binaries, unmodified, from their own
10+
# public releases — mirrored here only so `download_dependencies.sh` needs one
11+
# host instead of three.
912
#
1013
# Trigger: manual only (workflow_dispatch) — it installs torch and exports two
1114
# models, several GB and 10-20 minutes, not something to run on every push.
@@ -62,13 +65,26 @@ jobs:
6265
id: tf-export
6366
run: python scripts/export_tableformer.py models/tableformer
6467

68+
# --- pdfium + OCR (re-hosted third-party binaries, not exports) -----
69+
- name: Fetch pdfium + OCR assets
70+
run: |
71+
mkdir -p third-party
72+
curl -fsSL -o /tmp/pdfium.tgz \
73+
"https://github.com/bblanchon/pdfium-binaries/releases/latest/download/pdfium-linux-x64.tgz"
74+
tar -xzf /tmp/pdfium.tgz -C third-party
75+
curl -fsSL -o third-party/ocr_rec.onnx \
76+
"https://huggingface.co/SWHL/RapidOCR/resolve/main/PP-OCRv3/ch_PP-OCRv3_rec_infer.onnx"
77+
curl -fsSL -o third-party/ppocr_keys_v1.txt \
78+
"https://raw.githubusercontent.com/PaddlePaddle/PaddleOCR/main/ppocr/utils/ppocr_keys_v1.txt"
79+
6580
# --- stage + publish -------------------------------------------------
6681
# GitHub release assets can't contain "/", so tableformer/*.onnx is
67-
# flattened to tableformer-*.onnx (deps.js downloads the matching flat
68-
# names by default). Each file is staged only if the export produced it,
69-
# including the optional `.onnx.data` external-weights sidecar some
70-
# exports need above ONNX's ~2GB protobuf limit (currently just the
71-
# TableFormer decoder, but checked for every file since that's
82+
# flattened to bare encoder.onnx / decoder.onnx / bbox.onnx (no naming
83+
# collision with anything else in this release; download_dependencies.sh
84+
# downloads these exact names). Each file is staged only if the export
85+
# produced it, including the optional `.onnx.data` external-weights
86+
# sidecar some exports need above ONNX's ~2GB protobuf limit (currently
87+
# just the TableFormer decoder, but checked for every file since that's
7288
# export-size dependent, not fixed).
7389
- name: Stage release assets
7490
run: |
@@ -77,10 +93,13 @@ jobs:
7793
[ -f "$1" ] && cp "$1" "release-assets/$2"
7894
[ -f "$1.data" ] && cp "$1.data" "release-assets/$2.data"
7995
}
96+
stage third-party/lib/libpdfium.so libpdfium.so
97+
stage third-party/ocr_rec.onnx ocr_rec.onnx
98+
stage third-party/ppocr_keys_v1.txt ppocr_keys_v1.txt
8099
stage models/layout_heron.onnx layout_heron.onnx
81-
stage models/tableformer/encoder.onnx tableformer-encoder.onnx
82-
stage models/tableformer/decoder.onnx tableformer-decoder.onnx
83-
stage models/tableformer/bbox.onnx tableformer-bbox.onnx
100+
stage models/tableformer/encoder.onnx encoder.onnx
101+
stage models/tableformer/decoder.onnx decoder.onnx
102+
stage models/tableformer/bbox.onnx bbox.onnx
84103
echo "staged:"
85104
ls -la release-assets/
86105

MODELS_NOTICE.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ code (see [`LICENSE`](./LICENSE)) under their upstream terms:
1313
`scripts/export_layout.py` and `scripts/export_tableformer.py` do the
1414
conversion (PyTorch → ONNX via `torch.onnx.export`); no weights are retrained,
1515
fine-tuned, or otherwise altered. `.github/workflows/publish-models.yml` runs
16-
that conversion and republishes the resulting `.onnx` files as GitHub Release
17-
assets on this repo (tag `models-v1`, downloaded automatically by
18-
`installDependencies()` in the `fleischwolf` npm package — see
19-
`crates/fleischwolf-node/deps.js`), purely to spare downstream users the
16+
that conversion (and re-hosts pdfium + the OCR model alongside it) and
17+
publishes everything as GitHub Release assets on this repo (tag `models-v1`),
18+
fetched by `scripts/download_dependencies.sh` — see that script and
19+
`crates/fleischwolf-node/deps.js` purely to spare downstream users the
2020
PyTorch/`transformers`/`docling_ibm_models` toolchain needed to export them
2121
locally.
2222

README.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,15 +192,15 @@ const json = await convertFileAsync('report.docx', { to: 'json' })
192192

193193
Declarative formats (Markdown, HTML, DOCX, XLSX, …) work out of the box. The
194194
PDF/image pipeline needs pdfium + the ONNX models (not bundled), so it throws
195-
until you call `installDependencies()`which downloads everything with zero
196-
configuration (pdfium and OCR from their own upstream releases; the layout
197-
model and TableFormer — PyTorch→ONNX exports of docling-project's own models,
195+
until you fetch thema one-liner from your app's directory (pdfium and OCR
196+
from their own upstream releases; the layout model and TableFormer —
197+
PyTorch→ONNX exports of docling-project's own models,
198198
Apache-2.0/CDLA-Permissive-2.0, see [`MODELS_NOTICE.md`](./MODELS_NOTICE.md)
199-
from fleischwolf's own hosted release). Pre-fetch everything ahead of time
200-
(e.g. in a container build step) with a one-liner from your app's directory:
199+
from fleischwolf's own hosted release), straight into `./models` and
200+
`./.pdfium`, which the package looks for by default — no env vars needed:
201201

202202
```bash
203-
curl -fsSL https://raw.githubusercontent.com/artiz/fleischwolf/master/scripts/setup_nodejs_dependencies.sh | bash
203+
curl -fsSL https://raw.githubusercontent.com/artiz/fleischwolf/master/scripts/download_dependencies.sh | sh
204204
```
205205

206206
A reusable `Pipeline` keeps those models warm across many PDFs.
@@ -268,6 +268,11 @@ cargo run -p fleischwolf-cli -- --strict crates/fleischwolf/sample.html
268268
cargo run -p fleischwolf-cli -- --to json crates/fleischwolf/sample.html
269269
cargo run -p fleischwolf-cli -- --to json crates/fleischwolf/sample.html > out.json
270270

271+
# PDF/image conversion needs the ML models: scripts/download_dependencies.sh once,
272+
# then it just works — models/ and .pdfium/lib are picked up automatically.
273+
scripts/download_dependencies.sh
274+
cargo run -p fleischwolf-cli -- document.pdf
275+
271276
# extract pictures (PDF/image inputs): embed as data URIs, or write ./artifacts/*.png
272277
cargo run -p fleischwolf-cli -- --images embedded document.pdf
273278
cargo run -p fleischwolf-cli -- --images referenced document.pdf > out.md

crates/fleischwolf-node/README.md

Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -93,61 +93,58 @@ for await (const chunk of streamFileMarkdown('paper.pdf')) {
9393
}
9494
```
9595

96-
### PDF / images: installing the ML models
96+
### PDF / images: getting the ML models
9797

9898
Declarative formats (Markdown, HTML, DOCX, XLSX, …) are pure Rust and need
9999
nothing. The **PDF/image** path needs native assets that are *not* bundled in the
100-
addon — pdfium plus the ONNX models (layout, OCR, TableFormer) — the same way
101-
Python docling downloads its models on first use. Converting a PDF/image/METS
102-
input **throws** until they're installed:
100+
addon — pdfium plus the ONNX models (layout, OCR, TableFormer). Converting a
101+
PDF/image/METS input **throws** until they're on disk. Fetch them with a
102+
one-liner from your app's directory (where you'll `npm install fleischwolf`):
103103

104-
```js
105-
import { installDependencies, checkDependencies, convertFileAsync } from 'fleischwolf'
104+
```bash
105+
curl -fsSL https://raw.githubusercontent.com/artiz/fleischwolf/master/scripts/download_dependencies.sh | sh
106+
```
106107

107-
await convertFileAsync('paper.pdf') // ❌ throws: "requires the PDF/ML dependencies … call installDependencies()"
108+
```js
109+
import { convertFileAsync } from 'fleischwolf'
108110

109-
await installDependencies() // provisions everything, then:
110-
await convertFileAsync('paper.pdf') // ✅ works
111+
const res = await convertFileAsync('paper.pdf', { to: 'markdown' }) // ✅ works
111112
```
112113

113-
What `installDependencies()` fetches, into `~/.cache/fleischwolf` (override with
114-
`dir` or `$FLEISCHWOLF_HOME`), wiring the matching `DOCLING_*` /
115-
`PDFIUM_DYNAMIC_LIB_PATH` env vars in-process:
114+
`scripts/download_dependencies.sh` fetches everything from this repo's
115+
[GitHub Releases](https://github.com/artiz/fleischwolf/releases) straight into
116+
`./models` and `./.pdfium` — which this package (and the Rust CLI) look for by
117+
default, relative to the process's current directory, so no env vars or setup
118+
call are needed afterwards:
116119

117-
| Asset | Source | Required for |
118-
| --- | --- | --- |
119-
| **pdfium** | bblanchon prebuilt (auto, platform-detected) | PDF |
120-
| **OCR** rec model + dictionary | HuggingFace / GitHub (auto) | scanned pages |
121-
| **layout** (`layout_heron.onnx`) | fleischwolf's hosted release (auto) | PDF **and** image |
122-
| **TableFormer** (`tableformer-*.onnx`) | fleischwolf's hosted release (auto) | tables (else geometric fallback) |
120+
| Asset | Destination |
121+
| --- | --- |
122+
| **pdfium** | `.pdfium/lib/libpdfium.so` |
123+
| **layout** (`layout_heron.onnx`) | `models/layout_heron.onnx` |
124+
| **OCR** rec model + dictionary | `models/ocr_rec.onnx`, `models/ppocr_keys_v1.txt` |
125+
| **TableFormer** | `models/tableformer/{encoder,decoder,bbox}.onnx` |
123126

124127
> **layout + TableFormer are PyTorch→ONNX exports**
125128
> (`docling-project/docling-layout-heron`, Apache-2.0;
126129
> `docling-project/docling-models`, CDLA-Permissive-2.0/Apache-2.0 — see
127130
> [`MODELS_NOTICE.md`](../../MODELS_NOTICE.md) for full attribution), not
128-
> fleischwolf's own weights. `installDependencies()` fetches the export
129-
> fleischwolf hosts as a GitHub Release by default — no configuration needed.
130-
> Override with `{ modelsUrl }` / `FLEISCHWOLF_MODELS_URL` to use your own
131-
> export/host instead (serving `layout_heron.onnx` and, optionally,
132-
> `tableformer-{encoder,decoder,bbox}.onnx`), or export locally (repo
133-
> `scripts/export_layout.py`, `scripts/export_tableformer.py`) and set
134-
> `DOCLING_LAYOUT_ONNX` / `DOCLING_TABLEFORMER_*` directly —
135-
> `installDependencies` detects those as already installed and skips fetching.
131+
> fleischwolf's own weights — fleischwolf hosts the converted `.onnx` as a
132+
> GitHub Release purely so you don't need a local Python/torch toolchain.
133+
> pdfium and the OCR model are re-hosted, unmodified, from their own public
134+
> releases, on the same host for convenience.
136135
>
137-
> Pre-fetch everything ahead of time (e.g. in a container build step), from
138-
> your app's directory:
136+
> Run it from wherever your app lives — the script only writes to `./models`
137+
> and `./.pdfium` under the current directory, e.g. in a container build step:
139138
> ```bash
140-
> curl -fsSL https://raw.githubusercontent.com/artiz/fleischwolf/master/scripts/setup_nodejs_dependencies.sh | bash
139+
> cd /path/to/your/app && curl -fsSL https://raw.githubusercontent.com/artiz/fleischwolf/master/scripts/download_dependencies.sh | sh
141140
> ```
141+
>
142+
> To use your own export/host instead, point the env vars at it directly:
143+
> `DOCLING_LAYOUT_ONNX`, `DOCLING_OCR_REC_ONNX`, `DOCLING_OCR_DICT`,
144+
> `DOCLING_TABLEFORMER_{ENCODER,DECODER,BBOX}`, `PDFIUM_DYNAMIC_LIB_PATH` — an
145+
> env var always wins over the `./models` / `./.pdfium` default.
142146
143147
```js
144-
await installDependencies({ onProgress: (m) => console.log(m) })
145-
146-
// or, to use your own export/host instead of fleischwolf's default:
147-
await installDependencies({
148-
modelsUrl: 'https://you.example/fleischwolf-models', // serves layout_heron.onnx, tableformer-*.onnx
149-
})
150-
151148
checkDependencies() // { home, pdfium, layout, ocr, tableformer, ready, missing }
152149
```
153150
@@ -199,7 +196,6 @@ JSON output always embeds extracted images as data URIs.
199196
| `streamFileMarkdown(path, options?)` | `AsyncGenerator<string>` | Markdown chunks in document order. |
200197
| `supportedFormats()` | `string[]` | Supported input format ids. |
201198
| `formatFromName(name)` | `string \| null` | Detect a format id from a filename/extension. |
202-
| `installDependencies(options?)` | `Promise<DependencyStatus>` | Download/validate the PDF/image models + pdfium. |
203199
| `checkDependencies(options?)` | `DependencyStatus` | Report which PDF/image deps are present. |
204200
205201
`Pipeline` is the reusable warm PDF/image converter: `new Pipeline(converterOptions)`
@@ -246,12 +242,12 @@ cd examples
246242
npm install
247243
node node-basic.mjs # ESM: file, bytes, JSON, reuse
248244
bun run bun-basic.ts # Bun + TypeScript: async + streaming
249-
node pdf-pipeline.mjs # installDependencies + warm Pipeline for PDFs
245+
node pdf-pipeline.mjs # warm Pipeline for PDFs (run scripts/download_dependencies.sh first)
250246
```
251247
252248
- [`examples/node-basic.mjs`](examples/node-basic.mjs) — Node.js (ESM): file, bytes, JSON, reuse.
253249
- [`examples/bun-basic.ts`](examples/bun-basic.ts) — Bun + TypeScript, with async and streaming.
254-
- [`examples/pdf-pipeline.mjs`](examples/pdf-pipeline.mjs) — `installDependencies` + warm `Pipeline` for PDFs.
250+
- [`examples/pdf-pipeline.mjs`](examples/pdf-pipeline.mjs) — warm `Pipeline` for PDFs.
255251
256252
The smoke test exercises the locally-built addon instead: `npm run build` once at
257253
the package root, then `node test/smoke.mjs` (or `bun test/smoke.mjs`).

0 commit comments

Comments
 (0)