@heripo/pdf-parser 0.1.33 → 0.1.35

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.d.cts CHANGED
@@ -54,6 +54,12 @@ interface PDFCorrectionOptions {
54
54
  * Enabled by the engine demo; defaults to false everywhere else.
55
55
  */
56
56
  forceAutoApply?: boolean;
57
+ /**
58
+ * When false, the review-assistance stage (the per-page eligibility gate plus
59
+ * the structural review-assistance runner) is skipped entirely and only the
60
+ * text-correction stage runs. Defaults to true.
61
+ */
62
+ reviewAssistanceEnabled?: boolean;
57
63
  temperature?: number;
58
64
  }
59
65
  interface NormalizedPDFCorrectionOptions {
@@ -67,6 +73,7 @@ interface NormalizedPDFCorrectionOptions {
67
73
  autoApplyThreshold: number;
68
74
  proposalThreshold: number;
69
75
  forceAutoApply: boolean;
76
+ reviewAssistanceEnabled: boolean;
70
77
  temperature: number;
71
78
  }
72
79
  declare const PDF_CORRECTION_DEFAULTS: Omit<NormalizedPDFCorrectionOptions, 'models'>;
package/dist/index.d.ts CHANGED
@@ -54,6 +54,12 @@ interface PDFCorrectionOptions {
54
54
  * Enabled by the engine demo; defaults to false everywhere else.
55
55
  */
56
56
  forceAutoApply?: boolean;
57
+ /**
58
+ * When false, the review-assistance stage (the per-page eligibility gate plus
59
+ * the structural review-assistance runner) is skipped entirely and only the
60
+ * text-correction stage runs. Defaults to true.
61
+ */
62
+ reviewAssistanceEnabled?: boolean;
57
63
  temperature?: number;
58
64
  }
59
65
  interface NormalizedPDFCorrectionOptions {
@@ -67,6 +73,7 @@ interface NormalizedPDFCorrectionOptions {
67
73
  autoApplyThreshold: number;
68
74
  proposalThreshold: number;
69
75
  forceAutoApply: boolean;
76
+ reviewAssistanceEnabled: boolean;
70
77
  temperature: number;
71
78
  }
72
79
  declare const PDF_CORRECTION_DEFAULTS: Omit<NormalizedPDFCorrectionOptions, 'models'>;
package/dist/index.js CHANGED
@@ -343,6 +343,29 @@ var LLMCaller = class {
343
343
  function formatTokens(usage) {
344
344
  return `${usage.inputTokens} input, ${usage.outputTokens} output, ${usage.totalTokens} total`;
345
345
  }
346
+ function emptyUsage() {
347
+ return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
348
+ }
349
+ function addInto(target, src) {
350
+ target.inputTokens += src.inputTokens;
351
+ target.outputTokens += src.outputTokens;
352
+ target.totalTokens += src.totalTokens;
353
+ }
354
+ function sumUsage(a, b) {
355
+ return {
356
+ inputTokens: (a?.inputTokens ?? 0) + (b?.inputTokens ?? 0),
357
+ outputTokens: (a?.outputTokens ?? 0) + (b?.outputTokens ?? 0),
358
+ totalTokens: (a?.totalTokens ?? 0) + (b?.totalTokens ?? 0)
359
+ };
360
+ }
361
+ function accumulateModel(bucket, modelName, usage) {
362
+ let entry = bucket.get(modelName);
363
+ if (!entry) {
364
+ entry = emptyUsage();
365
+ bucket.set(modelName, entry);
366
+ }
367
+ addInto(entry, usage);
368
+ }
346
369
  var LLMTokenUsageAggregator = class {
347
370
  usage = {};
348
371
  /**
@@ -355,21 +378,15 @@ var LLMTokenUsageAggregator = class {
355
378
  this.usage[usage.component] = {
356
379
  component: usage.component,
357
380
  phases: {},
358
- total: {
359
- inputTokens: 0,
360
- outputTokens: 0,
361
- totalTokens: 0
362
- }
381
+ total: emptyUsage()
363
382
  };
364
383
  }
365
384
  const component = this.usage[usage.component];
366
385
  if (!component.phases[usage.phase]) {
367
386
  component.phases[usage.phase] = {
368
- total: {
369
- inputTokens: 0,
370
- outputTokens: 0,
371
- totalTokens: 0
372
- },
387
+ primary: /* @__PURE__ */ new Map(),
388
+ fallback: /* @__PURE__ */ new Map(),
389
+ total: emptyUsage(),
373
390
  metadata: []
374
391
  };
375
392
  }
@@ -378,41 +395,17 @@ var LLMTokenUsageAggregator = class {
378
395
  phase.metadata.push(usage.metadata);
379
396
  }
380
397
  if (usage.model === "primary") {
381
- if (!phase.primary) {
382
- phase.primary = {
383
- modelName: usage.modelName,
384
- inputTokens: 0,
385
- outputTokens: 0,
386
- totalTokens: 0
387
- };
388
- }
389
- phase.primary.inputTokens += usage.inputTokens;
390
- phase.primary.outputTokens += usage.outputTokens;
391
- phase.primary.totalTokens += usage.totalTokens;
398
+ accumulateModel(phase.primary, usage.modelName, usage);
392
399
  } else if (usage.model === "fallback") {
393
- if (!phase.fallback) {
394
- phase.fallback = {
395
- modelName: usage.modelName,
396
- inputTokens: 0,
397
- outputTokens: 0,
398
- totalTokens: 0
399
- };
400
- }
401
- phase.fallback.inputTokens += usage.inputTokens;
402
- phase.fallback.outputTokens += usage.outputTokens;
403
- phase.fallback.totalTokens += usage.totalTokens;
400
+ accumulateModel(phase.fallback, usage.modelName, usage);
404
401
  }
405
- phase.total.inputTokens += usage.inputTokens;
406
- phase.total.outputTokens += usage.outputTokens;
407
- phase.total.totalTokens += usage.totalTokens;
408
- component.total.inputTokens += usage.inputTokens;
409
- component.total.outputTokens += usage.outputTokens;
410
- component.total.totalTokens += usage.totalTokens;
402
+ addInto(phase.total, usage);
403
+ addInto(component.total, usage);
411
404
  }
412
405
  /**
413
406
  * Get aggregated usage grouped by component
414
407
  *
415
- * @returns Array of component aggregates with phase breakdown
408
+ * @returns Array of component aggregates with per-model phase breakdown
416
409
  */
417
410
  getByComponent() {
418
411
  return Object.values(this.usage);
@@ -421,8 +414,16 @@ var LLMTokenUsageAggregator = class {
421
414
  * Get token usage report in structured JSON format
422
415
  *
423
416
  * Converts internal usage data to external TokenUsageReport format suitable
424
- * for serialization and reporting. The report includes component breakdown,
425
- * phase-level details, and both primary and fallback model usage.
417
+ * for serialization and reporting.
418
+ *
419
+ * Each (phase, model) pair becomes its own PhaseUsageReport entry. A phase
420
+ * that used a single primary (and/or a single fallback) model yields exactly
421
+ * one entry — identical to the legacy shape — while a phase that mixed models
422
+ * yields one entry per model, all sharing the same `phase` name. Every
423
+ * (phase, tier, modelName) combination appears in exactly one entry, so
424
+ * consumers that key by `${component}|${phase}|${tier}|${modelName}`
425
+ * (heripo-web's token-usage emitter / ledger recorder) attribute each model's
426
+ * tokens — and cost — correctly without double counting.
426
427
  *
427
428
  * @returns Structured token usage report with components and total
428
429
  */
@@ -431,34 +432,41 @@ var LLMTokenUsageAggregator = class {
431
432
  for (const component of Object.values(this.usage)) {
432
433
  const phases = [];
433
434
  for (const [phaseName, phaseData] of Object.entries(component.phases)) {
434
- const phaseReport = {
435
- phase: phaseName,
436
- total: {
437
- inputTokens: phaseData.total.inputTokens,
438
- outputTokens: phaseData.total.outputTokens,
439
- totalTokens: phaseData.total.totalTokens
440
- }
441
- };
442
- if (phaseData.metadata.length > 0) {
443
- phaseReport.metadata = [...phaseData.metadata];
444
- }
445
- if (phaseData.primary) {
446
- phaseReport.primary = {
447
- modelName: phaseData.primary.modelName,
448
- inputTokens: phaseData.primary.inputTokens,
449
- outputTokens: phaseData.primary.outputTokens,
450
- totalTokens: phaseData.primary.totalTokens
435
+ const primaryEntries = [...phaseData.primary.entries()];
436
+ const fallbackEntries = [...phaseData.fallback.entries()];
437
+ const entryCount = Math.max(
438
+ primaryEntries.length,
439
+ fallbackEntries.length
440
+ );
441
+ if (entryCount === 0) {
442
+ const phaseReport = {
443
+ phase: phaseName,
444
+ total: { ...phaseData.total }
451
445
  };
446
+ if (phaseData.metadata.length > 0) {
447
+ phaseReport.metadata = [...phaseData.metadata];
448
+ }
449
+ phases.push(phaseReport);
450
+ continue;
452
451
  }
453
- if (phaseData.fallback) {
454
- phaseReport.fallback = {
455
- modelName: phaseData.fallback.modelName,
456
- inputTokens: phaseData.fallback.inputTokens,
457
- outputTokens: phaseData.fallback.outputTokens,
458
- totalTokens: phaseData.fallback.totalTokens
452
+ for (let i = 0; i < entryCount; i++) {
453
+ const primary = primaryEntries[i];
454
+ const fallback = fallbackEntries[i];
455
+ const phaseReport = {
456
+ phase: phaseName,
457
+ total: sumUsage(primary?.[1], fallback?.[1])
459
458
  };
459
+ if (primary) {
460
+ phaseReport.primary = { modelName: primary[0], ...primary[1] };
461
+ }
462
+ if (fallback) {
463
+ phaseReport.fallback = { modelName: fallback[0], ...fallback[1] };
464
+ }
465
+ if (i === 0 && phaseData.metadata.length > 0) {
466
+ phaseReport.metadata = [...phaseData.metadata];
467
+ }
468
+ phases.push(phaseReport);
460
469
  }
461
- phases.push(phaseReport);
462
470
  }
463
471
  components.push({
464
472
  component: component.component,
@@ -503,8 +511,8 @@ var LLMTokenUsageAggregator = class {
503
511
  /**
504
512
  * Log comprehensive token usage summary
505
513
  *
506
- * Outputs usage grouped by component, with phase and model breakdown.
507
- * Shows primary and fallback token usage separately for each phase.
514
+ * Outputs usage grouped by component, with phase and per-model breakdown.
515
+ * Shows each primary and fallback model on its own line for each phase.
508
516
  * Call this once at the end of document processing.
509
517
  *
510
518
  * @param logger - Logger instance for output
@@ -520,31 +528,23 @@ var LLMTokenUsageAggregator = class {
520
528
  let grandInputTokens = 0;
521
529
  let grandOutputTokens = 0;
522
530
  let grandTotalTokens = 0;
523
- let grandPrimaryInputTokens = 0;
524
- let grandPrimaryOutputTokens = 0;
525
- let grandPrimaryTotalTokens = 0;
526
- let grandFallbackInputTokens = 0;
527
- let grandFallbackOutputTokens = 0;
528
- let grandFallbackTotalTokens = 0;
531
+ const grandPrimary = emptyUsage();
532
+ const grandFallback = emptyUsage();
529
533
  for (const component of components) {
530
534
  logger.info(`${component.component}:`);
531
- for (const [phase, phaseData] of Object.entries(component.phases)) {
532
- logger.info(` - ${phase}:`);
533
- if (phaseData.primary) {
535
+ for (const [phaseName, phaseData] of Object.entries(component.phases)) {
536
+ logger.info(` - ${phaseName}:`);
537
+ for (const [modelName, modelUsage] of phaseData.primary) {
534
538
  logger.info(
535
- ` primary (${phaseData.primary.modelName}): ${formatTokens(phaseData.primary)}`
539
+ ` primary (${modelName}): ${formatTokens(modelUsage)}`
536
540
  );
537
- grandPrimaryInputTokens += phaseData.primary.inputTokens;
538
- grandPrimaryOutputTokens += phaseData.primary.outputTokens;
539
- grandPrimaryTotalTokens += phaseData.primary.totalTokens;
541
+ addInto(grandPrimary, modelUsage);
540
542
  }
541
- if (phaseData.fallback) {
543
+ for (const [modelName, modelUsage] of phaseData.fallback) {
542
544
  logger.info(
543
- ` fallback (${phaseData.fallback.modelName}): ${formatTokens(phaseData.fallback)}`
545
+ ` fallback (${modelName}): ${formatTokens(modelUsage)}`
544
546
  );
545
- grandFallbackInputTokens += phaseData.fallback.inputTokens;
546
- grandFallbackOutputTokens += phaseData.fallback.outputTokens;
547
- grandFallbackTotalTokens += phaseData.fallback.totalTokens;
547
+ addInto(grandFallback, modelUsage);
548
548
  }
549
549
  logger.info(` subtotal: ${formatTokens(phaseData.total)}`);
550
550
  }
@@ -557,23 +557,11 @@ var LLMTokenUsageAggregator = class {
557
557
  grandTotalTokens += component.total.totalTokens;
558
558
  }
559
559
  logger.info("--- Summary ---");
560
- if (grandPrimaryTotalTokens > 0) {
561
- logger.info(
562
- `Primary total: ${formatTokens({
563
- inputTokens: grandPrimaryInputTokens,
564
- outputTokens: grandPrimaryOutputTokens,
565
- totalTokens: grandPrimaryTotalTokens
566
- })}`
567
- );
560
+ if (grandPrimary.totalTokens > 0) {
561
+ logger.info(`Primary total: ${formatTokens(grandPrimary)}`);
568
562
  }
569
- if (grandFallbackTotalTokens > 0) {
570
- logger.info(
571
- `Fallback total: ${formatTokens({
572
- inputTokens: grandFallbackInputTokens,
573
- outputTokens: grandFallbackOutputTokens,
574
- totalTokens: grandFallbackTotalTokens
575
- })}`
576
- );
563
+ if (grandFallback.totalTokens > 0) {
564
+ logger.info(`Fallback total: ${formatTokens(grandFallback)}`);
577
565
  }
578
566
  logger.info(
579
567
  `Grand total: ${formatTokens({
@@ -10449,6 +10437,7 @@ var PDF_CORRECTION_DEFAULTS = {
10449
10437
  autoApplyThreshold: 0.85,
10450
10438
  proposalThreshold: 0.5,
10451
10439
  forceAutoApply: false,
10440
+ reviewAssistanceEnabled: true,
10452
10441
  temperature: 0
10453
10442
  };
10454
10443
  function normalizeNumber(value, fallback) {
@@ -10548,6 +10537,7 @@ function normalizePDFCorrectionOptions(value) {
10548
10537
  autoApplyThreshold,
10549
10538
  proposalThreshold,
10550
10539
  forceAutoApply: value.forceAutoApply ?? PDF_CORRECTION_DEFAULTS.forceAutoApply,
10540
+ reviewAssistanceEnabled: value.reviewAssistanceEnabled ?? PDF_CORRECTION_DEFAULTS.reviewAssistanceEnabled,
10551
10541
  temperature: normalizeTemperature(
10552
10542
  value.temperature,
10553
10543
  PDF_CORRECTION_DEFAULTS.temperature
@@ -10581,44 +10571,49 @@ var PostDoclingCorrectionPipeline = class {
10581
10571
  onTokenUsage: options.onTokenUsage,
10582
10572
  documentLanguages: options.ocr_lang,
10583
10573
  pageTexts,
10584
- reviewAssistanceGate: {
10574
+ // When review assistance is disabled, omit the gate entirely so the
10575
+ // per-page eligibility checks (pageGate LLM calls + sidecar report)
10576
+ // never run — only text correction executes.
10577
+ reviewAssistanceGate: correction.reviewAssistanceEnabled ? {
10585
10578
  model: correction.models.pageGate,
10586
10579
  fallback: correction.models.pageGateFallback,
10587
10580
  maxRetries: correction.maxRetries.pageGate,
10588
10581
  temperature: correction.temperature,
10589
10582
  outputLanguage: correction.outputLanguage
10590
- }
10591
- }
10592
- );
10593
- const runner = new ReviewAssistanceRunner(this.logger);
10594
- await runner.analyzeAndSave(
10595
- outputDir,
10596
- reportId,
10597
- this.buildReviewAssistanceModelResolver(correction),
10598
- {
10599
- pdfPath,
10600
- pageTexts,
10601
- pageGateModel: correction.models.pageGate,
10602
- pageGateFallback: correction.models.pageGateFallback,
10603
- pageGateMaxRetries: correction.maxRetries.pageGate,
10604
- pageGateTemperature: correction.temperature,
10605
- pageConcurrency: correction.concurrency.pages,
10606
- taskConcurrency: correction.concurrency.reviewTasks,
10607
- modelConcurrency: correction.modelConcurrency,
10608
- workItemTimeoutMs: correction.workItemTimeoutMs,
10609
- autoApplyThreshold: correction.autoApplyThreshold,
10610
- proposalThreshold: correction.proposalThreshold,
10611
- forceAutoApply: correction.forceAutoApply,
10612
- maxRetries: correction.maxRetries.reviewAssistance,
10613
- tableMaxRetries: correction.maxRetries.tableCorrection,
10614
- temperature: correction.temperature,
10615
- outputLanguage: correction.outputLanguage,
10616
- aggregator: options.aggregator,
10617
- abortSignal,
10618
- onTokenUsage: options.onTokenUsage,
10619
- onProgress: options.onReviewAssistanceProgress
10583
+ } : void 0
10620
10584
  }
10621
10585
  );
10586
+ if (correction.reviewAssistanceEnabled) {
10587
+ const runner = new ReviewAssistanceRunner(this.logger);
10588
+ await runner.analyzeAndSave(
10589
+ outputDir,
10590
+ reportId,
10591
+ this.buildReviewAssistanceModelResolver(correction),
10592
+ {
10593
+ pdfPath,
10594
+ pageTexts,
10595
+ pageGateModel: correction.models.pageGate,
10596
+ pageGateFallback: correction.models.pageGateFallback,
10597
+ pageGateMaxRetries: correction.maxRetries.pageGate,
10598
+ pageGateTemperature: correction.temperature,
10599
+ pageConcurrency: correction.concurrency.pages,
10600
+ taskConcurrency: correction.concurrency.reviewTasks,
10601
+ modelConcurrency: correction.modelConcurrency,
10602
+ workItemTimeoutMs: correction.workItemTimeoutMs,
10603
+ autoApplyThreshold: correction.autoApplyThreshold,
10604
+ proposalThreshold: correction.proposalThreshold,
10605
+ forceAutoApply: correction.forceAutoApply,
10606
+ maxRetries: correction.maxRetries.reviewAssistance,
10607
+ tableMaxRetries: correction.maxRetries.tableCorrection,
10608
+ temperature: correction.temperature,
10609
+ outputLanguage: correction.outputLanguage,
10610
+ aggregator: options.aggregator,
10611
+ abortSignal,
10612
+ onTokenUsage: options.onTokenUsage,
10613
+ onProgress: options.onReviewAssistanceProgress
10614
+ }
10615
+ );
10616
+ }
10622
10617
  await originalCallback(outputDir);
10623
10618
  };
10624
10619
  }