@librechat/agents 3.0.79 → 3.0.80
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 +35 -74
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +35 -74
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/types/llm/bedrock/index.d.ts +7 -4
- package/package.json +2 -1
- package/src/llm/bedrock/index.ts +42 -96
- package/src/llm/bedrock/utils/message_outputs.ts +34 -20
- package/src/scripts/thinking-bedrock.ts +159 -0
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +0 -465
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +0 -1
- package/dist/cjs/llm/bedrock/utils/message_outputs.cjs +0 -238
- package/dist/cjs/llm/bedrock/utils/message_outputs.cjs.map +0 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +0 -460
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +0 -1
- package/dist/esm/llm/bedrock/utils/message_outputs.mjs +0 -231
- package/dist/esm/llm/bedrock/utils/message_outputs.mjs.map +0 -1
|
@@ -38,13 +38,13 @@ export function bedrockReasoningDeltaToLangchainPartialReasoningBlock(
|
|
|
38
38
|
reasoningText: { text },
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
-
if (signature) {
|
|
41
|
+
if (signature != null) {
|
|
42
42
|
return {
|
|
43
43
|
type: 'reasoning_content',
|
|
44
44
|
reasoningText: { signature },
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
|
-
if (redactedContent) {
|
|
47
|
+
if (redactedContent != null) {
|
|
48
48
|
return {
|
|
49
49
|
type: 'reasoning_content',
|
|
50
50
|
redactedContent: Buffer.from(redactedContent).toString('base64'),
|
|
@@ -65,13 +65,13 @@ export function bedrockReasoningBlockToLangchainReasoningBlock(
|
|
|
65
65
|
redactedContent?: Uint8Array;
|
|
66
66
|
};
|
|
67
67
|
|
|
68
|
-
if (reasoningText) {
|
|
68
|
+
if (reasoningText != null) {
|
|
69
69
|
return {
|
|
70
70
|
type: 'reasoning_content',
|
|
71
71
|
reasoningText: reasoningText,
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
|
-
if (redactedContent) {
|
|
74
|
+
if (redactedContent != null) {
|
|
75
75
|
return {
|
|
76
76
|
type: 'reasoning_content',
|
|
77
77
|
redactedContent: Buffer.from(redactedContent).toString('base64'),
|
|
@@ -87,7 +87,7 @@ export function convertConverseMessageToLangChainMessage(
|
|
|
87
87
|
message: BedrockMessage,
|
|
88
88
|
responseMetadata: Omit<ConverseResponse, 'output'>
|
|
89
89
|
): AIMessage {
|
|
90
|
-
if (
|
|
90
|
+
if (message.content == null) {
|
|
91
91
|
throw new Error('No message content found in response.');
|
|
92
92
|
}
|
|
93
93
|
if (message.role !== 'assistant') {
|
|
@@ -99,7 +99,7 @@ export function convertConverseMessageToLangChainMessage(
|
|
|
99
99
|
let requestId: string | undefined;
|
|
100
100
|
if (
|
|
101
101
|
'$metadata' in responseMetadata &&
|
|
102
|
-
responseMetadata.$metadata &&
|
|
102
|
+
responseMetadata.$metadata != null &&
|
|
103
103
|
typeof responseMetadata.$metadata === 'object' &&
|
|
104
104
|
'requestId' in responseMetadata.$metadata
|
|
105
105
|
) {
|
|
@@ -109,7 +109,7 @@ export function convertConverseMessageToLangChainMessage(
|
|
|
109
109
|
let tokenUsage:
|
|
110
110
|
| { input_tokens: number; output_tokens: number; total_tokens: number }
|
|
111
111
|
| undefined;
|
|
112
|
-
if (responseMetadata.usage) {
|
|
112
|
+
if (responseMetadata.usage != null) {
|
|
113
113
|
const input_tokens = responseMetadata.usage.inputTokens ?? 0;
|
|
114
114
|
const output_tokens = responseMetadata.usage.outputTokens ?? 0;
|
|
115
115
|
tokenUsage = {
|
|
@@ -144,9 +144,10 @@ export function convertConverseMessageToLangChainMessage(
|
|
|
144
144
|
message.content.forEach((c) => {
|
|
145
145
|
if (
|
|
146
146
|
'toolUse' in c &&
|
|
147
|
-
c.toolUse &&
|
|
148
|
-
c.toolUse.name &&
|
|
149
|
-
c.toolUse.
|
|
147
|
+
c.toolUse != null &&
|
|
148
|
+
c.toolUse.name != null &&
|
|
149
|
+
c.toolUse.name !== '' &&
|
|
150
|
+
c.toolUse.input != null &&
|
|
150
151
|
typeof c.toolUse.input === 'object'
|
|
151
152
|
) {
|
|
152
153
|
toolCalls.push({
|
|
@@ -157,7 +158,7 @@ export function convertConverseMessageToLangChainMessage(
|
|
|
157
158
|
});
|
|
158
159
|
} else if ('text' in c && typeof c.text === 'string') {
|
|
159
160
|
content.push({ type: 'text', text: c.text });
|
|
160
|
-
} else if ('reasoningContent' in c && c.reasoningContent) {
|
|
161
|
+
} else if ('reasoningContent' in c && c.reasoningContent != null) {
|
|
161
162
|
content.push(
|
|
162
163
|
bedrockReasoningBlockToLangchainReasoningBlock(c.reasoningContent)
|
|
163
164
|
);
|
|
@@ -182,7 +183,7 @@ export function convertConverseMessageToLangChainMessage(
|
|
|
182
183
|
export function handleConverseStreamContentBlockDelta(
|
|
183
184
|
contentBlockDelta: ContentBlockDeltaEvent
|
|
184
185
|
): ChatGenerationChunk {
|
|
185
|
-
if (
|
|
186
|
+
if (contentBlockDelta.delta == null) {
|
|
186
187
|
throw new Error('No delta found in content block.');
|
|
187
188
|
}
|
|
188
189
|
|
|
@@ -196,7 +197,7 @@ export function handleConverseStreamContentBlockDelta(
|
|
|
196
197
|
},
|
|
197
198
|
}),
|
|
198
199
|
});
|
|
199
|
-
} else if (contentBlockDelta.delta.toolUse) {
|
|
200
|
+
} else if (contentBlockDelta.delta.toolUse != null) {
|
|
200
201
|
const index = contentBlockDelta.contentBlockIndex;
|
|
201
202
|
return new ChatGenerationChunk({
|
|
202
203
|
text: '',
|
|
@@ -214,15 +215,28 @@ export function handleConverseStreamContentBlockDelta(
|
|
|
214
215
|
},
|
|
215
216
|
}),
|
|
216
217
|
});
|
|
217
|
-
} else if (contentBlockDelta.delta.reasoningContent) {
|
|
218
|
+
} else if (contentBlockDelta.delta.reasoningContent != null) {
|
|
219
|
+
const reasoningBlock =
|
|
220
|
+
bedrockReasoningDeltaToLangchainPartialReasoningBlock(
|
|
221
|
+
contentBlockDelta.delta.reasoningContent
|
|
222
|
+
);
|
|
223
|
+
// Extract the text for additional_kwargs.reasoning_content (for stream handler compatibility)
|
|
224
|
+
const reasoningText =
|
|
225
|
+
'reasoningText' in reasoningBlock
|
|
226
|
+
? (reasoningBlock.reasoningText.text ??
|
|
227
|
+
reasoningBlock.reasoningText.signature ??
|
|
228
|
+
('redactedContent' in reasoningBlock
|
|
229
|
+
? reasoningBlock.redactedContent
|
|
230
|
+
: ''))
|
|
231
|
+
: '';
|
|
218
232
|
return new ChatGenerationChunk({
|
|
219
233
|
text: '',
|
|
220
234
|
message: new AIMessageChunk({
|
|
221
|
-
content: [
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
235
|
+
content: [reasoningBlock],
|
|
236
|
+
additional_kwargs: {
|
|
237
|
+
// Set reasoning_content for stream handler to detect reasoning mode
|
|
238
|
+
reasoning_content: reasoningText,
|
|
239
|
+
},
|
|
226
240
|
response_metadata: {
|
|
227
241
|
contentBlockIndex: contentBlockDelta.contentBlockIndex,
|
|
228
242
|
},
|
|
@@ -243,7 +257,7 @@ export function handleConverseStreamContentBlockStart(
|
|
|
243
257
|
): ChatGenerationChunk | null {
|
|
244
258
|
const index = contentBlockStart.contentBlockIndex;
|
|
245
259
|
|
|
246
|
-
if (contentBlockStart.start?.toolUse) {
|
|
260
|
+
if (contentBlockStart.start?.toolUse != null) {
|
|
247
261
|
return new ChatGenerationChunk({
|
|
248
262
|
text: '',
|
|
249
263
|
message: new AIMessageChunk({
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// src/scripts/thinking-bedrock.ts
|
|
2
|
+
import { config } from 'dotenv';
|
|
3
|
+
config();
|
|
4
|
+
import { HumanMessage, BaseMessage } from '@langchain/core/messages';
|
|
5
|
+
import type { UsageMetadata } from '@langchain/core/messages';
|
|
6
|
+
import * as t from '@/types';
|
|
7
|
+
import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
|
|
8
|
+
import { createCodeExecutionTool } from '@/tools/CodeExecutor';
|
|
9
|
+
import { ToolEndHandler, ModelEndHandler } from '@/events';
|
|
10
|
+
import { GraphEvents, Providers } from '@/common';
|
|
11
|
+
import { getLLMConfig } from '@/utils/llmConfig';
|
|
12
|
+
import { getArgs } from '@/scripts/args';
|
|
13
|
+
import { Run } from '@/run';
|
|
14
|
+
|
|
15
|
+
const conversationHistory: BaseMessage[] = [];
|
|
16
|
+
let _contentParts: t.MessageContentComplex[] = [];
|
|
17
|
+
const collectedUsage: UsageMetadata[] = [];
|
|
18
|
+
|
|
19
|
+
async function testBedrockThinking(): Promise<void> {
|
|
20
|
+
const { userName } = await getArgs();
|
|
21
|
+
const instructions = `You are a helpful AI assistant for ${userName}. When answering questions, be thorough in your reasoning.`;
|
|
22
|
+
const { contentParts, aggregateContent } = createContentAggregator();
|
|
23
|
+
_contentParts = contentParts as t.MessageContentComplex[];
|
|
24
|
+
|
|
25
|
+
// Set up event handlers
|
|
26
|
+
const customHandlers = {
|
|
27
|
+
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
28
|
+
[GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(collectedUsage),
|
|
29
|
+
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
30
|
+
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
31
|
+
handle: (
|
|
32
|
+
event: GraphEvents.ON_RUN_STEP_COMPLETED,
|
|
33
|
+
data: t.StreamEventData
|
|
34
|
+
): void => {
|
|
35
|
+
console.log('====== ON_RUN_STEP_COMPLETED ======');
|
|
36
|
+
aggregateContent({
|
|
37
|
+
event,
|
|
38
|
+
data: data as unknown as { result: t.ToolEndEvent },
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
[GraphEvents.ON_RUN_STEP]: {
|
|
43
|
+
handle: (event: GraphEvents.ON_RUN_STEP, data: t.RunStep) => {
|
|
44
|
+
aggregateContent({ event, data });
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
|
48
|
+
handle: (
|
|
49
|
+
event: GraphEvents.ON_RUN_STEP_DELTA,
|
|
50
|
+
data: t.RunStepDeltaEvent
|
|
51
|
+
) => {
|
|
52
|
+
aggregateContent({ event, data });
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
56
|
+
handle: (
|
|
57
|
+
event: GraphEvents.ON_MESSAGE_DELTA,
|
|
58
|
+
data: t.MessageDeltaEvent
|
|
59
|
+
) => {
|
|
60
|
+
aggregateContent({ event, data });
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
[GraphEvents.ON_REASONING_DELTA]: {
|
|
64
|
+
handle: (
|
|
65
|
+
event: GraphEvents.ON_REASONING_DELTA,
|
|
66
|
+
data: t.ReasoningDeltaEvent
|
|
67
|
+
) => {
|
|
68
|
+
aggregateContent({ event, data });
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const baseLlmConfig = getLLMConfig(Providers.BEDROCK);
|
|
74
|
+
|
|
75
|
+
// Enable thinking with token budget for Bedrock
|
|
76
|
+
const llmConfig = {
|
|
77
|
+
...baseLlmConfig,
|
|
78
|
+
model: 'us.anthropic.claude-3-7-sonnet-20250219-v1:0',
|
|
79
|
+
maxTokens: 5000,
|
|
80
|
+
additionalModelRequestFields: {
|
|
81
|
+
thinking: { type: 'enabled', budget_tokens: 2000 },
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const run = await Run.create<t.IState>({
|
|
86
|
+
runId: 'test-bedrock-thinking-id',
|
|
87
|
+
graphConfig: {
|
|
88
|
+
instructions,
|
|
89
|
+
type: 'standard',
|
|
90
|
+
tools: [createCodeExecutionTool()],
|
|
91
|
+
llmConfig,
|
|
92
|
+
},
|
|
93
|
+
returnContent: true,
|
|
94
|
+
customHandlers: customHandlers as t.RunConfig['customHandlers'],
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const config = {
|
|
98
|
+
configurable: {
|
|
99
|
+
thread_id: 'bedrock-thinking-test-thread',
|
|
100
|
+
},
|
|
101
|
+
streamMode: 'values',
|
|
102
|
+
version: 'v2' as const,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// Test 1: Regular thinking mode
|
|
106
|
+
console.log('\n\nTest 1: Bedrock Regular thinking mode');
|
|
107
|
+
const userMessage1 = `Please print 'hello world' in python`;
|
|
108
|
+
conversationHistory.push(new HumanMessage(userMessage1));
|
|
109
|
+
|
|
110
|
+
console.log('Running first query with Bedrock thinking enabled...');
|
|
111
|
+
const firstInputs = { messages: [...conversationHistory] };
|
|
112
|
+
await run.processStream(firstInputs, config);
|
|
113
|
+
|
|
114
|
+
// Extract and display thinking blocks
|
|
115
|
+
const finalMessages = run.getRunMessages();
|
|
116
|
+
console.log('\n\nFinal messages after Test 1:');
|
|
117
|
+
console.dir(finalMessages, { depth: null });
|
|
118
|
+
|
|
119
|
+
// Test 2: Try multi-turn conversation
|
|
120
|
+
console.log(
|
|
121
|
+
'\n\nTest 2: Multi-turn conversation with Bedrock thinking enabled'
|
|
122
|
+
);
|
|
123
|
+
const userMessage2 = `Given your previous analysis, what would be the most significant technical challenges in making this transition?`;
|
|
124
|
+
conversationHistory.push(new HumanMessage(userMessage2));
|
|
125
|
+
|
|
126
|
+
console.log('Running second query with Bedrock thinking enabled...');
|
|
127
|
+
const secondInputs = { messages: [...conversationHistory] };
|
|
128
|
+
await run.processStream(secondInputs, config);
|
|
129
|
+
|
|
130
|
+
// Display thinking blocks for second response
|
|
131
|
+
const finalMessages2 = run.getRunMessages();
|
|
132
|
+
console.log('\n\nBedrock thinking feature test completed!');
|
|
133
|
+
console.dir(finalMessages2, { depth: null });
|
|
134
|
+
|
|
135
|
+
console.log('\n\nContent parts:');
|
|
136
|
+
console.dir(_contentParts, { depth: null });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
140
|
+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
141
|
+
console.log('Conversation history:');
|
|
142
|
+
console.dir(conversationHistory, { depth: null });
|
|
143
|
+
console.log('Content parts:');
|
|
144
|
+
console.dir(_contentParts, { depth: null });
|
|
145
|
+
process.exit(1);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
process.on('uncaughtException', (err) => {
|
|
149
|
+
console.error('Uncaught Exception:', err);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
testBedrockThinking().catch((err) => {
|
|
153
|
+
console.error(err);
|
|
154
|
+
console.log('Conversation history:');
|
|
155
|
+
console.dir(conversationHistory, { depth: null });
|
|
156
|
+
console.log('Content parts:');
|
|
157
|
+
console.dir(_contentParts, { depth: null });
|
|
158
|
+
process.exit(1);
|
|
159
|
+
});
|