@librechat/agents 3.1.34 → 3.1.35
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/llm/bedrock/index.cjs +115 -55
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +465 -0
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -0
- package/dist/cjs/llm/bedrock/utils/message_outputs.cjs +155 -0
- package/dist/cjs/llm/bedrock/utils/message_outputs.cjs.map +1 -0
- package/dist/cjs/messages/core.cjs +3 -0
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +114 -54
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +460 -0
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -0
- package/dist/esm/llm/bedrock/utils/message_outputs.mjs +150 -0
- package/dist/esm/llm/bedrock/utils/message_outputs.mjs.map +1 -0
- package/dist/esm/messages/core.mjs +3 -0
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/types/llm/bedrock/index.d.ts +29 -21
- package/package.json +1 -1
- package/src/llm/bedrock/index.ts +147 -65
- package/src/llm/bedrock/llm.spec.ts +86 -11
- package/src/messages/core.ts +5 -0
- package/src/scripts/bedrock-merge-test.ts +107 -0
|
@@ -315,7 +315,7 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
315
315
|
expect(model.removeContentBlockIndex(undefined)).toBeUndefined();
|
|
316
316
|
});
|
|
317
317
|
|
|
318
|
-
test('
|
|
318
|
+
test('enrichChunk should strip contentBlockIndex from response_metadata', () => {
|
|
319
319
|
const model = getModelWithCleanMethods();
|
|
320
320
|
|
|
321
321
|
const chunkWithIndex = new ChatGenerationChunk({
|
|
@@ -329,18 +329,18 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
329
329
|
}),
|
|
330
330
|
});
|
|
331
331
|
|
|
332
|
-
const
|
|
332
|
+
const enriched = model.enrichChunk(chunkWithIndex, new Set([0]));
|
|
333
333
|
|
|
334
|
-
expect(
|
|
334
|
+
expect(enriched.message.response_metadata).toEqual({
|
|
335
335
|
stopReason: null,
|
|
336
336
|
});
|
|
337
337
|
expect(
|
|
338
|
-
(
|
|
338
|
+
(enriched.message.response_metadata as any).contentBlockIndex
|
|
339
339
|
).toBeUndefined();
|
|
340
|
-
expect(
|
|
340
|
+
expect(enriched.text).toBe('Hello');
|
|
341
341
|
});
|
|
342
342
|
|
|
343
|
-
test('
|
|
343
|
+
test('enrichChunk should pass through chunks without contentBlockIndex unchanged', () => {
|
|
344
344
|
const model = getModelWithCleanMethods();
|
|
345
345
|
|
|
346
346
|
const chunkWithoutIndex = new ChatGenerationChunk({
|
|
@@ -354,15 +354,89 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
354
354
|
}),
|
|
355
355
|
});
|
|
356
356
|
|
|
357
|
-
const
|
|
357
|
+
const enriched = model.enrichChunk(chunkWithoutIndex, new Set());
|
|
358
358
|
|
|
359
|
-
expect(
|
|
359
|
+
expect(enriched.message.response_metadata).toEqual({
|
|
360
360
|
stopReason: 'end_turn',
|
|
361
361
|
usage: { inputTokens: 10, outputTokens: 5 },
|
|
362
362
|
});
|
|
363
363
|
});
|
|
364
364
|
|
|
365
|
-
test('
|
|
365
|
+
test('enrichChunk should inject index on array content blocks', () => {
|
|
366
|
+
const model = getModelWithCleanMethods();
|
|
367
|
+
|
|
368
|
+
const chunkWithArrayContent = new ChatGenerationChunk({
|
|
369
|
+
text: '',
|
|
370
|
+
message: new AIMessageChunk({
|
|
371
|
+
content: [
|
|
372
|
+
{
|
|
373
|
+
type: 'reasoning_content',
|
|
374
|
+
reasoningText: { text: 'thinking...' },
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
response_metadata: {
|
|
378
|
+
contentBlockIndex: 0,
|
|
379
|
+
},
|
|
380
|
+
}),
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
const enriched = model.enrichChunk(chunkWithArrayContent, new Set([0]));
|
|
384
|
+
|
|
385
|
+
expect(Array.isArray(enriched.message.content)).toBe(true);
|
|
386
|
+
const blocks = enriched.message.content as any[];
|
|
387
|
+
expect(blocks[0].index).toBe(0);
|
|
388
|
+
expect(blocks[0].type).toBe('reasoning_content');
|
|
389
|
+
expect(
|
|
390
|
+
(enriched.message.response_metadata as any).contentBlockIndex
|
|
391
|
+
).toBeUndefined();
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
test('enrichChunk should promote text to array when multiple block indices seen', () => {
|
|
395
|
+
const model = getModelWithCleanMethods();
|
|
396
|
+
|
|
397
|
+
const textChunk = new ChatGenerationChunk({
|
|
398
|
+
text: 'Hello world',
|
|
399
|
+
message: new AIMessageChunk({
|
|
400
|
+
content: 'Hello world',
|
|
401
|
+
response_metadata: {
|
|
402
|
+
contentBlockIndex: 1,
|
|
403
|
+
},
|
|
404
|
+
}),
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
const enriched = model.enrichChunk(textChunk, new Set([0, 1]));
|
|
408
|
+
|
|
409
|
+
expect(Array.isArray(enriched.message.content)).toBe(true);
|
|
410
|
+
const blocks = enriched.message.content as any[];
|
|
411
|
+
expect(blocks).toHaveLength(1);
|
|
412
|
+
expect(blocks[0]).toEqual({
|
|
413
|
+
type: 'text',
|
|
414
|
+
text: 'Hello world',
|
|
415
|
+
index: 1,
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
test('enrichChunk should keep text as string when only one block index seen', () => {
|
|
420
|
+
const model = getModelWithCleanMethods();
|
|
421
|
+
|
|
422
|
+
const textChunk = new ChatGenerationChunk({
|
|
423
|
+
text: 'Hello',
|
|
424
|
+
message: new AIMessageChunk({
|
|
425
|
+
content: 'Hello',
|
|
426
|
+
response_metadata: {
|
|
427
|
+
contentBlockIndex: 0,
|
|
428
|
+
stopReason: null,
|
|
429
|
+
},
|
|
430
|
+
}),
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
const enriched = model.enrichChunk(textChunk, new Set([0]));
|
|
434
|
+
|
|
435
|
+
expect(typeof enriched.message.content).toBe('string');
|
|
436
|
+
expect(enriched.message.content).toBe('Hello');
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
test('enrichChunk should strip deeply nested contentBlockIndex from response_metadata', () => {
|
|
366
440
|
const model = getModelWithCleanMethods();
|
|
367
441
|
|
|
368
442
|
const chunkWithNestedIndex = new ChatGenerationChunk({
|
|
@@ -370,6 +444,7 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
370
444
|
message: new AIMessageChunk({
|
|
371
445
|
content: 'Test',
|
|
372
446
|
response_metadata: {
|
|
447
|
+
contentBlockIndex: 0,
|
|
373
448
|
amazon: {
|
|
374
449
|
bedrock: {
|
|
375
450
|
contentBlockIndex: 0,
|
|
@@ -381,9 +456,9 @@ describe('CustomChatBedrockConverse', () => {
|
|
|
381
456
|
}),
|
|
382
457
|
});
|
|
383
458
|
|
|
384
|
-
const
|
|
459
|
+
const enriched = model.enrichChunk(chunkWithNestedIndex, new Set([0]));
|
|
385
460
|
|
|
386
|
-
expect(
|
|
461
|
+
expect(enriched.message.response_metadata).toEqual({
|
|
387
462
|
amazon: {
|
|
388
463
|
bedrock: {
|
|
389
464
|
trace: { something: 'value' },
|
package/src/messages/core.ts
CHANGED
|
@@ -159,6 +159,11 @@ export function modifyDeltaProperties(
|
|
|
159
159
|
(obj as Partial<AIMessageChunk>).lc_kwargs &&
|
|
160
160
|
Array.isArray(obj.lc_kwargs.content)
|
|
161
161
|
) {
|
|
162
|
+
if (provider === Providers.BEDROCK) {
|
|
163
|
+
obj.lc_kwargs.content = reduceBlocks(
|
|
164
|
+
obj.lc_kwargs.content as ContentBlock[]
|
|
165
|
+
);
|
|
166
|
+
}
|
|
162
167
|
obj.lc_kwargs.content = modifyContent({
|
|
163
168
|
provider,
|
|
164
169
|
messageType,
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { config } from 'dotenv';
|
|
2
|
+
config();
|
|
3
|
+
import { HumanMessage } from '@langchain/core/messages';
|
|
4
|
+
import type { AIMessageChunk } from '@langchain/core/messages';
|
|
5
|
+
import { concat } from '@langchain/core/utils/stream';
|
|
6
|
+
import { CustomChatBedrockConverse } from '@/llm/bedrock';
|
|
7
|
+
import { modifyDeltaProperties } from '@/messages/core';
|
|
8
|
+
import { Providers } from '@/common';
|
|
9
|
+
|
|
10
|
+
async function testBedrockMerge(): Promise<void> {
|
|
11
|
+
const model = new CustomChatBedrockConverse({
|
|
12
|
+
model: 'us.anthropic.claude-3-7-sonnet-20250219-v1:0',
|
|
13
|
+
region: process.env.BEDROCK_AWS_REGION,
|
|
14
|
+
credentials: {
|
|
15
|
+
accessKeyId: process.env.BEDROCK_AWS_ACCESS_KEY_ID!,
|
|
16
|
+
secretAccessKey: process.env.BEDROCK_AWS_SECRET_ACCESS_KEY!,
|
|
17
|
+
},
|
|
18
|
+
maxTokens: 4000,
|
|
19
|
+
streaming: true,
|
|
20
|
+
streamUsage: true,
|
|
21
|
+
additionalModelRequestFields: {
|
|
22
|
+
thinking: { type: 'enabled', budget_tokens: 2000 },
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const messages = [new HumanMessage('What is 25 * 37? Think step by step.')];
|
|
27
|
+
|
|
28
|
+
console.log('Streaming from Bedrock with thinking enabled...\n');
|
|
29
|
+
|
|
30
|
+
const stream = await model.stream(messages);
|
|
31
|
+
let finalChunk: AIMessageChunk | undefined;
|
|
32
|
+
let chunkCount = 0;
|
|
33
|
+
let firstTextLogged = false;
|
|
34
|
+
|
|
35
|
+
for await (const chunk of stream) {
|
|
36
|
+
chunkCount++;
|
|
37
|
+
const isArr = Array.isArray(chunk.content);
|
|
38
|
+
const isStr = typeof chunk.content === 'string';
|
|
39
|
+
const isTextStr = isStr && (chunk.content as string).length > 0;
|
|
40
|
+
|
|
41
|
+
if (!firstTextLogged && isTextStr) {
|
|
42
|
+
console.log(
|
|
43
|
+
`chunk ${chunkCount} (first text): contentType=string, value="${chunk.content}"`
|
|
44
|
+
);
|
|
45
|
+
console.log(
|
|
46
|
+
' response_metadata:',
|
|
47
|
+
JSON.stringify(chunk.response_metadata)
|
|
48
|
+
);
|
|
49
|
+
firstTextLogged = true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (isArr) {
|
|
53
|
+
const blocks = chunk.content as Array<Record<string, unknown>>;
|
|
54
|
+
const info = blocks.map((b) => ({
|
|
55
|
+
type: b.type,
|
|
56
|
+
hasIndex: 'index' in b,
|
|
57
|
+
index: b.index,
|
|
58
|
+
}));
|
|
59
|
+
console.log(`chunk ${chunkCount}: array content, blocks:`, info);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
finalChunk = finalChunk ? concat(finalChunk, chunk) : chunk;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log(`Total chunks received: ${chunkCount}\n`);
|
|
66
|
+
|
|
67
|
+
console.log('=== RAW concat result (before modifyDeltaProperties) ===');
|
|
68
|
+
console.log('content type:', typeof finalChunk!.content);
|
|
69
|
+
if (Array.isArray(finalChunk!.content)) {
|
|
70
|
+
console.log('content array length:', finalChunk!.content.length);
|
|
71
|
+
const types = finalChunk!.content.map((b) =>
|
|
72
|
+
typeof b === 'object' && 'type' in b ? b.type : typeof b
|
|
73
|
+
);
|
|
74
|
+
const typeCounts = types.reduce(
|
|
75
|
+
(acc, t) => {
|
|
76
|
+
acc[t ?? ''] = (acc[t ?? ''] || 0) + 1;
|
|
77
|
+
return acc;
|
|
78
|
+
},
|
|
79
|
+
{} as Record<string, number>
|
|
80
|
+
);
|
|
81
|
+
console.log('content block type counts:', typeCounts);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
console.log('\ncontent:');
|
|
85
|
+
console.dir(finalChunk!.content, { depth: null });
|
|
86
|
+
|
|
87
|
+
console.log('\n=== lc_kwargs.content ===');
|
|
88
|
+
if (Array.isArray(finalChunk!.lc_kwargs.content)) {
|
|
89
|
+
console.log(
|
|
90
|
+
'lc_kwargs.content length:',
|
|
91
|
+
finalChunk!.lc_kwargs.content.length
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
console.dir(finalChunk!.lc_kwargs.content, { depth: null });
|
|
95
|
+
|
|
96
|
+
const modified = modifyDeltaProperties(Providers.BEDROCK, finalChunk);
|
|
97
|
+
console.log('\n=== After modifyDeltaProperties ===');
|
|
98
|
+
console.log('content:');
|
|
99
|
+
console.dir(modified!.content, { depth: null });
|
|
100
|
+
console.log('\nlc_kwargs.content:');
|
|
101
|
+
console.dir(modified!.lc_kwargs.content, { depth: null });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
testBedrockMerge().catch((err) => {
|
|
105
|
+
console.error(err);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
});
|