veryfront 0.1.286 → 0.1.288
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/esm/deno.d.ts +4 -0
- package/esm/deno.js +7 -3
- package/esm/src/chat/conversation.d.ts +236 -0
- package/esm/src/chat/conversation.d.ts.map +1 -0
- package/esm/src/chat/conversation.js +546 -0
- package/esm/src/chat/message-prep.d.ts +12 -0
- package/esm/src/chat/message-prep.d.ts.map +1 -0
- package/esm/src/chat/message-prep.js +492 -0
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +9 -1
- package/src/deno.js +7 -3
- package/src/src/chat/conversation.ts +780 -0
- package/src/src/chat/message-prep.ts +617 -0
- package/src/src/utils/version-constant.ts +1 -1
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
import { getStringField, isReasoningPart, isToolCallPart, isToolResultPart, } from "./conversation.js";
|
|
2
|
+
const CHARS_PER_TOKEN = 4;
|
|
3
|
+
export function estimateTokens(value) {
|
|
4
|
+
return Math.ceil(JSON.stringify(value ?? "").length / CHARS_PER_TOKEN);
|
|
5
|
+
}
|
|
6
|
+
function truncate(text, maxLen) {
|
|
7
|
+
if (text.length <= maxLen)
|
|
8
|
+
return text;
|
|
9
|
+
return `${text.slice(0, maxLen)}…`;
|
|
10
|
+
}
|
|
11
|
+
export function compressTurn(messages, startIdx, endIdx) {
|
|
12
|
+
let userQuery = "";
|
|
13
|
+
const toolNames = [];
|
|
14
|
+
let assistantConclusion = "";
|
|
15
|
+
for (let i = startIdx; i <= endIdx; i++) {
|
|
16
|
+
const msg = messages[i];
|
|
17
|
+
if (msg.role === "user") {
|
|
18
|
+
const text = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
19
|
+
userQuery = truncate(text, 100);
|
|
20
|
+
}
|
|
21
|
+
else if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
22
|
+
for (const part of msg.content) {
|
|
23
|
+
if (part.type === "tool-call") {
|
|
24
|
+
toolNames.push(part.toolName);
|
|
25
|
+
}
|
|
26
|
+
else if (part.type === "text") {
|
|
27
|
+
assistantConclusion = truncate(part.text, 150);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else if (msg.role === "assistant" && typeof msg.content === "string") {
|
|
32
|
+
assistantConclusion = truncate(msg.content, 150);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const toolSummary = toolNames.length > 0 ? ` → used ${toolNames.join(", ")}` : "";
|
|
36
|
+
const conclusionSummary = assistantConclusion ? ` → ${assistantConclusion}` : "";
|
|
37
|
+
const summary = `[Compressed: ${userQuery}${toolSummary}${conclusionSummary}]`;
|
|
38
|
+
return [
|
|
39
|
+
{ role: "user", content: summary },
|
|
40
|
+
{ role: "assistant", content: "Acknowledged." },
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
function collectTurns(messages) {
|
|
44
|
+
const turns = [];
|
|
45
|
+
let currentTurn = null;
|
|
46
|
+
for (let i = 0; i < messages.length; i++) {
|
|
47
|
+
if (messages[i].role === "user") {
|
|
48
|
+
if (currentTurn) {
|
|
49
|
+
currentTurn.endIdx = i - 1;
|
|
50
|
+
turns.push(currentTurn);
|
|
51
|
+
}
|
|
52
|
+
currentTurn = { startIdx: i, endIdx: i, tokens: estimateTokens(messages[i].content) };
|
|
53
|
+
}
|
|
54
|
+
else if (currentTurn) {
|
|
55
|
+
currentTurn.endIdx = i;
|
|
56
|
+
currentTurn.tokens += estimateTokens(messages[i].content);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (currentTurn) {
|
|
60
|
+
currentTurn.endIdx = messages.length - 1;
|
|
61
|
+
turns.push(currentTurn);
|
|
62
|
+
}
|
|
63
|
+
return turns;
|
|
64
|
+
}
|
|
65
|
+
export function enforceTokenBudgetWithTurnCompression(messages, budget, overhead) {
|
|
66
|
+
const effectiveBudget = budget - overhead;
|
|
67
|
+
let totalTokens = messages.reduce((sum, message) => sum + estimateTokens(message.content), 0);
|
|
68
|
+
if (totalTokens <= effectiveBudget)
|
|
69
|
+
return messages;
|
|
70
|
+
const turns = collectTurns(messages);
|
|
71
|
+
const minKeep = Math.min(2, turns.length);
|
|
72
|
+
const result = [...messages];
|
|
73
|
+
let compressCount = 0;
|
|
74
|
+
while (totalTokens > effectiveBudget && compressCount < turns.length - minKeep) {
|
|
75
|
+
const turn = turns[compressCount];
|
|
76
|
+
if (turn.compressed) {
|
|
77
|
+
compressCount++;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const compressed = compressTurn(messages, turn.startIdx, turn.endIdx);
|
|
81
|
+
const compressedTokens = compressed.reduce((sum, message) => sum + estimateTokens(message.content), 0);
|
|
82
|
+
const saved = turn.tokens - compressedTokens;
|
|
83
|
+
if (saved <= 0) {
|
|
84
|
+
compressCount++;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const turnLength = turn.endIdx - turn.startIdx + 1;
|
|
88
|
+
result.splice(turn.startIdx, turnLength, ...compressed);
|
|
89
|
+
const indexShift = compressed.length - turnLength;
|
|
90
|
+
for (let j = compressCount + 1; j < turns.length; j++) {
|
|
91
|
+
turns[j].startIdx += indexShift;
|
|
92
|
+
turns[j].endIdx += indexShift;
|
|
93
|
+
}
|
|
94
|
+
turn.endIdx = turn.startIdx + compressed.length - 1;
|
|
95
|
+
turn.tokens = compressedTokens;
|
|
96
|
+
turn.compressed = true;
|
|
97
|
+
totalTokens -= saved;
|
|
98
|
+
compressCount++;
|
|
99
|
+
}
|
|
100
|
+
if (totalTokens <= effectiveBudget)
|
|
101
|
+
return result;
|
|
102
|
+
const finalTurns = collectTurns(result);
|
|
103
|
+
const finalMinKeep = Math.min(2, finalTurns.length);
|
|
104
|
+
let dropCount = 0;
|
|
105
|
+
while (totalTokens > effectiveBudget && dropCount < finalTurns.length - finalMinKeep) {
|
|
106
|
+
totalTokens -= finalTurns[dropCount].tokens;
|
|
107
|
+
dropCount++;
|
|
108
|
+
}
|
|
109
|
+
if (dropCount === 0)
|
|
110
|
+
return result;
|
|
111
|
+
const firstKeepIdx = finalTurns[dropCount].startIdx;
|
|
112
|
+
return result.slice(firstKeepIdx);
|
|
113
|
+
}
|
|
114
|
+
const BUDGET_RATIO = 0.85;
|
|
115
|
+
const DEFAULT_CONTEXT_WINDOW = 200_000;
|
|
116
|
+
const DEFAULT_TOKEN_BUDGET = Math.floor(DEFAULT_CONTEXT_WINDOW * BUDGET_RATIO);
|
|
117
|
+
const TOKENS_PER_TOOL = 250;
|
|
118
|
+
const MASK_THRESHOLD = 500;
|
|
119
|
+
function serializedLength(value) {
|
|
120
|
+
return JSON.stringify(value ?? "").length;
|
|
121
|
+
}
|
|
122
|
+
function tryParseJson(value) {
|
|
123
|
+
if (typeof value !== "string")
|
|
124
|
+
return value;
|
|
125
|
+
try {
|
|
126
|
+
return JSON.parse(value);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function isRecord(value) {
|
|
133
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
134
|
+
}
|
|
135
|
+
function buildToolCallMap(messages) {
|
|
136
|
+
const map = new Map();
|
|
137
|
+
for (const msg of messages) {
|
|
138
|
+
if (msg.role !== "assistant" || !Array.isArray(msg.content))
|
|
139
|
+
continue;
|
|
140
|
+
for (const part of msg.content) {
|
|
141
|
+
if (isToolCallPart(part)) {
|
|
142
|
+
map.set(part.toolCallId, { toolName: part.toolName, input: part.input });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return map;
|
|
147
|
+
}
|
|
148
|
+
function maskReadFile(input, charCount) {
|
|
149
|
+
const path = getStringField(input, "path", "unknown");
|
|
150
|
+
return `[File read: ${path} — content omitted (${charCount} chars)]`;
|
|
151
|
+
}
|
|
152
|
+
function maskBash(input, output, charCount) {
|
|
153
|
+
const cmd = truncate(getStringField(input, "command", "unknown"), 80);
|
|
154
|
+
let exitCode = "?";
|
|
155
|
+
const parsed = tryParseJson(output);
|
|
156
|
+
if (isRecord(parsed) && "exitCode" in parsed) {
|
|
157
|
+
exitCode = String(parsed.exitCode);
|
|
158
|
+
}
|
|
159
|
+
return `[Command: ${cmd} — exit ${exitCode}, output omitted (${charCount} chars)]`;
|
|
160
|
+
}
|
|
161
|
+
function maskWebSearch(output) {
|
|
162
|
+
const parsed = tryParseJson(output);
|
|
163
|
+
if (!Array.isArray(parsed))
|
|
164
|
+
return output;
|
|
165
|
+
return parsed.map((item) => {
|
|
166
|
+
if (!isRecord(item))
|
|
167
|
+
return item;
|
|
168
|
+
const { encryptedContent: _, ...rest } = item;
|
|
169
|
+
return rest;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
function maskWebFetch(input, charCount) {
|
|
173
|
+
const url = getStringField(input, "url", "unknown");
|
|
174
|
+
return `[Fetched: ${url} — content omitted (${charCount} chars)]`;
|
|
175
|
+
}
|
|
176
|
+
function maskTask(output, charCount) {
|
|
177
|
+
const parsed = tryParseJson(output);
|
|
178
|
+
if (!isRecord(parsed)) {
|
|
179
|
+
return `[task output omitted (${charCount} chars)]`;
|
|
180
|
+
}
|
|
181
|
+
const masked = {};
|
|
182
|
+
if ("success" in parsed)
|
|
183
|
+
masked.success = parsed.success;
|
|
184
|
+
if ("description" in parsed)
|
|
185
|
+
masked.description = parsed.description;
|
|
186
|
+
if ("result" in parsed) {
|
|
187
|
+
masked.result = typeof parsed.result === "string"
|
|
188
|
+
? truncate(parsed.result, 500)
|
|
189
|
+
: parsed.result;
|
|
190
|
+
}
|
|
191
|
+
return masked;
|
|
192
|
+
}
|
|
193
|
+
function maskGeneric(toolName, charCount) {
|
|
194
|
+
return `[${toolName} output omitted (${charCount} chars)]`;
|
|
195
|
+
}
|
|
196
|
+
function getOutputValue(output) {
|
|
197
|
+
if (!isRecord(output))
|
|
198
|
+
return output;
|
|
199
|
+
if ((output.type === "text" || output.type === "json") && "value" in output) {
|
|
200
|
+
return output.value;
|
|
201
|
+
}
|
|
202
|
+
return output;
|
|
203
|
+
}
|
|
204
|
+
function wrapToolResultOutput(original, newValue) {
|
|
205
|
+
const textValue = typeof newValue === "string" ? newValue : JSON.stringify(newValue);
|
|
206
|
+
if (original.type === "text") {
|
|
207
|
+
return { ...original, value: textValue };
|
|
208
|
+
}
|
|
209
|
+
if (original.type === "json") {
|
|
210
|
+
return { type: "text", value: textValue };
|
|
211
|
+
}
|
|
212
|
+
return original;
|
|
213
|
+
}
|
|
214
|
+
export function maskOldToolOutputs(messages) {
|
|
215
|
+
let lastUserIdx = -1;
|
|
216
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
217
|
+
if (messages[i].role === "user") {
|
|
218
|
+
lastUserIdx = i;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (lastUserIdx <= 0)
|
|
223
|
+
return messages;
|
|
224
|
+
const toolCallMap = buildToolCallMap(messages);
|
|
225
|
+
return messages.map((msg, idx) => {
|
|
226
|
+
if (idx >= lastUserIdx)
|
|
227
|
+
return msg;
|
|
228
|
+
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
229
|
+
const filtered = msg.content.filter((part) => !isReasoningPart(part));
|
|
230
|
+
if (filtered.length !== msg.content.length) {
|
|
231
|
+
return { ...msg, content: filtered };
|
|
232
|
+
}
|
|
233
|
+
return msg;
|
|
234
|
+
}
|
|
235
|
+
if (msg.role !== "tool" || !Array.isArray(msg.content))
|
|
236
|
+
return msg;
|
|
237
|
+
const newContent = msg.content.map((part) => {
|
|
238
|
+
if (part.type !== "tool-result") {
|
|
239
|
+
return part;
|
|
240
|
+
}
|
|
241
|
+
const rawValue = getOutputValue(part.output);
|
|
242
|
+
const charCount = serializedLength(rawValue);
|
|
243
|
+
if (charCount < MASK_THRESHOLD)
|
|
244
|
+
return part;
|
|
245
|
+
const callInfo = toolCallMap.get(part.toolCallId);
|
|
246
|
+
const toolName = part.toolName || callInfo?.toolName || "unknown";
|
|
247
|
+
const input = callInfo?.input;
|
|
248
|
+
let masked;
|
|
249
|
+
switch (toolName) {
|
|
250
|
+
case "readFile":
|
|
251
|
+
case "get_file":
|
|
252
|
+
masked = maskReadFile(input, charCount);
|
|
253
|
+
break;
|
|
254
|
+
case "bash":
|
|
255
|
+
masked = maskBash(input, rawValue, charCount);
|
|
256
|
+
break;
|
|
257
|
+
case "web_search":
|
|
258
|
+
masked = maskWebSearch(rawValue);
|
|
259
|
+
break;
|
|
260
|
+
case "web_fetch":
|
|
261
|
+
masked = maskWebFetch(input, charCount);
|
|
262
|
+
break;
|
|
263
|
+
case "task":
|
|
264
|
+
masked = maskTask(rawValue, charCount);
|
|
265
|
+
break;
|
|
266
|
+
default:
|
|
267
|
+
masked = maskGeneric(toolName, charCount);
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
return { ...part, output: wrapToolResultOutput(part.output, masked) };
|
|
271
|
+
});
|
|
272
|
+
return { ...msg, content: newContent };
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
function createSyntheticToolResult(toolCallId, toolName) {
|
|
276
|
+
return {
|
|
277
|
+
type: "tool-result",
|
|
278
|
+
toolCallId,
|
|
279
|
+
toolName,
|
|
280
|
+
output: { type: "text", value: "[tool result unavailable]" },
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
export function repairToolPairs(messages) {
|
|
284
|
+
const result = [...messages];
|
|
285
|
+
let mutated = false;
|
|
286
|
+
for (let index = 0; index < result.length; index++) {
|
|
287
|
+
const message = result[index];
|
|
288
|
+
if (message.role !== "assistant" || !Array.isArray(message.content)) {
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
const inlineResultIds = new Set();
|
|
292
|
+
for (const part of message.content) {
|
|
293
|
+
if (isToolResultPart(part)) {
|
|
294
|
+
inlineResultIds.add(part.toolCallId);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const repairedContent = [];
|
|
298
|
+
const regularToolCalls = [];
|
|
299
|
+
for (const part of message.content) {
|
|
300
|
+
repairedContent.push(part);
|
|
301
|
+
if (!isToolCallPart(part)) {
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
const toolName = part.toolName ?? "unknown";
|
|
305
|
+
if (part.providerExecuted) {
|
|
306
|
+
if (!inlineResultIds.has(part.toolCallId)) {
|
|
307
|
+
repairedContent.push(createSyntheticToolResult(part.toolCallId, toolName));
|
|
308
|
+
mutated = true;
|
|
309
|
+
}
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (!inlineResultIds.has(part.toolCallId)) {
|
|
313
|
+
regularToolCalls.push({ id: part.toolCallId, toolName });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (repairedContent.length !== message.content.length) {
|
|
317
|
+
result[index] = {
|
|
318
|
+
...message,
|
|
319
|
+
content: repairedContent,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
if (regularToolCalls.length === 0) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const nextMessage = result[index + 1];
|
|
326
|
+
const immediateResultIds = new Set();
|
|
327
|
+
if (nextMessage?.role === "tool" && Array.isArray(nextMessage.content)) {
|
|
328
|
+
for (const part of nextMessage.content) {
|
|
329
|
+
if (isToolResultPart(part)) {
|
|
330
|
+
immediateResultIds.add(part.toolCallId);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const unresolvedCalls = regularToolCalls.filter((toolCall) => !immediateResultIds.has(toolCall.id));
|
|
335
|
+
if (unresolvedCalls.length === 0) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
const movedResults = new Map();
|
|
339
|
+
for (let laterIndex = index + 2; laterIndex < result.length && movedResults.size < unresolvedCalls.length; laterIndex++) {
|
|
340
|
+
const laterMessage = result[laterIndex];
|
|
341
|
+
if (laterMessage?.role !== "tool" || !Array.isArray(laterMessage.content)) {
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
let removedFromLater = false;
|
|
345
|
+
const keptLaterContent = laterMessage.content.filter((part) => {
|
|
346
|
+
if (!isToolResultPart(part)) {
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
if (!unresolvedCalls.some((toolCall) => toolCall.id === part.toolCallId) ||
|
|
350
|
+
movedResults.has(part.toolCallId)) {
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
movedResults.set(part.toolCallId, part);
|
|
354
|
+
removedFromLater = true;
|
|
355
|
+
return false;
|
|
356
|
+
});
|
|
357
|
+
if (!removedFromLater) {
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
mutated = true;
|
|
361
|
+
if (keptLaterContent.length === 0) {
|
|
362
|
+
result.splice(laterIndex, 1);
|
|
363
|
+
laterIndex--;
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
result[laterIndex] = {
|
|
367
|
+
...laterMessage,
|
|
368
|
+
content: keptLaterContent,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
const repairedResults = unresolvedCalls.map((toolCall) => movedResults.get(toolCall.id) ?? createSyntheticToolResult(toolCall.id, toolCall.toolName));
|
|
372
|
+
if (nextMessage?.role === "tool" && Array.isArray(nextMessage.content)) {
|
|
373
|
+
result[index + 1] = {
|
|
374
|
+
...nextMessage,
|
|
375
|
+
content: [...repairedResults, ...nextMessage.content],
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
const toolMessage = {
|
|
380
|
+
role: "tool",
|
|
381
|
+
content: repairedResults,
|
|
382
|
+
};
|
|
383
|
+
result.splice(index + 1, 0, toolMessage);
|
|
384
|
+
}
|
|
385
|
+
mutated = true;
|
|
386
|
+
}
|
|
387
|
+
return mutated ? result : messages;
|
|
388
|
+
}
|
|
389
|
+
export function estimateOverhead(instructions, toolCount) {
|
|
390
|
+
const instructionTokens = estimateTokens(instructions);
|
|
391
|
+
return instructionTokens + toolCount * TOKENS_PER_TOOL;
|
|
392
|
+
}
|
|
393
|
+
export function ensureToolCallInputs(messages) {
|
|
394
|
+
let mutated = false;
|
|
395
|
+
const result = messages.map((msg) => {
|
|
396
|
+
if (msg.role !== "assistant" || !Array.isArray(msg.content))
|
|
397
|
+
return msg;
|
|
398
|
+
let msgMutated = false;
|
|
399
|
+
const newContent = msg.content.map((part) => {
|
|
400
|
+
if (isToolCallPart(part)) {
|
|
401
|
+
const input = part.input;
|
|
402
|
+
if (input === undefined || input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
403
|
+
msgMutated = true;
|
|
404
|
+
return { ...part, input: {} };
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return part;
|
|
408
|
+
});
|
|
409
|
+
if (msgMutated) {
|
|
410
|
+
mutated = true;
|
|
411
|
+
return { ...msg, content: newContent };
|
|
412
|
+
}
|
|
413
|
+
return msg;
|
|
414
|
+
});
|
|
415
|
+
return mutated ? result : messages;
|
|
416
|
+
}
|
|
417
|
+
export function compactForStep(messages, overhead = 0) {
|
|
418
|
+
const compacted = enforceTokenBudget(maskOldToolOutputs(messages), DEFAULT_TOKEN_BUDGET, overhead);
|
|
419
|
+
let end = compacted.length;
|
|
420
|
+
while (end > 1 && compacted[end - 1].role === "assistant") {
|
|
421
|
+
end--;
|
|
422
|
+
}
|
|
423
|
+
const trimmed = end < compacted.length ? compacted.slice(0, end) : compacted;
|
|
424
|
+
return ensureToolCallInputs(repairToolPairs(dedupeToolHistory(trimmed)));
|
|
425
|
+
}
|
|
426
|
+
export function dedupeToolHistory(messages) {
|
|
427
|
+
const seenToolCallIds = new Set();
|
|
428
|
+
const seenToolResultIds = new Set();
|
|
429
|
+
let mutated = false;
|
|
430
|
+
const deduped = [];
|
|
431
|
+
const filterParts = (parts) => {
|
|
432
|
+
const filtered = parts.filter((part) => {
|
|
433
|
+
if (isToolCallPart(part)) {
|
|
434
|
+
if (seenToolCallIds.has(part.toolCallId)) {
|
|
435
|
+
mutated = true;
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
seenToolCallIds.add(part.toolCallId);
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
if (isToolResultPart(part)) {
|
|
442
|
+
if (seenToolResultIds.has(part.toolCallId)) {
|
|
443
|
+
mutated = true;
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
seenToolResultIds.add(part.toolCallId);
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
return true;
|
|
450
|
+
});
|
|
451
|
+
return { filtered, changed: filtered.length !== parts.length };
|
|
452
|
+
};
|
|
453
|
+
for (const message of messages) {
|
|
454
|
+
if (message.role === "user" && Array.isArray(message.content)) {
|
|
455
|
+
const { filtered, changed } = filterParts(message.content);
|
|
456
|
+
if (!changed) {
|
|
457
|
+
deduped.push(message);
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
if (filtered.length > 0)
|
|
461
|
+
deduped.push({ ...message, content: filtered });
|
|
462
|
+
}
|
|
463
|
+
else if (message.role === "assistant" && Array.isArray(message.content)) {
|
|
464
|
+
const { filtered, changed } = filterParts(message.content);
|
|
465
|
+
if (!changed) {
|
|
466
|
+
deduped.push(message);
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
if (filtered.length > 0)
|
|
470
|
+
deduped.push({ ...message, content: filtered });
|
|
471
|
+
}
|
|
472
|
+
else if (message.role === "tool") {
|
|
473
|
+
const { filtered, changed } = filterParts(message.content);
|
|
474
|
+
if (!changed) {
|
|
475
|
+
deduped.push(message);
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
if (filtered.length > 0)
|
|
479
|
+
deduped.push({ ...message, content: filtered });
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
deduped.push(message);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return mutated ? deduped : messages;
|
|
486
|
+
}
|
|
487
|
+
export function enforceTokenBudget(messages, budget = DEFAULT_TOKEN_BUDGET, overhead = 0) {
|
|
488
|
+
if (messages.length === 0) {
|
|
489
|
+
return messages;
|
|
490
|
+
}
|
|
491
|
+
return enforceTokenBudgetWithTurnCompression(messages, budget, overhead);
|
|
492
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.288";
|
|
2
2
|
//# sourceMappingURL=version-constant.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "veryfront",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.288",
|
|
4
4
|
"description": "The simplest way to build AI-powered apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -177,6 +177,14 @@
|
|
|
177
177
|
"import": "./esm/cli/main.js",
|
|
178
178
|
"types": "./esm/cli/main.d.ts"
|
|
179
179
|
},
|
|
180
|
+
"./chat/conversation": {
|
|
181
|
+
"import": "./esm/src/chat/conversation.js",
|
|
182
|
+
"types": "./esm/src/chat/conversation.d.ts"
|
|
183
|
+
},
|
|
184
|
+
"./chat/message-prep": {
|
|
185
|
+
"import": "./esm/src/chat/message-prep.js",
|
|
186
|
+
"types": "./esm/src/chat/message-prep.d.ts"
|
|
187
|
+
},
|
|
180
188
|
"./tsconfig.json": "./tsconfig.json"
|
|
181
189
|
},
|
|
182
190
|
"scripts": {},
|
package/src/deno.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
"name": "veryfront",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.288",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"nodeModulesDir": "auto",
|
|
6
6
|
"workspace": [
|
|
@@ -63,7 +63,9 @@ export default {
|
|
|
63
63
|
"./embedding": "./src/embedding/index.ts",
|
|
64
64
|
"./extensions": "./src/extensions/index.ts",
|
|
65
65
|
"./extensions/interfaces": "./src/extensions/interfaces/index.ts",
|
|
66
|
-
"./cli": "./cli/main.ts"
|
|
66
|
+
"./cli": "./cli/main.ts",
|
|
67
|
+
"./chat/conversation": "./src/chat/conversation.ts",
|
|
68
|
+
"./chat/message-prep": "./src/chat/message-prep.ts"
|
|
67
69
|
},
|
|
68
70
|
"imports": {
|
|
69
71
|
"veryfront/head": "./src/react/runtime/core.ts",
|
|
@@ -273,7 +275,9 @@ export default {
|
|
|
273
275
|
"unified": "npm:unified@11.0.5",
|
|
274
276
|
"vfile": "npm:vfile@6.0.3",
|
|
275
277
|
"class-variance-authority": "npm:class-variance-authority@0.7.1",
|
|
276
|
-
"tailwind-merge": "npm:tailwind-merge@3.5.0"
|
|
278
|
+
"tailwind-merge": "npm:tailwind-merge@3.5.0",
|
|
279
|
+
"veryfront/chat/conversation": "./src/chat/conversation.ts",
|
|
280
|
+
"veryfront/chat/message-prep": "./src/chat/message-prep.ts"
|
|
277
281
|
},
|
|
278
282
|
"compilerOptions": {
|
|
279
283
|
"jsx": "react-jsx",
|