@heripo/document-processor 0.1.5 → 0.1.6
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/dist/index.cjs +325 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -2
- package/dist/index.d.ts +80 -2
- package/dist/index.js +324 -14
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -39,6 +39,7 @@ __export(index_exports, {
|
|
|
39
39
|
CaptionValidator: () => CaptionValidator,
|
|
40
40
|
ChapterConverter: () => ChapterConverter,
|
|
41
41
|
DocumentProcessor: () => DocumentProcessor,
|
|
42
|
+
HanjaQualitySampler: () => HanjaQualitySampler,
|
|
42
43
|
PAGE_NUMBER_PATTERN: () => PAGE_NUMBER_PATTERN,
|
|
43
44
|
PagePattern: () => PagePattern,
|
|
44
45
|
PageRangeParseError: () => PageRangeParseError,
|
|
@@ -3408,6 +3409,283 @@ Remember: Look for SMALL numbers in MARGINS only. Ignore figure/table/photo numb
|
|
|
3408
3409
|
}
|
|
3409
3410
|
};
|
|
3410
3411
|
|
|
3412
|
+
// src/samplers/hanja-quality-sampler.ts
|
|
3413
|
+
var import_zod5 = require("zod");
|
|
3414
|
+
var MIN_TEXT_LENGTH = 100;
|
|
3415
|
+
var MAX_SAMPLE_PAGES = 10;
|
|
3416
|
+
var EDGE_TRIM_RATIO = 0.1;
|
|
3417
|
+
var IMAGE_PAGE_TEXT_THRESHOLD = 50;
|
|
3418
|
+
var CORRUPTION_THRESHOLD = 0.5;
|
|
3419
|
+
var MIN_SEVERE_CORRUPTED_COUNT = 3;
|
|
3420
|
+
var HanjaQualityResponseSchema = import_zod5.z.object({
|
|
3421
|
+
isCorrupted: import_zod5.z.boolean().describe(
|
|
3422
|
+
"Whether the KCJ (Hanja) characters in the OCR text are significantly corrupted compared to the page image"
|
|
3423
|
+
),
|
|
3424
|
+
corruptedCharCount: import_zod5.z.number().int().nonnegative().describe(
|
|
3425
|
+
"Approximate number of corrupted or incorrectly recognized KCJ characters"
|
|
3426
|
+
),
|
|
3427
|
+
totalKcjCharCount: import_zod5.z.number().int().nonnegative().describe("Total number of KCJ characters found in the OCR text"),
|
|
3428
|
+
explanation: import_zod5.z.string().describe("Brief explanation of the assessment result")
|
|
3429
|
+
});
|
|
3430
|
+
var HanjaQualitySampler = class extends VisionLLMComponent {
|
|
3431
|
+
constructor(logger, model, outputPath, maxRetries, fallbackModel, aggregator, abortSignal) {
|
|
3432
|
+
super(
|
|
3433
|
+
logger,
|
|
3434
|
+
model,
|
|
3435
|
+
"HanjaQualitySampler",
|
|
3436
|
+
outputPath,
|
|
3437
|
+
{ maxRetries, abortSignal },
|
|
3438
|
+
fallbackModel,
|
|
3439
|
+
aggregator
|
|
3440
|
+
);
|
|
3441
|
+
}
|
|
3442
|
+
/**
|
|
3443
|
+
* Assess the quality of KCJ character recognition in the document
|
|
3444
|
+
*
|
|
3445
|
+
* @param doclingDoc - DoclingDocument to assess
|
|
3446
|
+
* @returns Assessment result indicating whether VLM re-parse is needed
|
|
3447
|
+
*/
|
|
3448
|
+
async assess(doclingDoc) {
|
|
3449
|
+
const totalPages = Object.keys(doclingDoc.pages).length;
|
|
3450
|
+
this.log("info", "Starting KCJ quality assessment...");
|
|
3451
|
+
const { frontCutoff, backCutoff } = this.getEligiblePageRange(totalPages);
|
|
3452
|
+
const imageOnlyPages = this.getImageOnlyPages(doclingDoc);
|
|
3453
|
+
this.log(
|
|
3454
|
+
"info",
|
|
3455
|
+
`Total pages: ${totalPages}, eligible range: (${frontCutoff}, ${backCutoff}], image-only pages excluded: ${imageOnlyPages.size}`
|
|
3456
|
+
);
|
|
3457
|
+
const textPages = this.getTextPages(doclingDoc);
|
|
3458
|
+
if (textPages.length === 0) {
|
|
3459
|
+
this.log("info", "No text pages found for assessment");
|
|
3460
|
+
return {
|
|
3461
|
+
needsVlmReparse: false,
|
|
3462
|
+
severity: "none",
|
|
3463
|
+
kcjPageCount: 0,
|
|
3464
|
+
sampledPageCount: 0,
|
|
3465
|
+
corruptedRatio: 0,
|
|
3466
|
+
reason: "No text pages found for assessment"
|
|
3467
|
+
};
|
|
3468
|
+
}
|
|
3469
|
+
this.log(
|
|
3470
|
+
"info",
|
|
3471
|
+
`Found ${textPages.length} text pages (min length: ${MIN_TEXT_LENGTH})`
|
|
3472
|
+
);
|
|
3473
|
+
const sampled = this.selectSamplePages(
|
|
3474
|
+
textPages,
|
|
3475
|
+
totalPages,
|
|
3476
|
+
imageOnlyPages
|
|
3477
|
+
);
|
|
3478
|
+
this.log(
|
|
3479
|
+
"info",
|
|
3480
|
+
`Sampling ${sampled.length} pages: [${sampled.map((p) => p.pageNo).join(", ")}]`
|
|
3481
|
+
);
|
|
3482
|
+
const results = await this.evaluatePages(sampled);
|
|
3483
|
+
const assessment = this.aggregateResults(
|
|
3484
|
+
textPages.length,
|
|
3485
|
+
sampled.length,
|
|
3486
|
+
results
|
|
3487
|
+
);
|
|
3488
|
+
this.log(
|
|
3489
|
+
"info",
|
|
3490
|
+
`Assessment complete: severity=${assessment.severity}, corrupted=${assessment.corruptedRatio.toFixed(2)}, needsVlmReparse=${assessment.needsVlmReparse}`
|
|
3491
|
+
);
|
|
3492
|
+
return assessment;
|
|
3493
|
+
}
|
|
3494
|
+
/**
|
|
3495
|
+
* Get the eligible page range after trimming front/back edges.
|
|
3496
|
+
* Returns cutoff values where eligible pages satisfy: pageNo > frontCutoff && pageNo <= backCutoff
|
|
3497
|
+
*/
|
|
3498
|
+
getEligiblePageRange(totalPages) {
|
|
3499
|
+
const trimCount = Math.ceil(totalPages * EDGE_TRIM_RATIO);
|
|
3500
|
+
return {
|
|
3501
|
+
frontCutoff: trimCount,
|
|
3502
|
+
backCutoff: totalPages - trimCount
|
|
3503
|
+
};
|
|
3504
|
+
}
|
|
3505
|
+
/**
|
|
3506
|
+
* Get page numbers that are "image-only" (contain pictures but minimal text).
|
|
3507
|
+
* These pages (e.g., photo plates/도판) are unsuitable for KCJ quality assessment.
|
|
3508
|
+
*/
|
|
3509
|
+
getImageOnlyPages(doclingDoc) {
|
|
3510
|
+
const picturePages = /* @__PURE__ */ new Set();
|
|
3511
|
+
for (const picture of doclingDoc.pictures) {
|
|
3512
|
+
const pageNo = picture.prov?.[0]?.page_no;
|
|
3513
|
+
if (pageNo != null) {
|
|
3514
|
+
picturePages.add(pageNo);
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
if (picturePages.size === 0) return /* @__PURE__ */ new Set();
|
|
3518
|
+
const pageTextLength = /* @__PURE__ */ new Map();
|
|
3519
|
+
for (const text of doclingDoc.texts) {
|
|
3520
|
+
const pageNo = this.getPageNo(text);
|
|
3521
|
+
if (picturePages.has(pageNo)) {
|
|
3522
|
+
pageTextLength.set(
|
|
3523
|
+
pageNo,
|
|
3524
|
+
(pageTextLength.get(pageNo) ?? 0) + text.text.length
|
|
3525
|
+
);
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
const imageOnlyPages = /* @__PURE__ */ new Set();
|
|
3529
|
+
for (const pageNo of picturePages) {
|
|
3530
|
+
const textLength = pageTextLength.get(pageNo) ?? 0;
|
|
3531
|
+
if (textLength < IMAGE_PAGE_TEXT_THRESHOLD) {
|
|
3532
|
+
imageOnlyPages.add(pageNo);
|
|
3533
|
+
}
|
|
3534
|
+
}
|
|
3535
|
+
return imageOnlyPages;
|
|
3536
|
+
}
|
|
3537
|
+
/**
|
|
3538
|
+
* Get all pages with substantial text content.
|
|
3539
|
+
* Pages with fewer characters than MIN_TEXT_LENGTH (e.g., page numbers, headers only) are excluded.
|
|
3540
|
+
*/
|
|
3541
|
+
getTextPages(doclingDoc) {
|
|
3542
|
+
const pageMap = /* @__PURE__ */ new Map();
|
|
3543
|
+
for (const text of doclingDoc.texts) {
|
|
3544
|
+
const pageNo = this.getPageNo(text);
|
|
3545
|
+
const entry = pageMap.get(pageNo) ?? { textLength: 0, texts: [] };
|
|
3546
|
+
entry.textLength += text.text.length;
|
|
3547
|
+
entry.texts.push(text.text);
|
|
3548
|
+
pageMap.set(pageNo, entry);
|
|
3549
|
+
}
|
|
3550
|
+
return Array.from(pageMap.entries()).filter(([, data]) => data.textLength >= MIN_TEXT_LENGTH).map(([pageNo, data]) => ({
|
|
3551
|
+
pageNo,
|
|
3552
|
+
textLength: data.textLength,
|
|
3553
|
+
texts: data.texts
|
|
3554
|
+
}));
|
|
3555
|
+
}
|
|
3556
|
+
/**
|
|
3557
|
+
* Select sample pages with highest text density.
|
|
3558
|
+
* Prefers pages in the eligible range (not edge-trimmed) and not image-only.
|
|
3559
|
+
* Falls back to all text pages if filtering leaves too few candidates.
|
|
3560
|
+
*/
|
|
3561
|
+
selectSamplePages(textPages, totalPages, imageOnlyPages) {
|
|
3562
|
+
const { frontCutoff, backCutoff } = this.getEligiblePageRange(totalPages);
|
|
3563
|
+
const filtered = totalPages > 0 ? textPages.filter(
|
|
3564
|
+
(p) => p.pageNo > frontCutoff && p.pageNo <= backCutoff && !imageOnlyPages.has(p.pageNo)
|
|
3565
|
+
) : textPages;
|
|
3566
|
+
const candidates = filtered.length > 0 ? filtered : textPages;
|
|
3567
|
+
if (candidates !== filtered) {
|
|
3568
|
+
this.log(
|
|
3569
|
+
"warn",
|
|
3570
|
+
`All text pages were filtered out by edge/image exclusion. Falling back to all ${textPages.length} text pages.`
|
|
3571
|
+
);
|
|
3572
|
+
}
|
|
3573
|
+
const sorted = [...candidates].sort((a, b) => b.textLength - a.textLength);
|
|
3574
|
+
const count = Math.min(MAX_SAMPLE_PAGES, sorted.length);
|
|
3575
|
+
return sorted.slice(0, count);
|
|
3576
|
+
}
|
|
3577
|
+
/**
|
|
3578
|
+
* Evaluate sampled pages using Vision LLM
|
|
3579
|
+
*/
|
|
3580
|
+
async evaluatePages(pages) {
|
|
3581
|
+
const results = [];
|
|
3582
|
+
for (const page of pages) {
|
|
3583
|
+
const result = await this.evaluateSinglePage(page);
|
|
3584
|
+
results.push(result);
|
|
3585
|
+
}
|
|
3586
|
+
return results;
|
|
3587
|
+
}
|
|
3588
|
+
/**
|
|
3589
|
+
* Evaluate a single page by comparing OCR text against the page image
|
|
3590
|
+
*/
|
|
3591
|
+
async evaluateSinglePage(page) {
|
|
3592
|
+
const imagePath = `pages/page_${page.pageNo - 1}.png`;
|
|
3593
|
+
let imageContent;
|
|
3594
|
+
try {
|
|
3595
|
+
imageContent = this.buildImageContent(imagePath);
|
|
3596
|
+
} catch {
|
|
3597
|
+
this.log(
|
|
3598
|
+
"warn",
|
|
3599
|
+
`Failed to load page image for page ${page.pageNo}, marking as not corrupted`
|
|
3600
|
+
);
|
|
3601
|
+
return {
|
|
3602
|
+
isCorrupted: false,
|
|
3603
|
+
corruptedCharCount: 0,
|
|
3604
|
+
totalKcjCharCount: 0,
|
|
3605
|
+
explanation: `Page image not available for page ${page.pageNo}`
|
|
3606
|
+
};
|
|
3607
|
+
}
|
|
3608
|
+
const ocrText = page.texts.join("\n");
|
|
3609
|
+
const messages = [
|
|
3610
|
+
{
|
|
3611
|
+
role: "user",
|
|
3612
|
+
content: [
|
|
3613
|
+
imageContent,
|
|
3614
|
+
{
|
|
3615
|
+
type: "text",
|
|
3616
|
+
text: this.buildUserPrompt(ocrText)
|
|
3617
|
+
}
|
|
3618
|
+
]
|
|
3619
|
+
}
|
|
3620
|
+
];
|
|
3621
|
+
const { output } = await this.callVisionLLM(
|
|
3622
|
+
HanjaQualityResponseSchema,
|
|
3623
|
+
messages,
|
|
3624
|
+
`page-${page.pageNo}`
|
|
3625
|
+
);
|
|
3626
|
+
this.log(
|
|
3627
|
+
"info",
|
|
3628
|
+
`Page ${page.pageNo}: corrupted=${output.isCorrupted}, ${output.corruptedCharCount}/${output.totalKcjCharCount} KCJ chars corrupted`
|
|
3629
|
+
);
|
|
3630
|
+
return output;
|
|
3631
|
+
}
|
|
3632
|
+
/**
|
|
3633
|
+
* Aggregate individual page results into final assessment
|
|
3634
|
+
*/
|
|
3635
|
+
aggregateResults(totalTextPages, sampledCount, results) {
|
|
3636
|
+
const corruptedCount = results.filter((r) => r.isCorrupted).length;
|
|
3637
|
+
const corruptedRatio = sampledCount > 0 ? corruptedCount / sampledCount : 0;
|
|
3638
|
+
let severity;
|
|
3639
|
+
let needsVlmReparse;
|
|
3640
|
+
if (corruptedRatio >= CORRUPTION_THRESHOLD || corruptedCount >= MIN_SEVERE_CORRUPTED_COUNT) {
|
|
3641
|
+
severity = "severe";
|
|
3642
|
+
needsVlmReparse = true;
|
|
3643
|
+
} else if (corruptedCount > 0) {
|
|
3644
|
+
severity = "minor";
|
|
3645
|
+
needsVlmReparse = false;
|
|
3646
|
+
} else {
|
|
3647
|
+
severity = "none";
|
|
3648
|
+
needsVlmReparse = false;
|
|
3649
|
+
}
|
|
3650
|
+
const reason = corruptedCount === 0 ? "No Hanja character corruption detected" : `${corruptedCount}/${sampledCount} sampled pages have corrupted Hanja characters (ratio: ${corruptedRatio.toFixed(2)})`;
|
|
3651
|
+
return {
|
|
3652
|
+
needsVlmReparse,
|
|
3653
|
+
severity,
|
|
3654
|
+
kcjPageCount: totalTextPages,
|
|
3655
|
+
sampledPageCount: sampledCount,
|
|
3656
|
+
corruptedRatio,
|
|
3657
|
+
reason
|
|
3658
|
+
};
|
|
3659
|
+
}
|
|
3660
|
+
/**
|
|
3661
|
+
* Get page number from a text item
|
|
3662
|
+
*/
|
|
3663
|
+
getPageNo(text) {
|
|
3664
|
+
return text.prov?.[0]?.page_no ?? 1;
|
|
3665
|
+
}
|
|
3666
|
+
buildSystemPrompt() {
|
|
3667
|
+
return "";
|
|
3668
|
+
}
|
|
3669
|
+
buildUserPrompt(ocrText) {
|
|
3670
|
+
return `You are evaluating the quality of OCR text extraction for Hanja (\u6F22\u5B57) characters used in Korean archaeological reports.
|
|
3671
|
+
|
|
3672
|
+
Look at the page image and find any Hanja (\u6F22\u5B57) characters visible in the original document. Then compare them against the OCR text below.
|
|
3673
|
+
|
|
3674
|
+
## Evaluation Criteria
|
|
3675
|
+
- Find Hanja (\u6F22\u5B57) characters in the page image first
|
|
3676
|
+
- Check if each Hanja character is correctly recognized in the OCR text
|
|
3677
|
+
- A character is "corrupted" if a Hanja character in the image was replaced by Korean hangul, garbled text, symbols, or a completely different character in the OCR text
|
|
3678
|
+
- Common corruption patterns: Hanja replaced by similar-looking hangul (e.g., \u7C98\u571F \u2192 \uADF8\uC73C), wrong character substitution, partial recognition, question marks or boxes
|
|
3679
|
+
- Minor font rendering differences are NOT corruption
|
|
3680
|
+
- If no Hanja characters are visible in the page image, report as not corrupted with 0 total characters
|
|
3681
|
+
|
|
3682
|
+
## OCR Text to Evaluate
|
|
3683
|
+
${ocrText}
|
|
3684
|
+
|
|
3685
|
+
Compare the Hanja characters visible in the page image against the OCR text above and assess whether they were correctly recognized.`;
|
|
3686
|
+
}
|
|
3687
|
+
};
|
|
3688
|
+
|
|
3411
3689
|
// src/validators/base-validator.ts
|
|
3412
3690
|
var BaseValidator = class extends TextLLMComponent {
|
|
3413
3691
|
/**
|
|
@@ -3467,13 +3745,13 @@ var BaseValidator = class extends TextLLMComponent {
|
|
|
3467
3745
|
};
|
|
3468
3746
|
|
|
3469
3747
|
// src/validators/toc-content-validator.ts
|
|
3470
|
-
var
|
|
3471
|
-
var TocContentValidationSchema =
|
|
3472
|
-
isValid:
|
|
3473
|
-
confidence:
|
|
3474
|
-
contentType:
|
|
3475
|
-
extractedTocMarkdown:
|
|
3476
|
-
reason:
|
|
3748
|
+
var import_zod6 = require("zod");
|
|
3749
|
+
var TocContentValidationSchema = import_zod6.z.object({
|
|
3750
|
+
isValid: import_zod6.z.boolean().describe("Whether valid main document TOC was found"),
|
|
3751
|
+
confidence: import_zod6.z.number().min(0).max(1).describe("Confidence score between 0 and 1"),
|
|
3752
|
+
contentType: import_zod6.z.enum(["pure_toc", "mixed", "resource_only", "invalid"]).describe("Type of content detected"),
|
|
3753
|
+
extractedTocMarkdown: import_zod6.z.string().nullable().describe("Extracted main TOC markdown when mixed; null otherwise"),
|
|
3754
|
+
reason: import_zod6.z.string().describe("Brief explanation in English")
|
|
3477
3755
|
});
|
|
3478
3756
|
var TocContentValidator = class extends BaseValidator {
|
|
3479
3757
|
confidenceThreshold;
|
|
@@ -3624,14 +3902,14 @@ ${markdown}`;
|
|
|
3624
3902
|
};
|
|
3625
3903
|
|
|
3626
3904
|
// src/validators/caption-validator.ts
|
|
3627
|
-
var
|
|
3628
|
-
var CaptionValidationItemSchema =
|
|
3629
|
-
index:
|
|
3630
|
-
isValid:
|
|
3631
|
-
reason:
|
|
3905
|
+
var import_zod7 = require("zod");
|
|
3906
|
+
var CaptionValidationItemSchema = import_zod7.z.object({
|
|
3907
|
+
index: import_zod7.z.number().int().describe("Index of the caption in the input array"),
|
|
3908
|
+
isValid: import_zod7.z.boolean().describe("Whether the parsed caption is correct"),
|
|
3909
|
+
reason: import_zod7.z.string().nullable().describe("Brief explanation if invalid, null if valid")
|
|
3632
3910
|
});
|
|
3633
|
-
var CaptionValidationBatchSchema =
|
|
3634
|
-
results:
|
|
3911
|
+
var CaptionValidationBatchSchema = import_zod7.z.object({
|
|
3912
|
+
results: import_zod7.z.array(CaptionValidationItemSchema)
|
|
3635
3913
|
});
|
|
3636
3914
|
var CaptionValidator = class extends BaseValidator {
|
|
3637
3915
|
constructor(logger, model, options, fallbackModel, aggregator) {
|
|
@@ -3839,6 +4117,7 @@ var DocumentProcessor = class {
|
|
|
3839
4117
|
validatorModel;
|
|
3840
4118
|
visionTocExtractorModel;
|
|
3841
4119
|
captionParserModel;
|
|
4120
|
+
hanjaQualitySamplerModel;
|
|
3842
4121
|
textCleanerBatchSize;
|
|
3843
4122
|
captionParserBatchSize;
|
|
3844
4123
|
captionValidatorBatchSize;
|
|
@@ -3865,6 +4144,7 @@ var DocumentProcessor = class {
|
|
|
3865
4144
|
this.validatorModel = options.validatorModel ?? options.fallbackModel;
|
|
3866
4145
|
this.visionTocExtractorModel = options.visionTocExtractorModel ?? options.fallbackModel;
|
|
3867
4146
|
this.captionParserModel = options.captionParserModel ?? options.fallbackModel;
|
|
4147
|
+
this.hanjaQualitySamplerModel = options.hanjaQualitySamplerModel ?? options.fallbackModel;
|
|
3868
4148
|
this.textCleanerBatchSize = options.textCleanerBatchSize;
|
|
3869
4149
|
this.captionParserBatchSize = options.captionParserBatchSize;
|
|
3870
4150
|
this.captionValidatorBatchSize = options.captionValidatorBatchSize;
|
|
@@ -3884,6 +4164,36 @@ var DocumentProcessor = class {
|
|
|
3884
4164
|
throw error;
|
|
3885
4165
|
}
|
|
3886
4166
|
}
|
|
4167
|
+
/**
|
|
4168
|
+
* Assess the quality of KCJ (Hanja) character recognition in the OCR output.
|
|
4169
|
+
*
|
|
4170
|
+
* Samples pages containing KCJ characters and uses Vision LLM to compare
|
|
4171
|
+
* the OCR text against the original page images. Returns an assessment
|
|
4172
|
+
* indicating whether the document should be re-parsed with VLM pipeline.
|
|
4173
|
+
*
|
|
4174
|
+
* @param doclingDoc - Original document extracted from Docling SDK
|
|
4175
|
+
* @param outputPath - Path containing pages subdirectory (pages/page_0.png, etc.)
|
|
4176
|
+
* @returns Assessment result with severity and recommendation
|
|
4177
|
+
*/
|
|
4178
|
+
async assessHanjaQuality(doclingDoc, outputPath) {
|
|
4179
|
+
this.logger.info(
|
|
4180
|
+
"[DocumentProcessor] Starting Hanja quality assessment..."
|
|
4181
|
+
);
|
|
4182
|
+
const sampler = new HanjaQualitySampler(
|
|
4183
|
+
this.logger,
|
|
4184
|
+
this.hanjaQualitySamplerModel,
|
|
4185
|
+
outputPath,
|
|
4186
|
+
this.maxRetries,
|
|
4187
|
+
this.enableFallbackRetry ? this.fallbackModel : void 0,
|
|
4188
|
+
this.usageAggregator,
|
|
4189
|
+
this.abortSignal
|
|
4190
|
+
);
|
|
4191
|
+
const assessment = await sampler.assess(doclingDoc);
|
|
4192
|
+
this.logger.info(
|
|
4193
|
+
`[DocumentProcessor] Hanja assessment: severity=${assessment.severity}, needsVlmReparse=${assessment.needsVlmReparse}`
|
|
4194
|
+
);
|
|
4195
|
+
return assessment;
|
|
4196
|
+
}
|
|
3887
4197
|
/**
|
|
3888
4198
|
* Converts DoclingDocument to ProcessedDocument with token usage tracking.
|
|
3889
4199
|
*
|
|
@@ -4501,6 +4811,7 @@ var DocumentProcessor = class {
|
|
|
4501
4811
|
CaptionValidator,
|
|
4502
4812
|
ChapterConverter,
|
|
4503
4813
|
DocumentProcessor,
|
|
4814
|
+
HanjaQualitySampler,
|
|
4504
4815
|
PAGE_NUMBER_PATTERN,
|
|
4505
4816
|
PagePattern,
|
|
4506
4817
|
PageRangeParseError,
|