@heripo/document-processor 0.1.33 → 0.1.34

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
@@ -352,6 +352,29 @@ var LLMCaller = class {
352
352
  function formatTokens(usage) {
353
353
  return `${usage.inputTokens} input, ${usage.outputTokens} output, ${usage.totalTokens} total`;
354
354
  }
355
+ function emptyUsage() {
356
+ return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
357
+ }
358
+ function addInto(target, src) {
359
+ target.inputTokens += src.inputTokens;
360
+ target.outputTokens += src.outputTokens;
361
+ target.totalTokens += src.totalTokens;
362
+ }
363
+ function sumUsage(a, b) {
364
+ return {
365
+ inputTokens: (a?.inputTokens ?? 0) + (b?.inputTokens ?? 0),
366
+ outputTokens: (a?.outputTokens ?? 0) + (b?.outputTokens ?? 0),
367
+ totalTokens: (a?.totalTokens ?? 0) + (b?.totalTokens ?? 0)
368
+ };
369
+ }
370
+ function accumulateModel(bucket, modelName, usage) {
371
+ let entry = bucket.get(modelName);
372
+ if (!entry) {
373
+ entry = emptyUsage();
374
+ bucket.set(modelName, entry);
375
+ }
376
+ addInto(entry, usage);
377
+ }
355
378
  var LLMTokenUsageAggregator = class {
356
379
  usage = {};
357
380
  /**
@@ -364,21 +387,15 @@ var LLMTokenUsageAggregator = class {
364
387
  this.usage[usage.component] = {
365
388
  component: usage.component,
366
389
  phases: {},
367
- total: {
368
- inputTokens: 0,
369
- outputTokens: 0,
370
- totalTokens: 0
371
- }
390
+ total: emptyUsage()
372
391
  };
373
392
  }
374
393
  const component = this.usage[usage.component];
375
394
  if (!component.phases[usage.phase]) {
376
395
  component.phases[usage.phase] = {
377
- total: {
378
- inputTokens: 0,
379
- outputTokens: 0,
380
- totalTokens: 0
381
- },
396
+ primary: /* @__PURE__ */ new Map(),
397
+ fallback: /* @__PURE__ */ new Map(),
398
+ total: emptyUsage(),
382
399
  metadata: []
383
400
  };
384
401
  }
@@ -387,41 +404,17 @@ var LLMTokenUsageAggregator = class {
387
404
  phase.metadata.push(usage.metadata);
388
405
  }
389
406
  if (usage.model === "primary") {
390
- if (!phase.primary) {
391
- phase.primary = {
392
- modelName: usage.modelName,
393
- inputTokens: 0,
394
- outputTokens: 0,
395
- totalTokens: 0
396
- };
397
- }
398
- phase.primary.inputTokens += usage.inputTokens;
399
- phase.primary.outputTokens += usage.outputTokens;
400
- phase.primary.totalTokens += usage.totalTokens;
407
+ accumulateModel(phase.primary, usage.modelName, usage);
401
408
  } else if (usage.model === "fallback") {
402
- if (!phase.fallback) {
403
- phase.fallback = {
404
- modelName: usage.modelName,
405
- inputTokens: 0,
406
- outputTokens: 0,
407
- totalTokens: 0
408
- };
409
- }
410
- phase.fallback.inputTokens += usage.inputTokens;
411
- phase.fallback.outputTokens += usage.outputTokens;
412
- phase.fallback.totalTokens += usage.totalTokens;
409
+ accumulateModel(phase.fallback, usage.modelName, usage);
413
410
  }
414
- phase.total.inputTokens += usage.inputTokens;
415
- phase.total.outputTokens += usage.outputTokens;
416
- phase.total.totalTokens += usage.totalTokens;
417
- component.total.inputTokens += usage.inputTokens;
418
- component.total.outputTokens += usage.outputTokens;
419
- component.total.totalTokens += usage.totalTokens;
411
+ addInto(phase.total, usage);
412
+ addInto(component.total, usage);
420
413
  }
421
414
  /**
422
415
  * Get aggregated usage grouped by component
423
416
  *
424
- * @returns Array of component aggregates with phase breakdown
417
+ * @returns Array of component aggregates with per-model phase breakdown
425
418
  */
426
419
  getByComponent() {
427
420
  return Object.values(this.usage);
@@ -430,8 +423,16 @@ var LLMTokenUsageAggregator = class {
430
423
  * Get token usage report in structured JSON format
431
424
  *
432
425
  * Converts internal usage data to external TokenUsageReport format suitable
433
- * for serialization and reporting. The report includes component breakdown,
434
- * phase-level details, and both primary and fallback model usage.
426
+ * for serialization and reporting.
427
+ *
428
+ * Each (phase, model) pair becomes its own PhaseUsageReport entry. A phase
429
+ * that used a single primary (and/or a single fallback) model yields exactly
430
+ * one entry — identical to the legacy shape — while a phase that mixed models
431
+ * yields one entry per model, all sharing the same `phase` name. Every
432
+ * (phase, tier, modelName) combination appears in exactly one entry, so
433
+ * consumers that key by `${component}|${phase}|${tier}|${modelName}`
434
+ * (heripo-web's token-usage emitter / ledger recorder) attribute each model's
435
+ * tokens — and cost — correctly without double counting.
435
436
  *
436
437
  * @returns Structured token usage report with components and total
437
438
  */
@@ -440,34 +441,41 @@ var LLMTokenUsageAggregator = class {
440
441
  for (const component of Object.values(this.usage)) {
441
442
  const phases = [];
442
443
  for (const [phaseName, phaseData] of Object.entries(component.phases)) {
443
- const phaseReport = {
444
- phase: phaseName,
445
- total: {
446
- inputTokens: phaseData.total.inputTokens,
447
- outputTokens: phaseData.total.outputTokens,
448
- totalTokens: phaseData.total.totalTokens
449
- }
450
- };
451
- if (phaseData.metadata.length > 0) {
452
- phaseReport.metadata = [...phaseData.metadata];
453
- }
454
- if (phaseData.primary) {
455
- phaseReport.primary = {
456
- modelName: phaseData.primary.modelName,
457
- inputTokens: phaseData.primary.inputTokens,
458
- outputTokens: phaseData.primary.outputTokens,
459
- totalTokens: phaseData.primary.totalTokens
444
+ const primaryEntries = [...phaseData.primary.entries()];
445
+ const fallbackEntries = [...phaseData.fallback.entries()];
446
+ const entryCount = Math.max(
447
+ primaryEntries.length,
448
+ fallbackEntries.length
449
+ );
450
+ if (entryCount === 0) {
451
+ const phaseReport = {
452
+ phase: phaseName,
453
+ total: { ...phaseData.total }
460
454
  };
455
+ if (phaseData.metadata.length > 0) {
456
+ phaseReport.metadata = [...phaseData.metadata];
457
+ }
458
+ phases.push(phaseReport);
459
+ continue;
461
460
  }
462
- if (phaseData.fallback) {
463
- phaseReport.fallback = {
464
- modelName: phaseData.fallback.modelName,
465
- inputTokens: phaseData.fallback.inputTokens,
466
- outputTokens: phaseData.fallback.outputTokens,
467
- totalTokens: phaseData.fallback.totalTokens
461
+ for (let i = 0; i < entryCount; i++) {
462
+ const primary = primaryEntries[i];
463
+ const fallback = fallbackEntries[i];
464
+ const phaseReport = {
465
+ phase: phaseName,
466
+ total: sumUsage(primary?.[1], fallback?.[1])
468
467
  };
468
+ if (primary) {
469
+ phaseReport.primary = { modelName: primary[0], ...primary[1] };
470
+ }
471
+ if (fallback) {
472
+ phaseReport.fallback = { modelName: fallback[0], ...fallback[1] };
473
+ }
474
+ if (i === 0 && phaseData.metadata.length > 0) {
475
+ phaseReport.metadata = [...phaseData.metadata];
476
+ }
477
+ phases.push(phaseReport);
469
478
  }
470
- phases.push(phaseReport);
471
479
  }
472
480
  components.push({
473
481
  component: component.component,
@@ -512,8 +520,8 @@ var LLMTokenUsageAggregator = class {
512
520
  /**
513
521
  * Log comprehensive token usage summary
514
522
  *
515
- * Outputs usage grouped by component, with phase and model breakdown.
516
- * Shows primary and fallback token usage separately for each phase.
523
+ * Outputs usage grouped by component, with phase and per-model breakdown.
524
+ * Shows each primary and fallback model on its own line for each phase.
517
525
  * Call this once at the end of document processing.
518
526
  *
519
527
  * @param logger - Logger instance for output
@@ -529,31 +537,23 @@ var LLMTokenUsageAggregator = class {
529
537
  let grandInputTokens = 0;
530
538
  let grandOutputTokens = 0;
531
539
  let grandTotalTokens = 0;
532
- let grandPrimaryInputTokens = 0;
533
- let grandPrimaryOutputTokens = 0;
534
- let grandPrimaryTotalTokens = 0;
535
- let grandFallbackInputTokens = 0;
536
- let grandFallbackOutputTokens = 0;
537
- let grandFallbackTotalTokens = 0;
540
+ const grandPrimary = emptyUsage();
541
+ const grandFallback = emptyUsage();
538
542
  for (const component of components) {
539
543
  logger.info(`${component.component}:`);
540
- for (const [phase, phaseData] of Object.entries(component.phases)) {
541
- logger.info(` - ${phase}:`);
542
- if (phaseData.primary) {
544
+ for (const [phaseName, phaseData] of Object.entries(component.phases)) {
545
+ logger.info(` - ${phaseName}:`);
546
+ for (const [modelName, modelUsage] of phaseData.primary) {
543
547
  logger.info(
544
- ` primary (${phaseData.primary.modelName}): ${formatTokens(phaseData.primary)}`
548
+ ` primary (${modelName}): ${formatTokens(modelUsage)}`
545
549
  );
546
- grandPrimaryInputTokens += phaseData.primary.inputTokens;
547
- grandPrimaryOutputTokens += phaseData.primary.outputTokens;
548
- grandPrimaryTotalTokens += phaseData.primary.totalTokens;
550
+ addInto(grandPrimary, modelUsage);
549
551
  }
550
- if (phaseData.fallback) {
552
+ for (const [modelName, modelUsage] of phaseData.fallback) {
551
553
  logger.info(
552
- ` fallback (${phaseData.fallback.modelName}): ${formatTokens(phaseData.fallback)}`
554
+ ` fallback (${modelName}): ${formatTokens(modelUsage)}`
553
555
  );
554
- grandFallbackInputTokens += phaseData.fallback.inputTokens;
555
- grandFallbackOutputTokens += phaseData.fallback.outputTokens;
556
- grandFallbackTotalTokens += phaseData.fallback.totalTokens;
556
+ addInto(grandFallback, modelUsage);
557
557
  }
558
558
  logger.info(` subtotal: ${formatTokens(phaseData.total)}`);
559
559
  }
@@ -566,23 +566,11 @@ var LLMTokenUsageAggregator = class {
566
566
  grandTotalTokens += component.total.totalTokens;
567
567
  }
568
568
  logger.info("--- Summary ---");
569
- if (grandPrimaryTotalTokens > 0) {
570
- logger.info(
571
- `Primary total: ${formatTokens({
572
- inputTokens: grandPrimaryInputTokens,
573
- outputTokens: grandPrimaryOutputTokens,
574
- totalTokens: grandPrimaryTotalTokens
575
- })}`
576
- );
569
+ if (grandPrimary.totalTokens > 0) {
570
+ logger.info(`Primary total: ${formatTokens(grandPrimary)}`);
577
571
  }
578
- if (grandFallbackTotalTokens > 0) {
579
- logger.info(
580
- `Fallback total: ${formatTokens({
581
- inputTokens: grandFallbackInputTokens,
582
- outputTokens: grandFallbackOutputTokens,
583
- totalTokens: grandFallbackTotalTokens
584
- })}`
585
- );
572
+ if (grandFallback.totalTokens > 0) {
573
+ logger.info(`Fallback total: ${formatTokens(grandFallback)}`);
586
574
  }
587
575
  logger.info(
588
576
  `Grand total: ${formatTokens({
@@ -3219,11 +3207,11 @@ var PageRangeParser = class extends VisionLLMComponent {
3219
3207
  const pages = this.extractPages(doclingDoc);
3220
3208
  if (pages.length === 0) {
3221
3209
  this.log("warn", "No pages found");
3222
- const emptyUsage = this.createEmptyUsage("sampling");
3223
- this.trackUsage(emptyUsage);
3210
+ const emptyUsage2 = this.createEmptyUsage("sampling");
3211
+ this.trackUsage(emptyUsage2);
3224
3212
  return {
3225
3213
  pageRangeMap: {},
3226
- usage: [emptyUsage]
3214
+ usage: [emptyUsage2]
3227
3215
  };
3228
3216
  }
3229
3217
  const sizeGroups = this.analyzeSizes(pages);