@librechat/agents 3.0.775 → 3.1.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 +19 -5
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +98 -25
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +27 -77
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +1 -1
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/stream.cjs +4 -2
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +9 -5
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +19 -5
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +97 -24
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/messages/cache.mjs +27 -77
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +1 -1
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/stream.mjs +4 -2
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +9 -5
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/types/llm/bedrock/index.d.ts +86 -7
- package/dist/types/llm/bedrock/types.d.ts +27 -0
- package/dist/types/llm/bedrock/utils/index.d.ts +5 -0
- package/dist/types/llm/bedrock/utils/message_inputs.d.ts +31 -0
- package/dist/types/llm/bedrock/utils/message_outputs.d.ts +33 -0
- package/dist/types/types/tools.d.ts +2 -0
- package/package.json +7 -4
- package/src/graphs/Graph.ts +23 -5
- package/src/llm/bedrock/index.ts +180 -43
- package/src/llm/bedrock/llm.spec.ts +616 -0
- package/src/llm/bedrock/types.ts +51 -0
- package/src/llm/bedrock/utils/index.ts +18 -0
- package/src/llm/bedrock/utils/message_inputs.ts +563 -0
- package/src/llm/bedrock/utils/message_outputs.ts +310 -0
- package/src/messages/cache.test.ts +6 -12
- package/src/messages/cache.ts +48 -107
- package/src/messages/core.ts +1 -1
- package/src/scripts/code_exec_multi_session.ts +241 -0
- package/src/scripts/thinking-bedrock.ts +159 -0
- package/src/scripts/thinking.ts +39 -18
- package/src/scripts/tools.ts +7 -3
- package/src/specs/cache.simple.test.ts +396 -0
- package/src/stream.ts +4 -2
- package/src/tools/ToolNode.ts +9 -5
- package/src/types/tools.ts +2 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for converting Bedrock Converse responses to LangChain messages.
|
|
3
|
+
* Ported from @langchain/aws common.js
|
|
4
|
+
*/
|
|
5
|
+
import { AIMessage, AIMessageChunk } from '@langchain/core/messages';
|
|
6
|
+
import { ChatGenerationChunk } from '@langchain/core/outputs';
|
|
7
|
+
import type {
|
|
8
|
+
BedrockMessage,
|
|
9
|
+
ConverseResponse,
|
|
10
|
+
ContentBlockDeltaEvent,
|
|
11
|
+
ConverseStreamMetadataEvent,
|
|
12
|
+
ContentBlockStartEvent,
|
|
13
|
+
ReasoningContentBlock,
|
|
14
|
+
ReasoningContentBlockDelta,
|
|
15
|
+
MessageContentReasoningBlock,
|
|
16
|
+
MessageContentReasoningBlockReasoningTextPartial,
|
|
17
|
+
MessageContentReasoningBlockRedacted,
|
|
18
|
+
} from '../types';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Convert a Bedrock reasoning block delta to a LangChain partial reasoning block.
|
|
22
|
+
*/
|
|
23
|
+
export function bedrockReasoningDeltaToLangchainPartialReasoningBlock(
|
|
24
|
+
reasoningContent: ReasoningContentBlockDelta
|
|
25
|
+
):
|
|
26
|
+
| MessageContentReasoningBlockReasoningTextPartial
|
|
27
|
+
| MessageContentReasoningBlockRedacted {
|
|
28
|
+
const { text, redactedContent, signature } =
|
|
29
|
+
reasoningContent as ReasoningContentBlockDelta & {
|
|
30
|
+
text?: string;
|
|
31
|
+
redactedContent?: Uint8Array;
|
|
32
|
+
signature?: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
if (typeof text === 'string') {
|
|
36
|
+
return {
|
|
37
|
+
type: 'reasoning_content',
|
|
38
|
+
reasoningText: { text },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (signature != null) {
|
|
42
|
+
return {
|
|
43
|
+
type: 'reasoning_content',
|
|
44
|
+
reasoningText: { signature },
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (redactedContent != null) {
|
|
48
|
+
return {
|
|
49
|
+
type: 'reasoning_content',
|
|
50
|
+
redactedContent: Buffer.from(redactedContent).toString('base64'),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
throw new Error('Invalid reasoning content');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Convert a Bedrock reasoning block to a LangChain reasoning block.
|
|
58
|
+
*/
|
|
59
|
+
export function bedrockReasoningBlockToLangchainReasoningBlock(
|
|
60
|
+
reasoningContent: ReasoningContentBlock
|
|
61
|
+
): MessageContentReasoningBlock {
|
|
62
|
+
const { reasoningText, redactedContent } =
|
|
63
|
+
reasoningContent as ReasoningContentBlock & {
|
|
64
|
+
reasoningText?: { text?: string; signature?: string };
|
|
65
|
+
redactedContent?: Uint8Array;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (reasoningText != null) {
|
|
69
|
+
return {
|
|
70
|
+
type: 'reasoning_content',
|
|
71
|
+
reasoningText: reasoningText,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (redactedContent != null) {
|
|
75
|
+
return {
|
|
76
|
+
type: 'reasoning_content',
|
|
77
|
+
redactedContent: Buffer.from(redactedContent).toString('base64'),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
throw new Error('Invalid reasoning content');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Convert a Bedrock Converse message to a LangChain message.
|
|
85
|
+
*/
|
|
86
|
+
export function convertConverseMessageToLangChainMessage(
|
|
87
|
+
message: BedrockMessage,
|
|
88
|
+
responseMetadata: Omit<ConverseResponse, 'output'>
|
|
89
|
+
): AIMessage {
|
|
90
|
+
if (message.content == null) {
|
|
91
|
+
throw new Error('No message content found in response.');
|
|
92
|
+
}
|
|
93
|
+
if (message.role !== 'assistant') {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`Unsupported message role received in ChatBedrockConverse response: ${message.role}`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let requestId: string | undefined;
|
|
100
|
+
if (
|
|
101
|
+
'$metadata' in responseMetadata &&
|
|
102
|
+
responseMetadata.$metadata != null &&
|
|
103
|
+
typeof responseMetadata.$metadata === 'object' &&
|
|
104
|
+
'requestId' in responseMetadata.$metadata
|
|
105
|
+
) {
|
|
106
|
+
requestId = responseMetadata.$metadata.requestId as string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let tokenUsage:
|
|
110
|
+
| { input_tokens: number; output_tokens: number; total_tokens: number }
|
|
111
|
+
| undefined;
|
|
112
|
+
if (responseMetadata.usage != null) {
|
|
113
|
+
const input_tokens = responseMetadata.usage.inputTokens ?? 0;
|
|
114
|
+
const output_tokens = responseMetadata.usage.outputTokens ?? 0;
|
|
115
|
+
tokenUsage = {
|
|
116
|
+
input_tokens,
|
|
117
|
+
output_tokens,
|
|
118
|
+
total_tokens:
|
|
119
|
+
responseMetadata.usage.totalTokens ?? input_tokens + output_tokens,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (
|
|
124
|
+
message.content.length === 1 &&
|
|
125
|
+
'text' in message.content[0] &&
|
|
126
|
+
typeof message.content[0].text === 'string'
|
|
127
|
+
) {
|
|
128
|
+
return new AIMessage({
|
|
129
|
+
content: message.content[0].text,
|
|
130
|
+
response_metadata: responseMetadata,
|
|
131
|
+
usage_metadata: tokenUsage,
|
|
132
|
+
id: requestId,
|
|
133
|
+
});
|
|
134
|
+
} else {
|
|
135
|
+
const toolCalls: Array<{
|
|
136
|
+
id?: string;
|
|
137
|
+
name: string;
|
|
138
|
+
args: Record<string, unknown>;
|
|
139
|
+
type: 'tool_call';
|
|
140
|
+
}> = [];
|
|
141
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
142
|
+
const content: any[] = [];
|
|
143
|
+
|
|
144
|
+
message.content.forEach((c) => {
|
|
145
|
+
if (
|
|
146
|
+
'toolUse' in c &&
|
|
147
|
+
c.toolUse != null &&
|
|
148
|
+
c.toolUse.name != null &&
|
|
149
|
+
c.toolUse.name !== '' &&
|
|
150
|
+
c.toolUse.input != null &&
|
|
151
|
+
typeof c.toolUse.input === 'object'
|
|
152
|
+
) {
|
|
153
|
+
toolCalls.push({
|
|
154
|
+
id: c.toolUse.toolUseId,
|
|
155
|
+
name: c.toolUse.name,
|
|
156
|
+
args: c.toolUse.input as Record<string, unknown>,
|
|
157
|
+
type: 'tool_call',
|
|
158
|
+
});
|
|
159
|
+
} else if ('text' in c && typeof c.text === 'string') {
|
|
160
|
+
content.push({ type: 'text', text: c.text });
|
|
161
|
+
} else if ('reasoningContent' in c && c.reasoningContent != null) {
|
|
162
|
+
content.push(
|
|
163
|
+
bedrockReasoningBlockToLangchainReasoningBlock(c.reasoningContent)
|
|
164
|
+
);
|
|
165
|
+
} else {
|
|
166
|
+
content.push(c);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
return new AIMessage({
|
|
171
|
+
content: content.length ? content : '',
|
|
172
|
+
tool_calls: toolCalls.length ? toolCalls : undefined,
|
|
173
|
+
response_metadata: responseMetadata,
|
|
174
|
+
usage_metadata: tokenUsage,
|
|
175
|
+
id: requestId,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Handle a content block delta event from Bedrock Converse stream.
|
|
182
|
+
*/
|
|
183
|
+
export function handleConverseStreamContentBlockDelta(
|
|
184
|
+
contentBlockDelta: ContentBlockDeltaEvent
|
|
185
|
+
): ChatGenerationChunk {
|
|
186
|
+
if (contentBlockDelta.delta == null) {
|
|
187
|
+
throw new Error('No delta found in content block.');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (typeof contentBlockDelta.delta.text === 'string') {
|
|
191
|
+
return new ChatGenerationChunk({
|
|
192
|
+
text: contentBlockDelta.delta.text,
|
|
193
|
+
message: new AIMessageChunk({
|
|
194
|
+
content: contentBlockDelta.delta.text,
|
|
195
|
+
response_metadata: {
|
|
196
|
+
contentBlockIndex: contentBlockDelta.contentBlockIndex,
|
|
197
|
+
},
|
|
198
|
+
}),
|
|
199
|
+
});
|
|
200
|
+
} else if (contentBlockDelta.delta.toolUse != null) {
|
|
201
|
+
const index = contentBlockDelta.contentBlockIndex;
|
|
202
|
+
return new ChatGenerationChunk({
|
|
203
|
+
text: '',
|
|
204
|
+
message: new AIMessageChunk({
|
|
205
|
+
content: '',
|
|
206
|
+
tool_call_chunks: [
|
|
207
|
+
{
|
|
208
|
+
args: contentBlockDelta.delta.toolUse.input as string,
|
|
209
|
+
index,
|
|
210
|
+
type: 'tool_call_chunk',
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
response_metadata: {
|
|
214
|
+
contentBlockIndex: contentBlockDelta.contentBlockIndex,
|
|
215
|
+
},
|
|
216
|
+
}),
|
|
217
|
+
});
|
|
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
|
+
: '';
|
|
232
|
+
return new ChatGenerationChunk({
|
|
233
|
+
text: '',
|
|
234
|
+
message: new AIMessageChunk({
|
|
235
|
+
content: [reasoningBlock],
|
|
236
|
+
additional_kwargs: {
|
|
237
|
+
// Set reasoning_content for stream handler to detect reasoning mode
|
|
238
|
+
reasoning_content: reasoningText,
|
|
239
|
+
},
|
|
240
|
+
response_metadata: {
|
|
241
|
+
contentBlockIndex: contentBlockDelta.contentBlockIndex,
|
|
242
|
+
},
|
|
243
|
+
}),
|
|
244
|
+
});
|
|
245
|
+
} else {
|
|
246
|
+
throw new Error(
|
|
247
|
+
`Unsupported content block type(s): ${JSON.stringify(contentBlockDelta.delta, null, 2)}`
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Handle a content block start event from Bedrock Converse stream.
|
|
254
|
+
*/
|
|
255
|
+
export function handleConverseStreamContentBlockStart(
|
|
256
|
+
contentBlockStart: ContentBlockStartEvent
|
|
257
|
+
): ChatGenerationChunk | null {
|
|
258
|
+
const index = contentBlockStart.contentBlockIndex;
|
|
259
|
+
|
|
260
|
+
if (contentBlockStart.start?.toolUse != null) {
|
|
261
|
+
return new ChatGenerationChunk({
|
|
262
|
+
text: '',
|
|
263
|
+
message: new AIMessageChunk({
|
|
264
|
+
content: '',
|
|
265
|
+
tool_call_chunks: [
|
|
266
|
+
{
|
|
267
|
+
name: contentBlockStart.start.toolUse.name,
|
|
268
|
+
id: contentBlockStart.start.toolUse.toolUseId,
|
|
269
|
+
index,
|
|
270
|
+
type: 'tool_call_chunk',
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
response_metadata: {
|
|
274
|
+
contentBlockIndex: index,
|
|
275
|
+
},
|
|
276
|
+
}),
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Return null for non-tool content block starts (text blocks don't need special handling)
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Handle a metadata event from Bedrock Converse stream.
|
|
286
|
+
*/
|
|
287
|
+
export function handleConverseStreamMetadata(
|
|
288
|
+
metadata: ConverseStreamMetadataEvent,
|
|
289
|
+
extra: { streamUsage: boolean }
|
|
290
|
+
): ChatGenerationChunk {
|
|
291
|
+
const inputTokens = metadata.usage?.inputTokens ?? 0;
|
|
292
|
+
const outputTokens = metadata.usage?.outputTokens ?? 0;
|
|
293
|
+
const usage_metadata = {
|
|
294
|
+
input_tokens: inputTokens,
|
|
295
|
+
output_tokens: outputTokens,
|
|
296
|
+
total_tokens: metadata.usage?.totalTokens ?? inputTokens + outputTokens,
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
return new ChatGenerationChunk({
|
|
300
|
+
text: '',
|
|
301
|
+
message: new AIMessageChunk({
|
|
302
|
+
content: '',
|
|
303
|
+
usage_metadata: extra.streamUsage ? usage_metadata : undefined,
|
|
304
|
+
response_metadata: {
|
|
305
|
+
// Use the same key as returned from the Converse API
|
|
306
|
+
metadata,
|
|
307
|
+
},
|
|
308
|
+
}),
|
|
309
|
+
});
|
|
310
|
+
}
|
|
@@ -921,7 +921,7 @@ describe('Immutability - addCacheControl does not mutate original messages', ()
|
|
|
921
921
|
expect('cache_control' in originalFirstBlock).toBe(true);
|
|
922
922
|
});
|
|
923
923
|
|
|
924
|
-
it('should
|
|
924
|
+
it('should remove lc_kwargs to prevent serialization mismatch for LangChain messages', () => {
|
|
925
925
|
type LangChainLikeMsg = TestMsg & {
|
|
926
926
|
lc_kwargs?: { content?: MessageContentComplex[] };
|
|
927
927
|
};
|
|
@@ -955,14 +955,11 @@ describe('Immutability - addCacheControl does not mutate original messages', ()
|
|
|
955
955
|
const resultFirst = result[0] as LangChainLikeMsg;
|
|
956
956
|
const resultThird = result[2] as LangChainLikeMsg;
|
|
957
957
|
|
|
958
|
-
expect(resultFirst.
|
|
959
|
-
expect(resultThird.
|
|
958
|
+
expect(resultFirst.lc_kwargs).toBeUndefined();
|
|
959
|
+
expect(resultThird.lc_kwargs).toBeUndefined();
|
|
960
960
|
|
|
961
961
|
const firstContent = resultFirst.content as MessageContentComplex[];
|
|
962
|
-
const firstLcContent = resultFirst.lc_kwargs
|
|
963
|
-
?.content as MessageContentComplex[];
|
|
964
962
|
expect('cache_control' in firstContent[0]).toBe(true);
|
|
965
|
-
expect('cache_control' in firstLcContent[0]).toBe(true);
|
|
966
963
|
|
|
967
964
|
const originalFirst = messagesWithLcKwargs[0];
|
|
968
965
|
const originalContent = originalFirst.content as MessageContentComplex[];
|
|
@@ -1100,7 +1097,7 @@ describe('Immutability - addBedrockCacheControl does not mutate original message
|
|
|
1100
1097
|
expect('cache_control' in anthropicFirstContent[0]).toBe(true);
|
|
1101
1098
|
});
|
|
1102
1099
|
|
|
1103
|
-
it('should
|
|
1100
|
+
it('should remove lc_kwargs to prevent serialization mismatch for LangChain messages', () => {
|
|
1104
1101
|
type LangChainLikeMsg = TestMsg & {
|
|
1105
1102
|
lc_kwargs?: { content?: MessageContentComplex[] };
|
|
1106
1103
|
};
|
|
@@ -1127,14 +1124,11 @@ describe('Immutability - addBedrockCacheControl does not mutate original message
|
|
|
1127
1124
|
const resultFirst = bedrockResult[0] as LangChainLikeMsg;
|
|
1128
1125
|
const resultSecond = bedrockResult[1] as LangChainLikeMsg;
|
|
1129
1126
|
|
|
1130
|
-
expect(resultFirst.
|
|
1131
|
-
expect(resultSecond.
|
|
1127
|
+
expect(resultFirst.lc_kwargs).toBeUndefined();
|
|
1128
|
+
expect(resultSecond.lc_kwargs).toBeUndefined();
|
|
1132
1129
|
|
|
1133
1130
|
const firstContent = resultFirst.content as MessageContentComplex[];
|
|
1134
|
-
const firstLcContent = resultFirst.lc_kwargs
|
|
1135
|
-
?.content as MessageContentComplex[];
|
|
1136
1131
|
expect(firstContent.some((b) => 'cachePoint' in b)).toBe(true);
|
|
1137
|
-
expect(firstLcContent.some((b) => 'cachePoint' in b)).toBe(true);
|
|
1138
1132
|
|
|
1139
1133
|
const originalFirst = messagesWithLcKwargs[0];
|
|
1140
1134
|
const originalContent = originalFirst.content as MessageContentComplex[];
|
package/src/messages/cache.ts
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BaseMessage,
|
|
3
|
-
MessageContentComplex,
|
|
4
|
-
AIMessage,
|
|
5
|
-
HumanMessage,
|
|
6
|
-
SystemMessage,
|
|
7
|
-
ToolMessage,
|
|
8
|
-
} from '@langchain/core/messages';
|
|
1
|
+
import { BaseMessage, MessageContentComplex } from '@langchain/core/messages';
|
|
9
2
|
import type { AnthropicMessage } from '@/types/messages';
|
|
10
3
|
import type Anthropic from '@anthropic-ai/sdk';
|
|
11
4
|
import { ContentTypes } from '@/common/enum';
|
|
@@ -30,73 +23,42 @@ function deepCloneContent<T extends string | MessageContentComplex[]>(
|
|
|
30
23
|
}
|
|
31
24
|
|
|
32
25
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
26
|
+
* Clones a message with deep-cloned content, explicitly excluding LangChain
|
|
27
|
+
* serialization metadata to prevent coercion issues.
|
|
35
28
|
*/
|
|
36
|
-
function
|
|
37
|
-
return {
|
|
38
|
-
...message,
|
|
39
|
-
content: deepCloneContent(message.content ?? ''),
|
|
40
|
-
} as T;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Creates a new LangChain message instance with the given content.
|
|
45
|
-
* Required when adding cache points to ensure proper serialization.
|
|
46
|
-
*/
|
|
47
|
-
function createNewMessage<T extends MessageWithContent>(
|
|
29
|
+
function cloneMessage<T extends MessageWithContent>(
|
|
48
30
|
message: T,
|
|
49
|
-
content: MessageContentComplex[]
|
|
31
|
+
content: string | MessageContentComplex[]
|
|
50
32
|
): T {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
33
|
+
const {
|
|
34
|
+
lc_kwargs: _lc_kwargs,
|
|
35
|
+
lc_serializable: _lc_serializable,
|
|
36
|
+
lc_namespace: _lc_namespace,
|
|
37
|
+
...rest
|
|
38
|
+
} = message as T & {
|
|
39
|
+
lc_kwargs?: unknown;
|
|
40
|
+
lc_serializable?: unknown;
|
|
41
|
+
lc_namespace?: unknown;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const cloned = { ...rest, content } as T;
|
|
45
|
+
|
|
46
|
+
// LangChain messages don't have a direct 'role' property - derive it from getType()
|
|
47
|
+
if (
|
|
48
|
+
'getType' in message &&
|
|
49
|
+
typeof message.getType === 'function' &&
|
|
50
|
+
!('role' in cloned)
|
|
51
|
+
) {
|
|
52
|
+
const msgType = (message as unknown as BaseMessage).getType();
|
|
53
|
+
const roleMap: Record<string, string> = {
|
|
54
|
+
human: 'user',
|
|
55
|
+
ai: 'assistant',
|
|
56
|
+
system: 'system',
|
|
57
|
+
tool: 'tool',
|
|
61
58
|
};
|
|
62
|
-
|
|
63
|
-
switch (msgType) {
|
|
64
|
-
case 'human':
|
|
65
|
-
return new HumanMessage(baseFields) as unknown as T;
|
|
66
|
-
case 'ai': {
|
|
67
|
-
const aiMsg = baseMsg as AIMessage;
|
|
68
|
-
return new AIMessage({
|
|
69
|
-
...baseFields,
|
|
70
|
-
tool_calls: aiMsg.tool_calls ? [...aiMsg.tool_calls] : [],
|
|
71
|
-
invalid_tool_calls: aiMsg.invalid_tool_calls
|
|
72
|
-
? [...aiMsg.invalid_tool_calls]
|
|
73
|
-
: [],
|
|
74
|
-
usage_metadata: aiMsg.usage_metadata,
|
|
75
|
-
}) as unknown as T;
|
|
76
|
-
}
|
|
77
|
-
case 'system':
|
|
78
|
-
return new SystemMessage(baseFields) as unknown as T;
|
|
79
|
-
case 'tool': {
|
|
80
|
-
const toolMsg = baseMsg as ToolMessage;
|
|
81
|
-
return new ToolMessage({
|
|
82
|
-
...baseFields,
|
|
83
|
-
tool_call_id: toolMsg.tool_call_id,
|
|
84
|
-
status: toolMsg.status,
|
|
85
|
-
artifact: toolMsg.artifact,
|
|
86
|
-
}) as unknown as T;
|
|
87
|
-
}
|
|
88
|
-
default:
|
|
89
|
-
break;
|
|
90
|
-
}
|
|
59
|
+
(cloned as Record<string, unknown>).role = roleMap[msgType] || msgType;
|
|
91
60
|
}
|
|
92
61
|
|
|
93
|
-
const cloned = { ...message, content } as T;
|
|
94
|
-
const lcKwargs = (cloned as Record<string, unknown>).lc_kwargs as
|
|
95
|
-
| Record<string, unknown>
|
|
96
|
-
| undefined;
|
|
97
|
-
if (lcKwargs != null) {
|
|
98
|
-
(cloned as Record<string, unknown>).lc_kwargs = { ...lcKwargs, content };
|
|
99
|
-
}
|
|
100
62
|
return cloned;
|
|
101
63
|
}
|
|
102
64
|
|
|
@@ -174,37 +136,28 @@ export function addCacheControl<T extends AnthropicMessage | BaseMessage>(
|
|
|
174
136
|
}
|
|
175
137
|
|
|
176
138
|
if (userMessagesModified >= 2 || !isUserMessage) {
|
|
177
|
-
updatedMessages[i] =
|
|
178
|
-
originalMessage as MessageWithContent
|
|
139
|
+
updatedMessages[i] = cloneMessage(
|
|
140
|
+
originalMessage as MessageWithContent,
|
|
141
|
+
workingContent
|
|
179
142
|
) as T;
|
|
180
|
-
(updatedMessages[i] as MessageWithContent).content = workingContent;
|
|
181
143
|
continue;
|
|
182
144
|
}
|
|
183
145
|
|
|
184
|
-
let cacheAdded = false;
|
|
185
146
|
for (let j = workingContent.length - 1; j >= 0; j--) {
|
|
186
147
|
const contentPart = workingContent[j];
|
|
187
148
|
if ('type' in contentPart && contentPart.type === 'text') {
|
|
188
149
|
(contentPart as Anthropic.TextBlockParam).cache_control = {
|
|
189
150
|
type: 'ephemeral',
|
|
190
151
|
};
|
|
191
|
-
cacheAdded = true;
|
|
192
152
|
userMessagesModified++;
|
|
193
153
|
break;
|
|
194
154
|
}
|
|
195
155
|
}
|
|
196
156
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
) as T;
|
|
202
|
-
} else {
|
|
203
|
-
updatedMessages[i] = shallowCloneMessage(
|
|
204
|
-
originalMessage as MessageWithContent
|
|
205
|
-
) as T;
|
|
206
|
-
(updatedMessages[i] as MessageWithContent).content = workingContent;
|
|
207
|
-
}
|
|
157
|
+
updatedMessages[i] = cloneMessage(
|
|
158
|
+
originalMessage as MessageWithContent,
|
|
159
|
+
workingContent
|
|
160
|
+
) as T;
|
|
208
161
|
}
|
|
209
162
|
|
|
210
163
|
return updatedMessages;
|
|
@@ -249,22 +202,14 @@ export function stripAnthropicCacheControl<T extends MessageWithContent>(
|
|
|
249
202
|
continue;
|
|
250
203
|
}
|
|
251
204
|
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
for (
|
|
256
|
-
let j = 0;
|
|
257
|
-
j < (message.content as MessageContentComplex[]).length;
|
|
258
|
-
j++
|
|
259
|
-
) {
|
|
260
|
-
const block = (message.content as MessageContentComplex[])[j] as Record<
|
|
261
|
-
string,
|
|
262
|
-
unknown
|
|
263
|
-
>;
|
|
205
|
+
const clonedContent = deepCloneContent(content);
|
|
206
|
+
for (let j = 0; j < clonedContent.length; j++) {
|
|
207
|
+
const block = clonedContent[j] as Record<string, unknown>;
|
|
264
208
|
if ('cache_control' in block) {
|
|
265
209
|
delete block.cache_control;
|
|
266
210
|
}
|
|
267
211
|
}
|
|
212
|
+
updatedMessages[i] = cloneMessage(originalMessage, clonedContent);
|
|
268
213
|
}
|
|
269
214
|
|
|
270
215
|
return updatedMessages;
|
|
@@ -302,12 +247,10 @@ export function stripBedrockCacheControl<T extends MessageWithContent>(
|
|
|
302
247
|
continue;
|
|
303
248
|
}
|
|
304
249
|
|
|
305
|
-
const
|
|
306
|
-
updatedMessages[i] = message;
|
|
307
|
-
|
|
308
|
-
message.content = (message.content as MessageContentComplex[]).filter(
|
|
250
|
+
const clonedContent = deepCloneContent(content).filter(
|
|
309
251
|
(block) => !isCachePoint(block as MessageContentComplex)
|
|
310
|
-
)
|
|
252
|
+
);
|
|
253
|
+
updatedMessages[i] = cloneMessage(originalMessage, clonedContent);
|
|
311
254
|
}
|
|
312
255
|
|
|
313
256
|
return updatedMessages;
|
|
@@ -377,8 +320,7 @@ export function addBedrockCacheControl<
|
|
|
377
320
|
}
|
|
378
321
|
|
|
379
322
|
if (messagesModified >= 2 || isToolMessage || isEmptyString) {
|
|
380
|
-
updatedMessages[i] =
|
|
381
|
-
(updatedMessages[i] as MessageWithContent).content = workingContent;
|
|
323
|
+
updatedMessages[i] = cloneMessage(originalMessage, workingContent);
|
|
382
324
|
continue;
|
|
383
325
|
}
|
|
384
326
|
|
|
@@ -397,8 +339,7 @@ export function addBedrockCacheControl<
|
|
|
397
339
|
}
|
|
398
340
|
|
|
399
341
|
if (!hasCacheableContent) {
|
|
400
|
-
updatedMessages[i] =
|
|
401
|
-
(updatedMessages[i] as MessageWithContent).content = workingContent;
|
|
342
|
+
updatedMessages[i] = cloneMessage(originalMessage, workingContent);
|
|
402
343
|
continue;
|
|
403
344
|
}
|
|
404
345
|
|
|
@@ -424,7 +365,7 @@ export function addBedrockCacheControl<
|
|
|
424
365
|
} as MessageContentComplex);
|
|
425
366
|
}
|
|
426
367
|
|
|
427
|
-
updatedMessages[i] =
|
|
368
|
+
updatedMessages[i] = cloneMessage(originalMessage, workingContent);
|
|
428
369
|
messagesModified++;
|
|
429
370
|
}
|
|
430
371
|
|
package/src/messages/core.ts
CHANGED
|
@@ -41,7 +41,7 @@ User: ${userMessage[1]}
|
|
|
41
41
|
const _allowedTypes = ['image_url', 'text', 'tool_use', 'tool_result'];
|
|
42
42
|
const allowedTypesByProvider: Record<string, string[]> = {
|
|
43
43
|
default: _allowedTypes,
|
|
44
|
-
[Providers.ANTHROPIC]: [..._allowedTypes, 'thinking'],
|
|
44
|
+
[Providers.ANTHROPIC]: [..._allowedTypes, 'thinking', 'redacted_thinking'],
|
|
45
45
|
[Providers.BEDROCK]: [..._allowedTypes, 'reasoning_content'],
|
|
46
46
|
[Providers.OPENAI]: _allowedTypes,
|
|
47
47
|
};
|