@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.cjs CHANGED
@@ -377,6 +377,29 @@ var LLMCaller = class {
377
377
  function formatTokens(usage) {
378
378
  return `${usage.inputTokens} input, ${usage.outputTokens} output, ${usage.totalTokens} total`;
379
379
  }
380
+ function emptyUsage() {
381
+ return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
382
+ }
383
+ function addInto(target, src) {
384
+ target.inputTokens += src.inputTokens;
385
+ target.outputTokens += src.outputTokens;
386
+ target.totalTokens += src.totalTokens;
387
+ }
388
+ function sumUsage(a, b) {
389
+ return {
390
+ inputTokens: (a?.inputTokens ?? 0) + (b?.inputTokens ?? 0),
391
+ outputTokens: (a?.outputTokens ?? 0) + (b?.outputTokens ?? 0),
392
+ totalTokens: (a?.totalTokens ?? 0) + (b?.totalTokens ?? 0)
393
+ };
394
+ }
395
+ function accumulateModel(bucket, modelName, usage) {
396
+ let entry = bucket.get(modelName);
397
+ if (!entry) {
398
+ entry = emptyUsage();
399
+ bucket.set(modelName, entry);
400
+ }
401
+ addInto(entry, usage);
402
+ }
380
403
  var LLMTokenUsageAggregator = class {
381
404
  usage = {};
382
405
  /**
@@ -389,21 +412,15 @@ var LLMTokenUsageAggregator = class {
389
412
  this.usage[usage.component] = {
390
413
  component: usage.component,
391
414
  phases: {},
392
- total: {
393
- inputTokens: 0,
394
- outputTokens: 0,
395
- totalTokens: 0
396
- }
415
+ total: emptyUsage()
397
416
  };
398
417
  }
399
418
  const component = this.usage[usage.component];
400
419
  if (!component.phases[usage.phase]) {
401
420
  component.phases[usage.phase] = {
402
- total: {
403
- inputTokens: 0,
404
- outputTokens: 0,
405
- totalTokens: 0
406
- },
421
+ primary: /* @__PURE__ */ new Map(),
422
+ fallback: /* @__PURE__ */ new Map(),
423
+ total: emptyUsage(),
407
424
  metadata: []
408
425
  };
409
426
  }
@@ -412,41 +429,17 @@ var LLMTokenUsageAggregator = class {
412
429
  phase.metadata.push(usage.metadata);
413
430
  }
414
431
  if (usage.model === "primary") {
415
- if (!phase.primary) {
416
- phase.primary = {
417
- modelName: usage.modelName,
418
- inputTokens: 0,
419
- outputTokens: 0,
420
- totalTokens: 0
421
- };
422
- }
423
- phase.primary.inputTokens += usage.inputTokens;
424
- phase.primary.outputTokens += usage.outputTokens;
425
- phase.primary.totalTokens += usage.totalTokens;
432
+ accumulateModel(phase.primary, usage.modelName, usage);
426
433
  } else if (usage.model === "fallback") {
427
- if (!phase.fallback) {
428
- phase.fallback = {
429
- modelName: usage.modelName,
430
- inputTokens: 0,
431
- outputTokens: 0,
432
- totalTokens: 0
433
- };
434
- }
435
- phase.fallback.inputTokens += usage.inputTokens;
436
- phase.fallback.outputTokens += usage.outputTokens;
437
- phase.fallback.totalTokens += usage.totalTokens;
434
+ accumulateModel(phase.fallback, usage.modelName, usage);
438
435
  }
439
- phase.total.inputTokens += usage.inputTokens;
440
- phase.total.outputTokens += usage.outputTokens;
441
- phase.total.totalTokens += usage.totalTokens;
442
- component.total.inputTokens += usage.inputTokens;
443
- component.total.outputTokens += usage.outputTokens;
444
- component.total.totalTokens += usage.totalTokens;
436
+ addInto(phase.total, usage);
437
+ addInto(component.total, usage);
445
438
  }
446
439
  /**
447
440
  * Get aggregated usage grouped by component
448
441
  *
449
- * @returns Array of component aggregates with phase breakdown
442
+ * @returns Array of component aggregates with per-model phase breakdown
450
443
  */
451
444
  getByComponent() {
452
445
  return Object.values(this.usage);
@@ -455,8 +448,16 @@ var LLMTokenUsageAggregator = class {
455
448
  * Get token usage report in structured JSON format
456
449
  *
457
450
  * Converts internal usage data to external TokenUsageReport format suitable
458
- * for serialization and reporting. The report includes component breakdown,
459
- * phase-level details, and both primary and fallback model usage.
451
+ * for serialization and reporting.
452
+ *
453
+ * Each (phase, model) pair becomes its own PhaseUsageReport entry. A phase
454
+ * that used a single primary (and/or a single fallback) model yields exactly
455
+ * one entry — identical to the legacy shape — while a phase that mixed models
456
+ * yields one entry per model, all sharing the same `phase` name. Every
457
+ * (phase, tier, modelName) combination appears in exactly one entry, so
458
+ * consumers that key by `${component}|${phase}|${tier}|${modelName}`
459
+ * (heripo-web's token-usage emitter / ledger recorder) attribute each model's
460
+ * tokens — and cost — correctly without double counting.
460
461
  *
461
462
  * @returns Structured token usage report with components and total
462
463
  */
@@ -465,34 +466,41 @@ var LLMTokenUsageAggregator = class {
465
466
  for (const component of Object.values(this.usage)) {
466
467
  const phases = [];
467
468
  for (const [phaseName, phaseData] of Object.entries(component.phases)) {
468
- const phaseReport = {
469
- phase: phaseName,
470
- total: {
471
- inputTokens: phaseData.total.inputTokens,
472
- outputTokens: phaseData.total.outputTokens,
473
- totalTokens: phaseData.total.totalTokens
474
- }
475
- };
476
- if (phaseData.metadata.length > 0) {
477
- phaseReport.metadata = [...phaseData.metadata];
478
- }
479
- if (phaseData.primary) {
480
- phaseReport.primary = {
481
- modelName: phaseData.primary.modelName,
482
- inputTokens: phaseData.primary.inputTokens,
483
- outputTokens: phaseData.primary.outputTokens,
484
- totalTokens: phaseData.primary.totalTokens
469
+ const primaryEntries = [...phaseData.primary.entries()];
470
+ const fallbackEntries = [...phaseData.fallback.entries()];
471
+ const entryCount = Math.max(
472
+ primaryEntries.length,
473
+ fallbackEntries.length
474
+ );
475
+ if (entryCount === 0) {
476
+ const phaseReport = {
477
+ phase: phaseName,
478
+ total: { ...phaseData.total }
485
479
  };
480
+ if (phaseData.metadata.length > 0) {
481
+ phaseReport.metadata = [...phaseData.metadata];
482
+ }
483
+ phases.push(phaseReport);
484
+ continue;
486
485
  }
487
- if (phaseData.fallback) {
488
- phaseReport.fallback = {
489
- modelName: phaseData.fallback.modelName,
490
- inputTokens: phaseData.fallback.inputTokens,
491
- outputTokens: phaseData.fallback.outputTokens,
492
- totalTokens: phaseData.fallback.totalTokens
486
+ for (let i = 0; i < entryCount; i++) {
487
+ const primary = primaryEntries[i];
488
+ const fallback = fallbackEntries[i];
489
+ const phaseReport = {
490
+ phase: phaseName,
491
+ total: sumUsage(primary?.[1], fallback?.[1])
493
492
  };
493
+ if (primary) {
494
+ phaseReport.primary = { modelName: primary[0], ...primary[1] };
495
+ }
496
+ if (fallback) {
497
+ phaseReport.fallback = { modelName: fallback[0], ...fallback[1] };
498
+ }
499
+ if (i === 0 && phaseData.metadata.length > 0) {
500
+ phaseReport.metadata = [...phaseData.metadata];
501
+ }
502
+ phases.push(phaseReport);
494
503
  }
495
- phases.push(phaseReport);
496
504
  }
497
505
  components.push({
498
506
  component: component.component,
@@ -537,8 +545,8 @@ var LLMTokenUsageAggregator = class {
537
545
  /**
538
546
  * Log comprehensive token usage summary
539
547
  *
540
- * Outputs usage grouped by component, with phase and model breakdown.
541
- * Shows primary and fallback token usage separately for each phase.
548
+ * Outputs usage grouped by component, with phase and per-model breakdown.
549
+ * Shows each primary and fallback model on its own line for each phase.
542
550
  * Call this once at the end of document processing.
543
551
  *
544
552
  * @param logger - Logger instance for output
@@ -554,31 +562,23 @@ var LLMTokenUsageAggregator = class {
554
562
  let grandInputTokens = 0;
555
563
  let grandOutputTokens = 0;
556
564
  let grandTotalTokens = 0;
557
- let grandPrimaryInputTokens = 0;
558
- let grandPrimaryOutputTokens = 0;
559
- let grandPrimaryTotalTokens = 0;
560
- let grandFallbackInputTokens = 0;
561
- let grandFallbackOutputTokens = 0;
562
- let grandFallbackTotalTokens = 0;
565
+ const grandPrimary = emptyUsage();
566
+ const grandFallback = emptyUsage();
563
567
  for (const component of components) {
564
568
  logger.info(`${component.component}:`);
565
- for (const [phase, phaseData] of Object.entries(component.phases)) {
566
- logger.info(` - ${phase}:`);
567
- if (phaseData.primary) {
569
+ for (const [phaseName, phaseData] of Object.entries(component.phases)) {
570
+ logger.info(` - ${phaseName}:`);
571
+ for (const [modelName, modelUsage] of phaseData.primary) {
568
572
  logger.info(
569
- ` primary (${phaseData.primary.modelName}): ${formatTokens(phaseData.primary)}`
573
+ ` primary (${modelName}): ${formatTokens(modelUsage)}`
570
574
  );
571
- grandPrimaryInputTokens += phaseData.primary.inputTokens;
572
- grandPrimaryOutputTokens += phaseData.primary.outputTokens;
573
- grandPrimaryTotalTokens += phaseData.primary.totalTokens;
575
+ addInto(grandPrimary, modelUsage);
574
576
  }
575
- if (phaseData.fallback) {
577
+ for (const [modelName, modelUsage] of phaseData.fallback) {
576
578
  logger.info(
577
- ` fallback (${phaseData.fallback.modelName}): ${formatTokens(phaseData.fallback)}`
579
+ ` fallback (${modelName}): ${formatTokens(modelUsage)}`
578
580
  );
579
- grandFallbackInputTokens += phaseData.fallback.inputTokens;
580
- grandFallbackOutputTokens += phaseData.fallback.outputTokens;
581
- grandFallbackTotalTokens += phaseData.fallback.totalTokens;
581
+ addInto(grandFallback, modelUsage);
582
582
  }
583
583
  logger.info(` subtotal: ${formatTokens(phaseData.total)}`);
584
584
  }
@@ -591,23 +591,11 @@ var LLMTokenUsageAggregator = class {
591
591
  grandTotalTokens += component.total.totalTokens;
592
592
  }
593
593
  logger.info("--- Summary ---");
594
- if (grandPrimaryTotalTokens > 0) {
595
- logger.info(
596
- `Primary total: ${formatTokens({
597
- inputTokens: grandPrimaryInputTokens,
598
- outputTokens: grandPrimaryOutputTokens,
599
- totalTokens: grandPrimaryTotalTokens
600
- })}`
601
- );
594
+ if (grandPrimary.totalTokens > 0) {
595
+ logger.info(`Primary total: ${formatTokens(grandPrimary)}`);
602
596
  }
603
- if (grandFallbackTotalTokens > 0) {
604
- logger.info(
605
- `Fallback total: ${formatTokens({
606
- inputTokens: grandFallbackInputTokens,
607
- outputTokens: grandFallbackOutputTokens,
608
- totalTokens: grandFallbackTotalTokens
609
- })}`
610
- );
597
+ if (grandFallback.totalTokens > 0) {
598
+ logger.info(`Fallback total: ${formatTokens(grandFallback)}`);
611
599
  }
612
600
  logger.info(
613
601
  `Grand total: ${formatTokens({
@@ -10467,6 +10455,7 @@ var PDF_CORRECTION_DEFAULTS = {
10467
10455
  autoApplyThreshold: 0.85,
10468
10456
  proposalThreshold: 0.5,
10469
10457
  forceAutoApply: false,
10458
+ reviewAssistanceEnabled: true,
10470
10459
  temperature: 0
10471
10460
  };
10472
10461
  function normalizeNumber(value, fallback) {
@@ -10566,6 +10555,7 @@ function normalizePDFCorrectionOptions(value) {
10566
10555
  autoApplyThreshold,
10567
10556
  proposalThreshold,
10568
10557
  forceAutoApply: value.forceAutoApply ?? PDF_CORRECTION_DEFAULTS.forceAutoApply,
10558
+ reviewAssistanceEnabled: value.reviewAssistanceEnabled ?? PDF_CORRECTION_DEFAULTS.reviewAssistanceEnabled,
10569
10559
  temperature: normalizeTemperature(
10570
10560
  value.temperature,
10571
10561
  PDF_CORRECTION_DEFAULTS.temperature
@@ -10599,44 +10589,49 @@ var PostDoclingCorrectionPipeline = class {
10599
10589
  onTokenUsage: options.onTokenUsage,
10600
10590
  documentLanguages: options.ocr_lang,
10601
10591
  pageTexts,
10602
- reviewAssistanceGate: {
10592
+ // When review assistance is disabled, omit the gate entirely so the
10593
+ // per-page eligibility checks (pageGate LLM calls + sidecar report)
10594
+ // never run — only text correction executes.
10595
+ reviewAssistanceGate: correction.reviewAssistanceEnabled ? {
10603
10596
  model: correction.models.pageGate,
10604
10597
  fallback: correction.models.pageGateFallback,
10605
10598
  maxRetries: correction.maxRetries.pageGate,
10606
10599
  temperature: correction.temperature,
10607
10600
  outputLanguage: correction.outputLanguage
10608
- }
10609
- }
10610
- );
10611
- const runner = new ReviewAssistanceRunner(this.logger);
10612
- await runner.analyzeAndSave(
10613
- outputDir,
10614
- reportId,
10615
- this.buildReviewAssistanceModelResolver(correction),
10616
- {
10617
- pdfPath,
10618
- pageTexts,
10619
- pageGateModel: correction.models.pageGate,
10620
- pageGateFallback: correction.models.pageGateFallback,
10621
- pageGateMaxRetries: correction.maxRetries.pageGate,
10622
- pageGateTemperature: correction.temperature,
10623
- pageConcurrency: correction.concurrency.pages,
10624
- taskConcurrency: correction.concurrency.reviewTasks,
10625
- modelConcurrency: correction.modelConcurrency,
10626
- workItemTimeoutMs: correction.workItemTimeoutMs,
10627
- autoApplyThreshold: correction.autoApplyThreshold,
10628
- proposalThreshold: correction.proposalThreshold,
10629
- forceAutoApply: correction.forceAutoApply,
10630
- maxRetries: correction.maxRetries.reviewAssistance,
10631
- tableMaxRetries: correction.maxRetries.tableCorrection,
10632
- temperature: correction.temperature,
10633
- outputLanguage: correction.outputLanguage,
10634
- aggregator: options.aggregator,
10635
- abortSignal,
10636
- onTokenUsage: options.onTokenUsage,
10637
- onProgress: options.onReviewAssistanceProgress
10601
+ } : void 0
10638
10602
  }
10639
10603
  );
10604
+ if (correction.reviewAssistanceEnabled) {
10605
+ const runner = new ReviewAssistanceRunner(this.logger);
10606
+ await runner.analyzeAndSave(
10607
+ outputDir,
10608
+ reportId,
10609
+ this.buildReviewAssistanceModelResolver(correction),
10610
+ {
10611
+ pdfPath,
10612
+ pageTexts,
10613
+ pageGateModel: correction.models.pageGate,
10614
+ pageGateFallback: correction.models.pageGateFallback,
10615
+ pageGateMaxRetries: correction.maxRetries.pageGate,
10616
+ pageGateTemperature: correction.temperature,
10617
+ pageConcurrency: correction.concurrency.pages,
10618
+ taskConcurrency: correction.concurrency.reviewTasks,
10619
+ modelConcurrency: correction.modelConcurrency,
10620
+ workItemTimeoutMs: correction.workItemTimeoutMs,
10621
+ autoApplyThreshold: correction.autoApplyThreshold,
10622
+ proposalThreshold: correction.proposalThreshold,
10623
+ forceAutoApply: correction.forceAutoApply,
10624
+ maxRetries: correction.maxRetries.reviewAssistance,
10625
+ tableMaxRetries: correction.maxRetries.tableCorrection,
10626
+ temperature: correction.temperature,
10627
+ outputLanguage: correction.outputLanguage,
10628
+ aggregator: options.aggregator,
10629
+ abortSignal,
10630
+ onTokenUsage: options.onTokenUsage,
10631
+ onProgress: options.onReviewAssistanceProgress
10632
+ }
10633
+ );
10634
+ }
10640
10635
  await originalCallback(outputDir);
10641
10636
  };
10642
10637
  }