illuma-agents 1.0.8 → 1.0.9

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 (217) hide show
  1. package/LICENSE +1 -5
  2. package/dist/cjs/common/enum.cjs +1 -2
  3. package/dist/cjs/common/enum.cjs.map +1 -1
  4. package/dist/cjs/instrumentation.cjs.map +1 -1
  5. package/dist/cjs/llm/anthropic/types.cjs.map +1 -1
  6. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +79 -2
  7. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  8. package/dist/cjs/llm/anthropic/utils/tools.cjs.map +1 -1
  9. package/dist/cjs/llm/bedrock/index.cjs +99 -0
  10. package/dist/cjs/llm/bedrock/index.cjs.map +1 -0
  11. package/dist/cjs/llm/fake.cjs.map +1 -1
  12. package/dist/cjs/llm/providers.cjs +13 -16
  13. package/dist/cjs/llm/providers.cjs.map +1 -1
  14. package/dist/cjs/llm/text.cjs.map +1 -1
  15. package/dist/cjs/messages/core.cjs +14 -14
  16. package/dist/cjs/messages/core.cjs.map +1 -1
  17. package/dist/cjs/messages/ids.cjs.map +1 -1
  18. package/dist/cjs/messages/prune.cjs.map +1 -1
  19. package/dist/cjs/run.cjs +10 -1
  20. package/dist/cjs/run.cjs.map +1 -1
  21. package/dist/cjs/splitStream.cjs.map +1 -1
  22. package/dist/cjs/stream.cjs +4 -1
  23. package/dist/cjs/stream.cjs.map +1 -1
  24. package/dist/cjs/tools/ToolNode.cjs +10 -1
  25. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  26. package/dist/cjs/tools/handlers.cjs +29 -25
  27. package/dist/cjs/tools/handlers.cjs.map +1 -1
  28. package/dist/cjs/tools/search/anthropic.cjs.map +1 -1
  29. package/dist/cjs/tools/search/content.cjs.map +1 -1
  30. package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
  31. package/dist/cjs/tools/search/format.cjs.map +1 -1
  32. package/dist/cjs/tools/search/highlights.cjs.map +1 -1
  33. package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
  34. package/dist/cjs/tools/search/schema.cjs +25 -25
  35. package/dist/cjs/tools/search/schema.cjs.map +1 -1
  36. package/dist/cjs/tools/search/search.cjs +6 -1
  37. package/dist/cjs/tools/search/search.cjs.map +1 -1
  38. package/dist/cjs/tools/search/serper-scraper.cjs.map +1 -1
  39. package/dist/cjs/tools/search/tool.cjs +162 -35
  40. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  41. package/dist/cjs/tools/search/utils.cjs.map +1 -1
  42. package/dist/cjs/utils/graph.cjs.map +1 -1
  43. package/dist/cjs/utils/llm.cjs +0 -1
  44. package/dist/cjs/utils/llm.cjs.map +1 -1
  45. package/dist/cjs/utils/misc.cjs.map +1 -1
  46. package/dist/cjs/utils/run.cjs.map +1 -1
  47. package/dist/cjs/utils/title.cjs +7 -7
  48. package/dist/cjs/utils/title.cjs.map +1 -1
  49. package/dist/esm/common/enum.mjs +1 -2
  50. package/dist/esm/common/enum.mjs.map +1 -1
  51. package/dist/esm/instrumentation.mjs.map +1 -1
  52. package/dist/esm/llm/anthropic/types.mjs.map +1 -1
  53. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +79 -2
  54. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  55. package/dist/esm/llm/anthropic/utils/tools.mjs.map +1 -1
  56. package/dist/esm/llm/bedrock/index.mjs +97 -0
  57. package/dist/esm/llm/bedrock/index.mjs.map +1 -0
  58. package/dist/esm/llm/fake.mjs.map +1 -1
  59. package/dist/esm/llm/providers.mjs +2 -5
  60. package/dist/esm/llm/providers.mjs.map +1 -1
  61. package/dist/esm/llm/text.mjs.map +1 -1
  62. package/dist/esm/messages/core.mjs +14 -14
  63. package/dist/esm/messages/core.mjs.map +1 -1
  64. package/dist/esm/messages/ids.mjs.map +1 -1
  65. package/dist/esm/messages/prune.mjs.map +1 -1
  66. package/dist/esm/run.mjs +10 -1
  67. package/dist/esm/run.mjs.map +1 -1
  68. package/dist/esm/splitStream.mjs.map +1 -1
  69. package/dist/esm/stream.mjs +4 -1
  70. package/dist/esm/stream.mjs.map +1 -1
  71. package/dist/esm/tools/ToolNode.mjs +10 -1
  72. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  73. package/dist/esm/tools/handlers.mjs +30 -26
  74. package/dist/esm/tools/handlers.mjs.map +1 -1
  75. package/dist/esm/tools/search/anthropic.mjs.map +1 -1
  76. package/dist/esm/tools/search/content.mjs.map +1 -1
  77. package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
  78. package/dist/esm/tools/search/format.mjs.map +1 -1
  79. package/dist/esm/tools/search/highlights.mjs.map +1 -1
  80. package/dist/esm/tools/search/rerankers.mjs.map +1 -1
  81. package/dist/esm/tools/search/schema.mjs +25 -25
  82. package/dist/esm/tools/search/schema.mjs.map +1 -1
  83. package/dist/esm/tools/search/search.mjs +6 -1
  84. package/dist/esm/tools/search/search.mjs.map +1 -1
  85. package/dist/esm/tools/search/serper-scraper.mjs.map +1 -1
  86. package/dist/esm/tools/search/tool.mjs +162 -35
  87. package/dist/esm/tools/search/tool.mjs.map +1 -1
  88. package/dist/esm/tools/search/utils.mjs.map +1 -1
  89. package/dist/esm/utils/graph.mjs.map +1 -1
  90. package/dist/esm/utils/llm.mjs +0 -1
  91. package/dist/esm/utils/llm.mjs.map +1 -1
  92. package/dist/esm/utils/misc.mjs.map +1 -1
  93. package/dist/esm/utils/run.mjs.map +1 -1
  94. package/dist/esm/utils/title.mjs +7 -7
  95. package/dist/esm/utils/title.mjs.map +1 -1
  96. package/dist/types/common/enum.d.ts +1 -2
  97. package/dist/types/llm/bedrock/index.d.ts +36 -0
  98. package/dist/types/tools/search/types.d.ts +2 -0
  99. package/dist/types/types/llm.d.ts +3 -8
  100. package/package.json +15 -11
  101. package/src/common/enum.ts +1 -2
  102. package/src/common/index.ts +1 -1
  103. package/src/instrumentation.ts +22 -22
  104. package/src/llm/anthropic/llm.spec.ts +1442 -1442
  105. package/src/llm/anthropic/types.ts +140 -140
  106. package/src/llm/anthropic/utils/message_inputs.ts +757 -660
  107. package/src/llm/anthropic/utils/output_parsers.ts +133 -133
  108. package/src/llm/anthropic/utils/tools.ts +29 -29
  109. package/src/llm/bedrock/index.ts +128 -0
  110. package/src/llm/fake.ts +133 -133
  111. package/src/llm/google/utils/tools.ts +160 -160
  112. package/src/llm/openai/types.ts +24 -24
  113. package/src/llm/openai/utils/isReasoningModel.test.ts +90 -90
  114. package/src/llm/providers.ts +2 -7
  115. package/src/llm/text.ts +94 -94
  116. package/src/messages/core.ts +463 -463
  117. package/src/messages/formatAgentMessages.tools.test.ts +400 -400
  118. package/src/messages/formatMessage.test.ts +693 -693
  119. package/src/messages/ids.ts +26 -26
  120. package/src/messages/prune.ts +567 -567
  121. package/src/messages/shiftIndexTokenCountMap.test.ts +81 -81
  122. package/src/mockStream.ts +98 -98
  123. package/src/prompts/collab.ts +5 -5
  124. package/src/prompts/index.ts +1 -1
  125. package/src/prompts/taskmanager.ts +61 -61
  126. package/src/run.ts +13 -4
  127. package/src/scripts/ant_web_search_edge_case.ts +162 -0
  128. package/src/scripts/ant_web_search_error_edge_case.ts +148 -0
  129. package/src/scripts/args.ts +48 -48
  130. package/src/scripts/caching.ts +123 -123
  131. package/src/scripts/code_exec_files.ts +193 -193
  132. package/src/scripts/empty_input.ts +137 -137
  133. package/src/scripts/image.ts +178 -178
  134. package/src/scripts/memory.ts +97 -97
  135. package/src/scripts/thinking.ts +149 -149
  136. package/src/specs/anthropic.simple.test.ts +67 -0
  137. package/src/specs/spec.utils.ts +3 -3
  138. package/src/specs/token-distribution-edge-case.test.ts +316 -316
  139. package/src/specs/tool-error.test.ts +193 -193
  140. package/src/splitStream.test.ts +691 -691
  141. package/src/splitStream.ts +234 -234
  142. package/src/stream.test.ts +94 -94
  143. package/src/stream.ts +4 -1
  144. package/src/tools/ToolNode.ts +12 -1
  145. package/src/tools/handlers.ts +32 -28
  146. package/src/tools/search/anthropic.ts +51 -51
  147. package/src/tools/search/content.test.ts +173 -173
  148. package/src/tools/search/content.ts +147 -147
  149. package/src/tools/search/direct-url.test.ts +530 -0
  150. package/src/tools/search/firecrawl.ts +210 -210
  151. package/src/tools/search/format.ts +250 -250
  152. package/src/tools/search/highlights.ts +320 -320
  153. package/src/tools/search/index.ts +2 -2
  154. package/src/tools/search/jina-reranker.test.ts +126 -126
  155. package/src/tools/search/output.md +2775 -2775
  156. package/src/tools/search/rerankers.ts +242 -242
  157. package/src/tools/search/schema.ts +63 -63
  158. package/src/tools/search/search.ts +766 -759
  159. package/src/tools/search/serper-scraper.ts +155 -155
  160. package/src/tools/search/test.html +883 -883
  161. package/src/tools/search/test.md +642 -642
  162. package/src/tools/search/test.ts +159 -159
  163. package/src/tools/search/tool.ts +619 -471
  164. package/src/tools/search/types.ts +689 -687
  165. package/src/tools/search/utils.ts +79 -79
  166. package/src/types/index.ts +6 -6
  167. package/src/types/llm.ts +2 -8
  168. package/src/utils/graph.ts +10 -10
  169. package/src/utils/llm.ts +26 -27
  170. package/src/utils/llmConfig.ts +5 -3
  171. package/src/utils/logging.ts +48 -48
  172. package/src/utils/misc.ts +57 -57
  173. package/src/utils/run.ts +100 -100
  174. package/src/utils/title.ts +165 -165
  175. package/dist/cjs/llm/ollama/index.cjs +0 -70
  176. package/dist/cjs/llm/ollama/index.cjs.map +0 -1
  177. package/dist/cjs/llm/ollama/utils.cjs +0 -158
  178. package/dist/cjs/llm/ollama/utils.cjs.map +0 -1
  179. package/dist/esm/llm/ollama/index.mjs +0 -68
  180. package/dist/esm/llm/ollama/index.mjs.map +0 -1
  181. package/dist/esm/llm/ollama/utils.mjs +0 -155
  182. package/dist/esm/llm/ollama/utils.mjs.map +0 -1
  183. package/dist/types/llm/ollama/index.d.ts +0 -8
  184. package/dist/types/llm/ollama/utils.d.ts +0 -7
  185. package/src/llm/ollama/index.ts +0 -92
  186. package/src/llm/ollama/utils.ts +0 -193
  187. package/src/proto/CollabGraph.ts +0 -269
  188. package/src/proto/TaskManager.ts +0 -243
  189. package/src/proto/collab.ts +0 -200
  190. package/src/proto/collab_design.ts +0 -184
  191. package/src/proto/collab_design_v2.ts +0 -224
  192. package/src/proto/collab_design_v3.ts +0 -255
  193. package/src/proto/collab_design_v4.ts +0 -220
  194. package/src/proto/collab_design_v5.ts +0 -251
  195. package/src/proto/collab_graph.ts +0 -181
  196. package/src/proto/collab_original.ts +0 -123
  197. package/src/proto/example.ts +0 -93
  198. package/src/proto/example_new.ts +0 -68
  199. package/src/proto/example_old.ts +0 -201
  200. package/src/proto/example_test.ts +0 -152
  201. package/src/proto/example_test_anthropic.ts +0 -100
  202. package/src/proto/log_stream.ts +0 -202
  203. package/src/proto/main_collab_community_event.ts +0 -133
  204. package/src/proto/main_collab_design_v2.ts +0 -96
  205. package/src/proto/main_collab_design_v4.ts +0 -100
  206. package/src/proto/main_collab_design_v5.ts +0 -135
  207. package/src/proto/main_collab_global_analysis.ts +0 -122
  208. package/src/proto/main_collab_hackathon_event.ts +0 -153
  209. package/src/proto/main_collab_space_mission.ts +0 -153
  210. package/src/proto/main_philosophy.ts +0 -210
  211. package/src/proto/original_script.ts +0 -126
  212. package/src/proto/standard.ts +0 -100
  213. package/src/proto/stream.ts +0 -56
  214. package/src/proto/tasks.ts +0 -118
  215. package/src/proto/tools/global_analysis_tools.ts +0 -86
  216. package/src/proto/tools/space_mission_tools.ts +0 -60
  217. package/src/proto/vertexai.ts +0 -54
@@ -1,463 +1,463 @@
1
- // src/messages.ts
2
- import {
3
- AIMessageChunk,
4
- HumanMessage,
5
- ToolMessage,
6
- AIMessage,
7
- BaseMessage,
8
- } from '@langchain/core/messages';
9
- import type { ToolCall } from '@langchain/core/messages/tool';
10
- import type * as t from '@/types';
11
- import { Providers } from '@/common';
12
-
13
- export function getConverseOverrideMessage({
14
- userMessage,
15
- lastMessageX,
16
- lastMessageY,
17
- }: {
18
- userMessage: string[];
19
- lastMessageX: AIMessageChunk | null;
20
- lastMessageY: ToolMessage;
21
- }): HumanMessage {
22
- const content = `
23
- User: ${userMessage[1]}
24
-
25
- ---
26
- # YOU HAVE ALREADY RESPONDED TO THE LATEST USER MESSAGE:
27
-
28
- # Observations:
29
- - ${lastMessageX?.content}
30
-
31
- # Tool Calls:
32
- - ${lastMessageX?.tool_calls?.join('\n- ')}
33
-
34
- # Tool Responses:
35
- - ${lastMessageY.content}
36
- `;
37
-
38
- return new HumanMessage(content);
39
- }
40
-
41
- const _allowedTypes = ['image_url', 'text', 'tool_use', 'tool_result'];
42
- const allowedTypesByProvider: Record<string, string[]> = {
43
- default: _allowedTypes,
44
- [Providers.ANTHROPIC]: [..._allowedTypes, 'thinking'],
45
- [Providers.BEDROCK]: [..._allowedTypes, 'reasoning_content'],
46
- [Providers.OPENAI]: _allowedTypes,
47
- };
48
-
49
- const modifyContent = ({
50
- provider,
51
- messageType,
52
- content,
53
- }: {
54
- provider: Providers;
55
- messageType: string;
56
- content: t.ExtendedMessageContent[];
57
- }): t.ExtendedMessageContent[] => {
58
- const allowedTypes =
59
- allowedTypesByProvider[provider] ?? allowedTypesByProvider.default;
60
- return content.map((item) => {
61
- if (
62
- item &&
63
- typeof item === 'object' &&
64
- 'type' in item &&
65
- item.type != null &&
66
- item.type
67
- ) {
68
- let newType = item.type;
69
- if (newType.endsWith('_delta')) {
70
- newType = newType.replace('_delta', '');
71
- }
72
- if (!allowedTypes.includes(newType)) {
73
- newType = 'text';
74
- }
75
-
76
- /* Handle the edge case for empty object 'tool_use' input in AI messages */
77
- if (
78
- messageType === 'ai' &&
79
- newType === 'tool_use' &&
80
- 'input' in item &&
81
- item.input === ''
82
- ) {
83
- return { ...item, type: newType, input: '{}' };
84
- }
85
-
86
- return { ...item, type: newType };
87
- }
88
- return item;
89
- });
90
- };
91
-
92
- type ContentBlock =
93
- | Partial<t.BedrockReasoningContentText>
94
- | t.MessageDeltaUpdate;
95
-
96
- function reduceBlocks(blocks: ContentBlock[]): ContentBlock[] {
97
- const reduced: ContentBlock[] = [];
98
-
99
- for (const block of blocks) {
100
- const lastBlock = reduced[reduced.length - 1] as ContentBlock | undefined;
101
-
102
- // Merge consecutive 'reasoning_content'
103
- if (
104
- block.type === 'reasoning_content' &&
105
- lastBlock?.type === 'reasoning_content'
106
- ) {
107
- // append text if exists
108
- if (block.reasoningText?.text != null && block.reasoningText.text) {
109
- (
110
- lastBlock.reasoningText as t.BedrockReasoningContentText['reasoningText']
111
- ).text =
112
- (lastBlock.reasoningText?.text ?? '') + block.reasoningText.text;
113
- }
114
- // preserve the signature if exists
115
- if (
116
- block.reasoningText?.signature != null &&
117
- block.reasoningText.signature
118
- ) {
119
- (
120
- lastBlock.reasoningText as t.BedrockReasoningContentText['reasoningText']
121
- ).signature = block.reasoningText.signature;
122
- }
123
- }
124
- // Merge consecutive 'text'
125
- else if (block.type === 'text' && lastBlock?.type === 'text') {
126
- lastBlock.text += block.text;
127
- }
128
- // add a new block as it's a different type or first element
129
- else {
130
- // deep copy to avoid mutation of original
131
- reduced.push(JSON.parse(JSON.stringify(block)));
132
- }
133
- }
134
-
135
- return reduced;
136
- }
137
-
138
- export function modifyDeltaProperties(
139
- provider: Providers,
140
- obj?: AIMessageChunk
141
- ): AIMessageChunk | undefined {
142
- if (!obj || typeof obj !== 'object') return obj;
143
-
144
- const messageType = (obj as Partial<AIMessageChunk>)._getType
145
- ? obj._getType()
146
- : '';
147
-
148
- if (provider === Providers.BEDROCK && Array.isArray(obj.content)) {
149
- obj.content = reduceBlocks(obj.content as ContentBlock[]);
150
- }
151
- if (Array.isArray(obj.content)) {
152
- obj.content = modifyContent({
153
- provider,
154
- messageType,
155
- content: obj.content,
156
- });
157
- }
158
- if (
159
- (obj as Partial<AIMessageChunk>).lc_kwargs &&
160
- Array.isArray(obj.lc_kwargs.content)
161
- ) {
162
- obj.lc_kwargs.content = modifyContent({
163
- provider,
164
- messageType,
165
- content: obj.lc_kwargs.content,
166
- });
167
- }
168
- return obj;
169
- }
170
-
171
- export function formatAnthropicMessage(message: AIMessageChunk): AIMessage {
172
- if (!message.tool_calls || message.tool_calls.length === 0) {
173
- return new AIMessage({ content: message.content });
174
- }
175
-
176
- const toolCallMap = new Map(message.tool_calls.map((tc) => [tc.id, tc]));
177
- let formattedContent: string | t.ExtendedMessageContent[];
178
-
179
- if (Array.isArray(message.content)) {
180
- formattedContent = message.content.reduce<t.ExtendedMessageContent[]>(
181
- (acc, item) => {
182
- if (typeof item === 'object') {
183
- const extendedItem = item as t.ExtendedMessageContent;
184
- if (
185
- extendedItem.type === 'text' &&
186
- extendedItem.text != null &&
187
- extendedItem.text
188
- ) {
189
- acc.push({ type: 'text', text: extendedItem.text });
190
- } else if (
191
- extendedItem.type === 'tool_use' &&
192
- extendedItem.id != null &&
193
- extendedItem.id
194
- ) {
195
- const toolCall = toolCallMap.get(extendedItem.id);
196
- if (toolCall) {
197
- acc.push({
198
- type: 'tool_use',
199
- id: extendedItem.id,
200
- name: toolCall.name,
201
- input: toolCall.args as unknown as string,
202
- });
203
- }
204
- } else if (
205
- 'input' in extendedItem &&
206
- extendedItem.input != null &&
207
- extendedItem.input
208
- ) {
209
- try {
210
- const parsedInput = JSON.parse(extendedItem.input);
211
- const toolCall = message.tool_calls?.find(
212
- (tc) => tc.args.input === parsedInput.input
213
- );
214
- if (toolCall) {
215
- acc.push({
216
- type: 'tool_use',
217
- id: toolCall.id,
218
- name: toolCall.name,
219
- input: toolCall.args as unknown as string,
220
- });
221
- }
222
- } catch {
223
- if (extendedItem.input) {
224
- acc.push({ type: 'text', text: extendedItem.input });
225
- }
226
- }
227
- }
228
- } else if (typeof item === 'string') {
229
- acc.push({ type: 'text', text: item });
230
- }
231
- return acc;
232
- },
233
- []
234
- );
235
- } else if (typeof message.content === 'string') {
236
- formattedContent = message.content;
237
- } else {
238
- formattedContent = [];
239
- }
240
-
241
- // const formattedToolCalls: ToolCall[] = message.tool_calls.map(toolCall => ({
242
- // id: toolCall.id ?? '',
243
- // name: toolCall.name,
244
- // args: toolCall.args,
245
- // type: 'tool_call',
246
- // }));
247
-
248
- const formattedToolCalls: t.AgentToolCall[] = message.tool_calls.map(
249
- (toolCall) => ({
250
- id: toolCall.id ?? '',
251
- type: 'function',
252
- function: {
253
- name: toolCall.name,
254
- arguments: toolCall.args,
255
- },
256
- })
257
- );
258
-
259
- return new AIMessage({
260
- content: formattedContent,
261
- tool_calls: formattedToolCalls as ToolCall[],
262
- additional_kwargs: {
263
- ...message.additional_kwargs,
264
- },
265
- });
266
- }
267
-
268
- export function convertMessagesToContent(
269
- messages: BaseMessage[]
270
- ): t.MessageContentComplex[] {
271
- const processedContent: t.MessageContentComplex[] = [];
272
-
273
- const addContentPart = (message: BaseMessage | null): void => {
274
- const content =
275
- message?.lc_kwargs.content != null
276
- ? message.lc_kwargs.content
277
- : message?.content;
278
- if (content === undefined) {
279
- return;
280
- }
281
- if (typeof content === 'string') {
282
- processedContent.push({
283
- type: 'text',
284
- text: content,
285
- });
286
- } else if (Array.isArray(content)) {
287
- const filteredContent = content.filter(
288
- (item) => item != null && item.type !== 'tool_use'
289
- );
290
- processedContent.push(...filteredContent);
291
- }
292
- };
293
-
294
- let currentAIMessageIndex = -1;
295
- const toolCallMap = new Map<string, t.CustomToolCall>();
296
-
297
- for (let i = 0; i < messages.length; i++) {
298
- const message = messages[i] as BaseMessage | null;
299
- const messageType = message?._getType();
300
-
301
- if (
302
- messageType === 'ai' &&
303
- ((message as AIMessage).tool_calls?.length ?? 0) > 0
304
- ) {
305
- const tool_calls = (message as AIMessage).tool_calls || [];
306
- for (const tool_call of tool_calls) {
307
- if (tool_call.id == null || !tool_call.id) {
308
- continue;
309
- }
310
-
311
- toolCallMap.set(tool_call.id, tool_call);
312
- }
313
-
314
- addContentPart(message);
315
- currentAIMessageIndex = processedContent.length - 1;
316
- continue;
317
- } else if (
318
- messageType === 'tool' &&
319
- (message as ToolMessage).tool_call_id
320
- ) {
321
- const id = (message as ToolMessage).tool_call_id;
322
- const output = (message as ToolMessage).content;
323
- const tool_call = toolCallMap.get(id);
324
- if (currentAIMessageIndex === -1) {
325
- processedContent.push({ type: 'text', text: '' });
326
- currentAIMessageIndex = processedContent.length - 1;
327
- }
328
- const contentPart = processedContent[currentAIMessageIndex];
329
- processedContent.push({
330
- type: 'tool_call',
331
- tool_call: Object.assign({}, tool_call, { output }),
332
- });
333
- const tool_call_ids = contentPart.tool_call_ids || [];
334
- tool_call_ids.push(id);
335
- contentPart.tool_call_ids = tool_call_ids;
336
- continue;
337
- } else if (messageType !== 'ai') {
338
- continue;
339
- }
340
-
341
- addContentPart(message);
342
- }
343
-
344
- return processedContent;
345
- }
346
-
347
- export function formatAnthropicArtifactContent(messages: BaseMessage[]): void {
348
- const lastMessage = messages[messages.length - 1];
349
- if (!(lastMessage instanceof ToolMessage)) return;
350
-
351
- // Find the latest AIMessage with tool_calls that this tool message belongs to
352
- const latestAIParentIndex = findLastIndex(
353
- messages,
354
- (msg) =>
355
- (msg instanceof AIMessageChunk &&
356
- (msg.tool_calls?.length ?? 0) > 0 &&
357
- msg.tool_calls?.some((tc) => tc.id === lastMessage.tool_call_id)) ??
358
- false
359
- );
360
-
361
- if (latestAIParentIndex === -1) return;
362
-
363
- // Check if any tool message after the AI message has array artifact content
364
- const hasArtifactContent = messages.some(
365
- (msg, i) =>
366
- i > latestAIParentIndex &&
367
- msg instanceof ToolMessage &&
368
- msg.artifact != null &&
369
- msg.artifact?.content != null &&
370
- Array.isArray(msg.artifact.content)
371
- );
372
-
373
- if (!hasArtifactContent) return;
374
-
375
- const message = messages[latestAIParentIndex] as AIMessageChunk;
376
- const toolCallIds = message.tool_calls?.map((tc) => tc.id) ?? [];
377
-
378
- for (let j = latestAIParentIndex + 1; j < messages.length; j++) {
379
- const msg = messages[j];
380
- if (
381
- msg instanceof ToolMessage &&
382
- toolCallIds.includes(msg.tool_call_id) &&
383
- msg.artifact != null &&
384
- Array.isArray(msg.artifact?.content) &&
385
- Array.isArray(msg.content)
386
- ) {
387
- msg.content = msg.content.concat(msg.artifact.content);
388
- }
389
- }
390
- }
391
-
392
- export function formatArtifactPayload(messages: BaseMessage[]): void {
393
- const lastMessageY = messages[messages.length - 1];
394
- if (!(lastMessageY instanceof ToolMessage)) return;
395
-
396
- // Find the latest AIMessage with tool_calls that this tool message belongs to
397
- const latestAIParentIndex = findLastIndex(
398
- messages,
399
- (msg) =>
400
- (msg instanceof AIMessageChunk &&
401
- (msg.tool_calls?.length ?? 0) > 0 &&
402
- msg.tool_calls?.some((tc) => tc.id === lastMessageY.tool_call_id)) ??
403
- false
404
- );
405
-
406
- if (latestAIParentIndex === -1) return;
407
-
408
- // Check if any tool message after the AI message has array artifact content
409
- const hasArtifactContent = messages.some(
410
- (msg, i) =>
411
- i > latestAIParentIndex &&
412
- msg instanceof ToolMessage &&
413
- msg.artifact != null &&
414
- msg.artifact?.content != null &&
415
- Array.isArray(msg.artifact.content)
416
- );
417
-
418
- if (!hasArtifactContent) return;
419
-
420
- // Collect all relevant tool messages and their artifacts
421
- const relevantMessages = messages
422
- .slice(latestAIParentIndex + 1)
423
- .filter((msg) => msg instanceof ToolMessage) as ToolMessage[];
424
-
425
- // Aggregate all content and artifacts
426
- const aggregatedContent: t.MessageContentComplex[] = [];
427
-
428
- relevantMessages.forEach((msg) => {
429
- if (!Array.isArray(msg.artifact?.content)) {
430
- return;
431
- }
432
- let currentContent = msg.content;
433
- if (!Array.isArray(currentContent)) {
434
- currentContent = [
435
- {
436
- type: 'text',
437
- text: msg.content,
438
- },
439
- ];
440
- }
441
- aggregatedContent.push(...currentContent);
442
- msg.content =
443
- 'Tool response is included in the next message as a Human message';
444
- aggregatedContent.push(...msg.artifact.content);
445
- });
446
-
447
- // Add single HumanMessage with all aggregated content
448
- if (aggregatedContent.length > 0) {
449
- messages.push(new HumanMessage({ content: aggregatedContent }));
450
- }
451
- }
452
-
453
- export function findLastIndex<T>(
454
- array: T[],
455
- predicate: (value: T) => boolean
456
- ): number {
457
- for (let i = array.length - 1; i >= 0; i--) {
458
- if (predicate(array[i])) {
459
- return i;
460
- }
461
- }
462
- return -1;
463
- }
1
+ // src/messages.ts
2
+ import {
3
+ AIMessageChunk,
4
+ HumanMessage,
5
+ ToolMessage,
6
+ AIMessage,
7
+ BaseMessage,
8
+ } from '@langchain/core/messages';
9
+ import type { ToolCall } from '@langchain/core/messages/tool';
10
+ import type * as t from '@/types';
11
+ import { Providers } from '@/common';
12
+
13
+ export function getConverseOverrideMessage({
14
+ userMessage,
15
+ lastMessageX,
16
+ lastMessageY,
17
+ }: {
18
+ userMessage: string[];
19
+ lastMessageX: AIMessageChunk | null;
20
+ lastMessageY: ToolMessage;
21
+ }): HumanMessage {
22
+ const content = `
23
+ User: ${userMessage[1]}
24
+
25
+ ---
26
+ # YOU HAVE ALREADY RESPONDED TO THE LATEST USER MESSAGE:
27
+
28
+ # Observations:
29
+ - ${lastMessageX?.content}
30
+
31
+ # Tool Calls:
32
+ - ${lastMessageX?.tool_calls?.join('\n- ')}
33
+
34
+ # Tool Responses:
35
+ - ${lastMessageY.content}
36
+ `;
37
+
38
+ return new HumanMessage(content);
39
+ }
40
+
41
+ const _allowedTypes = ['image_url', 'text', 'tool_use', 'tool_result'];
42
+ const allowedTypesByProvider: Record<string, string[]> = {
43
+ default: _allowedTypes,
44
+ [Providers.ANTHROPIC]: [..._allowedTypes, 'thinking'],
45
+ [Providers.BEDROCK]: [..._allowedTypes, 'reasoning_content'],
46
+ [Providers.OPENAI]: _allowedTypes,
47
+ };
48
+
49
+ const modifyContent = ({
50
+ provider,
51
+ messageType,
52
+ content,
53
+ }: {
54
+ provider: Providers;
55
+ messageType: string;
56
+ content: t.ExtendedMessageContent[];
57
+ }): t.ExtendedMessageContent[] => {
58
+ const allowedTypes =
59
+ allowedTypesByProvider[provider] ?? allowedTypesByProvider.default;
60
+ return content.map((item) => {
61
+ if (
62
+ item &&
63
+ typeof item === 'object' &&
64
+ 'type' in item &&
65
+ item.type != null &&
66
+ item.type
67
+ ) {
68
+ let newType = item.type;
69
+ if (newType.endsWith('_delta')) {
70
+ newType = newType.replace('_delta', '');
71
+ }
72
+ if (!allowedTypes.includes(newType)) {
73
+ newType = 'text';
74
+ }
75
+
76
+ /* Handle the edge case for empty object 'tool_use' input in AI messages */
77
+ if (
78
+ messageType === 'ai' &&
79
+ newType === 'tool_use' &&
80
+ 'input' in item &&
81
+ item.input === ''
82
+ ) {
83
+ return { ...item, type: newType, input: '{}' };
84
+ }
85
+
86
+ return { ...item, type: newType };
87
+ }
88
+ return item;
89
+ });
90
+ };
91
+
92
+ type ContentBlock =
93
+ | Partial<t.BedrockReasoningContentText>
94
+ | t.MessageDeltaUpdate;
95
+
96
+ function reduceBlocks(blocks: ContentBlock[]): ContentBlock[] {
97
+ const reduced: ContentBlock[] = [];
98
+
99
+ for (const block of blocks) {
100
+ const lastBlock = reduced[reduced.length - 1] as ContentBlock | undefined;
101
+
102
+ // Merge consecutive 'reasoning_content'
103
+ if (
104
+ block.type === 'reasoning_content' &&
105
+ lastBlock?.type === 'reasoning_content'
106
+ ) {
107
+ // append text if exists
108
+ if (block.reasoningText?.text != null && block.reasoningText.text) {
109
+ (
110
+ lastBlock.reasoningText as t.BedrockReasoningContentText['reasoningText']
111
+ ).text =
112
+ (lastBlock.reasoningText?.text ?? '') + block.reasoningText.text;
113
+ }
114
+ // preserve the signature if exists
115
+ if (
116
+ block.reasoningText?.signature != null &&
117
+ block.reasoningText.signature
118
+ ) {
119
+ (
120
+ lastBlock.reasoningText as t.BedrockReasoningContentText['reasoningText']
121
+ ).signature = block.reasoningText.signature;
122
+ }
123
+ }
124
+ // Merge consecutive 'text'
125
+ else if (block.type === 'text' && lastBlock?.type === 'text') {
126
+ lastBlock.text += block.text;
127
+ }
128
+ // add a new block as it's a different type or first element
129
+ else {
130
+ // deep copy to avoid mutation of original
131
+ reduced.push(JSON.parse(JSON.stringify(block)));
132
+ }
133
+ }
134
+
135
+ return reduced;
136
+ }
137
+
138
+ export function modifyDeltaProperties(
139
+ provider: Providers,
140
+ obj?: AIMessageChunk
141
+ ): AIMessageChunk | undefined {
142
+ if (!obj || typeof obj !== 'object') return obj;
143
+
144
+ const messageType = (obj as Partial<AIMessageChunk>)._getType
145
+ ? obj._getType()
146
+ : '';
147
+
148
+ if (provider === Providers.BEDROCK && Array.isArray(obj.content)) {
149
+ obj.content = reduceBlocks(obj.content as ContentBlock[]);
150
+ }
151
+ if (Array.isArray(obj.content)) {
152
+ obj.content = modifyContent({
153
+ provider,
154
+ messageType,
155
+ content: obj.content,
156
+ });
157
+ }
158
+ if (
159
+ (obj as Partial<AIMessageChunk>).lc_kwargs &&
160
+ Array.isArray(obj.lc_kwargs.content)
161
+ ) {
162
+ obj.lc_kwargs.content = modifyContent({
163
+ provider,
164
+ messageType,
165
+ content: obj.lc_kwargs.content,
166
+ });
167
+ }
168
+ return obj;
169
+ }
170
+
171
+ export function formatAnthropicMessage(message: AIMessageChunk): AIMessage {
172
+ if (!message.tool_calls || message.tool_calls.length === 0) {
173
+ return new AIMessage({ content: message.content });
174
+ }
175
+
176
+ const toolCallMap = new Map(message.tool_calls.map((tc) => [tc.id, tc]));
177
+ let formattedContent: string | t.ExtendedMessageContent[];
178
+
179
+ if (Array.isArray(message.content)) {
180
+ formattedContent = message.content.reduce<t.ExtendedMessageContent[]>(
181
+ (acc, item) => {
182
+ if (typeof item === 'object') {
183
+ const extendedItem = item as t.ExtendedMessageContent;
184
+ if (
185
+ extendedItem.type === 'text' &&
186
+ extendedItem.text != null &&
187
+ extendedItem.text
188
+ ) {
189
+ acc.push({ type: 'text', text: extendedItem.text });
190
+ } else if (
191
+ extendedItem.type === 'tool_use' &&
192
+ extendedItem.id != null &&
193
+ extendedItem.id
194
+ ) {
195
+ const toolCall = toolCallMap.get(extendedItem.id);
196
+ if (toolCall) {
197
+ acc.push({
198
+ type: 'tool_use',
199
+ id: extendedItem.id,
200
+ name: toolCall.name,
201
+ input: toolCall.args as unknown as string,
202
+ });
203
+ }
204
+ } else if (
205
+ 'input' in extendedItem &&
206
+ extendedItem.input != null &&
207
+ extendedItem.input
208
+ ) {
209
+ try {
210
+ const parsedInput = JSON.parse(extendedItem.input);
211
+ const toolCall = message.tool_calls?.find(
212
+ (tc) => tc.args.input === parsedInput.input
213
+ );
214
+ if (toolCall) {
215
+ acc.push({
216
+ type: 'tool_use',
217
+ id: toolCall.id,
218
+ name: toolCall.name,
219
+ input: toolCall.args as unknown as string,
220
+ });
221
+ }
222
+ } catch {
223
+ if (extendedItem.input) {
224
+ acc.push({ type: 'text', text: extendedItem.input });
225
+ }
226
+ }
227
+ }
228
+ } else if (typeof item === 'string') {
229
+ acc.push({ type: 'text', text: item });
230
+ }
231
+ return acc;
232
+ },
233
+ []
234
+ );
235
+ } else if (typeof message.content === 'string') {
236
+ formattedContent = message.content;
237
+ } else {
238
+ formattedContent = [];
239
+ }
240
+
241
+ // const formattedToolCalls: ToolCall[] = message.tool_calls.map(toolCall => ({
242
+ // id: toolCall.id ?? '',
243
+ // name: toolCall.name,
244
+ // args: toolCall.args,
245
+ // type: 'tool_call',
246
+ // }));
247
+
248
+ const formattedToolCalls: t.AgentToolCall[] = message.tool_calls.map(
249
+ (toolCall) => ({
250
+ id: toolCall.id ?? '',
251
+ type: 'function',
252
+ function: {
253
+ name: toolCall.name,
254
+ arguments: toolCall.args,
255
+ },
256
+ })
257
+ );
258
+
259
+ return new AIMessage({
260
+ content: formattedContent,
261
+ tool_calls: formattedToolCalls as ToolCall[],
262
+ additional_kwargs: {
263
+ ...message.additional_kwargs,
264
+ },
265
+ });
266
+ }
267
+
268
+ export function convertMessagesToContent(
269
+ messages: BaseMessage[]
270
+ ): t.MessageContentComplex[] {
271
+ const processedContent: t.MessageContentComplex[] = [];
272
+
273
+ const addContentPart = (message: BaseMessage | null): void => {
274
+ const content =
275
+ message?.lc_kwargs.content != null
276
+ ? message.lc_kwargs.content
277
+ : message?.content;
278
+ if (content === undefined) {
279
+ return;
280
+ }
281
+ if (typeof content === 'string') {
282
+ processedContent.push({
283
+ type: 'text',
284
+ text: content,
285
+ });
286
+ } else if (Array.isArray(content)) {
287
+ const filteredContent = content.filter(
288
+ (item) => item != null && item.type !== 'tool_use'
289
+ );
290
+ processedContent.push(...filteredContent);
291
+ }
292
+ };
293
+
294
+ let currentAIMessageIndex = -1;
295
+ const toolCallMap = new Map<string, t.CustomToolCall>();
296
+
297
+ for (let i = 0; i < messages.length; i++) {
298
+ const message = messages[i] as BaseMessage | null;
299
+ const messageType = message?._getType();
300
+
301
+ if (
302
+ messageType === 'ai' &&
303
+ ((message as AIMessage).tool_calls?.length ?? 0) > 0
304
+ ) {
305
+ const tool_calls = (message as AIMessage).tool_calls || [];
306
+ for (const tool_call of tool_calls) {
307
+ if (tool_call.id == null || !tool_call.id) {
308
+ continue;
309
+ }
310
+
311
+ toolCallMap.set(tool_call.id, tool_call);
312
+ }
313
+
314
+ addContentPart(message);
315
+ currentAIMessageIndex = processedContent.length - 1;
316
+ continue;
317
+ } else if (
318
+ messageType === 'tool' &&
319
+ (message as ToolMessage).tool_call_id
320
+ ) {
321
+ const id = (message as ToolMessage).tool_call_id;
322
+ const output = (message as ToolMessage).content;
323
+ const tool_call = toolCallMap.get(id);
324
+ if (currentAIMessageIndex === -1) {
325
+ processedContent.push({ type: 'text', text: '' });
326
+ currentAIMessageIndex = processedContent.length - 1;
327
+ }
328
+ const contentPart = processedContent[currentAIMessageIndex];
329
+ processedContent.push({
330
+ type: 'tool_call',
331
+ tool_call: Object.assign({}, tool_call, { output }),
332
+ });
333
+ const tool_call_ids = contentPart.tool_call_ids || [];
334
+ tool_call_ids.push(id);
335
+ contentPart.tool_call_ids = tool_call_ids;
336
+ continue;
337
+ } else if (messageType !== 'ai') {
338
+ continue;
339
+ }
340
+
341
+ addContentPart(message);
342
+ }
343
+
344
+ return processedContent;
345
+ }
346
+
347
+ export function formatAnthropicArtifactContent(messages: BaseMessage[]): void {
348
+ const lastMessage = messages[messages.length - 1];
349
+ if (!(lastMessage instanceof ToolMessage)) return;
350
+
351
+ // Find the latest AIMessage with tool_calls that this tool message belongs to
352
+ const latestAIParentIndex = findLastIndex(
353
+ messages,
354
+ (msg) =>
355
+ (msg instanceof AIMessageChunk &&
356
+ (msg.tool_calls?.length ?? 0) > 0 &&
357
+ msg.tool_calls?.some((tc) => tc.id === lastMessage.tool_call_id)) ??
358
+ false
359
+ );
360
+
361
+ if (latestAIParentIndex === -1) return;
362
+
363
+ // Check if any tool message after the AI message has array artifact content
364
+ const hasArtifactContent = messages.some(
365
+ (msg, i) =>
366
+ i > latestAIParentIndex &&
367
+ msg instanceof ToolMessage &&
368
+ msg.artifact != null &&
369
+ msg.artifact?.content != null &&
370
+ Array.isArray(msg.artifact.content)
371
+ );
372
+
373
+ if (!hasArtifactContent) return;
374
+
375
+ const message = messages[latestAIParentIndex] as AIMessageChunk;
376
+ const toolCallIds = message.tool_calls?.map((tc) => tc.id) ?? [];
377
+
378
+ for (let j = latestAIParentIndex + 1; j < messages.length; j++) {
379
+ const msg = messages[j];
380
+ if (
381
+ msg instanceof ToolMessage &&
382
+ toolCallIds.includes(msg.tool_call_id) &&
383
+ msg.artifact != null &&
384
+ Array.isArray(msg.artifact?.content) &&
385
+ Array.isArray(msg.content)
386
+ ) {
387
+ msg.content = msg.content.concat(msg.artifact.content);
388
+ }
389
+ }
390
+ }
391
+
392
+ export function formatArtifactPayload(messages: BaseMessage[]): void {
393
+ const lastMessageY = messages[messages.length - 1];
394
+ if (!(lastMessageY instanceof ToolMessage)) return;
395
+
396
+ // Find the latest AIMessage with tool_calls that this tool message belongs to
397
+ const latestAIParentIndex = findLastIndex(
398
+ messages,
399
+ (msg) =>
400
+ (msg instanceof AIMessageChunk &&
401
+ (msg.tool_calls?.length ?? 0) > 0 &&
402
+ msg.tool_calls?.some((tc) => tc.id === lastMessageY.tool_call_id)) ??
403
+ false
404
+ );
405
+
406
+ if (latestAIParentIndex === -1) return;
407
+
408
+ // Check if any tool message after the AI message has array artifact content
409
+ const hasArtifactContent = messages.some(
410
+ (msg, i) =>
411
+ i > latestAIParentIndex &&
412
+ msg instanceof ToolMessage &&
413
+ msg.artifact != null &&
414
+ msg.artifact?.content != null &&
415
+ Array.isArray(msg.artifact.content)
416
+ );
417
+
418
+ if (!hasArtifactContent) return;
419
+
420
+ // Collect all relevant tool messages and their artifacts
421
+ const relevantMessages = messages
422
+ .slice(latestAIParentIndex + 1)
423
+ .filter((msg) => msg instanceof ToolMessage) as ToolMessage[];
424
+
425
+ // Aggregate all content and artifacts
426
+ const aggregatedContent: t.MessageContentComplex[] = [];
427
+
428
+ relevantMessages.forEach((msg) => {
429
+ if (!Array.isArray(msg.artifact?.content)) {
430
+ return;
431
+ }
432
+ let currentContent = msg.content;
433
+ if (!Array.isArray(currentContent)) {
434
+ currentContent = [
435
+ {
436
+ type: 'text',
437
+ text: msg.content,
438
+ },
439
+ ];
440
+ }
441
+ aggregatedContent.push(...currentContent);
442
+ msg.content =
443
+ 'Tool response is included in the next message as a Human message';
444
+ aggregatedContent.push(...msg.artifact.content);
445
+ });
446
+
447
+ // Add single HumanMessage with all aggregated content
448
+ if (aggregatedContent.length > 0) {
449
+ messages.push(new HumanMessage({ content: aggregatedContent }));
450
+ }
451
+ }
452
+
453
+ export function findLastIndex<T>(
454
+ array: T[],
455
+ predicate: (value: T) => boolean
456
+ ): number {
457
+ for (let i = array.length - 1; i >= 0; i--) {
458
+ if (predicate(array[i])) {
459
+ return i;
460
+ }
461
+ }
462
+ return -1;
463
+ }