@librechat/agents 2.2.2 → 2.2.4
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 +51 -14
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/main.cjs +6 -4
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +21 -0
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/prune.cjs +124 -0
- package/dist/cjs/messages/prune.cjs.map +1 -0
- package/dist/cjs/run.cjs +24 -0
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +1 -0
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +65 -0
- package/dist/cjs/utils/tokens.cjs.map +1 -0
- package/dist/esm/graphs/Graph.mjs +51 -14
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/main.mjs +3 -3
- package/dist/esm/messages/format.mjs +21 -1
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/prune.mjs +122 -0
- package/dist/esm/messages/prune.mjs.map +1 -0
- package/dist/esm/run.mjs +24 -0
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +1 -0
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +62 -0
- package/dist/esm/utils/tokens.mjs.map +1 -0
- package/dist/types/graphs/Graph.d.ts +8 -1
- package/dist/types/messages/format.d.ts +9 -0
- package/dist/types/messages/index.d.ts +1 -2
- package/dist/types/messages/prune.d.ts +16 -0
- package/dist/types/types/run.d.ts +4 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/tokens.d.ts +3 -0
- package/package.json +1 -1
- package/src/graphs/Graph.ts +54 -16
- package/src/messages/format.ts +27 -0
- package/src/messages/index.ts +1 -2
- package/src/messages/prune.ts +167 -0
- package/src/messages/shiftIndexTokenCountMap.test.ts +81 -0
- package/src/run.ts +26 -0
- package/src/scripts/code_exec_simple.ts +21 -8
- package/src/specs/prune.test.ts +444 -0
- package/src/types/run.ts +5 -0
- package/src/utils/index.ts +2 -1
- package/src/utils/tokens.ts +70 -0
- package/dist/cjs/messages/transformers.cjs +0 -318
- package/dist/cjs/messages/transformers.cjs.map +0 -1
- package/dist/cjs/messages/trimMessagesFactory.cjs +0 -129
- package/dist/cjs/messages/trimMessagesFactory.cjs.map +0 -1
- package/dist/esm/messages/transformers.mjs +0 -316
- package/dist/esm/messages/transformers.mjs.map +0 -1
- package/dist/esm/messages/trimMessagesFactory.mjs +0 -127
- package/dist/esm/messages/trimMessagesFactory.mjs.map +0 -1
- package/dist/types/messages/transformers.d.ts +0 -320
- package/dist/types/messages/trimMessagesFactory.d.ts +0 -37
- package/src/messages/transformers.ts +0 -786
- package/src/messages/trimMessagesFactory.test.ts +0 -331
- package/src/messages/trimMessagesFactory.ts +0 -140
|
@@ -1,331 +0,0 @@
|
|
|
1
|
-
import { AIMessage, BaseMessage, HumanMessage, SystemMessage } from "@langchain/core/messages";
|
|
2
|
-
import { createTrimMessagesFunction } from "./trimMessagesFactory";
|
|
3
|
-
|
|
4
|
-
describe("createTrimMessagesFunction", () => {
|
|
5
|
-
// Mock token counter that simply counts characters as tokens for testing
|
|
6
|
-
const mockTokenCounter = (messages: any[]): number => {
|
|
7
|
-
return messages.reduce((sum, msg) => {
|
|
8
|
-
const content = typeof msg.content === "string"
|
|
9
|
-
? msg.content
|
|
10
|
-
: JSON.stringify(msg.content);
|
|
11
|
-
return sum + content.length;
|
|
12
|
-
}, 0);
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
it("should create a function that trims messages", async () => {
|
|
16
|
-
const trimmer = createTrimMessagesFunction({
|
|
17
|
-
trimOptions: {
|
|
18
|
-
maxTokens: 50,
|
|
19
|
-
tokenCounter: mockTokenCounter,
|
|
20
|
-
strategy: "last"
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
const messages = [
|
|
25
|
-
new SystemMessage("System prompt with some instructions"),
|
|
26
|
-
new HumanMessage("First user message"),
|
|
27
|
-
new AIMessage("First AI response"),
|
|
28
|
-
new HumanMessage("Second user message"),
|
|
29
|
-
new AIMessage("Second AI response that is quite long and should be trimmed")
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
const result = await trimmer(messages);
|
|
33
|
-
|
|
34
|
-
expect(result.messages.length).toBeLessThan(messages.length);
|
|
35
|
-
expect(result.indexTokenCountMap).toBeDefined();
|
|
36
|
-
expect(Object.keys(result.indexTokenCountMap).length).toBeGreaterThan(0);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("should maintain token counts across multiple calls", async () => {
|
|
40
|
-
const trimmer = createTrimMessagesFunction({
|
|
41
|
-
trimOptions: {
|
|
42
|
-
maxTokens: 100,
|
|
43
|
-
tokenCounter: mockTokenCounter,
|
|
44
|
-
strategy: "last",
|
|
45
|
-
includeSystem: true
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
// Initial set of messages
|
|
50
|
-
const initialMessages = [
|
|
51
|
-
new SystemMessage("System prompt"),
|
|
52
|
-
new HumanMessage("First user message"),
|
|
53
|
-
new AIMessage("First AI response")
|
|
54
|
-
];
|
|
55
|
-
|
|
56
|
-
// First call should count all messages
|
|
57
|
-
const firstResult = await trimmer(initialMessages);
|
|
58
|
-
expect(firstResult.messages.length).toBe(3);
|
|
59
|
-
expect(Object.keys(firstResult.indexTokenCountMap).length).toBe(3);
|
|
60
|
-
|
|
61
|
-
// Add a new message
|
|
62
|
-
const updatedMessages = [
|
|
63
|
-
...initialMessages,
|
|
64
|
-
new HumanMessage("Second user message")
|
|
65
|
-
];
|
|
66
|
-
|
|
67
|
-
// Second call should only count the new message
|
|
68
|
-
const secondResult = await trimmer(updatedMessages);
|
|
69
|
-
expect(secondResult.messages.length).toBe(4);
|
|
70
|
-
expect(Object.keys(secondResult.indexTokenCountMap).length).toBe(4);
|
|
71
|
-
|
|
72
|
-
// The token counts for the first three messages should be the same
|
|
73
|
-
for (let i = 0; i < 3; i++) {
|
|
74
|
-
expect(secondResult.indexTokenCountMap[i]).toBe(firstResult.indexTokenCountMap[i]);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// The new message should have a token count
|
|
78
|
-
expect(secondResult.indexTokenCountMap[3]).toBeDefined();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("should handle different trimming strategies", async () => {
|
|
82
|
-
// Test with "first" strategy
|
|
83
|
-
const firstTrimmer = createTrimMessagesFunction({
|
|
84
|
-
trimOptions: {
|
|
85
|
-
maxTokens: 50,
|
|
86
|
-
tokenCounter: mockTokenCounter,
|
|
87
|
-
strategy: "first"
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
const messages = [
|
|
92
|
-
new SystemMessage("System prompt with some instructions"),
|
|
93
|
-
new HumanMessage("First user message"),
|
|
94
|
-
new AIMessage("First AI response"),
|
|
95
|
-
new HumanMessage("Second user message"),
|
|
96
|
-
new AIMessage("Second AI response that is quite long and should be trimmed")
|
|
97
|
-
];
|
|
98
|
-
|
|
99
|
-
const firstResult = await firstTrimmer(messages);
|
|
100
|
-
expect(firstResult.messages.length).toBeLessThan(messages.length);
|
|
101
|
-
|
|
102
|
-
// Test with "last" strategy
|
|
103
|
-
const lastTrimmer = createTrimMessagesFunction({
|
|
104
|
-
trimOptions: {
|
|
105
|
-
maxTokens: 50,
|
|
106
|
-
tokenCounter: mockTokenCounter,
|
|
107
|
-
strategy: "last",
|
|
108
|
-
includeSystem: true
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
const lastResult = await lastTrimmer(messages);
|
|
113
|
-
expect(lastResult.messages.length).toBeLessThan(messages.length);
|
|
114
|
-
|
|
115
|
-
// The system message should be included in the "last" strategy result
|
|
116
|
-
const hasSystemMessage = lastResult.messages.some(
|
|
117
|
-
msg => msg instanceof SystemMessage
|
|
118
|
-
);
|
|
119
|
-
expect(hasSystemMessage).toBe(true);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it("should handle accumulating messages efficiently", async () => {
|
|
123
|
-
// Create a spy token counter to track how many times it's called
|
|
124
|
-
const tokenCounterCalls: number[] = [];
|
|
125
|
-
const spyTokenCounter = (messages: any[]): number => {
|
|
126
|
-
tokenCounterCalls.push(messages.length);
|
|
127
|
-
return messages.reduce((sum, msg) => {
|
|
128
|
-
const content = typeof msg.content === "string"
|
|
129
|
-
? msg.content
|
|
130
|
-
: JSON.stringify(msg.content);
|
|
131
|
-
return sum + content.length;
|
|
132
|
-
}, 0);
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
const trimmer = createTrimMessagesFunction({
|
|
136
|
-
trimOptions: {
|
|
137
|
-
maxTokens: 200,
|
|
138
|
-
tokenCounter: spyTokenCounter,
|
|
139
|
-
strategy: "last",
|
|
140
|
-
includeSystem: true
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
// Start with a few messages
|
|
145
|
-
let currentMessages = [
|
|
146
|
-
new SystemMessage("System prompt"),
|
|
147
|
-
new HumanMessage("First user message"),
|
|
148
|
-
new AIMessage("First AI response")
|
|
149
|
-
];
|
|
150
|
-
|
|
151
|
-
// First call
|
|
152
|
-
const firstResult = await trimmer(currentMessages);
|
|
153
|
-
|
|
154
|
-
// Add more messages one by one and verify token counting efficiency
|
|
155
|
-
for (let i = 0; i < 5; i++) {
|
|
156
|
-
// Add a new message pair
|
|
157
|
-
currentMessages = [
|
|
158
|
-
...currentMessages,
|
|
159
|
-
new HumanMessage(`User message ${i + 2}`),
|
|
160
|
-
new AIMessage(`AI response ${i + 2}`)
|
|
161
|
-
];
|
|
162
|
-
|
|
163
|
-
// Get trimmed messages
|
|
164
|
-
const result = await trimmer(currentMessages);
|
|
165
|
-
|
|
166
|
-
// Verify that all messages have token counts
|
|
167
|
-
expect(Object.keys(result.indexTokenCountMap).length).toBe(currentMessages.length);
|
|
168
|
-
|
|
169
|
-
// The token counts for existing messages should remain the same
|
|
170
|
-
for (let j = 0; j < currentMessages.length - 2; j++) {
|
|
171
|
-
if (j < Object.keys(firstResult.indexTokenCountMap).length) {
|
|
172
|
-
expect(result.indexTokenCountMap[j]).toBe(firstResult.indexTokenCountMap[j]);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Verify that the token counter was called with single messages for new messages
|
|
178
|
-
// The first call should count 3 messages (initial batch)
|
|
179
|
-
// Then each subsequent call should count only 1 message at a time (the new message)
|
|
180
|
-
expect(tokenCounterCalls[0]).toBe(1); // First message
|
|
181
|
-
expect(tokenCounterCalls[1]).toBe(1); // Second message
|
|
182
|
-
expect(tokenCounterCalls[2]).toBe(1); // Third message
|
|
183
|
-
// After initial batch, each new message should be counted individually
|
|
184
|
-
for (let i = 3; i < tokenCounterCalls.length; i++) {
|
|
185
|
-
expect(tokenCounterCalls[i]).toBe(1);
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it("should handle function-based token counter that returns a promise", async () => {
|
|
190
|
-
// Track calls to the token counter
|
|
191
|
-
const tokenCounterCalls: Array<BaseMessage[]> = [];
|
|
192
|
-
|
|
193
|
-
// Mock token counter that returns a promise
|
|
194
|
-
const asyncTokenCounter = async (messages: BaseMessage[]): Promise<number> => {
|
|
195
|
-
tokenCounterCalls.push([...messages]);
|
|
196
|
-
return Promise.resolve(messages.reduce((sum, msg) => {
|
|
197
|
-
const content = typeof msg.content === "string"
|
|
198
|
-
? msg.content
|
|
199
|
-
: JSON.stringify(msg.content);
|
|
200
|
-
return sum + content.length;
|
|
201
|
-
}, 0));
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const trimmer = createTrimMessagesFunction({
|
|
205
|
-
trimOptions: {
|
|
206
|
-
maxTokens: 40, // Smaller token limit to ensure trimming occurs
|
|
207
|
-
tokenCounter: asyncTokenCounter,
|
|
208
|
-
strategy: "last"
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
const messages = [
|
|
213
|
-
new SystemMessage("System prompt with instructions"),
|
|
214
|
-
new HumanMessage("First user message that is longer"),
|
|
215
|
-
new AIMessage("First AI response with details"),
|
|
216
|
-
new HumanMessage("Second user message with question"),
|
|
217
|
-
new AIMessage("Second AI response with answer")
|
|
218
|
-
];
|
|
219
|
-
|
|
220
|
-
const result = await trimmer(messages);
|
|
221
|
-
expect(result.messages.length).toBeLessThan(messages.length);
|
|
222
|
-
expect(Object.keys(result.indexTokenCountMap).length).toBeGreaterThan(0);
|
|
223
|
-
|
|
224
|
-
// Verify that each message was counted individually
|
|
225
|
-
expect(tokenCounterCalls.length).toBe(messages.length);
|
|
226
|
-
tokenCounterCalls.forEach(call => {
|
|
227
|
-
expect(call.length).toBe(1); // Each call should have exactly one message
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
it("should correctly handle growing message arrays", async () => {
|
|
232
|
-
// Create a map to track which messages are processed
|
|
233
|
-
const processedMessages = new Map<number, string>();
|
|
234
|
-
|
|
235
|
-
const tokenCounter = (messages: BaseMessage[]): number => {
|
|
236
|
-
// We should only get one message at a time
|
|
237
|
-
expect(messages.length).toBe(1);
|
|
238
|
-
|
|
239
|
-
const msg = messages[0];
|
|
240
|
-
const content = typeof msg.content === "string"
|
|
241
|
-
? msg.content
|
|
242
|
-
: JSON.stringify(msg.content);
|
|
243
|
-
|
|
244
|
-
// Find the index of this message in the current array
|
|
245
|
-
const msgContent = msg.content.toString();
|
|
246
|
-
|
|
247
|
-
// Log for debugging
|
|
248
|
-
console.log(`Processing message: ${msgContent}`);
|
|
249
|
-
|
|
250
|
-
// Store the message content with its index
|
|
251
|
-
processedMessages.set(processedMessages.size, msgContent);
|
|
252
|
-
|
|
253
|
-
return content.length;
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
const trimmer = createTrimMessagesFunction({
|
|
257
|
-
trimOptions: {
|
|
258
|
-
maxTokens: 500, // Large enough to include all messages
|
|
259
|
-
tokenCounter,
|
|
260
|
-
strategy: "last"
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
// Initial messages
|
|
265
|
-
const initialMessages = [
|
|
266
|
-
new SystemMessage("System prompt"),
|
|
267
|
-
new HumanMessage("First user message")
|
|
268
|
-
];
|
|
269
|
-
|
|
270
|
-
// Clear the map before first call
|
|
271
|
-
processedMessages.clear();
|
|
272
|
-
|
|
273
|
-
console.log("First call with 2 messages");
|
|
274
|
-
await trimmer(initialMessages);
|
|
275
|
-
expect(processedMessages.size).toBe(2);
|
|
276
|
-
|
|
277
|
-
// Add more messages
|
|
278
|
-
const moreMessages = [
|
|
279
|
-
...initialMessages,
|
|
280
|
-
new AIMessage("First AI response"),
|
|
281
|
-
new HumanMessage("Second user message")
|
|
282
|
-
];
|
|
283
|
-
|
|
284
|
-
// Clear the map before second call
|
|
285
|
-
processedMessages.clear();
|
|
286
|
-
|
|
287
|
-
console.log("Second call with 4 messages");
|
|
288
|
-
await trimmer(moreMessages);
|
|
289
|
-
// Only the new messages should be processed
|
|
290
|
-
expect(processedMessages.size).toBe(2);
|
|
291
|
-
|
|
292
|
-
// Check if the processed messages are the new ones
|
|
293
|
-
const secondCallMessages = Array.from(processedMessages.values());
|
|
294
|
-
expect(secondCallMessages).toContain("First AI response");
|
|
295
|
-
expect(secondCallMessages).toContain("Second user message");
|
|
296
|
-
|
|
297
|
-
// Add even more messages
|
|
298
|
-
const evenMoreMessages = [
|
|
299
|
-
...moreMessages,
|
|
300
|
-
new AIMessage("Second AI response"),
|
|
301
|
-
new HumanMessage("Third user message"),
|
|
302
|
-
new AIMessage("Third AI response")
|
|
303
|
-
];
|
|
304
|
-
|
|
305
|
-
// Clear the map before third call
|
|
306
|
-
processedMessages.clear();
|
|
307
|
-
|
|
308
|
-
console.log("Third call with 7 messages");
|
|
309
|
-
const finalResult = await trimmer(evenMoreMessages);
|
|
310
|
-
// Only the new messages should be processed
|
|
311
|
-
expect(processedMessages.size).toBe(3);
|
|
312
|
-
|
|
313
|
-
// Check if the processed messages are the new ones
|
|
314
|
-
const thirdCallMessages = Array.from(processedMessages.values());
|
|
315
|
-
expect(thirdCallMessages).toContain("Second AI response");
|
|
316
|
-
expect(thirdCallMessages).toContain("Third user message");
|
|
317
|
-
expect(thirdCallMessages).toContain("Third AI response");
|
|
318
|
-
|
|
319
|
-
// Verify all messages are in the token count map
|
|
320
|
-
expect(Object.keys(finalResult.indexTokenCountMap).length).toBe(7);
|
|
321
|
-
|
|
322
|
-
// Verify the token counts are accurate
|
|
323
|
-
for (let i = 0; i < evenMoreMessages.length; i++) {
|
|
324
|
-
const msg = evenMoreMessages[i];
|
|
325
|
-
const content = typeof msg.content === "string"
|
|
326
|
-
? msg.content
|
|
327
|
-
: JSON.stringify(msg.content);
|
|
328
|
-
expect(finalResult.indexTokenCountMap[i]).toBe(content.length);
|
|
329
|
-
}
|
|
330
|
-
});
|
|
331
|
-
});
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { BaseMessage } from "@langchain/core/messages";
|
|
2
|
-
import { trimMessages, TrimMessagesFields } from "./transformers";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Factory function that creates a trimMessages function that maintains an indexTokenCountMap
|
|
6
|
-
* during a run. This allows for efficient token counting by avoiding recounting tokens
|
|
7
|
-
* for messages that have already been processed.
|
|
8
|
-
*
|
|
9
|
-
* @param {Object} options - Configuration options for the trimMessages function
|
|
10
|
-
* @param {TrimMessagesFields} options.trimOptions - Options for the trimMessages function
|
|
11
|
-
* @returns {Function} A function that trims messages while maintaining a token count map
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```typescript
|
|
15
|
-
* // Create a trimmer function with specific options
|
|
16
|
-
* const trimmer = createTrimMessagesFunction({
|
|
17
|
-
* trimOptions: {
|
|
18
|
-
* maxTokens: 4000,
|
|
19
|
-
* tokenCounter: myTokenCounter,
|
|
20
|
-
* strategy: "last",
|
|
21
|
-
* includeSystem: true
|
|
22
|
-
* }
|
|
23
|
-
* });
|
|
24
|
-
*
|
|
25
|
-
* // Use the trimmer with an array of messages
|
|
26
|
-
* // First call with initial messages
|
|
27
|
-
* const { messages: trimmedMessages1 } = await trimmer(initialMessages);
|
|
28
|
-
*
|
|
29
|
-
* // Later call with additional messages
|
|
30
|
-
* const { messages: trimmedMessages2 } = await trimmer([...initialMessages, ...newMessages]);
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
export function createTrimMessagesFunction({ trimOptions }: {
|
|
34
|
-
trimOptions: TrimMessagesFields
|
|
35
|
-
}) {
|
|
36
|
-
// Initialize the token count map and keep track of the last processed message array
|
|
37
|
-
const indexTokenCountMap: Record<number, number> = {};
|
|
38
|
-
let lastProcessedMessages: BaseMessage[] = [];
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Trims messages while maintaining a token count map for efficiency
|
|
42
|
-
*
|
|
43
|
-
* @param {BaseMessage[]} messages - Array of messages to trim
|
|
44
|
-
* @returns {Promise<Object>} Object containing trimmed messages and the current token count map
|
|
45
|
-
*/
|
|
46
|
-
return async function trimMessagesWithMap(messages: BaseMessage[]): Promise<{
|
|
47
|
-
messages: BaseMessage[];
|
|
48
|
-
indexTokenCountMap: Record<number, number>;
|
|
49
|
-
}> {
|
|
50
|
-
// Determine which messages are new by comparing with the last processed messages
|
|
51
|
-
const newMessages: BaseMessage[] = [];
|
|
52
|
-
const newIndices: number[] = [];
|
|
53
|
-
|
|
54
|
-
// If this is the first call or we have more messages than before
|
|
55
|
-
if (lastProcessedMessages.length === 0) {
|
|
56
|
-
// First call, all messages are new
|
|
57
|
-
for (let i = 0; i < messages.length; i++) {
|
|
58
|
-
newMessages.push(messages[i]);
|
|
59
|
-
newIndices.push(i);
|
|
60
|
-
}
|
|
61
|
-
} else if (messages.length > lastProcessedMessages.length) {
|
|
62
|
-
// We have new messages, only count those
|
|
63
|
-
for (let i = lastProcessedMessages.length; i < messages.length; i++) {
|
|
64
|
-
newMessages.push(messages[i]);
|
|
65
|
-
newIndices.push(i);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Update the last processed messages
|
|
70
|
-
lastProcessedMessages = [...messages];
|
|
71
|
-
|
|
72
|
-
// Create a copy of the token counter to use for new messages
|
|
73
|
-
const originalTokenCounter = trimOptions.tokenCounter;
|
|
74
|
-
|
|
75
|
-
// Count tokens for new messages
|
|
76
|
-
for (let i = 0; i < newMessages.length; i++) {
|
|
77
|
-
const messageIndex = newIndices[i];
|
|
78
|
-
let messageTokenCount: number;
|
|
79
|
-
|
|
80
|
-
if (typeof originalTokenCounter === 'function') {
|
|
81
|
-
// Count this single message by passing it as a one-element array
|
|
82
|
-
messageTokenCount = await originalTokenCounter([newMessages[i]]);
|
|
83
|
-
} else if ('getNumTokens' in originalTokenCounter) {
|
|
84
|
-
// Use the language model's getNumTokens method
|
|
85
|
-
messageTokenCount = await originalTokenCounter.getNumTokens(newMessages[i].content);
|
|
86
|
-
} else {
|
|
87
|
-
throw new Error("Unsupported token counter type");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Store the token count for this message
|
|
91
|
-
indexTokenCountMap[messageIndex] = messageTokenCount;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Create a token counter that uses the map
|
|
95
|
-
const enhancedTokenCounter = async (msgsToCount: BaseMessage[]): Promise<number> => {
|
|
96
|
-
let totalTokens = 0;
|
|
97
|
-
|
|
98
|
-
// Use the cached token counts
|
|
99
|
-
for (let i = 0; i < msgsToCount.length; i++) {
|
|
100
|
-
if (indexTokenCountMap[i] !== undefined) {
|
|
101
|
-
totalTokens += indexTokenCountMap[i];
|
|
102
|
-
} else {
|
|
103
|
-
// This shouldn't happen if we've counted all messages
|
|
104
|
-
console.warn(`Missing token count for message at index ${i}`);
|
|
105
|
-
|
|
106
|
-
// Count it now as a fallback
|
|
107
|
-
let messageTokenCount: number;
|
|
108
|
-
|
|
109
|
-
if (typeof originalTokenCounter === 'function') {
|
|
110
|
-
messageTokenCount = await originalTokenCounter([msgsToCount[i]]);
|
|
111
|
-
} else if ('getNumTokens' in originalTokenCounter) {
|
|
112
|
-
messageTokenCount = await originalTokenCounter.getNumTokens(msgsToCount[i].content);
|
|
113
|
-
} else {
|
|
114
|
-
throw new Error("Unsupported token counter type");
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
indexTokenCountMap[i] = messageTokenCount;
|
|
118
|
-
totalTokens += messageTokenCount;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return totalTokens;
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// Create modified trim options with our enhanced token counter
|
|
126
|
-
const modifiedTrimOptions: TrimMessagesFields = {
|
|
127
|
-
...trimOptions,
|
|
128
|
-
tokenCounter: enhancedTokenCounter
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
// Trim the messages using the original function
|
|
132
|
-
const trimmedMessages = await trimMessages(messages, modifiedTrimOptions);
|
|
133
|
-
|
|
134
|
-
// Return both the trimmed messages and the updated token count map
|
|
135
|
-
return {
|
|
136
|
-
messages: trimmedMessages,
|
|
137
|
-
indexTokenCountMap
|
|
138
|
-
};
|
|
139
|
-
};
|
|
140
|
-
}
|