@librechat/agents 3.3.12 → 3.4.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/dist/cjs/graphs/Graph.cjs +10 -0
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/instrumentation.cjs +1 -0
- package/dist/cjs/instrumentation.cjs.map +1 -1
- package/dist/cjs/langfuseSpanRegistry.cjs +6 -3
- package/dist/cjs/langfuseSpanRegistry.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/index.cjs +35 -206
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +121 -241
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/llm/google/index.cjs +19 -12
- package/dist/cjs/llm/google/index.cjs.map +1 -1
- package/dist/cjs/llm/mistral/index.cjs +26 -0
- package/dist/cjs/llm/mistral/index.cjs.map +1 -0
- package/dist/cjs/llm/openai/index.cjs +82 -80
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/llm/providers.cjs +19 -19
- package/dist/cjs/llm/providers.cjs.map +1 -1
- package/dist/cjs/llm/stream/chunkAdapters.cjs +198 -0
- package/dist/cjs/llm/stream/chunkAdapters.cjs.map +1 -0
- package/dist/cjs/llm/stream/smoother.cjs +369 -0
- package/dist/cjs/llm/stream/smoother.cjs.map +1 -0
- package/dist/cjs/llm/vertexai/index.cjs +13 -1
- package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +18 -10
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +34 -2
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +76 -29
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +10 -0
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/instrumentation.mjs +1 -0
- package/dist/esm/instrumentation.mjs.map +1 -1
- package/dist/esm/langfuseSpanRegistry.mjs +6 -3
- package/dist/esm/langfuseSpanRegistry.mjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +34 -205
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +120 -240
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/llm/google/index.mjs +19 -12
- package/dist/esm/llm/google/index.mjs.map +1 -1
- package/dist/esm/llm/mistral/index.mjs +26 -0
- package/dist/esm/llm/mistral/index.mjs.map +1 -0
- package/dist/esm/llm/openai/index.mjs +82 -80
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/llm/providers.mjs +3 -3
- package/dist/esm/llm/providers.mjs.map +1 -1
- package/dist/esm/llm/stream/chunkAdapters.mjs +195 -0
- package/dist/esm/llm/stream/chunkAdapters.mjs.map +1 -0
- package/dist/esm/llm/stream/smoother.mjs +365 -0
- package/dist/esm/llm/stream/smoother.mjs.map +1 -0
- package/dist/esm/llm/vertexai/index.mjs +13 -1
- package/dist/esm/llm/vertexai/index.mjs.map +1 -1
- package/dist/esm/main.mjs +4 -2
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +34 -2
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +76 -30
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/types/graphs/Graph.d.ts +3 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/llm/google/index.d.ts +2 -0
- package/dist/types/llm/mistral/index.d.ts +11 -0
- package/dist/types/llm/openai/index.d.ts +20 -4
- package/dist/types/llm/openrouter/index.d.ts +4 -1
- package/dist/types/llm/stream/chunkAdapters.d.ts +48 -0
- package/dist/types/llm/stream/smoother.d.ts +95 -0
- package/dist/types/llm/vertexai/index.d.ts +2 -0
- package/dist/types/tools/subagent/SubagentExecutor.d.ts +3 -0
- package/dist/types/types/graph.d.ts +5 -0
- package/dist/types/types/llm.d.ts +21 -14
- package/dist/types/utils/tokens.d.ts +10 -0
- package/package.json +2 -2
- package/src/graphs/Graph.ts +11 -0
- package/src/index.ts +8 -0
- package/src/instrumentation.ts +1 -0
- package/src/langfuseSpanRegistry.ts +9 -0
- package/src/llm/anthropic/index.ts +85 -354
- package/src/llm/bedrock/index.ts +174 -390
- package/src/llm/bedrock/llm.spec.ts +2 -0
- package/src/llm/bedrock/streamSealDispatch.test.ts +76 -0
- package/src/llm/custom-chat-models.smoke.test.ts +16 -1
- package/src/llm/google/index.ts +17 -18
- package/src/llm/google/streamSmoothing.test.ts +121 -0
- package/src/llm/mistral/index.ts +33 -0
- package/src/llm/mistral/streamSmoothing.test.ts +97 -0
- package/src/llm/openai/deepseek.test.ts +56 -0
- package/src/llm/openai/index.ts +119 -126
- package/src/llm/openrouter/index.ts +4 -1
- package/src/llm/providers.ts +3 -3
- package/src/llm/stream/chunkAdapters.test.ts +202 -0
- package/src/llm/stream/chunkAdapters.ts +317 -0
- package/src/llm/stream/reassembly.test.ts +241 -0
- package/src/llm/stream/smoother.bench.test.ts +155 -0
- package/src/llm/stream/smoother.test.ts +519 -0
- package/src/llm/stream/smoother.ts +574 -0
- package/src/llm/vertexai/index.ts +17 -1
- package/src/llm/vertexai/streamSmoothing.test.ts +109 -0
- package/src/specs/langfuse-instrumentation.test.ts +26 -0
- package/src/specs/langfuse-span-registry.test.ts +17 -0
- package/src/specs/subagent.test.ts +44 -0
- package/src/specs/summarization.test.ts +5 -2
- package/src/specs/tokens.test.ts +159 -14
- package/src/tools/__tests__/SubagentExecutor.test.ts +48 -1
- package/src/tools/subagent/SubagentExecutor.ts +69 -5
- package/src/types/graph.ts +5 -0
- package/src/types/llm.ts +53 -36
- package/src/utils/tokens.ts +115 -30
package/src/llm/bedrock/index.ts
CHANGED
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
|
|
34
34
|
import type { BaseMessage, ResponseMetadata } from '@langchain/core/messages';
|
|
35
35
|
import type { ChatBedrockConverseInput } from '@langchain/aws';
|
|
36
|
+
import type { SmoothItem } from '@/llm/stream/smoother';
|
|
36
37
|
import type { ContentBlockDeltaEvent } from './types';
|
|
37
38
|
import {
|
|
38
39
|
convertToConverseMessages,
|
|
@@ -46,6 +47,7 @@ import {
|
|
|
46
47
|
supportsBedrockToolCache,
|
|
47
48
|
type PromptCacheTtl,
|
|
48
49
|
} from '@/messages/cache';
|
|
50
|
+
import { smoothStream, resolveStreamDelay, isSignalAborted } from '@/llm/stream/smoother';
|
|
49
51
|
import { linkStreamLimitCanonical } from '@/llm/streamLimits';
|
|
50
52
|
import { applyCachePointsToConversePayload } from './cachePoints';
|
|
51
53
|
import { insertBedrockToolCachePoint } from './toolCache';
|
|
@@ -60,50 +62,12 @@ export type ServiceTierType = 'priority' | 'default' | 'flex' | 'reserved';
|
|
|
60
62
|
export type CustomGuardrailConfiguration = GuardrailConfiguration &
|
|
61
63
|
Pick<GuardrailStreamConfiguration, 'streamProcessingMode'>;
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
const MAX_STREAM_QUEUE_TEXT_CHARS = 8192;
|
|
65
|
-
const STREAM_CHUNK_MIN_SIZE = 4;
|
|
66
|
-
const STREAM_BOUNDARIES = new Set([' ', '.', ',', '!', '?', ';', ':']);
|
|
67
|
-
|
|
68
|
-
type QueuedGenerationChunk = {
|
|
65
|
+
type BedrockEmittedChunk = {
|
|
69
66
|
chunk: ChatGenerationChunk;
|
|
70
67
|
callbackChunk?: ChatGenerationChunk;
|
|
71
68
|
callbackToken: string;
|
|
72
|
-
smooth: boolean;
|
|
73
|
-
textLength: number;
|
|
74
69
|
};
|
|
75
70
|
|
|
76
|
-
function findStreamChunkBoundary(text: string, minSize: number): number {
|
|
77
|
-
if (minSize >= text.length) {
|
|
78
|
-
return text.length;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
for (let position = minSize; position < text.length; position++) {
|
|
82
|
-
if (STREAM_BOUNDARIES.has(text[position])) {
|
|
83
|
-
return position + 1;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return text.length;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function splitStreamToken(text: string): string[] {
|
|
91
|
-
const chunks: string[] = [];
|
|
92
|
-
let currentIndex = 0;
|
|
93
|
-
|
|
94
|
-
while (currentIndex < text.length) {
|
|
95
|
-
const remainingText = text.slice(currentIndex);
|
|
96
|
-
const chunkSize = findStreamChunkBoundary(
|
|
97
|
-
remainingText,
|
|
98
|
-
STREAM_CHUNK_MIN_SIZE
|
|
99
|
-
);
|
|
100
|
-
chunks.push(text.slice(currentIndex, currentIndex + chunkSize));
|
|
101
|
-
currentIndex += chunkSize;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return chunks;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
71
|
/**
|
|
108
72
|
* Resolves the text a delta contributes to the smoothing cadence, preferring a
|
|
109
73
|
* text delta over a reasoning delta and ignoring non-string payloads.
|
|
@@ -120,52 +84,6 @@ function resolveVisibleText(text?: string, reasoningText?: string): string {
|
|
|
120
84
|
return '';
|
|
121
85
|
}
|
|
122
86
|
|
|
123
|
-
function getCadencedStreamDelay({
|
|
124
|
-
targetDelay,
|
|
125
|
-
lastVisibleContentAt,
|
|
126
|
-
now,
|
|
127
|
-
}: {
|
|
128
|
-
targetDelay: number;
|
|
129
|
-
lastVisibleContentAt?: number;
|
|
130
|
-
now: number;
|
|
131
|
-
}): number {
|
|
132
|
-
if (targetDelay <= 0 || lastVisibleContentAt == null) {
|
|
133
|
-
return 0;
|
|
134
|
-
}
|
|
135
|
-
return Math.max(0, targetDelay - (now - lastVisibleContentAt));
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
async function waitForStreamDelay(
|
|
139
|
-
delay: number,
|
|
140
|
-
signal?: AbortSignal
|
|
141
|
-
): Promise<void> {
|
|
142
|
-
if (delay <= 0 || isSignalAborted(signal)) {
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
await new Promise<void>((resolve) => {
|
|
146
|
-
const timeoutRef: { current?: ReturnType<typeof setTimeout> } = {};
|
|
147
|
-
const onAbort = (): void => {
|
|
148
|
-
if (timeoutRef.current) {
|
|
149
|
-
clearTimeout(timeoutRef.current);
|
|
150
|
-
}
|
|
151
|
-
signal?.removeEventListener('abort', onAbort);
|
|
152
|
-
resolve();
|
|
153
|
-
};
|
|
154
|
-
timeoutRef.current = setTimeout(() => {
|
|
155
|
-
signal?.removeEventListener('abort', onAbort);
|
|
156
|
-
resolve();
|
|
157
|
-
}, delay);
|
|
158
|
-
signal?.addEventListener('abort', onAbort, { once: true });
|
|
159
|
-
if (isSignalAborted(signal)) {
|
|
160
|
-
onAbort();
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function isSignalAborted(signal?: AbortSignal): boolean {
|
|
166
|
-
return signal?.aborted === true;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
87
|
/**
|
|
170
88
|
* Extended input interface with additional features:
|
|
171
89
|
* - applicationInferenceProfile: Use an inference profile ARN instead of model ID
|
|
@@ -267,7 +185,7 @@ export class CustomChatBedrockConverse extends ChatBedrockConverse {
|
|
|
267
185
|
super(fields);
|
|
268
186
|
this.promptCache = fields?.promptCache;
|
|
269
187
|
this.promptCacheTtl = fields?.promptCacheTtl;
|
|
270
|
-
this._lc_stream_delay =
|
|
188
|
+
this._lc_stream_delay = resolveStreamDelay(fields?._lc_stream_delay);
|
|
271
189
|
this.applicationInferenceProfile = fields?.applicationInferenceProfile;
|
|
272
190
|
this.serviceTier = fields?.serviceTier;
|
|
273
191
|
// `super(fields)` initializes `this.model` to LangChain's default Claude
|
|
@@ -398,15 +316,6 @@ export class CustomChatBedrockConverse extends ChatBedrockConverse {
|
|
|
398
316
|
|
|
399
317
|
const seenBlockIndices = new Set<number>();
|
|
400
318
|
const toolUseBlockIndices = new Set<number>();
|
|
401
|
-
const queuedChunks: QueuedGenerationChunk[] = [];
|
|
402
|
-
const producerState: { done: boolean; error?: unknown } = { done: false };
|
|
403
|
-
let queuedChunkIndex = 0;
|
|
404
|
-
let bufferedTextLength = 0;
|
|
405
|
-
let consumerClosed = false;
|
|
406
|
-
let notifyConsumer: (() => void) | undefined;
|
|
407
|
-
let notifyProducer: (() => void) | undefined;
|
|
408
|
-
let hasEmittedVisibleContent = false;
|
|
409
|
-
let lastVisibleContentAt: number | undefined;
|
|
410
319
|
|
|
411
320
|
/**
|
|
412
321
|
* Guardrails can reject an already-streamed toolUse block at
|
|
@@ -417,325 +326,200 @@ export class CustomChatBedrockConverse extends ChatBedrockConverse {
|
|
|
417
326
|
const sealToolUseOnStop =
|
|
418
327
|
options.guardrailConfig == null && this.guardrailConfig == null;
|
|
419
328
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
const getQueuedChunkCount = (): number =>
|
|
434
|
-
queuedChunks.length - queuedChunkIndex;
|
|
435
|
-
|
|
436
|
-
const isQueueAtCapacity = (): boolean =>
|
|
437
|
-
getQueuedChunkCount() >= MAX_STREAM_QUEUE_CHUNKS ||
|
|
438
|
-
bufferedTextLength >= MAX_STREAM_QUEUE_TEXT_CHARS;
|
|
439
|
-
|
|
440
|
-
const waitForNextChunk = async (): Promise<void> => {
|
|
441
|
-
if (
|
|
442
|
-
hasQueuedChunks() ||
|
|
443
|
-
producerState.done ||
|
|
444
|
-
producerState.error != null
|
|
445
|
-
) {
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
await new Promise<void>((resolve) => {
|
|
449
|
-
notifyConsumer = resolve;
|
|
450
|
-
});
|
|
451
|
-
};
|
|
452
|
-
|
|
453
|
-
const waitForQueueSpace = async (): Promise<void> => {
|
|
454
|
-
while (
|
|
455
|
-
isQueueAtCapacity() &&
|
|
456
|
-
!consumerClosed &&
|
|
457
|
-
!isSignalAborted(options.signal)
|
|
458
|
-
) {
|
|
459
|
-
await new Promise<void>((resolve) => {
|
|
460
|
-
const signal = options.signal;
|
|
461
|
-
const onAbort = (): void => {
|
|
462
|
-
signal?.removeEventListener('abort', onAbort);
|
|
463
|
-
resolve();
|
|
464
|
-
};
|
|
465
|
-
const onSpace = (): void => {
|
|
466
|
-
signal?.removeEventListener('abort', onAbort);
|
|
467
|
-
resolve();
|
|
468
|
-
};
|
|
469
|
-
notifyProducer = onSpace;
|
|
470
|
-
signal?.addEventListener('abort', onAbort, { once: true });
|
|
471
|
-
if (isSignalAborted(signal)) {
|
|
472
|
-
onAbort();
|
|
473
|
-
}
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
|
-
};
|
|
477
|
-
|
|
478
|
-
const dequeue = (): QueuedGenerationChunk | undefined => {
|
|
479
|
-
if (!hasQueuedChunks()) {
|
|
480
|
-
return undefined;
|
|
481
|
-
}
|
|
482
|
-
const queuedChunk = queuedChunks[queuedChunkIndex];
|
|
483
|
-
queuedChunkIndex++;
|
|
484
|
-
if (
|
|
485
|
-
queuedChunkIndex > 128 &&
|
|
486
|
-
queuedChunkIndex * 2 >= queuedChunks.length
|
|
487
|
-
) {
|
|
488
|
-
queuedChunks.splice(0, queuedChunkIndex);
|
|
489
|
-
queuedChunkIndex = 0;
|
|
490
|
-
}
|
|
491
|
-
return queuedChunk;
|
|
492
|
-
};
|
|
493
|
-
|
|
494
|
-
const enqueue = async (
|
|
495
|
-
queuedChunk: QueuedGenerationChunk
|
|
496
|
-
): Promise<void> => {
|
|
497
|
-
await waitForQueueSpace();
|
|
498
|
-
if (consumerClosed || isSignalAborted(options.signal)) {
|
|
499
|
-
abortStream();
|
|
500
|
-
throw new Error('AbortError: User aborted the request.');
|
|
501
|
-
}
|
|
502
|
-
queuedChunks.push(queuedChunk);
|
|
503
|
-
if (queuedChunk.smooth) {
|
|
504
|
-
bufferedTextLength += queuedChunk.textLength;
|
|
505
|
-
}
|
|
506
|
-
notifyConsumerForChunk();
|
|
507
|
-
};
|
|
508
|
-
|
|
509
|
-
const enqueueChunk = async ({
|
|
510
|
-
chunk,
|
|
511
|
-
callbackChunk,
|
|
512
|
-
callbackToken = '',
|
|
513
|
-
smooth = false,
|
|
514
|
-
textLength = 0,
|
|
515
|
-
}: {
|
|
516
|
-
chunk: ChatGenerationChunk;
|
|
517
|
-
callbackChunk?: ChatGenerationChunk;
|
|
518
|
-
callbackToken?: string;
|
|
519
|
-
smooth?: boolean;
|
|
520
|
-
textLength?: number;
|
|
521
|
-
}): Promise<void> => {
|
|
522
|
-
await enqueue({
|
|
523
|
-
chunk,
|
|
524
|
-
callbackChunk,
|
|
525
|
-
callbackToken,
|
|
526
|
-
smooth,
|
|
527
|
-
textLength: smooth ? textLength : 0,
|
|
528
|
-
});
|
|
529
|
-
};
|
|
530
|
-
|
|
531
|
-
const enqueueDelta = async (
|
|
532
|
-
contentBlockDelta: ContentBlockDeltaEvent
|
|
533
|
-
): Promise<void> => {
|
|
329
|
+
/**
|
|
330
|
+
* Builds the emission for one piece of a delta, reproducing the exact
|
|
331
|
+
* per-piece pipeline (sliced delta → chunk → enrichment → stream-limit
|
|
332
|
+
* link). `indicesSnapshot` is the arrival-time copy of the seen block
|
|
333
|
+
* indices, so lazily emitted pieces observe the same enrichment
|
|
334
|
+
* decisions the delta saw when it arrived.
|
|
335
|
+
*/
|
|
336
|
+
const buildDeltaEmission = (
|
|
337
|
+
contentBlockDelta: ContentBlockDeltaEvent,
|
|
338
|
+
token: string,
|
|
339
|
+
indicesSnapshot: Set<number>
|
|
340
|
+
): BedrockEmittedChunk => {
|
|
534
341
|
const delta = contentBlockDelta.delta;
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
const idx = contentBlockDelta.contentBlockIndex;
|
|
540
|
-
if (idx != null) {
|
|
541
|
-
seenBlockIndices.add(idx);
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
const text = delta.text;
|
|
545
|
-
const reasoningContent = delta.reasoningContent;
|
|
342
|
+
const text = delta?.text;
|
|
343
|
+
const reasoningContent = delta?.reasoningContent;
|
|
546
344
|
const reasoningText = reasoningContent?.text;
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
splitDelta = {
|
|
565
|
-
...contentBlockDelta,
|
|
566
|
-
delta: {
|
|
567
|
-
reasoningContent: {
|
|
568
|
-
...reasoningContent,
|
|
569
|
-
text: token,
|
|
570
|
-
},
|
|
345
|
+
|
|
346
|
+
let splitDelta = contentBlockDelta;
|
|
347
|
+
if (typeof text === 'string') {
|
|
348
|
+
splitDelta = {
|
|
349
|
+
...contentBlockDelta,
|
|
350
|
+
delta: { text: token },
|
|
351
|
+
};
|
|
352
|
+
} else if (
|
|
353
|
+
typeof reasoningText === 'string' &&
|
|
354
|
+
reasoningContent != null
|
|
355
|
+
) {
|
|
356
|
+
splitDelta = {
|
|
357
|
+
...contentBlockDelta,
|
|
358
|
+
delta: {
|
|
359
|
+
reasoningContent: {
|
|
360
|
+
...reasoningContent,
|
|
361
|
+
text: token,
|
|
571
362
|
},
|
|
572
|
-
}
|
|
573
|
-
}
|
|
363
|
+
},
|
|
364
|
+
};
|
|
365
|
+
}
|
|
574
366
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
}
|
|
585
|
-
await enqueueChunk({
|
|
586
|
-
chunk: enrichedChunk,
|
|
587
|
-
callbackChunk: deltaChunk,
|
|
588
|
-
callbackToken: deltaChunk.text,
|
|
589
|
-
smooth,
|
|
590
|
-
textLength: token.length,
|
|
591
|
-
});
|
|
367
|
+
const deltaChunk = handleConverseStreamContentBlockDelta(splitDelta);
|
|
368
|
+
const enrichedChunk = this.enrichChunk(deltaChunk, indicesSnapshot);
|
|
369
|
+
if (enrichedChunk !== deltaChunk) {
|
|
370
|
+
/** The callback copy is the same emission as the enriched yield;
|
|
371
|
+
* without the link, stream-limit accounting charges both message
|
|
372
|
+
* objects and Bedrock tool arguments falsely trip near half the
|
|
373
|
+
* cap. Linked on the messages, which are what the accounting
|
|
374
|
+
* observes. */
|
|
375
|
+
linkStreamLimitCanonical(deltaChunk.message, enrichedChunk.message);
|
|
592
376
|
}
|
|
377
|
+
return {
|
|
378
|
+
chunk: enrichedChunk,
|
|
379
|
+
callbackChunk: deltaChunk,
|
|
380
|
+
callbackToken: deltaChunk.text,
|
|
381
|
+
};
|
|
593
382
|
};
|
|
594
383
|
|
|
595
|
-
const
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
)
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
seenBlockIndices.add(idx);
|
|
611
|
-
if (event.contentBlockStart.start?.toolUse != null) {
|
|
612
|
-
toolUseBlockIndices.add(idx);
|
|
613
|
-
}
|
|
384
|
+
const enrichChunk = this.enrichChunk.bind(this);
|
|
385
|
+
const source = (async function* (): AsyncGenerator<
|
|
386
|
+
SmoothItem<BedrockEmittedChunk>
|
|
387
|
+
> {
|
|
388
|
+
for await (const event of stream) {
|
|
389
|
+
if (event.contentBlockStart != null) {
|
|
390
|
+
const startChunk = handleConverseStreamContentBlockStart(
|
|
391
|
+
event.contentBlockStart
|
|
392
|
+
);
|
|
393
|
+
if (startChunk != null) {
|
|
394
|
+
const idx = event.contentBlockStart.contentBlockIndex;
|
|
395
|
+
if (idx != null) {
|
|
396
|
+
seenBlockIndices.add(idx);
|
|
397
|
+
if (event.contentBlockStart.start?.toolUse != null) {
|
|
398
|
+
toolUseBlockIndices.add(idx);
|
|
614
399
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
400
|
+
}
|
|
401
|
+
const enrichedStart = enrichChunk(startChunk, seenBlockIndices);
|
|
402
|
+
if (enrichedStart !== startChunk) {
|
|
403
|
+
linkStreamLimitCanonical(
|
|
404
|
+
startChunk.message,
|
|
405
|
+
enrichedStart.message
|
|
618
406
|
);
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
}
|
|
625
|
-
await enqueueChunk({
|
|
407
|
+
}
|
|
408
|
+
yield {
|
|
409
|
+
text: '',
|
|
410
|
+
smooth: false,
|
|
411
|
+
emit: (): BedrockEmittedChunk => ({
|
|
626
412
|
chunk: enrichedStart,
|
|
627
413
|
callbackChunk: startChunk,
|
|
628
414
|
callbackToken: startChunk.text,
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
} else if (event.contentBlockDelta != null) {
|
|
632
|
-
await enqueueDelta(event.contentBlockDelta);
|
|
633
|
-
} else if (event.metadata != null) {
|
|
634
|
-
await enqueueChunk({
|
|
635
|
-
chunk: handleConverseStreamMetadata(event.metadata, {
|
|
636
|
-
streamUsage,
|
|
637
415
|
}),
|
|
638
|
-
}
|
|
639
|
-
} else if (event.contentBlockStop != null) {
|
|
640
|
-
const stopIdx = event.contentBlockStop.contentBlockIndex;
|
|
641
|
-
if (stopIdx != null) {
|
|
642
|
-
seenBlockIndices.add(stopIdx);
|
|
643
|
-
if (sealToolUseOnStop && toolUseBlockIndices.has(stopIdx)) {
|
|
644
|
-
const sealChunk = createConverseToolUseStopChunk(stopIdx);
|
|
645
|
-
await enqueueChunk({
|
|
646
|
-
chunk: sealChunk,
|
|
647
|
-
callbackChunk: sealChunk,
|
|
648
|
-
callbackToken: sealChunk.text,
|
|
649
|
-
});
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
} else {
|
|
653
|
-
await enqueueChunk({
|
|
654
|
-
chunk: new ChatGenerationChunk({
|
|
655
|
-
text: '',
|
|
656
|
-
message: new AIMessageChunk({
|
|
657
|
-
content: '',
|
|
658
|
-
response_metadata: { ...event } as ResponseMetadata,
|
|
659
|
-
}),
|
|
660
|
-
}),
|
|
661
|
-
});
|
|
416
|
+
};
|
|
662
417
|
}
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
notifyConsumerForChunk();
|
|
669
|
-
}
|
|
670
|
-
})();
|
|
671
|
-
|
|
672
|
-
try {
|
|
673
|
-
let keepStreaming = true;
|
|
674
|
-
while (keepStreaming) {
|
|
675
|
-
if (isSignalAborted(options.signal)) {
|
|
676
|
-
abortStream();
|
|
677
|
-
throw new Error('AbortError: User aborted the request.');
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
await waitForNextChunk();
|
|
681
|
-
const queuedChunk = dequeue();
|
|
682
|
-
|
|
683
|
-
if (!queuedChunk) {
|
|
684
|
-
if (producerState.error != null) {
|
|
685
|
-
throw producerState.error;
|
|
418
|
+
} else if (event.contentBlockDelta != null) {
|
|
419
|
+
const contentBlockDelta = event.contentBlockDelta;
|
|
420
|
+
const delta = contentBlockDelta.delta;
|
|
421
|
+
if (delta == null) {
|
|
422
|
+
throw new Error('No delta found in content block.');
|
|
686
423
|
}
|
|
687
|
-
|
|
688
|
-
|
|
424
|
+
|
|
425
|
+
const idx = contentBlockDelta.contentBlockIndex;
|
|
426
|
+
if (idx != null) {
|
|
427
|
+
seenBlockIndices.add(idx);
|
|
689
428
|
}
|
|
690
|
-
continue;
|
|
691
|
-
}
|
|
692
429
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
bufferedTextLength - queuedChunk.textLength
|
|
430
|
+
const visibleText = resolveVisibleText(
|
|
431
|
+
delta.text,
|
|
432
|
+
delta.reasoningContent?.text
|
|
697
433
|
);
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
434
|
+
const indicesSnapshot = new Set(seenBlockIndices);
|
|
435
|
+
|
|
436
|
+
if (visibleText === '') {
|
|
437
|
+
yield {
|
|
438
|
+
text: '',
|
|
439
|
+
smooth: false,
|
|
440
|
+
emit: (): BedrockEmittedChunk =>
|
|
441
|
+
buildDeltaEmission(
|
|
442
|
+
contentBlockDelta,
|
|
443
|
+
visibleText,
|
|
444
|
+
indicesSnapshot
|
|
445
|
+
),
|
|
446
|
+
};
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
yield {
|
|
451
|
+
text: visibleText,
|
|
452
|
+
smooth: true,
|
|
453
|
+
emit: (piece): BedrockEmittedChunk =>
|
|
454
|
+
buildDeltaEmission(
|
|
455
|
+
contentBlockDelta,
|
|
456
|
+
piece.text,
|
|
457
|
+
indicesSnapshot
|
|
458
|
+
),
|
|
459
|
+
};
|
|
460
|
+
} else if (event.metadata != null) {
|
|
461
|
+
const metadataChunk = handleConverseStreamMetadata(event.metadata, {
|
|
462
|
+
streamUsage,
|
|
463
|
+
});
|
|
464
|
+
yield {
|
|
465
|
+
text: '',
|
|
466
|
+
smooth: false,
|
|
467
|
+
emit: (): BedrockEmittedChunk => ({ chunk: metadataChunk, callbackToken: '' }),
|
|
468
|
+
};
|
|
469
|
+
} else if (event.contentBlockStop != null) {
|
|
470
|
+
const stopIdx = event.contentBlockStop.contentBlockIndex;
|
|
471
|
+
if (stopIdx != null) {
|
|
472
|
+
seenBlockIndices.add(stopIdx);
|
|
473
|
+
if (sealToolUseOnStop && toolUseBlockIndices.has(stopIdx)) {
|
|
474
|
+
const sealChunk = createConverseToolUseStopChunk(stopIdx);
|
|
475
|
+
yield {
|
|
476
|
+
text: '',
|
|
477
|
+
smooth: false,
|
|
478
|
+
emit: (): BedrockEmittedChunk => ({
|
|
479
|
+
chunk: sealChunk,
|
|
480
|
+
callbackChunk: sealChunk,
|
|
481
|
+
callbackToken: sealChunk.text,
|
|
482
|
+
}),
|
|
483
|
+
};
|
|
484
|
+
}
|
|
712
485
|
}
|
|
713
|
-
hasEmittedVisibleContent = true;
|
|
714
|
-
lastVisibleContentAt = Date.now();
|
|
715
486
|
} else {
|
|
716
|
-
|
|
487
|
+
const eventChunk = new ChatGenerationChunk({
|
|
488
|
+
text: '',
|
|
489
|
+
message: new AIMessageChunk({
|
|
490
|
+
content: '',
|
|
491
|
+
response_metadata: { ...event } as ResponseMetadata,
|
|
492
|
+
}),
|
|
493
|
+
});
|
|
494
|
+
yield {
|
|
495
|
+
text: '',
|
|
496
|
+
smooth: false,
|
|
497
|
+
emit: (): BedrockEmittedChunk => ({ chunk: eventChunk, callbackToken: '' }),
|
|
498
|
+
};
|
|
717
499
|
}
|
|
500
|
+
}
|
|
501
|
+
})();
|
|
718
502
|
|
|
719
|
-
|
|
503
|
+
const smoothed = smoothStream({
|
|
504
|
+
source,
|
|
505
|
+
delayMs: this._lc_stream_delay,
|
|
506
|
+
signal: options.signal,
|
|
507
|
+
abortUpstream: abortStream,
|
|
508
|
+
});
|
|
720
509
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
consumerClosed = true;
|
|
734
|
-
if (!producerState.done) {
|
|
735
|
-
abortStream();
|
|
736
|
-
notifyProducerForSpace();
|
|
510
|
+
for await (const emitted of smoothed) {
|
|
511
|
+
yield emitted.chunk;
|
|
512
|
+
|
|
513
|
+
if (emitted.callbackChunk != null) {
|
|
514
|
+
await runManager?.handleLLMNewToken(
|
|
515
|
+
emitted.callbackToken,
|
|
516
|
+
undefined,
|
|
517
|
+
undefined,
|
|
518
|
+
undefined,
|
|
519
|
+
undefined,
|
|
520
|
+
{ chunk: emitted.callbackChunk }
|
|
521
|
+
);
|
|
737
522
|
}
|
|
738
|
-
await producer;
|
|
739
523
|
}
|
|
740
524
|
} finally {
|
|
741
525
|
options.signal?.removeEventListener('abort', abortStream);
|
|
@@ -233,6 +233,7 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
233
233
|
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
234
234
|
applicationInferenceProfile: testArn,
|
|
235
235
|
client: mockClient,
|
|
236
|
+
_lc_stream_delay: 0,
|
|
236
237
|
});
|
|
237
238
|
|
|
238
239
|
let chunks = 0;
|
|
@@ -281,6 +282,7 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
281
282
|
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
282
283
|
guardrailConfig,
|
|
283
284
|
client: mockClient,
|
|
285
|
+
_lc_stream_delay: 0,
|
|
284
286
|
});
|
|
285
287
|
|
|
286
288
|
let chunks = 0;
|