illuma-agents 1.0.37 → 1.0.39

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 (139) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +112 -14
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/common/enum.cjs +5 -1
  4. package/dist/cjs/common/enum.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +148 -8
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/graphs/MultiAgentGraph.cjs +277 -11
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  9. package/dist/cjs/llm/bedrock/index.cjs +128 -61
  10. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  11. package/dist/cjs/main.cjs +22 -7
  12. package/dist/cjs/main.cjs.map +1 -1
  13. package/dist/cjs/messages/cache.cjs +140 -46
  14. package/dist/cjs/messages/cache.cjs.map +1 -1
  15. package/dist/cjs/messages/core.cjs +1 -1
  16. package/dist/cjs/messages/core.cjs.map +1 -1
  17. package/dist/cjs/messages/tools.cjs +2 -2
  18. package/dist/cjs/messages/tools.cjs.map +1 -1
  19. package/dist/cjs/schemas/validate.cjs +173 -0
  20. package/dist/cjs/schemas/validate.cjs.map +1 -0
  21. package/dist/cjs/stream.cjs +4 -2
  22. package/dist/cjs/stream.cjs.map +1 -1
  23. package/dist/cjs/tools/BrowserTools.cjs.map +1 -1
  24. package/dist/cjs/tools/CodeExecutor.cjs +22 -21
  25. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  26. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +14 -11
  27. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  28. package/dist/cjs/tools/ToolNode.cjs +101 -2
  29. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  30. package/dist/cjs/tools/ToolSearch.cjs +862 -0
  31. package/dist/cjs/tools/ToolSearch.cjs.map +1 -0
  32. package/dist/esm/agents/AgentContext.mjs +112 -14
  33. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  34. package/dist/esm/common/enum.mjs +5 -1
  35. package/dist/esm/common/enum.mjs.map +1 -1
  36. package/dist/esm/graphs/Graph.mjs +149 -9
  37. package/dist/esm/graphs/Graph.mjs.map +1 -1
  38. package/dist/esm/graphs/MultiAgentGraph.mjs +278 -12
  39. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  40. package/dist/esm/llm/bedrock/index.mjs +127 -60
  41. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  42. package/dist/esm/main.mjs +2 -1
  43. package/dist/esm/main.mjs.map +1 -1
  44. package/dist/esm/messages/cache.mjs +140 -46
  45. package/dist/esm/messages/cache.mjs.map +1 -1
  46. package/dist/esm/messages/core.mjs +1 -1
  47. package/dist/esm/messages/core.mjs.map +1 -1
  48. package/dist/esm/messages/tools.mjs +2 -2
  49. package/dist/esm/messages/tools.mjs.map +1 -1
  50. package/dist/esm/schemas/validate.mjs +167 -0
  51. package/dist/esm/schemas/validate.mjs.map +1 -0
  52. package/dist/esm/stream.mjs +4 -2
  53. package/dist/esm/stream.mjs.map +1 -1
  54. package/dist/esm/tools/BrowserTools.mjs.map +1 -1
  55. package/dist/esm/tools/CodeExecutor.mjs +22 -21
  56. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  57. package/dist/esm/tools/ProgrammaticToolCalling.mjs +14 -11
  58. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  59. package/dist/esm/tools/ToolNode.mjs +102 -3
  60. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  61. package/dist/esm/tools/ToolSearch.mjs +827 -0
  62. package/dist/esm/tools/ToolSearch.mjs.map +1 -0
  63. package/dist/types/agents/AgentContext.d.ts +51 -1
  64. package/dist/types/common/enum.d.ts +6 -2
  65. package/dist/types/graphs/Graph.d.ts +12 -0
  66. package/dist/types/graphs/MultiAgentGraph.d.ts +16 -0
  67. package/dist/types/index.d.ts +2 -1
  68. package/dist/types/llm/bedrock/index.d.ts +89 -11
  69. package/dist/types/llm/bedrock/types.d.ts +27 -0
  70. package/dist/types/llm/bedrock/utils/index.d.ts +5 -0
  71. package/dist/types/llm/bedrock/utils/message_inputs.d.ts +31 -0
  72. package/dist/types/llm/bedrock/utils/message_outputs.d.ts +33 -0
  73. package/dist/types/messages/cache.d.ts +4 -1
  74. package/dist/types/schemas/index.d.ts +1 -0
  75. package/dist/types/schemas/validate.d.ts +36 -0
  76. package/dist/types/tools/CodeExecutor.d.ts +0 -3
  77. package/dist/types/tools/ProgrammaticToolCalling.d.ts +0 -3
  78. package/dist/types/tools/ToolNode.d.ts +3 -1
  79. package/dist/types/tools/ToolSearch.d.ts +148 -0
  80. package/dist/types/types/graph.d.ts +71 -0
  81. package/dist/types/types/llm.d.ts +3 -1
  82. package/dist/types/types/tools.d.ts +42 -2
  83. package/package.json +13 -6
  84. package/src/agents/AgentContext.test.ts +312 -0
  85. package/src/agents/AgentContext.ts +144 -16
  86. package/src/common/enum.ts +5 -1
  87. package/src/graphs/Graph.ts +214 -13
  88. package/src/graphs/MultiAgentGraph.ts +350 -13
  89. package/src/index.ts +4 -1
  90. package/src/llm/bedrock/index.ts +221 -99
  91. package/src/llm/bedrock/llm.spec.ts +616 -0
  92. package/src/llm/bedrock/types.ts +51 -0
  93. package/src/llm/bedrock/utils/index.ts +18 -0
  94. package/src/llm/bedrock/utils/message_inputs.ts +563 -0
  95. package/src/llm/bedrock/utils/message_outputs.ts +310 -0
  96. package/src/messages/__tests__/tools.test.ts +21 -21
  97. package/src/messages/cache.test.ts +304 -0
  98. package/src/messages/cache.ts +183 -53
  99. package/src/messages/core.ts +1 -1
  100. package/src/messages/tools.ts +2 -2
  101. package/src/schemas/index.ts +2 -0
  102. package/src/schemas/validate.test.ts +358 -0
  103. package/src/schemas/validate.ts +238 -0
  104. package/src/scripts/caching.ts +27 -19
  105. package/src/scripts/code_exec_files.ts +58 -15
  106. package/src/scripts/code_exec_multi_session.ts +241 -0
  107. package/src/scripts/code_exec_session.ts +282 -0
  108. package/src/scripts/multi-agent-conditional.ts +1 -0
  109. package/src/scripts/multi-agent-supervisor.ts +1 -0
  110. package/src/scripts/programmatic_exec_agent.ts +4 -4
  111. package/src/scripts/test-handoff-preamble.ts +277 -0
  112. package/src/scripts/test-parallel-handoffs.ts +291 -0
  113. package/src/scripts/test-tools-before-handoff.ts +8 -4
  114. package/src/scripts/test_code_api.ts +361 -0
  115. package/src/scripts/thinking-bedrock.ts +159 -0
  116. package/src/scripts/thinking.ts +39 -18
  117. package/src/scripts/{tool_search_regex.ts → tool_search.ts} +5 -5
  118. package/src/scripts/tools.ts +7 -3
  119. package/src/specs/cache.simple.test.ts +396 -0
  120. package/src/stream.ts +4 -2
  121. package/src/tools/BrowserTools.ts +39 -17
  122. package/src/tools/CodeExecutor.ts +26 -23
  123. package/src/tools/ProgrammaticToolCalling.ts +18 -14
  124. package/src/tools/ToolNode.ts +114 -1
  125. package/src/tools/ToolSearch.ts +1041 -0
  126. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +0 -2
  127. package/src/tools/__tests__/{ToolSearchRegex.integration.test.ts → ToolSearch.integration.test.ts} +6 -6
  128. package/src/tools/__tests__/ToolSearch.test.ts +1003 -0
  129. package/src/types/graph.test.ts +183 -0
  130. package/src/types/graph.ts +73 -0
  131. package/src/types/llm.ts +3 -1
  132. package/src/types/tools.ts +51 -2
  133. package/dist/cjs/tools/ToolSearchRegex.cjs +0 -455
  134. package/dist/cjs/tools/ToolSearchRegex.cjs.map +0 -1
  135. package/dist/esm/tools/ToolSearchRegex.mjs +0 -448
  136. package/dist/esm/tools/ToolSearchRegex.mjs.map +0 -1
  137. package/dist/types/tools/ToolSearchRegex.d.ts +0 -80
  138. package/src/tools/ToolSearchRegex.ts +0 -535
  139. package/src/tools/__tests__/ToolSearchRegex.test.ts +0 -232
@@ -6,7 +6,11 @@ var outputs = require('@langchain/core/outputs');
6
6
 
7
7
  /**
8
8
  * Optimized ChatBedrockConverse wrapper that fixes contentBlockIndex conflicts
9
- * and adds prompt caching support for Bedrock Converse API.
9
+ * and adds support for:
10
+ *
11
+ * - Prompt caching support for Bedrock Converse API (Illuma feature)
12
+ * - Application Inference Profiles (PR #9129)
13
+ * - Service Tiers (Priority/Standard/Flex) (PR #9785) - requires AWS SDK 3.966.0+
10
14
  *
11
15
  * Bedrock sends the same contentBlockIndex for both text and tool_use content blocks,
12
16
  * causing LangChain's merge logic to fail with "field[contentBlockIndex] already exists"
@@ -27,19 +31,34 @@ var outputs = require('@langchain/core/outputs');
27
31
  * response. This wrapper adds input_token_details to usage_metadata with cache information.
28
32
  */
29
33
  class CustomChatBedrockConverse extends aws.ChatBedrockConverse {
34
+ /** Enable Bedrock prompt caching for tool definitions */
30
35
  promptCache;
36
+ /** Application Inference Profile ARN to use instead of model ID */
37
+ applicationInferenceProfile;
38
+ /** Service tier for model invocation */
39
+ serviceTier;
31
40
  constructor(fields) {
32
41
  super(fields);
33
42
  this.promptCache = fields?.promptCache ?? false;
43
+ this.applicationInferenceProfile = fields?.applicationInferenceProfile;
44
+ this.serviceTier = fields?.serviceTier;
34
45
  }
35
46
  static lc_name() {
36
47
  return 'IllumaBedrockConverse';
37
48
  }
38
49
  /**
39
- * Override invocationParams to add cachePoint to tools when promptCache is enabled.
40
- * This enables Bedrock prompt caching for tool definitions.
50
+ * Get the model ID to use for API calls.
51
+ * Returns applicationInferenceProfile if set, otherwise returns this.model.
52
+ */
53
+ getModelId() {
54
+ return this.applicationInferenceProfile ?? this.model;
55
+ }
56
+ /**
57
+ * Override invocationParams to:
58
+ * 1. Add cachePoint to tools when promptCache is enabled
59
+ * 2. Add serviceTier support
41
60
  *
42
- * STRATEGY: Separate cachePoints for core tools and MCP tools
61
+ * CACHING STRATEGY: Separate cachePoints for core tools and MCP tools
43
62
  * - Core tools (web_search, execute_code, etc.) are stable → cache first
44
63
  * - MCP tools (have '_mcp_' in name) are dynamic → cache separately after
45
64
  * - This allows core tools to stay cached when MCP selection changes
@@ -90,69 +109,117 @@ class CustomChatBedrockConverse extends aws.ChatBedrockConverse {
90
109
  }
91
110
  params.toolConfig.tools = toolsWithCache;
92
111
  }
93
- return params;
112
+ // Add serviceTier support
113
+ const serviceTierType = options?.serviceTier ?? this.serviceTier;
114
+ return {
115
+ ...params,
116
+ serviceTier: serviceTierType ? { type: serviceTierType } : undefined,
117
+ };
118
+ }
119
+ /**
120
+ * Override _generateNonStreaming to use applicationInferenceProfile as modelId.
121
+ * Uses the same model-swapping pattern as streaming for consistency.
122
+ */
123
+ async _generateNonStreaming(messages, options, runManager) {
124
+ // Temporarily swap model for applicationInferenceProfile support
125
+ const originalModel = this.model;
126
+ if (this.applicationInferenceProfile != null &&
127
+ this.applicationInferenceProfile !== '') {
128
+ this.model = this.applicationInferenceProfile;
129
+ }
130
+ try {
131
+ return await super._generateNonStreaming(messages, options, runManager);
132
+ }
133
+ finally {
134
+ // Restore original model
135
+ this.model = originalModel;
136
+ }
94
137
  }
95
138
  /**
96
139
  * Override _streamResponseChunks to:
97
- * 1. Strip contentBlockIndex from response_metadata to prevent merge conflicts
98
- * 2. Extract cacheReadInputTokens/cacheWriteInputTokens and add to usage_metadata
140
+ * 1. Use applicationInferenceProfile as modelId (by temporarily swapping this.model)
141
+ * 2. Strip contentBlockIndex from response_metadata to prevent merge conflicts
142
+ * 3. Extract cacheReadInputTokens/cacheWriteInputTokens and add to usage_metadata
143
+ *
144
+ * Note: We delegate to super._streamResponseChunks() to preserve @langchain/aws's
145
+ * internal chunk handling which correctly preserves array content for reasoning blocks.
99
146
  */
100
- async *_streamResponseChunks(messages$1, options, runManager) {
101
- const baseStream = super._streamResponseChunks(messages$1, options, runManager);
102
- for await (const chunk of baseStream) {
103
- // Only process if we have response_metadata
104
- if (chunk.message instanceof messages.AIMessageChunk &&
105
- chunk.message.response_metadata &&
106
- typeof chunk.message.response_metadata === 'object') {
107
- const responseMetadata = chunk.message.response_metadata;
108
- let needsModification = false;
109
- let cleanedMetadata = responseMetadata;
110
- // Check if contentBlockIndex exists anywhere in response_metadata
111
- const hasContentBlockIndex = this.hasContentBlockIndex(responseMetadata);
112
- if (hasContentBlockIndex) {
113
- cleanedMetadata = this.removeContentBlockIndex(responseMetadata);
114
- needsModification = true;
115
- }
116
- // Extract cache tokens from metadata.usage (Bedrock streaming format)
117
- // The metadata chunk contains usage with cacheReadInputTokens/cacheWriteInputTokens
118
- const metadata = responseMetadata.metadata;
119
- const usage = (metadata?.usage ?? responseMetadata.usage);
120
- let enhancedUsageMetadata = chunk.message.usage_metadata;
121
- if (usage) {
122
- const cacheRead = usage.cacheReadInputTokens ?? 0;
123
- const cacheWrite = usage.cacheWriteInputTokens ?? 0;
124
- const inputTokens = usage.inputTokens ?? 0;
125
- const outputTokens = usage.outputTokens ?? 0;
126
- if (cacheRead > 0 || cacheWrite > 0) {
127
- needsModification = true;
128
- enhancedUsageMetadata = {
129
- input_tokens: chunk.message.usage_metadata?.input_tokens ?? inputTokens,
130
- output_tokens: chunk.message.usage_metadata?.output_tokens ?? outputTokens,
131
- total_tokens: chunk.message.usage_metadata?.total_tokens ??
132
- usage.totalTokens ??
133
- 0,
134
- input_token_details: {
135
- cache_read: cacheRead,
136
- cache_creation: cacheWrite,
137
- },
138
- };
139
- }
140
- }
141
- if (needsModification) {
142
- yield new outputs.ChatGenerationChunk({
143
- text: chunk.text,
144
- message: new messages.AIMessageChunk({
145
- ...chunk.message,
146
- response_metadata: cleanedMetadata,
147
- usage_metadata: enhancedUsageMetadata,
148
- }),
149
- generationInfo: chunk.generationInfo,
150
- });
151
- continue;
152
- }
147
+ async *_streamResponseChunks(messages, options, runManager) {
148
+ // Temporarily swap model for applicationInferenceProfile support
149
+ const originalModel = this.model;
150
+ if (this.applicationInferenceProfile != null &&
151
+ this.applicationInferenceProfile !== '') {
152
+ this.model = this.applicationInferenceProfile;
153
+ }
154
+ try {
155
+ // Use parent's streaming logic which correctly handles reasoning content
156
+ const baseStream = super._streamResponseChunks(messages, options, runManager);
157
+ for await (const chunk of baseStream) {
158
+ // Clean and enhance chunk
159
+ yield this.processChunk(chunk);
160
+ }
161
+ }
162
+ finally {
163
+ // Restore original model
164
+ this.model = originalModel;
165
+ }
166
+ }
167
+ /**
168
+ * Process a chunk by:
169
+ * 1. Removing contentBlockIndex from response_metadata
170
+ * 2. Extracting cache token information from Bedrock's usage data
171
+ */
172
+ processChunk(chunk) {
173
+ const message = chunk.message;
174
+ if (!(message instanceof messages.AIMessageChunk)) {
175
+ return chunk;
176
+ }
177
+ const responseMetadata = message.response_metadata;
178
+ let needsModification = false;
179
+ let cleanedMetadata = responseMetadata;
180
+ // Check if contentBlockIndex exists anywhere in response_metadata
181
+ const hasContentBlockIndex = this.hasContentBlockIndex(responseMetadata);
182
+ if (hasContentBlockIndex) {
183
+ cleanedMetadata = this.removeContentBlockIndex(responseMetadata);
184
+ needsModification = true;
185
+ }
186
+ // Extract cache tokens from metadata.usage (Bedrock streaming format)
187
+ // The metadata chunk contains usage with cacheReadInputTokens/cacheWriteInputTokens
188
+ const metadata = responseMetadata.metadata;
189
+ const usage = (metadata?.usage ?? responseMetadata.usage);
190
+ let enhancedUsageMetadata = message.usage_metadata;
191
+ if (usage) {
192
+ const cacheRead = usage.cacheReadInputTokens ?? 0;
193
+ const cacheWrite = usage.cacheWriteInputTokens ?? 0;
194
+ const inputTokens = usage.inputTokens ?? 0;
195
+ const outputTokens = usage.outputTokens ?? 0;
196
+ if (cacheRead > 0 || cacheWrite > 0) {
197
+ needsModification = true;
198
+ enhancedUsageMetadata = {
199
+ input_tokens: message.usage_metadata?.input_tokens ?? inputTokens,
200
+ output_tokens: message.usage_metadata?.output_tokens ?? outputTokens,
201
+ total_tokens: message.usage_metadata?.total_tokens ??
202
+ usage.totalTokens ??
203
+ 0,
204
+ input_token_details: {
205
+ cache_read: cacheRead,
206
+ cache_creation: cacheWrite,
207
+ },
208
+ };
153
209
  }
154
- yield chunk;
155
210
  }
211
+ if (needsModification) {
212
+ return new outputs.ChatGenerationChunk({
213
+ text: chunk.text,
214
+ message: new messages.AIMessageChunk({
215
+ ...message,
216
+ response_metadata: cleanedMetadata,
217
+ usage_metadata: enhancedUsageMetadata,
218
+ }),
219
+ generationInfo: chunk.generationInfo,
220
+ });
221
+ }
222
+ return chunk;
156
223
  }
157
224
  /**
158
225
  * Check if contentBlockIndex exists at any level in the object
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../../../src/llm/bedrock/index.ts"],"sourcesContent":["/**\n * Optimized ChatBedrockConverse wrapper that fixes contentBlockIndex conflicts\n * and adds prompt caching support for Bedrock Converse API.\n *\n * Bedrock sends the same contentBlockIndex for both text and tool_use content blocks,\n * causing LangChain's merge logic to fail with \"field[contentBlockIndex] already exists\"\n * errors. This wrapper simply strips contentBlockIndex from response_metadata to avoid\n * the conflict.\n *\n * The contentBlockIndex field is only used internally by Bedrock's streaming protocol\n * and isn't needed by application logic - the index field on tool_call_chunks serves\n * the purpose of tracking tool call ordering.\n *\n * PROMPT CACHING:\n * When promptCache: true is set, this wrapper adds cachePoint markers to the tools array\n * to enable Bedrock prompt caching for tool definitions. This allows tool schemas to be\n * cached and reused across requests, reducing latency and costs.\n *\n * CACHE TOKEN EXTRACTION:\n * LangChain AWS doesn't extract cacheReadInputTokens/cacheWriteInputTokens from Bedrock's\n * response. This wrapper adds input_token_details to usage_metadata with cache information.\n */\n\nimport { ChatBedrockConverse } from '@langchain/aws';\nimport type { ChatBedrockConverseInput } from '@langchain/aws';\nimport { AIMessageChunk } from '@langchain/core/messages';\nimport type { BaseMessage, UsageMetadata } from '@langchain/core/messages';\nimport { ChatGenerationChunk } from '@langchain/core/outputs';\nimport type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';\n\n/** Extended input type with promptCache option */\nexport interface CustomChatBedrockConverseInput\n extends ChatBedrockConverseInput {\n promptCache?: boolean;\n}\n\nexport class CustomChatBedrockConverse extends ChatBedrockConverse {\n promptCache: boolean;\n\n constructor(fields?: CustomChatBedrockConverseInput) {\n super(fields);\n this.promptCache = fields?.promptCache ?? false;\n }\n\n static lc_name(): string {\n return 'IllumaBedrockConverse';\n }\n\n /**\n * Override invocationParams to add cachePoint to tools when promptCache is enabled.\n * This enables Bedrock prompt caching for tool definitions.\n *\n * STRATEGY: Separate cachePoints for core tools and MCP tools\n * - Core tools (web_search, execute_code, etc.) are stable → cache first\n * - MCP tools (have '_mcp_' in name) are dynamic → cache separately after\n * - This allows core tools to stay cached when MCP selection changes\n *\n * NOTE: Only Claude models support cachePoint - Nova and other models will reject it.\n */\n invocationParams(\n options?: this['ParsedCallOptions']\n ): ReturnType<ChatBedrockConverse['invocationParams']> {\n const params = super.invocationParams(options);\n\n // Add cachePoint to tools array if promptCache is enabled and tools exist\n // Only Claude models support cachePoint - check model name\n const modelId = this.model.toLowerCase();\n const isClaudeModel =\n modelId.includes('claude') || modelId.includes('anthropic');\n\n if (\n this.promptCache &&\n isClaudeModel &&\n params.toolConfig?.tools &&\n Array.isArray(params.toolConfig.tools) &&\n params.toolConfig.tools.length > 0\n ) {\n // Separate core tools from MCP tools\n // MCP tools have '_mcp_' in their name (e.g., 'search_emails_mcp_Google-Workspace')\n const coreTools: typeof params.toolConfig.tools = [];\n const mcpTools: typeof params.toolConfig.tools = [];\n const coreToolNames: string[] = [];\n const mcpToolNames: string[] = [];\n\n for (const tool of params.toolConfig.tools) {\n // Check if tool has a name property with '_mcp_' pattern\n const toolName =\n (tool as { toolSpec?: { name?: string } }).toolSpec?.name ?? '';\n if (toolName.includes('_mcp_')) {\n mcpTools.push(tool);\n mcpToolNames.push(toolName);\n } else {\n coreTools.push(tool);\n coreToolNames.push(toolName);\n }\n }\n\n // Build tools array with strategic cachePoints:\n // [CoreTool1, CoreTool2, cachePoint] + [MCPTool1, MCPTool2, cachePoint]\n const toolsWithCache: typeof params.toolConfig.tools = [];\n\n // Add core tools with cachePoint (if any)\n if (coreTools.length > 0) {\n toolsWithCache.push(...coreTools);\n toolsWithCache.push({ cachePoint: { type: 'default' } });\n }\n\n // Add MCP tools with their own cachePoint (if any)\n if (mcpTools.length > 0) {\n toolsWithCache.push(...mcpTools);\n toolsWithCache.push({ cachePoint: { type: 'default' } });\n }\n\n // If no tools at all (shouldn't happen but safety check)\n if (toolsWithCache.length === 0) {\n toolsWithCache.push({ cachePoint: { type: 'default' } });\n }\n\n params.toolConfig.tools = toolsWithCache;\n }\n\n return params;\n }\n\n /**\n * Override _streamResponseChunks to:\n * 1. Strip contentBlockIndex from response_metadata to prevent merge conflicts\n * 2. Extract cacheReadInputTokens/cacheWriteInputTokens and add to usage_metadata\n */\n async *_streamResponseChunks(\n messages: BaseMessage[],\n options: this['ParsedCallOptions'],\n runManager?: CallbackManagerForLLMRun\n ): AsyncGenerator<ChatGenerationChunk> {\n const baseStream = super._streamResponseChunks(\n messages,\n options,\n runManager\n );\n\n for await (const chunk of baseStream) {\n // Only process if we have response_metadata\n if (\n chunk.message instanceof AIMessageChunk &&\n (chunk.message as Partial<AIMessageChunk>).response_metadata &&\n typeof chunk.message.response_metadata === 'object'\n ) {\n const responseMetadata = chunk.message.response_metadata as Record<\n string,\n unknown\n >;\n let needsModification = false;\n let cleanedMetadata = responseMetadata;\n\n // Check if contentBlockIndex exists anywhere in response_metadata\n const hasContentBlockIndex =\n this.hasContentBlockIndex(responseMetadata);\n if (hasContentBlockIndex) {\n cleanedMetadata = this.removeContentBlockIndex(\n responseMetadata\n ) as Record<string, unknown>;\n needsModification = true;\n }\n\n // Extract cache tokens from metadata.usage (Bedrock streaming format)\n // The metadata chunk contains usage with cacheReadInputTokens/cacheWriteInputTokens\n const metadata = responseMetadata.metadata as\n | Record<string, unknown>\n | undefined;\n const usage = (metadata?.usage ?? responseMetadata.usage) as\n | Record<string, unknown>\n | undefined;\n\n let enhancedUsageMetadata: UsageMetadata | undefined =\n chunk.message.usage_metadata;\n\n if (usage) {\n const cacheRead =\n (usage.cacheReadInputTokens as number | undefined) ?? 0;\n const cacheWrite =\n (usage.cacheWriteInputTokens as number | undefined) ?? 0;\n const inputTokens = (usage.inputTokens as number | undefined) ?? 0;\n const outputTokens = (usage.outputTokens as number | undefined) ?? 0;\n\n if (cacheRead > 0 || cacheWrite > 0) {\n needsModification = true;\n enhancedUsageMetadata = {\n input_tokens:\n chunk.message.usage_metadata?.input_tokens ?? inputTokens,\n output_tokens:\n chunk.message.usage_metadata?.output_tokens ?? outputTokens,\n total_tokens:\n chunk.message.usage_metadata?.total_tokens ??\n (usage.totalTokens as number | undefined) ??\n 0,\n input_token_details: {\n cache_read: cacheRead,\n cache_creation: cacheWrite,\n },\n };\n }\n }\n\n if (needsModification) {\n yield new ChatGenerationChunk({\n text: chunk.text,\n message: new AIMessageChunk({\n ...chunk.message,\n response_metadata: cleanedMetadata,\n usage_metadata: enhancedUsageMetadata,\n }),\n generationInfo: chunk.generationInfo,\n });\n continue;\n }\n }\n\n yield chunk;\n }\n }\n\n /**\n * Check if contentBlockIndex exists at any level in the object\n */\n private hasContentBlockIndex(obj: unknown): boolean {\n if (obj === null || obj === undefined || typeof obj !== 'object') {\n return false;\n }\n\n if ('contentBlockIndex' in obj) {\n return true;\n }\n\n for (const value of Object.values(obj)) {\n if (typeof value === 'object' && value !== null) {\n if (this.hasContentBlockIndex(value)) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n /**\n * Recursively remove contentBlockIndex from all levels of an object\n */\n private removeContentBlockIndex(obj: unknown): unknown {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => this.removeContentBlockIndex(item));\n }\n\n if (typeof obj === 'object') {\n const cleaned: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (key !== 'contentBlockIndex') {\n cleaned[key] = this.removeContentBlockIndex(value);\n }\n }\n return cleaned;\n }\n\n return obj;\n }\n}\n\nexport type { ChatBedrockConverseInput };\n"],"names":["ChatBedrockConverse","messages","AIMessageChunk","ChatGenerationChunk"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/llm/bedrock/index.ts"],"sourcesContent":["/**\n * Optimized ChatBedrockConverse wrapper that fixes contentBlockIndex conflicts\n * and adds support for:\n *\n * - Prompt caching support for Bedrock Converse API (Illuma feature)\n * - Application Inference Profiles (PR #9129)\n * - Service Tiers (Priority/Standard/Flex) (PR #9785) - requires AWS SDK 3.966.0+\n *\n * Bedrock sends the same contentBlockIndex for both text and tool_use content blocks,\n * causing LangChain's merge logic to fail with \"field[contentBlockIndex] already exists\"\n * errors. This wrapper simply strips contentBlockIndex from response_metadata to avoid\n * the conflict.\n *\n * The contentBlockIndex field is only used internally by Bedrock's streaming protocol\n * and isn't needed by application logic - the index field on tool_call_chunks serves\n * the purpose of tracking tool call ordering.\n *\n * PROMPT CACHING:\n * When promptCache: true is set, this wrapper adds cachePoint markers to the tools array\n * to enable Bedrock prompt caching for tool definitions. This allows tool schemas to be\n * cached and reused across requests, reducing latency and costs.\n *\n * CACHE TOKEN EXTRACTION:\n * LangChain AWS doesn't extract cacheReadInputTokens/cacheWriteInputTokens from Bedrock's\n * response. This wrapper adds input_token_details to usage_metadata with cache information.\n */\n\nimport { ChatBedrockConverse } from '@langchain/aws';\nimport { AIMessageChunk } from '@langchain/core/messages';\nimport type { BaseMessage, UsageMetadata } from '@langchain/core/messages';\nimport { ChatGenerationChunk, ChatResult } from '@langchain/core/outputs';\nimport type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';\nimport type { ChatBedrockConverseInput } from '@langchain/aws';\n\n/**\n * Service tier type for Bedrock invocations.\n * Requires AWS SDK >= 3.966.0 to actually work.\n * @see https://docs.aws.amazon.com/bedrock/latest/userguide/service-tiers-inference.html\n */\nexport type ServiceTierType = 'priority' | 'default' | 'flex' | 'reserved';\n\n/**\n * Extended input interface with additional features:\n * - promptCache: Enable Bedrock prompt caching for tool definitions\n * - applicationInferenceProfile: Use an inference profile ARN instead of model ID\n * - serviceTier: Specify service tier (Priority, Standard, Flex, Reserved)\n */\nexport interface CustomChatBedrockConverseInput\n extends ChatBedrockConverseInput {\n /**\n * Enable Bedrock prompt caching for tool definitions.\n * When true, adds cachePoint markers to tools array.\n */\n promptCache?: boolean;\n\n /**\n * Application Inference Profile ARN to use for the model.\n * For example, \"arn:aws:bedrock:eu-west-1:123456789102:application-inference-profile/fm16bt65tzgx\"\n * When provided, this ARN will be used for the actual inference calls instead of the model ID.\n * Must still provide `model` as normal modelId to benefit from all the metadata.\n * @see https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-create.html\n */\n applicationInferenceProfile?: string;\n\n /**\n * Service tier for model invocation.\n * Specifies the processing tier type used for serving the request.\n * Supported values are 'priority', 'default', 'flex', and 'reserved'.\n *\n * - 'priority': Prioritized processing for lower latency\n * - 'default': Standard processing tier\n * - 'flex': Flexible processing tier with lower cost\n * - 'reserved': Reserved capacity for consistent performance\n *\n * If not provided, AWS uses the default tier.\n * Note: Requires AWS SDK >= 3.966.0 to work.\n * @see https://docs.aws.amazon.com/bedrock/latest/userguide/service-tiers-inference.html\n */\n serviceTier?: ServiceTierType;\n}\n\n/**\n * Extended call options with serviceTier override support.\n */\nexport interface CustomChatBedrockConverseCallOptions {\n serviceTier?: ServiceTierType;\n}\n\nexport class CustomChatBedrockConverse extends ChatBedrockConverse {\n /** Enable Bedrock prompt caching for tool definitions */\n promptCache: boolean;\n\n /** Application Inference Profile ARN to use instead of model ID */\n applicationInferenceProfile?: string;\n\n /** Service tier for model invocation */\n serviceTier?: ServiceTierType;\n\n constructor(fields?: CustomChatBedrockConverseInput) {\n super(fields);\n this.promptCache = fields?.promptCache ?? false;\n this.applicationInferenceProfile = fields?.applicationInferenceProfile;\n this.serviceTier = fields?.serviceTier;\n }\n\n static lc_name(): string {\n return 'IllumaBedrockConverse';\n }\n\n /**\n * Get the model ID to use for API calls.\n * Returns applicationInferenceProfile if set, otherwise returns this.model.\n */\n protected getModelId(): string {\n return this.applicationInferenceProfile ?? this.model;\n }\n\n /**\n * Override invocationParams to:\n * 1. Add cachePoint to tools when promptCache is enabled\n * 2. Add serviceTier support\n *\n * CACHING STRATEGY: Separate cachePoints for core tools and MCP tools\n * - Core tools (web_search, execute_code, etc.) are stable → cache first\n * - MCP tools (have '_mcp_' in name) are dynamic → cache separately after\n * - This allows core tools to stay cached when MCP selection changes\n *\n * NOTE: Only Claude models support cachePoint - Nova and other models will reject it.\n */\n override invocationParams(\n options?: this['ParsedCallOptions'] & CustomChatBedrockConverseCallOptions\n ): ReturnType<ChatBedrockConverse['invocationParams']> & {\n serviceTier?: { type: ServiceTierType };\n } {\n const params = super.invocationParams(options);\n\n // Add cachePoint to tools array if promptCache is enabled and tools exist\n // Only Claude models support cachePoint - check model name\n const modelId = this.model.toLowerCase();\n const isClaudeModel =\n modelId.includes('claude') || modelId.includes('anthropic');\n\n if (\n this.promptCache &&\n isClaudeModel &&\n params.toolConfig?.tools &&\n Array.isArray(params.toolConfig.tools) &&\n params.toolConfig.tools.length > 0\n ) {\n // Separate core tools from MCP tools\n // MCP tools have '_mcp_' in their name (e.g., 'search_emails_mcp_Google-Workspace')\n const coreTools: typeof params.toolConfig.tools = [];\n const mcpTools: typeof params.toolConfig.tools = [];\n\n for (const tool of params.toolConfig.tools) {\n // Check if tool has a name property with '_mcp_' pattern\n const toolName =\n (tool as { toolSpec?: { name?: string } }).toolSpec?.name ?? '';\n if (toolName.includes('_mcp_')) {\n mcpTools.push(tool);\n } else {\n coreTools.push(tool);\n }\n }\n\n // Build tools array with strategic cachePoints:\n // [CoreTool1, CoreTool2, cachePoint] + [MCPTool1, MCPTool2, cachePoint]\n const toolsWithCache: typeof params.toolConfig.tools = [];\n\n // Add core tools with cachePoint (if any)\n if (coreTools.length > 0) {\n toolsWithCache.push(...coreTools);\n toolsWithCache.push({ cachePoint: { type: 'default' } });\n }\n\n // Add MCP tools with their own cachePoint (if any)\n if (mcpTools.length > 0) {\n toolsWithCache.push(...mcpTools);\n toolsWithCache.push({ cachePoint: { type: 'default' } });\n }\n\n // If no tools at all (shouldn't happen but safety check)\n if (toolsWithCache.length === 0) {\n toolsWithCache.push({ cachePoint: { type: 'default' } });\n }\n\n params.toolConfig.tools = toolsWithCache;\n }\n\n // Add serviceTier support\n const serviceTierType = options?.serviceTier ?? this.serviceTier;\n\n return {\n ...params,\n serviceTier: serviceTierType ? { type: serviceTierType } : undefined,\n };\n }\n\n /**\n * Override _generateNonStreaming to use applicationInferenceProfile as modelId.\n * Uses the same model-swapping pattern as streaming for consistency.\n */\n override async _generateNonStreaming(\n messages: BaseMessage[],\n options: this['ParsedCallOptions'] & CustomChatBedrockConverseCallOptions,\n runManager?: CallbackManagerForLLMRun\n ): Promise<ChatResult> {\n // Temporarily swap model for applicationInferenceProfile support\n const originalModel = this.model;\n if (\n this.applicationInferenceProfile != null &&\n this.applicationInferenceProfile !== ''\n ) {\n this.model = this.applicationInferenceProfile;\n }\n\n try {\n return await super._generateNonStreaming(messages, options, runManager);\n } finally {\n // Restore original model\n this.model = originalModel;\n }\n }\n\n /**\n * Override _streamResponseChunks to:\n * 1. Use applicationInferenceProfile as modelId (by temporarily swapping this.model)\n * 2. Strip contentBlockIndex from response_metadata to prevent merge conflicts\n * 3. Extract cacheReadInputTokens/cacheWriteInputTokens and add to usage_metadata\n *\n * Note: We delegate to super._streamResponseChunks() to preserve @langchain/aws's\n * internal chunk handling which correctly preserves array content for reasoning blocks.\n */\n override async *_streamResponseChunks(\n messages: BaseMessage[],\n options: this['ParsedCallOptions'] & CustomChatBedrockConverseCallOptions,\n runManager?: CallbackManagerForLLMRun\n ): AsyncGenerator<ChatGenerationChunk> {\n // Temporarily swap model for applicationInferenceProfile support\n const originalModel = this.model;\n if (\n this.applicationInferenceProfile != null &&\n this.applicationInferenceProfile !== ''\n ) {\n this.model = this.applicationInferenceProfile;\n }\n\n try {\n // Use parent's streaming logic which correctly handles reasoning content\n const baseStream = super._streamResponseChunks(\n messages,\n options,\n runManager\n );\n\n for await (const chunk of baseStream) {\n // Clean and enhance chunk\n yield this.processChunk(chunk);\n }\n } finally {\n // Restore original model\n this.model = originalModel;\n }\n }\n\n /**\n * Process a chunk by:\n * 1. Removing contentBlockIndex from response_metadata\n * 2. Extracting cache token information from Bedrock's usage data\n */\n private processChunk(chunk: ChatGenerationChunk): ChatGenerationChunk {\n const message = chunk.message;\n if (!(message instanceof AIMessageChunk)) {\n return chunk;\n }\n\n const responseMetadata = message.response_metadata as Record<\n string,\n unknown\n >;\n let needsModification = false;\n let cleanedMetadata = responseMetadata;\n\n // Check if contentBlockIndex exists anywhere in response_metadata\n const hasContentBlockIndex = this.hasContentBlockIndex(responseMetadata);\n if (hasContentBlockIndex) {\n cleanedMetadata = this.removeContentBlockIndex(\n responseMetadata\n ) as Record<string, unknown>;\n needsModification = true;\n }\n\n // Extract cache tokens from metadata.usage (Bedrock streaming format)\n // The metadata chunk contains usage with cacheReadInputTokens/cacheWriteInputTokens\n const metadata = responseMetadata.metadata as\n | Record<string, unknown>\n | undefined;\n const usage = (metadata?.usage ?? responseMetadata.usage) as\n | Record<string, unknown>\n | undefined;\n\n let enhancedUsageMetadata: UsageMetadata | undefined =\n message.usage_metadata;\n\n if (usage) {\n const cacheRead = (usage.cacheReadInputTokens as number | undefined) ?? 0;\n const cacheWrite =\n (usage.cacheWriteInputTokens as number | undefined) ?? 0;\n const inputTokens = (usage.inputTokens as number | undefined) ?? 0;\n const outputTokens = (usage.outputTokens as number | undefined) ?? 0;\n\n if (cacheRead > 0 || cacheWrite > 0) {\n needsModification = true;\n enhancedUsageMetadata = {\n input_tokens: message.usage_metadata?.input_tokens ?? inputTokens,\n output_tokens: message.usage_metadata?.output_tokens ?? outputTokens,\n total_tokens:\n message.usage_metadata?.total_tokens ??\n (usage.totalTokens as number | undefined) ??\n 0,\n input_token_details: {\n cache_read: cacheRead,\n cache_creation: cacheWrite,\n },\n };\n }\n }\n\n if (needsModification) {\n return new ChatGenerationChunk({\n text: chunk.text,\n message: new AIMessageChunk({\n ...message,\n response_metadata: cleanedMetadata,\n usage_metadata: enhancedUsageMetadata,\n }),\n generationInfo: chunk.generationInfo,\n });\n }\n\n return chunk;\n }\n\n /**\n * Check if contentBlockIndex exists at any level in the object\n */\n private hasContentBlockIndex(obj: unknown): boolean {\n if (obj === null || obj === undefined || typeof obj !== 'object') {\n return false;\n }\n\n if ('contentBlockIndex' in obj) {\n return true;\n }\n\n for (const value of Object.values(obj)) {\n if (typeof value === 'object' && value !== null) {\n if (this.hasContentBlockIndex(value)) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n /**\n * Recursively remove contentBlockIndex from all levels of an object\n */\n private removeContentBlockIndex(obj: unknown): unknown {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => this.removeContentBlockIndex(item));\n }\n\n if (typeof obj === 'object') {\n const cleaned: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (key !== 'contentBlockIndex') {\n cleaned[key] = this.removeContentBlockIndex(value);\n }\n }\n return cleaned;\n }\n\n return obj;\n }\n}\n\nexport type { ChatBedrockConverseInput };\n"],"names":["ChatBedrockConverse","AIMessageChunk","ChatGenerationChunk"],"mappings":"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"}
package/dist/cjs/main.cjs CHANGED
@@ -17,9 +17,10 @@ var Calculator = require('./tools/Calculator.cjs');
17
17
  var CodeExecutor = require('./tools/CodeExecutor.cjs');
18
18
  var BrowserTools = require('./tools/BrowserTools.cjs');
19
19
  var ProgrammaticToolCalling = require('./tools/ProgrammaticToolCalling.cjs');
20
- var ToolSearchRegex = require('./tools/ToolSearchRegex.cjs');
20
+ var ToolSearch = require('./tools/ToolSearch.cjs');
21
21
  var handlers = require('./tools/handlers.cjs');
22
22
  var tool = require('./tools/search/tool.cjs');
23
+ var validate = require('./schemas/validate.cjs');
23
24
  var _enum = require('./common/enum.cjs');
24
25
  var graph = require('./utils/graph.cjs');
25
26
  var llm = require('./utils/llm.cjs');
@@ -93,17 +94,31 @@ exports.formatCompletedResponse = ProgrammaticToolCalling.formatCompletedRespons
93
94
  exports.makeRequest = ProgrammaticToolCalling.makeRequest;
94
95
  exports.normalizeToPythonIdentifier = ProgrammaticToolCalling.normalizeToPythonIdentifier;
95
96
  exports.unwrapToolResponse = ProgrammaticToolCalling.unwrapToolResponse;
96
- exports.countNestedGroups = ToolSearchRegex.countNestedGroups;
97
- exports.createToolSearchRegexTool = ToolSearchRegex.createToolSearchRegexTool;
98
- exports.escapeRegexSpecialChars = ToolSearchRegex.escapeRegexSpecialChars;
99
- exports.hasNestedQuantifiers = ToolSearchRegex.hasNestedQuantifiers;
100
- exports.isDangerousPattern = ToolSearchRegex.isDangerousPattern;
101
- exports.sanitizeRegex = ToolSearchRegex.sanitizeRegex;
97
+ exports.countNestedGroups = ToolSearch.countNestedGroups;
98
+ exports.createToolSearch = ToolSearch.createToolSearch;
99
+ exports.escapeRegexSpecialChars = ToolSearch.escapeRegexSpecialChars;
100
+ exports.extractMcpServerName = ToolSearch.extractMcpServerName;
101
+ exports.formatServerListing = ToolSearch.formatServerListing;
102
+ exports.getAvailableMcpServers = ToolSearch.getAvailableMcpServers;
103
+ exports.getBaseToolName = ToolSearch.getBaseToolName;
104
+ exports.getDeferredToolsListing = ToolSearch.getDeferredToolsListing;
105
+ exports.hasNestedQuantifiers = ToolSearch.hasNestedQuantifiers;
106
+ exports.isDangerousPattern = ToolSearch.isDangerousPattern;
107
+ exports.isFromAnyMcpServer = ToolSearch.isFromAnyMcpServer;
108
+ exports.isFromMcpServer = ToolSearch.isFromMcpServer;
109
+ exports.normalizeServerFilter = ToolSearch.normalizeServerFilter;
110
+ exports.performLocalSearch = ToolSearch.performLocalSearch;
111
+ exports.sanitizeRegex = ToolSearch.sanitizeRegex;
102
112
  exports.handleServerToolResult = handlers.handleServerToolResult;
103
113
  exports.handleToolCallChunks = handlers.handleToolCallChunks;
104
114
  exports.handleToolCalls = handlers.handleToolCalls;
105
115
  exports.toolResultTypes = handlers.toolResultTypes;
106
116
  exports.createSearchTool = tool.createSearchTool;
117
+ exports.createValidationErrorMessage = validate.createValidationErrorMessage;
118
+ exports.isValidJsonSchema = validate.isValidJsonSchema;
119
+ exports.normalizeJsonSchema = validate.normalizeJsonSchema;
120
+ exports.validateStructuredOutput = validate.validateStructuredOutput;
121
+ exports.zodToJsonSchema = validate.zodToJsonSchema;
107
122
  Object.defineProperty(exports, "Callback", {
108
123
  enumerable: true,
109
124
  get: function () { return _enum.Callback; }
@@ -1 +1 @@
1
- {"version":3,"file":"main.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"main.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -9,13 +9,97 @@ const debugCache = (message, data) => {
9
9
  console.log(`[Cache] ${message}`, data !== undefined ? JSON.stringify(data, null, 2) : '');
10
10
  }
11
11
  };
12
+ /**
13
+ * Deep clones a message's content to prevent mutation of the original.
14
+ */
15
+ function deepCloneContent(content) {
16
+ if (typeof content === 'string') {
17
+ return content;
18
+ }
19
+ if (Array.isArray(content)) {
20
+ return content.map((block) => ({ ...block }));
21
+ }
22
+ return content;
23
+ }
24
+ /**
25
+ * Clones a message with deep-cloned content, explicitly excluding LangChain
26
+ * serialization metadata to prevent coercion issues.
27
+ * Keeps lc_kwargs in sync with content to prevent LangChain serialization issues.
28
+ */
29
+ function cloneMessage(message, content) {
30
+ const { lc_kwargs: _lc_kwargs, lc_serializable: _lc_serializable, lc_namespace: _lc_namespace, ...rest } = message;
31
+ const cloned = { ...rest, content };
32
+ // Sync lc_kwargs.content with the new content to prevent LangChain coercion issues
33
+ const lcKwargs = message.lc_kwargs;
34
+ if (lcKwargs != null) {
35
+ cloned.lc_kwargs = {
36
+ ...lcKwargs,
37
+ content: content,
38
+ };
39
+ }
40
+ // LangChain messages don't have a direct 'role' property - derive it from getType()
41
+ if ('getType' in message &&
42
+ typeof message.getType === 'function' &&
43
+ !('role' in cloned)) {
44
+ const msgType = message.getType();
45
+ const roleMap = {
46
+ human: 'user',
47
+ ai: 'assistant',
48
+ system: 'system',
49
+ tool: 'tool',
50
+ };
51
+ cloned.role = roleMap[msgType] || msgType;
52
+ }
53
+ return cloned;
54
+ }
55
+ /**
56
+ * Checks if a content block is a cache point
57
+ */
58
+ function isCachePoint(block) {
59
+ return 'cachePoint' in block && !('type' in block);
60
+ }
61
+ /**
62
+ * Checks if a message's content needs cache control stripping.
63
+ * Returns true if content has cachePoint blocks or cache_control fields.
64
+ */
65
+ function needsCacheStripping(content) {
66
+ for (let i = 0; i < content.length; i++) {
67
+ const block = content[i];
68
+ if (isCachePoint(block))
69
+ return true;
70
+ if ('cache_control' in block)
71
+ return true;
72
+ }
73
+ return false;
74
+ }
75
+ /**
76
+ * Checks if a message's content has Anthropic cache_control fields.
77
+ */
78
+ function hasAnthropicCacheControl(content) {
79
+ for (let i = 0; i < content.length; i++) {
80
+ if ('cache_control' in content[i])
81
+ return true;
82
+ }
83
+ return false;
84
+ }
85
+ /**
86
+ * Checks if a message's content has Bedrock cachePoint blocks.
87
+ */
88
+ function hasBedrockCachePoint(content) {
89
+ for (let i = 0; i < content.length; i++) {
90
+ if (isCachePoint(content[i]))
91
+ return true;
92
+ }
93
+ return false;
94
+ }
12
95
  /**
13
96
  * Anthropic API: Adds cache control to the appropriate user messages in the payload.
14
97
  * Strips ALL existing cache control (both Anthropic and Bedrock formats) from all messages,
15
98
  * then adds fresh cache control to the last 2 user messages in a single backward pass.
16
99
  * This ensures we don't accumulate stale cache points across multiple turns.
100
+ * Returns a new array - only clones messages that require modification.
17
101
  * @param messages - The array of message objects.
18
- * @returns - The updated array of message objects with cache control added.
102
+ * @returns - A new array of message objects with cache control added.
19
103
  */
20
104
  function addCacheControl(messages) {
21
105
  if (!Array.isArray(messages) || messages.length < 2) {
@@ -24,55 +108,59 @@ function addCacheControl(messages) {
24
108
  const updatedMessages = [...messages];
25
109
  let userMessagesModified = 0;
26
110
  for (let i = updatedMessages.length - 1; i >= 0; i--) {
27
- const message = updatedMessages[i];
28
- const isUserMessage = ('getType' in message && message.getType() === 'human') ||
29
- ('role' in message && message.role === 'user');
30
- if (Array.isArray(message.content)) {
31
- message.content = message.content.filter((block) => !isCachePoint(block));
32
- for (let j = 0; j < message.content.length; j++) {
33
- const block = message.content[j];
111
+ const originalMessage = updatedMessages[i];
112
+ const content = originalMessage.content;
113
+ const isUserMessage = ('getType' in originalMessage && originalMessage.getType() === 'human') ||
114
+ ('role' in originalMessage && originalMessage.role === 'user');
115
+ const hasArrayContent = Array.isArray(content);
116
+ const needsStripping = hasArrayContent &&
117
+ needsCacheStripping(content);
118
+ const needsCacheAdd = userMessagesModified < 2 &&
119
+ isUserMessage &&
120
+ (typeof content === 'string' || hasArrayContent);
121
+ if (!needsStripping && !needsCacheAdd) {
122
+ continue;
123
+ }
124
+ let workingContent;
125
+ if (hasArrayContent) {
126
+ workingContent = deepCloneContent(content).filter((block) => !isCachePoint(block));
127
+ for (let j = 0; j < workingContent.length; j++) {
128
+ const block = workingContent[j];
34
129
  if ('cache_control' in block) {
35
130
  delete block.cache_control;
36
131
  }
37
132
  }
38
133
  }
134
+ else if (typeof content === 'string') {
135
+ workingContent = [
136
+ { type: 'text', text: content },
137
+ ];
138
+ }
139
+ else {
140
+ workingContent = [];
141
+ }
39
142
  if (userMessagesModified >= 2 || !isUserMessage) {
143
+ updatedMessages[i] = cloneMessage(originalMessage, workingContent);
40
144
  continue;
41
145
  }
42
- if (typeof message.content === 'string') {
43
- message.content = [
44
- {
45
- type: 'text',
46
- text: message.content,
47
- cache_control: { type: 'ephemeral' },
48
- },
49
- ];
50
- userMessagesModified++;
51
- }
52
- else if (Array.isArray(message.content)) {
53
- for (let j = message.content.length - 1; j >= 0; j--) {
54
- const contentPart = message.content[j];
55
- if ('type' in contentPart && contentPart.type === 'text') {
56
- contentPart.cache_control = {
57
- type: 'ephemeral',
58
- };
59
- userMessagesModified++;
60
- break;
61
- }
146
+ for (let j = workingContent.length - 1; j >= 0; j--) {
147
+ const contentPart = workingContent[j];
148
+ if ('type' in contentPart && contentPart.type === 'text') {
149
+ contentPart.cache_control = {
150
+ type: 'ephemeral',
151
+ };
152
+ userMessagesModified++;
153
+ break;
62
154
  }
63
155
  }
156
+ updatedMessages[i] = cloneMessage(originalMessage, workingContent);
64
157
  }
65
158
  return updatedMessages;
66
159
  }
67
- /**
68
- * Checks if a content block is a cache point
69
- */
70
- function isCachePoint(block) {
71
- return 'cachePoint' in block && !('type' in block);
72
- }
73
160
  /**
74
161
  * Removes all Anthropic cache_control fields from messages
75
162
  * Used when switching from Anthropic to Bedrock provider
163
+ * Returns a new array - only clones messages that require modification.
76
164
  */
77
165
  function stripAnthropicCacheControl(messages) {
78
166
  if (!Array.isArray(messages)) {
@@ -80,22 +168,26 @@ function stripAnthropicCacheControl(messages) {
80
168
  }
81
169
  const updatedMessages = [...messages];
82
170
  for (let i = 0; i < updatedMessages.length; i++) {
83
- const message = updatedMessages[i];
84
- const content = message.content;
85
- if (Array.isArray(content)) {
86
- for (let j = 0; j < content.length; j++) {
87
- const block = content[j];
88
- if ('cache_control' in block) {
89
- delete block.cache_control;
90
- }
171
+ const originalMessage = updatedMessages[i];
172
+ const content = originalMessage.content;
173
+ if (!Array.isArray(content) || !hasAnthropicCacheControl(content)) {
174
+ continue;
175
+ }
176
+ const clonedContent = deepCloneContent(content);
177
+ for (let j = 0; j < clonedContent.length; j++) {
178
+ const block = clonedContent[j];
179
+ if ('cache_control' in block) {
180
+ delete block.cache_control;
91
181
  }
92
182
  }
183
+ updatedMessages[i] = cloneMessage(originalMessage, clonedContent);
93
184
  }
94
185
  return updatedMessages;
95
186
  }
96
187
  /**
97
188
  * Removes all Bedrock cachePoint blocks from messages
98
189
  * Used when switching from Bedrock to Anthropic provider
190
+ * Returns a new array - only clones messages that require modification.
99
191
  */
100
192
  function stripBedrockCacheControl(messages) {
101
193
  if (!Array.isArray(messages)) {
@@ -103,11 +195,13 @@ function stripBedrockCacheControl(messages) {
103
195
  }
104
196
  const updatedMessages = [...messages];
105
197
  for (let i = 0; i < updatedMessages.length; i++) {
106
- const message = updatedMessages[i];
107
- const content = message.content;
108
- if (Array.isArray(content)) {
109
- message.content = content.filter((block) => !isCachePoint(block));
198
+ const originalMessage = updatedMessages[i];
199
+ const content = originalMessage.content;
200
+ if (!Array.isArray(content) || !hasBedrockCachePoint(content)) {
201
+ continue;
110
202
  }
203
+ const clonedContent = deepCloneContent(content).filter((block) => !isCachePoint(block));
204
+ updatedMessages[i] = cloneMessage(originalMessage, clonedContent);
111
205
  }
112
206
  return updatedMessages;
113
207
  }