@heripo/pdf-parser 0.1.29 → 0.1.30
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.ko.md +158 -85
- package/README.md +158 -86
- package/dist/index.cjs +7819 -5826
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -45
- package/dist/index.d.ts +45 -45
- package/dist/index.js +7826 -5831
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
- [Prerequisites](#prerequisites)
|
|
21
21
|
- [Installation](#installation)
|
|
22
22
|
- [Usage](#usage)
|
|
23
|
-
- [
|
|
23
|
+
- [Mandatory Correction Pipeline](#mandatory-correction-pipeline)
|
|
24
24
|
- [Review Assistance](#review-assistance)
|
|
25
25
|
- [Document Type Validation](#document-type-validation)
|
|
26
26
|
- [Large PDF Chunked Conversion](#large-pdf-chunked-conversion)
|
|
@@ -36,15 +36,16 @@
|
|
|
36
36
|
|
|
37
37
|
## Key Features
|
|
38
38
|
|
|
39
|
-
- **
|
|
40
|
-
- **
|
|
39
|
+
- **Fixed ocrmac OCR**: Docling conversion always uses ocrmac / Apple Vision Framework
|
|
40
|
+
- **Mandatory VLM Correction**: Post-Docling correction always runs with text correction, page gating, and structural review models
|
|
41
41
|
- **Apple Silicon Optimized**: GPU acceleration on M1/M2/M3/M4/M5 chips
|
|
42
42
|
- **Automatic Environment Setup**: Automatic Python virtual environment and docling-serve installation
|
|
43
43
|
- **Image Extraction**: Automatic extraction and saving of images from PDFs
|
|
44
44
|
- **Document Type Validation**: Optional LLM-based validation that a PDF is an archaeological report
|
|
45
45
|
- **Chunked Conversion**: Split large PDFs into chunks for reliable processing
|
|
46
46
|
- **Image PDF Fallback**: Automatic fallback to image-based PDF when conversion fails
|
|
47
|
-
- **Review Assistance**:
|
|
47
|
+
- **Review Assistance**: Page-level VLM review writes audit proposals, skips low-value pages with observable reasons, and can auto-apply high-confidence fixes
|
|
48
|
+
- **Table Correction**: Table-specific work items validate cells, spans, headers, units, footnotes, and adjacent-page continuations
|
|
48
49
|
- **AbortSignal Support**: Cancel ongoing parsing operations
|
|
49
50
|
- **Server Crash Recovery**: Automatic restart of docling-serve on ECONNREFUSED
|
|
50
51
|
|
|
@@ -84,7 +85,7 @@ python3.11 --version
|
|
|
84
85
|
|
|
85
86
|
#### 4. poppler (PDF text extraction)
|
|
86
87
|
|
|
87
|
-
Required for PDF page counting (`pdfinfo`) and text layer extraction (`pdftotext`), used
|
|
88
|
+
Required for PDF page counting (`pdfinfo`) and text layer extraction (`pdftotext`), used as a post-Docling correction reference.
|
|
88
89
|
|
|
89
90
|
```bash
|
|
90
91
|
brew install poppler
|
|
@@ -140,6 +141,7 @@ yarn add @heripo/pdf-parser @heripo/logger
|
|
|
140
141
|
### Basic Usage
|
|
141
142
|
|
|
142
143
|
```typescript
|
|
144
|
+
import { openai } from '@ai-sdk/openai';
|
|
143
145
|
import { Logger } from '@heripo/logger';
|
|
144
146
|
import { PDFParser } from '@heripo/pdf-parser';
|
|
145
147
|
|
|
@@ -156,6 +158,8 @@ const pdfParser = new PDFParser({
|
|
|
156
158
|
logger,
|
|
157
159
|
});
|
|
158
160
|
|
|
161
|
+
const correctionModel = openai('gpt-5.1');
|
|
162
|
+
|
|
159
163
|
// Initialize (environment setup and start docling-serve)
|
|
160
164
|
await pdfParser.init();
|
|
161
165
|
|
|
@@ -168,7 +172,15 @@ const tokenUsageReport = await pdfParser.parse(
|
|
|
168
172
|
console.log('PDF conversion complete:', outputPath);
|
|
169
173
|
},
|
|
170
174
|
false, // cleanupAfterCallback
|
|
171
|
-
{
|
|
175
|
+
{
|
|
176
|
+
correction: {
|
|
177
|
+
models: {
|
|
178
|
+
textCorrection: correctionModel,
|
|
179
|
+
pageGate: correctionModel,
|
|
180
|
+
reviewAssistance: correctionModel,
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
}, // PDFConvertOptions
|
|
172
184
|
);
|
|
173
185
|
|
|
174
186
|
// Token usage report (null when no LLM usage)
|
|
@@ -201,23 +213,41 @@ const tokenUsageReport = await pdfParser.parse(
|
|
|
201
213
|
async (outputPath) => console.log(outputPath),
|
|
202
214
|
false,
|
|
203
215
|
{
|
|
204
|
-
//
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
216
|
+
// Mandatory post-Docling correction
|
|
217
|
+
correction: {
|
|
218
|
+
models: {
|
|
219
|
+
textCorrection: openai('gpt-5.1'),
|
|
220
|
+
pageGate: openai('gpt-5.1'),
|
|
221
|
+
reviewAssistance: openai('gpt-5.1'),
|
|
222
|
+
tableCorrection: openai('gpt-5.1'),
|
|
223
|
+
reviewAssistanceTasks: {
|
|
224
|
+
text_ocr_hanja: openai('gpt-5.1'),
|
|
225
|
+
tables: openai('gpt-5.1'),
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
concurrency: {
|
|
229
|
+
pages: 1,
|
|
230
|
+
reviewTasks: 4,
|
|
231
|
+
tables: 1,
|
|
232
|
+
},
|
|
233
|
+
localModelConcurrency: 1,
|
|
234
|
+
workItemTimeoutMs: 900000,
|
|
235
|
+
maxRetries: {
|
|
236
|
+
textCorrection: 3,
|
|
237
|
+
pageGate: 3,
|
|
238
|
+
reviewAssistance: 3,
|
|
239
|
+
tableCorrection: 3,
|
|
240
|
+
},
|
|
215
241
|
autoApplyThreshold: 0.85,
|
|
216
242
|
proposalThreshold: 0.5,
|
|
243
|
+
temperature: 0,
|
|
217
244
|
outputLanguage: 'en-US',
|
|
218
245
|
},
|
|
219
246
|
onReviewAssistanceProgress: (event) => console.log(event),
|
|
220
247
|
|
|
248
|
+
// Document validation
|
|
249
|
+
documentValidationModel: openai('gpt-5.1'),
|
|
250
|
+
|
|
221
251
|
// Chunked conversion for large PDFs
|
|
222
252
|
chunkedConversion: true,
|
|
223
253
|
chunkSize: 50,
|
|
@@ -244,58 +274,52 @@ Clean up resources after work is complete:
|
|
|
244
274
|
await pdfParser.dispose();
|
|
245
275
|
```
|
|
246
276
|
|
|
247
|
-
##
|
|
277
|
+
## Mandatory Correction Pipeline
|
|
248
278
|
|
|
249
|
-
### Why
|
|
279
|
+
### Why ocrmac Is Fixed
|
|
250
280
|
|
|
251
281
|
**ocrmac (Apple Vision Framework) is an excellent OCR engine** -- it's free, GPU-accelerated, and delivers high-quality results. For processing thousands to millions of archaeological reports, there's no better solution.
|
|
252
282
|
|
|
253
|
-
|
|
283
|
+
`@heripo/pdf-parser` no longer samples OCR strategies or switches to a VLM OCR path. Docling conversion always uses ocrmac. VLMs are used only after Docling conversion as a mandatory correction stage.
|
|
254
284
|
|
|
255
|
-
###
|
|
285
|
+
### Required Correction Contract
|
|
256
286
|
|
|
257
|
-
|
|
258
|
-
2. **VLM Sampling** (only when needed): Samples up to 15 pages (trimming 10% from front/back to skip covers and appendices) and analyzes them with a Vision LLM. Uses early exit when `ko-KR` is detected.
|
|
287
|
+
Every `parse()` call must provide `correction.models.textCorrection`, `correction.models.pageGate`, and `correction.models.reviewAssistance`. If any required correction model is missing, parsing fails before conversion callback wrapping.
|
|
259
288
|
|
|
260
|
-
|
|
289
|
+
The correction stage runs in this order:
|
|
261
290
|
|
|
262
|
-
|
|
291
|
+
1. Save `result_ocr_origin.json` before mutations.
|
|
292
|
+
2. Run page-level text and table-cell OCR correction with `textCorrection`.
|
|
293
|
+
3. Run the Review Assistance page gate with `pageGate` and write `review_assistance_page_gate.json`.
|
|
294
|
+
4. Run structural Review Assistance work items with task-specific models.
|
|
295
|
+
5. Run table-specific correction work items for each detected table.
|
|
296
|
+
6. Write `review_assistance_checkpoint.json` during execution and `review_assistance.json` at the end.
|
|
263
297
|
|
|
264
|
-
|
|
265
|
-
- Uses `pdftotext` reference text as a quality anchor
|
|
266
|
-
- VLM returns substitution-based corrections (find -> replace)
|
|
267
|
-
- Failed page corrections are gracefully skipped, preserving original OCR text
|
|
298
|
+
Text correction applies to every page with text or table content. The page gate only controls structural Review Assistance noise; it does not disable OCR text correction.
|
|
268
299
|
|
|
269
|
-
###
|
|
300
|
+
### Local Model Execution
|
|
270
301
|
|
|
271
|
-
|
|
272
|
-
const tokenUsageReport = await pdfParser.parse(
|
|
273
|
-
'file:///path/to/input.pdf',
|
|
274
|
-
'report-001',
|
|
275
|
-
async (outputPath) => console.log(outputPath),
|
|
276
|
-
false,
|
|
277
|
-
{
|
|
278
|
-
// Enable OCR strategy sampling (provide a Vision LLM model)
|
|
279
|
-
strategySamplerModel: openai('gpt-5.1'),
|
|
302
|
+
The correction pipeline is optimized for local VLMs: small contexts, many calls, deterministic validation, retry loops, bounded concurrency, and resumable checkpoints. For local models, start with `concurrency.pages: 1`, `concurrency.tables: 1`, `localModelConcurrency: 1`, `temperature: 0`, and a generous `workItemTimeoutMs`. Increase concurrency only after the model is stable.
|
|
280
303
|
|
|
281
|
-
|
|
282
|
-
vlmProcessorModel: openai('gpt-5.1'),
|
|
304
|
+
### Rollout Smoke Test
|
|
283
305
|
|
|
284
|
-
|
|
285
|
-
vlmConcurrency: 3,
|
|
306
|
+
Repository contributors can run the correction rollout smoke test against two local demo archaeological report artifacts:
|
|
286
307
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
},
|
|
290
|
-
);
|
|
308
|
+
```bash
|
|
309
|
+
pnpm --filter @heripo/pdf-parser smoke:correction
|
|
291
310
|
```
|
|
292
311
|
|
|
312
|
+
The smoke test copies existing demo artifacts into `/private/tmp/heripo-pdf-parser-correction-smoke`, runs correction with a deterministic local fake model, verifies `review_assistance.json`, table work-item traces, validation status, and checkpoint resume behavior. It exercises pipeline mechanics; semantic table quality still depends on the configured real local VLM.
|
|
313
|
+
|
|
293
314
|
## Review Assistance
|
|
294
315
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
316
|
+
Review Assistance always runs after text correction, but it does not process every page with the same intensity. The page gate marks covers, chapter covers, barcode/ISBN pages, and decorative pages as low-value for structural review. Skipped pages remain observable in `review_assistance.json` with an info issue and skip reason.
|
|
317
|
+
|
|
318
|
+
Eligible pages are split into small work items for text OCR/Hanja review, text integrity, text role/footnote review, tables, pictures/captions, layout/bbox/order, and table-specific correction. Each call records timing, model id, attempts, target refs, and deterministic validation status in `review_assistance.json`.
|
|
319
|
+
|
|
320
|
+
### Table Correction Strategy
|
|
321
|
+
|
|
322
|
+
Tables are treated as first-class correction targets. The scheduler creates one table-specific work item per table so pages with multiple tables keep independent bbox, crop, context, and validation state. The table validator checks target identity, same-page table mixing, cells, spans, headers, units, footnotes, empty-cell expansion, and adjacent-page continuation refs before any command can become an auto-apply or proposal decision.
|
|
299
323
|
|
|
300
324
|
```typescript
|
|
301
325
|
const tokenUsageReport = await pdfParser.parse(
|
|
@@ -304,13 +328,21 @@ const tokenUsageReport = await pdfParser.parse(
|
|
|
304
328
|
async (outputPath) => console.log(outputPath),
|
|
305
329
|
false,
|
|
306
330
|
{
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
331
|
+
correction: {
|
|
332
|
+
models: {
|
|
333
|
+
textCorrection: openai('gpt-5.1'),
|
|
334
|
+
pageGate: openai('gpt-5.1-mini'),
|
|
335
|
+
reviewAssistance: openai('gpt-5.1'),
|
|
336
|
+
tableCorrection: openai('gpt-5.1'),
|
|
337
|
+
},
|
|
311
338
|
autoApplyThreshold: 0.85,
|
|
312
339
|
proposalThreshold: 0.5,
|
|
313
|
-
maxRetries:
|
|
340
|
+
maxRetries: {
|
|
341
|
+
textCorrection: 3,
|
|
342
|
+
pageGate: 3,
|
|
343
|
+
reviewAssistance: 3,
|
|
344
|
+
tableCorrection: 3,
|
|
345
|
+
},
|
|
314
346
|
temperature: 0,
|
|
315
347
|
outputLanguage: 'en-US',
|
|
316
348
|
},
|
|
@@ -321,11 +353,7 @@ const tokenUsageReport = await pdfParser.parse(
|
|
|
321
353
|
);
|
|
322
354
|
```
|
|
323
355
|
|
|
324
|
-
Review Assistance requires a local `file://` PDF and
|
|
325
|
-
`vlmProcessorModel` or `strategySamplerModel`. It updates `result.json` with
|
|
326
|
-
auto-applied fixes, keeps snapshots in `result_review_origin.json` and
|
|
327
|
-
`result_ocr_origin.json`, and writes `review_assistance.json` containing
|
|
328
|
-
per-page decisions, issues, proposals, and summary counts.
|
|
356
|
+
Review Assistance requires a local `file://` PDF for page image and text-layer references. It updates `result.json` with auto-applied fixes, keeps snapshots in `result_review_origin.json` and `result_ocr_origin.json`, writes `review_assistance_page_gate.json`, and writes `review_assistance.json` containing per-page decisions, issues, proposals, call traces, validation status, and summary counts.
|
|
329
357
|
|
|
330
358
|
## Document Type Validation
|
|
331
359
|
|
|
@@ -341,6 +369,13 @@ try {
|
|
|
341
369
|
async (outputPath) => console.log(outputPath),
|
|
342
370
|
false,
|
|
343
371
|
{
|
|
372
|
+
correction: {
|
|
373
|
+
models: {
|
|
374
|
+
textCorrection: openai('gpt-5.1'),
|
|
375
|
+
pageGate: openai('gpt-5.1'),
|
|
376
|
+
reviewAssistance: openai('gpt-5.1'),
|
|
377
|
+
},
|
|
378
|
+
},
|
|
344
379
|
documentValidationModel: openai('gpt-5.1'),
|
|
345
380
|
},
|
|
346
381
|
);
|
|
@@ -362,6 +397,13 @@ const tokenUsageReport = await pdfParser.parse(
|
|
|
362
397
|
async (outputPath) => console.log(outputPath),
|
|
363
398
|
false,
|
|
364
399
|
{
|
|
400
|
+
correction: {
|
|
401
|
+
models: {
|
|
402
|
+
textCorrection: openai('gpt-5.1'),
|
|
403
|
+
pageGate: openai('gpt-5.1'),
|
|
404
|
+
reviewAssistance: openai('gpt-5.1'),
|
|
405
|
+
},
|
|
406
|
+
},
|
|
365
407
|
chunkedConversion: true,
|
|
366
408
|
chunkSize: 50, // Pages per chunk (default: configured in constants)
|
|
367
409
|
chunkMaxRetries: 3, // Max retry attempts per failed chunk (default: configured in constants)
|
|
@@ -396,6 +438,13 @@ const tokenUsageReport = await pdfParser.parse(
|
|
|
396
438
|
async (outputPath) => console.log(outputPath),
|
|
397
439
|
false,
|
|
398
440
|
{
|
|
441
|
+
correction: {
|
|
442
|
+
models: {
|
|
443
|
+
textCorrection: openai('gpt-5.1'),
|
|
444
|
+
pageGate: openai('gpt-5.1'),
|
|
445
|
+
reviewAssistance: openai('gpt-5.1'),
|
|
446
|
+
},
|
|
447
|
+
},
|
|
399
448
|
forceImagePdf: true, // Always convert to image PDF first
|
|
400
449
|
},
|
|
401
450
|
);
|
|
@@ -419,7 +468,15 @@ try {
|
|
|
419
468
|
'report-001',
|
|
420
469
|
async (outputPath) => console.log(outputPath),
|
|
421
470
|
false,
|
|
422
|
-
{
|
|
471
|
+
{
|
|
472
|
+
correction: {
|
|
473
|
+
models: {
|
|
474
|
+
textCorrection: openai('gpt-5.1'),
|
|
475
|
+
pageGate: openai('gpt-5.1'),
|
|
476
|
+
reviewAssistance: openai('gpt-5.1'),
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
},
|
|
423
480
|
controller.signal, // AbortSignal
|
|
424
481
|
);
|
|
425
482
|
} catch (error) {
|
|
@@ -561,12 +618,8 @@ await pdfParser.dispose();
|
|
|
561
618
|
|
|
562
619
|
```typescript
|
|
563
620
|
type PDFConvertOptions = {
|
|
564
|
-
//
|
|
565
|
-
|
|
566
|
-
vlmProcessorModel?: LanguageModel; // Vision LLM for text correction
|
|
567
|
-
vlmConcurrency?: number; // Parallel page processing (default: 1)
|
|
568
|
-
skipSampling?: boolean; // Skip strategy sampling
|
|
569
|
-
forcedMethod?: 'ocrmac' | 'vlm'; // Force specific OCR method
|
|
621
|
+
// Mandatory post-Docling correction
|
|
622
|
+
correction: PDFCorrectionOptions;
|
|
570
623
|
|
|
571
624
|
// Image PDF options
|
|
572
625
|
forceImagePdf?: boolean; // Force pre-conversion to image-based PDF
|
|
@@ -579,8 +632,7 @@ type PDFConvertOptions = {
|
|
|
579
632
|
document_timeout?: number; // Document processing timeout in seconds
|
|
580
633
|
documentValidationModel?: LanguageModel; // LLM for document type validation
|
|
581
634
|
|
|
582
|
-
//
|
|
583
|
-
reviewAssistance?: boolean | ReviewAssistanceOptions; // Optional page-level review
|
|
635
|
+
// Correction progress
|
|
584
636
|
onReviewAssistanceProgress?: (event: ReviewAssistanceProgressEvent) => void; // Progress callback
|
|
585
637
|
|
|
586
638
|
// Chunked conversion (large PDFs)
|
|
@@ -595,27 +647,47 @@ type PDFConvertOptions = {
|
|
|
595
647
|
};
|
|
596
648
|
```
|
|
597
649
|
|
|
598
|
-
###
|
|
650
|
+
### PDFCorrectionOptions
|
|
599
651
|
|
|
600
652
|
```typescript
|
|
601
|
-
interface
|
|
602
|
-
|
|
653
|
+
interface PDFCorrectionOptions {
|
|
654
|
+
models: {
|
|
655
|
+
textCorrection: LanguageModel; // Required: page text and table-cell OCR correction
|
|
656
|
+
pageGate: LanguageModel; // Required: structural Review Assistance page gate
|
|
657
|
+
reviewAssistance: LanguageModel; // Required: default structural review model
|
|
658
|
+
tableCorrection?: LanguageModel; // Optional: table-specific correction override
|
|
659
|
+
reviewAssistanceTasks?: Partial<
|
|
660
|
+
Record<
|
|
661
|
+
| 'text_ocr_hanja'
|
|
662
|
+
| 'text_integrity'
|
|
663
|
+
| 'text_role_footnote'
|
|
664
|
+
| 'tables'
|
|
665
|
+
| 'pictures_captions'
|
|
666
|
+
| 'layout_bbox_order',
|
|
667
|
+
LanguageModel
|
|
668
|
+
>
|
|
669
|
+
>;
|
|
670
|
+
};
|
|
671
|
+
concurrency?: {
|
|
672
|
+
pages?: number; // Page-level text correction and page gate concurrency
|
|
673
|
+
reviewTasks?: number; // Structural Review Assistance work-item concurrency
|
|
674
|
+
tables?: number; // Table-specific correction concurrency
|
|
675
|
+
};
|
|
676
|
+
maxRetries?: {
|
|
677
|
+
textCorrection?: number;
|
|
678
|
+
pageGate?: number;
|
|
679
|
+
reviewAssistance?: number;
|
|
680
|
+
tableCorrection?: number;
|
|
681
|
+
};
|
|
682
|
+
localModelConcurrency?: number; // Bounded local model request concurrency
|
|
683
|
+
workItemTimeoutMs?: number; // Per-work-item timeout
|
|
684
|
+
outputLanguage?: string; // Human-readable review reason language (default: en-US)
|
|
685
|
+
pageGate?: {
|
|
686
|
+
structuralNoiseThreshold?: number;
|
|
687
|
+
};
|
|
603
688
|
autoApplyThreshold?: number; // Minimum confidence for direct mutation (default: 0.85)
|
|
604
689
|
proposalThreshold?: number; // Minimum confidence for sidecar proposal (default: 0.5)
|
|
605
|
-
maxRetries?: number; // Max retries per page-level VLM call (default: 3)
|
|
606
690
|
temperature?: number; // VLM generation temperature (default: 0)
|
|
607
|
-
outputLanguage?: string; // Human-readable review reason language (default: en-US)
|
|
608
|
-
}
|
|
609
|
-
```
|
|
610
|
-
|
|
611
|
-
### ConvertWithStrategyResult
|
|
612
|
-
|
|
613
|
-
```typescript
|
|
614
|
-
interface ConvertWithStrategyResult {
|
|
615
|
-
/** The OCR strategy that was determined */
|
|
616
|
-
strategy: OcrStrategy;
|
|
617
|
-
/** Token usage report from sampling and/or VLM processing (null when no LLM usage occurs) */
|
|
618
|
-
tokenUsageReport: TokenUsageReport | null;
|
|
619
691
|
}
|
|
620
692
|
```
|
|
621
693
|
|