docling.rs 0.32.0 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -4
- package/index.d.ts +25 -0
- package/index.js +81 -43
- package/native.d.ts +21 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -159,12 +159,22 @@ import { Pipeline } from 'docling.rs'
|
|
|
159
159
|
|
|
160
160
|
const pipeline = new Pipeline({ strict: true })
|
|
161
161
|
for (const path of pdfPaths) {
|
|
162
|
-
const { content } = pipeline.
|
|
162
|
+
const { content } = await pipeline.convertFileAsync(path, { to: 'json' }) // warm models, off the event loop
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Or stream a PDF's Markdown as pages finish converting:
|
|
166
|
+
for await (const chunk of pipeline.streamFileMarkdown('paper.pdf')) {
|
|
167
|
+
process.stdout.write(chunk)
|
|
163
168
|
}
|
|
164
169
|
```
|
|
165
170
|
|
|
166
|
-
`Pipeline` handles `pdf` and `image` inputs (the ML pipeline)
|
|
167
|
-
|
|
171
|
+
`Pipeline` handles `pdf` and `image` inputs (the ML pipeline). The sync
|
|
172
|
+
`convertFile` / `convert` block the event loop; the `*Async` variants run on the
|
|
173
|
+
libuv thread pool, and `streamFileMarkdown` yields Markdown chunks in document
|
|
174
|
+
order as pages finish. Conversions on one instance run one at a time (the
|
|
175
|
+
models are mutable sessions) — overlapping `*Async` calls queue in submission
|
|
176
|
+
order, so batch throughput comes from keeping the models warm, not from
|
|
177
|
+
parallel calls.
|
|
168
178
|
|
|
169
179
|
### Images
|
|
170
180
|
|
|
@@ -199,7 +209,8 @@ JSON output always embeds extracted images as data URIs.
|
|
|
199
209
|
| `checkDependencies(options?)` | `DependencyStatus` | Report which PDF/image deps are present. |
|
|
200
210
|
|
|
201
211
|
`Pipeline` is the reusable warm PDF/image converter: `new Pipeline(converterOptions)`
|
|
202
|
-
then `convertFile` / `convert
|
|
212
|
+
then `convertFile` / `convert` / `convertFileAsync` / `convertAsync` /
|
|
213
|
+
`convertFileStreaming` / `streamFileMarkdown`.
|
|
203
214
|
|
|
204
215
|
`DocumentConverter` is the reusable form: `new DocumentConverter(converterOptions)`
|
|
205
216
|
then `convert` / `convertFile` / `convertFileAsync` / `convertAsync` /
|
package/index.d.ts
CHANGED
|
@@ -37,15 +37,40 @@ export declare class DocumentConverter {
|
|
|
37
37
|
convertFileStreaming(path: string, callback: StreamCallback, options?: OutputOptions | null): void
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/** Output options for {@link Pipeline.streamFileMarkdown} (streamable modes only). */
|
|
41
|
+
export interface PipelineStreamOptions {
|
|
42
|
+
imageMode?: 'placeholder' | 'embedded'
|
|
43
|
+
artifactsDir?: string
|
|
44
|
+
}
|
|
45
|
+
|
|
40
46
|
/**
|
|
41
47
|
* A reusable PDF/image pipeline that keeps the ONNX models loaded across calls.
|
|
42
48
|
* Use instead of the per-call functions when converting many PDFs/images — the
|
|
43
49
|
* one-shot path reloads every model each call. Handles `pdf` and `image` inputs.
|
|
50
|
+
*
|
|
51
|
+
* The `*Async` variants run the conversion off the event loop; overlapping
|
|
52
|
+
* calls on one instance queue (the models are mutable sessions), so batch
|
|
53
|
+
* throughput comes from keeping the models warm, not from parallel calls.
|
|
44
54
|
*/
|
|
45
55
|
export declare class Pipeline {
|
|
46
56
|
constructor(options?: ConverterOptions | null)
|
|
47
57
|
convertFile(path: string, options?: OutputOptions | null): ConvertResult
|
|
48
58
|
convert(input: ConvertInput, options?: OutputOptions | null): ConvertResult
|
|
59
|
+
/** Async (Promise) file conversion on the warm pipeline, off the event loop. */
|
|
60
|
+
convertFileAsync(path: string, options?: OutputOptions | null): Promise<ConvertResult>
|
|
61
|
+
/** Async (Promise) bytes conversion on the warm pipeline, off the event loop. */
|
|
62
|
+
convertAsync(input: ConvertInput, options?: OutputOptions | null): Promise<ConvertResult>
|
|
63
|
+
/** Callback-form streaming (prefer {@link Pipeline.streamFileMarkdown}). */
|
|
64
|
+
convertFileStreaming(path: string, callback: StreamCallback, options?: OutputOptions | null): void
|
|
65
|
+
/**
|
|
66
|
+
* Stream a PDF's Markdown in chunks through the warm pipeline, in document
|
|
67
|
+
* order, as pages finish converting (an image arrives as a single chunk).
|
|
68
|
+
* Concatenating the chunks reproduces the buffered Markdown byte-for-byte.
|
|
69
|
+
*/
|
|
70
|
+
streamFileMarkdown(
|
|
71
|
+
filePath: string,
|
|
72
|
+
options?: PipelineStreamOptions,
|
|
73
|
+
): AsyncGenerator<string, void, unknown>
|
|
49
74
|
}
|
|
50
75
|
|
|
51
76
|
// --- dependency provisioning (PDF/image ML pipeline) -----------------------
|
package/index.js
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
// 1. dependency guards — converting a PDF/image/METS input throws a clear
|
|
6
6
|
// error unless the ML models + pdfium are on disk (see
|
|
7
7
|
// scripts/download_dependencies.sh);
|
|
8
|
-
// 2.
|
|
8
|
+
// 2. `streamFileMarkdown` async generators over Markdown chunks (module-level
|
|
9
|
+
// and on the warm `Pipeline`).
|
|
9
10
|
//
|
|
10
11
|
// Works in Node.js and Bun (Bun implements N-API).
|
|
11
12
|
|
|
@@ -23,6 +24,48 @@ function mlFormatOf(name, format) {
|
|
|
23
24
|
return native.formatFromName(name || '') || ''
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
// Bridge a native (err, chunk) streaming callback into an async generator.
|
|
28
|
+
// `start` receives the callback and kicks off the native conversion. Chunks
|
|
29
|
+
// are delivered on the event loop (via a threadsafe function); a null chunk
|
|
30
|
+
// ends the stream, a non-null err ends it with a throw.
|
|
31
|
+
async function* chunkStream(start) {
|
|
32
|
+
const queue = []
|
|
33
|
+
let done = false
|
|
34
|
+
let failure = null
|
|
35
|
+
let notify = null
|
|
36
|
+
const wake = () => {
|
|
37
|
+
if (notify) {
|
|
38
|
+
const n = notify
|
|
39
|
+
notify = null
|
|
40
|
+
n()
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
start((err, chunk) => {
|
|
45
|
+
if (err) {
|
|
46
|
+
failure = err
|
|
47
|
+
done = true
|
|
48
|
+
} else if (chunk === null || chunk === undefined) {
|
|
49
|
+
done = true
|
|
50
|
+
} else {
|
|
51
|
+
queue.push(chunk)
|
|
52
|
+
}
|
|
53
|
+
wake()
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
while (true) {
|
|
57
|
+
if (queue.length > 0) {
|
|
58
|
+
yield queue.shift()
|
|
59
|
+
continue
|
|
60
|
+
}
|
|
61
|
+
if (failure) throw failure
|
|
62
|
+
if (done) return
|
|
63
|
+
await new Promise((resolve) => {
|
|
64
|
+
notify = resolve
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
26
69
|
// --- guarded one-shot functions --------------------------------------------
|
|
27
70
|
|
|
28
71
|
function convertFile(path, options) {
|
|
@@ -94,6 +137,41 @@ class Pipeline {
|
|
|
94
137
|
assertMlReady(mlFormatOf(input && input.name, input && input.format))
|
|
95
138
|
return this._inner.convert(input, options)
|
|
96
139
|
}
|
|
140
|
+
|
|
141
|
+
// async so a guard failure surfaces as a rejected promise, not a sync throw.
|
|
142
|
+
async convertFileAsync(path, options) {
|
|
143
|
+
assertMlReady(mlFormatOf(path))
|
|
144
|
+
return this._inner.convertFileAsync(path, options)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async convertAsync(input, options) {
|
|
148
|
+
assertMlReady(mlFormatOf(input && input.name, input && input.format))
|
|
149
|
+
return this._inner.convertAsync(input, options)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
convertFileStreaming(path, callback, options) {
|
|
153
|
+
assertMlReady(mlFormatOf(path))
|
|
154
|
+
return this._inner.convertFileStreaming(path, callback, options)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Stream a PDF's Markdown in chunks through the warm pipeline, in document
|
|
159
|
+
* order, as pages finish converting (an image arrives as a single chunk).
|
|
160
|
+
* Same contract as the module-level `streamFileMarkdown`, but reusing this
|
|
161
|
+
* instance's loaded models — no per-call model reload.
|
|
162
|
+
*
|
|
163
|
+
* @param {string} filePath
|
|
164
|
+
* @param {object} [options] output options (`imageMode`: `placeholder` or
|
|
165
|
+
* `embedded`; `referenced` is rejected)
|
|
166
|
+
* @returns {AsyncGenerator<string, void, unknown>}
|
|
167
|
+
*/
|
|
168
|
+
async *streamFileMarkdown(filePath, options = {}) {
|
|
169
|
+
assertMlReady(mlFormatOf(filePath))
|
|
170
|
+
const { imageMode, artifactsDir } = options
|
|
171
|
+
yield* chunkStream((callback) =>
|
|
172
|
+
this._inner.convertFileStreaming(filePath, callback, { imageMode, artifactsDir }),
|
|
173
|
+
)
|
|
174
|
+
}
|
|
97
175
|
}
|
|
98
176
|
|
|
99
177
|
// --- streaming --------------------------------------------------------------
|
|
@@ -113,49 +191,9 @@ async function* streamFileMarkdown(filePath, options = {}) {
|
|
|
113
191
|
assertMlReady(mlFormatOf(filePath))
|
|
114
192
|
const { strict, fetchImages, allowedFormats, imageMode, artifactsDir } = options
|
|
115
193
|
const converter = new native.DocumentConverter({ strict, fetchImages, allowedFormats })
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// delivered on the event loop (via a threadsafe function); a null chunk ends
|
|
119
|
-
// the stream, a non-null err ends it with a throw.
|
|
120
|
-
const queue = []
|
|
121
|
-
let done = false
|
|
122
|
-
let failure = null
|
|
123
|
-
let notify = null
|
|
124
|
-
const wake = () => {
|
|
125
|
-
if (notify) {
|
|
126
|
-
const n = notify
|
|
127
|
-
notify = null
|
|
128
|
-
n()
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
converter.convertFileStreaming(
|
|
133
|
-
filePath,
|
|
134
|
-
(err, chunk) => {
|
|
135
|
-
if (err) {
|
|
136
|
-
failure = err
|
|
137
|
-
done = true
|
|
138
|
-
} else if (chunk === null || chunk === undefined) {
|
|
139
|
-
done = true
|
|
140
|
-
} else {
|
|
141
|
-
queue.push(chunk)
|
|
142
|
-
}
|
|
143
|
-
wake()
|
|
144
|
-
},
|
|
145
|
-
{ imageMode, artifactsDir },
|
|
194
|
+
yield* chunkStream((callback) =>
|
|
195
|
+
converter.convertFileStreaming(filePath, callback, { imageMode, artifactsDir }),
|
|
146
196
|
)
|
|
147
|
-
|
|
148
|
-
while (true) {
|
|
149
|
-
if (queue.length > 0) {
|
|
150
|
-
yield queue.shift()
|
|
151
|
-
continue
|
|
152
|
-
}
|
|
153
|
-
if (failure) throw failure
|
|
154
|
-
if (done) return
|
|
155
|
-
await new Promise((resolve) => {
|
|
156
|
-
notify = resolve
|
|
157
|
-
})
|
|
158
|
-
}
|
|
159
197
|
}
|
|
160
198
|
|
|
161
199
|
// --- exports (explicit, so ESM named imports work in Node and Bun) ----------
|
package/native.d.ts
CHANGED
|
@@ -154,4 +154,25 @@ export declare class Pipeline {
|
|
|
154
154
|
convertFile(path: string, options?: OutputOptions | undefined | null): ConvertResult
|
|
155
155
|
/** Convert PDF or image bytes, reusing the warm models. */
|
|
156
156
|
convert(input: ConvertInput, options?: OutputOptions | undefined | null): ConvertResult
|
|
157
|
+
/**
|
|
158
|
+
* Async (Promise-returning) file conversion on the warm pipeline. The
|
|
159
|
+
* CPU-bound work runs on the libuv thread pool, keeping the event loop
|
|
160
|
+
* free; calls on the same instance run one at a time (the models are
|
|
161
|
+
* mutable sessions), so overlapping Promises queue in submission order.
|
|
162
|
+
*/
|
|
163
|
+
convertFileAsync(path: string, options?: OutputOptions | undefined | null): Promise<ConvertResult>
|
|
164
|
+
/** Async (Promise-returning) bytes conversion on the warm pipeline. */
|
|
165
|
+
convertAsync(input: ConvertInput, options?: OutputOptions | undefined | null): Promise<ConvertResult>
|
|
166
|
+
/**
|
|
167
|
+
* Stream a PDF's Markdown in chunks through the warm pipeline, in document
|
|
168
|
+
* order, as pages finish converting (an image converts in one step and
|
|
169
|
+
* arrives as a single chunk).
|
|
170
|
+
*
|
|
171
|
+
* `callback` is invoked as `(err, chunk)`: once per Markdown chunk with
|
|
172
|
+
* `chunk` a string, once with `chunk === null` at the end, or once with a
|
|
173
|
+
* non-null `err` on failure. Only `placeholder` / `embedded` image modes
|
|
174
|
+
* stream; `referenced` is rejected. Prefer the `streamFileMarkdown`
|
|
175
|
+
* async-generator wrapper in JS over calling this directly.
|
|
176
|
+
*/
|
|
177
|
+
convertFileStreaming(path: string, callback: (err: Error | null, arg: string | undefined | null) => any, options?: OutputOptions | undefined | null): void
|
|
157
178
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docling.rs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"description": "Node.js / Bun bindings for docling.rs — a Rust port of docling. Convert Markdown, HTML, DOCX, PPTX, XLSX, PDF, images and more into a unified DoclingDocument (Markdown or docling-core JSON).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"docling",
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"@napi-rs/cli": "^2.18.4"
|
|
65
65
|
},
|
|
66
66
|
"optionalDependencies": {
|
|
67
|
-
"docling.rs-linux-x64-gnu": "0.
|
|
68
|
-
"docling.rs-linux-arm64-gnu": "0.
|
|
69
|
-
"docling.rs-win32-x64-msvc": "0.
|
|
67
|
+
"docling.rs-linux-x64-gnu": "0.33.0",
|
|
68
|
+
"docling.rs-linux-arm64-gnu": "0.33.0",
|
|
69
|
+
"docling.rs-win32-x64-msvc": "0.33.0"
|
|
70
70
|
}
|
|
71
71
|
}
|