@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.
Files changed (49) hide show
  1. package/dist/cjs/graphs/Graph.cjs +19 -5
  2. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  3. package/dist/cjs/llm/bedrock/index.cjs +98 -25
  4. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  5. package/dist/cjs/messages/cache.cjs +27 -77
  6. package/dist/cjs/messages/cache.cjs.map +1 -1
  7. package/dist/cjs/messages/core.cjs +1 -1
  8. package/dist/cjs/messages/core.cjs.map +1 -1
  9. package/dist/cjs/stream.cjs +4 -2
  10. package/dist/cjs/stream.cjs.map +1 -1
  11. package/dist/cjs/tools/ToolNode.cjs +9 -5
  12. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  13. package/dist/esm/graphs/Graph.mjs +19 -5
  14. package/dist/esm/graphs/Graph.mjs.map +1 -1
  15. package/dist/esm/llm/bedrock/index.mjs +97 -24
  16. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  17. package/dist/esm/messages/cache.mjs +27 -77
  18. package/dist/esm/messages/cache.mjs.map +1 -1
  19. package/dist/esm/messages/core.mjs +1 -1
  20. package/dist/esm/messages/core.mjs.map +1 -1
  21. package/dist/esm/stream.mjs +4 -2
  22. package/dist/esm/stream.mjs.map +1 -1
  23. package/dist/esm/tools/ToolNode.mjs +9 -5
  24. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  25. package/dist/types/llm/bedrock/index.d.ts +86 -7
  26. package/dist/types/llm/bedrock/types.d.ts +27 -0
  27. package/dist/types/llm/bedrock/utils/index.d.ts +5 -0
  28. package/dist/types/llm/bedrock/utils/message_inputs.d.ts +31 -0
  29. package/dist/types/llm/bedrock/utils/message_outputs.d.ts +33 -0
  30. package/dist/types/types/tools.d.ts +2 -0
  31. package/package.json +7 -4
  32. package/src/graphs/Graph.ts +23 -5
  33. package/src/llm/bedrock/index.ts +180 -43
  34. package/src/llm/bedrock/llm.spec.ts +616 -0
  35. package/src/llm/bedrock/types.ts +51 -0
  36. package/src/llm/bedrock/utils/index.ts +18 -0
  37. package/src/llm/bedrock/utils/message_inputs.ts +563 -0
  38. package/src/llm/bedrock/utils/message_outputs.ts +310 -0
  39. package/src/messages/cache.test.ts +6 -12
  40. package/src/messages/cache.ts +48 -107
  41. package/src/messages/core.ts +1 -1
  42. package/src/scripts/code_exec_multi_session.ts +241 -0
  43. package/src/scripts/thinking-bedrock.ts +159 -0
  44. package/src/scripts/thinking.ts +39 -18
  45. package/src/scripts/tools.ts +7 -3
  46. package/src/specs/cache.simple.test.ts +396 -0
  47. package/src/stream.ts +4 -2
  48. package/src/tools/ToolNode.ts +9 -5
  49. 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 keep lc_kwargs.content in sync with content for LangChain messages', () => {
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.content).toEqual(resultFirst.lc_kwargs?.content);
959
- expect(resultThird.content).toEqual(resultThird.lc_kwargs?.content);
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 keep lc_kwargs.content in sync with content for LangChain messages', () => {
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.content).toEqual(resultFirst.lc_kwargs?.content);
1131
- expect(resultSecond.content).toEqual(resultSecond.lc_kwargs?.content);
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[];
@@ -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
- * Simple shallow clone with deep-cloned content.
34
- * Used for stripping cache control where we don't need proper LangChain instances.
26
+ * Clones a message with deep-cloned content, explicitly excluding LangChain
27
+ * serialization metadata to prevent coercion issues.
35
28
  */
36
- function shallowCloneMessage<T extends MessageWithContent>(message: T): T {
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
- if ('getType' in message && typeof message.getType === 'function') {
52
- const baseMsg = message as unknown as BaseMessage;
53
- const msgType = baseMsg.getType();
54
-
55
- const baseFields = {
56
- content,
57
- name: baseMsg.name,
58
- additional_kwargs: { ...baseMsg.additional_kwargs },
59
- response_metadata: { ...baseMsg.response_metadata },
60
- id: baseMsg.id,
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] = shallowCloneMessage(
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
- if (cacheAdded) {
198
- updatedMessages[i] = createNewMessage(
199
- originalMessage as MessageWithContent,
200
- workingContent
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 message = shallowCloneMessage(originalMessage);
253
- updatedMessages[i] = message;
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 message = shallowCloneMessage(originalMessage);
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
- ) as typeof content;
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] = shallowCloneMessage(originalMessage);
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] = shallowCloneMessage(originalMessage);
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] = createNewMessage(originalMessage, workingContent);
368
+ updatedMessages[i] = cloneMessage(originalMessage, workingContent);
428
369
  messagesModified++;
429
370
  }
430
371
 
@@ -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
  };