@heripo/pdf-parser 0.1.28 → 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.md CHANGED
@@ -20,7 +20,8 @@
20
20
  - [Prerequisites](#prerequisites)
21
21
  - [Installation](#installation)
22
22
  - [Usage](#usage)
23
- - [OCR Strategy System](#ocr-strategy-system)
23
+ - [Mandatory Correction Pipeline](#mandatory-correction-pipeline)
24
+ - [Review Assistance](#review-assistance)
24
25
  - [Document Type Validation](#document-type-validation)
25
26
  - [Large PDF Chunked Conversion](#large-pdf-chunked-conversion)
26
27
  - [Image PDF Fallback](#image-pdf-fallback)
@@ -30,18 +31,21 @@
30
31
  - [System Dependencies Details](#system-dependencies-details)
31
32
  - [API Documentation](#api-documentation)
32
33
  - [Troubleshooting](#troubleshooting)
34
+ - [Sponsor](#sponsor)
33
35
  - [License](#license)
34
36
 
35
37
  ## Key Features
36
38
 
37
- - **High-Quality OCR**: Document recognition using Docling SDK (ocrmac / Apple Vision Framework)
38
- - **Korean Report VLM Correction**: Automatically detects Korean reports and applies VLM text correction to all pages
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
39
41
  - **Apple Silicon Optimized**: GPU acceleration on M1/M2/M3/M4/M5 chips
40
42
  - **Automatic Environment Setup**: Automatic Python virtual environment and docling-serve installation
41
43
  - **Image Extraction**: Automatic extraction and saving of images from PDFs
42
44
  - **Document Type Validation**: Optional LLM-based validation that a PDF is an archaeological report
43
45
  - **Chunked Conversion**: Split large PDFs into chunks for reliable processing
44
46
  - **Image PDF Fallback**: Automatic fallback to image-based PDF when conversion fails
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
45
49
  - **AbortSignal Support**: Cancel ongoing parsing operations
46
50
  - **Server Crash Recovery**: Automatic restart of docling-serve on ECONNREFUSED
47
51
 
@@ -49,7 +53,7 @@
49
53
 
50
54
  ### System Requirements
51
55
 
52
- - **macOS** with Apple Silicon (M1/M2/M3) - Recommended for optimal performance
56
+ - **macOS** with Apple Silicon (M1/M2/M3/M4/M5) - Recommended for optimal performance
53
57
  - **macOS** with Intel - Supported but slower
54
58
  - **Linux/Windows** - Currently not supported
55
59
 
@@ -61,7 +65,7 @@
61
65
  brew install node
62
66
  ```
63
67
 
64
- #### 2. pnpm >= 9.0.0
68
+ #### 2. pnpm >= 10.0.0
65
69
 
66
70
  ```bash
67
71
  npm install -g pnpm
@@ -81,7 +85,7 @@ python3.11 --version
81
85
 
82
86
  #### 4. poppler (PDF text extraction)
83
87
 
84
- Required for PDF page counting (`pdfinfo`) and text layer extraction (`pdftotext`), used by the OCR strategy system's text layer pre-check.
88
+ Required for PDF page counting (`pdfinfo`) and text layer extraction (`pdftotext`), used as a post-Docling correction reference.
85
89
 
86
90
  ```bash
87
91
  brew install poppler
@@ -123,13 +127,13 @@ This setup runs only once and may take 5-10 minutes depending on internet connec
123
127
 
124
128
  ```bash
125
129
  # Install with npm
126
- npm install @heripo/pdf-parser
130
+ npm install @heripo/pdf-parser @heripo/logger
127
131
 
128
132
  # Install with pnpm
129
- pnpm add @heripo/pdf-parser
133
+ pnpm add @heripo/pdf-parser @heripo/logger
130
134
 
131
135
  # Install with yarn
132
- yarn add @heripo/pdf-parser
136
+ yarn add @heripo/pdf-parser @heripo/logger
133
137
  ```
134
138
 
135
139
  ## Usage
@@ -137,10 +141,16 @@ yarn add @heripo/pdf-parser
137
141
  ### Basic Usage
138
142
 
139
143
  ```typescript
144
+ import { openai } from '@ai-sdk/openai';
140
145
  import { Logger } from '@heripo/logger';
141
146
  import { PDFParser } from '@heripo/pdf-parser';
142
147
 
143
- const logger = Logger(...);
148
+ const logger = new Logger({
149
+ debug: (...args) => console.debug('[heripo]', ...args),
150
+ info: (...args) => console.info('[heripo]', ...args),
151
+ warn: (...args) => console.warn('[heripo]', ...args),
152
+ error: (...args) => console.error('[heripo]', ...args),
153
+ });
144
154
 
145
155
  // Create PDFParser instance (logger is required)
146
156
  const pdfParser = new PDFParser({
@@ -148,19 +158,29 @@ const pdfParser = new PDFParser({
148
158
  logger,
149
159
  });
150
160
 
161
+ const correctionModel = openai('gpt-5.1');
162
+
151
163
  // Initialize (environment setup and start docling-serve)
152
164
  await pdfParser.init();
153
165
 
154
166
  // Parse PDF
155
167
  const tokenUsageReport = await pdfParser.parse(
156
168
  'file:///path/to/report.pdf', // PDF URL (file:// or http://)
157
- 'report-001', // Report ID
169
+ 'report-001', // Report ID
158
170
  async (outputPath) => {
159
171
  // Conversion complete callback
160
172
  console.log('PDF conversion complete:', outputPath);
161
173
  },
162
- false, // cleanupAfterCallback
163
- {}, // PDFConvertOptions
174
+ false, // cleanupAfterCallback
175
+ {
176
+ correction: {
177
+ models: {
178
+ textCorrection: correctionModel,
179
+ pageGate: correctionModel,
180
+ reviewAssistance: correctionModel,
181
+ },
182
+ },
183
+ }, // PDFConvertOptions
164
184
  );
165
185
 
166
186
  // Token usage report (null when no LLM usage)
@@ -193,10 +213,37 @@ const tokenUsageReport = await pdfParser.parse(
193
213
  async (outputPath) => console.log(outputPath),
194
214
  false,
195
215
  {
196
- // OCR strategy options
197
- strategySamplerModel: openai('gpt-5.1'),
198
- vlmProcessorModel: openai('gpt-5.1'),
199
- vlmConcurrency: 3,
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
+ },
241
+ autoApplyThreshold: 0.85,
242
+ proposalThreshold: 0.5,
243
+ temperature: 0,
244
+ outputLanguage: 'en-US',
245
+ },
246
+ onReviewAssistanceProgress: (event) => console.log(event),
200
247
 
201
248
  // Document validation
202
249
  documentValidationModel: openai('gpt-5.1'),
@@ -227,29 +274,52 @@ Clean up resources after work is complete:
227
274
  await pdfParser.dispose();
228
275
  ```
229
276
 
230
- ## OCR Strategy System
277
+ ## Mandatory Correction Pipeline
231
278
 
232
- ### Why This Strategy?
279
+ ### Why ocrmac Is Fixed
233
280
 
234
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.
235
282
 
236
- **However, Korean archaeological reports often need script-aware correction.** Hanja restoration, CJK mojibake, phonetic substitutions, and institution names can be unreliable with standard OCR alone. Rather than using VLM as the primary OCR engine, the system runs the fast ocrmac pipeline first and then applies VLM text correction to Korean reports.
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.
284
+
285
+ ### Required Correction Contract
286
+
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.
288
+
289
+ The correction stage runs in this order:
237
290
 
238
- ### Two-Stage Korean Detection (`OcrStrategySampler`)
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.
297
+
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.
299
+
300
+ ### Local Model Execution
301
+
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.
303
+
304
+ ### Rollout Smoke Test
305
+
306
+ Repository contributors can run the correction rollout smoke test against two local demo archaeological report artifacts:
307
+
308
+ ```bash
309
+ pnpm --filter @heripo/pdf-parser smoke:correction
310
+ ```
239
311
 
240
- 1. **Text Layer Pre-Check** (zero cost): Extracts the document's text layer using `pdftotext` and checks for Hangul. If Hangul is present, the document is immediately treated as Korean.
241
- 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.
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.
242
313
 
243
- ### Full-Document Correction (`VlmTextCorrector`)
314
+ ## Review Assistance
244
315
 
245
- When a Korean report is detected, every page is sent to the VLM for correction:
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.
246
317
 
247
- - Extracts OCR text elements and table cells from each page
248
- - Uses `pdftotext` reference text as a quality anchor
249
- - VLM returns substitution-based corrections (find -> replace)
250
- - Failed page corrections are gracefully skipped, preserving original OCR text
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`.
251
319
 
252
- ### Strategy Options
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.
253
323
 
254
324
  ```typescript
255
325
  const tokenUsageReport = await pdfParser.parse(
@@ -258,21 +328,33 @@ const tokenUsageReport = await pdfParser.parse(
258
328
  async (outputPath) => console.log(outputPath),
259
329
  false,
260
330
  {
261
- // Enable OCR strategy sampling (provide a Vision LLM model)
262
- strategySamplerModel: openai('gpt-5.1'),
263
-
264
- // VLM model for text correction (required when Korean reports are detected)
265
- vlmProcessorModel: openai('gpt-5.1'),
266
-
267
- // Concurrency for VLM page processing (default: 1)
268
- vlmConcurrency: 3,
269
-
270
- // Skip sampling and force a specific OCR method
271
- forcedMethod: 'ocrmac', // or 'vlm'
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
+ },
338
+ autoApplyThreshold: 0.85,
339
+ proposalThreshold: 0.5,
340
+ maxRetries: {
341
+ textCorrection: 3,
342
+ pageGate: 3,
343
+ reviewAssistance: 3,
344
+ tableCorrection: 3,
345
+ },
346
+ temperature: 0,
347
+ outputLanguage: 'en-US',
348
+ },
349
+ onReviewAssistanceProgress: (event) => {
350
+ console.log(event.substage, event.status, event.pageNo);
351
+ },
272
352
  },
273
353
  );
274
354
  ```
275
355
 
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.
357
+
276
358
  ## Document Type Validation
277
359
 
278
360
  Optional LLM-based validation that a PDF is an archaeological investigation report. When `documentValidationModel` is provided, the parser extracts text from the PDF and uses the LLM to verify the document type before processing. If validation fails, an `InvalidDocumentTypeError` is thrown.
@@ -287,6 +369,13 @@ try {
287
369
  async (outputPath) => console.log(outputPath),
288
370
  false,
289
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
+ },
290
379
  documentValidationModel: openai('gpt-5.1'),
291
380
  },
292
381
  );
@@ -308,6 +397,13 @@ const tokenUsageReport = await pdfParser.parse(
308
397
  async (outputPath) => console.log(outputPath),
309
398
  false,
310
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
+ },
311
407
  chunkedConversion: true,
312
408
  chunkSize: 50, // Pages per chunk (default: configured in constants)
313
409
  chunkMaxRetries: 3, // Max retry attempts per failed chunk (default: configured in constants)
@@ -342,6 +438,13 @@ const tokenUsageReport = await pdfParser.parse(
342
438
  async (outputPath) => console.log(outputPath),
343
439
  false,
344
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
+ },
345
448
  forceImagePdf: true, // Always convert to image PDF first
346
449
  },
347
450
  );
@@ -365,7 +468,15 @@ try {
365
468
  'report-001',
366
469
  async (outputPath) => console.log(outputPath),
367
470
  false,
368
- {},
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
+ },
369
480
  controller.signal, // AbortSignal
370
481
  );
371
482
  } catch (error) {
@@ -404,7 +515,7 @@ Archaeological excavation report PDFs have the following characteristics:
404
515
  ### Key Advantages
405
516
 
406
517
  - **Cost**: 100x+ cheaper than cloud OCR (free)
407
- - **Performance**: Fast processing with GPU acceleration on Apple Silicon M1/M2/M3
518
+ - **Performance**: Fast processing with GPU acceleration on Apple Silicon M1/M2/M3/M4/M5
408
519
  - **Quality**: Accurate recognition even for complex documents
409
520
  - **Privacy**: Documents are not sent to external servers
410
521
 
@@ -507,12 +618,8 @@ await pdfParser.dispose();
507
618
 
508
619
  ```typescript
509
620
  type PDFConvertOptions = {
510
- // OCR strategy options
511
- strategySamplerModel?: LanguageModel; // Vision LLM for OCR strategy sampling
512
- vlmProcessorModel?: LanguageModel; // Vision LLM for text correction
513
- vlmConcurrency?: number; // Parallel page processing (default: 1)
514
- skipSampling?: boolean; // Skip strategy sampling
515
- forcedMethod?: 'ocrmac' | 'vlm'; // Force specific OCR method
621
+ // Mandatory post-Docling correction
622
+ correction: PDFCorrectionOptions;
516
623
 
517
624
  // Image PDF options
518
625
  forceImagePdf?: boolean; // Force pre-conversion to image-based PDF
@@ -525,6 +632,9 @@ type PDFConvertOptions = {
525
632
  document_timeout?: number; // Document processing timeout in seconds
526
633
  documentValidationModel?: LanguageModel; // LLM for document type validation
527
634
 
635
+ // Correction progress
636
+ onReviewAssistanceProgress?: (event: ReviewAssistanceProgressEvent) => void; // Progress callback
637
+
528
638
  // Chunked conversion (large PDFs)
529
639
  chunkedConversion?: boolean; // Enable chunked conversion
530
640
  chunkSize?: number; // Pages per chunk
@@ -537,14 +647,47 @@ type PDFConvertOptions = {
537
647
  };
538
648
  ```
539
649
 
540
- ### ConvertWithStrategyResult
650
+ ### PDFCorrectionOptions
541
651
 
542
652
  ```typescript
543
- interface ConvertWithStrategyResult {
544
- /** The OCR strategy that was determined */
545
- strategy: OcrStrategy;
546
- /** Token usage report from sampling and/or VLM processing (null when no LLM usage occurs) */
547
- tokenUsageReport: TokenUsageReport | null;
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
+ };
688
+ autoApplyThreshold?: number; // Minimum confidence for direct mutation (default: 0.85)
689
+ proposalThreshold?: number; // Minimum confidence for sidecar proposal (default: 0.5)
690
+ temperature?: number; // VLM generation temperature (default: 0)
548
691
  }
549
692
  ```
550
693
 
@@ -665,6 +808,13 @@ If you have ideas for supporting Linux while maintaining both performance and co
665
808
  - [@heripo/document-processor](../document-processor) - Document structure analysis and LLM processing
666
809
  - [@heripo/model](../model) - Data models and type definitions
667
810
 
811
+ ## Sponsor
812
+
813
+ If you'd like to support heripo lab's open-source research, you can sponsor us through:
814
+
815
+ - [Open Collective](https://opencollective.com/heripo-project) for general project sponsorship.
816
+ - [fairy.hada.io/@heripo](https://fairy.hada.io/@heripo) for Korean individual supporters who prefer KRW payments.
817
+
668
818
  ## License
669
819
 
670
820
  This package is distributed under the [Apache License 2.0](../../LICENSE).