illuma-agents 1.0.2 → 1.0.3
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/LICENSE +25 -21
- package/dist/cjs/agents/AgentContext.cjs +222 -0
- package/dist/cjs/agents/AgentContext.cjs.map +1 -0
- package/dist/cjs/common/enum.cjs +7 -6
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/events.cjs +7 -5
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +328 -207
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +507 -0
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -0
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/google/index.cjs.map +1 -1
- package/dist/cjs/llm/ollama/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +35 -0
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs +3 -1
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/llm/providers.cjs +0 -2
- package/dist/cjs/llm/providers.cjs.map +1 -1
- package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +12 -1
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +123 -0
- package/dist/cjs/messages/cache.cjs.map +1 -0
- package/dist/cjs/messages/content.cjs +53 -0
- package/dist/cjs/messages/content.cjs.map +1 -0
- package/dist/cjs/messages/format.cjs +17 -29
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/run.cjs +119 -74
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +77 -73
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/Calculator.cjs +45 -0
- package/dist/cjs/tools/Calculator.cjs.map +1 -0
- package/dist/cjs/tools/CodeExecutor.cjs +22 -22
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +5 -3
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs +20 -20
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/utils/events.cjs +31 -0
- package/dist/cjs/utils/events.cjs.map +1 -0
- package/dist/cjs/utils/handlers.cjs +70 -0
- package/dist/cjs/utils/handlers.cjs.map +1 -0
- package/dist/cjs/utils/tokens.cjs +54 -7
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +220 -0
- package/dist/esm/agents/AgentContext.mjs.map +1 -0
- package/dist/esm/common/enum.mjs +7 -6
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/events.mjs +7 -5
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +330 -209
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +505 -0
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -0
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/google/index.mjs.map +1 -1
- package/dist/esm/llm/ollama/index.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +35 -0
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs +3 -1
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/llm/providers.mjs +0 -2
- package/dist/esm/llm/providers.mjs.map +1 -1
- package/dist/esm/llm/vertexai/index.mjs.map +1 -1
- package/dist/esm/main.mjs +7 -2
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/messages/cache.mjs +120 -0
- package/dist/esm/messages/cache.mjs.map +1 -0
- package/dist/esm/messages/content.mjs +51 -0
- package/dist/esm/messages/content.mjs.map +1 -0
- package/dist/esm/messages/format.mjs +18 -29
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/run.mjs +119 -74
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +77 -73
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/Calculator.mjs +24 -0
- package/dist/esm/tools/Calculator.mjs.map +1 -0
- package/dist/esm/tools/CodeExecutor.mjs +22 -22
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +5 -3
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs +20 -20
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/utils/events.mjs +29 -0
- package/dist/esm/utils/events.mjs.map +1 -0
- package/dist/esm/utils/handlers.mjs +68 -0
- package/dist/esm/utils/handlers.mjs.map +1 -0
- package/dist/esm/utils/tokens.mjs +54 -8
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +94 -0
- package/dist/types/common/enum.d.ts +9 -7
- package/dist/types/events.d.ts +3 -3
- package/dist/types/graphs/Graph.d.ts +60 -66
- package/dist/types/graphs/MultiAgentGraph.d.ts +47 -0
- package/dist/types/graphs/index.d.ts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/llm/openai/index.d.ts +10 -0
- package/dist/types/messages/cache.d.ts +20 -0
- package/dist/types/messages/content.d.ts +7 -0
- package/dist/types/messages/format.d.ts +1 -7
- package/dist/types/messages/index.d.ts +2 -0
- package/dist/types/messages/reducer.d.ts +9 -0
- package/dist/types/run.d.ts +16 -10
- package/dist/types/stream.d.ts +4 -3
- package/dist/types/tools/Calculator.d.ts +8 -0
- package/dist/types/tools/ToolNode.d.ts +1 -1
- package/dist/types/tools/handlers.d.ts +9 -7
- package/dist/types/tools/search/tool.d.ts +4 -4
- package/dist/types/types/graph.d.ts +124 -11
- package/dist/types/types/llm.d.ts +13 -9
- package/dist/types/types/messages.d.ts +4 -0
- package/dist/types/types/run.d.ts +46 -8
- package/dist/types/types/stream.d.ts +3 -2
- package/dist/types/utils/events.d.ts +6 -0
- package/dist/types/utils/handlers.d.ts +34 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/tokens.d.ts +24 -0
- package/package.json +162 -145
- package/src/agents/AgentContext.ts +323 -0
- package/src/common/enum.ts +177 -176
- package/src/events.ts +197 -191
- package/src/graphs/Graph.ts +1058 -846
- package/src/graphs/MultiAgentGraph.ts +598 -0
- package/src/graphs/index.ts +2 -1
- package/src/index.ts +25 -24
- package/src/llm/anthropic/index.ts +413 -413
- package/src/llm/google/index.ts +222 -222
- package/src/llm/google/utils/zod_to_genai_parameters.ts +86 -88
- package/src/llm/ollama/index.ts +92 -92
- package/src/llm/openai/index.ts +894 -853
- package/src/llm/openai/utils/index.ts +920 -918
- package/src/llm/openrouter/index.ts +60 -60
- package/src/llm/providers.ts +55 -57
- package/src/llm/vertexai/index.ts +360 -360
- package/src/messages/cache.test.ts +461 -0
- package/src/messages/cache.ts +151 -0
- package/src/messages/content.test.ts +362 -0
- package/src/messages/content.ts +63 -0
- package/src/messages/format.ts +611 -625
- package/src/messages/formatAgentMessages.test.ts +1144 -917
- package/src/messages/index.ts +6 -4
- package/src/messages/reducer.ts +80 -0
- package/src/run.ts +447 -381
- package/src/scripts/abort.ts +157 -138
- package/src/scripts/ant_web_search.ts +158 -158
- package/src/scripts/cli.ts +172 -167
- package/src/scripts/cli2.ts +133 -125
- package/src/scripts/cli3.ts +184 -178
- package/src/scripts/cli4.ts +191 -184
- package/src/scripts/cli5.ts +191 -184
- package/src/scripts/code_exec.ts +213 -214
- package/src/scripts/code_exec_simple.ts +147 -129
- package/src/scripts/content.ts +138 -120
- package/src/scripts/handoff-test.ts +135 -0
- package/src/scripts/multi-agent-chain.ts +278 -0
- package/src/scripts/multi-agent-conditional.ts +220 -0
- package/src/scripts/multi-agent-document-review-chain.ts +197 -0
- package/src/scripts/multi-agent-hybrid-flow.ts +310 -0
- package/src/scripts/multi-agent-parallel.ts +343 -0
- package/src/scripts/multi-agent-sequence.ts +212 -0
- package/src/scripts/multi-agent-supervisor.ts +364 -0
- package/src/scripts/multi-agent-test.ts +186 -0
- package/src/scripts/search.ts +146 -150
- package/src/scripts/simple.ts +225 -225
- package/src/scripts/stream.ts +140 -122
- package/src/scripts/test-custom-prompt-key.ts +145 -0
- package/src/scripts/test-handoff-input.ts +170 -0
- package/src/scripts/test-multi-agent-list-handoff.ts +261 -0
- package/src/scripts/test-tools-before-handoff.ts +222 -0
- package/src/scripts/tools.ts +153 -155
- package/src/specs/agent-handoffs.test.ts +889 -0
- package/src/specs/anthropic.simple.test.ts +320 -317
- package/src/specs/azure.simple.test.ts +325 -316
- package/src/specs/openai.simple.test.ts +311 -316
- package/src/specs/openrouter.simple.test.ts +107 -0
- package/src/specs/prune.test.ts +758 -763
- package/src/specs/reasoning.test.ts +201 -165
- package/src/specs/thinking-prune.test.ts +769 -703
- package/src/specs/token-memoization.test.ts +39 -0
- package/src/stream.ts +664 -651
- package/src/tools/Calculator.test.ts +278 -0
- package/src/tools/Calculator.ts +25 -0
- package/src/tools/CodeExecutor.ts +220 -220
- package/src/tools/ToolNode.ts +170 -170
- package/src/tools/handlers.ts +341 -336
- package/src/types/graph.ts +372 -185
- package/src/types/llm.ts +141 -140
- package/src/types/messages.ts +4 -0
- package/src/types/run.ts +128 -89
- package/src/types/stream.ts +401 -400
- package/src/utils/events.ts +32 -0
- package/src/utils/handlers.ts +107 -0
- package/src/utils/index.ts +6 -5
- package/src/utils/llmConfig.ts +183 -183
- package/src/utils/tokens.ts +129 -70
- package/dist/types/scripts/abort.d.ts +0 -1
- package/dist/types/scripts/ant_web_search.d.ts +0 -1
- package/dist/types/scripts/args.d.ts +0 -7
- package/dist/types/scripts/caching.d.ts +0 -1
- package/dist/types/scripts/cli.d.ts +0 -1
- package/dist/types/scripts/cli2.d.ts +0 -1
- package/dist/types/scripts/cli3.d.ts +0 -1
- package/dist/types/scripts/cli4.d.ts +0 -1
- package/dist/types/scripts/cli5.d.ts +0 -1
- package/dist/types/scripts/code_exec.d.ts +0 -1
- package/dist/types/scripts/code_exec_files.d.ts +0 -1
- package/dist/types/scripts/code_exec_simple.d.ts +0 -1
- package/dist/types/scripts/content.d.ts +0 -1
- package/dist/types/scripts/empty_input.d.ts +0 -1
- package/dist/types/scripts/image.d.ts +0 -1
- package/dist/types/scripts/memory.d.ts +0 -1
- package/dist/types/scripts/search.d.ts +0 -1
- package/dist/types/scripts/simple.d.ts +0 -1
- package/dist/types/scripts/stream.d.ts +0 -1
- package/dist/types/scripts/thinking.d.ts +0 -1
- package/dist/types/scripts/tools.d.ts +0 -1
- package/dist/types/specs/spec.utils.d.ts +0 -1
- package/dist/types/tools/example.d.ts +0 -78
- package/src/tools/example.ts +0 -129
|
@@ -1,917 +1,1144 @@
|
|
|
1
|
-
import {
|
|
2
|
-
HumanMessage,
|
|
3
|
-
AIMessage,
|
|
4
|
-
SystemMessage,
|
|
5
|
-
ToolMessage,
|
|
6
|
-
} from '@langchain/core/messages';
|
|
7
|
-
import type { TPayload } from '@/types';
|
|
8
|
-
import { formatAgentMessages } from './format';
|
|
9
|
-
import { ContentTypes } from '@/common';
|
|
10
|
-
|
|
11
|
-
describe('formatAgentMessages', () => {
|
|
12
|
-
it('should format simple user and AI messages', () => {
|
|
13
|
-
const payload: TPayload = [
|
|
14
|
-
{ role: 'user', content: 'Hello' },
|
|
15
|
-
{ role: 'assistant', content: 'Hi there!' },
|
|
16
|
-
];
|
|
17
|
-
const result = formatAgentMessages(payload);
|
|
18
|
-
expect(result.messages).toHaveLength(2);
|
|
19
|
-
expect(result.messages[0]).toBeInstanceOf(HumanMessage);
|
|
20
|
-
expect(result.messages[1]).toBeInstanceOf(AIMessage);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('should handle system messages', () => {
|
|
24
|
-
const payload = [
|
|
25
|
-
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
26
|
-
];
|
|
27
|
-
const result = formatAgentMessages(payload);
|
|
28
|
-
expect(result.messages).toHaveLength(1);
|
|
29
|
-
expect(result.messages[0]).toBeInstanceOf(SystemMessage);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('should format messages with content arrays', () => {
|
|
33
|
-
const payload = [
|
|
34
|
-
{
|
|
35
|
-
role: 'user',
|
|
36
|
-
content: [{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: 'Hello' }],
|
|
37
|
-
},
|
|
38
|
-
];
|
|
39
|
-
const result = formatAgentMessages(payload);
|
|
40
|
-
expect(result.messages).toHaveLength(1);
|
|
41
|
-
expect(result.messages[0]).toBeInstanceOf(HumanMessage);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should handle tool calls and create ToolMessages', () => {
|
|
45
|
-
const payload = [
|
|
46
|
-
{
|
|
47
|
-
role: 'assistant',
|
|
48
|
-
content: [
|
|
49
|
-
{
|
|
50
|
-
type: ContentTypes.TEXT,
|
|
51
|
-
[ContentTypes.TEXT]: 'Let me check that for you.',
|
|
52
|
-
tool_call_ids: ['123'],
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
type: ContentTypes.TOOL_CALL,
|
|
56
|
-
tool_call: {
|
|
57
|
-
id: '123',
|
|
58
|
-
name: 'search',
|
|
59
|
-
args: '{"query":"weather"}',
|
|
60
|
-
output: 'The weather is sunny.',
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
],
|
|
64
|
-
},
|
|
65
|
-
];
|
|
66
|
-
const result = formatAgentMessages(payload);
|
|
67
|
-
expect(result.messages).toHaveLength(2);
|
|
68
|
-
expect(result.messages[0]).toBeInstanceOf(AIMessage);
|
|
69
|
-
expect(result.messages[1]).toBeInstanceOf(ToolMessage);
|
|
70
|
-
expect((result.messages[0] as AIMessage).tool_calls).toHaveLength(1);
|
|
71
|
-
expect((result.messages[1] as ToolMessage).tool_call_id).toBe('123');
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('should handle
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
{
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
{
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
);
|
|
213
|
-
expect(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
],
|
|
257
|
-
},
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
},
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
expect(result.messages).
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
expect(result.messages[
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
{
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
{
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
},
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
);
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
expect(result.messages[
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
expect(result.
|
|
453
|
-
expect(result.
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
{
|
|
523
|
-
type: ContentTypes.TEXT,
|
|
524
|
-
[ContentTypes.TEXT]: '
|
|
525
|
-
tool_call_ids: ['
|
|
526
|
-
},
|
|
527
|
-
{
|
|
528
|
-
type: ContentTypes.TOOL_CALL,
|
|
529
|
-
tool_call: {
|
|
530
|
-
id: '
|
|
531
|
-
name: '
|
|
532
|
-
args: '{"
|
|
533
|
-
output: '
|
|
534
|
-
},
|
|
535
|
-
},
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
//
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
const
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
//
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
{
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
expect(
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
expect(
|
|
802
|
-
expect(result.messages[
|
|
803
|
-
expect(result.messages[
|
|
804
|
-
|
|
805
|
-
);
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
expect(result.messages[
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
expect((result.messages[
|
|
850
|
-
expect(
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
expect(
|
|
856
|
-
|
|
857
|
-
);
|
|
858
|
-
expect(result.messages[
|
|
859
|
-
expect(result.messages[
|
|
860
|
-
|
|
861
|
-
);
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
},
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
expect(result.messages[
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
expect(
|
|
916
|
-
|
|
917
|
-
|
|
1
|
+
import {
|
|
2
|
+
HumanMessage,
|
|
3
|
+
AIMessage,
|
|
4
|
+
SystemMessage,
|
|
5
|
+
ToolMessage,
|
|
6
|
+
} from '@langchain/core/messages';
|
|
7
|
+
import type { TPayload } from '@/types';
|
|
8
|
+
import { formatAgentMessages } from './format';
|
|
9
|
+
import { ContentTypes } from '@/common';
|
|
10
|
+
|
|
11
|
+
describe('formatAgentMessages', () => {
|
|
12
|
+
it('should format simple user and AI messages', () => {
|
|
13
|
+
const payload: TPayload = [
|
|
14
|
+
{ role: 'user', content: 'Hello' },
|
|
15
|
+
{ role: 'assistant', content: 'Hi there!' },
|
|
16
|
+
];
|
|
17
|
+
const result = formatAgentMessages(payload);
|
|
18
|
+
expect(result.messages).toHaveLength(2);
|
|
19
|
+
expect(result.messages[0]).toBeInstanceOf(HumanMessage);
|
|
20
|
+
expect(result.messages[1]).toBeInstanceOf(AIMessage);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should handle system messages', () => {
|
|
24
|
+
const payload = [
|
|
25
|
+
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
26
|
+
];
|
|
27
|
+
const result = formatAgentMessages(payload);
|
|
28
|
+
expect(result.messages).toHaveLength(1);
|
|
29
|
+
expect(result.messages[0]).toBeInstanceOf(SystemMessage);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should format messages with content arrays', () => {
|
|
33
|
+
const payload = [
|
|
34
|
+
{
|
|
35
|
+
role: 'user',
|
|
36
|
+
content: [{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: 'Hello' }],
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
const result = formatAgentMessages(payload);
|
|
40
|
+
expect(result.messages).toHaveLength(1);
|
|
41
|
+
expect(result.messages[0]).toBeInstanceOf(HumanMessage);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should handle tool calls and create ToolMessages', () => {
|
|
45
|
+
const payload = [
|
|
46
|
+
{
|
|
47
|
+
role: 'assistant',
|
|
48
|
+
content: [
|
|
49
|
+
{
|
|
50
|
+
type: ContentTypes.TEXT,
|
|
51
|
+
[ContentTypes.TEXT]: 'Let me check that for you.',
|
|
52
|
+
tool_call_ids: ['123'],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: ContentTypes.TOOL_CALL,
|
|
56
|
+
tool_call: {
|
|
57
|
+
id: '123',
|
|
58
|
+
name: 'search',
|
|
59
|
+
args: '{"query":"weather"}',
|
|
60
|
+
output: 'The weather is sunny.',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
const result = formatAgentMessages(payload);
|
|
67
|
+
expect(result.messages).toHaveLength(2);
|
|
68
|
+
expect(result.messages[0]).toBeInstanceOf(AIMessage);
|
|
69
|
+
expect(result.messages[1]).toBeInstanceOf(ToolMessage);
|
|
70
|
+
expect((result.messages[0] as AIMessage).tool_calls).toHaveLength(1);
|
|
71
|
+
expect((result.messages[1] as ToolMessage).tool_call_id).toBe('123');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should handle malformed tool call entries with missing tool_call property', () => {
|
|
75
|
+
const tools = new Set(['search']);
|
|
76
|
+
const payload = [
|
|
77
|
+
{
|
|
78
|
+
role: 'assistant',
|
|
79
|
+
content: [
|
|
80
|
+
{
|
|
81
|
+
type: ContentTypes.TEXT,
|
|
82
|
+
[ContentTypes.TEXT]: 'Let me check that.',
|
|
83
|
+
tool_call_ids: ['123'],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
type: ContentTypes.TOOL_CALL,
|
|
87
|
+
// Missing tool_call property - should not crash
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
type: ContentTypes.TOOL_CALL,
|
|
91
|
+
tool_call: {
|
|
92
|
+
id: '123',
|
|
93
|
+
name: 'search',
|
|
94
|
+
args: '{"query":"test"}',
|
|
95
|
+
output: 'Result',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
// Should not throw error
|
|
102
|
+
const result = formatAgentMessages(payload, undefined, tools);
|
|
103
|
+
expect(result.messages).toBeDefined();
|
|
104
|
+
expect(result.messages.length).toBeGreaterThan(0);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should handle malformed tool call entries with missing name', () => {
|
|
108
|
+
const tools = new Set(['search']);
|
|
109
|
+
const payload = [
|
|
110
|
+
{
|
|
111
|
+
role: 'assistant',
|
|
112
|
+
content: [
|
|
113
|
+
{
|
|
114
|
+
type: ContentTypes.TEXT,
|
|
115
|
+
[ContentTypes.TEXT]: 'Checking...',
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
type: ContentTypes.TOOL_CALL,
|
|
119
|
+
tool_call: {
|
|
120
|
+
id: '456',
|
|
121
|
+
// Missing name property
|
|
122
|
+
args: '{}',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
// Should not throw error
|
|
129
|
+
const result = formatAgentMessages(payload, undefined, tools);
|
|
130
|
+
expect(result.messages).toBeDefined();
|
|
131
|
+
expect(result.messages.length).toBeGreaterThan(0);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should handle multiple content parts in assistant messages', () => {
|
|
135
|
+
const payload = [
|
|
136
|
+
{
|
|
137
|
+
role: 'assistant',
|
|
138
|
+
content: [
|
|
139
|
+
{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: 'Part 1' },
|
|
140
|
+
{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: 'Part 2' },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
];
|
|
144
|
+
const result = formatAgentMessages(payload);
|
|
145
|
+
expect(result.messages).toHaveLength(1);
|
|
146
|
+
expect(result.messages[0]).toBeInstanceOf(AIMessage);
|
|
147
|
+
expect(result.messages[0].content).toHaveLength(2);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should heal invalid tool call structure by creating a preceding AIMessage', () => {
|
|
151
|
+
const payload = [
|
|
152
|
+
{
|
|
153
|
+
role: 'assistant',
|
|
154
|
+
content: [
|
|
155
|
+
{
|
|
156
|
+
type: ContentTypes.TOOL_CALL,
|
|
157
|
+
tool_call: {
|
|
158
|
+
id: '123',
|
|
159
|
+
name: 'search',
|
|
160
|
+
args: '{"query":"weather"}',
|
|
161
|
+
output: 'The weather is sunny.',
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
];
|
|
167
|
+
const result = formatAgentMessages(payload);
|
|
168
|
+
|
|
169
|
+
// Should have 2 messages: an AIMessage and a ToolMessage
|
|
170
|
+
expect(result.messages).toHaveLength(2);
|
|
171
|
+
expect(result.messages[0]).toBeInstanceOf(AIMessage);
|
|
172
|
+
expect(result.messages[1]).toBeInstanceOf(ToolMessage);
|
|
173
|
+
|
|
174
|
+
// The AIMessage should have an empty content and the tool_call
|
|
175
|
+
expect(result.messages[0].content).toBe('');
|
|
176
|
+
expect((result.messages[0] as AIMessage).tool_calls).toHaveLength(1);
|
|
177
|
+
expect((result.messages[0] as AIMessage).tool_calls?.[0]).toEqual({
|
|
178
|
+
id: '123',
|
|
179
|
+
name: 'search',
|
|
180
|
+
args: { query: 'weather' },
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// The ToolMessage should have the correct properties
|
|
184
|
+
expect((result.messages[1] as ToolMessage).tool_call_id).toBe('123');
|
|
185
|
+
expect(result.messages[1].name).toBe('search');
|
|
186
|
+
expect(result.messages[1].content).toBe('The weather is sunny.');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should handle tool calls with non-JSON args', () => {
|
|
190
|
+
const payload = [
|
|
191
|
+
{
|
|
192
|
+
role: 'assistant',
|
|
193
|
+
content: [
|
|
194
|
+
{
|
|
195
|
+
type: ContentTypes.TEXT,
|
|
196
|
+
[ContentTypes.TEXT]: 'Checking...',
|
|
197
|
+
tool_call_ids: ['123'],
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
type: ContentTypes.TOOL_CALL,
|
|
201
|
+
tool_call: {
|
|
202
|
+
id: '123',
|
|
203
|
+
name: 'search',
|
|
204
|
+
args: 'non-json-string',
|
|
205
|
+
output: 'Result',
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
];
|
|
211
|
+
const result = formatAgentMessages(payload);
|
|
212
|
+
expect(result.messages).toHaveLength(2);
|
|
213
|
+
expect(
|
|
214
|
+
(result.messages[0] as AIMessage).tool_calls?.[0].args
|
|
215
|
+
).toStrictEqual({ input: 'non-json-string' });
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('should handle complex tool calls with multiple steps', () => {
|
|
219
|
+
const payload = [
|
|
220
|
+
{
|
|
221
|
+
role: 'assistant',
|
|
222
|
+
content: [
|
|
223
|
+
{
|
|
224
|
+
type: ContentTypes.TEXT,
|
|
225
|
+
[ContentTypes.TEXT]: 'I\'ll search for that information.',
|
|
226
|
+
tool_call_ids: ['search_1'],
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
type: ContentTypes.TOOL_CALL,
|
|
230
|
+
tool_call: {
|
|
231
|
+
id: 'search_1',
|
|
232
|
+
name: 'search',
|
|
233
|
+
args: '{"query":"weather in New York"}',
|
|
234
|
+
output:
|
|
235
|
+
'The weather in New York is currently sunny with a temperature of 75°F.',
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
type: ContentTypes.TEXT,
|
|
240
|
+
[ContentTypes.TEXT]: 'Now, I\'ll convert the temperature.',
|
|
241
|
+
tool_call_ids: ['convert_1'],
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
type: ContentTypes.TOOL_CALL,
|
|
245
|
+
tool_call: {
|
|
246
|
+
id: 'convert_1',
|
|
247
|
+
name: 'convert_temperature',
|
|
248
|
+
args: '{"temperature": 75, "from": "F", "to": "C"}',
|
|
249
|
+
output: '23.89°C',
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
type: ContentTypes.TEXT,
|
|
254
|
+
[ContentTypes.TEXT]: 'Here\'s your answer.',
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
},
|
|
258
|
+
];
|
|
259
|
+
|
|
260
|
+
const result = formatAgentMessages(payload);
|
|
261
|
+
|
|
262
|
+
expect(result.messages).toHaveLength(5);
|
|
263
|
+
expect(result.messages[0]).toBeInstanceOf(AIMessage);
|
|
264
|
+
expect(result.messages[1]).toBeInstanceOf(ToolMessage);
|
|
265
|
+
expect(result.messages[2]).toBeInstanceOf(AIMessage);
|
|
266
|
+
expect(result.messages[3]).toBeInstanceOf(ToolMessage);
|
|
267
|
+
expect(result.messages[4]).toBeInstanceOf(AIMessage);
|
|
268
|
+
|
|
269
|
+
// Check first AIMessage
|
|
270
|
+
expect(result.messages[0].content).toBe(
|
|
271
|
+
'I\'ll search for that information.'
|
|
272
|
+
);
|
|
273
|
+
expect((result.messages[0] as AIMessage).tool_calls).toHaveLength(1);
|
|
274
|
+
expect((result.messages[0] as AIMessage).tool_calls?.[0]).toEqual({
|
|
275
|
+
id: 'search_1',
|
|
276
|
+
name: 'search',
|
|
277
|
+
args: { query: 'weather in New York' },
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Check first ToolMessage
|
|
281
|
+
expect((result.messages[1] as ToolMessage).tool_call_id).toBe('search_1');
|
|
282
|
+
expect(result.messages[1].name).toBe('search');
|
|
283
|
+
expect(result.messages[1].content).toBe(
|
|
284
|
+
'The weather in New York is currently sunny with a temperature of 75°F.'
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
// Check second AIMessage
|
|
288
|
+
expect(result.messages[2].content).toBe(
|
|
289
|
+
'Now, I\'ll convert the temperature.'
|
|
290
|
+
);
|
|
291
|
+
expect((result.messages[2] as AIMessage).tool_calls).toHaveLength(1);
|
|
292
|
+
expect((result.messages[2] as AIMessage).tool_calls?.[0]).toEqual({
|
|
293
|
+
id: 'convert_1',
|
|
294
|
+
name: 'convert_temperature',
|
|
295
|
+
args: { temperature: 75, from: 'F', to: 'C' },
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// Check second ToolMessage
|
|
299
|
+
expect((result.messages[3] as ToolMessage).tool_call_id).toBe('convert_1');
|
|
300
|
+
expect(result.messages[3].name).toBe('convert_temperature');
|
|
301
|
+
expect(result.messages[3].content).toBe('23.89°C');
|
|
302
|
+
|
|
303
|
+
// Check final AIMessage
|
|
304
|
+
expect(result.messages[4].content).toStrictEqual([
|
|
305
|
+
{ [ContentTypes.TEXT]: 'Here\'s your answer.', type: ContentTypes.TEXT },
|
|
306
|
+
]);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it.skip('should not produce two consecutive assistant messages and format content correctly', () => {
|
|
310
|
+
const payload = [
|
|
311
|
+
{ role: 'user', content: 'Hello' },
|
|
312
|
+
{
|
|
313
|
+
role: 'assistant',
|
|
314
|
+
content: [
|
|
315
|
+
{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: 'Hi there!' },
|
|
316
|
+
],
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
role: 'assistant',
|
|
320
|
+
content: [
|
|
321
|
+
{
|
|
322
|
+
type: ContentTypes.TEXT,
|
|
323
|
+
[ContentTypes.TEXT]: 'How can I help you?',
|
|
324
|
+
},
|
|
325
|
+
],
|
|
326
|
+
},
|
|
327
|
+
{ role: 'user', content: 'What\'s the weather?' },
|
|
328
|
+
{
|
|
329
|
+
role: 'assistant',
|
|
330
|
+
content: [
|
|
331
|
+
{
|
|
332
|
+
type: ContentTypes.TEXT,
|
|
333
|
+
[ContentTypes.TEXT]: 'Let me check that for you.',
|
|
334
|
+
tool_call_ids: ['weather_1'],
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
type: ContentTypes.TOOL_CALL,
|
|
338
|
+
tool_call: {
|
|
339
|
+
id: 'weather_1',
|
|
340
|
+
name: 'check_weather',
|
|
341
|
+
args: '{"location":"New York"}',
|
|
342
|
+
output: 'Sunny, 75°F',
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
],
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
role: 'assistant',
|
|
349
|
+
content: [
|
|
350
|
+
{
|
|
351
|
+
type: ContentTypes.TEXT,
|
|
352
|
+
[ContentTypes.TEXT]: 'Here\'s the weather information.',
|
|
353
|
+
},
|
|
354
|
+
],
|
|
355
|
+
},
|
|
356
|
+
];
|
|
357
|
+
|
|
358
|
+
const result = formatAgentMessages(payload);
|
|
359
|
+
|
|
360
|
+
// Check correct message count and types
|
|
361
|
+
expect(result.messages).toHaveLength(6);
|
|
362
|
+
expect(result.messages[0]).toBeInstanceOf(HumanMessage);
|
|
363
|
+
expect(result.messages[1]).toBeInstanceOf(AIMessage);
|
|
364
|
+
expect(result.messages[2]).toBeInstanceOf(HumanMessage);
|
|
365
|
+
expect(result.messages[3]).toBeInstanceOf(AIMessage);
|
|
366
|
+
expect(result.messages[4]).toBeInstanceOf(ToolMessage);
|
|
367
|
+
expect(result.messages[5]).toBeInstanceOf(AIMessage);
|
|
368
|
+
|
|
369
|
+
// Check content of messages
|
|
370
|
+
expect(result.messages[0].content).toStrictEqual([
|
|
371
|
+
{ [ContentTypes.TEXT]: 'Hello', type: ContentTypes.TEXT },
|
|
372
|
+
]);
|
|
373
|
+
expect(result.messages[1].content).toStrictEqual([
|
|
374
|
+
{ [ContentTypes.TEXT]: 'Hi there!', type: ContentTypes.TEXT },
|
|
375
|
+
{ [ContentTypes.TEXT]: 'How can I help you?', type: ContentTypes.TEXT },
|
|
376
|
+
]);
|
|
377
|
+
expect(result.messages[2].content).toStrictEqual([
|
|
378
|
+
{ [ContentTypes.TEXT]: 'What\'s the weather?', type: ContentTypes.TEXT },
|
|
379
|
+
]);
|
|
380
|
+
expect(result.messages[3].content).toBe('Let me check that for you.');
|
|
381
|
+
expect(result.messages[4].content).toBe('Sunny, 75°F');
|
|
382
|
+
expect(result.messages[5].content).toStrictEqual([
|
|
383
|
+
{
|
|
384
|
+
[ContentTypes.TEXT]: 'Here\'s the weather information.',
|
|
385
|
+
type: ContentTypes.TEXT,
|
|
386
|
+
},
|
|
387
|
+
]);
|
|
388
|
+
|
|
389
|
+
// Check that there are no consecutive AIMessages
|
|
390
|
+
const messageTypes = result.messages.map((message) => message.constructor);
|
|
391
|
+
for (let i = 0; i < messageTypes.length - 1; i++) {
|
|
392
|
+
expect(
|
|
393
|
+
messageTypes[i] === AIMessage && messageTypes[i + 1] === AIMessage
|
|
394
|
+
).toBe(false);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Additional check to ensure the consecutive assistant messages were combined
|
|
398
|
+
expect(result.messages[1].content).toHaveLength(2);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it('should skip THINK type content parts', () => {
|
|
402
|
+
const payload = [
|
|
403
|
+
{
|
|
404
|
+
role: 'assistant',
|
|
405
|
+
content: [
|
|
406
|
+
{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: 'Initial response' },
|
|
407
|
+
{
|
|
408
|
+
type: ContentTypes.THINK,
|
|
409
|
+
[ContentTypes.THINK]: 'Reasoning about the problem...',
|
|
410
|
+
},
|
|
411
|
+
{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: 'Final answer' },
|
|
412
|
+
],
|
|
413
|
+
},
|
|
414
|
+
];
|
|
415
|
+
|
|
416
|
+
const result = formatAgentMessages(payload);
|
|
417
|
+
|
|
418
|
+
expect(result.messages).toHaveLength(1);
|
|
419
|
+
expect(result.messages[0]).toBeInstanceOf(AIMessage);
|
|
420
|
+
expect(result.messages[0].content).toEqual(
|
|
421
|
+
'Initial response\nFinal answer'
|
|
422
|
+
);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('should join TEXT content as string when THINK content type is present', () => {
|
|
426
|
+
const payload = [
|
|
427
|
+
{
|
|
428
|
+
role: 'assistant',
|
|
429
|
+
content: [
|
|
430
|
+
{
|
|
431
|
+
type: ContentTypes.THINK,
|
|
432
|
+
[ContentTypes.THINK]: 'Analyzing the problem...',
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
type: ContentTypes.TEXT,
|
|
436
|
+
[ContentTypes.TEXT]: 'First part of response',
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
type: ContentTypes.TEXT,
|
|
440
|
+
[ContentTypes.TEXT]: 'Second part of response',
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
type: ContentTypes.TEXT,
|
|
444
|
+
[ContentTypes.TEXT]: 'Final part of response',
|
|
445
|
+
},
|
|
446
|
+
],
|
|
447
|
+
},
|
|
448
|
+
];
|
|
449
|
+
|
|
450
|
+
const result = formatAgentMessages(payload);
|
|
451
|
+
|
|
452
|
+
expect(result.messages).toHaveLength(1);
|
|
453
|
+
expect(result.messages[0]).toBeInstanceOf(AIMessage);
|
|
454
|
+
expect(typeof result.messages[0].content).toBe('string');
|
|
455
|
+
expect(result.messages[0].content).toBe(
|
|
456
|
+
'First part of response\nSecond part of response\nFinal part of response'
|
|
457
|
+
);
|
|
458
|
+
expect(result.messages[0].content).not.toContain(
|
|
459
|
+
'Analyzing the problem...'
|
|
460
|
+
);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it('should exclude ERROR type content parts', () => {
|
|
464
|
+
const payload = [
|
|
465
|
+
{
|
|
466
|
+
role: 'assistant',
|
|
467
|
+
content: [
|
|
468
|
+
{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: 'Hello there' },
|
|
469
|
+
{
|
|
470
|
+
type: ContentTypes.ERROR,
|
|
471
|
+
[ContentTypes.ERROR]:
|
|
472
|
+
'An error occurred while processing the request: Something went wrong',
|
|
473
|
+
},
|
|
474
|
+
{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: 'Final answer' },
|
|
475
|
+
],
|
|
476
|
+
},
|
|
477
|
+
];
|
|
478
|
+
|
|
479
|
+
const result = formatAgentMessages(payload);
|
|
480
|
+
|
|
481
|
+
expect(result.messages).toHaveLength(1);
|
|
482
|
+
expect(result.messages[0]).toBeInstanceOf(AIMessage);
|
|
483
|
+
expect(result.messages[0].content).toEqual([
|
|
484
|
+
{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: 'Hello there' },
|
|
485
|
+
{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: 'Final answer' },
|
|
486
|
+
]);
|
|
487
|
+
|
|
488
|
+
const hasErrorContent =
|
|
489
|
+
Array.isArray(result.messages[0].content) &&
|
|
490
|
+
result.messages[0].content.some(
|
|
491
|
+
(item) =>
|
|
492
|
+
item.type === ContentTypes.ERROR ||
|
|
493
|
+
JSON.stringify(item).includes('An error occurred')
|
|
494
|
+
);
|
|
495
|
+
expect(hasErrorContent).toBe(false);
|
|
496
|
+
});
|
|
497
|
+
it('should handle indexTokenCountMap and return updated map', () => {
|
|
498
|
+
const payload = [
|
|
499
|
+
{ role: 'user', content: 'Hello' },
|
|
500
|
+
{ role: 'assistant', content: 'Hi there!' },
|
|
501
|
+
];
|
|
502
|
+
|
|
503
|
+
const indexTokenCountMap = {
|
|
504
|
+
0: 5, // 5 tokens for "Hello"
|
|
505
|
+
1: 10, // 10 tokens for "Hi there!"
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
const result = formatAgentMessages(payload, indexTokenCountMap);
|
|
509
|
+
|
|
510
|
+
expect(result.messages).toHaveLength(2);
|
|
511
|
+
expect(result.indexTokenCountMap).toBeDefined();
|
|
512
|
+
expect(result.indexTokenCountMap?.[0]).toBe(5);
|
|
513
|
+
expect(result.indexTokenCountMap?.[1]).toBe(10);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
it('should handle complex message transformations with indexTokenCountMap', () => {
|
|
517
|
+
const payload = [
|
|
518
|
+
{ role: 'user', content: 'What\'s the weather?' },
|
|
519
|
+
{
|
|
520
|
+
role: 'assistant',
|
|
521
|
+
content: [
|
|
522
|
+
{
|
|
523
|
+
type: ContentTypes.TEXT,
|
|
524
|
+
[ContentTypes.TEXT]: 'Let me check that for you.',
|
|
525
|
+
tool_call_ids: ['weather_1'],
|
|
526
|
+
},
|
|
527
|
+
{
|
|
528
|
+
type: ContentTypes.TOOL_CALL,
|
|
529
|
+
tool_call: {
|
|
530
|
+
id: 'weather_1',
|
|
531
|
+
name: 'check_weather',
|
|
532
|
+
args: '{"location":"New York"}',
|
|
533
|
+
output: 'Sunny, 75°F',
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
],
|
|
537
|
+
},
|
|
538
|
+
];
|
|
539
|
+
|
|
540
|
+
const indexTokenCountMap = {
|
|
541
|
+
0: 10, // 10 tokens for "What's the weather?"
|
|
542
|
+
1: 50, // 50 tokens for the assistant message with tool call
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
const result = formatAgentMessages(payload, indexTokenCountMap);
|
|
546
|
+
|
|
547
|
+
// The original message at index 1 should be split into two messages
|
|
548
|
+
expect(result.messages).toHaveLength(3);
|
|
549
|
+
expect(result.indexTokenCountMap).toBeDefined();
|
|
550
|
+
expect(result.indexTokenCountMap?.[0]).toBe(10); // User message stays the same
|
|
551
|
+
|
|
552
|
+
// The assistant message tokens should be distributed across the resulting messages
|
|
553
|
+
const totalAssistantTokens =
|
|
554
|
+
Object.values(result.indexTokenCountMap || {}).reduce(
|
|
555
|
+
(sum, count) => sum + count,
|
|
556
|
+
0
|
|
557
|
+
) - 10; // Subtract user message tokens
|
|
558
|
+
|
|
559
|
+
expect(totalAssistantTokens).toBe(50); // Should match the original token count
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
it('should handle one-to-many message expansion with tool calls', () => {
|
|
563
|
+
// One message with multiple tool calls expands to multiple messages
|
|
564
|
+
const payload = [
|
|
565
|
+
{
|
|
566
|
+
role: 'assistant',
|
|
567
|
+
content: [
|
|
568
|
+
{
|
|
569
|
+
type: ContentTypes.TEXT,
|
|
570
|
+
[ContentTypes.TEXT]: 'First tool call:',
|
|
571
|
+
tool_call_ids: ['tool_1'],
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
type: ContentTypes.TOOL_CALL,
|
|
575
|
+
tool_call: {
|
|
576
|
+
id: 'tool_1',
|
|
577
|
+
name: 'search',
|
|
578
|
+
args: '{"query":"test"}',
|
|
579
|
+
output: 'Search result',
|
|
580
|
+
},
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
type: ContentTypes.TEXT,
|
|
584
|
+
[ContentTypes.TEXT]: 'Second tool call:',
|
|
585
|
+
tool_call_ids: ['tool_2'],
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
type: ContentTypes.TOOL_CALL,
|
|
589
|
+
tool_call: {
|
|
590
|
+
id: 'tool_2',
|
|
591
|
+
name: 'calculate',
|
|
592
|
+
args: '{"expression":"1+1"}',
|
|
593
|
+
output: '2',
|
|
594
|
+
},
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
type: ContentTypes.TEXT,
|
|
598
|
+
[ContentTypes.TEXT]: 'Final response',
|
|
599
|
+
},
|
|
600
|
+
],
|
|
601
|
+
},
|
|
602
|
+
];
|
|
603
|
+
|
|
604
|
+
const indexTokenCountMap = {
|
|
605
|
+
0: 100, // 100 tokens for the complex assistant message
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
const result = formatAgentMessages(payload, indexTokenCountMap);
|
|
609
|
+
|
|
610
|
+
// One message expands to 5 messages (2 tool calls + text before, between, and after)
|
|
611
|
+
expect(result.messages).toHaveLength(5);
|
|
612
|
+
expect(result.indexTokenCountMap).toBeDefined();
|
|
613
|
+
|
|
614
|
+
// The sum of all token counts should equal the original
|
|
615
|
+
const totalTokens = Object.values(result.indexTokenCountMap || {}).reduce(
|
|
616
|
+
(sum, count) => sum + count,
|
|
617
|
+
0
|
|
618
|
+
);
|
|
619
|
+
|
|
620
|
+
expect(totalTokens).toBe(100);
|
|
621
|
+
|
|
622
|
+
// Check that each resulting message has a token count
|
|
623
|
+
for (let i = 0; i < result.messages.length; i++) {
|
|
624
|
+
expect(result.indexTokenCountMap?.[i]).toBeDefined();
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
it('should handle content filtering that reduces message count', () => {
|
|
629
|
+
// Message with THINK and ERROR parts that get filtered out
|
|
630
|
+
const payload = [
|
|
631
|
+
{
|
|
632
|
+
role: 'assistant',
|
|
633
|
+
content: [
|
|
634
|
+
{ type: ContentTypes.THINK, [ContentTypes.THINK]: 'Thinking...' },
|
|
635
|
+
{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: 'Visible response' },
|
|
636
|
+
{ type: ContentTypes.ERROR, [ContentTypes.ERROR]: 'Error occurred' },
|
|
637
|
+
],
|
|
638
|
+
},
|
|
639
|
+
];
|
|
640
|
+
|
|
641
|
+
const indexTokenCountMap = {
|
|
642
|
+
0: 60, // 60 tokens for the message with filtered content
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
const result = formatAgentMessages(payload, indexTokenCountMap);
|
|
646
|
+
|
|
647
|
+
// Only one message should remain after filtering
|
|
648
|
+
expect(result.messages).toHaveLength(1);
|
|
649
|
+
expect(result.indexTokenCountMap).toBeDefined();
|
|
650
|
+
|
|
651
|
+
// All tokens should be assigned to the remaining message
|
|
652
|
+
expect(result.indexTokenCountMap?.[0]).toBe(60);
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
it('should handle empty result after content filtering', () => {
|
|
656
|
+
// Message with only THINK and ERROR parts that all get filtered out
|
|
657
|
+
const payload = [
|
|
658
|
+
{
|
|
659
|
+
role: 'assistant',
|
|
660
|
+
content: [
|
|
661
|
+
{ type: ContentTypes.THINK, [ContentTypes.THINK]: 'Thinking...' },
|
|
662
|
+
{ type: ContentTypes.ERROR, [ContentTypes.ERROR]: 'Error occurred' },
|
|
663
|
+
{ type: ContentTypes.AGENT_UPDATE, update: 'Processing...' },
|
|
664
|
+
],
|
|
665
|
+
},
|
|
666
|
+
];
|
|
667
|
+
|
|
668
|
+
const indexTokenCountMap = {
|
|
669
|
+
0: 40, // 40 tokens for the message with filtered content
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
const result = formatAgentMessages(payload, indexTokenCountMap);
|
|
673
|
+
|
|
674
|
+
// No messages should remain after filtering
|
|
675
|
+
expect(result.messages).toHaveLength(0);
|
|
676
|
+
expect(result.indexTokenCountMap).toBeDefined();
|
|
677
|
+
|
|
678
|
+
// The token count map should be empty since there are no messages
|
|
679
|
+
expect(Object.keys(result.indexTokenCountMap || {})).toHaveLength(0);
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
it('should demonstrate how 2 input messages can become more than 2 output messages', () => {
|
|
683
|
+
// Two input messages where one contains tool calls
|
|
684
|
+
const payload = [
|
|
685
|
+
{ role: 'user', content: 'Can you help me with something?' },
|
|
686
|
+
{
|
|
687
|
+
role: 'assistant',
|
|
688
|
+
content: [
|
|
689
|
+
{
|
|
690
|
+
type: ContentTypes.TEXT,
|
|
691
|
+
[ContentTypes.TEXT]: 'I\'ll help you with that.',
|
|
692
|
+
tool_call_ids: ['tool_1'],
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
type: ContentTypes.TOOL_CALL,
|
|
696
|
+
tool_call: {
|
|
697
|
+
id: 'tool_1',
|
|
698
|
+
name: 'search',
|
|
699
|
+
args: '{"query":"help topics"}',
|
|
700
|
+
output: 'Found several help topics.',
|
|
701
|
+
},
|
|
702
|
+
},
|
|
703
|
+
],
|
|
704
|
+
},
|
|
705
|
+
];
|
|
706
|
+
|
|
707
|
+
const indexTokenCountMap = {
|
|
708
|
+
0: 15, // 15 tokens for the user message
|
|
709
|
+
1: 45, // 45 tokens for the assistant message with tool call
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
const result = formatAgentMessages(payload, indexTokenCountMap);
|
|
713
|
+
|
|
714
|
+
// 2 input messages become 3 output messages (user + assistant + tool)
|
|
715
|
+
expect(payload).toHaveLength(2);
|
|
716
|
+
expect(result.messages).toHaveLength(3);
|
|
717
|
+
expect(result.indexTokenCountMap).toBeDefined();
|
|
718
|
+
expect(Object.keys(result.indexTokenCountMap ?? {}).length).toBe(3);
|
|
719
|
+
|
|
720
|
+
// Check message types
|
|
721
|
+
expect(result.messages[0]).toBeInstanceOf(HumanMessage);
|
|
722
|
+
expect(result.messages[1]).toBeInstanceOf(AIMessage);
|
|
723
|
+
expect(result.messages[2]).toBeInstanceOf(ToolMessage);
|
|
724
|
+
|
|
725
|
+
// The sum of all token counts should equal the original total
|
|
726
|
+
const totalTokens = Object.values(result.indexTokenCountMap || {}).reduce(
|
|
727
|
+
(sum, count) => sum + count,
|
|
728
|
+
0
|
|
729
|
+
);
|
|
730
|
+
|
|
731
|
+
expect(totalTokens).toBe(60); // 15 + 45
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
it('should handle an AI message with 5 tool calls in a single message', () => {
|
|
735
|
+
const payload = [
|
|
736
|
+
{
|
|
737
|
+
role: 'assistant',
|
|
738
|
+
content: [
|
|
739
|
+
{
|
|
740
|
+
type: ContentTypes.TEXT,
|
|
741
|
+
[ContentTypes.TEXT]: 'I\'ll perform multiple operations for you.',
|
|
742
|
+
tool_call_ids: ['tool_1', 'tool_2', 'tool_3', 'tool_4', 'tool_5'],
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
type: ContentTypes.TOOL_CALL,
|
|
746
|
+
tool_call: {
|
|
747
|
+
id: 'tool_1',
|
|
748
|
+
name: 'search',
|
|
749
|
+
args: '{"query":"latest news"}',
|
|
750
|
+
output: 'Found several news articles.',
|
|
751
|
+
},
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
type: ContentTypes.TOOL_CALL,
|
|
755
|
+
tool_call: {
|
|
756
|
+
id: 'tool_2',
|
|
757
|
+
name: 'check_weather',
|
|
758
|
+
args: '{"location":"New York"}',
|
|
759
|
+
output: 'Sunny, 75°F',
|
|
760
|
+
},
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
type: ContentTypes.TOOL_CALL,
|
|
764
|
+
tool_call: {
|
|
765
|
+
id: 'tool_3',
|
|
766
|
+
name: 'calculate',
|
|
767
|
+
args: '{"expression":"356 * 24"}',
|
|
768
|
+
output: '8544',
|
|
769
|
+
},
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
type: ContentTypes.TOOL_CALL,
|
|
773
|
+
tool_call: {
|
|
774
|
+
id: 'tool_4',
|
|
775
|
+
name: 'translate',
|
|
776
|
+
args: '{"text":"Hello world","source":"en","target":"fr"}',
|
|
777
|
+
output: 'Bonjour le monde',
|
|
778
|
+
},
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
type: ContentTypes.TOOL_CALL,
|
|
782
|
+
tool_call: {
|
|
783
|
+
id: 'tool_5',
|
|
784
|
+
name: 'fetch_data',
|
|
785
|
+
args: '{"endpoint":"/api/users","params":{"limit":5}}',
|
|
786
|
+
output:
|
|
787
|
+
'{"users":[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"},{"id":3,"name":"Charlie"},{"id":4,"name":"David"},{"id":5,"name":"Eve"}]}',
|
|
788
|
+
},
|
|
789
|
+
},
|
|
790
|
+
],
|
|
791
|
+
},
|
|
792
|
+
];
|
|
793
|
+
|
|
794
|
+
const result = formatAgentMessages(payload);
|
|
795
|
+
|
|
796
|
+
// Should have 6 messages: 1 AIMessage and 5 ToolMessages
|
|
797
|
+
expect(result.messages).toHaveLength(6);
|
|
798
|
+
|
|
799
|
+
// Check message types in the correct sequence
|
|
800
|
+
expect(result.messages[0]).toBeInstanceOf(AIMessage); // Initial message with all tool calls
|
|
801
|
+
expect(result.messages[1]).toBeInstanceOf(ToolMessage); // Tool 1 response
|
|
802
|
+
expect(result.messages[2]).toBeInstanceOf(ToolMessage); // Tool 2 response
|
|
803
|
+
expect(result.messages[3]).toBeInstanceOf(ToolMessage); // Tool 3 response
|
|
804
|
+
expect(result.messages[4]).toBeInstanceOf(ToolMessage); // Tool 4 response
|
|
805
|
+
expect(result.messages[5]).toBeInstanceOf(ToolMessage); // Tool 5 response
|
|
806
|
+
|
|
807
|
+
// Check AIMessage has all 5 tool calls
|
|
808
|
+
expect(result.messages[0].content).toBe(
|
|
809
|
+
'I\'ll perform multiple operations for you.'
|
|
810
|
+
);
|
|
811
|
+
expect((result.messages[0] as AIMessage).tool_calls).toHaveLength(5);
|
|
812
|
+
|
|
813
|
+
// Verify each tool call in the AIMessage
|
|
814
|
+
expect((result.messages[0] as AIMessage).tool_calls?.[0]).toEqual({
|
|
815
|
+
id: 'tool_1',
|
|
816
|
+
name: 'search',
|
|
817
|
+
args: { query: 'latest news' },
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
expect((result.messages[0] as AIMessage).tool_calls?.[1]).toEqual({
|
|
821
|
+
id: 'tool_2',
|
|
822
|
+
name: 'check_weather',
|
|
823
|
+
args: { location: 'New York' },
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
expect((result.messages[0] as AIMessage).tool_calls?.[2]).toEqual({
|
|
827
|
+
id: 'tool_3',
|
|
828
|
+
name: 'calculate',
|
|
829
|
+
args: { expression: '356 * 24' },
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
expect((result.messages[0] as AIMessage).tool_calls?.[3]).toEqual({
|
|
833
|
+
id: 'tool_4',
|
|
834
|
+
name: 'translate',
|
|
835
|
+
args: { text: 'Hello world', source: 'en', target: 'fr' },
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
expect((result.messages[0] as AIMessage).tool_calls?.[4]).toEqual({
|
|
839
|
+
id: 'tool_5',
|
|
840
|
+
name: 'fetch_data',
|
|
841
|
+
args: { endpoint: '/api/users', params: { limit: 5 } },
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
// Check each ToolMessage
|
|
845
|
+
expect((result.messages[1] as ToolMessage).tool_call_id).toBe('tool_1');
|
|
846
|
+
expect(result.messages[1].name).toBe('search');
|
|
847
|
+
expect(result.messages[1].content).toBe('Found several news articles.');
|
|
848
|
+
|
|
849
|
+
expect((result.messages[2] as ToolMessage).tool_call_id).toBe('tool_2');
|
|
850
|
+
expect(result.messages[2].name).toBe('check_weather');
|
|
851
|
+
expect(result.messages[2].content).toBe('Sunny, 75°F');
|
|
852
|
+
|
|
853
|
+
expect((result.messages[3] as ToolMessage).tool_call_id).toBe('tool_3');
|
|
854
|
+
expect(result.messages[3].name).toBe('calculate');
|
|
855
|
+
expect(result.messages[3].content).toBe('8544');
|
|
856
|
+
|
|
857
|
+
expect((result.messages[4] as ToolMessage).tool_call_id).toBe('tool_4');
|
|
858
|
+
expect(result.messages[4].name).toBe('translate');
|
|
859
|
+
expect(result.messages[4].content).toBe('Bonjour le monde');
|
|
860
|
+
|
|
861
|
+
expect((result.messages[5] as ToolMessage).tool_call_id).toBe('tool_5');
|
|
862
|
+
expect(result.messages[5].name).toBe('fetch_data');
|
|
863
|
+
expect(result.messages[5].content).toBe(
|
|
864
|
+
'{"users":[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"},{"id":3,"name":"Charlie"},{"id":4,"name":"David"},{"id":5,"name":"Eve"}]}'
|
|
865
|
+
);
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
it('should heal tool call structure with thinking content', () => {
|
|
869
|
+
const payload = [
|
|
870
|
+
{
|
|
871
|
+
role: 'assistant',
|
|
872
|
+
content: [
|
|
873
|
+
{
|
|
874
|
+
type: ContentTypes.THINK,
|
|
875
|
+
[ContentTypes.THINK]:
|
|
876
|
+
'I\'ll add this agreement as an observation to our existing troubleshooting task in the project memory system.',
|
|
877
|
+
},
|
|
878
|
+
{
|
|
879
|
+
type: ContentTypes.TOOL_CALL,
|
|
880
|
+
tool_call: {
|
|
881
|
+
id: 'tooluse_Zz-mw_wHTrWTvDHaCbfaZg',
|
|
882
|
+
name: 'add_observations_mcp_project-memory',
|
|
883
|
+
args: '{"observations":[{"entityName":"MCP_Tool_Error_Troubleshooting","contents":["Agreement established: Document all future tests in the project memory system to maintain a comprehensive troubleshooting log","This will provide a structured record of the entire troubleshooting process and help identify patterns in the error behavior"]}]}',
|
|
884
|
+
type: 'tool_call',
|
|
885
|
+
progress: 1,
|
|
886
|
+
output:
|
|
887
|
+
'[\n {\n "entityName": "MCP_Tool_Error_Troubleshooting",\n "addedObservations": [\n {\n "content": "Agreement established: Document all future tests in the project memory system to maintain a comprehensive troubleshooting log",\n "timestamp": "2025-03-26T00:46:42.154Z"\n },\n {\n "content": "This will provide a structured record of the entire troubleshooting process and help identify patterns in the error behavior",\n "timestamp": "2025-03-26T00:46:42.154Z"\n }\n ]\n }\n]',
|
|
888
|
+
},
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
type: ContentTypes.TEXT,
|
|
892
|
+
[ContentTypes.TEXT]:
|
|
893
|
+
'\n\nI\'ve successfully added our agreement to the project memory system. The observation has been recorded in the "MCP_Tool_Error_Troubleshooting" entity with the current timestamp.\n\nGoing forward, I will:\n\n1. Document each test we perform\n2. Record the methodology and results\n3. Update the project memory with our findings\n4. Establish appropriate relationships between tests and related components\n5. Provide a summary of what we\'ve learned from each test\n\nThis structured approach will help us build a comprehensive knowledge base of the error behavior and our troubleshooting process, which may prove valuable for resolving similar issues in the future or for other developers facing similar challenges.\n\nWhat test would you like to perform next in our troubleshooting process?',
|
|
894
|
+
},
|
|
895
|
+
],
|
|
896
|
+
},
|
|
897
|
+
];
|
|
898
|
+
|
|
899
|
+
const result = formatAgentMessages(payload);
|
|
900
|
+
|
|
901
|
+
// Should have 3 messages: an AIMessage with empty content, a ToolMessage, and a final AIMessage with the text
|
|
902
|
+
expect(result.messages).toHaveLength(3);
|
|
903
|
+
expect(result.messages[0]).toBeInstanceOf(AIMessage);
|
|
904
|
+
expect(result.messages[1]).toBeInstanceOf(ToolMessage);
|
|
905
|
+
expect(result.messages[2]).toBeInstanceOf(AIMessage);
|
|
906
|
+
|
|
907
|
+
// The first AIMessage should have an empty content and the tool_call
|
|
908
|
+
expect(result.messages[0].content).toBe('');
|
|
909
|
+
expect((result.messages[0] as AIMessage).tool_calls).toHaveLength(1);
|
|
910
|
+
expect((result.messages[0] as AIMessage).tool_calls?.[0].name).toBe(
|
|
911
|
+
'add_observations_mcp_project-memory'
|
|
912
|
+
);
|
|
913
|
+
|
|
914
|
+
// The ToolMessage should have the correct properties
|
|
915
|
+
expect((result.messages[1] as ToolMessage).tool_call_id).toBe(
|
|
916
|
+
'tooluse_Zz-mw_wHTrWTvDHaCbfaZg'
|
|
917
|
+
);
|
|
918
|
+
expect(result.messages[1].name).toBe('add_observations_mcp_project-memory');
|
|
919
|
+
expect(result.messages[1].content).toContain(
|
|
920
|
+
'MCP_Tool_Error_Troubleshooting'
|
|
921
|
+
);
|
|
922
|
+
|
|
923
|
+
// The final AIMessage should contain the text response
|
|
924
|
+
expect(typeof result.messages[2].content).toBe('string');
|
|
925
|
+
expect((result.messages[2].content as string).trim()).toContain(
|
|
926
|
+
'I\'ve successfully added our agreement to the project memory system'
|
|
927
|
+
);
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
it('should demonstrate how messages can be filtered out, reducing count', () => {
|
|
931
|
+
// Two input messages where one gets completely filtered out
|
|
932
|
+
const payload = [
|
|
933
|
+
{ role: 'user', content: 'Hello there' },
|
|
934
|
+
{
|
|
935
|
+
role: 'assistant',
|
|
936
|
+
content: [
|
|
937
|
+
{
|
|
938
|
+
type: ContentTypes.THINK,
|
|
939
|
+
[ContentTypes.THINK]: 'Thinking about response...',
|
|
940
|
+
},
|
|
941
|
+
{
|
|
942
|
+
type: ContentTypes.ERROR,
|
|
943
|
+
[ContentTypes.ERROR]: 'Error in processing',
|
|
944
|
+
},
|
|
945
|
+
{ type: ContentTypes.AGENT_UPDATE, update: 'Working on it...' },
|
|
946
|
+
],
|
|
947
|
+
},
|
|
948
|
+
];
|
|
949
|
+
|
|
950
|
+
const indexTokenCountMap = {
|
|
951
|
+
0: 10, // 10 tokens for the user message
|
|
952
|
+
1: 30, // 30 tokens for the assistant message that will be filtered out
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
const result = formatAgentMessages(payload, indexTokenCountMap);
|
|
956
|
+
|
|
957
|
+
// 2 input messages become 1 output message (only the user message remains)
|
|
958
|
+
expect(payload).toHaveLength(2);
|
|
959
|
+
expect(result.messages).toHaveLength(1);
|
|
960
|
+
expect(result.indexTokenCountMap).toBeDefined();
|
|
961
|
+
expect(Object.keys(result.indexTokenCountMap ?? {}).length).toBe(1);
|
|
962
|
+
|
|
963
|
+
// Check message type
|
|
964
|
+
expect(result.messages[0]).toBeInstanceOf(HumanMessage);
|
|
965
|
+
|
|
966
|
+
// Only the user message tokens should remain
|
|
967
|
+
expect(result.indexTokenCountMap?.[0]).toBe(10);
|
|
968
|
+
|
|
969
|
+
// The total tokens should be just the user message tokens
|
|
970
|
+
const totalTokens = Object.values(result.indexTokenCountMap || {}).reduce(
|
|
971
|
+
(sum, count) => sum + count,
|
|
972
|
+
0
|
|
973
|
+
);
|
|
974
|
+
|
|
975
|
+
expect(totalTokens).toBe(10);
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
it('should skip invalid tool calls with no name AND no output', () => {
|
|
979
|
+
const payload = [
|
|
980
|
+
{
|
|
981
|
+
role: 'assistant',
|
|
982
|
+
content: [
|
|
983
|
+
{
|
|
984
|
+
type: ContentTypes.TEXT,
|
|
985
|
+
[ContentTypes.TEXT]: 'Let me help you with that.',
|
|
986
|
+
tool_call_ids: ['valid_tool_1'],
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
type: ContentTypes.TOOL_CALL,
|
|
990
|
+
tool_call: {
|
|
991
|
+
id: 'invalid_tool_1',
|
|
992
|
+
name: '',
|
|
993
|
+
args: '{"query":"test"}',
|
|
994
|
+
output: '',
|
|
995
|
+
},
|
|
996
|
+
},
|
|
997
|
+
{
|
|
998
|
+
type: ContentTypes.TOOL_CALL,
|
|
999
|
+
tool_call: {
|
|
1000
|
+
id: 'valid_tool_1',
|
|
1001
|
+
name: 'search',
|
|
1002
|
+
args: '{"query":"weather"}',
|
|
1003
|
+
output: 'The weather is sunny.',
|
|
1004
|
+
},
|
|
1005
|
+
},
|
|
1006
|
+
],
|
|
1007
|
+
},
|
|
1008
|
+
];
|
|
1009
|
+
|
|
1010
|
+
const result = formatAgentMessages(payload);
|
|
1011
|
+
|
|
1012
|
+
// Should have 2 messages: AIMessage and ToolMessage (invalid tool call is skipped)
|
|
1013
|
+
expect(result.messages).toHaveLength(2);
|
|
1014
|
+
expect(result.messages[0]).toBeInstanceOf(AIMessage);
|
|
1015
|
+
expect(result.messages[1]).toBeInstanceOf(ToolMessage);
|
|
1016
|
+
|
|
1017
|
+
// The AIMessage should only have 1 tool call (the valid one)
|
|
1018
|
+
expect((result.messages[0] as AIMessage).tool_calls).toHaveLength(1);
|
|
1019
|
+
expect((result.messages[0] as AIMessage).tool_calls?.[0].name).toBe(
|
|
1020
|
+
'search'
|
|
1021
|
+
);
|
|
1022
|
+
expect((result.messages[0] as AIMessage).tool_calls?.[0].id).toBe(
|
|
1023
|
+
'valid_tool_1'
|
|
1024
|
+
);
|
|
1025
|
+
|
|
1026
|
+
// The ToolMessage should be for the valid tool call
|
|
1027
|
+
expect((result.messages[1] as ToolMessage).tool_call_id).toBe(
|
|
1028
|
+
'valid_tool_1'
|
|
1029
|
+
);
|
|
1030
|
+
expect(result.messages[1].name).toBe('search');
|
|
1031
|
+
expect(result.messages[1].content).toBe('The weather is sunny.');
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
it('should skip tool calls with no name AND null output', () => {
|
|
1035
|
+
const payload = [
|
|
1036
|
+
{
|
|
1037
|
+
role: 'assistant',
|
|
1038
|
+
content: [
|
|
1039
|
+
{
|
|
1040
|
+
type: ContentTypes.TOOL_CALL,
|
|
1041
|
+
tool_call: {
|
|
1042
|
+
id: 'invalid_tool_1',
|
|
1043
|
+
name: '',
|
|
1044
|
+
args: '{"query":"test"}',
|
|
1045
|
+
output: null,
|
|
1046
|
+
},
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
type: ContentTypes.TEXT,
|
|
1050
|
+
[ContentTypes.TEXT]: 'Here is the information.',
|
|
1051
|
+
},
|
|
1052
|
+
],
|
|
1053
|
+
},
|
|
1054
|
+
];
|
|
1055
|
+
|
|
1056
|
+
const result = formatAgentMessages(payload);
|
|
1057
|
+
|
|
1058
|
+
// Should have 1 message: AIMessage (invalid tool call is skipped)
|
|
1059
|
+
expect(result.messages).toHaveLength(1);
|
|
1060
|
+
expect(result.messages[0]).toBeInstanceOf(AIMessage);
|
|
1061
|
+
|
|
1062
|
+
// The AIMessage should have no tool calls or an empty array
|
|
1063
|
+
const toolCalls = (result.messages[0] as AIMessage).tool_calls;
|
|
1064
|
+
expect(toolCalls === undefined || toolCalls.length === 0).toBe(true);
|
|
1065
|
+
expect(result.messages[0].content).toStrictEqual([
|
|
1066
|
+
{
|
|
1067
|
+
type: ContentTypes.TEXT,
|
|
1068
|
+
[ContentTypes.TEXT]: 'Here is the information.',
|
|
1069
|
+
},
|
|
1070
|
+
]);
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
it('should NOT skip tool calls with no name but valid output', () => {
|
|
1074
|
+
const payload = [
|
|
1075
|
+
{
|
|
1076
|
+
role: 'assistant',
|
|
1077
|
+
content: [
|
|
1078
|
+
{
|
|
1079
|
+
type: ContentTypes.TOOL_CALL,
|
|
1080
|
+
tool_call: {
|
|
1081
|
+
id: 'tool_1',
|
|
1082
|
+
name: '',
|
|
1083
|
+
args: '{"query":"test"}',
|
|
1084
|
+
output: 'Valid output despite missing name',
|
|
1085
|
+
},
|
|
1086
|
+
},
|
|
1087
|
+
],
|
|
1088
|
+
},
|
|
1089
|
+
];
|
|
1090
|
+
|
|
1091
|
+
const result = formatAgentMessages(payload);
|
|
1092
|
+
|
|
1093
|
+
// Should have 2 messages: AIMessage and ToolMessage
|
|
1094
|
+
expect(result.messages).toHaveLength(2);
|
|
1095
|
+
expect(result.messages[0]).toBeInstanceOf(AIMessage);
|
|
1096
|
+
expect(result.messages[1]).toBeInstanceOf(ToolMessage);
|
|
1097
|
+
|
|
1098
|
+
// The AIMessage should have 1 tool call
|
|
1099
|
+
expect((result.messages[0] as AIMessage).tool_calls).toHaveLength(1);
|
|
1100
|
+
|
|
1101
|
+
// The ToolMessage should have the output
|
|
1102
|
+
expect((result.messages[1] as ToolMessage).tool_call_id).toBe('tool_1');
|
|
1103
|
+
expect(result.messages[1].content).toBe(
|
|
1104
|
+
'Valid output despite missing name'
|
|
1105
|
+
);
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
it('should NOT skip tool calls with valid name but no output', () => {
|
|
1109
|
+
const payload = [
|
|
1110
|
+
{
|
|
1111
|
+
role: 'assistant',
|
|
1112
|
+
content: [
|
|
1113
|
+
{
|
|
1114
|
+
type: ContentTypes.TOOL_CALL,
|
|
1115
|
+
tool_call: {
|
|
1116
|
+
id: 'tool_1',
|
|
1117
|
+
name: 'search',
|
|
1118
|
+
args: '{"query":"test"}',
|
|
1119
|
+
output: '',
|
|
1120
|
+
},
|
|
1121
|
+
},
|
|
1122
|
+
],
|
|
1123
|
+
},
|
|
1124
|
+
];
|
|
1125
|
+
|
|
1126
|
+
const result = formatAgentMessages(payload);
|
|
1127
|
+
|
|
1128
|
+
// Should have 2 messages: AIMessage and ToolMessage
|
|
1129
|
+
expect(result.messages).toHaveLength(2);
|
|
1130
|
+
expect(result.messages[0]).toBeInstanceOf(AIMessage);
|
|
1131
|
+
expect(result.messages[1]).toBeInstanceOf(ToolMessage);
|
|
1132
|
+
|
|
1133
|
+
// The AIMessage should have 1 tool call
|
|
1134
|
+
expect((result.messages[0] as AIMessage).tool_calls).toHaveLength(1);
|
|
1135
|
+
expect((result.messages[0] as AIMessage).tool_calls?.[0].name).toBe(
|
|
1136
|
+
'search'
|
|
1137
|
+
);
|
|
1138
|
+
|
|
1139
|
+
// The ToolMessage should have empty content
|
|
1140
|
+
expect((result.messages[1] as ToolMessage).tool_call_id).toBe('tool_1');
|
|
1141
|
+
expect(result.messages[1].name).toBe('search');
|
|
1142
|
+
expect(result.messages[1].content).toBe('');
|
|
1143
|
+
});
|
|
1144
|
+
});
|