@heripo/document-processor 0.1.44 → 0.1.46
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/README.ko.md +43 -21
- package/README.md +45 -24
- package/dist/index.js +47 -27
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/README.ko.md
CHANGED
|
@@ -56,6 +56,14 @@ npm install ai @ai-sdk/openai @ai-sdk/anthropic @ai-sdk/google
|
|
|
56
56
|
|
|
57
57
|
## 사용법
|
|
58
58
|
|
|
59
|
+
### 실행 조건과 결과 저장
|
|
60
|
+
|
|
61
|
+
이 패키지는 Node.js 24 이상에서 사용하는 ESM 라이브러리입니다. Python이나 macOS는 이 패키지 자체의 필수 조건이 아니며, PDF를 만드는 `@heripo/pdf-parser`에서 요구합니다. AI SDK/provider 버전은 저장소의 [catalog](../../pnpm-workspace.yaml)와 맞추세요.
|
|
62
|
+
|
|
63
|
+
아래 예제의 `doclingDocument`는 파서의 보정된 `result.json`, `artifactDir`는 해당 이미지·페이지 산출물이 있는 디렉터리입니다. 기본 모델은 개별 모델이 생략된 모든 컴포넌트에도 사용되므로 이미지 입력과 구조화된 출력을 지원해야 합니다. 예제용 환경 변수 `HERIPO_ANTHROPIC_MODEL`과 `HERIPO_OPENAI_MODEL`에 사용할 모델 ID를 설정하고, 선택한 provider의 API 키도 설정하세요.
|
|
64
|
+
|
|
65
|
+
`process()`는 파일을 저장하지 않습니다. 반환한 `document`를 직접 JSON으로 저장하고 참조 이미지도 보존하세요. 출력 `schemaVersion`은 내보낸 `PROCESSED_DOCUMENT_SCHEMA_VERSION` 값인 `processed-document.v2`입니다.
|
|
66
|
+
|
|
59
67
|
### 기본 사용법
|
|
60
68
|
|
|
61
69
|
```typescript
|
|
@@ -73,7 +81,7 @@ const logger = new Logger({
|
|
|
73
81
|
// 기본 사용 - fallback 모델만 지정
|
|
74
82
|
const processor = new DocumentProcessor({
|
|
75
83
|
logger,
|
|
76
|
-
fallbackModel: anthropic(
|
|
84
|
+
fallbackModel: anthropic(process.env.HERIPO_ANTHROPIC_MODEL!),
|
|
77
85
|
textCleanerBatchSize: 10,
|
|
78
86
|
captionParserBatchSize: 5,
|
|
79
87
|
captionValidatorBatchSize: 5,
|
|
@@ -103,14 +111,14 @@ import { openai } from '@ai-sdk/openai';
|
|
|
103
111
|
const processor = new DocumentProcessor({
|
|
104
112
|
logger,
|
|
105
113
|
// Fallback 모델 (실패 시 재시도용)
|
|
106
|
-
fallbackModel: anthropic(
|
|
114
|
+
fallbackModel: anthropic(process.env.HERIPO_ANTHROPIC_MODEL!),
|
|
107
115
|
|
|
108
116
|
// 컴포넌트별 모델 지정
|
|
109
|
-
pageRangeParserModel: openai(
|
|
110
|
-
tocExtractorModel: openai(
|
|
111
|
-
validatorModel: openai(
|
|
112
|
-
visionTocExtractorModel: openai(
|
|
113
|
-
captionParserModel: openai(
|
|
117
|
+
pageRangeParserModel: openai(process.env.HERIPO_OPENAI_MODEL!), // Vision 필요
|
|
118
|
+
tocExtractorModel: openai(process.env.HERIPO_OPENAI_MODEL!), // 구조화 출력
|
|
119
|
+
validatorModel: openai(process.env.HERIPO_OPENAI_MODEL!), // 간단한 검증
|
|
120
|
+
visionTocExtractorModel: openai(process.env.HERIPO_OPENAI_MODEL!), // Vision 필요
|
|
121
|
+
captionParserModel: openai(process.env.HERIPO_OPENAI_MODEL!), // 캡션 파싱
|
|
114
122
|
|
|
115
123
|
// 배치 크기 설정
|
|
116
124
|
textCleanerBatchSize: 20, // 동기 처리 (크게 가능)
|
|
@@ -228,7 +236,7 @@ console.log(document.images[0].captionSourceRefs);
|
|
|
228
236
|
|
|
229
237
|
`sourceRefValidationMode: 'error'`는 누락된 참조가 있으면 처리를 실패시킵니다. `validateSourceRefs: true`는 호환용 단축 옵션이며, 별도 mode를 지정하지 않으면 `'error'`와 같습니다.
|
|
230
238
|
|
|
231
|
-
테이블 셀에는
|
|
239
|
+
테이블 셀에는 개별 `sourceRef`가 없습니다. `table.sourceRef`로 원천 표를 찾고 span과 원천 셀 좌표를 대조하세요. 압축된 `grid`의 배열 인덱스는 원천 표의 논리적 열 번호와 다를 수 있습니다.
|
|
232
240
|
|
|
233
241
|
### 테이블 그리드 처리
|
|
234
242
|
|
|
@@ -239,7 +247,7 @@ console.log(document.images[0].captionSourceRefs);
|
|
|
239
247
|
- Docling이 병합 셀의 덮인 영역을 반복 제공하는 경우 shadow entry 제거
|
|
240
248
|
- Docling의 `data.grid`가 비어 있으면 `table_cells`에서 그리드 구성
|
|
241
249
|
|
|
242
|
-
`numRows`와 `numCols`는 논리적 테이블
|
|
250
|
+
`numRows`와 `numCols`는 논리적 테이블 크기이며 각 배열의 길이와 같다고 가정하면 안 됩니다. 렌더링 시 `rowSpan`과 `colSpan`으로 이미 덮인 위치를 건너뛰세요.
|
|
243
251
|
|
|
244
252
|
## 처리 파이프라인
|
|
245
253
|
|
|
@@ -294,6 +302,8 @@ DocumentProcessor는 다음 5단계 파이프라인으로 문서를 처리합니
|
|
|
294
302
|
|
|
295
303
|
### 5. 챕터 변환 (ChapterConverter)
|
|
296
304
|
|
|
305
|
+
`Front Matter` (`ch-000`) 챕터를 항상 첫 항목으로 생성하여 첫 목차 항목 이전의 내용을 보존합니다. 이후 계층은 제공·추출한 TOC로 구성됩니다.
|
|
306
|
+
|
|
297
307
|
- TOC 기반 챕터 트리 구성
|
|
298
308
|
- Chapter 계층 생성
|
|
299
309
|
- 페이지 범위별로 텍스트 블록을 챕터에 연결
|
|
@@ -371,8 +381,8 @@ interface DocumentProcessorProcessOptions {
|
|
|
371
381
|
```typescript
|
|
372
382
|
const processor = new DocumentProcessor({
|
|
373
383
|
logger,
|
|
374
|
-
fallbackModel: anthropic(
|
|
375
|
-
pageRangeParserModel: openai(
|
|
384
|
+
fallbackModel: anthropic(process.env.HERIPO_ANTHROPIC_MODEL!), // 재시도용
|
|
385
|
+
pageRangeParserModel: openai(process.env.HERIPO_OPENAI_MODEL!), // 첫 시도
|
|
376
386
|
enableFallbackRetry: true, // 실패 시 fallback 사용 (기본값: false)
|
|
377
387
|
textCleanerBatchSize: 10,
|
|
378
388
|
captionParserBatchSize: 5,
|
|
@@ -386,8 +396,10 @@ const { document, usage } = await processor.process(doc, 'id', 'path');
|
|
|
386
396
|
### 배치 크기 파라미터
|
|
387
397
|
|
|
388
398
|
- **textCleanerBatchSize**: 동기 텍스트 정규화 및 필터링 배치 크기. 로컬 처리이므로 큰 값 가능
|
|
389
|
-
- **captionParserBatchSize**: LLM 기반 캡션 파싱 배치 크기.
|
|
390
|
-
- **captionValidatorBatchSize**: LLM 기반 캡션 검증 배치 크기.
|
|
399
|
+
- **captionParserBatchSize**: LLM 기반 캡션 파싱 배치 크기. 요청 하나에 포함하는 캡션 수 조절
|
|
400
|
+
- **captionValidatorBatchSize**: LLM 기반 캡션 검증 배치 크기. 검증할 캡션 묶음 크기 조절
|
|
401
|
+
|
|
402
|
+
`textCleanerBatchSize`, `captionParserBatchSize`, `captionValidatorBatchSize`는 `0`이면 묶음 처리 없이 순차 처리합니다. 묶음 처리를 사용할 때는 양의 정수로 지정하세요. 양수 배치 크기는 항목 묶음 크기이며 전체 API 호출 동시성의 상한은 아닙니다.
|
|
391
403
|
|
|
392
404
|
## 에러 처리
|
|
393
405
|
|
|
@@ -397,9 +409,15 @@ TOC 추출 실패 시 발생하는 에러들:
|
|
|
397
409
|
|
|
398
410
|
- `TocNotFoundError`: 문서에서 TOC를 찾을 수 없음
|
|
399
411
|
- `TocParseError`: LLM 응답 파싱 실패
|
|
400
|
-
- `TocValidationError`:
|
|
412
|
+
- `TocValidationError`: 내부 검증 오류로 발생할 수 있지만 패키지 루트에서 export하지 않습니다. 공개 API에서는 `TocExtractError`를 사용해 처리하세요.
|
|
401
413
|
|
|
402
414
|
```typescript
|
|
415
|
+
import {
|
|
416
|
+
TocExtractError,
|
|
417
|
+
TocNotFoundError,
|
|
418
|
+
TocParseError,
|
|
419
|
+
} from '@heripo/document-processor';
|
|
420
|
+
|
|
403
421
|
try {
|
|
404
422
|
const { document, usage } = await processor.process(doc, 'id', 'path');
|
|
405
423
|
} catch (error) {
|
|
@@ -407,6 +425,10 @@ try {
|
|
|
407
425
|
console.error('TOC를 찾을 수 없습니다. 수동 TOC 검수가 필요합니다.');
|
|
408
426
|
} else if (error instanceof TocParseError) {
|
|
409
427
|
console.error('TOC 파싱 실패:', error.message);
|
|
428
|
+
} else if (error instanceof TocExtractError) {
|
|
429
|
+
console.error(error.message);
|
|
430
|
+
} else {
|
|
431
|
+
throw error;
|
|
410
432
|
}
|
|
411
433
|
}
|
|
412
434
|
```
|
|
@@ -436,18 +458,18 @@ import {
|
|
|
436
458
|
|
|
437
459
|
```typescript
|
|
438
460
|
// PageRangeParser
|
|
439
|
-
const { pageRangeMap,
|
|
440
|
-
console.log('
|
|
461
|
+
const { pageRangeMap, usage } = await pageRangeParser.parse(doc);
|
|
462
|
+
console.log('Token usage:', usage);
|
|
441
463
|
|
|
442
464
|
// TocExtractor
|
|
443
|
-
const { entries,
|
|
444
|
-
console.log('
|
|
465
|
+
const { entries, usages } = await tocExtractor.extract(markdown);
|
|
466
|
+
console.log('Token usage:', usages);
|
|
445
467
|
```
|
|
446
468
|
|
|
447
469
|
## 관련 패키지
|
|
448
470
|
|
|
449
|
-
- [@heripo/pdf-parser](../pdf-parser) - PDF 파싱 및 OCR
|
|
450
|
-
- [@heripo/model](../model) - 데이터 모델 및 타입 정의
|
|
471
|
+
- [@heripo/pdf-parser](../pdf-parser/README.ko.md) - PDF 파싱 및 OCR
|
|
472
|
+
- [@heripo/model](../model/README.ko.md) - 데이터 모델 및 타입 정의
|
|
451
473
|
|
|
452
474
|
## 후원
|
|
453
475
|
|
|
@@ -462,7 +484,7 @@ heripo lab의 오픈소스 연구를 후원하려면 다음 경로를 이용할
|
|
|
462
484
|
|
|
463
485
|
## 기여하기
|
|
464
486
|
|
|
465
|
-
기여는 언제나 환영합니다! [기여 가이드](../../CONTRIBUTING.
|
|
487
|
+
기여는 언제나 환영합니다! [기여 가이드](../../CONTRIBUTING.md)를 참고하세요.
|
|
466
488
|
|
|
467
489
|
## 이슈 및 지원
|
|
468
490
|
|
package/README.md
CHANGED
|
@@ -56,6 +56,14 @@ npm install ai @ai-sdk/openai @ai-sdk/anthropic @ai-sdk/google
|
|
|
56
56
|
|
|
57
57
|
## Usage
|
|
58
58
|
|
|
59
|
+
### Runtime and Result Persistence
|
|
60
|
+
|
|
61
|
+
This package is an ESM library for Node.js 24+. It does not itself require Python or macOS; those requirements belong to `@heripo/pdf-parser`. Align AI SDK/provider versions with the repository [catalog](../../pnpm-workspace.yaml).
|
|
62
|
+
|
|
63
|
+
In the examples, `doclingDocument` is the parser's corrected `result.json`, and `artifactDir` contains its image/page artifacts. The fallback model also serves every component without an explicit model, so it must support image input and structured output. Set the example-specific `HERIPO_ANTHROPIC_MODEL` and `HERIPO_OPENAI_MODEL` environment variables to available model IDs, and configure credentials for the selected providers.
|
|
64
|
+
|
|
65
|
+
`process()` does not save files. Save the returned `document` as JSON and preserve referenced images yourself. Its `schemaVersion` is the exported `PROCESSED_DOCUMENT_SCHEMA_VERSION`, currently `processed-document.v2`.
|
|
66
|
+
|
|
59
67
|
### Basic Usage
|
|
60
68
|
|
|
61
69
|
```typescript
|
|
@@ -73,7 +81,7 @@ const logger = new Logger({
|
|
|
73
81
|
// Basic usage - specify fallback model only
|
|
74
82
|
const processor = new DocumentProcessor({
|
|
75
83
|
logger,
|
|
76
|
-
fallbackModel: anthropic(
|
|
84
|
+
fallbackModel: anthropic(process.env.HERIPO_ANTHROPIC_MODEL!),
|
|
77
85
|
textCleanerBatchSize: 10,
|
|
78
86
|
captionParserBatchSize: 5,
|
|
79
87
|
captionValidatorBatchSize: 5,
|
|
@@ -103,14 +111,14 @@ import { openai } from '@ai-sdk/openai';
|
|
|
103
111
|
const processor = new DocumentProcessor({
|
|
104
112
|
logger,
|
|
105
113
|
// Fallback model (for retry on failure)
|
|
106
|
-
fallbackModel: anthropic(
|
|
114
|
+
fallbackModel: anthropic(process.env.HERIPO_ANTHROPIC_MODEL!),
|
|
107
115
|
|
|
108
116
|
// Per-component model specification
|
|
109
|
-
pageRangeParserModel: openai(
|
|
110
|
-
tocExtractorModel: openai(
|
|
111
|
-
validatorModel: openai(
|
|
112
|
-
visionTocExtractorModel: openai(
|
|
113
|
-
captionParserModel: openai(
|
|
117
|
+
pageRangeParserModel: openai(process.env.HERIPO_OPENAI_MODEL!), // Vision required
|
|
118
|
+
tocExtractorModel: openai(process.env.HERIPO_OPENAI_MODEL!), // Structured output
|
|
119
|
+
validatorModel: openai(process.env.HERIPO_OPENAI_MODEL!), // Simple validation
|
|
120
|
+
visionTocExtractorModel: openai(process.env.HERIPO_OPENAI_MODEL!), // Vision required
|
|
121
|
+
captionParserModel: openai(process.env.HERIPO_OPENAI_MODEL!), // Caption parsing
|
|
114
122
|
|
|
115
123
|
// Batch size settings
|
|
116
124
|
textCleanerBatchSize: 20, // Synchronous processing (can be large)
|
|
@@ -240,9 +248,9 @@ node reference.
|
|
|
240
248
|
`validateSourceRefs: true` is a compatibility shortcut and behaves like
|
|
241
249
|
`'error'` unless a mode is explicitly provided.
|
|
242
250
|
|
|
243
|
-
Table cells do not receive cell-level `sourceRef` values.
|
|
244
|
-
|
|
245
|
-
`grid
|
|
251
|
+
Table cells do not receive cell-level `sourceRef` values. Use `table.sourceRef`
|
|
252
|
+
to find the source table, then compare spans and source cell coordinates.
|
|
253
|
+
Compact `grid` array indexes may differ from logical source column indexes.
|
|
246
254
|
|
|
247
255
|
### Table Grid Handling
|
|
248
256
|
|
|
@@ -253,9 +261,8 @@ Processed tables expose a compact `grid` of visible cells. The processor:
|
|
|
253
261
|
- Removes merged-cell shadow entries when Docling repeats covered cells
|
|
254
262
|
- Falls back to `table_cells` when Docling's `data.grid` is empty
|
|
255
263
|
|
|
256
|
-
`numRows` and `numCols` keep the logical table size
|
|
257
|
-
|
|
258
|
-
position when tracing a cell back to the source table.
|
|
264
|
+
`numRows` and `numCols` keep the logical table size; do not assume they match
|
|
265
|
+
array lengths. When rendering, skip positions covered by `rowSpan`/`colSpan`.
|
|
259
266
|
|
|
260
267
|
## Processing Pipeline
|
|
261
268
|
|
|
@@ -310,6 +317,8 @@ DocumentProcessor processes documents through a 5-stage pipeline:
|
|
|
310
317
|
|
|
311
318
|
### 5. Chapter Conversion (ChapterConverter)
|
|
312
319
|
|
|
320
|
+
A `Front Matter` (`ch-000`) chapter is always prepended to retain content before the first TOC entry. The remaining hierarchy is built from the supplied or extracted TOC.
|
|
321
|
+
|
|
313
322
|
- Build chapter tree based on TOC
|
|
314
323
|
- Create Chapter hierarchy
|
|
315
324
|
- Link text blocks to chapters by page range
|
|
@@ -387,8 +396,8 @@ When `enableFallbackRetry: true` is set (default is `false`), LLM components aut
|
|
|
387
396
|
```typescript
|
|
388
397
|
const processor = new DocumentProcessor({
|
|
389
398
|
logger,
|
|
390
|
-
fallbackModel: anthropic(
|
|
391
|
-
pageRangeParserModel: openai(
|
|
399
|
+
fallbackModel: anthropic(process.env.HERIPO_ANTHROPIC_MODEL!), // For retry
|
|
400
|
+
pageRangeParserModel: openai(process.env.HERIPO_OPENAI_MODEL!), // First attempt
|
|
392
401
|
enableFallbackRetry: true, // Use fallback on failure (default: false)
|
|
393
402
|
textCleanerBatchSize: 10,
|
|
394
403
|
captionParserBatchSize: 5,
|
|
@@ -402,8 +411,10 @@ const { document, usage } = await processor.process(doc, 'id', 'path');
|
|
|
402
411
|
### Batch Size Parameters
|
|
403
412
|
|
|
404
413
|
- **textCleanerBatchSize**: Synchronous text normalization and filtering batch size. Large values possible due to local processing
|
|
405
|
-
- **captionParserBatchSize**: LLM-based caption parsing batch size.
|
|
406
|
-
- **captionValidatorBatchSize**: LLM-based caption validation batch size.
|
|
414
|
+
- **captionParserBatchSize**: LLM-based caption parsing batch size. Controls captions included per parsing request
|
|
415
|
+
- **captionValidatorBatchSize**: LLM-based caption validation batch size. Controls captions grouped for validation
|
|
416
|
+
|
|
417
|
+
`textCleanerBatchSize`, `captionParserBatchSize` and `captionValidatorBatchSize` accept `0` for sequential processing without batching. Use positive integers to enable batching. Positive batch sizes group items; they do not impose a global API concurrency limit.
|
|
407
418
|
|
|
408
419
|
## Error Handling
|
|
409
420
|
|
|
@@ -413,9 +424,15 @@ Errors thrown when TOC extraction fails:
|
|
|
413
424
|
|
|
414
425
|
- `TocNotFoundError`: TOC not found in document
|
|
415
426
|
- `TocParseError`: LLM response parsing failed
|
|
416
|
-
- `TocValidationError`:
|
|
427
|
+
- `TocValidationError`: May be raised internally, but is not exported from the package root. Catch the exported `TocExtractError` base class.
|
|
417
428
|
|
|
418
429
|
```typescript
|
|
430
|
+
import {
|
|
431
|
+
TocExtractError,
|
|
432
|
+
TocNotFoundError,
|
|
433
|
+
TocParseError,
|
|
434
|
+
} from '@heripo/document-processor';
|
|
435
|
+
|
|
419
436
|
try {
|
|
420
437
|
const { document, usage } = await processor.process(doc, 'id', 'path');
|
|
421
438
|
} catch (error) {
|
|
@@ -423,6 +440,10 @@ try {
|
|
|
423
440
|
console.error('TOC not found. Manual TOC review is required.');
|
|
424
441
|
} else if (error instanceof TocParseError) {
|
|
425
442
|
console.error('TOC parsing failed:', error.message);
|
|
443
|
+
} else if (error instanceof TocExtractError) {
|
|
444
|
+
console.error(error.message);
|
|
445
|
+
} else {
|
|
446
|
+
throw error;
|
|
426
447
|
}
|
|
427
448
|
}
|
|
428
449
|
```
|
|
@@ -452,18 +473,18 @@ Major LLM components return token usage:
|
|
|
452
473
|
|
|
453
474
|
```typescript
|
|
454
475
|
// PageRangeParser
|
|
455
|
-
const { pageRangeMap,
|
|
456
|
-
console.log('Token usage:',
|
|
476
|
+
const { pageRangeMap, usage } = await pageRangeParser.parse(doc);
|
|
477
|
+
console.log('Token usage:', usage);
|
|
457
478
|
|
|
458
479
|
// TocExtractor
|
|
459
|
-
const { entries,
|
|
460
|
-
console.log('Token usage:',
|
|
480
|
+
const { entries, usages } = await tocExtractor.extract(markdown);
|
|
481
|
+
console.log('Token usage:', usages);
|
|
461
482
|
```
|
|
462
483
|
|
|
463
484
|
## Related Packages
|
|
464
485
|
|
|
465
|
-
- [@heripo/pdf-parser](../pdf-parser) - PDF parsing and OCR
|
|
466
|
-
- [@heripo/model](../model) - Data models and type definitions
|
|
486
|
+
- [@heripo/pdf-parser](../pdf-parser/README.md) - PDF parsing and OCR
|
|
487
|
+
- [@heripo/model](../model/README.md) - Data models and type definitions
|
|
467
488
|
|
|
468
489
|
## Sponsor
|
|
469
490
|
|
package/dist/index.js
CHANGED
|
@@ -90,6 +90,15 @@ function detectProvider(model) {
|
|
|
90
90
|
if (providerId.includes("lmstudio")) return "lmstudio";
|
|
91
91
|
return "unknown";
|
|
92
92
|
}
|
|
93
|
+
function cacheWrite1hTokens(steps) {
|
|
94
|
+
let total;
|
|
95
|
+
for (const step of steps ?? []) {
|
|
96
|
+
const raw = step.usage.raw;
|
|
97
|
+
const value = raw?.cache_creation?.ephemeral_1h_input_tokens;
|
|
98
|
+
if (value !== void 0) total = (total ?? 0) + value;
|
|
99
|
+
}
|
|
100
|
+
return total;
|
|
101
|
+
}
|
|
93
102
|
var LLMCaller = class {
|
|
94
103
|
/**
|
|
95
104
|
* Extract model name from LanguageModel object
|
|
@@ -108,6 +117,9 @@ var LLMCaller = class {
|
|
|
108
117
|
* Build usage information from response
|
|
109
118
|
*/
|
|
110
119
|
static buildUsage(config, modelName, response, usedFallback) {
|
|
120
|
+
const usage = response.usage;
|
|
121
|
+
const write1h = cacheWrite1hTokens(response.steps);
|
|
122
|
+
const allWrites = usage?.inputTokenDetails?.cacheWriteTokens;
|
|
111
123
|
return {
|
|
112
124
|
component: config.component,
|
|
113
125
|
phase: config.phase,
|
|
@@ -116,6 +128,13 @@ var LLMCaller = class {
|
|
|
116
128
|
inputTokens: response.usage?.inputTokens ?? 0,
|
|
117
129
|
outputTokens: response.usage?.outputTokens ?? 0,
|
|
118
130
|
totalTokens: response.usage?.totalTokens ?? 0,
|
|
131
|
+
...usage?.inputTokenDetails?.cacheReadTokens != null && {
|
|
132
|
+
cachedInputTokens: usage.inputTokenDetails.cacheReadTokens
|
|
133
|
+
},
|
|
134
|
+
...allWrites != null && {
|
|
135
|
+
cacheWriteTokens: allWrites - (write1h ?? 0)
|
|
136
|
+
},
|
|
137
|
+
...write1h !== void 0 && { cacheWrite1hTokens: write1h },
|
|
119
138
|
metadata: config.metadata
|
|
120
139
|
};
|
|
121
140
|
}
|
|
@@ -158,7 +177,8 @@ var LLMCaller = class {
|
|
|
158
177
|
if (toolCall) {
|
|
159
178
|
return {
|
|
160
179
|
output: toolCall.input,
|
|
161
|
-
usage: lastResult.usage
|
|
180
|
+
usage: lastResult.usage,
|
|
181
|
+
steps: lastResult.steps
|
|
162
182
|
};
|
|
163
183
|
}
|
|
164
184
|
}
|
|
@@ -295,16 +315,32 @@ function emptyUsage() {
|
|
|
295
315
|
return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
296
316
|
}
|
|
297
317
|
function addInto(target, src) {
|
|
318
|
+
const hadUsage = target.inputTokens !== 0 || target.outputTokens !== 0 || target.totalTokens !== 0;
|
|
298
319
|
target.inputTokens += src.inputTokens;
|
|
299
320
|
target.outputTokens += src.outputTokens;
|
|
300
321
|
target.totalTokens += src.totalTokens;
|
|
322
|
+
for (const key of CACHE_KEYS) {
|
|
323
|
+
const next = src[key];
|
|
324
|
+
const current = target[key];
|
|
325
|
+
if (next === void 0) {
|
|
326
|
+
if (current !== void 0) target[key] = null;
|
|
327
|
+
} else if (current === void 0) {
|
|
328
|
+
target[key] = hadUsage ? null : next;
|
|
329
|
+
} else {
|
|
330
|
+
target[key] = current === null || next === null ? null : current + next;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
301
333
|
}
|
|
334
|
+
var CACHE_KEYS = [
|
|
335
|
+
"cachedInputTokens",
|
|
336
|
+
"cacheWriteTokens",
|
|
337
|
+
"cacheWrite1hTokens"
|
|
338
|
+
];
|
|
302
339
|
function sumUsage(a, b) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
};
|
|
340
|
+
const total = emptyUsage();
|
|
341
|
+
if (a) addInto(total, a);
|
|
342
|
+
if (b) addInto(total, b);
|
|
343
|
+
return total;
|
|
308
344
|
}
|
|
309
345
|
function accumulateModel(bucket, modelName, usage) {
|
|
310
346
|
let entry = bucket.get(modelName);
|
|
@@ -419,21 +455,13 @@ var LLMTokenUsageAggregator = class {
|
|
|
419
455
|
components.push({
|
|
420
456
|
component: component.component,
|
|
421
457
|
phases,
|
|
422
|
-
total: {
|
|
423
|
-
inputTokens: component.total.inputTokens,
|
|
424
|
-
outputTokens: component.total.outputTokens,
|
|
425
|
-
totalTokens: component.total.totalTokens
|
|
426
|
-
}
|
|
458
|
+
total: { ...component.total }
|
|
427
459
|
});
|
|
428
460
|
}
|
|
429
461
|
const totalUsage = this.getTotalUsage();
|
|
430
462
|
return {
|
|
431
463
|
components,
|
|
432
|
-
total:
|
|
433
|
-
inputTokens: totalUsage.inputTokens,
|
|
434
|
-
outputTokens: totalUsage.outputTokens,
|
|
435
|
-
totalTokens: totalUsage.totalTokens
|
|
436
|
-
}
|
|
464
|
+
total: totalUsage
|
|
437
465
|
};
|
|
438
466
|
}
|
|
439
467
|
/**
|
|
@@ -442,19 +470,11 @@ var LLMTokenUsageAggregator = class {
|
|
|
442
470
|
* @returns Aggregated token usage totals
|
|
443
471
|
*/
|
|
444
472
|
getTotalUsage() {
|
|
445
|
-
|
|
446
|
-
let totalOutput = 0;
|
|
447
|
-
let totalTokens = 0;
|
|
473
|
+
const total = emptyUsage();
|
|
448
474
|
for (const component of Object.values(this.usage)) {
|
|
449
|
-
|
|
450
|
-
totalOutput += component.total.outputTokens;
|
|
451
|
-
totalTokens += component.total.totalTokens;
|
|
475
|
+
addInto(total, component.total);
|
|
452
476
|
}
|
|
453
|
-
return
|
|
454
|
-
inputTokens: totalInput,
|
|
455
|
-
outputTokens: totalOutput,
|
|
456
|
-
totalTokens
|
|
457
|
-
};
|
|
477
|
+
return total;
|
|
458
478
|
}
|
|
459
479
|
/**
|
|
460
480
|
* Log comprehensive token usage summary
|