@librechat/agents 3.2.64 → 3.2.65
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/cjs/graphs/Graph.cjs +3 -0
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/index.cjs +73 -9
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/types.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +41 -9
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +3 -1
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/stream_events.cjs +337 -0
- package/dist/cjs/llm/anthropic/utils/stream_events.cjs.map +1 -0
- package/dist/cjs/tools/search/crw-scraper.cjs +165 -0
- package/dist/cjs/tools/search/crw-scraper.cjs.map +1 -0
- package/dist/cjs/tools/search/crw-search.cjs +105 -0
- package/dist/cjs/tools/search/crw-search.cjs.map +1 -0
- package/dist/cjs/tools/search/search.cjs +4 -2
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +15 -3
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +3 -0
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +73 -9
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/anthropic/types.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +41 -9
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs +3 -2
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/stream_events.mjs +337 -0
- package/dist/esm/llm/anthropic/utils/stream_events.mjs.map +1 -0
- package/dist/esm/tools/search/crw-scraper.mjs +163 -0
- package/dist/esm/tools/search/crw-scraper.mjs.map +1 -0
- package/dist/esm/tools/search/crw-search.mjs +103 -0
- package/dist/esm/tools/search/crw-search.mjs.map +1 -0
- package/dist/esm/tools/search/search.mjs +4 -2
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +15 -3
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/types/llm/anthropic/index.d.ts +2 -0
- package/dist/types/llm/anthropic/types.d.ts +2 -0
- package/dist/types/llm/anthropic/utils/message_outputs.d.ts +1 -2
- package/dist/types/llm/anthropic/utils/stream_events.d.ts +25 -0
- package/dist/types/tools/search/crw-scraper.d.ts +41 -0
- package/dist/types/tools/search/crw-search.d.ts +4 -0
- package/dist/types/tools/search/types.d.ts +88 -3
- package/package.json +1 -1
- package/src/graphs/Graph.ts +15 -0
- package/src/llm/anthropic/index.ts +134 -10
- package/src/llm/anthropic/inherited-content-utils.spec.ts +10 -5
- package/src/llm/anthropic/inherited-stream-events.spec.ts +513 -9
- package/src/llm/anthropic/llm.spec.ts +100 -16
- package/src/llm/anthropic/types.ts +3 -0
- package/src/llm/anthropic/utils/message_inputs.ts +60 -3
- package/src/llm/anthropic/utils/message_outputs.ts +10 -2
- package/src/llm/anthropic/utils/stream_events.ts +471 -0
- package/src/tools/search/crw-scraper.ts +244 -0
- package/src/tools/search/crw-search.ts +167 -0
- package/src/tools/search/crw.test.ts +836 -0
- package/src/tools/search/search.ts +7 -1
- package/src/tools/search/tool.ts +23 -3
- package/src/tools/search/types.ts +103 -3
|
@@ -2,8 +2,8 @@ import type { RunnableConfig } from '@langchain/core/runnables';
|
|
|
2
2
|
import type { Logger as WinstonLogger } from 'winston';
|
|
3
3
|
import type { BaseReranker } from './rerankers';
|
|
4
4
|
import { DATE_RANGE } from './schema';
|
|
5
|
-
export type SearchProvider = 'serper' | 'searxng' | 'tavily' | 'keenable';
|
|
6
|
-
export type ScraperProvider = 'firecrawl' | 'serper' | 'tavily';
|
|
5
|
+
export type SearchProvider = 'serper' | 'searxng' | 'tavily' | 'keenable' | 'crw';
|
|
6
|
+
export type ScraperProvider = 'firecrawl' | 'serper' | 'tavily' | 'crw';
|
|
7
7
|
export type RerankerType = 'infinity' | 'jina' | 'cohere' | 'none';
|
|
8
8
|
export interface Highlight {
|
|
9
9
|
score: number;
|
|
@@ -91,6 +91,48 @@ export interface TavilySearchPayload {
|
|
|
91
91
|
include_favicon?: boolean;
|
|
92
92
|
chunks_per_source?: number;
|
|
93
93
|
}
|
|
94
|
+
export interface CrwSearchOptions {
|
|
95
|
+
/** Max results to request (maps to `limit`; clamped 1..20). */
|
|
96
|
+
maxResults?: number;
|
|
97
|
+
/** Add 'images' to the sources array. */
|
|
98
|
+
includeImages?: boolean;
|
|
99
|
+
timeout?: number;
|
|
100
|
+
}
|
|
101
|
+
export type CrwSearchSource = 'web' | 'images' | 'news';
|
|
102
|
+
export interface CrwSearchPayload {
|
|
103
|
+
query: string;
|
|
104
|
+
limit: number;
|
|
105
|
+
sources?: CrwSearchSource[];
|
|
106
|
+
tbs?: string;
|
|
107
|
+
}
|
|
108
|
+
export interface CrwSearchResult {
|
|
109
|
+
title?: string;
|
|
110
|
+
url?: string;
|
|
111
|
+
description?: string;
|
|
112
|
+
snippet?: string;
|
|
113
|
+
position?: number;
|
|
114
|
+
category?: string;
|
|
115
|
+
}
|
|
116
|
+
export interface CrwImageSearchResult extends CrwSearchResult {
|
|
117
|
+
imageUrl?: string;
|
|
118
|
+
imageFormat?: string;
|
|
119
|
+
}
|
|
120
|
+
export interface CrwNewsSearchResult extends CrwSearchResult {
|
|
121
|
+
publishedDate?: string;
|
|
122
|
+
}
|
|
123
|
+
export interface CrwSearchGroups {
|
|
124
|
+
web?: CrwSearchResult[];
|
|
125
|
+
images?: CrwImageSearchResult[];
|
|
126
|
+
news?: CrwNewsSearchResult[];
|
|
127
|
+
}
|
|
128
|
+
export interface CrwSearchResponse {
|
|
129
|
+
success: boolean;
|
|
130
|
+
data?: (CrwSearchGroups & {
|
|
131
|
+
results?: CrwSearchGroups;
|
|
132
|
+
}) | CrwSearchResult[];
|
|
133
|
+
error?: string;
|
|
134
|
+
error_code?: string;
|
|
135
|
+
}
|
|
94
136
|
export interface SearchConfig {
|
|
95
137
|
searchProvider?: SearchProvider;
|
|
96
138
|
serperApiKey?: string;
|
|
@@ -103,6 +145,9 @@ export interface SearchConfig {
|
|
|
103
145
|
keenableApiKey?: string;
|
|
104
146
|
keenableApiUrl?: string;
|
|
105
147
|
keenableSearchOptions?: KeenableSearchOptions;
|
|
148
|
+
crwApiKey?: string;
|
|
149
|
+
crwApiUrl?: string;
|
|
150
|
+
crwSearchOptions?: CrwSearchOptions;
|
|
106
151
|
}
|
|
107
152
|
export interface KeenableSearchOptions {
|
|
108
153
|
maxResults?: number;
|
|
@@ -224,6 +269,7 @@ export type SafeSearchLevel = 0 | 1 | 2;
|
|
|
224
269
|
export type Logger = WinstonLogger;
|
|
225
270
|
export interface SearchToolConfig extends SearchConfig, ProcessSourcesConfig, FirecrawlConfig {
|
|
226
271
|
tavilyScraperOptions?: TavilyScraperConfig;
|
|
272
|
+
crwScraperOptions?: CrwScraperConfig;
|
|
227
273
|
/** Max chars of highlight content this tool feeds the MODEL per search (the
|
|
228
274
|
* dominant, otherwise-unbounded part of the output). Distinct from
|
|
229
275
|
* `maxContentLength`, which caps scraped/reranked content per source — full
|
|
@@ -255,7 +301,7 @@ export type UsedReferences = {
|
|
|
255
301
|
originalIndex: number;
|
|
256
302
|
reference: MediaReference;
|
|
257
303
|
}[];
|
|
258
|
-
export type AnyScraperResponse = FirecrawlScrapeResponse | SerperScrapeResponse | TavilyScrapeResponse;
|
|
304
|
+
export type AnyScraperResponse = FirecrawlScrapeResponse | SerperScrapeResponse | TavilyScrapeResponse | CrwScrapeResponse;
|
|
259
305
|
/** Base Scraper Interface */
|
|
260
306
|
export interface BaseScraper {
|
|
261
307
|
scrapeUrl(url: string, options?: unknown): Promise<[string, AnyScraperResponse]>;
|
|
@@ -265,6 +311,24 @@ export interface BaseScraper {
|
|
|
265
311
|
}
|
|
266
312
|
/** Firecrawl */
|
|
267
313
|
export type FirecrawlScrapeOptions = Omit<FirecrawlScraperConfig, 'apiKey' | 'apiUrl' | 'version' | 'logger'>;
|
|
314
|
+
export interface CrwScraperConfig {
|
|
315
|
+
apiKey?: string;
|
|
316
|
+
apiUrl?: string;
|
|
317
|
+
formats?: string[];
|
|
318
|
+
timeout?: number;
|
|
319
|
+
logger?: Logger;
|
|
320
|
+
onlyMainContent?: boolean;
|
|
321
|
+
includeTags?: string[];
|
|
322
|
+
excludeTags?: string[];
|
|
323
|
+
waitFor?: number;
|
|
324
|
+
headers?: Record<string, string>;
|
|
325
|
+
renderJs?: boolean | null;
|
|
326
|
+
cssSelector?: string;
|
|
327
|
+
xpath?: string;
|
|
328
|
+
proxy?: string;
|
|
329
|
+
stealth?: boolean;
|
|
330
|
+
}
|
|
331
|
+
export type CrwScrapeOptions = Omit<CrwScraperConfig, 'apiKey' | 'apiUrl' | 'logger'>;
|
|
268
332
|
export type SerperScrapeOptions = Omit<SerperScraperConfig, 'apiKey' | 'apiUrl' | 'logger'>;
|
|
269
333
|
export type TavilyScrapeOptions = Omit<TavilyScraperConfig, 'apiKey' | 'apiUrl' | 'logger'>;
|
|
270
334
|
export interface TavilyExtractPayload {
|
|
@@ -341,6 +405,27 @@ export interface FirecrawlScrapeResponse {
|
|
|
341
405
|
};
|
|
342
406
|
error?: string;
|
|
343
407
|
}
|
|
408
|
+
export interface CrwScrapeData {
|
|
409
|
+
markdown?: string;
|
|
410
|
+
html?: string;
|
|
411
|
+
rawHtml?: string;
|
|
412
|
+
plainText?: string;
|
|
413
|
+
screenshot?: string;
|
|
414
|
+
links?: string[];
|
|
415
|
+
metadata?: ScrapeMetadata;
|
|
416
|
+
}
|
|
417
|
+
export interface CrwScrapeResponse {
|
|
418
|
+
success: boolean;
|
|
419
|
+
data?: CrwScrapeData;
|
|
420
|
+
error?: string;
|
|
421
|
+
error_code?: string;
|
|
422
|
+
}
|
|
423
|
+
export interface CrwRawScrapeResponse extends CrwScrapeData {
|
|
424
|
+
success?: boolean;
|
|
425
|
+
data?: CrwScrapeData;
|
|
426
|
+
error?: string;
|
|
427
|
+
error_code?: string;
|
|
428
|
+
}
|
|
344
429
|
export interface SerperScrapeResponse {
|
|
345
430
|
success: boolean;
|
|
346
431
|
data?: {
|
package/package.json
CHANGED
package/src/graphs/Graph.ts
CHANGED
|
@@ -1130,6 +1130,9 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1130
1130
|
/* Misc.*/
|
|
1131
1131
|
|
|
1132
1132
|
getRunMessages(): BaseMessage[] | undefined {
|
|
1133
|
+
if (this.messages == null) {
|
|
1134
|
+
return this.cachedRunMessages;
|
|
1135
|
+
}
|
|
1133
1136
|
if (this.messages.length === 0 && this.cachedRunMessages != null) {
|
|
1134
1137
|
return this.cachedRunMessages;
|
|
1135
1138
|
}
|
|
@@ -1137,6 +1140,12 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1137
1140
|
}
|
|
1138
1141
|
|
|
1139
1142
|
getContentParts(): t.MessageContentComplex[] | undefined {
|
|
1143
|
+
// `messages` can be null/undefined on a graph that has been disposed
|
|
1144
|
+
// (clearHeavyState) but is still reachable via a cache (e.g. RedisJobStore's
|
|
1145
|
+
// WeakRef) during a HITL resume/reconnect. Guard instead of dereferencing null.
|
|
1146
|
+
if (this.messages == null) {
|
|
1147
|
+
return undefined;
|
|
1148
|
+
}
|
|
1140
1149
|
return convertMessagesToContent(this.messages.slice(this.startIndex));
|
|
1141
1150
|
}
|
|
1142
1151
|
|
|
@@ -1169,6 +1178,12 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1169
1178
|
* Get all run steps, optionally filtered by agent ID
|
|
1170
1179
|
*/
|
|
1171
1180
|
getRunSteps(agentId?: string): t.RunStep[] {
|
|
1181
|
+
// `contentData` can be null/undefined on a disposed-but-cached graph during a
|
|
1182
|
+
// HITL resume/reconnect; without this guard `[...this.contentData]` throws
|
|
1183
|
+
// "this.contentData is not iterable".
|
|
1184
|
+
if (this.contentData == null) {
|
|
1185
|
+
return [];
|
|
1186
|
+
}
|
|
1172
1187
|
if (agentId == null || agentId === '') {
|
|
1173
1188
|
return [...this.contentData];
|
|
1174
1189
|
}
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
BaseMessage,
|
|
7
7
|
MessageContentComplex,
|
|
8
8
|
} from '@langchain/core/messages';
|
|
9
|
+
import type { ChatModelStreamEvent } from '@langchain/core/language_models/event';
|
|
9
10
|
import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
|
|
10
11
|
import type { AnthropicInput } from '@langchain/anthropic';
|
|
11
12
|
import type { Anthropic } from '@anthropic-ai/sdk';
|
|
@@ -18,12 +19,15 @@ import type {
|
|
|
18
19
|
AnthropicMCPServerURLDefinition,
|
|
19
20
|
AnthropicContextManagementConfigParam,
|
|
20
21
|
AnthropicRequestOptions,
|
|
22
|
+
AnthropicMessageStreamEvent,
|
|
21
23
|
} from '@/llm/anthropic/types';
|
|
24
|
+
import type { AnthropicUsageData } from './utils/message_outputs';
|
|
22
25
|
import {
|
|
23
26
|
_convertMessagesToAnthropicPayload,
|
|
24
27
|
stripUnsupportedAssistantPrefill,
|
|
25
28
|
} from './utils/message_inputs';
|
|
26
29
|
import { _makeMessageChunkFromAnthropicEvent } from './utils/message_outputs';
|
|
30
|
+
import { convertAnthropicStream } from './utils/stream_events';
|
|
27
31
|
import { handleToolChoice } from './utils/tools';
|
|
28
32
|
|
|
29
33
|
const DEFAULT_STREAM_DELAY = 25;
|
|
@@ -34,6 +38,23 @@ const STREAM_BOUNDARIES = new Set([' ', '.', ',', '!', '?', ';', ':']);
|
|
|
34
38
|
|
|
35
39
|
type StreamTokenType = 'string' | 'input' | 'content';
|
|
36
40
|
|
|
41
|
+
interface AnthropicStreamUsage {
|
|
42
|
+
inputTokens: number;
|
|
43
|
+
cacheCreationInputTokens: number;
|
|
44
|
+
cacheReadInputTokens: number;
|
|
45
|
+
outputTokens: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface CumulativeUsageValue {
|
|
49
|
+
cumulative: number;
|
|
50
|
+
increment: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface AnthropicEventStream
|
|
54
|
+
extends AsyncIterable<AnthropicMessageStreamEvent> {
|
|
55
|
+
controller?: { abort: () => void };
|
|
56
|
+
}
|
|
57
|
+
|
|
37
58
|
const ANTHROPIC_TOOL_BETAS: Partial<Record<string, AnthropicBeta>> = {
|
|
38
59
|
tool_search_tool_regex_20251119: 'advanced-tool-use-2025-11-20',
|
|
39
60
|
tool_search_tool_bm25_20251119: 'advanced-tool-use-2025-11-20',
|
|
@@ -324,6 +345,19 @@ function isSignalAborted(signal?: AbortSignal): boolean {
|
|
|
324
345
|
return signal?.aborted === true;
|
|
325
346
|
}
|
|
326
347
|
|
|
348
|
+
async function* abortableAnthropicStream(
|
|
349
|
+
source: AnthropicEventStream,
|
|
350
|
+
signal?: AbortSignal
|
|
351
|
+
): AsyncGenerator<AnthropicMessageStreamEvent> {
|
|
352
|
+
for await (const data of source) {
|
|
353
|
+
if (isSignalAborted(signal)) {
|
|
354
|
+
source.controller?.abort();
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
yield data;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
327
361
|
function extractToken(
|
|
328
362
|
chunk: AIMessageChunk
|
|
329
363
|
): [string, StreamTokenType] | [undefined] {
|
|
@@ -392,27 +426,77 @@ function cloneChunk(
|
|
|
392
426
|
return chunk;
|
|
393
427
|
}
|
|
394
428
|
|
|
429
|
+
function resolveCumulativeUsageValue(
|
|
430
|
+
current: number | null | undefined,
|
|
431
|
+
previous: number
|
|
432
|
+
): CumulativeUsageValue {
|
|
433
|
+
if (current == null) {
|
|
434
|
+
return { cumulative: previous, increment: 0 };
|
|
435
|
+
}
|
|
436
|
+
const cumulative = Math.max(previous, current);
|
|
437
|
+
return { cumulative, increment: cumulative - previous };
|
|
438
|
+
}
|
|
439
|
+
|
|
395
440
|
function withIncrementalMessageDeltaUsage(
|
|
396
441
|
chunk: AIMessageChunk,
|
|
397
|
-
|
|
398
|
-
|
|
442
|
+
previousUsage: AnthropicStreamUsage,
|
|
443
|
+
cumulativeUsage: AnthropicUsageData
|
|
444
|
+
): { chunk: AIMessageChunk; usage: AnthropicStreamUsage } {
|
|
399
445
|
const usage = chunk.usage_metadata;
|
|
400
446
|
if (usage == null) {
|
|
401
|
-
return { chunk,
|
|
447
|
+
return { chunk, usage: previousUsage };
|
|
402
448
|
}
|
|
403
449
|
|
|
404
|
-
const
|
|
450
|
+
const inputTokens = resolveCumulativeUsageValue(
|
|
451
|
+
cumulativeUsage.input_tokens,
|
|
452
|
+
previousUsage.inputTokens
|
|
453
|
+
);
|
|
454
|
+
const cacheCreationInputTokens = resolveCumulativeUsageValue(
|
|
455
|
+
cumulativeUsage.cache_creation_input_tokens,
|
|
456
|
+
previousUsage.cacheCreationInputTokens
|
|
457
|
+
);
|
|
458
|
+
const cacheReadInputTokens = resolveCumulativeUsageValue(
|
|
459
|
+
cumulativeUsage.cache_read_input_tokens,
|
|
460
|
+
previousUsage.cacheReadInputTokens
|
|
461
|
+
);
|
|
462
|
+
const outputTokens = resolveCumulativeUsageValue(
|
|
463
|
+
cumulativeUsage.output_tokens,
|
|
464
|
+
previousUsage.outputTokens
|
|
465
|
+
);
|
|
466
|
+
const incrementalInputTokens =
|
|
467
|
+
inputTokens.increment +
|
|
468
|
+
cacheCreationInputTokens.increment +
|
|
469
|
+
cacheReadInputTokens.increment;
|
|
470
|
+
const hasCacheUsage =
|
|
471
|
+
cumulativeUsage.cache_creation_input_tokens != null ||
|
|
472
|
+
cumulativeUsage.cache_read_input_tokens != null;
|
|
473
|
+
const inputTokenDetails = {
|
|
474
|
+
...(cumulativeUsage.cache_creation_input_tokens != null && {
|
|
475
|
+
cache_creation: cacheCreationInputTokens.increment,
|
|
476
|
+
}),
|
|
477
|
+
...(cumulativeUsage.cache_read_input_tokens != null && {
|
|
478
|
+
cache_read: cacheReadInputTokens.increment,
|
|
479
|
+
}),
|
|
480
|
+
};
|
|
481
|
+
|
|
405
482
|
return {
|
|
406
483
|
chunk: new AIMessageChunk(
|
|
407
484
|
Object.assign({}, chunk, {
|
|
408
485
|
usage_metadata: {
|
|
409
486
|
...usage,
|
|
410
|
-
|
|
411
|
-
|
|
487
|
+
input_tokens: incrementalInputTokens,
|
|
488
|
+
output_tokens: outputTokens.increment,
|
|
489
|
+
total_tokens: incrementalInputTokens + outputTokens.increment,
|
|
490
|
+
input_token_details: hasCacheUsage ? inputTokenDetails : undefined,
|
|
412
491
|
},
|
|
413
492
|
})
|
|
414
493
|
),
|
|
415
|
-
|
|
494
|
+
usage: {
|
|
495
|
+
inputTokens: inputTokens.cumulative,
|
|
496
|
+
cacheCreationInputTokens: cacheCreationInputTokens.cumulative,
|
|
497
|
+
cacheReadInputTokens: cacheReadInputTokens.cumulative,
|
|
498
|
+
outputTokens: outputTokens.cumulative,
|
|
499
|
+
},
|
|
416
500
|
};
|
|
417
501
|
}
|
|
418
502
|
|
|
@@ -626,6 +710,29 @@ export class CustomAnthropic extends ChatAnthropicMessages {
|
|
|
626
710
|
);
|
|
627
711
|
}
|
|
628
712
|
|
|
713
|
+
async *_streamChatModelEvents(
|
|
714
|
+
messages: BaseMessage[],
|
|
715
|
+
options: this['ParsedCallOptions'],
|
|
716
|
+
_runManager?: CallbackManagerForLLMRun
|
|
717
|
+
): AsyncGenerator<ChatModelStreamEvent> {
|
|
718
|
+
const params = this.invocationParams(options);
|
|
719
|
+
const formattedMessages = _convertMessagesToAnthropicPayload(messages);
|
|
720
|
+
const payload = stripUnsupportedAssistantPrefill({
|
|
721
|
+
...params,
|
|
722
|
+
...formattedMessages,
|
|
723
|
+
stream: true,
|
|
724
|
+
} as const);
|
|
725
|
+
const stream = await this.createStreamWithRetry(payload, {
|
|
726
|
+
headers: options.headers,
|
|
727
|
+
signal: options.signal,
|
|
728
|
+
});
|
|
729
|
+
const shouldStreamUsage = options.streamUsage ?? this.streamUsage;
|
|
730
|
+
yield* convertAnthropicStream(
|
|
731
|
+
abortableAnthropicStream(stream, options.signal),
|
|
732
|
+
{ streamUsage: shouldStreamUsage }
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
|
|
629
736
|
async *_streamResponseChunks(
|
|
630
737
|
messages: BaseMessage[],
|
|
631
738
|
options: this['ParsedCallOptions'],
|
|
@@ -651,7 +758,12 @@ export class CustomAnthropic extends ChatAnthropicMessages {
|
|
|
651
758
|
});
|
|
652
759
|
|
|
653
760
|
const shouldStreamUsage = options.streamUsage ?? this.streamUsage;
|
|
654
|
-
let
|
|
761
|
+
let streamUsage: AnthropicStreamUsage = {
|
|
762
|
+
inputTokens: 0,
|
|
763
|
+
cacheCreationInputTokens: 0,
|
|
764
|
+
cacheReadInputTokens: 0,
|
|
765
|
+
outputTokens: 0,
|
|
766
|
+
};
|
|
655
767
|
const queuedChunks: QueuedGenerationChunk[] = [];
|
|
656
768
|
const producerState: {
|
|
657
769
|
done: boolean;
|
|
@@ -833,13 +945,25 @@ export class CustomAnthropic extends ChatAnthropicMessages {
|
|
|
833
945
|
}
|
|
834
946
|
|
|
835
947
|
let { chunk } = result;
|
|
948
|
+
if (data.type === 'message_start') {
|
|
949
|
+
streamUsage = {
|
|
950
|
+
...streamUsage,
|
|
951
|
+
inputTokens: data.message.usage.input_tokens,
|
|
952
|
+
outputTokens: data.message.usage.output_tokens,
|
|
953
|
+
cacheCreationInputTokens:
|
|
954
|
+
data.message.usage.cache_creation_input_tokens ?? 0,
|
|
955
|
+
cacheReadInputTokens:
|
|
956
|
+
data.message.usage.cache_read_input_tokens ?? 0,
|
|
957
|
+
};
|
|
958
|
+
}
|
|
836
959
|
if (data.type === 'message_delta') {
|
|
837
960
|
const incremental = withIncrementalMessageDeltaUsage(
|
|
838
961
|
chunk,
|
|
839
|
-
|
|
962
|
+
streamUsage,
|
|
963
|
+
data.usage
|
|
840
964
|
);
|
|
841
965
|
chunk = incremental.chunk;
|
|
842
|
-
|
|
966
|
+
streamUsage = incremental.usage;
|
|
843
967
|
}
|
|
844
968
|
|
|
845
969
|
const [token = '', tokenType] = extractToken(chunk);
|
|
@@ -185,7 +185,7 @@ describe('_makeMessageChunkFromAnthropicEvent (ported from message_outputs.test.
|
|
|
185
185
|
expect(usage.input_token_details?.cache_read).toBe(1000);
|
|
186
186
|
});
|
|
187
187
|
|
|
188
|
-
it('message_delta chunk
|
|
188
|
+
it('message_delta chunk preserves cumulative input and cache usage', () => {
|
|
189
189
|
const event = {
|
|
190
190
|
type: 'message_delta' as const,
|
|
191
191
|
delta: { stop_reason: 'end_turn' as const, stop_sequence: null },
|
|
@@ -202,10 +202,15 @@ describe('_makeMessageChunkFromAnthropicEvent (ported from message_outputs.test.
|
|
|
202
202
|
expect(result).not.toBeNull();
|
|
203
203
|
|
|
204
204
|
const usage = result!.chunk.usage_metadata!;
|
|
205
|
-
expect(usage
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
205
|
+
expect(usage).toEqual({
|
|
206
|
+
input_tokens: 1600,
|
|
207
|
+
output_tokens: 42,
|
|
208
|
+
total_tokens: 1642,
|
|
209
|
+
input_token_details: {
|
|
210
|
+
cache_creation: 500,
|
|
211
|
+
cache_read: 1000,
|
|
212
|
+
},
|
|
213
|
+
});
|
|
209
214
|
});
|
|
210
215
|
});
|
|
211
216
|
|