illuma-agents 1.0.4 → 1.0.6
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 +80 -1
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
- package/dist/cjs/main.cjs +4 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +87 -14
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +242 -6
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/stream.cjs +3 -2
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs +5 -5
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +80 -1
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
- package/dist/esm/main.mjs +2 -2
- package/dist/esm/messages/cache.mjs +86 -15
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +242 -8
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/stream.mjs +3 -2
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs +5 -5
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/types/graphs/Graph.d.ts +19 -2
- package/dist/types/messages/cache.d.ts +16 -0
- package/dist/types/messages/format.d.ts +25 -1
- package/dist/types/tools/handlers.d.ts +2 -1
- package/dist/types/types/stream.d.ts +1 -0
- package/package.json +4 -1
- package/src/graphs/Graph.ts +99 -2
- package/src/llm/anthropic/utils/message_outputs.ts +289 -289
- package/src/messages/cache.test.ts +499 -3
- package/src/messages/cache.ts +115 -25
- package/src/messages/ensureThinkingBlock.test.ts +393 -0
- package/src/messages/format.ts +312 -6
- package/src/messages/labelContentByAgent.test.ts +887 -0
- package/src/scripts/test-multi-agent-list-handoff.ts +169 -13
- package/src/scripts/test-parallel-agent-labeling.ts +325 -0
- package/src/scripts/test-thinking-handoff-bedrock.ts +153 -0
- package/src/scripts/test-thinking-handoff.ts +147 -0
- package/src/specs/thinking-handoff.test.ts +620 -0
- package/src/stream.ts +19 -10
- package/src/tools/handlers.ts +36 -18
- package/src/types/stream.ts +1 -0
|
@@ -2,6 +2,9 @@ import { ContentTypes } from '../common/enum.mjs';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Anthropic API: Adds cache control to the appropriate user messages in the payload.
|
|
5
|
+
* Strips ALL existing cache control (both Anthropic and Bedrock formats) from all messages,
|
|
6
|
+
* then adds fresh cache control to the last 2 user messages in a single backward pass.
|
|
7
|
+
* This ensures we don't accumulate stale cache points across multiple turns.
|
|
5
8
|
* @param messages - The array of message objects.
|
|
6
9
|
* @returns - The updated array of message objects with cache control added.
|
|
7
10
|
*/
|
|
@@ -11,12 +14,20 @@ function addCacheControl(messages) {
|
|
|
11
14
|
}
|
|
12
15
|
const updatedMessages = [...messages];
|
|
13
16
|
let userMessagesModified = 0;
|
|
14
|
-
for (let i = updatedMessages.length - 1; i >= 0
|
|
17
|
+
for (let i = updatedMessages.length - 1; i >= 0; i--) {
|
|
15
18
|
const message = updatedMessages[i];
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
const isUserMessage = ('getType' in message && message.getType() === 'human') ||
|
|
20
|
+
('role' in message && message.role === 'user');
|
|
21
|
+
if (Array.isArray(message.content)) {
|
|
22
|
+
message.content = message.content.filter((block) => !isCachePoint(block));
|
|
23
|
+
for (let j = 0; j < message.content.length; j++) {
|
|
24
|
+
const block = message.content[j];
|
|
25
|
+
if ('cache_control' in block) {
|
|
26
|
+
delete block.cache_control;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
18
29
|
}
|
|
19
|
-
|
|
30
|
+
if (userMessagesModified >= 2 || !isUserMessage) {
|
|
20
31
|
continue;
|
|
21
32
|
}
|
|
22
33
|
if (typeof message.content === 'string') {
|
|
@@ -44,10 +55,60 @@ function addCacheControl(messages) {
|
|
|
44
55
|
}
|
|
45
56
|
return updatedMessages;
|
|
46
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Checks if a content block is a cache point
|
|
60
|
+
*/
|
|
61
|
+
function isCachePoint(block) {
|
|
62
|
+
return 'cachePoint' in block && !('type' in block);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Removes all Anthropic cache_control fields from messages
|
|
66
|
+
* Used when switching from Anthropic to Bedrock provider
|
|
67
|
+
*/
|
|
68
|
+
function stripAnthropicCacheControl(messages) {
|
|
69
|
+
if (!Array.isArray(messages)) {
|
|
70
|
+
return messages;
|
|
71
|
+
}
|
|
72
|
+
const updatedMessages = [...messages];
|
|
73
|
+
for (let i = 0; i < updatedMessages.length; i++) {
|
|
74
|
+
const message = updatedMessages[i];
|
|
75
|
+
const content = message.content;
|
|
76
|
+
if (Array.isArray(content)) {
|
|
77
|
+
for (let j = 0; j < content.length; j++) {
|
|
78
|
+
const block = content[j];
|
|
79
|
+
if ('cache_control' in block) {
|
|
80
|
+
delete block.cache_control;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return updatedMessages;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Removes all Bedrock cachePoint blocks from messages
|
|
89
|
+
* Used when switching from Bedrock to Anthropic provider
|
|
90
|
+
*/
|
|
91
|
+
function stripBedrockCacheControl(messages) {
|
|
92
|
+
if (!Array.isArray(messages)) {
|
|
93
|
+
return messages;
|
|
94
|
+
}
|
|
95
|
+
const updatedMessages = [...messages];
|
|
96
|
+
for (let i = 0; i < updatedMessages.length; i++) {
|
|
97
|
+
const message = updatedMessages[i];
|
|
98
|
+
const content = message.content;
|
|
99
|
+
if (Array.isArray(content)) {
|
|
100
|
+
message.content = content.filter((block) => !isCachePoint(block));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return updatedMessages;
|
|
104
|
+
}
|
|
47
105
|
/**
|
|
48
106
|
* Adds Bedrock Converse API cache points to the last two messages.
|
|
49
107
|
* Inserts `{ cachePoint: { type: 'default' } }` as a separate content block
|
|
50
108
|
* immediately after the last text block in each targeted message.
|
|
109
|
+
* Strips ALL existing cache control (both Bedrock and Anthropic formats) from all messages,
|
|
110
|
+
* then adds fresh cache points to the last 2 messages in a single backward pass.
|
|
111
|
+
* This ensures we don't accumulate stale cache points across multiple turns.
|
|
51
112
|
* @param messages - The array of message objects.
|
|
52
113
|
* @returns - The updated array of message objects with cache points added.
|
|
53
114
|
*/
|
|
@@ -57,14 +118,24 @@ function addBedrockCacheControl(messages) {
|
|
|
57
118
|
}
|
|
58
119
|
const updatedMessages = messages.slice();
|
|
59
120
|
let messagesModified = 0;
|
|
60
|
-
for (let i = updatedMessages.length - 1; i >= 0
|
|
121
|
+
for (let i = updatedMessages.length - 1; i >= 0; i--) {
|
|
61
122
|
const message = updatedMessages[i];
|
|
62
|
-
|
|
123
|
+
const isToolMessage = 'getType' in message &&
|
|
63
124
|
typeof message.getType === 'function' &&
|
|
64
|
-
message.getType() === 'tool'
|
|
125
|
+
message.getType() === 'tool';
|
|
126
|
+
const content = message.content;
|
|
127
|
+
if (Array.isArray(content)) {
|
|
128
|
+
message.content = content.filter((block) => !isCachePoint(block));
|
|
129
|
+
for (let j = 0; j < message.content.length; j++) {
|
|
130
|
+
const block = message.content[j];
|
|
131
|
+
if ('cache_control' in block) {
|
|
132
|
+
delete block.cache_control;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (messagesModified >= 2 || isToolMessage) {
|
|
65
137
|
continue;
|
|
66
138
|
}
|
|
67
|
-
const content = message.content;
|
|
68
139
|
if (typeof content === 'string' && content === '') {
|
|
69
140
|
continue;
|
|
70
141
|
}
|
|
@@ -76,9 +147,9 @@ function addBedrockCacheControl(messages) {
|
|
|
76
147
|
messagesModified++;
|
|
77
148
|
continue;
|
|
78
149
|
}
|
|
79
|
-
if (Array.isArray(content)) {
|
|
150
|
+
if (Array.isArray(message.content)) {
|
|
80
151
|
let hasCacheableContent = false;
|
|
81
|
-
for (const block of content) {
|
|
152
|
+
for (const block of message.content) {
|
|
82
153
|
if (block.type === ContentTypes.TEXT) {
|
|
83
154
|
if (typeof block.text === 'string' && block.text !== '') {
|
|
84
155
|
hasCacheableContent = true;
|
|
@@ -90,15 +161,15 @@ function addBedrockCacheControl(messages) {
|
|
|
90
161
|
continue;
|
|
91
162
|
}
|
|
92
163
|
let inserted = false;
|
|
93
|
-
for (let j = content.length - 1; j >= 0; j--) {
|
|
94
|
-
const block = content[j];
|
|
164
|
+
for (let j = message.content.length - 1; j >= 0; j--) {
|
|
165
|
+
const block = message.content[j];
|
|
95
166
|
const type = block.type;
|
|
96
167
|
if (type === ContentTypes.TEXT || type === 'text') {
|
|
97
168
|
const text = block.text;
|
|
98
169
|
if (text === '' || text === undefined) {
|
|
99
170
|
continue;
|
|
100
171
|
}
|
|
101
|
-
content.splice(j + 1, 0, {
|
|
172
|
+
message.content.splice(j + 1, 0, {
|
|
102
173
|
cachePoint: { type: 'default' },
|
|
103
174
|
});
|
|
104
175
|
inserted = true;
|
|
@@ -106,7 +177,7 @@ function addBedrockCacheControl(messages) {
|
|
|
106
177
|
}
|
|
107
178
|
}
|
|
108
179
|
if (!inserted) {
|
|
109
|
-
content.push({
|
|
180
|
+
message.content.push({
|
|
110
181
|
cachePoint: { type: 'default' },
|
|
111
182
|
});
|
|
112
183
|
}
|
|
@@ -116,5 +187,5 @@ function addBedrockCacheControl(messages) {
|
|
|
116
187
|
return updatedMessages;
|
|
117
188
|
}
|
|
118
189
|
|
|
119
|
-
export { addBedrockCacheControl, addCacheControl };
|
|
190
|
+
export { addBedrockCacheControl, addCacheControl, stripAnthropicCacheControl, stripBedrockCacheControl };
|
|
120
191
|
//# sourceMappingURL=cache.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.mjs","sources":["../../../src/messages/cache.ts"],"sourcesContent":["import { BaseMessage, MessageContentComplex } from '@langchain/core/messages';\nimport type { AnthropicMessage } from '@/types/messages';\nimport type Anthropic from '@anthropic-ai/sdk';\nimport { ContentTypes } from '@/common/enum';\n\ntype MessageWithContent = {\n content?: string | MessageContentComplex[];\n};\n\n/**\n * Anthropic API: Adds cache control to the appropriate user messages in the payload.\n * @param messages - The array of message objects.\n * @returns - The updated array of message objects with cache control added.\n */\nexport function addCacheControl<T extends AnthropicMessage | BaseMessage>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages) || messages.length < 2) {\n return messages;\n }\n\n const updatedMessages = [...messages];\n let userMessagesModified = 0;\n\n for (\n let i = updatedMessages.length - 1;\n i >= 0 && userMessagesModified < 2;\n i--\n ) {\n const message = updatedMessages[i];\n if ('getType' in message && message.getType() !== 'human') {\n continue;\n } else if ('role' in message && message.role !== 'user') {\n continue;\n }\n\n if (typeof message.content === 'string') {\n message.content = [\n {\n type: 'text',\n text: message.content,\n cache_control: { type: 'ephemeral' },\n },\n ];\n userMessagesModified++;\n } else if (Array.isArray(message.content)) {\n for (let j = message.content.length - 1; j >= 0; j--) {\n const contentPart = message.content[j];\n if ('type' in contentPart && contentPart.type === 'text') {\n (contentPart as Anthropic.TextBlockParam).cache_control = {\n type: 'ephemeral',\n };\n userMessagesModified++;\n break;\n }\n }\n }\n }\n\n return updatedMessages;\n}\n\n/**\n * Adds Bedrock Converse API cache points to the last two messages.\n * Inserts `{ cachePoint: { type: 'default' } }` as a separate content block\n * immediately after the last text block in each targeted message.\n * @param messages - The array of message objects.\n * @returns - The updated array of message objects with cache points added.\n */\nexport function addBedrockCacheControl<\n T extends Partial<BaseMessage> & MessageWithContent,\n>(messages: T[]): T[] {\n if (!Array.isArray(messages) || messages.length < 2) {\n return messages;\n }\n\n const updatedMessages: T[] = messages.slice();\n let messagesModified = 0;\n\n for (\n let i = updatedMessages.length - 1;\n i >= 0 && messagesModified < 2;\n i--\n ) {\n const message = updatedMessages[i];\n\n if (\n 'getType' in message &&\n typeof message.getType === 'function' &&\n message.getType() === 'tool'\n ) {\n continue;\n }\n\n const content = message.content;\n\n if (typeof content === 'string' && content === '') {\n continue;\n }\n\n if (typeof content === 'string') {\n message.content = [\n { type: ContentTypes.TEXT, text: content },\n { cachePoint: { type: 'default' } },\n ] as MessageContentComplex[];\n messagesModified++;\n continue;\n }\n\n if (Array.isArray(content)) {\n let hasCacheableContent = false;\n for (const block of content) {\n if (block.type === ContentTypes.TEXT) {\n if (typeof block.text === 'string' && block.text !== '') {\n hasCacheableContent = true;\n break;\n }\n }\n }\n\n if (!hasCacheableContent) {\n continue;\n }\n\n let inserted = false;\n for (let j = content.length - 1; j >= 0; j--) {\n const block = content[j] as MessageContentComplex;\n const type = (block as { type?: string }).type;\n if (type === ContentTypes.TEXT || type === 'text') {\n const text = (block as { text?: string }).text;\n if (text === '' || text === undefined) {\n continue;\n }\n content.splice(j + 1, 0, {\n cachePoint: { type: 'default' },\n } as MessageContentComplex);\n inserted = true;\n break;\n }\n }\n if (!inserted) {\n content.push({\n cachePoint: { type: 'default' },\n } as MessageContentComplex);\n }\n messagesModified++;\n }\n }\n\n return updatedMessages;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cache.mjs","sources":["../../../src/messages/cache.ts"],"sourcesContent":["import { BaseMessage, MessageContentComplex } from '@langchain/core/messages';\nimport type { AnthropicMessage } from '@/types/messages';\nimport type Anthropic from '@anthropic-ai/sdk';\nimport { ContentTypes } from '@/common/enum';\n\ntype MessageWithContent = {\n content?: string | MessageContentComplex[];\n};\n\n/**\n * Anthropic API: Adds cache control to the appropriate user messages in the payload.\n * Strips ALL existing cache control (both Anthropic and Bedrock formats) from all messages,\n * then adds fresh cache control to the last 2 user messages in a single backward pass.\n * This ensures we don't accumulate stale cache points across multiple turns.\n * @param messages - The array of message objects.\n * @returns - The updated array of message objects with cache control added.\n */\nexport function addCacheControl<T extends AnthropicMessage | BaseMessage>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages) || messages.length < 2) {\n return messages;\n }\n\n const updatedMessages = [...messages];\n let userMessagesModified = 0;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const message = updatedMessages[i];\n const isUserMessage =\n ('getType' in message && message.getType() === 'human') ||\n ('role' in message && message.role === 'user');\n\n if (Array.isArray(message.content)) {\n message.content = message.content.filter(\n (block) => !isCachePoint(block as MessageContentComplex)\n ) as typeof message.content;\n\n for (let j = 0; j < message.content.length; j++) {\n const block = message.content[j] as Record<string, unknown>;\n if ('cache_control' in block) {\n delete block.cache_control;\n }\n }\n }\n\n if (userMessagesModified >= 2 || !isUserMessage) {\n continue;\n }\n\n if (typeof message.content === 'string') {\n message.content = [\n {\n type: 'text',\n text: message.content,\n cache_control: { type: 'ephemeral' },\n },\n ];\n userMessagesModified++;\n } else if (Array.isArray(message.content)) {\n for (let j = message.content.length - 1; j >= 0; j--) {\n const contentPart = message.content[j];\n if ('type' in contentPart && contentPart.type === 'text') {\n (contentPart as Anthropic.TextBlockParam).cache_control = {\n type: 'ephemeral',\n };\n userMessagesModified++;\n break;\n }\n }\n }\n }\n\n return updatedMessages;\n}\n\n/**\n * Checks if a content block is a cache point\n */\nfunction isCachePoint(block: MessageContentComplex): boolean {\n return 'cachePoint' in block && !('type' in block);\n}\n\n/**\n * Removes all Anthropic cache_control fields from messages\n * Used when switching from Anthropic to Bedrock provider\n */\nexport function stripAnthropicCacheControl<T extends MessageWithContent>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages)) {\n return messages;\n }\n\n const updatedMessages = [...messages];\n\n for (let i = 0; i < updatedMessages.length; i++) {\n const message = updatedMessages[i];\n const content = message.content;\n\n if (Array.isArray(content)) {\n for (let j = 0; j < content.length; j++) {\n const block = content[j] as Record<string, unknown>;\n if ('cache_control' in block) {\n delete block.cache_control;\n }\n }\n }\n }\n\n return updatedMessages;\n}\n\n/**\n * Removes all Bedrock cachePoint blocks from messages\n * Used when switching from Bedrock to Anthropic provider\n */\nexport function stripBedrockCacheControl<T extends MessageWithContent>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages)) {\n return messages;\n }\n\n const updatedMessages = [...messages];\n\n for (let i = 0; i < updatedMessages.length; i++) {\n const message = updatedMessages[i];\n const content = message.content;\n\n if (Array.isArray(content)) {\n message.content = content.filter(\n (block) => !isCachePoint(block as MessageContentComplex)\n ) as typeof content;\n }\n }\n\n return updatedMessages;\n}\n\n/**\n * Adds Bedrock Converse API cache points to the last two messages.\n * Inserts `{ cachePoint: { type: 'default' } }` as a separate content block\n * immediately after the last text block in each targeted message.\n * Strips ALL existing cache control (both Bedrock and Anthropic formats) from all messages,\n * then adds fresh cache points to the last 2 messages in a single backward pass.\n * This ensures we don't accumulate stale cache points across multiple turns.\n * @param messages - The array of message objects.\n * @returns - The updated array of message objects with cache points added.\n */\nexport function addBedrockCacheControl<\n T extends Partial<BaseMessage> & MessageWithContent,\n>(messages: T[]): T[] {\n if (!Array.isArray(messages) || messages.length < 2) {\n return messages;\n }\n\n const updatedMessages: T[] = messages.slice();\n let messagesModified = 0;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const message = updatedMessages[i];\n const isToolMessage =\n 'getType' in message &&\n typeof message.getType === 'function' &&\n message.getType() === 'tool';\n\n const content = message.content;\n\n if (Array.isArray(content)) {\n message.content = content.filter(\n (block) => !isCachePoint(block)\n ) as typeof content;\n\n for (let j = 0; j < message.content.length; j++) {\n const block = message.content[j] as Record<string, unknown>;\n if ('cache_control' in block) {\n delete block.cache_control;\n }\n }\n }\n\n if (messagesModified >= 2 || isToolMessage) {\n continue;\n }\n\n if (typeof content === 'string' && content === '') {\n continue;\n }\n\n if (typeof content === 'string') {\n message.content = [\n { type: ContentTypes.TEXT, text: content },\n { cachePoint: { type: 'default' } },\n ] as MessageContentComplex[];\n messagesModified++;\n continue;\n }\n\n if (Array.isArray(message.content)) {\n let hasCacheableContent = false;\n for (const block of message.content) {\n if (block.type === ContentTypes.TEXT) {\n if (typeof block.text === 'string' && block.text !== '') {\n hasCacheableContent = true;\n break;\n }\n }\n }\n\n if (!hasCacheableContent) {\n continue;\n }\n\n let inserted = false;\n for (let j = message.content.length - 1; j >= 0; j--) {\n const block = message.content[j] as MessageContentComplex;\n const type = (block as { type?: string }).type;\n if (type === ContentTypes.TEXT || type === 'text') {\n const text = (block as { text?: string }).text;\n if (text === '' || text === undefined) {\n continue;\n }\n message.content.splice(j + 1, 0, {\n cachePoint: { type: 'default' },\n } as MessageContentComplex);\n inserted = true;\n break;\n }\n }\n if (!inserted) {\n message.content.push({\n cachePoint: { type: 'default' },\n } as MessageContentComplex);\n }\n messagesModified++;\n }\n }\n\n return updatedMessages;\n}\n"],"names":[],"mappings":";;AASA;;;;;;;AAOG;AACG,SAAU,eAAe,CAC7B,QAAa,EAAA;AAEb,IAAA,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;AACnD,QAAA,OAAO,QAAQ;;AAGjB,IAAA,MAAM,eAAe,GAAG,CAAC,GAAG,QAAQ,CAAC;IACrC,IAAI,oBAAoB,GAAG,CAAC;AAE5B,IAAA,KAAK,IAAI,CAAC,GAAG,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE;AACpD,QAAA,MAAM,OAAO,GAAG,eAAe,CAAC,CAAC,CAAC;AAClC,QAAA,MAAM,aAAa,GACjB,CAAC,SAAS,IAAI,OAAO,IAAI,OAAO,CAAC,OAAO,EAAE,KAAK,OAAO;aACrD,MAAM,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC;QAEhD,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;YAClC,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CACtC,CAAC,KAAK,KAAK,CAAC,YAAY,CAAC,KAA8B,CAAC,CAC/B;AAE3B,YAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;gBAC/C,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAA4B;AAC3D,gBAAA,IAAI,eAAe,IAAI,KAAK,EAAE;oBAC5B,OAAO,KAAK,CAAC,aAAa;;;;AAKhC,QAAA,IAAI,oBAAoB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;YAC/C;;AAGF,QAAA,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,EAAE;YACvC,OAAO,CAAC,OAAO,GAAG;AAChB,gBAAA;AACE,oBAAA,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,OAAO,CAAC,OAAO;AACrB,oBAAA,aAAa,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE;AACrC,iBAAA;aACF;AACD,YAAA,oBAAoB,EAAE;;aACjB,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;AACzC,YAAA,KAAK,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE;gBACpD,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;gBACtC,IAAI,MAAM,IAAI,WAAW,IAAI,WAAW,CAAC,IAAI,KAAK,MAAM,EAAE;oBACvD,WAAwC,CAAC,aAAa,GAAG;AACxD,wBAAA,IAAI,EAAE,WAAW;qBAClB;AACD,oBAAA,oBAAoB,EAAE;oBACtB;;;;;AAMR,IAAA,OAAO,eAAe;AACxB;AAEA;;AAEG;AACH,SAAS,YAAY,CAAC,KAA4B,EAAA;IAChD,OAAO,YAAY,IAAI,KAAK,IAAI,EAAE,MAAM,IAAI,KAAK,CAAC;AACpD;AAEA;;;AAGG;AACG,SAAU,0BAA0B,CACxC,QAAa,EAAA;IAEb,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;AAC5B,QAAA,OAAO,QAAQ;;AAGjB,IAAA,MAAM,eAAe,GAAG,CAAC,GAAG,QAAQ,CAAC;AAErC,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AAC/C,QAAA,MAAM,OAAO,GAAG,eAAe,CAAC,CAAC,CAAC;AAClC,QAAA,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO;AAE/B,QAAA,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;AAC1B,YAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACvC,gBAAA,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAA4B;AACnD,gBAAA,IAAI,eAAe,IAAI,KAAK,EAAE;oBAC5B,OAAO,KAAK,CAAC,aAAa;;;;;AAMlC,IAAA,OAAO,eAAe;AACxB;AAEA;;;AAGG;AACG,SAAU,wBAAwB,CACtC,QAAa,EAAA;IAEb,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;AAC5B,QAAA,OAAO,QAAQ;;AAGjB,IAAA,MAAM,eAAe,GAAG,CAAC,GAAG,QAAQ,CAAC;AAErC,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AAC/C,QAAA,MAAM,OAAO,GAAG,eAAe,CAAC,CAAC,CAAC;AAClC,QAAA,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO;AAE/B,QAAA,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;AAC1B,YAAA,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,MAAM,CAC9B,CAAC,KAAK,KAAK,CAAC,YAAY,CAAC,KAA8B,CAAC,CACvC;;;AAIvB,IAAA,OAAO,eAAe;AACxB;AAEA;;;;;;;;;AASG;AACG,SAAU,sBAAsB,CAEpC,QAAa,EAAA;AACb,IAAA,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;AACnD,QAAA,OAAO,QAAQ;;AAGjB,IAAA,MAAM,eAAe,GAAQ,QAAQ,CAAC,KAAK,EAAE;IAC7C,IAAI,gBAAgB,GAAG,CAAC;AAExB,IAAA,KAAK,IAAI,CAAC,GAAG,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE;AACpD,QAAA,MAAM,OAAO,GAAG,eAAe,CAAC,CAAC,CAAC;AAClC,QAAA,MAAM,aAAa,GACjB,SAAS,IAAI,OAAO;AACpB,YAAA,OAAO,OAAO,CAAC,OAAO,KAAK,UAAU;AACrC,YAAA,OAAO,CAAC,OAAO,EAAE,KAAK,MAAM;AAE9B,QAAA,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO;AAE/B,QAAA,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;AAC1B,YAAA,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,MAAM,CAC9B,CAAC,KAAK,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CACd;AAEnB,YAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;gBAC/C,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAA4B;AAC3D,gBAAA,IAAI,eAAe,IAAI,KAAK,EAAE;oBAC5B,OAAO,KAAK,CAAC,aAAa;;;;AAKhC,QAAA,IAAI,gBAAgB,IAAI,CAAC,IAAI,aAAa,EAAE;YAC1C;;QAGF,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,EAAE,EAAE;YACjD;;AAGF,QAAA,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE;YAC/B,OAAO,CAAC,OAAO,GAAG;gBAChB,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE;AAC1C,gBAAA,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE;aACT;AAC5B,YAAA,gBAAgB,EAAE;YAClB;;QAGF,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;YAClC,IAAI,mBAAmB,GAAG,KAAK;AAC/B,YAAA,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE;gBACnC,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,IAAI,EAAE;AACpC,oBAAA,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,EAAE,EAAE;wBACvD,mBAAmB,GAAG,IAAI;wBAC1B;;;;YAKN,IAAI,CAAC,mBAAmB,EAAE;gBACxB;;YAGF,IAAI,QAAQ,GAAG,KAAK;AACpB,YAAA,KAAK,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE;gBACpD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAA0B;AACzD,gBAAA,MAAM,IAAI,GAAI,KAA2B,CAAC,IAAI;gBAC9C,IAAI,IAAI,KAAK,YAAY,CAAC,IAAI,IAAI,IAAI,KAAK,MAAM,EAAE;AACjD,oBAAA,MAAM,IAAI,GAAI,KAA2B,CAAC,IAAI;oBAC9C,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,SAAS,EAAE;wBACrC;;oBAEF,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;AAC/B,wBAAA,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;AACP,qBAAA,CAAC;oBAC3B,QAAQ,GAAG,IAAI;oBACf;;;YAGJ,IAAI,CAAC,QAAQ,EAAE;AACb,gBAAA,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;AACnB,oBAAA,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;AACP,iBAAA,CAAC;;AAE7B,YAAA,gBAAgB,EAAE;;;AAItB,IAAA,OAAO,eAAe;AACxB;;;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HumanMessage, AIMessage, SystemMessage, getBufferString, ToolMessage } from '@langchain/core/messages';
|
|
1
|
+
import { HumanMessage, AIMessage, SystemMessage, getBufferString, ToolMessage, AIMessageChunk } from '@langchain/core/messages';
|
|
2
2
|
import { Providers, ContentTypes } from '../common/enum.mjs';
|
|
3
3
|
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
@@ -180,19 +180,20 @@ function formatAssistantMessage(message) {
|
|
|
180
180
|
}
|
|
181
181
|
// Create a new AIMessage with this text and prepare for tool calls
|
|
182
182
|
lastAIMessage = new AIMessage({
|
|
183
|
-
content: part.text
|
|
183
|
+
content: part.text != null ? part.text : '',
|
|
184
184
|
});
|
|
185
185
|
formattedMessages.push(lastAIMessage);
|
|
186
186
|
}
|
|
187
187
|
else if (part.type === ContentTypes.TOOL_CALL) {
|
|
188
188
|
// Skip malformed tool call entries without tool_call property
|
|
189
|
-
if (
|
|
189
|
+
if (part.tool_call == null) {
|
|
190
190
|
continue;
|
|
191
191
|
}
|
|
192
192
|
// Note: `tool_calls` list is defined when constructed by `AIMessage` class, and outputs should be excluded from it
|
|
193
193
|
const { output, args: _args, ..._tool_call } = part.tool_call;
|
|
194
194
|
// Skip invalid tool calls that have no name AND no output
|
|
195
|
-
if (
|
|
195
|
+
if (_tool_call.name == null ||
|
|
196
|
+
(_tool_call.name === '' && (output == null || output === ''))) {
|
|
196
197
|
continue;
|
|
197
198
|
}
|
|
198
199
|
if (!lastAIMessage) {
|
|
@@ -221,7 +222,7 @@ function formatAssistantMessage(message) {
|
|
|
221
222
|
formattedMessages.push(new ToolMessage({
|
|
222
223
|
tool_call_id: tool_call.id ?? '',
|
|
223
224
|
name: tool_call.name,
|
|
224
|
-
content: output
|
|
225
|
+
content: output != null ? output : '',
|
|
225
226
|
}));
|
|
226
227
|
}
|
|
227
228
|
else if (part.type === ContentTypes.THINK) {
|
|
@@ -255,6 +256,177 @@ function formatAssistantMessage(message) {
|
|
|
255
256
|
}
|
|
256
257
|
return formattedMessages;
|
|
257
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* Labels all agent content for parallel patterns (fan-out/fan-in)
|
|
261
|
+
* Groups consecutive content by agent and wraps with clear labels
|
|
262
|
+
*/
|
|
263
|
+
function labelAllAgentContent(contentParts, agentIdMap, agentNames) {
|
|
264
|
+
const result = [];
|
|
265
|
+
let currentAgentId;
|
|
266
|
+
let agentContentBuffer = [];
|
|
267
|
+
const flushAgentBuffer = () => {
|
|
268
|
+
if (agentContentBuffer.length === 0) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (currentAgentId != null && currentAgentId !== '') {
|
|
272
|
+
const agentName = (agentNames?.[currentAgentId] ?? '') || currentAgentId;
|
|
273
|
+
const formattedParts = [];
|
|
274
|
+
formattedParts.push(`--- ${agentName} ---`);
|
|
275
|
+
for (const part of agentContentBuffer) {
|
|
276
|
+
if (part.type === ContentTypes.THINK) {
|
|
277
|
+
const thinkContent = part.think || '';
|
|
278
|
+
if (thinkContent) {
|
|
279
|
+
formattedParts.push(`${agentName}: ${JSON.stringify({
|
|
280
|
+
type: 'think',
|
|
281
|
+
think: thinkContent,
|
|
282
|
+
})}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
else if (part.type === ContentTypes.TEXT) {
|
|
286
|
+
const textContent = part.text ?? '';
|
|
287
|
+
if (textContent) {
|
|
288
|
+
formattedParts.push(`${agentName}: ${textContent}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
else if (part.type === ContentTypes.TOOL_CALL) {
|
|
292
|
+
formattedParts.push(`${agentName}: ${JSON.stringify({
|
|
293
|
+
type: 'tool_call',
|
|
294
|
+
tool_call: part.tool_call,
|
|
295
|
+
})}`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
formattedParts.push(`--- End of ${agentName} ---`);
|
|
299
|
+
// Create a single text content part with all agent content
|
|
300
|
+
result.push({
|
|
301
|
+
type: ContentTypes.TEXT,
|
|
302
|
+
text: formattedParts.join('\n\n'),
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
// No agent ID, pass through as-is
|
|
307
|
+
result.push(...agentContentBuffer);
|
|
308
|
+
}
|
|
309
|
+
agentContentBuffer = [];
|
|
310
|
+
};
|
|
311
|
+
for (let i = 0; i < contentParts.length; i++) {
|
|
312
|
+
const part = contentParts[i];
|
|
313
|
+
const agentId = agentIdMap[i];
|
|
314
|
+
// If agent changed, flush previous buffer
|
|
315
|
+
if (agentId !== currentAgentId && currentAgentId !== undefined) {
|
|
316
|
+
flushAgentBuffer();
|
|
317
|
+
}
|
|
318
|
+
currentAgentId = agentId;
|
|
319
|
+
agentContentBuffer.push(part);
|
|
320
|
+
}
|
|
321
|
+
// Flush any remaining content
|
|
322
|
+
flushAgentBuffer();
|
|
323
|
+
return result;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Groups content parts by agent and formats them with agent labels
|
|
327
|
+
* This preprocesses multi-agent content to prevent identity confusion
|
|
328
|
+
*
|
|
329
|
+
* @param contentParts - The content parts from a run
|
|
330
|
+
* @param agentIdMap - Map of content part index to agent ID
|
|
331
|
+
* @param agentNames - Optional map of agent ID to display name
|
|
332
|
+
* @param options - Configuration options
|
|
333
|
+
* @param options.labelNonTransferContent - If true, labels all agent transitions (for parallel patterns)
|
|
334
|
+
* @returns Modified content parts with agent labels where appropriate
|
|
335
|
+
*/
|
|
336
|
+
const labelContentByAgent = (contentParts, agentIdMap, agentNames, options) => {
|
|
337
|
+
if (!agentIdMap || Object.keys(agentIdMap).length === 0) {
|
|
338
|
+
return contentParts;
|
|
339
|
+
}
|
|
340
|
+
// If labelNonTransferContent is true, use a different strategy for parallel patterns
|
|
341
|
+
if (options?.labelNonTransferContent === true) {
|
|
342
|
+
return labelAllAgentContent(contentParts, agentIdMap, agentNames);
|
|
343
|
+
}
|
|
344
|
+
const result = [];
|
|
345
|
+
let currentAgentId;
|
|
346
|
+
let agentContentBuffer = [];
|
|
347
|
+
let transferToolCallIndex;
|
|
348
|
+
let transferToolCallId;
|
|
349
|
+
const flushAgentBuffer = () => {
|
|
350
|
+
if (agentContentBuffer.length === 0) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
// If this is content from a transferred agent, format it specially
|
|
354
|
+
if (currentAgentId != null &&
|
|
355
|
+
currentAgentId !== '' &&
|
|
356
|
+
transferToolCallIndex !== undefined) {
|
|
357
|
+
const agentName = (agentNames?.[currentAgentId] ?? '') || currentAgentId;
|
|
358
|
+
const formattedParts = [];
|
|
359
|
+
formattedParts.push(`--- Transfer to ${agentName} ---`);
|
|
360
|
+
for (const part of agentContentBuffer) {
|
|
361
|
+
if (part.type === ContentTypes.THINK) {
|
|
362
|
+
formattedParts.push(`${agentName}: ${JSON.stringify({
|
|
363
|
+
type: 'think',
|
|
364
|
+
think: part.think,
|
|
365
|
+
})}`);
|
|
366
|
+
}
|
|
367
|
+
else if ('text' in part && part.type === ContentTypes.TEXT) {
|
|
368
|
+
const textContent = part.text ?? '';
|
|
369
|
+
if (textContent) {
|
|
370
|
+
formattedParts.push(`${agentName}: ${JSON.stringify({
|
|
371
|
+
type: 'text',
|
|
372
|
+
text: textContent,
|
|
373
|
+
})}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
else if (part.type === ContentTypes.TOOL_CALL) {
|
|
377
|
+
formattedParts.push(`${agentName}: ${JSON.stringify({
|
|
378
|
+
type: 'tool_call',
|
|
379
|
+
tool_call: part.tool_call,
|
|
380
|
+
})}`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
formattedParts.push(`--- End of ${agentName} response ---`);
|
|
384
|
+
// Find the tool call that triggered this transfer and update its output
|
|
385
|
+
if (transferToolCallIndex < result.length) {
|
|
386
|
+
const transferToolCall = result[transferToolCallIndex];
|
|
387
|
+
if (transferToolCall.type === ContentTypes.TOOL_CALL &&
|
|
388
|
+
transferToolCall.tool_call?.id === transferToolCallId) {
|
|
389
|
+
transferToolCall.tool_call.output = formattedParts.join('\n\n');
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
// Not from a transfer, add as-is
|
|
395
|
+
result.push(...agentContentBuffer);
|
|
396
|
+
}
|
|
397
|
+
agentContentBuffer = [];
|
|
398
|
+
transferToolCallIndex = undefined;
|
|
399
|
+
transferToolCallId = undefined;
|
|
400
|
+
};
|
|
401
|
+
for (let i = 0; i < contentParts.length; i++) {
|
|
402
|
+
const part = contentParts[i];
|
|
403
|
+
const agentId = agentIdMap[i];
|
|
404
|
+
// Check if this is a transfer tool call
|
|
405
|
+
const isTransferTool = (part.type === ContentTypes.TOOL_CALL &&
|
|
406
|
+
part.tool_call?.name?.startsWith('lc_transfer_to_')) ??
|
|
407
|
+
false;
|
|
408
|
+
// If agent changed, flush previous buffer
|
|
409
|
+
if (agentId !== currentAgentId && currentAgentId !== undefined) {
|
|
410
|
+
flushAgentBuffer();
|
|
411
|
+
}
|
|
412
|
+
currentAgentId = agentId;
|
|
413
|
+
if (isTransferTool) {
|
|
414
|
+
// Flush any existing buffer first
|
|
415
|
+
flushAgentBuffer();
|
|
416
|
+
// Add the transfer tool call to result
|
|
417
|
+
result.push(part);
|
|
418
|
+
// Mark that the next agent's content should be captured
|
|
419
|
+
transferToolCallIndex = result.length - 1;
|
|
420
|
+
transferToolCallId = part.tool_call?.id;
|
|
421
|
+
currentAgentId = undefined; // Reset to capture the next agent
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
agentContentBuffer.push(part);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
flushAgentBuffer();
|
|
428
|
+
return result;
|
|
429
|
+
};
|
|
258
430
|
/**
|
|
259
431
|
* Formats an array of messages for LangChain, handling tool calls and creating ToolMessage instances.
|
|
260
432
|
*
|
|
@@ -305,7 +477,7 @@ const formatAgentMessages = (payload, indexTokenCountMap, tools) => {
|
|
|
305
477
|
break;
|
|
306
478
|
}
|
|
307
479
|
// Protect against malformed tool call entries
|
|
308
|
-
if (
|
|
480
|
+
if (part.tool_call == null ||
|
|
309
481
|
part.tool_call.name == null ||
|
|
310
482
|
part.tool_call.name === '') {
|
|
311
483
|
hasInvalidTool = true;
|
|
@@ -332,7 +504,7 @@ const formatAgentMessages = (payload, indexTokenCountMap, tools) => {
|
|
|
332
504
|
// Check if this is a continuation of the tool sequence
|
|
333
505
|
let isToolResponse = false;
|
|
334
506
|
const content = payload[j].content;
|
|
335
|
-
if (content && Array.isArray(content)) {
|
|
507
|
+
if (content != null && Array.isArray(content)) {
|
|
336
508
|
for (const part of content) {
|
|
337
509
|
if (part.type === ContentTypes.TOOL_CALL) {
|
|
338
510
|
isToolResponse = true;
|
|
@@ -431,6 +603,68 @@ function shiftIndexTokenCountMap(indexTokenCountMap, instructionsTokenCount) {
|
|
|
431
603
|
}
|
|
432
604
|
return shiftedMap;
|
|
433
605
|
}
|
|
606
|
+
/**
|
|
607
|
+
* Ensures compatibility when switching from a non-thinking agent to a thinking-enabled agent.
|
|
608
|
+
* Converts AI messages with tool calls (that lack thinking blocks) into buffer strings,
|
|
609
|
+
* avoiding the thinking block signature requirement.
|
|
610
|
+
*
|
|
611
|
+
* @param messages - Array of messages to process
|
|
612
|
+
* @param provider - The provider being used (unused but kept for future compatibility)
|
|
613
|
+
* @returns The messages array with tool sequences converted to buffer strings if necessary
|
|
614
|
+
*/
|
|
615
|
+
function ensureThinkingBlockInMessages(messages, _provider) {
|
|
616
|
+
const result = [];
|
|
617
|
+
let i = 0;
|
|
618
|
+
while (i < messages.length) {
|
|
619
|
+
const msg = messages[i];
|
|
620
|
+
const isAI = msg instanceof AIMessage || msg instanceof AIMessageChunk;
|
|
621
|
+
if (!isAI) {
|
|
622
|
+
result.push(msg);
|
|
623
|
+
i++;
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
const aiMsg = msg;
|
|
627
|
+
const hasToolCalls = aiMsg.tool_calls && aiMsg.tool_calls.length > 0;
|
|
628
|
+
const contentIsArray = Array.isArray(aiMsg.content);
|
|
629
|
+
// Check if the message has tool calls or tool_use content
|
|
630
|
+
let hasToolUse = hasToolCalls ?? false;
|
|
631
|
+
let firstContentType;
|
|
632
|
+
if (contentIsArray && aiMsg.content.length > 0) {
|
|
633
|
+
const content = aiMsg.content;
|
|
634
|
+
firstContentType = content[0]?.type;
|
|
635
|
+
hasToolUse =
|
|
636
|
+
hasToolUse ||
|
|
637
|
+
content.some((c) => typeof c === 'object' && c.type === 'tool_use');
|
|
638
|
+
}
|
|
639
|
+
// If message has tool use but no thinking block, convert to buffer string
|
|
640
|
+
if (hasToolUse &&
|
|
641
|
+
firstContentType !== ContentTypes.THINKING &&
|
|
642
|
+
firstContentType !== 'redacted_thinking') {
|
|
643
|
+
// Collect the AI message and any following tool messages
|
|
644
|
+
const toolSequence = [msg];
|
|
645
|
+
let j = i + 1;
|
|
646
|
+
// Look ahead for tool messages that belong to this AI message
|
|
647
|
+
while (j < messages.length && messages[j] instanceof ToolMessage) {
|
|
648
|
+
toolSequence.push(messages[j]);
|
|
649
|
+
j++;
|
|
650
|
+
}
|
|
651
|
+
// Convert the sequence to a buffer string and wrap in a HumanMessage
|
|
652
|
+
// This avoids the thinking block requirement which only applies to AI messages
|
|
653
|
+
const bufferString = getBufferString(toolSequence);
|
|
654
|
+
result.push(new HumanMessage({
|
|
655
|
+
content: `[Previous agent context]\n${bufferString}`,
|
|
656
|
+
}));
|
|
657
|
+
// Skip the messages we've processed
|
|
658
|
+
i = j;
|
|
659
|
+
}
|
|
660
|
+
else {
|
|
661
|
+
// Keep the message as is
|
|
662
|
+
result.push(msg);
|
|
663
|
+
i++;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return result;
|
|
667
|
+
}
|
|
434
668
|
|
|
435
|
-
export { formatAgentMessages, formatFromLangChain, formatLangChainMessages, formatMediaMessage, formatMessage, shiftIndexTokenCountMap };
|
|
669
|
+
export { ensureThinkingBlockInMessages, formatAgentMessages, formatFromLangChain, formatLangChainMessages, formatMediaMessage, formatMessage, labelContentByAgent, shiftIndexTokenCountMap };
|
|
436
670
|
//# sourceMappingURL=format.mjs.map
|