byterover-cli 1.3.0 → 1.5.0
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.md +71 -6
- package/dist/core/domain/cipher/errors/file-system-error.d.ts +11 -0
- package/dist/core/domain/cipher/errors/file-system-error.js +17 -0
- package/dist/core/domain/cipher/file-system/types.d.ts +40 -6
- package/dist/core/domain/cipher/process/types.d.ts +1 -1
- package/dist/core/domain/entities/agent.d.ts +1 -1
- package/dist/core/domain/entities/agent.js +5 -0
- package/dist/core/domain/entities/provider-config.d.ts +92 -0
- package/dist/core/domain/entities/provider-config.js +181 -0
- package/dist/core/domain/entities/provider-registry.d.ts +55 -0
- package/dist/core/domain/entities/provider-registry.js +74 -0
- package/dist/core/interfaces/cipher/cipher-services.d.ts +0 -3
- package/dist/core/interfaces/cipher/i-content-generator.d.ts +30 -0
- package/dist/core/interfaces/cipher/i-content-generator.js +12 -1
- package/dist/core/interfaces/cipher/index.d.ts +0 -2
- package/dist/core/interfaces/cipher/message-factory.d.ts +4 -1
- package/dist/core/interfaces/cipher/message-factory.js +5 -0
- package/dist/core/interfaces/cipher/message-types.d.ts +19 -1
- package/dist/core/interfaces/i-provider-config-store.d.ts +88 -0
- package/dist/core/interfaces/i-provider-keychain-store.d.ts +33 -0
- package/dist/infra/cipher/file-system/binary-utils.d.ts +15 -2
- package/dist/infra/cipher/file-system/binary-utils.js +26 -3
- package/dist/infra/cipher/file-system/file-system-service.d.ts +9 -0
- package/dist/infra/cipher/file-system/file-system-service.js +96 -13
- package/dist/infra/cipher/file-system/pdf-extractor.d.ts +100 -0
- package/dist/infra/cipher/file-system/pdf-extractor.js +226 -0
- package/dist/infra/cipher/http/internal-llm-http-service.d.ts +40 -0
- package/dist/infra/cipher/http/internal-llm-http-service.js +152 -2
- package/dist/infra/cipher/llm/formatters/gemini-formatter.js +8 -1
- package/dist/infra/cipher/llm/generators/byterover-content-generator.d.ts +2 -3
- package/dist/infra/cipher/llm/generators/byterover-content-generator.js +20 -11
- package/dist/infra/cipher/llm/generators/openrouter-content-generator.d.ts +1 -0
- package/dist/infra/cipher/llm/generators/openrouter-content-generator.js +26 -0
- package/dist/infra/cipher/llm/internal-llm-service.d.ts +13 -0
- package/dist/infra/cipher/llm/internal-llm-service.js +75 -4
- package/dist/infra/cipher/llm/model-capabilities.d.ts +74 -0
- package/dist/infra/cipher/llm/model-capabilities.js +157 -0
- package/dist/infra/cipher/llm/openrouter-llm-service.d.ts +35 -1
- package/dist/infra/cipher/llm/openrouter-llm-service.js +216 -28
- package/dist/infra/cipher/llm/stream-processor.d.ts +22 -2
- package/dist/infra/cipher/llm/stream-processor.js +78 -4
- package/dist/infra/cipher/llm/thought-parser.d.ts +1 -1
- package/dist/infra/cipher/llm/thought-parser.js +5 -5
- package/dist/infra/cipher/llm/transformers/openrouter-stream-transformer.d.ts +49 -0
- package/dist/infra/cipher/llm/transformers/openrouter-stream-transformer.js +272 -0
- package/dist/infra/cipher/llm/transformers/reasoning-extractor.d.ts +71 -0
- package/dist/infra/cipher/llm/transformers/reasoning-extractor.js +253 -0
- package/dist/infra/cipher/process/process-service.js +1 -1
- package/dist/infra/cipher/session/chat-session.d.ts +2 -0
- package/dist/infra/cipher/session/chat-session.js +13 -2
- package/dist/infra/cipher/storage/message-storage-service.js +4 -0
- package/dist/infra/cipher/tools/implementations/bash-exec-tool.js +3 -3
- package/dist/infra/cipher/tools/implementations/read-file-tool.js +24 -4
- package/dist/infra/cipher/tools/implementations/task-tool.js +1 -1
- package/dist/infra/connectors/rules/rules-connector-config.d.ts +4 -0
- package/dist/infra/connectors/rules/rules-connector-config.js +4 -0
- package/dist/infra/http/openrouter-api-client.d.ts +148 -0
- package/dist/infra/http/openrouter-api-client.js +161 -0
- package/dist/infra/mcp/tools/brv-curate-tool.d.ts +10 -4
- package/dist/infra/mcp/tools/brv-curate-tool.js +9 -4
- package/dist/infra/mcp/tools/task-result-waiter.js +9 -1
- package/dist/infra/process/agent-worker.js +178 -70
- package/dist/infra/process/transport-handlers.d.ts +25 -4
- package/dist/infra/process/transport-handlers.js +57 -10
- package/dist/infra/repl/commands/connectors-command.js +2 -2
- package/dist/infra/repl/commands/index.js +5 -0
- package/dist/infra/repl/commands/model-command.d.ts +13 -0
- package/dist/infra/repl/commands/model-command.js +212 -0
- package/dist/infra/repl/commands/provider-command.d.ts +13 -0
- package/dist/infra/repl/commands/provider-command.js +181 -0
- package/dist/infra/repl/commands/space/switch-command.js +0 -2
- package/dist/infra/repl/transport-client-helper.js +6 -2
- package/dist/infra/storage/file-provider-config-store.d.ts +83 -0
- package/dist/infra/storage/file-provider-config-store.js +157 -0
- package/dist/infra/storage/provider-keychain-store.d.ts +37 -0
- package/dist/infra/storage/provider-keychain-store.js +75 -0
- package/dist/infra/transport/socket-io-transport-client.d.ts +20 -0
- package/dist/infra/transport/socket-io-transport-client.js +88 -1
- package/dist/infra/usecase/curate-use-case.js +10 -4
- package/dist/infra/usecase/space-switch-use-case.d.ts +0 -10
- package/dist/infra/usecase/space-switch-use-case.js +7 -37
- package/dist/oclif/hooks/init/welcome.js +4 -17
- package/dist/resources/prompts/curate.yml +1 -0
- package/dist/resources/tools/bash_exec.txt +1 -1
- package/dist/resources/tools/read_file.txt +5 -2
- package/dist/tui/components/api-key-dialog.d.ts +39 -0
- package/dist/tui/components/api-key-dialog.js +94 -0
- package/dist/tui/components/execution/execution-changes.d.ts +3 -1
- package/dist/tui/components/execution/execution-changes.js +4 -4
- package/dist/tui/components/execution/execution-content.d.ts +1 -1
- package/dist/tui/components/execution/execution-content.js +4 -12
- package/dist/tui/components/execution/execution-input.js +1 -1
- package/dist/tui/components/execution/execution-progress.d.ts +10 -13
- package/dist/tui/components/execution/execution-progress.js +70 -17
- package/dist/tui/components/execution/execution-reasoning.d.ts +16 -0
- package/dist/tui/components/execution/execution-reasoning.js +34 -0
- package/dist/tui/components/execution/execution-tool.d.ts +23 -0
- package/dist/tui/components/execution/execution-tool.js +125 -0
- package/dist/tui/components/execution/expanded-log-view.js +3 -3
- package/dist/tui/components/execution/log-item.d.ts +2 -0
- package/dist/tui/components/execution/log-item.js +6 -4
- package/dist/tui/components/index.d.ts +2 -0
- package/dist/tui/components/index.js +2 -0
- package/dist/tui/components/inline-prompts/inline-select.js +3 -2
- package/dist/tui/components/model-dialog.d.ts +63 -0
- package/dist/tui/components/model-dialog.js +89 -0
- package/dist/tui/components/onboarding/onboarding-flow.js +8 -2
- package/dist/tui/components/provider-dialog.d.ts +27 -0
- package/dist/tui/components/provider-dialog.js +31 -0
- package/dist/tui/components/reasoning-text.d.ts +26 -0
- package/dist/tui/components/reasoning-text.js +49 -0
- package/dist/tui/components/selectable-list.d.ts +54 -0
- package/dist/tui/components/selectable-list.js +180 -0
- package/dist/tui/components/streaming-text.d.ts +30 -0
- package/dist/tui/components/streaming-text.js +52 -0
- package/dist/tui/contexts/tasks-context.d.ts +15 -0
- package/dist/tui/contexts/tasks-context.js +224 -40
- package/dist/tui/contexts/theme-context.d.ts +1 -0
- package/dist/tui/contexts/theme-context.js +3 -2
- package/dist/tui/hooks/use-activity-logs.js +7 -1
- package/dist/tui/types/messages.d.ts +32 -5
- package/dist/tui/utils/index.d.ts +1 -1
- package/dist/tui/utils/index.js +1 -1
- package/dist/tui/utils/log.d.ts +0 -9
- package/dist/tui/utils/log.js +2 -53
- package/dist/tui/views/command-view.js +4 -1
- package/dist/utils/file-validator.js +8 -4
- package/oclif.manifest.json +1 -54
- package/package.json +4 -2
- package/dist/core/interfaces/cipher/i-coding-agent-log-parser.d.ts +0 -20
- package/dist/core/interfaces/cipher/i-coding-agent-log-watcher.d.ts +0 -31
- package/dist/core/interfaces/i-file-watcher-service.d.ts +0 -41
- package/dist/core/interfaces/i-file-watcher-service.js +0 -1
- package/dist/core/interfaces/parser/i-clean-parser-service.d.ts +0 -18
- package/dist/core/interfaces/parser/i-clean-parser-service.js +0 -1
- package/dist/core/interfaces/parser/i-raw-parser-service.d.ts +0 -17
- package/dist/core/interfaces/parser/i-raw-parser-service.js +0 -1
- package/dist/core/interfaces/parser/i-session-normalizer.d.ts +0 -56
- package/dist/core/interfaces/parser/i-session-normalizer.js +0 -1
- package/dist/infra/cipher/parsers/coding-agent-log-parser.d.ts +0 -24
- package/dist/infra/cipher/parsers/coding-agent-log-parser.js +0 -51
- package/dist/infra/cipher/watcher/coding-agent-log-watcher.d.ts +0 -14
- package/dist/infra/cipher/watcher/coding-agent-log-watcher.js +0 -55
- package/dist/infra/parsers/clean/clean-claude-service.d.ts +0 -111
- package/dist/infra/parsers/clean/clean-claude-service.js +0 -271
- package/dist/infra/parsers/clean/clean-codex-service.d.ts +0 -231
- package/dist/infra/parsers/clean/clean-codex-service.js +0 -534
- package/dist/infra/parsers/clean/clean-copilot-service.d.ts +0 -255
- package/dist/infra/parsers/clean/clean-copilot-service.js +0 -729
- package/dist/infra/parsers/clean/clean-cursor-service.d.ts +0 -161
- package/dist/infra/parsers/clean/clean-cursor-service.js +0 -432
- package/dist/infra/parsers/clean/clean-parser-service-factory.d.ts +0 -54
- package/dist/infra/parsers/clean/clean-parser-service-factory.js +0 -80
- package/dist/infra/parsers/clean/shared.d.ts +0 -84
- package/dist/infra/parsers/clean/shared.js +0 -273
- package/dist/infra/parsers/raw/raw-claude-service.d.ts +0 -195
- package/dist/infra/parsers/raw/raw-claude-service.js +0 -548
- package/dist/infra/parsers/raw/raw-codex-service.d.ts +0 -313
- package/dist/infra/parsers/raw/raw-codex-service.js +0 -782
- package/dist/infra/parsers/raw/raw-copilot-service.d.ts +0 -196
- package/dist/infra/parsers/raw/raw-copilot-service.js +0 -558
- package/dist/infra/parsers/raw/raw-cursor-service.d.ts +0 -316
- package/dist/infra/parsers/raw/raw-cursor-service.js +0 -818
- package/dist/infra/parsers/raw/raw-parser-service-factory.d.ts +0 -54
- package/dist/infra/parsers/raw/raw-parser-service-factory.js +0 -81
- package/dist/infra/watcher/file-watcher-service.d.ts +0 -10
- package/dist/infra/watcher/file-watcher-service.js +0 -81
- package/dist/oclif/commands/watch.d.ts +0 -25
- package/dist/oclif/commands/watch.js +0 -175
- /package/dist/core/interfaces/{cipher/i-coding-agent-log-parser.js → i-provider-config-store.js} +0 -0
- /package/dist/core/interfaces/{cipher/i-coding-agent-log-watcher.js → i-provider-keychain-store.js} +0 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { PdfMetadata, PdfPageContent } from '../../../core/domain/cipher/file-system/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Options for PDF text extraction.
|
|
4
|
+
*/
|
|
5
|
+
export interface PdfExtractOptions {
|
|
6
|
+
/** Maximum number of pages to extract (default: 100, max: 200) */
|
|
7
|
+
limit?: number;
|
|
8
|
+
/** Starting page number (1-based, default: 1) */
|
|
9
|
+
offset?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Result of PDF text extraction.
|
|
13
|
+
*/
|
|
14
|
+
export interface PdfExtractResult {
|
|
15
|
+
/** Whether there are more pages available after this extraction */
|
|
16
|
+
hasMore: boolean;
|
|
17
|
+
/** PDF metadata (page count, title, author, etc.) */
|
|
18
|
+
metadata: PdfMetadata;
|
|
19
|
+
/** Extracted page contents */
|
|
20
|
+
pages: PdfPageContent[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* PDF text extraction and metadata extraction utility.
|
|
24
|
+
* Provides page-by-page extraction with pagination support.
|
|
25
|
+
*
|
|
26
|
+
* Features:
|
|
27
|
+
* - Magic byte validation
|
|
28
|
+
* - Fast metadata-only extraction
|
|
29
|
+
* - Page-by-page text extraction with offset/limit
|
|
30
|
+
* - Default: 100 pages, max: 200 pages per extraction
|
|
31
|
+
*/
|
|
32
|
+
export declare class PdfExtractor {
|
|
33
|
+
/**
|
|
34
|
+
* Extracts metadata from a PDF buffer without extracting text.
|
|
35
|
+
* This is a fast path when you only need page count, title, author, etc.
|
|
36
|
+
*
|
|
37
|
+
* @param buffer - PDF file buffer
|
|
38
|
+
* @param filePath - Path to the PDF file (for error messages)
|
|
39
|
+
* @returns PDF metadata
|
|
40
|
+
*/
|
|
41
|
+
static extractMetadata(buffer: Buffer, filePath: string): Promise<PdfMetadata>;
|
|
42
|
+
/**
|
|
43
|
+
* Extracts text from a PDF buffer with pagination support.
|
|
44
|
+
*
|
|
45
|
+
* @param buffer - PDF file buffer
|
|
46
|
+
* @param filePath - Path to the PDF file (for error messages)
|
|
47
|
+
* @param options - Extraction options (offset, limit)
|
|
48
|
+
* @returns Extraction result with pages, metadata, and continuation info
|
|
49
|
+
*/
|
|
50
|
+
static extractText(buffer: Buffer, filePath: string, options?: PdfExtractOptions): Promise<PdfExtractResult>;
|
|
51
|
+
/**
|
|
52
|
+
* Checks if a buffer contains valid PDF magic bytes.
|
|
53
|
+
* @param buffer - Buffer to check
|
|
54
|
+
* @returns true if buffer starts with %PDF-
|
|
55
|
+
*/
|
|
56
|
+
static isValidPdf(buffer: Buffer): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Builds PdfMetadata from unpdf meta info object.
|
|
59
|
+
* @param pageCount - Total number of pages
|
|
60
|
+
* @param info - Optional info object from unpdf getMeta
|
|
61
|
+
* @returns PdfMetadata object
|
|
62
|
+
*/
|
|
63
|
+
private static buildMetadataFromInfo;
|
|
64
|
+
/**
|
|
65
|
+
* Extracts text from specific pages of a PDF document.
|
|
66
|
+
* Uses PDF.js page-level API for efficient extraction of page ranges.
|
|
67
|
+
*
|
|
68
|
+
* @param pdf - PDF document proxy from unpdf
|
|
69
|
+
* @param startPage - Starting page number (1-based)
|
|
70
|
+
* @param endPage - Ending page number (1-based, inclusive)
|
|
71
|
+
* @returns Array of PdfPageContent with extracted text
|
|
72
|
+
*/
|
|
73
|
+
private static extractPagesFromDocument;
|
|
74
|
+
/**
|
|
75
|
+
* Extracts a meaningful error message from an unknown error.
|
|
76
|
+
*/
|
|
77
|
+
private static getExtractionErrorMessage;
|
|
78
|
+
/**
|
|
79
|
+
* Parses PDF date string format (D:YYYYMMDDHHmmSS) to Date object.
|
|
80
|
+
* @param dateStr - PDF date string
|
|
81
|
+
* @returns Parsed Date or undefined if invalid
|
|
82
|
+
*/
|
|
83
|
+
private static parsePdfDate;
|
|
84
|
+
/**
|
|
85
|
+
* Wraps extraction errors with appropriate PdfExtractionError.
|
|
86
|
+
* @param error - The caught error
|
|
87
|
+
* @param filePath - Path to the PDF file
|
|
88
|
+
* @returns PdfExtractionError with appropriate message
|
|
89
|
+
*/
|
|
90
|
+
private static wrapExtractionError;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Formats extracted PDF pages into a readable string with page separators.
|
|
94
|
+
* @param pages - Array of extracted page contents
|
|
95
|
+
* @param metadata - PDF metadata
|
|
96
|
+
* @param hasMore - Whether there are more pages
|
|
97
|
+
* @param nextOffset - Next offset for continuation (if hasMore is true)
|
|
98
|
+
* @returns Formatted string with page separators
|
|
99
|
+
*/
|
|
100
|
+
export declare function formatPdfContent(pages: PdfPageContent[], metadata: PdfMetadata, hasMore: boolean, nextOffset: number): string;
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { getDocumentProxy, getMeta } from 'unpdf';
|
|
2
|
+
import { PdfExtractionError } from '../../../core/domain/cipher/errors/file-system-error.js';
|
|
3
|
+
/**
|
|
4
|
+
* PDF magic bytes: %PDF-
|
|
5
|
+
*/
|
|
6
|
+
const PDF_MAGIC_BYTES = [0x25, 0x50, 0x44, 0x46, 0x2d];
|
|
7
|
+
/**
|
|
8
|
+
* Default number of pages to extract when no limit specified.
|
|
9
|
+
*/
|
|
10
|
+
const DEFAULT_PAGE_LIMIT = 100;
|
|
11
|
+
/**
|
|
12
|
+
* Maximum number of pages allowed per extraction.
|
|
13
|
+
*/
|
|
14
|
+
const MAX_PAGE_LIMIT = 200;
|
|
15
|
+
/**
|
|
16
|
+
* PDF text extraction and metadata extraction utility.
|
|
17
|
+
* Provides page-by-page extraction with pagination support.
|
|
18
|
+
*
|
|
19
|
+
* Features:
|
|
20
|
+
* - Magic byte validation
|
|
21
|
+
* - Fast metadata-only extraction
|
|
22
|
+
* - Page-by-page text extraction with offset/limit
|
|
23
|
+
* - Default: 100 pages, max: 200 pages per extraction
|
|
24
|
+
*/
|
|
25
|
+
export class PdfExtractor {
|
|
26
|
+
/**
|
|
27
|
+
* Extracts metadata from a PDF buffer without extracting text.
|
|
28
|
+
* This is a fast path when you only need page count, title, author, etc.
|
|
29
|
+
*
|
|
30
|
+
* @param buffer - PDF file buffer
|
|
31
|
+
* @param filePath - Path to the PDF file (for error messages)
|
|
32
|
+
* @returns PDF metadata
|
|
33
|
+
*/
|
|
34
|
+
static async extractMetadata(buffer, filePath) {
|
|
35
|
+
try {
|
|
36
|
+
const pdf = await getDocumentProxy(new Uint8Array(buffer));
|
|
37
|
+
const meta = await getMeta(pdf);
|
|
38
|
+
return PdfExtractor.buildMetadataFromInfo(pdf.numPages, meta.info);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
throw PdfExtractor.wrapExtractionError(error, filePath);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Extracts text from a PDF buffer with pagination support.
|
|
46
|
+
*
|
|
47
|
+
* @param buffer - PDF file buffer
|
|
48
|
+
* @param filePath - Path to the PDF file (for error messages)
|
|
49
|
+
* @param options - Extraction options (offset, limit)
|
|
50
|
+
* @returns Extraction result with pages, metadata, and continuation info
|
|
51
|
+
*/
|
|
52
|
+
static async extractText(buffer, filePath, options = {}) {
|
|
53
|
+
// Validate PDF magic bytes
|
|
54
|
+
if (!PdfExtractor.isValidPdf(buffer)) {
|
|
55
|
+
throw new PdfExtractionError(filePath, 'Invalid PDF file format (missing PDF header)');
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const pdf = await getDocumentProxy(new Uint8Array(buffer));
|
|
59
|
+
const meta = await getMeta(pdf);
|
|
60
|
+
const totalPages = pdf.numPages;
|
|
61
|
+
const metaInfo = meta.info;
|
|
62
|
+
// Calculate pagination
|
|
63
|
+
const offset = Math.max(1, options.offset ?? 1);
|
|
64
|
+
const limit = Math.min(options.limit ?? DEFAULT_PAGE_LIMIT, MAX_PAGE_LIMIT);
|
|
65
|
+
// Return empty result if offset is beyond total pages
|
|
66
|
+
if (offset > totalPages) {
|
|
67
|
+
return {
|
|
68
|
+
hasMore: false,
|
|
69
|
+
metadata: PdfExtractor.buildMetadataFromInfo(totalPages, metaInfo),
|
|
70
|
+
pages: [],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// Calculate page range
|
|
74
|
+
const endPage = Math.min(offset + limit - 1, totalPages);
|
|
75
|
+
// Extract text only from requested pages using PDF.js page-level API
|
|
76
|
+
const pages = await PdfExtractor.extractPagesFromDocument(pdf, offset, endPage);
|
|
77
|
+
return {
|
|
78
|
+
hasMore: endPage < totalPages,
|
|
79
|
+
metadata: PdfExtractor.buildMetadataFromInfo(totalPages, metaInfo),
|
|
80
|
+
pages,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
throw PdfExtractor.wrapExtractionError(error, filePath);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Checks if a buffer contains valid PDF magic bytes.
|
|
89
|
+
* @param buffer - Buffer to check
|
|
90
|
+
* @returns true if buffer starts with %PDF-
|
|
91
|
+
*/
|
|
92
|
+
static isValidPdf(buffer) {
|
|
93
|
+
if (buffer.length < PDF_MAGIC_BYTES.length) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
for (const [index, byte] of PDF_MAGIC_BYTES.entries()) {
|
|
97
|
+
if (buffer[index] !== byte) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Builds PdfMetadata from unpdf meta info object.
|
|
105
|
+
* @param pageCount - Total number of pages
|
|
106
|
+
* @param info - Optional info object from unpdf getMeta
|
|
107
|
+
* @returns PdfMetadata object
|
|
108
|
+
*/
|
|
109
|
+
static buildMetadataFromInfo(pageCount, info) {
|
|
110
|
+
const metadata = { pageCount };
|
|
111
|
+
if (!info) {
|
|
112
|
+
return metadata;
|
|
113
|
+
}
|
|
114
|
+
if (typeof info.Title === 'string' && info.Title.trim()) {
|
|
115
|
+
metadata.title = info.Title.trim();
|
|
116
|
+
}
|
|
117
|
+
if (typeof info.Author === 'string' && info.Author.trim()) {
|
|
118
|
+
metadata.author = info.Author.trim();
|
|
119
|
+
}
|
|
120
|
+
if (info.CreationDate) {
|
|
121
|
+
const parsed = PdfExtractor.parsePdfDate(info.CreationDate);
|
|
122
|
+
if (parsed) {
|
|
123
|
+
metadata.creationDate = parsed;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return metadata;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Extracts text from specific pages of a PDF document.
|
|
130
|
+
* Uses PDF.js page-level API for efficient extraction of page ranges.
|
|
131
|
+
*
|
|
132
|
+
* @param pdf - PDF document proxy from unpdf
|
|
133
|
+
* @param startPage - Starting page number (1-based)
|
|
134
|
+
* @param endPage - Ending page number (1-based, inclusive)
|
|
135
|
+
* @returns Array of PdfPageContent with extracted text
|
|
136
|
+
*/
|
|
137
|
+
static async extractPagesFromDocument(pdf, startPage, endPage) {
|
|
138
|
+
// Build array of page numbers to extract
|
|
139
|
+
const pageNumbers = [];
|
|
140
|
+
for (let pageNum = startPage; pageNum <= endPage; pageNum++) {
|
|
141
|
+
pageNumbers.push(pageNum);
|
|
142
|
+
}
|
|
143
|
+
// Extract all requested pages in parallel
|
|
144
|
+
const pages = await Promise.all(pageNumbers.map(async (pageNum) => {
|
|
145
|
+
const page = await pdf.getPage(pageNum);
|
|
146
|
+
const textContent = await page.getTextContent();
|
|
147
|
+
// Extract text from text items, handling EOL markers
|
|
148
|
+
// TextContent.items contains TextItem and TextMarkedContent - we only want TextItem (has 'str')
|
|
149
|
+
const text = textContent.items
|
|
150
|
+
.filter((item) => typeof item === 'object' && item !== null && 'str' in item)
|
|
151
|
+
.map((item) => item.str + (item.hasEOL ? '\n' : ''))
|
|
152
|
+
.join('');
|
|
153
|
+
return {
|
|
154
|
+
pageNumber: pageNum,
|
|
155
|
+
text: text.trim(),
|
|
156
|
+
};
|
|
157
|
+
}));
|
|
158
|
+
return pages;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Extracts a meaningful error message from an unknown error.
|
|
162
|
+
*/
|
|
163
|
+
static getExtractionErrorMessage(error) {
|
|
164
|
+
if (error instanceof Error) {
|
|
165
|
+
return error.message;
|
|
166
|
+
}
|
|
167
|
+
if (typeof error === 'string') {
|
|
168
|
+
return error;
|
|
169
|
+
}
|
|
170
|
+
return 'Unknown PDF extraction error';
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Parses PDF date string format (D:YYYYMMDDHHmmSS) to Date object.
|
|
174
|
+
* @param dateStr - PDF date string
|
|
175
|
+
* @returns Parsed Date or undefined if invalid
|
|
176
|
+
*/
|
|
177
|
+
static parsePdfDate(dateStr) {
|
|
178
|
+
if (!dateStr) {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
// PDF date format: D:YYYYMMDDHHmmSS+HH'mm' or variations
|
|
182
|
+
const match = dateStr.match(/D:(\d{4})(\d{2})?(\d{2})?(\d{2})?(\d{2})?(\d{2})?/);
|
|
183
|
+
if (!match) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
const year = Number.parseInt(match[1], 10);
|
|
187
|
+
const month = match[2] ? Number.parseInt(match[2], 10) - 1 : 0;
|
|
188
|
+
const day = match[3] ? Number.parseInt(match[3], 10) : 1;
|
|
189
|
+
const hour = match[4] ? Number.parseInt(match[4], 10) : 0;
|
|
190
|
+
const minute = match[5] ? Number.parseInt(match[5], 10) : 0;
|
|
191
|
+
const second = match[6] ? Number.parseInt(match[6], 10) : 0;
|
|
192
|
+
return new Date(year, month, day, hour, minute, second);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Wraps extraction errors with appropriate PdfExtractionError.
|
|
196
|
+
* @param error - The caught error
|
|
197
|
+
* @param filePath - Path to the PDF file
|
|
198
|
+
* @returns PdfExtractionError with appropriate message
|
|
199
|
+
*/
|
|
200
|
+
static wrapExtractionError(error, filePath) {
|
|
201
|
+
const errorMessage = PdfExtractor.getExtractionErrorMessage(error);
|
|
202
|
+
const lowerMessage = errorMessage.toLowerCase();
|
|
203
|
+
if (lowerMessage.includes('password') || lowerMessage.includes('encrypted')) {
|
|
204
|
+
return new PdfExtractionError(filePath, 'PDF is password-protected or encrypted');
|
|
205
|
+
}
|
|
206
|
+
return new PdfExtractionError(filePath, errorMessage);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Formats extracted PDF pages into a readable string with page separators.
|
|
211
|
+
* @param pages - Array of extracted page contents
|
|
212
|
+
* @param metadata - PDF metadata
|
|
213
|
+
* @param hasMore - Whether there are more pages
|
|
214
|
+
* @param nextOffset - Next offset for continuation (if hasMore is true)
|
|
215
|
+
* @returns Formatted string with page separators
|
|
216
|
+
*/
|
|
217
|
+
export function formatPdfContent(pages, metadata, hasMore, nextOffset) {
|
|
218
|
+
if (pages.length === 0) {
|
|
219
|
+
return '';
|
|
220
|
+
}
|
|
221
|
+
const formattedPages = pages.map((page) => `--- Page ${page.pageNumber} ---\n${page.text}`).join('\n\n');
|
|
222
|
+
const truncationNote = hasMore
|
|
223
|
+
? `\n\n(PDF has more pages. Use offset=${nextOffset} to continue)`
|
|
224
|
+
: `\n\n(End of PDF - ${metadata.pageCount} pages)`;
|
|
225
|
+
return formattedPages + truncationNote;
|
|
226
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RequestOptions } from '@anthropic-ai/sdk/internal/request-options';
|
|
2
2
|
import type { MessageCreateParamsNonStreaming } from '@anthropic-ai/sdk/resources/messages.js';
|
|
3
3
|
import type { Content, GenerateContentConfig, GenerateContentResponse } from '@google/genai';
|
|
4
|
+
import type { GenerateContentChunk } from '../../../core/interfaces/cipher/i-content-generator.js';
|
|
4
5
|
/**
|
|
5
6
|
* ByteRover HTTP LLM provider configuration.
|
|
6
7
|
*/
|
|
@@ -61,6 +62,22 @@ export declare class ByteRoverLlmHttpService {
|
|
|
61
62
|
* @returns Response in GenerateContentResponse format
|
|
62
63
|
*/
|
|
63
64
|
generateContent(contents: Content[] | MessageCreateParamsNonStreaming, config: GenerateContentConfig | RequestOptions, model: string, executionMetadata?: Record<string, unknown>): Promise<GenerateContentResponse>;
|
|
65
|
+
/**
|
|
66
|
+
* Call ByteRover REST LLM service to generate content with streaming.
|
|
67
|
+
*
|
|
68
|
+
* Currently falls back to non-streaming endpoint since /api/llm/generate/stream
|
|
69
|
+
* doesn't exist on the backend yet. Extracts thinking/reasoning from the complete
|
|
70
|
+
* response and yields them as separate chunks.
|
|
71
|
+
*
|
|
72
|
+
* When backend streaming is available, this will use SSE for true streaming.
|
|
73
|
+
*
|
|
74
|
+
* @param contents - For Gemini: Content[]. For Claude: MessageCreateParamsNonStreaming (complete body)
|
|
75
|
+
* @param config - For Gemini: GenerateContentConfig. For Claude: RequestOptions (optional HTTP options)
|
|
76
|
+
* @param model - Model to use (detects provider from model name)
|
|
77
|
+
* @param executionMetadata - Optional execution metadata (mode, executionContext)
|
|
78
|
+
* @yields GenerateContentChunk objects as they are generated
|
|
79
|
+
*/
|
|
80
|
+
generateContentStream(contents: Content[] | MessageCreateParamsNonStreaming, config: GenerateContentConfig | RequestOptions, model: string, executionMetadata?: Record<string, unknown>): AsyncGenerator<GenerateContentChunk>;
|
|
64
81
|
/**
|
|
65
82
|
* Call the ByteRover REST Generate endpoint.
|
|
66
83
|
*
|
|
@@ -91,4 +108,27 @@ export declare class ByteRoverLlmHttpService {
|
|
|
91
108
|
* @returns GCP region identifier ('us-east5' or 'global')
|
|
92
109
|
*/
|
|
93
110
|
private detectRegionFromModel;
|
|
111
|
+
/**
|
|
112
|
+
* Extract content chunks from a complete response.
|
|
113
|
+
*
|
|
114
|
+
* Looks for text parts (excluding thinking) and function calls,
|
|
115
|
+
* yields them as final chunks.
|
|
116
|
+
*
|
|
117
|
+
* @param response - Complete GenerateContentResponse
|
|
118
|
+
* @yields GenerateContentChunk for content and tool calls
|
|
119
|
+
*/
|
|
120
|
+
private extractContentFromResponse;
|
|
121
|
+
/**
|
|
122
|
+
* Extract thinking/reasoning chunks from a complete response.
|
|
123
|
+
*
|
|
124
|
+
* Looks for parts with `thought: true` and yields them as THINKING chunks.
|
|
125
|
+
*
|
|
126
|
+
* @param response - Complete GenerateContentResponse
|
|
127
|
+
* @yields GenerateContentChunk for each thinking part
|
|
128
|
+
*/
|
|
129
|
+
private extractThinkingFromResponse;
|
|
130
|
+
/**
|
|
131
|
+
* Map provider finish reason to standard format.
|
|
132
|
+
*/
|
|
133
|
+
private mapFinishReason;
|
|
94
134
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { StreamChunkType } from '../../../core/interfaces/cipher/i-content-generator.js';
|
|
1
2
|
import { AuthenticatedHttpClient } from '../../http/authenticated-http-client.js';
|
|
3
|
+
import { ThoughtParser } from '../llm/thought-parser.js';
|
|
2
4
|
/**
|
|
3
5
|
* ByteRover HTTP LLM API client.
|
|
4
6
|
*
|
|
@@ -72,6 +74,30 @@ export class ByteRoverLlmHttpService {
|
|
|
72
74
|
};
|
|
73
75
|
return this.callHttpGenerate(request);
|
|
74
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Call ByteRover REST LLM service to generate content with streaming.
|
|
79
|
+
*
|
|
80
|
+
* Currently falls back to non-streaming endpoint since /api/llm/generate/stream
|
|
81
|
+
* doesn't exist on the backend yet. Extracts thinking/reasoning from the complete
|
|
82
|
+
* response and yields them as separate chunks.
|
|
83
|
+
*
|
|
84
|
+
* When backend streaming is available, this will use SSE for true streaming.
|
|
85
|
+
*
|
|
86
|
+
* @param contents - For Gemini: Content[]. For Claude: MessageCreateParamsNonStreaming (complete body)
|
|
87
|
+
* @param config - For Gemini: GenerateContentConfig. For Claude: RequestOptions (optional HTTP options)
|
|
88
|
+
* @param model - Model to use (detects provider from model name)
|
|
89
|
+
* @param executionMetadata - Optional execution metadata (mode, executionContext)
|
|
90
|
+
* @yields GenerateContentChunk objects as they are generated
|
|
91
|
+
*/
|
|
92
|
+
async *generateContentStream(contents, config, model, executionMetadata) {
|
|
93
|
+
// Fall back to non-streaming endpoint and simulate streaming
|
|
94
|
+
// by extracting thinking from the complete response
|
|
95
|
+
const response = await this.generateContent(contents, config, model, executionMetadata);
|
|
96
|
+
// Extract and yield thinking/reasoning chunks first
|
|
97
|
+
yield* this.extractThinkingFromResponse(response);
|
|
98
|
+
// Then yield the final content
|
|
99
|
+
yield* this.extractContentFromResponse(response);
|
|
100
|
+
}
|
|
75
101
|
/**
|
|
76
102
|
* Call the ByteRover REST Generate endpoint.
|
|
77
103
|
*
|
|
@@ -84,10 +110,10 @@ export class ByteRoverLlmHttpService {
|
|
|
84
110
|
async callHttpGenerate(request) {
|
|
85
111
|
const url = `${this.config.apiBaseUrl}/api/llm/generate`;
|
|
86
112
|
const httpClient = new AuthenticatedHttpClient(this.config.accessToken, this.config.sessionKey);
|
|
87
|
-
const
|
|
113
|
+
const httpResponse = await httpClient.post(url, request, {
|
|
88
114
|
timeout: this.config.timeout,
|
|
89
115
|
});
|
|
90
|
-
return
|
|
116
|
+
return httpResponse.data;
|
|
91
117
|
}
|
|
92
118
|
/**
|
|
93
119
|
* Detect LLM provider from model identifier.
|
|
@@ -113,4 +139,128 @@ export class ByteRoverLlmHttpService {
|
|
|
113
139
|
detectRegionFromModel(model) {
|
|
114
140
|
return model.toLowerCase().startsWith('claude') ? 'us-east5' : 'global';
|
|
115
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Extract content chunks from a complete response.
|
|
144
|
+
*
|
|
145
|
+
* Looks for text parts (excluding thinking) and function calls,
|
|
146
|
+
* yields them as final chunks.
|
|
147
|
+
*
|
|
148
|
+
* @param response - Complete GenerateContentResponse
|
|
149
|
+
* @yields GenerateContentChunk for content and tool calls
|
|
150
|
+
*/
|
|
151
|
+
*extractContentFromResponse(response) {
|
|
152
|
+
const { candidates } = response;
|
|
153
|
+
if (!candidates || candidates.length === 0) {
|
|
154
|
+
yield {
|
|
155
|
+
content: '',
|
|
156
|
+
finishReason: 'stop',
|
|
157
|
+
isComplete: true,
|
|
158
|
+
};
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const candidate = candidates[0];
|
|
162
|
+
const parts = candidate?.content?.parts;
|
|
163
|
+
const finishReason = this.mapFinishReason(candidate?.finishReason ?? 'STOP');
|
|
164
|
+
if (!parts || parts.length === 0) {
|
|
165
|
+
yield {
|
|
166
|
+
content: '',
|
|
167
|
+
finishReason,
|
|
168
|
+
isComplete: true,
|
|
169
|
+
};
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
// Collect text content (excluding thinking parts)
|
|
173
|
+
const textParts = [];
|
|
174
|
+
const functionCalls = [];
|
|
175
|
+
for (const part of parts) {
|
|
176
|
+
const partRecord = part;
|
|
177
|
+
// Skip thinking parts
|
|
178
|
+
if (partRecord.thought === true)
|
|
179
|
+
continue;
|
|
180
|
+
// Collect text
|
|
181
|
+
if (partRecord.text && typeof partRecord.text === 'string') {
|
|
182
|
+
textParts.push(partRecord.text);
|
|
183
|
+
}
|
|
184
|
+
// Collect function calls
|
|
185
|
+
if (partRecord.functionCall) {
|
|
186
|
+
functionCalls.push(partRecord.functionCall);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Yield final content chunk
|
|
190
|
+
yield {
|
|
191
|
+
content: textParts.join('').trimEnd(),
|
|
192
|
+
finishReason,
|
|
193
|
+
isComplete: true,
|
|
194
|
+
toolCalls: functionCalls.length > 0
|
|
195
|
+
? functionCalls.map((fc, index) => ({
|
|
196
|
+
function: {
|
|
197
|
+
arguments: JSON.stringify(fc.args ?? {}),
|
|
198
|
+
name: fc.name ?? '',
|
|
199
|
+
},
|
|
200
|
+
id: `call_${Date.now()}_${index}`,
|
|
201
|
+
type: 'function',
|
|
202
|
+
}))
|
|
203
|
+
: undefined,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Extract thinking/reasoning chunks from a complete response.
|
|
208
|
+
*
|
|
209
|
+
* Looks for parts with `thought: true` and yields them as THINKING chunks.
|
|
210
|
+
*
|
|
211
|
+
* @param response - Complete GenerateContentResponse
|
|
212
|
+
* @yields GenerateContentChunk for each thinking part
|
|
213
|
+
*/
|
|
214
|
+
*extractThinkingFromResponse(response) {
|
|
215
|
+
const { candidates } = response;
|
|
216
|
+
if (!candidates || candidates.length === 0)
|
|
217
|
+
return;
|
|
218
|
+
const parts = candidates[0]?.content?.parts;
|
|
219
|
+
if (!parts)
|
|
220
|
+
return;
|
|
221
|
+
let thinkingSubject;
|
|
222
|
+
for (const part of parts) {
|
|
223
|
+
const partRecord = part;
|
|
224
|
+
// Check for thinking part (thought: true)
|
|
225
|
+
if (partRecord.thought === true && partRecord.text && typeof partRecord.text === 'string') {
|
|
226
|
+
const delta = partRecord.text;
|
|
227
|
+
// Extract subject from **Subject** markdown if not already found
|
|
228
|
+
if (!thinkingSubject && delta) {
|
|
229
|
+
const parsed = ThoughtParser.parse(delta);
|
|
230
|
+
if (parsed.subject) {
|
|
231
|
+
thinkingSubject = parsed.subject;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
yield {
|
|
235
|
+
isComplete: false,
|
|
236
|
+
providerMetadata: {
|
|
237
|
+
subject: thinkingSubject,
|
|
238
|
+
},
|
|
239
|
+
reasoning: delta.trimEnd(),
|
|
240
|
+
type: StreamChunkType.THINKING,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Map provider finish reason to standard format.
|
|
247
|
+
*/
|
|
248
|
+
mapFinishReason(reason) {
|
|
249
|
+
switch (reason.toUpperCase()) {
|
|
250
|
+
case 'FUNCTION_CALL':
|
|
251
|
+
case 'TOOL_CALLS': {
|
|
252
|
+
return 'tool_calls';
|
|
253
|
+
}
|
|
254
|
+
case 'LENGTH':
|
|
255
|
+
case 'MAX_TOKENS': {
|
|
256
|
+
return 'max_tokens';
|
|
257
|
+
}
|
|
258
|
+
case 'STOP': {
|
|
259
|
+
return 'stop';
|
|
260
|
+
}
|
|
261
|
+
default: {
|
|
262
|
+
return 'stop';
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
116
266
|
}
|
|
@@ -72,8 +72,14 @@ export class GeminiMessageFormatter {
|
|
|
72
72
|
}
|
|
73
73
|
const textParts = [];
|
|
74
74
|
const functionCallsWithSignatures = [];
|
|
75
|
-
|
|
75
|
+
let thoughtText;
|
|
76
|
+
// Extract text, thoughts, and function calls from response parts
|
|
76
77
|
for (const part of candidate.content.parts) {
|
|
78
|
+
// Check for thought parts first (Gemini 2.5+ with includeThoughts: true)
|
|
79
|
+
if ('thought' in part && part.thought === true && 'text' in part && part.text) {
|
|
80
|
+
thoughtText = part.text;
|
|
81
|
+
continue; // Don't add thought to textParts
|
|
82
|
+
}
|
|
77
83
|
if ('text' in part && part.text) {
|
|
78
84
|
textParts.push(part.text);
|
|
79
85
|
}
|
|
@@ -102,6 +108,7 @@ export class GeminiMessageFormatter {
|
|
|
102
108
|
{
|
|
103
109
|
content: textParts.join('') || null,
|
|
104
110
|
role: 'assistant',
|
|
111
|
+
thought: thoughtText,
|
|
105
112
|
toolCalls,
|
|
106
113
|
},
|
|
107
114
|
];
|
|
@@ -60,9 +60,8 @@ export declare class ByteRoverContentGenerator implements IContentGenerator {
|
|
|
60
60
|
/**
|
|
61
61
|
* Generate content with streaming.
|
|
62
62
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* True streaming can be implemented when the gRPC service exposes the stream.
|
|
63
|
+
* Uses the HTTP service's streaming endpoint to yield chunks as they arrive.
|
|
64
|
+
* Handles both regular content and thinking/reasoning parts from Gemini models.
|
|
66
65
|
*
|
|
67
66
|
* @param request - Generation request
|
|
68
67
|
* @yields Content chunks as they are generated
|
|
@@ -117,24 +117,33 @@ export class ByteRoverContentGenerator {
|
|
|
117
117
|
/**
|
|
118
118
|
* Generate content with streaming.
|
|
119
119
|
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* True streaming can be implemented when the gRPC service exposes the stream.
|
|
120
|
+
* Uses the HTTP service's streaming endpoint to yield chunks as they arrive.
|
|
121
|
+
* Handles both regular content and thinking/reasoning parts from Gemini models.
|
|
123
122
|
*
|
|
124
123
|
* @param request - Generation request
|
|
125
124
|
* @yields Content chunks as they are generated
|
|
126
125
|
* @returns Async generator yielding content chunks
|
|
127
126
|
*/
|
|
128
127
|
async *generateContentStream(request) {
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
128
|
+
// Format messages for provider
|
|
129
|
+
let formattedMessages = this.formatter.format(request.contents);
|
|
130
|
+
// For Gemini 3+ models, ensure function calls in the active loop have thought signatures
|
|
131
|
+
if (this.providerType === 'gemini') {
|
|
132
|
+
formattedMessages = ensureActiveLoopHasThoughtSignatures(formattedMessages, this.config.model);
|
|
133
|
+
}
|
|
134
|
+
// Build generation config
|
|
135
|
+
const genConfig = this.buildGenerationConfig(request.tools ?? {}, request.systemPrompt ?? '', formattedMessages);
|
|
136
|
+
// Build execution metadata from request
|
|
137
|
+
const executionMetadata = {
|
|
138
|
+
sessionId: request.taskId,
|
|
139
|
+
taskId: request.taskId,
|
|
140
|
+
...(request.executionContext && { executionContext: request.executionContext }),
|
|
137
141
|
};
|
|
142
|
+
// Determine contents and config based on provider
|
|
143
|
+
const contents = this.providerType === 'claude' ? genConfig : formattedMessages;
|
|
144
|
+
const config = this.providerType === 'claude' ? {} : genConfig;
|
|
145
|
+
// Stream from HTTP service
|
|
146
|
+
yield* this.httpService.generateContentStream(contents, config, this.config.model, executionMetadata);
|
|
138
147
|
}
|
|
139
148
|
/**
|
|
140
149
|
* Build Claude-specific generation configuration.
|
|
@@ -64,6 +64,7 @@ export declare class OpenRouterContentGenerator implements IContentGenerator {
|
|
|
64
64
|
* Generate content with streaming.
|
|
65
65
|
*
|
|
66
66
|
* Uses OpenAI SDK's native streaming support for real-time content generation.
|
|
67
|
+
* Includes rawChunk for native reasoning extraction by the stream transformer.
|
|
67
68
|
*
|
|
68
69
|
* @param request - Generation request
|
|
69
70
|
* @yields Content chunks as they are generated
|