@heripo/document-processor 0.1.32 → 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 +100 -106
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +100 -106
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -86,6 +86,8 @@ function detectProvider(model) {
|
|
|
86
86
|
if (providerId.includes("google")) return "google";
|
|
87
87
|
if (providerId.includes("anthropic")) return "anthropic";
|
|
88
88
|
if (providerId.includes("together")) return "togetherai";
|
|
89
|
+
if (providerId.includes("ollama")) return "ollama";
|
|
90
|
+
if (providerId.includes("lmstudio")) return "lmstudio";
|
|
89
91
|
return "unknown";
|
|
90
92
|
}
|
|
91
93
|
var LLMCaller = class {
|
|
@@ -173,14 +175,18 @@ var LLMCaller = class {
|
|
|
173
175
|
*
|
|
174
176
|
* Strategy per provider:
|
|
175
177
|
* - OpenAI / Anthropic / Google Gemini: `Output.object()` with schema retry
|
|
176
|
-
* - Together AI / unknown: forced tool call pattern
|
|
178
|
+
* - Together AI / Ollama / unknown: forced tool call pattern
|
|
179
|
+
*
|
|
180
|
+
* Ollama uses tool-call because MLX backends ignore the `format` grammar
|
|
181
|
+
* that `Output.object()` relies on, and gguf's grammar is unstable once
|
|
182
|
+
* thinking is disabled. Applies to both text and vision paths.
|
|
177
183
|
*
|
|
178
184
|
* Retries up to MAX_STRUCTURED_OUTPUT_RETRIES times on NoObjectGeneratedError
|
|
179
185
|
* (schema mismatch), re-throwing the last error if all attempts fail.
|
|
180
186
|
*/
|
|
181
187
|
static async generateStructuredOutput(model, schema, promptParams) {
|
|
182
188
|
const providerType = detectProvider(model);
|
|
183
|
-
if (providerType === "togetherai" || providerType === "unknown") {
|
|
189
|
+
if (providerType === "togetherai" || providerType === "ollama" || providerType === "unknown") {
|
|
184
190
|
return this.generateViaToolCall(model, schema, promptParams);
|
|
185
191
|
}
|
|
186
192
|
let lastError;
|
|
@@ -285,6 +291,29 @@ var LLMCaller = class {
|
|
|
285
291
|
function formatTokens(usage) {
|
|
286
292
|
return `${usage.inputTokens} input, ${usage.outputTokens} output, ${usage.totalTokens} total`;
|
|
287
293
|
}
|
|
294
|
+
function emptyUsage() {
|
|
295
|
+
return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
296
|
+
}
|
|
297
|
+
function addInto(target, src) {
|
|
298
|
+
target.inputTokens += src.inputTokens;
|
|
299
|
+
target.outputTokens += src.outputTokens;
|
|
300
|
+
target.totalTokens += src.totalTokens;
|
|
301
|
+
}
|
|
302
|
+
function sumUsage(a, b) {
|
|
303
|
+
return {
|
|
304
|
+
inputTokens: (a?.inputTokens ?? 0) + (b?.inputTokens ?? 0),
|
|
305
|
+
outputTokens: (a?.outputTokens ?? 0) + (b?.outputTokens ?? 0),
|
|
306
|
+
totalTokens: (a?.totalTokens ?? 0) + (b?.totalTokens ?? 0)
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function accumulateModel(bucket, modelName, usage) {
|
|
310
|
+
let entry = bucket.get(modelName);
|
|
311
|
+
if (!entry) {
|
|
312
|
+
entry = emptyUsage();
|
|
313
|
+
bucket.set(modelName, entry);
|
|
314
|
+
}
|
|
315
|
+
addInto(entry, usage);
|
|
316
|
+
}
|
|
288
317
|
var LLMTokenUsageAggregator = class {
|
|
289
318
|
usage = {};
|
|
290
319
|
/**
|
|
@@ -297,21 +326,15 @@ var LLMTokenUsageAggregator = class {
|
|
|
297
326
|
this.usage[usage.component] = {
|
|
298
327
|
component: usage.component,
|
|
299
328
|
phases: {},
|
|
300
|
-
total:
|
|
301
|
-
inputTokens: 0,
|
|
302
|
-
outputTokens: 0,
|
|
303
|
-
totalTokens: 0
|
|
304
|
-
}
|
|
329
|
+
total: emptyUsage()
|
|
305
330
|
};
|
|
306
331
|
}
|
|
307
332
|
const component = this.usage[usage.component];
|
|
308
333
|
if (!component.phases[usage.phase]) {
|
|
309
334
|
component.phases[usage.phase] = {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
totalTokens: 0
|
|
314
|
-
},
|
|
335
|
+
primary: /* @__PURE__ */ new Map(),
|
|
336
|
+
fallback: /* @__PURE__ */ new Map(),
|
|
337
|
+
total: emptyUsage(),
|
|
315
338
|
metadata: []
|
|
316
339
|
};
|
|
317
340
|
}
|
|
@@ -320,41 +343,17 @@ var LLMTokenUsageAggregator = class {
|
|
|
320
343
|
phase.metadata.push(usage.metadata);
|
|
321
344
|
}
|
|
322
345
|
if (usage.model === "primary") {
|
|
323
|
-
|
|
324
|
-
phase.primary = {
|
|
325
|
-
modelName: usage.modelName,
|
|
326
|
-
inputTokens: 0,
|
|
327
|
-
outputTokens: 0,
|
|
328
|
-
totalTokens: 0
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
phase.primary.inputTokens += usage.inputTokens;
|
|
332
|
-
phase.primary.outputTokens += usage.outputTokens;
|
|
333
|
-
phase.primary.totalTokens += usage.totalTokens;
|
|
346
|
+
accumulateModel(phase.primary, usage.modelName, usage);
|
|
334
347
|
} else if (usage.model === "fallback") {
|
|
335
|
-
|
|
336
|
-
phase.fallback = {
|
|
337
|
-
modelName: usage.modelName,
|
|
338
|
-
inputTokens: 0,
|
|
339
|
-
outputTokens: 0,
|
|
340
|
-
totalTokens: 0
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
phase.fallback.inputTokens += usage.inputTokens;
|
|
344
|
-
phase.fallback.outputTokens += usage.outputTokens;
|
|
345
|
-
phase.fallback.totalTokens += usage.totalTokens;
|
|
348
|
+
accumulateModel(phase.fallback, usage.modelName, usage);
|
|
346
349
|
}
|
|
347
|
-
phase.total
|
|
348
|
-
|
|
349
|
-
phase.total.totalTokens += usage.totalTokens;
|
|
350
|
-
component.total.inputTokens += usage.inputTokens;
|
|
351
|
-
component.total.outputTokens += usage.outputTokens;
|
|
352
|
-
component.total.totalTokens += usage.totalTokens;
|
|
350
|
+
addInto(phase.total, usage);
|
|
351
|
+
addInto(component.total, usage);
|
|
353
352
|
}
|
|
354
353
|
/**
|
|
355
354
|
* Get aggregated usage grouped by component
|
|
356
355
|
*
|
|
357
|
-
* @returns Array of component aggregates with phase breakdown
|
|
356
|
+
* @returns Array of component aggregates with per-model phase breakdown
|
|
358
357
|
*/
|
|
359
358
|
getByComponent() {
|
|
360
359
|
return Object.values(this.usage);
|
|
@@ -363,8 +362,16 @@ var LLMTokenUsageAggregator = class {
|
|
|
363
362
|
* Get token usage report in structured JSON format
|
|
364
363
|
*
|
|
365
364
|
* Converts internal usage data to external TokenUsageReport format suitable
|
|
366
|
-
* for serialization and reporting.
|
|
367
|
-
*
|
|
365
|
+
* for serialization and reporting.
|
|
366
|
+
*
|
|
367
|
+
* Each (phase, model) pair becomes its own PhaseUsageReport entry. A phase
|
|
368
|
+
* that used a single primary (and/or a single fallback) model yields exactly
|
|
369
|
+
* one entry — identical to the legacy shape — while a phase that mixed models
|
|
370
|
+
* yields one entry per model, all sharing the same `phase` name. Every
|
|
371
|
+
* (phase, tier, modelName) combination appears in exactly one entry, so
|
|
372
|
+
* consumers that key by `${component}|${phase}|${tier}|${modelName}`
|
|
373
|
+
* (heripo-web's token-usage emitter / ledger recorder) attribute each model's
|
|
374
|
+
* tokens — and cost — correctly without double counting.
|
|
368
375
|
*
|
|
369
376
|
* @returns Structured token usage report with components and total
|
|
370
377
|
*/
|
|
@@ -373,34 +380,41 @@ var LLMTokenUsageAggregator = class {
|
|
|
373
380
|
for (const component of Object.values(this.usage)) {
|
|
374
381
|
const phases = [];
|
|
375
382
|
for (const [phaseName, phaseData] of Object.entries(component.phases)) {
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
}
|
|
387
|
-
if (phaseData.primary) {
|
|
388
|
-
phaseReport.primary = {
|
|
389
|
-
modelName: phaseData.primary.modelName,
|
|
390
|
-
inputTokens: phaseData.primary.inputTokens,
|
|
391
|
-
outputTokens: phaseData.primary.outputTokens,
|
|
392
|
-
totalTokens: phaseData.primary.totalTokens
|
|
383
|
+
const primaryEntries = [...phaseData.primary.entries()];
|
|
384
|
+
const fallbackEntries = [...phaseData.fallback.entries()];
|
|
385
|
+
const entryCount = Math.max(
|
|
386
|
+
primaryEntries.length,
|
|
387
|
+
fallbackEntries.length
|
|
388
|
+
);
|
|
389
|
+
if (entryCount === 0) {
|
|
390
|
+
const phaseReport = {
|
|
391
|
+
phase: phaseName,
|
|
392
|
+
total: { ...phaseData.total }
|
|
393
393
|
};
|
|
394
|
+
if (phaseData.metadata.length > 0) {
|
|
395
|
+
phaseReport.metadata = [...phaseData.metadata];
|
|
396
|
+
}
|
|
397
|
+
phases.push(phaseReport);
|
|
398
|
+
continue;
|
|
394
399
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
400
|
+
for (let i = 0; i < entryCount; i++) {
|
|
401
|
+
const primary = primaryEntries[i];
|
|
402
|
+
const fallback = fallbackEntries[i];
|
|
403
|
+
const phaseReport = {
|
|
404
|
+
phase: phaseName,
|
|
405
|
+
total: sumUsage(primary?.[1], fallback?.[1])
|
|
401
406
|
};
|
|
407
|
+
if (primary) {
|
|
408
|
+
phaseReport.primary = { modelName: primary[0], ...primary[1] };
|
|
409
|
+
}
|
|
410
|
+
if (fallback) {
|
|
411
|
+
phaseReport.fallback = { modelName: fallback[0], ...fallback[1] };
|
|
412
|
+
}
|
|
413
|
+
if (i === 0 && phaseData.metadata.length > 0) {
|
|
414
|
+
phaseReport.metadata = [...phaseData.metadata];
|
|
415
|
+
}
|
|
416
|
+
phases.push(phaseReport);
|
|
402
417
|
}
|
|
403
|
-
phases.push(phaseReport);
|
|
404
418
|
}
|
|
405
419
|
components.push({
|
|
406
420
|
component: component.component,
|
|
@@ -445,8 +459,8 @@ var LLMTokenUsageAggregator = class {
|
|
|
445
459
|
/**
|
|
446
460
|
* Log comprehensive token usage summary
|
|
447
461
|
*
|
|
448
|
-
* Outputs usage grouped by component, with phase and model breakdown.
|
|
449
|
-
* Shows primary and fallback
|
|
462
|
+
* Outputs usage grouped by component, with phase and per-model breakdown.
|
|
463
|
+
* Shows each primary and fallback model on its own line for each phase.
|
|
450
464
|
* Call this once at the end of document processing.
|
|
451
465
|
*
|
|
452
466
|
* @param logger - Logger instance for output
|
|
@@ -462,31 +476,23 @@ var LLMTokenUsageAggregator = class {
|
|
|
462
476
|
let grandInputTokens = 0;
|
|
463
477
|
let grandOutputTokens = 0;
|
|
464
478
|
let grandTotalTokens = 0;
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
let grandPrimaryTotalTokens = 0;
|
|
468
|
-
let grandFallbackInputTokens = 0;
|
|
469
|
-
let grandFallbackOutputTokens = 0;
|
|
470
|
-
let grandFallbackTotalTokens = 0;
|
|
479
|
+
const grandPrimary = emptyUsage();
|
|
480
|
+
const grandFallback = emptyUsage();
|
|
471
481
|
for (const component of components) {
|
|
472
482
|
logger.info(`${component.component}:`);
|
|
473
|
-
for (const [
|
|
474
|
-
logger.info(` - ${
|
|
475
|
-
|
|
483
|
+
for (const [phaseName, phaseData] of Object.entries(component.phases)) {
|
|
484
|
+
logger.info(` - ${phaseName}:`);
|
|
485
|
+
for (const [modelName, modelUsage] of phaseData.primary) {
|
|
476
486
|
logger.info(
|
|
477
|
-
` primary (${
|
|
487
|
+
` primary (${modelName}): ${formatTokens(modelUsage)}`
|
|
478
488
|
);
|
|
479
|
-
|
|
480
|
-
grandPrimaryOutputTokens += phaseData.primary.outputTokens;
|
|
481
|
-
grandPrimaryTotalTokens += phaseData.primary.totalTokens;
|
|
489
|
+
addInto(grandPrimary, modelUsage);
|
|
482
490
|
}
|
|
483
|
-
|
|
491
|
+
for (const [modelName, modelUsage] of phaseData.fallback) {
|
|
484
492
|
logger.info(
|
|
485
|
-
` fallback (${
|
|
493
|
+
` fallback (${modelName}): ${formatTokens(modelUsage)}`
|
|
486
494
|
);
|
|
487
|
-
|
|
488
|
-
grandFallbackOutputTokens += phaseData.fallback.outputTokens;
|
|
489
|
-
grandFallbackTotalTokens += phaseData.fallback.totalTokens;
|
|
495
|
+
addInto(grandFallback, modelUsage);
|
|
490
496
|
}
|
|
491
497
|
logger.info(` subtotal: ${formatTokens(phaseData.total)}`);
|
|
492
498
|
}
|
|
@@ -499,23 +505,11 @@ var LLMTokenUsageAggregator = class {
|
|
|
499
505
|
grandTotalTokens += component.total.totalTokens;
|
|
500
506
|
}
|
|
501
507
|
logger.info("--- Summary ---");
|
|
502
|
-
if (
|
|
503
|
-
logger.info(
|
|
504
|
-
`Primary total: ${formatTokens({
|
|
505
|
-
inputTokens: grandPrimaryInputTokens,
|
|
506
|
-
outputTokens: grandPrimaryOutputTokens,
|
|
507
|
-
totalTokens: grandPrimaryTotalTokens
|
|
508
|
-
})}`
|
|
509
|
-
);
|
|
508
|
+
if (grandPrimary.totalTokens > 0) {
|
|
509
|
+
logger.info(`Primary total: ${formatTokens(grandPrimary)}`);
|
|
510
510
|
}
|
|
511
|
-
if (
|
|
512
|
-
logger.info(
|
|
513
|
-
`Fallback total: ${formatTokens({
|
|
514
|
-
inputTokens: grandFallbackInputTokens,
|
|
515
|
-
outputTokens: grandFallbackOutputTokens,
|
|
516
|
-
totalTokens: grandFallbackTotalTokens
|
|
517
|
-
})}`
|
|
518
|
-
);
|
|
511
|
+
if (grandFallback.totalTokens > 0) {
|
|
512
|
+
logger.info(`Fallback total: ${formatTokens(grandFallback)}`);
|
|
519
513
|
}
|
|
520
514
|
logger.info(
|
|
521
515
|
`Grand total: ${formatTokens({
|
|
@@ -3152,11 +3146,11 @@ var PageRangeParser = class extends VisionLLMComponent {
|
|
|
3152
3146
|
const pages = this.extractPages(doclingDoc);
|
|
3153
3147
|
if (pages.length === 0) {
|
|
3154
3148
|
this.log("warn", "No pages found");
|
|
3155
|
-
const
|
|
3156
|
-
this.trackUsage(
|
|
3149
|
+
const emptyUsage2 = this.createEmptyUsage("sampling");
|
|
3150
|
+
this.trackUsage(emptyUsage2);
|
|
3157
3151
|
return {
|
|
3158
3152
|
pageRangeMap: {},
|
|
3159
|
-
usage: [
|
|
3153
|
+
usage: [emptyUsage2]
|
|
3160
3154
|
};
|
|
3161
3155
|
}
|
|
3162
3156
|
const sizeGroups = this.analyzeSizes(pages);
|