@illuma-ai/agents 1.1.21 → 1.1.23
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 +12 -1
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +105 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/run.cjs +20 -9
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/utils/llm.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +12 -1
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +105 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/run.mjs +20 -9
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/utils/llm.mjs.map +1 -1
- package/dist/types/graphs/MultiAgentGraph.d.ts +17 -0
- package/package.json +1 -1
- package/src/graphs/Graph.ts +13 -1
- package/src/graphs/MultiAgentGraph.ts +128 -1
- package/src/graphs/__tests__/multi-agent-delegate.test.ts +205 -0
- package/src/run.ts +20 -11
- package/src/scripts/test-bedrock-handoff-autonomous.ts +231 -0
- package/src/utils/llm.ts +1 -0
- package/src/agents/AgentContext.js +0 -782
- package/src/agents/AgentContext.test.js +0 -421
- package/src/agents/__tests__/AgentContext.test.js +0 -678
- package/src/agents/__tests__/resolveStructuredOutputMode.test.js +0 -117
- package/src/common/enum.js +0 -192
- package/src/common/index.js +0 -3
- package/src/events.js +0 -166
- package/src/graphs/Graph.js +0 -1857
- package/src/graphs/MultiAgentGraph.js +0 -1092
- package/src/graphs/__tests__/structured-output.integration.test.js +0 -624
- package/src/graphs/__tests__/structured-output.test.js +0 -144
- package/src/graphs/contextManagement.e2e.test.js +0 -718
- package/src/graphs/contextManagement.test.js +0 -485
- package/src/graphs/handoffValidation.test.js +0 -276
- package/src/graphs/index.js +0 -3
- package/src/index.js +0 -28
- package/src/instrumentation.js +0 -21
- package/src/llm/anthropic/index.js +0 -319
- package/src/llm/anthropic/types.js +0 -46
- package/src/llm/anthropic/utils/message_inputs.js +0 -627
- package/src/llm/anthropic/utils/message_outputs.js +0 -290
- package/src/llm/anthropic/utils/output_parsers.js +0 -89
- package/src/llm/anthropic/utils/tools.js +0 -25
- package/src/llm/bedrock/__tests__/bedrock-caching.test.js +0 -392
- package/src/llm/bedrock/index.js +0 -303
- package/src/llm/bedrock/types.js +0 -2
- package/src/llm/bedrock/utils/index.js +0 -6
- package/src/llm/bedrock/utils/message_inputs.js +0 -463
- package/src/llm/bedrock/utils/message_outputs.js +0 -269
- package/src/llm/fake.js +0 -92
- package/src/llm/google/index.js +0 -215
- package/src/llm/google/types.js +0 -12
- package/src/llm/google/utils/common.js +0 -670
- package/src/llm/google/utils/tools.js +0 -111
- package/src/llm/google/utils/zod_to_genai_parameters.js +0 -47
- package/src/llm/openai/index.js +0 -1033
- package/src/llm/openai/types.js +0 -2
- package/src/llm/openai/utils/index.js +0 -756
- package/src/llm/openai/utils/isReasoningModel.test.js +0 -79
- package/src/llm/openrouter/index.js +0 -261
- package/src/llm/openrouter/reasoning.test.js +0 -181
- package/src/llm/providers.js +0 -36
- package/src/llm/text.js +0 -65
- package/src/llm/vertexai/index.js +0 -402
- package/src/messages/__tests__/tools.test.js +0 -392
- package/src/messages/cache.js +0 -404
- package/src/messages/cache.test.js +0 -1167
- package/src/messages/content.js +0 -48
- package/src/messages/content.test.js +0 -314
- package/src/messages/core.js +0 -359
- package/src/messages/ensureThinkingBlock.test.js +0 -997
- package/src/messages/format.js +0 -973
- package/src/messages/formatAgentMessages.test.js +0 -2278
- package/src/messages/formatAgentMessages.tools.test.js +0 -362
- package/src/messages/formatMessage.test.js +0 -608
- package/src/messages/ids.js +0 -18
- package/src/messages/index.js +0 -9
- package/src/messages/labelContentByAgent.test.js +0 -725
- package/src/messages/prune.js +0 -438
- package/src/messages/reducer.js +0 -60
- package/src/messages/shiftIndexTokenCountMap.test.js +0 -63
- package/src/messages/summarize.js +0 -146
- package/src/messages/summarize.test.js +0 -332
- package/src/messages/tools.js +0 -90
- package/src/mockStream.js +0 -81
- package/src/prompts/collab.js +0 -7
- package/src/prompts/index.js +0 -3
- package/src/prompts/taskmanager.js +0 -58
- package/src/run.js +0 -427
- package/src/schemas/index.js +0 -3
- package/src/schemas/schema-preparation.test.js +0 -370
- package/src/schemas/validate.js +0 -314
- package/src/schemas/validate.test.js +0 -264
- package/src/scripts/abort.js +0 -127
- package/src/scripts/ant_web_search.js +0 -130
- package/src/scripts/ant_web_search_edge_case.js +0 -133
- package/src/scripts/ant_web_search_error_edge_case.js +0 -119
- package/src/scripts/args.js +0 -41
- package/src/scripts/bedrock-cache-debug.js +0 -186
- package/src/scripts/bedrock-content-aggregation-test.js +0 -195
- package/src/scripts/bedrock-merge-test.js +0 -80
- package/src/scripts/bedrock-parallel-tools-test.js +0 -150
- package/src/scripts/caching.js +0 -106
- package/src/scripts/cli.js +0 -152
- package/src/scripts/cli2.js +0 -119
- package/src/scripts/cli3.js +0 -163
- package/src/scripts/cli4.js +0 -165
- package/src/scripts/cli5.js +0 -165
- package/src/scripts/code_exec.js +0 -171
- package/src/scripts/code_exec_files.js +0 -180
- package/src/scripts/code_exec_multi_session.js +0 -185
- package/src/scripts/code_exec_ptc.js +0 -265
- package/src/scripts/code_exec_session.js +0 -217
- package/src/scripts/code_exec_simple.js +0 -120
- package/src/scripts/content.js +0 -111
- package/src/scripts/empty_input.js +0 -125
- package/src/scripts/handoff-test.js +0 -96
- package/src/scripts/image.js +0 -138
- package/src/scripts/memory.js +0 -83
- package/src/scripts/multi-agent-chain.js +0 -271
- package/src/scripts/multi-agent-conditional.js +0 -185
- package/src/scripts/multi-agent-document-review-chain.js +0 -171
- package/src/scripts/multi-agent-hybrid-flow.js +0 -264
- package/src/scripts/multi-agent-parallel-start.js +0 -214
- package/src/scripts/multi-agent-parallel.js +0 -346
- package/src/scripts/multi-agent-sequence.js +0 -184
- package/src/scripts/multi-agent-supervisor.js +0 -324
- package/src/scripts/multi-agent-test.js +0 -147
- package/src/scripts/parallel-asymmetric-tools-test.js +0 -202
- package/src/scripts/parallel-full-metadata-test.js +0 -176
- package/src/scripts/parallel-tools-test.js +0 -256
- package/src/scripts/programmatic_exec.js +0 -277
- package/src/scripts/programmatic_exec_agent.js +0 -168
- package/src/scripts/search.js +0 -118
- package/src/scripts/sequential-full-metadata-test.js +0 -143
- package/src/scripts/simple.js +0 -174
- package/src/scripts/single-agent-metadata-test.js +0 -152
- package/src/scripts/stream.js +0 -113
- package/src/scripts/test-custom-prompt-key.js +0 -132
- package/src/scripts/test-handoff-input.js +0 -143
- package/src/scripts/test-handoff-preamble.js +0 -227
- package/src/scripts/test-handoff-steering.js +0 -353
- package/src/scripts/test-multi-agent-list-handoff.js +0 -318
- package/src/scripts/test-parallel-agent-labeling.js +0 -253
- package/src/scripts/test-parallel-handoffs.js +0 -229
- package/src/scripts/test-thinking-handoff-bedrock.js +0 -132
- package/src/scripts/test-thinking-handoff.js +0 -132
- package/src/scripts/test-thinking-to-thinking-handoff-bedrock.js +0 -140
- package/src/scripts/test-tool-before-handoff-role-order.js +0 -223
- package/src/scripts/test-tools-before-handoff.js +0 -187
- package/src/scripts/test_code_api.js +0 -263
- package/src/scripts/thinking-bedrock.js +0 -128
- package/src/scripts/thinking-vertexai.js +0 -130
- package/src/scripts/thinking.js +0 -134
- package/src/scripts/tool_search.js +0 -114
- package/src/scripts/tools.js +0 -125
- package/src/specs/agent-handoffs-bedrock.integration.test.js +0 -280
- package/src/specs/agent-handoffs.test.js +0 -924
- package/src/specs/anthropic.simple.test.js +0 -287
- package/src/specs/azure.simple.test.js +0 -381
- package/src/specs/cache.simple.test.js +0 -282
- package/src/specs/custom-event-await.test.js +0 -148
- package/src/specs/deepseek.simple.test.js +0 -189
- package/src/specs/emergency-prune.test.js +0 -308
- package/src/specs/moonshot.simple.test.js +0 -237
- package/src/specs/observability.integration.test.js +0 -1337
- package/src/specs/openai.simple.test.js +0 -233
- package/src/specs/openrouter.simple.test.js +0 -202
- package/src/specs/prune.test.js +0 -733
- package/src/specs/reasoning.test.js +0 -144
- package/src/specs/spec.utils.js +0 -4
- package/src/specs/thinking-handoff.test.js +0 -486
- package/src/specs/thinking-prune.test.js +0 -600
- package/src/specs/token-distribution-edge-case.test.js +0 -246
- package/src/specs/token-memoization.test.js +0 -32
- package/src/specs/tokens.test.js +0 -49
- package/src/specs/tool-error.test.js +0 -139
- package/src/splitStream.js +0 -204
- package/src/splitStream.test.js +0 -504
- package/src/stream.js +0 -650
- package/src/stream.test.js +0 -225
- package/src/test/mockTools.js +0 -340
- package/src/tools/BrowserTools.js +0 -245
- package/src/tools/Calculator.js +0 -38
- package/src/tools/Calculator.test.js +0 -225
- package/src/tools/CodeExecutor.js +0 -233
- package/src/tools/ProgrammaticToolCalling.js +0 -602
- package/src/tools/StreamingToolCallBuffer.js +0 -179
- package/src/tools/ToolNode.js +0 -930
- package/src/tools/ToolSearch.js +0 -904
- package/src/tools/__tests__/BrowserTools.test.js +0 -306
- package/src/tools/__tests__/ProgrammaticToolCalling.integration.test.js +0 -276
- package/src/tools/__tests__/ProgrammaticToolCalling.test.js +0 -807
- package/src/tools/__tests__/StreamingToolCallBuffer.test.js +0 -175
- package/src/tools/__tests__/ToolApproval.test.js +0 -675
- package/src/tools/__tests__/ToolNode.recovery.test.js +0 -200
- package/src/tools/__tests__/ToolNode.session.test.js +0 -319
- package/src/tools/__tests__/ToolSearch.integration.test.js +0 -125
- package/src/tools/__tests__/ToolSearch.test.js +0 -812
- package/src/tools/__tests__/handlers.test.js +0 -799
- package/src/tools/__tests__/truncation-recovery.integration.test.js +0 -362
- package/src/tools/handlers.js +0 -306
- package/src/tools/schema.js +0 -25
- package/src/tools/search/anthropic.js +0 -34
- package/src/tools/search/content.js +0 -116
- package/src/tools/search/content.test.js +0 -133
- package/src/tools/search/firecrawl.js +0 -173
- package/src/tools/search/format.js +0 -198
- package/src/tools/search/highlights.js +0 -241
- package/src/tools/search/index.js +0 -3
- package/src/tools/search/jina-reranker.test.js +0 -106
- package/src/tools/search/rerankers.js +0 -165
- package/src/tools/search/schema.js +0 -102
- package/src/tools/search/search.js +0 -561
- package/src/tools/search/serper-scraper.js +0 -126
- package/src/tools/search/test.js +0 -129
- package/src/tools/search/tool.js +0 -453
- package/src/tools/search/types.js +0 -2
- package/src/tools/search/utils.js +0 -59
- package/src/types/graph.js +0 -24
- package/src/types/graph.test.js +0 -192
- package/src/types/index.js +0 -7
- package/src/types/llm.js +0 -2
- package/src/types/messages.js +0 -2
- package/src/types/run.js +0 -2
- package/src/types/stream.js +0 -2
- package/src/types/tools.js +0 -2
- package/src/utils/contextAnalytics.js +0 -79
- package/src/utils/contextAnalytics.test.js +0 -166
- package/src/utils/events.js +0 -26
- package/src/utils/graph.js +0 -11
- package/src/utils/handlers.js +0 -65
- package/src/utils/index.js +0 -10
- package/src/utils/llm.js +0 -21
- package/src/utils/llmConfig.js +0 -205
- package/src/utils/logging.js +0 -37
- package/src/utils/misc.js +0 -51
- package/src/utils/run.js +0 -69
- package/src/utils/schema.js +0 -21
- package/src/utils/title.js +0 -119
- package/src/utils/tokens.js +0 -92
- package/src/utils/toonFormat.js +0 -379
package/src/messages/cache.js
DELETED
|
@@ -1,404 +0,0 @@
|
|
|
1
|
-
import { AIMessage, BaseMessage, ToolMessage, HumanMessage, SystemMessage, } from '@langchain/core/messages';
|
|
2
|
-
import { ContentTypes } from '@/common/enum';
|
|
3
|
-
/** Debug logger for cache operations - set ILLUMA_DEBUG_CACHE=true to enable */
|
|
4
|
-
const debugCache = (message, data) => {
|
|
5
|
-
if (process.env.ILLUMA_DEBUG_CACHE === 'true') {
|
|
6
|
-
// eslint-disable-next-line no-console
|
|
7
|
-
console.log(`[Cache] ${message}`, data !== undefined ? JSON.stringify(data, null, 2) : '');
|
|
8
|
-
}
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
* Deep clones a message's content to prevent mutation of the original.
|
|
12
|
-
*/
|
|
13
|
-
function deepCloneContent(content) {
|
|
14
|
-
if (typeof content === 'string') {
|
|
15
|
-
return content;
|
|
16
|
-
}
|
|
17
|
-
if (Array.isArray(content)) {
|
|
18
|
-
return content.map((block) => ({ ...block }));
|
|
19
|
-
}
|
|
20
|
-
return content;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Clones a message with new content. For LangChain BaseMessage instances,
|
|
24
|
-
* constructs a proper class instance so that `instanceof` checks are preserved
|
|
25
|
-
* in downstream code (e.g., ensureThinkingBlockInMessages).
|
|
26
|
-
* For plain objects (AnthropicMessage), uses object spread.
|
|
27
|
-
*/
|
|
28
|
-
function cloneMessage(message, content) {
|
|
29
|
-
if (message instanceof BaseMessage) {
|
|
30
|
-
const baseParams = {
|
|
31
|
-
content,
|
|
32
|
-
additional_kwargs: { ...message.additional_kwargs },
|
|
33
|
-
response_metadata: { ...message.response_metadata },
|
|
34
|
-
id: message.id,
|
|
35
|
-
name: message.name,
|
|
36
|
-
};
|
|
37
|
-
const msgType = message.getType();
|
|
38
|
-
switch (msgType) {
|
|
39
|
-
case 'ai':
|
|
40
|
-
return new AIMessage({
|
|
41
|
-
...baseParams,
|
|
42
|
-
tool_calls: message.tool_calls,
|
|
43
|
-
});
|
|
44
|
-
case 'human':
|
|
45
|
-
return new HumanMessage(baseParams);
|
|
46
|
-
case 'system':
|
|
47
|
-
return new SystemMessage(baseParams);
|
|
48
|
-
case 'tool':
|
|
49
|
-
return new ToolMessage({
|
|
50
|
-
...baseParams,
|
|
51
|
-
tool_call_id: message.tool_call_id,
|
|
52
|
-
});
|
|
53
|
-
default:
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
const { lc_kwargs: _lc_kwargs, lc_serializable: _lc_serializable, lc_namespace: _lc_namespace, ...rest } = message;
|
|
58
|
-
const cloned = { ...rest, content };
|
|
59
|
-
// Sync lc_kwargs.content with the new content to prevent LangChain coercion issues
|
|
60
|
-
const lcKwargs = message.lc_kwargs;
|
|
61
|
-
if (lcKwargs != null) {
|
|
62
|
-
cloned.lc_kwargs = {
|
|
63
|
-
...lcKwargs,
|
|
64
|
-
content: content,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
// LangChain messages don't have a direct 'role' property - derive it from getType()
|
|
68
|
-
if ('getType' in message &&
|
|
69
|
-
typeof message.getType === 'function' &&
|
|
70
|
-
!('role' in cloned)) {
|
|
71
|
-
const msgType = message.getType();
|
|
72
|
-
const roleMap = {
|
|
73
|
-
human: 'user',
|
|
74
|
-
ai: 'assistant',
|
|
75
|
-
system: 'system',
|
|
76
|
-
tool: 'tool',
|
|
77
|
-
};
|
|
78
|
-
cloned.role = roleMap[msgType] || msgType;
|
|
79
|
-
}
|
|
80
|
-
return cloned;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Checks if a content block is a cache point
|
|
84
|
-
*/
|
|
85
|
-
function isCachePoint(block) {
|
|
86
|
-
return 'cachePoint' in block && !('type' in block);
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Checks if a message's content needs cache control stripping.
|
|
90
|
-
* Returns true if content has cachePoint blocks or cache_control fields.
|
|
91
|
-
*/
|
|
92
|
-
function needsCacheStripping(content) {
|
|
93
|
-
for (let i = 0; i < content.length; i++) {
|
|
94
|
-
const block = content[i];
|
|
95
|
-
if (isCachePoint(block))
|
|
96
|
-
return true;
|
|
97
|
-
if ('cache_control' in block)
|
|
98
|
-
return true;
|
|
99
|
-
}
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Checks if a message's content has Anthropic cache_control fields.
|
|
104
|
-
*/
|
|
105
|
-
function hasAnthropicCacheControl(content) {
|
|
106
|
-
for (let i = 0; i < content.length; i++) {
|
|
107
|
-
if ('cache_control' in content[i])
|
|
108
|
-
return true;
|
|
109
|
-
}
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Checks if a message's content has Bedrock cachePoint blocks.
|
|
114
|
-
*/
|
|
115
|
-
function hasBedrockCachePoint(content) {
|
|
116
|
-
for (let i = 0; i < content.length; i++) {
|
|
117
|
-
if (isCachePoint(content[i]))
|
|
118
|
-
return true;
|
|
119
|
-
}
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Anthropic API: Adds cache control to the appropriate user messages in the payload.
|
|
124
|
-
* Strips ALL existing cache control (both Anthropic and Bedrock formats) from all messages,
|
|
125
|
-
* then adds fresh cache control to the last 2 user messages in a single backward pass.
|
|
126
|
-
* This ensures we don't accumulate stale cache points across multiple turns.
|
|
127
|
-
* Returns a new array - only clones messages that require modification.
|
|
128
|
-
* @param messages - The array of message objects.
|
|
129
|
-
* @returns - A new array of message objects with cache control added.
|
|
130
|
-
*/
|
|
131
|
-
export function addCacheControl(messages) {
|
|
132
|
-
if (!Array.isArray(messages) || messages.length < 2) {
|
|
133
|
-
return messages;
|
|
134
|
-
}
|
|
135
|
-
const updatedMessages = [...messages];
|
|
136
|
-
let userMessagesModified = 0;
|
|
137
|
-
for (let i = updatedMessages.length - 1; i >= 0; i--) {
|
|
138
|
-
const originalMessage = updatedMessages[i];
|
|
139
|
-
const content = originalMessage.content;
|
|
140
|
-
const isUserMessage = ('getType' in originalMessage && originalMessage.getType() === 'human') ||
|
|
141
|
-
('role' in originalMessage && originalMessage.role === 'user');
|
|
142
|
-
const hasArrayContent = Array.isArray(content);
|
|
143
|
-
const needsStripping = hasArrayContent &&
|
|
144
|
-
needsCacheStripping(content);
|
|
145
|
-
const needsCacheAdd = userMessagesModified < 2 &&
|
|
146
|
-
isUserMessage &&
|
|
147
|
-
(typeof content === 'string' || hasArrayContent);
|
|
148
|
-
if (!needsStripping && !needsCacheAdd) {
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
let workingContent;
|
|
152
|
-
if (hasArrayContent) {
|
|
153
|
-
workingContent = deepCloneContent(content).filter((block) => !isCachePoint(block));
|
|
154
|
-
for (let j = 0; j < workingContent.length; j++) {
|
|
155
|
-
const block = workingContent[j];
|
|
156
|
-
if ('cache_control' in block) {
|
|
157
|
-
delete block.cache_control;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
else if (typeof content === 'string') {
|
|
162
|
-
workingContent = [
|
|
163
|
-
{ type: 'text', text: content },
|
|
164
|
-
];
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
workingContent = [];
|
|
168
|
-
}
|
|
169
|
-
if (userMessagesModified >= 2 || !isUserMessage) {
|
|
170
|
-
updatedMessages[i] = cloneMessage(originalMessage, workingContent);
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
for (let j = workingContent.length - 1; j >= 0; j--) {
|
|
174
|
-
const contentPart = workingContent[j];
|
|
175
|
-
if ('type' in contentPart && contentPart.type === 'text') {
|
|
176
|
-
contentPart.cache_control = {
|
|
177
|
-
type: 'ephemeral',
|
|
178
|
-
};
|
|
179
|
-
userMessagesModified++;
|
|
180
|
-
break;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
updatedMessages[i] = cloneMessage(originalMessage, workingContent);
|
|
184
|
-
}
|
|
185
|
-
return updatedMessages;
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Removes all Anthropic cache_control fields from messages
|
|
189
|
-
* Used when switching from Anthropic to Bedrock provider
|
|
190
|
-
* Returns a new array - only clones messages that require modification.
|
|
191
|
-
*/
|
|
192
|
-
export function stripAnthropicCacheControl(messages) {
|
|
193
|
-
if (!Array.isArray(messages)) {
|
|
194
|
-
return messages;
|
|
195
|
-
}
|
|
196
|
-
const updatedMessages = [...messages];
|
|
197
|
-
for (let i = 0; i < updatedMessages.length; i++) {
|
|
198
|
-
const originalMessage = updatedMessages[i];
|
|
199
|
-
const content = originalMessage.content;
|
|
200
|
-
if (!Array.isArray(content) || !hasAnthropicCacheControl(content)) {
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
const clonedContent = deepCloneContent(content);
|
|
204
|
-
for (let j = 0; j < clonedContent.length; j++) {
|
|
205
|
-
const block = clonedContent[j];
|
|
206
|
-
if ('cache_control' in block) {
|
|
207
|
-
delete block.cache_control;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
updatedMessages[i] = cloneMessage(originalMessage, clonedContent);
|
|
211
|
-
}
|
|
212
|
-
return updatedMessages;
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Removes all Bedrock cachePoint blocks from messages
|
|
216
|
-
* Used when switching from Bedrock to Anthropic provider
|
|
217
|
-
* Returns a new array - only clones messages that require modification.
|
|
218
|
-
*/
|
|
219
|
-
export function stripBedrockCacheControl(messages) {
|
|
220
|
-
if (!Array.isArray(messages)) {
|
|
221
|
-
return messages;
|
|
222
|
-
}
|
|
223
|
-
const updatedMessages = [...messages];
|
|
224
|
-
for (let i = 0; i < updatedMessages.length; i++) {
|
|
225
|
-
const originalMessage = updatedMessages[i];
|
|
226
|
-
const content = originalMessage.content;
|
|
227
|
-
if (!Array.isArray(content) || !hasBedrockCachePoint(content)) {
|
|
228
|
-
continue;
|
|
229
|
-
}
|
|
230
|
-
const clonedContent = deepCloneContent(content).filter((block) => !isCachePoint(block));
|
|
231
|
-
updatedMessages[i] = cloneMessage(originalMessage, clonedContent);
|
|
232
|
-
}
|
|
233
|
-
return updatedMessages;
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Adds Bedrock Converse API cache points using "Stable Prefix Caching" strategy.
|
|
237
|
-
*
|
|
238
|
-
* STRATEGY: Place cache point after the LAST ASSISTANT message only.
|
|
239
|
-
* This ensures the prefix (everything before the cache point) remains STABLE
|
|
240
|
-
* as the conversation grows, maximizing cache hits.
|
|
241
|
-
*
|
|
242
|
-
* Why this works:
|
|
243
|
-
* - System message has its own cachePoint (added in AgentContext)
|
|
244
|
-
* - Tools have their own cachePoint (added in IllumaBedrockConverse)
|
|
245
|
-
* - Conversation history grows, but the PREFIX stays the same
|
|
246
|
-
* - Only the NEW user message is uncached (it's always different)
|
|
247
|
-
*
|
|
248
|
-
* Example conversation flow:
|
|
249
|
-
* Request 1: [System+cachePoint][Tools+cachePoint][User1] → No conversation cache yet
|
|
250
|
-
* Request 2: [System][Tools][User1][Assistant1+cachePoint][User2] → Cache User1+Assistant1
|
|
251
|
-
* Request 3: [System][Tools][User1][Assistant1][User2][Assistant2+cachePoint][User3]
|
|
252
|
-
* → Cache reads User1+A1+User2+A2, cache writes new portion
|
|
253
|
-
*
|
|
254
|
-
* Claude's "Simplified Cache Management" automatically looks back up to 20 content
|
|
255
|
-
* blocks from the cache checkpoint to find the longest matching prefix.
|
|
256
|
-
*
|
|
257
|
-
* @param messages - The array of message objects (excluding system message).
|
|
258
|
-
* @returns - The updated array with a single cache point after the last assistant message.
|
|
259
|
-
*/
|
|
260
|
-
export function addBedrockCacheControl(messages) {
|
|
261
|
-
if (!Array.isArray(messages) || messages.length < 1) {
|
|
262
|
-
debugCache('addBedrockCacheControl: Skipping - no messages', {
|
|
263
|
-
count: messages.length,
|
|
264
|
-
});
|
|
265
|
-
return messages;
|
|
266
|
-
}
|
|
267
|
-
debugCache('addBedrockCacheControl: Processing messages with stable prefix strategy', {
|
|
268
|
-
count: messages.length,
|
|
269
|
-
});
|
|
270
|
-
// Clone messages to avoid mutating originals
|
|
271
|
-
const updatedMessages = messages.map((msg) => {
|
|
272
|
-
const content = msg.content;
|
|
273
|
-
if (Array.isArray(content)) {
|
|
274
|
-
// Strip existing cachePoint blocks and Anthropic-style cache_control
|
|
275
|
-
const stripped = content
|
|
276
|
-
.filter((block) => !isCachePoint(block))
|
|
277
|
-
.map((block) => {
|
|
278
|
-
const rec = block;
|
|
279
|
-
if ('cache_control' in rec) {
|
|
280
|
-
const { cache_control: _, ...rest } = rec;
|
|
281
|
-
return rest;
|
|
282
|
-
}
|
|
283
|
-
return block;
|
|
284
|
-
});
|
|
285
|
-
if (needsCacheStripping(content) || hasAnthropicCacheControl(content)) {
|
|
286
|
-
return cloneMessage(msg, stripped);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return cloneMessage(msg, content);
|
|
290
|
-
});
|
|
291
|
-
// Helper function to check if a message contains reasoning/thinking blocks
|
|
292
|
-
const hasReasoningBlock = (message) => {
|
|
293
|
-
const content = message.content;
|
|
294
|
-
if (!Array.isArray(content)) {
|
|
295
|
-
return false;
|
|
296
|
-
}
|
|
297
|
-
for (const block of content) {
|
|
298
|
-
const type = block.type;
|
|
299
|
-
if (type === 'reasoning_content' ||
|
|
300
|
-
type === 'reasoning' ||
|
|
301
|
-
type === 'thinking' ||
|
|
302
|
-
type === 'redacted_thinking') {
|
|
303
|
-
return true;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
return false;
|
|
307
|
-
};
|
|
308
|
-
/**
|
|
309
|
-
* Helper to add a cachePoint to a message's content.
|
|
310
|
-
* For strings: wraps into [{type: 'text', text}, {cachePoint}]
|
|
311
|
-
* For arrays: inserts cachePoint after the last non-whitespace text block
|
|
312
|
-
* Returns the new message or null if no suitable insertion point.
|
|
313
|
-
*/
|
|
314
|
-
const addCachePointToMessage = (message) => {
|
|
315
|
-
const msgContent = message.content;
|
|
316
|
-
if (typeof msgContent === 'string' && msgContent !== '') {
|
|
317
|
-
const newContent = [
|
|
318
|
-
{ type: ContentTypes.TEXT, text: msgContent },
|
|
319
|
-
{ cachePoint: { type: 'default' } },
|
|
320
|
-
];
|
|
321
|
-
return cloneMessage(message, newContent);
|
|
322
|
-
}
|
|
323
|
-
if (Array.isArray(msgContent) && msgContent.length > 0) {
|
|
324
|
-
if (hasReasoningBlock(message)) {
|
|
325
|
-
return null;
|
|
326
|
-
}
|
|
327
|
-
// Find the last text block and insert cache point after it
|
|
328
|
-
for (let j = msgContent.length - 1; j >= 0; j--) {
|
|
329
|
-
const type = msgContent[j].type;
|
|
330
|
-
if (type === ContentTypes.TEXT || type === 'text') {
|
|
331
|
-
const text = msgContent[j].text;
|
|
332
|
-
if (text != null && text.trim() !== '') {
|
|
333
|
-
const newContent = [
|
|
334
|
-
...msgContent.slice(0, j + 1),
|
|
335
|
-
{ cachePoint: { type: 'default' } },
|
|
336
|
-
...msgContent.slice(j + 1),
|
|
337
|
-
];
|
|
338
|
-
return cloneMessage(message, newContent);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
return null;
|
|
344
|
-
};
|
|
345
|
-
// Add cache points to the last 2 messages (from the end) that have eligible content.
|
|
346
|
-
// This mirrors Anthropic's two-breakpoint strategy for Bedrock's cache API.
|
|
347
|
-
// Skip messages with only whitespace, empty content, or reasoning blocks.
|
|
348
|
-
const MAX_CACHE_POINTS = 2;
|
|
349
|
-
let applied = 0;
|
|
350
|
-
for (let i = updatedMessages.length - 1; i >= 0 && applied < MAX_CACHE_POINTS; i--) {
|
|
351
|
-
const message = updatedMessages[i];
|
|
352
|
-
const msgContent = message.content;
|
|
353
|
-
// Skip empty/whitespace-only content
|
|
354
|
-
if (msgContent == null)
|
|
355
|
-
continue;
|
|
356
|
-
if (typeof msgContent === 'string' && msgContent.trim() === '')
|
|
357
|
-
continue;
|
|
358
|
-
if (Array.isArray(msgContent) && msgContent.length === 0)
|
|
359
|
-
continue;
|
|
360
|
-
// Skip non-string, non-array content (e.g., number, object without type)
|
|
361
|
-
if (typeof msgContent !== 'string' && !Array.isArray(msgContent))
|
|
362
|
-
continue;
|
|
363
|
-
// Skip AI messages with only whitespace text and reasoning blocks (tool-call scenario)
|
|
364
|
-
const messageType = 'getType' in message && typeof message.getType === 'function'
|
|
365
|
-
? message.getType()
|
|
366
|
-
: 'unknown';
|
|
367
|
-
const role = message.role;
|
|
368
|
-
const isAi = messageType === 'ai' || role === 'assistant';
|
|
369
|
-
if (isAi && hasReasoningBlock(message)) {
|
|
370
|
-
// Check if all text blocks are whitespace-only
|
|
371
|
-
if (Array.isArray(msgContent)) {
|
|
372
|
-
const hasNonWhitespaceText = msgContent.some((block) => {
|
|
373
|
-
const type = block.type;
|
|
374
|
-
if (type === ContentTypes.TEXT || type === 'text') {
|
|
375
|
-
const text = block.text;
|
|
376
|
-
return text != null && text.trim() !== '';
|
|
377
|
-
}
|
|
378
|
-
return false;
|
|
379
|
-
});
|
|
380
|
-
if (!hasNonWhitespaceText) {
|
|
381
|
-
debugCache(`⚠️ Message cachePoint SKIPPED at index ${i} (AI with whitespace-only text + reasoning)`);
|
|
382
|
-
continue;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
// Skip messages that already have cache points (multi-agent scenarios)
|
|
387
|
-
if (Array.isArray(msgContent) &&
|
|
388
|
-
msgContent.some((b) => isCachePoint(b))) {
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
const updated = addCachePointToMessage(message);
|
|
392
|
-
if (updated != null) {
|
|
393
|
-
updatedMessages[i] = updated;
|
|
394
|
-
applied++;
|
|
395
|
-
debugCache(`📍 Message cachePoint at index ${i} (${typeof message.content === 'string' ? 'string' : 'array'})`);
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
debugCache('addBedrockCacheControl: Complete - stable prefix caching applied', {
|
|
399
|
-
appliedCachePoints: applied,
|
|
400
|
-
totalMessages: updatedMessages.length,
|
|
401
|
-
});
|
|
402
|
-
return updatedMessages;
|
|
403
|
-
}
|
|
404
|
-
//# sourceMappingURL=cache.js.map
|