@librechat/agents 2.3.1 → 2.3.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/dist/cjs/graphs/Graph.cjs +6 -6
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/llm.cjs +7 -7
- package/dist/cjs/llm/anthropic/llm.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +6 -6
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +24 -24
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
- package/dist/cjs/llm/fake.cjs.map +1 -1
- package/dist/cjs/llm/text.cjs.map +1 -1
- package/dist/cjs/main.cjs +3 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +6 -6
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +11 -9
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/prune.cjs +155 -205
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +7 -7
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +1 -1
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +3 -3
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +6 -6
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/llm.mjs +7 -7
- package/dist/esm/llm/anthropic/llm.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +6 -6
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs +24 -24
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
- package/dist/esm/llm/fake.mjs.map +1 -1
- package/dist/esm/llm/text.mjs.map +1 -1
- package/dist/esm/main.mjs +1 -1
- package/dist/esm/messages/core.mjs +6 -6
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +11 -9
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/prune.mjs +153 -206
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +7 -7
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +1 -1
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +3 -3
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/types/messages/format.d.ts +1 -2
- package/dist/types/messages/prune.d.ts +31 -2
- package/dist/types/types/stream.d.ts +2 -2
- package/dist/types/utils/tokens.d.ts +1 -1
- package/package.json +4 -3
- package/src/graphs/Graph.ts +8 -8
- package/src/llm/anthropic/llm.ts +7 -8
- package/src/llm/anthropic/types.ts +4 -4
- package/src/llm/anthropic/utils/message_inputs.ts +6 -6
- package/src/llm/anthropic/utils/message_outputs.ts +39 -39
- package/src/llm/fake.ts +2 -2
- package/src/llm/text.ts +1 -1
- package/src/messages/core.ts +9 -9
- package/src/messages/format.ts +43 -42
- package/src/messages/formatAgentMessages.test.ts +35 -35
- package/src/messages/formatAgentMessages.tools.test.ts +30 -30
- package/src/messages/prune.ts +182 -255
- package/src/messages/shiftIndexTokenCountMap.test.ts +18 -18
- package/src/mockStream.ts +1 -1
- package/src/run.ts +2 -2
- package/src/specs/prune.test.ts +89 -89
- package/src/specs/reasoning.test.ts +1 -1
- package/src/specs/thinking-prune.test.ts +265 -261
- package/src/specs/tool-error.test.ts +16 -17
- package/src/stream.ts +21 -18
- package/src/tools/ToolNode.ts +1 -1
- package/src/types/stream.ts +4 -3
- package/src/utils/tokens.ts +12 -12
|
@@ -7,21 +7,21 @@ import { createPruneMessages } from '@/messages/prune';
|
|
|
7
7
|
const createTestTokenCounter = (): t.TokenCounter => {
|
|
8
8
|
return (message: BaseMessage): number => {
|
|
9
9
|
// Use type assertion to help TypeScript understand the type
|
|
10
|
-
const content = message.content as string | Array<
|
|
11
|
-
|
|
10
|
+
const content = message.content as string | Array<t.MessageContentComplex | string> | undefined;
|
|
11
|
+
|
|
12
12
|
// Handle string content
|
|
13
13
|
if (typeof content === 'string') {
|
|
14
14
|
return content.length;
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
// Handle array content
|
|
18
18
|
if (Array.isArray(content)) {
|
|
19
19
|
let totalLength = 0;
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
for (const item of content) {
|
|
22
22
|
if (typeof item === 'string') {
|
|
23
23
|
totalLength += item.length;
|
|
24
|
-
} else if (
|
|
24
|
+
} else if (typeof item === 'object') {
|
|
25
25
|
if (item.type === 'thinking' && typeof item.thinking === 'string') {
|
|
26
26
|
totalLength += item.thinking.length;
|
|
27
27
|
} else if ('text' in item && typeof item.text === 'string') {
|
|
@@ -31,10 +31,10 @@ const createTestTokenCounter = (): t.TokenCounter => {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
return totalLength;
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
// Default case - if content is null, undefined, or any other type
|
|
39
39
|
return 0;
|
|
40
40
|
};
|
|
@@ -42,129 +42,129 @@ const createTestTokenCounter = (): t.TokenCounter => {
|
|
|
42
42
|
|
|
43
43
|
describe('Prune Messages with Thinking Mode Tests', () => {
|
|
44
44
|
jest.setTimeout(30000);
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
it('should preserve thinking blocks when pruning with thinking mode enabled', () => {
|
|
47
47
|
// Create a token counter
|
|
48
48
|
const tokenCounter = createTestTokenCounter();
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
// Create messages based on the example provided
|
|
51
51
|
const userMessage = new HumanMessage({
|
|
52
52
|
content: [
|
|
53
53
|
{
|
|
54
|
-
type:
|
|
55
|
-
text:
|
|
54
|
+
type: 'text',
|
|
55
|
+
text: '/home/danny/LibreChat/gistfile1.txt\n\nread it 200 lines at a time\n\nthere are 5000 lines\n\ndo not stop until done',
|
|
56
56
|
},
|
|
57
57
|
],
|
|
58
58
|
});
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
const assistantMessageWithThinking = new AIMessage({
|
|
61
61
|
content: [
|
|
62
62
|
{
|
|
63
|
-
type:
|
|
64
|
-
thinking:
|
|
63
|
+
type: 'thinking',
|
|
64
|
+
thinking: 'The user is asking me to read a file located at `/home/danny/LibreChat/gistfile1.txt` in chunks of 200 lines at a time, mentioning that the file has 5000 lines total. They want me to continue reading through the entire file without stopping.\n\nI\'ll need to use the text editor tool to view the file in chunks of 200 lines each. Since the file has 5000 lines, I\'ll need to view it in 25 chunks (5000 ÷ 200 = 25).\n\nI\'ll need to make multiple calls to the text editor with the `view` command, specifying different line ranges for each call.\n\nLet me plan out the approach:\n1. Start with lines 1-200\n2. Then 201-400\n3. Then 401-600\n4. And so on until I reach 4801-5000\n\nFor each call, I\'ll use the `view` command with the specific line range in the `view_range` parameter. I\'ll continue until I\'ve shown all 5000 lines as requested.',
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
|
-
type:
|
|
68
|
-
text:
|
|
67
|
+
type: 'text',
|
|
68
|
+
text: 'I\'ll read the file for you in chunks of 200 lines at a time until all 5000 lines are displayed.\n\nLet me start with the first 200 lines:',
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
|
-
type:
|
|
72
|
-
id:
|
|
73
|
-
name:
|
|
74
|
-
input:
|
|
71
|
+
type: 'tool_use',
|
|
72
|
+
id: 'toolu_01YApWuFsEQCuBFDgYXmcmeZ',
|
|
73
|
+
name: 'text_editor_mcp_textEditor',
|
|
74
|
+
input: '{"command": "view", "path": "/home/danny/LibreChat/gistfile1.txt", "description": "Viewing lines 1-200 of the file", "view_range": [1,200]}',
|
|
75
75
|
},
|
|
76
76
|
],
|
|
77
77
|
});
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
const toolResponseMessage1 = new ToolMessage({
|
|
80
80
|
content: [
|
|
81
81
|
{
|
|
82
|
-
type:
|
|
83
|
-
text:
|
|
82
|
+
type: 'text',
|
|
83
|
+
text: '{"success":true,"message":"File content (truncated):"',
|
|
84
84
|
},
|
|
85
85
|
],
|
|
86
|
-
tool_call_id:
|
|
87
|
-
name:
|
|
86
|
+
tool_call_id: 'toolu_01YApWuFsEQCuBFDgYXmcmeZ',
|
|
87
|
+
name: 'text_editor_mcp_textEditor',
|
|
88
88
|
});
|
|
89
|
-
|
|
89
|
+
|
|
90
90
|
const assistantMessage2 = new AIMessage({
|
|
91
91
|
content: [
|
|
92
92
|
{
|
|
93
|
-
type:
|
|
94
|
-
text:
|
|
93
|
+
type: 'text',
|
|
94
|
+
text: 'It looks like the file content is clipped in the response. I\'ll continue with the next chunks:',
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
|
-
type:
|
|
98
|
-
id:
|
|
99
|
-
name:
|
|
100
|
-
input:
|
|
97
|
+
type: 'tool_use',
|
|
98
|
+
id: 'toolu_01VnyMQ4CvEd6zLDxxtTd6d4',
|
|
99
|
+
name: 'text_editor_mcp_textEditor',
|
|
100
|
+
input: '{"command": "view", "path": "/home/danny/LibreChat/gistfile1.txt", "description": "Viewing lines 201-400 of the file", "view_range": [201,400]}',
|
|
101
101
|
},
|
|
102
102
|
],
|
|
103
103
|
});
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
const toolResponseMessage2 = new ToolMessage({
|
|
106
106
|
content: [
|
|
107
107
|
{
|
|
108
|
-
type:
|
|
109
|
-
text:
|
|
108
|
+
type: 'text',
|
|
109
|
+
text: '{"success":true,"message":"File content (truncated):"',
|
|
110
110
|
},
|
|
111
111
|
],
|
|
112
|
-
tool_call_id:
|
|
113
|
-
name:
|
|
112
|
+
tool_call_id: 'toolu_01VnyMQ4CvEd6zLDxxtTd6d4',
|
|
113
|
+
name: 'text_editor_mcp_textEditor',
|
|
114
114
|
});
|
|
115
|
-
|
|
115
|
+
|
|
116
116
|
const assistantMessage3 = new AIMessage({
|
|
117
117
|
content: [
|
|
118
118
|
{
|
|
119
|
-
type:
|
|
120
|
-
text:
|
|
119
|
+
type: 'text',
|
|
120
|
+
text: 'Continuing with the next chunk:',
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
|
-
type:
|
|
124
|
-
id:
|
|
125
|
-
name:
|
|
126
|
-
input:
|
|
123
|
+
type: 'tool_use',
|
|
124
|
+
id: 'toolu_01TZKs4nnBc58BYXKz1Mw4fp',
|
|
125
|
+
name: 'text_editor_mcp_textEditor',
|
|
126
|
+
input: '{"command": "view", "path": "/home/danny/LibreChat/gistfile1.txt", "description": "Viewing lines 401-600 of the file", "view_range": [401,600]}',
|
|
127
127
|
},
|
|
128
128
|
],
|
|
129
129
|
});
|
|
130
|
-
|
|
130
|
+
|
|
131
131
|
const toolResponseMessage3 = new ToolMessage({
|
|
132
132
|
content: [
|
|
133
133
|
{
|
|
134
|
-
type:
|
|
135
|
-
text:
|
|
134
|
+
type: 'text',
|
|
135
|
+
text: '{"success":true,"message":"File content (truncated):"',
|
|
136
136
|
},
|
|
137
137
|
],
|
|
138
|
-
tool_call_id:
|
|
139
|
-
name:
|
|
138
|
+
tool_call_id: 'toolu_01TZKs4nnBc58BYXKz1Mw4fp',
|
|
139
|
+
name: 'text_editor_mcp_textEditor',
|
|
140
140
|
});
|
|
141
|
-
|
|
141
|
+
|
|
142
142
|
const assistantMessage4 = new AIMessage({
|
|
143
143
|
content: [
|
|
144
144
|
{
|
|
145
|
-
type:
|
|
146
|
-
text:
|
|
145
|
+
type: 'text',
|
|
146
|
+
text: 'Let\'s continue with the next chunk:',
|
|
147
147
|
},
|
|
148
148
|
{
|
|
149
|
-
type:
|
|
150
|
-
id:
|
|
151
|
-
name:
|
|
152
|
-
input:
|
|
149
|
+
type: 'tool_use',
|
|
150
|
+
id: 'toolu_01TZgBacNxjx1QNUpJg9hca5',
|
|
151
|
+
name: 'text_editor_mcp_textEditor',
|
|
152
|
+
input: '{"command": "view", "path": "/home/danny/LibreChat/gistfile1.txt", "description": "Viewing lines 601-800 of the file", "view_range": [601,800]}',
|
|
153
153
|
},
|
|
154
154
|
],
|
|
155
155
|
});
|
|
156
|
-
|
|
156
|
+
|
|
157
157
|
const toolResponseMessage4 = new ToolMessage({
|
|
158
158
|
content: [
|
|
159
159
|
{
|
|
160
|
-
type:
|
|
161
|
-
text:
|
|
160
|
+
type: 'text',
|
|
161
|
+
text: '{"success":true,"message":"File content (truncated):"',
|
|
162
162
|
},
|
|
163
163
|
],
|
|
164
|
-
tool_call_id:
|
|
165
|
-
name:
|
|
164
|
+
tool_call_id: 'toolu_01TZgBacNxjx1QNUpJg9hca5',
|
|
165
|
+
name: 'text_editor_mcp_textEditor',
|
|
166
166
|
});
|
|
167
|
-
|
|
167
|
+
|
|
168
168
|
const messages = [
|
|
169
169
|
userMessage,
|
|
170
170
|
assistantMessageWithThinking,
|
|
@@ -176,20 +176,20 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
176
176
|
assistantMessage4,
|
|
177
177
|
toolResponseMessage4,
|
|
178
178
|
];
|
|
179
|
-
|
|
179
|
+
|
|
180
180
|
// Create indexTokenCountMap based on the example provided
|
|
181
181
|
const indexTokenCountMap = {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
182
|
+
'0': 617, // userMessage
|
|
183
|
+
'1': 52, // assistantMessageWithThinking
|
|
184
|
+
'2': 4995, // toolResponseMessage1
|
|
185
|
+
'3': 307, // assistantMessage2
|
|
186
|
+
'4': 9359, // toolResponseMessage2
|
|
187
|
+
'5': 178, // assistantMessage3
|
|
188
|
+
'6': 5463, // toolResponseMessage3
|
|
189
|
+
'7': 125, // assistantMessage4
|
|
190
|
+
'8': 4264, // toolResponseMessage4
|
|
191
191
|
};
|
|
192
|
-
|
|
192
|
+
|
|
193
193
|
// Create pruneMessages function with thinking mode enabled
|
|
194
194
|
const pruneMessages = createPruneMessages({
|
|
195
195
|
maxTokens: 19800,
|
|
@@ -198,9 +198,9 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
198
198
|
indexTokenCountMap: { ...indexTokenCountMap },
|
|
199
199
|
thinkingEnabled: true,
|
|
200
200
|
});
|
|
201
|
-
|
|
201
|
+
|
|
202
202
|
// Prune messages
|
|
203
|
-
const result = pruneMessages({
|
|
203
|
+
const result = pruneMessages({
|
|
204
204
|
messages,
|
|
205
205
|
usageMetadata: {
|
|
206
206
|
input_tokens: 25254,
|
|
@@ -211,73 +211,73 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
211
211
|
cache_creation: 0,
|
|
212
212
|
},
|
|
213
213
|
},
|
|
214
|
-
|
|
214
|
+
startType: 'human',
|
|
215
215
|
});
|
|
216
|
-
|
|
216
|
+
|
|
217
217
|
// Verify that the first assistant message in the pruned context has a thinking block
|
|
218
218
|
expect(result.context.length).toBeGreaterThan(0);
|
|
219
|
-
|
|
219
|
+
|
|
220
220
|
// Find the first assistant message in the pruned context
|
|
221
221
|
const firstAssistantIndex = result.context.findIndex(msg => msg.getType() === 'ai');
|
|
222
222
|
expect(firstAssistantIndex).toBe(0);
|
|
223
|
-
|
|
223
|
+
|
|
224
224
|
const firstAssistantMsg = result.context[firstAssistantIndex];
|
|
225
225
|
expect(Array.isArray(firstAssistantMsg.content)).toBe(true);
|
|
226
|
-
|
|
226
|
+
|
|
227
227
|
// Verify that the first assistant message has a thinking block
|
|
228
|
-
const hasThinkingBlock = (firstAssistantMsg.content as
|
|
229
|
-
|
|
228
|
+
const hasThinkingBlock = (firstAssistantMsg.content as t.MessageContentComplex[]).some((item: t.MessageContentComplex) =>
|
|
229
|
+
typeof item === 'object' && item.type === 'thinking');
|
|
230
230
|
expect(hasThinkingBlock).toBe(true);
|
|
231
|
-
|
|
231
|
+
|
|
232
232
|
// Verify that the thinking block is from the original assistant message
|
|
233
|
-
const thinkingBlock = (firstAssistantMsg.content as
|
|
234
|
-
|
|
233
|
+
const thinkingBlock = (firstAssistantMsg.content as t.MessageContentComplex[]).find((item: t.MessageContentComplex) =>
|
|
234
|
+
typeof item === 'object' && item.type === 'thinking');
|
|
235
235
|
expect(thinkingBlock).toBeDefined();
|
|
236
|
-
expect(thinkingBlock.thinking).toContain(
|
|
236
|
+
expect((thinkingBlock as t.ThinkingContentText).thinking).toContain('The user is asking me to read a file');
|
|
237
237
|
});
|
|
238
|
-
|
|
238
|
+
|
|
239
239
|
it('should handle token recalculation when inserting thinking blocks', () => {
|
|
240
240
|
// Create a token counter
|
|
241
241
|
const tokenCounter = createTestTokenCounter();
|
|
242
|
-
|
|
242
|
+
|
|
243
243
|
// Create a message with thinking block
|
|
244
244
|
const assistantMessageWithThinking = new AIMessage({
|
|
245
245
|
content: [
|
|
246
246
|
{
|
|
247
|
-
type:
|
|
248
|
-
thinking:
|
|
247
|
+
type: 'thinking',
|
|
248
|
+
thinking: 'This is a thinking block',
|
|
249
249
|
},
|
|
250
250
|
{
|
|
251
|
-
type:
|
|
252
|
-
text:
|
|
251
|
+
type: 'text',
|
|
252
|
+
text: 'Response with thinking',
|
|
253
253
|
},
|
|
254
254
|
],
|
|
255
255
|
});
|
|
256
|
-
|
|
256
|
+
|
|
257
257
|
// Create a message without thinking block
|
|
258
258
|
const assistantMessageWithoutThinking = new AIMessage({
|
|
259
259
|
content: [
|
|
260
260
|
{
|
|
261
|
-
type:
|
|
262
|
-
text:
|
|
261
|
+
type: 'text',
|
|
262
|
+
text: 'Response without thinking',
|
|
263
263
|
},
|
|
264
264
|
],
|
|
265
265
|
});
|
|
266
|
-
|
|
266
|
+
|
|
267
267
|
const messages = [
|
|
268
|
-
new SystemMessage(
|
|
269
|
-
new HumanMessage(
|
|
268
|
+
new SystemMessage('System instruction'),
|
|
269
|
+
new HumanMessage('Hello'),
|
|
270
270
|
assistantMessageWithThinking,
|
|
271
|
-
new HumanMessage(
|
|
271
|
+
new HumanMessage('Next message'),
|
|
272
272
|
assistantMessageWithoutThinking,
|
|
273
273
|
];
|
|
274
|
-
|
|
274
|
+
|
|
275
275
|
// Calculate token counts for each message
|
|
276
276
|
const indexTokenCountMap: Record<string, number> = {};
|
|
277
277
|
for (let i = 0; i < messages.length; i++) {
|
|
278
278
|
indexTokenCountMap[i] = tokenCounter(messages[i]);
|
|
279
279
|
}
|
|
280
|
-
|
|
280
|
+
|
|
281
281
|
// Create pruneMessages function with thinking mode enabled
|
|
282
282
|
const pruneMessages = createPruneMessages({
|
|
283
283
|
maxTokens: 50, // Set a low token limit to force pruning
|
|
@@ -286,56 +286,44 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
286
286
|
indexTokenCountMap: { ...indexTokenCountMap },
|
|
287
287
|
thinkingEnabled: true,
|
|
288
288
|
});
|
|
289
|
-
|
|
289
|
+
|
|
290
290
|
// Prune messages
|
|
291
291
|
const result = pruneMessages({ messages });
|
|
292
|
-
|
|
292
|
+
|
|
293
293
|
// Verify that the pruned context has fewer messages than the original
|
|
294
294
|
expect(result.context.length).toBeLessThan(messages.length);
|
|
295
|
-
|
|
296
|
-
// Find the first assistant message in the pruned context
|
|
297
|
-
const firstAssistantIndex = result.context.findIndex(msg => msg.getType() === 'ai');
|
|
298
|
-
expect(firstAssistantIndex).toBeGreaterThan(-1);
|
|
299
|
-
|
|
300
|
-
const firstAssistantMsg = result.context[firstAssistantIndex];
|
|
301
|
-
expect(Array.isArray(firstAssistantMsg.content)).toBe(true);
|
|
302
|
-
|
|
303
|
-
// Verify that the first assistant message has a thinking block
|
|
304
|
-
const hasThinkingBlock = (firstAssistantMsg.content as any[]).some(item =>
|
|
305
|
-
item && typeof item === 'object' && item.type === 'thinking');
|
|
306
|
-
expect(hasThinkingBlock).toBe(true);
|
|
307
295
|
});
|
|
308
|
-
|
|
296
|
+
|
|
309
297
|
it('should not modify messages when under token limit', () => {
|
|
310
298
|
// Create a token counter
|
|
311
299
|
const tokenCounter = createTestTokenCounter();
|
|
312
|
-
|
|
300
|
+
|
|
313
301
|
// Create a message with thinking block
|
|
314
302
|
const assistantMessageWithThinking = new AIMessage({
|
|
315
303
|
content: [
|
|
316
304
|
{
|
|
317
|
-
type:
|
|
318
|
-
thinking:
|
|
305
|
+
type: 'thinking',
|
|
306
|
+
thinking: 'This is a thinking block',
|
|
319
307
|
},
|
|
320
308
|
{
|
|
321
|
-
type:
|
|
322
|
-
text:
|
|
309
|
+
type: 'text',
|
|
310
|
+
text: 'Response with thinking',
|
|
323
311
|
},
|
|
324
312
|
],
|
|
325
313
|
});
|
|
326
|
-
|
|
314
|
+
|
|
327
315
|
const messages = [
|
|
328
|
-
new SystemMessage(
|
|
329
|
-
new HumanMessage(
|
|
316
|
+
new SystemMessage('System instruction'),
|
|
317
|
+
new HumanMessage('Hello'),
|
|
330
318
|
assistantMessageWithThinking,
|
|
331
319
|
];
|
|
332
|
-
|
|
320
|
+
|
|
333
321
|
// Calculate token counts for each message
|
|
334
322
|
const indexTokenCountMap: Record<string, number> = {};
|
|
335
323
|
for (let i = 0; i < messages.length; i++) {
|
|
336
324
|
indexTokenCountMap[i] = tokenCounter(messages[i]);
|
|
337
325
|
}
|
|
338
|
-
|
|
326
|
+
|
|
339
327
|
// Create pruneMessages function with thinking mode enabled
|
|
340
328
|
const pruneMessages = createPruneMessages({
|
|
341
329
|
maxTokens: 1000, // Set a high token limit to avoid pruning
|
|
@@ -344,34 +332,34 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
344
332
|
indexTokenCountMap: { ...indexTokenCountMap },
|
|
345
333
|
thinkingEnabled: true,
|
|
346
334
|
});
|
|
347
|
-
|
|
335
|
+
|
|
348
336
|
// Prune messages
|
|
349
337
|
const result = pruneMessages({ messages });
|
|
350
|
-
|
|
338
|
+
|
|
351
339
|
// Verify that all messages are preserved
|
|
352
340
|
expect(result.context.length).toBe(messages.length);
|
|
353
341
|
expect(result.context).toEqual(messages);
|
|
354
342
|
});
|
|
355
|
-
|
|
343
|
+
|
|
356
344
|
it('should handle the case when no thinking blocks are found', () => {
|
|
357
345
|
// Create a token counter
|
|
358
346
|
const tokenCounter = createTestTokenCounter();
|
|
359
|
-
|
|
347
|
+
|
|
360
348
|
// Create messages without thinking blocks
|
|
361
349
|
const messages = [
|
|
362
|
-
new SystemMessage(
|
|
363
|
-
new HumanMessage(
|
|
364
|
-
new AIMessage(
|
|
365
|
-
new HumanMessage(
|
|
366
|
-
new AIMessage(
|
|
350
|
+
new SystemMessage('System instruction'),
|
|
351
|
+
new HumanMessage('Hello'),
|
|
352
|
+
new AIMessage('Response without thinking'),
|
|
353
|
+
new HumanMessage('Next message'),
|
|
354
|
+
new AIMessage('Another response without thinking'),
|
|
367
355
|
];
|
|
368
|
-
|
|
356
|
+
|
|
369
357
|
// Calculate token counts for each message
|
|
370
358
|
const indexTokenCountMap: Record<string, number> = {};
|
|
371
359
|
for (let i = 0; i < messages.length; i++) {
|
|
372
360
|
indexTokenCountMap[i] = tokenCounter(messages[i]);
|
|
373
361
|
}
|
|
374
|
-
|
|
362
|
+
|
|
375
363
|
// Create pruneMessages function with thinking mode enabled
|
|
376
364
|
const pruneMessages = createPruneMessages({
|
|
377
365
|
maxTokens: 50, // Set a low token limit to force pruning
|
|
@@ -380,76 +368,76 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
380
368
|
indexTokenCountMap: { ...indexTokenCountMap },
|
|
381
369
|
thinkingEnabled: true,
|
|
382
370
|
});
|
|
383
|
-
|
|
371
|
+
|
|
384
372
|
// Prune messages
|
|
385
373
|
const result = pruneMessages({ messages });
|
|
386
|
-
|
|
374
|
+
|
|
387
375
|
// Verify that the pruned context has fewer messages than the original
|
|
388
376
|
expect(result.context.length).toBeLessThan(messages.length);
|
|
389
|
-
|
|
377
|
+
|
|
390
378
|
// The function should not throw an error even though no thinking blocks are found
|
|
391
379
|
expect(() => pruneMessages({ messages })).not.toThrow();
|
|
392
380
|
});
|
|
393
|
-
|
|
381
|
+
|
|
394
382
|
it('should preserve AI <--> tool message correspondences when pruning', () => {
|
|
395
383
|
// Create a token counter
|
|
396
384
|
const tokenCounter = createTestTokenCounter();
|
|
397
|
-
|
|
385
|
+
|
|
398
386
|
// Create messages with tool calls
|
|
399
387
|
const assistantMessageWithToolCall = new AIMessage({
|
|
400
388
|
content: [
|
|
401
389
|
{
|
|
402
|
-
type:
|
|
403
|
-
text:
|
|
390
|
+
type: 'text',
|
|
391
|
+
text: 'Let me check that file:',
|
|
404
392
|
},
|
|
405
393
|
{
|
|
406
|
-
type:
|
|
407
|
-
id:
|
|
408
|
-
name:
|
|
409
|
-
input:
|
|
394
|
+
type: 'tool_use',
|
|
395
|
+
id: 'tool123',
|
|
396
|
+
name: 'text_editor_mcp_textEditor',
|
|
397
|
+
input: '{"command": "view", "path": "/path/to/file.txt"}',
|
|
410
398
|
},
|
|
411
399
|
],
|
|
412
400
|
});
|
|
413
|
-
|
|
401
|
+
|
|
414
402
|
const toolResponseMessage = new ToolMessage({
|
|
415
403
|
content: [
|
|
416
404
|
{
|
|
417
|
-
type:
|
|
418
|
-
text:
|
|
405
|
+
type: 'text',
|
|
406
|
+
text: '{"success":true,"message":"File content"}',
|
|
419
407
|
},
|
|
420
408
|
],
|
|
421
|
-
tool_call_id:
|
|
422
|
-
name:
|
|
409
|
+
tool_call_id: 'tool123',
|
|
410
|
+
name: 'text_editor_mcp_textEditor',
|
|
423
411
|
});
|
|
424
|
-
|
|
412
|
+
|
|
425
413
|
const assistantMessageWithThinking = new AIMessage({
|
|
426
414
|
content: [
|
|
427
415
|
{
|
|
428
|
-
type:
|
|
429
|
-
thinking:
|
|
416
|
+
type: 'thinking',
|
|
417
|
+
thinking: 'This is a thinking block',
|
|
430
418
|
},
|
|
431
419
|
{
|
|
432
|
-
type:
|
|
433
|
-
text:
|
|
420
|
+
type: 'text',
|
|
421
|
+
text: 'Response with thinking',
|
|
434
422
|
},
|
|
435
423
|
],
|
|
436
424
|
});
|
|
437
|
-
|
|
425
|
+
|
|
438
426
|
const messages = [
|
|
439
|
-
new SystemMessage(
|
|
440
|
-
new HumanMessage(
|
|
427
|
+
new SystemMessage('System instruction'),
|
|
428
|
+
new HumanMessage('Hello'),
|
|
441
429
|
assistantMessageWithToolCall,
|
|
442
430
|
toolResponseMessage,
|
|
443
|
-
new HumanMessage(
|
|
431
|
+
new HumanMessage('Next message'),
|
|
444
432
|
assistantMessageWithThinking,
|
|
445
433
|
];
|
|
446
|
-
|
|
434
|
+
|
|
447
435
|
// Calculate token counts for each message
|
|
448
436
|
const indexTokenCountMap: Record<string, number> = {};
|
|
449
437
|
for (let i = 0; i < messages.length; i++) {
|
|
450
438
|
indexTokenCountMap[i] = tokenCounter(messages[i]);
|
|
451
439
|
}
|
|
452
|
-
|
|
440
|
+
|
|
453
441
|
// Create pruneMessages function with thinking mode enabled and a low token limit
|
|
454
442
|
const pruneMessages = createPruneMessages({
|
|
455
443
|
maxTokens: 100, // Set a low token limit to force pruning
|
|
@@ -458,107 +446,107 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
458
446
|
indexTokenCountMap: { ...indexTokenCountMap },
|
|
459
447
|
thinkingEnabled: true,
|
|
460
448
|
});
|
|
461
|
-
|
|
449
|
+
|
|
462
450
|
// Prune messages
|
|
463
451
|
const result = pruneMessages({ messages });
|
|
464
|
-
|
|
452
|
+
|
|
465
453
|
// Find assistant message with tool call and its corresponding tool message in the pruned context
|
|
466
|
-
const assistantIndex = result.context.findIndex(msg =>
|
|
467
|
-
msg.getType() === 'ai' &&
|
|
468
|
-
Array.isArray(msg.content) &&
|
|
469
|
-
msg.content.some(item =>
|
|
454
|
+
const assistantIndex = result.context.findIndex(msg =>
|
|
455
|
+
msg.getType() === 'ai' &&
|
|
456
|
+
Array.isArray(msg.content) &&
|
|
457
|
+
msg.content.some(item => typeof item === 'object' && item.type === 'tool_use' && item.id === 'tool123')
|
|
470
458
|
);
|
|
471
|
-
|
|
459
|
+
|
|
472
460
|
// If the assistant message with tool call is in the context, its corresponding tool message should also be there
|
|
473
461
|
if (assistantIndex !== -1) {
|
|
474
|
-
const toolIndex = result.context.findIndex(msg =>
|
|
475
|
-
msg.getType() === 'tool' &&
|
|
476
|
-
'tool_call_id' in msg &&
|
|
462
|
+
const toolIndex = result.context.findIndex(msg =>
|
|
463
|
+
msg.getType() === 'tool' &&
|
|
464
|
+
'tool_call_id' in msg &&
|
|
477
465
|
msg.tool_call_id === 'tool123'
|
|
478
466
|
);
|
|
479
|
-
|
|
467
|
+
|
|
480
468
|
expect(toolIndex).not.toBe(-1);
|
|
481
469
|
}
|
|
482
|
-
|
|
470
|
+
|
|
483
471
|
// If the tool message is in the context, its corresponding assistant message should also be there
|
|
484
|
-
const toolIndex = result.context.findIndex(msg =>
|
|
485
|
-
msg.getType() === 'tool' &&
|
|
486
|
-
'tool_call_id' in msg &&
|
|
472
|
+
const toolIndex = result.context.findIndex(msg =>
|
|
473
|
+
msg.getType() === 'tool' &&
|
|
474
|
+
'tool_call_id' in msg &&
|
|
487
475
|
msg.tool_call_id === 'tool123'
|
|
488
476
|
);
|
|
489
|
-
|
|
477
|
+
|
|
490
478
|
if (toolIndex !== -1) {
|
|
491
|
-
const assistantWithToolIndex = result.context.findIndex(msg =>
|
|
492
|
-
msg.getType() === 'ai' &&
|
|
493
|
-
Array.isArray(msg.content) &&
|
|
494
|
-
msg.content.some(item =>
|
|
479
|
+
const assistantWithToolIndex = result.context.findIndex(msg =>
|
|
480
|
+
msg.getType() === 'ai' &&
|
|
481
|
+
Array.isArray(msg.content) &&
|
|
482
|
+
msg.content.some(item => typeof item === 'object' && item.type === 'tool_use' && item.id === 'tool123')
|
|
495
483
|
);
|
|
496
|
-
|
|
484
|
+
|
|
497
485
|
expect(assistantWithToolIndex).not.toBe(-1);
|
|
498
486
|
}
|
|
499
487
|
});
|
|
500
|
-
|
|
501
|
-
it('should ensure an assistant message with thinking appears
|
|
488
|
+
|
|
489
|
+
it('should ensure an assistant message with thinking appears in the latest sequence of assistant/tool messages', () => {
|
|
502
490
|
// Create a token counter
|
|
503
491
|
const tokenCounter = createTestTokenCounter();
|
|
504
|
-
|
|
492
|
+
|
|
505
493
|
// Create messages with the latest message being an assistant message with thinking
|
|
506
494
|
const assistantMessageWithThinking = new AIMessage({
|
|
507
495
|
content: [
|
|
508
496
|
{
|
|
509
|
-
type:
|
|
510
|
-
thinking:
|
|
497
|
+
type: 'thinking',
|
|
498
|
+
thinking: 'This is a thinking block',
|
|
511
499
|
},
|
|
512
500
|
{
|
|
513
|
-
type:
|
|
514
|
-
text:
|
|
501
|
+
type: 'text',
|
|
502
|
+
text: 'Response with thinking',
|
|
515
503
|
},
|
|
516
504
|
],
|
|
517
505
|
});
|
|
518
|
-
|
|
506
|
+
|
|
519
507
|
// Create an assistant message with tool use
|
|
520
508
|
const assistantMessageWithToolUse = new AIMessage({
|
|
521
509
|
content: [
|
|
522
510
|
{
|
|
523
|
-
type:
|
|
524
|
-
text:
|
|
511
|
+
type: 'text',
|
|
512
|
+
text: 'Let me check that file:',
|
|
525
513
|
},
|
|
526
514
|
{
|
|
527
|
-
type:
|
|
528
|
-
id:
|
|
529
|
-
name:
|
|
530
|
-
input:
|
|
515
|
+
type: 'tool_use',
|
|
516
|
+
id: 'tool123',
|
|
517
|
+
name: 'text_editor_mcp_textEditor',
|
|
518
|
+
input: '{"command": "view", "path": "/path/to/file.txt"}',
|
|
531
519
|
},
|
|
532
520
|
],
|
|
533
521
|
});
|
|
534
|
-
|
|
522
|
+
|
|
535
523
|
// Create a tool response message
|
|
536
524
|
const toolResponseMessage = new ToolMessage({
|
|
537
525
|
content: [
|
|
538
526
|
{
|
|
539
|
-
type:
|
|
540
|
-
text:
|
|
527
|
+
type: 'text',
|
|
528
|
+
text: '{"success":true,"message":"File content"}',
|
|
541
529
|
},
|
|
542
530
|
],
|
|
543
|
-
tool_call_id:
|
|
544
|
-
name:
|
|
531
|
+
tool_call_id: 'tool123',
|
|
532
|
+
name: 'text_editor_mcp_textEditor',
|
|
545
533
|
});
|
|
546
|
-
|
|
534
|
+
|
|
547
535
|
// Test case without system message
|
|
548
536
|
const messagesWithoutSystem = [
|
|
549
|
-
new HumanMessage(
|
|
537
|
+
new HumanMessage('Hello'),
|
|
550
538
|
assistantMessageWithToolUse,
|
|
551
539
|
toolResponseMessage,
|
|
552
|
-
new HumanMessage(
|
|
540
|
+
new HumanMessage('Next message'),
|
|
553
541
|
assistantMessageWithThinking, // Latest message is an assistant message with thinking
|
|
554
542
|
];
|
|
555
|
-
|
|
543
|
+
|
|
556
544
|
// Calculate token counts for each message
|
|
557
545
|
const indexTokenCountMapWithoutSystem: Record<string, number> = {};
|
|
558
546
|
for (let i = 0; i < messagesWithoutSystem.length; i++) {
|
|
559
547
|
indexTokenCountMapWithoutSystem[i] = tokenCounter(messagesWithoutSystem[i]);
|
|
560
548
|
}
|
|
561
|
-
|
|
549
|
+
|
|
562
550
|
// Create pruneMessages function with thinking mode enabled
|
|
563
551
|
const pruneMessagesWithoutSystem = createPruneMessages({
|
|
564
552
|
maxTokens: 100, // Set a token limit to force some pruning
|
|
@@ -567,37 +555,45 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
567
555
|
indexTokenCountMap: { ...indexTokenCountMapWithoutSystem },
|
|
568
556
|
thinkingEnabled: true,
|
|
569
557
|
});
|
|
570
|
-
|
|
558
|
+
|
|
571
559
|
// Prune messages
|
|
572
560
|
const resultWithoutSystem = pruneMessagesWithoutSystem({ messages: messagesWithoutSystem });
|
|
573
|
-
|
|
574
|
-
// Verify that the
|
|
561
|
+
|
|
562
|
+
// Verify that the pruned context contains at least one message
|
|
575
563
|
expect(resultWithoutSystem.context.length).toBeGreaterThan(0);
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
const
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
564
|
+
|
|
565
|
+
// Find all assistant messages in the latest sequence (after the last human message)
|
|
566
|
+
const lastHumanIndex = resultWithoutSystem.context.map(msg => msg.getType()).lastIndexOf('human');
|
|
567
|
+
const assistantMessagesAfterLastHuman = resultWithoutSystem.context.slice(lastHumanIndex + 1)
|
|
568
|
+
.filter(msg => msg.getType() === 'ai');
|
|
569
|
+
|
|
570
|
+
// Verify that at least one assistant message exists in the latest sequence
|
|
571
|
+
expect(assistantMessagesAfterLastHuman.length).toBeGreaterThan(0);
|
|
572
|
+
|
|
573
|
+
// Verify that at least one of these assistant messages has a thinking block
|
|
574
|
+
const hasThinkingBlock = assistantMessagesAfterLastHuman.some(msg => {
|
|
575
|
+
const content = msg.content as t.MessageContentComplex[];
|
|
576
|
+
return Array.isArray(content) && content.some(item =>
|
|
577
|
+
typeof item === 'object' && item.type === 'thinking');
|
|
578
|
+
});
|
|
583
579
|
expect(hasThinkingBlock).toBe(true);
|
|
584
|
-
|
|
580
|
+
|
|
585
581
|
// Test case with system message
|
|
586
582
|
const messagesWithSystem = [
|
|
587
|
-
new SystemMessage(
|
|
588
|
-
new HumanMessage(
|
|
583
|
+
new SystemMessage('System instruction'),
|
|
584
|
+
new HumanMessage('Hello'),
|
|
589
585
|
assistantMessageWithToolUse,
|
|
590
586
|
toolResponseMessage,
|
|
591
|
-
new HumanMessage(
|
|
587
|
+
new HumanMessage('Next message'),
|
|
592
588
|
assistantMessageWithThinking, // Latest message is an assistant message with thinking
|
|
593
589
|
];
|
|
594
|
-
|
|
590
|
+
|
|
595
591
|
// Calculate token counts for each message
|
|
596
592
|
const indexTokenCountMapWithSystem: Record<string, number> = {};
|
|
597
593
|
for (let i = 0; i < messagesWithSystem.length; i++) {
|
|
598
594
|
indexTokenCountMapWithSystem[i] = tokenCounter(messagesWithSystem[i]);
|
|
599
595
|
}
|
|
600
|
-
|
|
596
|
+
|
|
601
597
|
// Create pruneMessages function with thinking mode enabled
|
|
602
598
|
const pruneMessagesWithSystem = createPruneMessages({
|
|
603
599
|
maxTokens: 120, // Set a token limit to force some pruning but keep system message
|
|
@@ -606,68 +602,76 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
606
602
|
indexTokenCountMap: { ...indexTokenCountMapWithSystem },
|
|
607
603
|
thinkingEnabled: true,
|
|
608
604
|
});
|
|
609
|
-
|
|
605
|
+
|
|
610
606
|
// Prune messages
|
|
611
607
|
const resultWithSystem = pruneMessagesWithSystem({ messages: messagesWithSystem });
|
|
612
|
-
|
|
613
|
-
// Verify that the system message remains first
|
|
608
|
+
|
|
609
|
+
// Verify that the system message remains first
|
|
614
610
|
expect(resultWithSystem.context.length).toBeGreaterThan(1);
|
|
615
611
|
expect(resultWithSystem.context[0].getType()).toBe('system');
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
const
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
expect(
|
|
612
|
+
|
|
613
|
+
// Find all assistant messages in the latest sequence (after the last human message)
|
|
614
|
+
const lastHumanIndexWithSystem = resultWithSystem.context.map(msg => msg.getType()).lastIndexOf('human');
|
|
615
|
+
const assistantMessagesAfterLastHumanWithSystem = resultWithSystem.context.slice(lastHumanIndexWithSystem + 1)
|
|
616
|
+
.filter(msg => msg.getType() === 'ai');
|
|
617
|
+
|
|
618
|
+
// Verify that at least one assistant message exists in the latest sequence
|
|
619
|
+
expect(assistantMessagesAfterLastHumanWithSystem.length).toBeGreaterThan(0);
|
|
620
|
+
|
|
621
|
+
// Verify that at least one of these assistant messages has a thinking block
|
|
622
|
+
const hasThinkingBlockWithSystem = assistantMessagesAfterLastHumanWithSystem.some(msg => {
|
|
623
|
+
const content = msg.content as t.MessageContentComplex[];
|
|
624
|
+
return Array.isArray(content) && content.some(item =>
|
|
625
|
+
typeof item === 'object' && item.type === 'thinking');
|
|
626
|
+
});
|
|
627
|
+
expect(hasThinkingBlockWithSystem).toBe(true);
|
|
624
628
|
});
|
|
625
|
-
|
|
629
|
+
|
|
626
630
|
it('should look for thinking blocks starting from the most recent messages', () => {
|
|
627
631
|
// Create a token counter
|
|
628
632
|
const tokenCounter = createTestTokenCounter();
|
|
629
|
-
|
|
633
|
+
|
|
630
634
|
// Create messages with multiple thinking blocks
|
|
631
635
|
const olderAssistantMessageWithThinking = new AIMessage({
|
|
632
636
|
content: [
|
|
633
637
|
{
|
|
634
|
-
type:
|
|
635
|
-
thinking:
|
|
638
|
+
type: 'thinking',
|
|
639
|
+
thinking: 'This is an older thinking block',
|
|
636
640
|
},
|
|
637
641
|
{
|
|
638
|
-
type:
|
|
639
|
-
text:
|
|
642
|
+
type: 'text',
|
|
643
|
+
text: 'Older response with thinking',
|
|
640
644
|
},
|
|
641
645
|
],
|
|
642
646
|
});
|
|
643
|
-
|
|
647
|
+
|
|
644
648
|
const newerAssistantMessageWithThinking = new AIMessage({
|
|
645
649
|
content: [
|
|
646
650
|
{
|
|
647
|
-
type:
|
|
648
|
-
thinking:
|
|
651
|
+
type: 'thinking',
|
|
652
|
+
thinking: 'This is a newer thinking block',
|
|
649
653
|
},
|
|
650
654
|
{
|
|
651
|
-
type:
|
|
652
|
-
text:
|
|
655
|
+
type: 'text',
|
|
656
|
+
text: 'Newer response with thinking',
|
|
653
657
|
},
|
|
654
658
|
],
|
|
655
659
|
});
|
|
656
|
-
|
|
660
|
+
|
|
657
661
|
const messages = [
|
|
658
|
-
new SystemMessage(
|
|
659
|
-
new HumanMessage(
|
|
662
|
+
new SystemMessage('System instruction'),
|
|
663
|
+
new HumanMessage('Hello'),
|
|
660
664
|
olderAssistantMessageWithThinking,
|
|
661
|
-
new HumanMessage(
|
|
665
|
+
new HumanMessage('Next message'),
|
|
662
666
|
newerAssistantMessageWithThinking,
|
|
663
667
|
];
|
|
664
|
-
|
|
668
|
+
|
|
665
669
|
// Calculate token counts for each message
|
|
666
670
|
const indexTokenCountMap: Record<string, number> = {};
|
|
667
671
|
for (let i = 0; i < messages.length; i++) {
|
|
668
672
|
indexTokenCountMap[i] = tokenCounter(messages[i]);
|
|
669
673
|
}
|
|
670
|
-
|
|
674
|
+
|
|
671
675
|
// Create pruneMessages function with thinking mode enabled
|
|
672
676
|
// Set a token limit that will force pruning of the older assistant message
|
|
673
677
|
const pruneMessages = createPruneMessages({
|
|
@@ -677,23 +681,23 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
677
681
|
indexTokenCountMap: { ...indexTokenCountMap },
|
|
678
682
|
thinkingEnabled: true,
|
|
679
683
|
});
|
|
680
|
-
|
|
684
|
+
|
|
681
685
|
// Prune messages
|
|
682
686
|
const result = pruneMessages({ messages });
|
|
683
|
-
|
|
687
|
+
|
|
684
688
|
// Find the first assistant message in the pruned context
|
|
685
689
|
const firstAssistantIndex = result.context.findIndex(msg => msg.getType() === 'ai');
|
|
686
690
|
expect(firstAssistantIndex).not.toBe(-1);
|
|
687
|
-
|
|
691
|
+
|
|
688
692
|
const firstAssistantMsg = result.context[firstAssistantIndex];
|
|
689
693
|
expect(Array.isArray(firstAssistantMsg.content)).toBe(true);
|
|
690
|
-
|
|
694
|
+
|
|
691
695
|
// Verify that the first assistant message has a thinking block
|
|
692
|
-
const thinkingBlock = (firstAssistantMsg.content as
|
|
693
|
-
|
|
696
|
+
const thinkingBlock = (firstAssistantMsg.content as t.MessageContentComplex[]).find(item =>
|
|
697
|
+
typeof item === 'object' && item.type === 'thinking');
|
|
694
698
|
expect(thinkingBlock).toBeDefined();
|
|
695
|
-
|
|
699
|
+
|
|
696
700
|
// Verify that it's the newer thinking block
|
|
697
|
-
expect(thinkingBlock.thinking).toContain(
|
|
701
|
+
expect((thinkingBlock as t.ThinkingContentText).thinking).toContain('newer thinking block');
|
|
698
702
|
});
|
|
699
703
|
});
|