llmist 1.2.0 → 1.3.1
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/README.md +12 -75
- package/dist/{chunk-KORMY3CD.js → chunk-RZTAKIDE.js} +605 -4
- package/dist/chunk-RZTAKIDE.js.map +1 -0
- package/dist/{chunk-LELPPETT.js → chunk-TFIKR2RK.js} +459 -3
- package/dist/chunk-TFIKR2RK.js.map +1 -0
- package/dist/cli.cjs +628 -23
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +49 -22
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +769 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +354 -32
- package/dist/index.d.ts +354 -32
- package/dist/index.js +177 -2
- package/dist/index.js.map +1 -1
- package/dist/{mock-stream-DKF5yatf.d.cts → mock-stream-DNt-HBTn.d.cts} +525 -79
- package/dist/{mock-stream-DKF5yatf.d.ts → mock-stream-DNt-HBTn.d.ts} +525 -79
- package/dist/testing/index.cjs +1063 -4
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +437 -3
- package/dist/testing/index.d.ts +437 -3
- package/dist/testing/index.js +54 -4
- package/package.json +1 -1
- package/dist/chunk-KORMY3CD.js.map +0 -1
- package/dist/chunk-LELPPETT.js.map +0 -1
|
@@ -348,10 +348,21 @@ function resolveRulesTemplate(rules, context) {
|
|
|
348
348
|
}
|
|
349
349
|
return [resolved];
|
|
350
350
|
}
|
|
351
|
-
|
|
351
|
+
function resolveHintTemplate(template, defaultValue, context) {
|
|
352
|
+
const resolved = template ?? defaultValue;
|
|
353
|
+
if (typeof resolved === "function") {
|
|
354
|
+
return resolved(context);
|
|
355
|
+
}
|
|
356
|
+
return resolved.replace(/\{iteration\}/g, String(context.iteration)).replace(/\{maxIterations\}/g, String(context.maxIterations)).replace(/\{remaining\}/g, String(context.remaining));
|
|
357
|
+
}
|
|
358
|
+
var DEFAULT_HINTS, DEFAULT_PROMPTS;
|
|
352
359
|
var init_prompt_config = __esm({
|
|
353
360
|
"src/core/prompt-config.ts"() {
|
|
354
361
|
"use strict";
|
|
362
|
+
DEFAULT_HINTS = {
|
|
363
|
+
parallelGadgetsHint: "Tip: You can call multiple gadgets in a single response for efficiency.",
|
|
364
|
+
iterationProgressHint: "[Iteration {iteration}/{maxIterations}] Plan your actions accordingly."
|
|
365
|
+
};
|
|
355
366
|
DEFAULT_PROMPTS = {
|
|
356
367
|
mainInstruction: [
|
|
357
368
|
"\u26A0\uFE0F CRITICAL: RESPOND ONLY WITH GADGET INVOCATIONS",
|
|
@@ -1116,6 +1127,417 @@ var init_output_viewer = __esm({
|
|
|
1116
1127
|
}
|
|
1117
1128
|
});
|
|
1118
1129
|
|
|
1130
|
+
// src/agent/compaction/config.ts
|
|
1131
|
+
function resolveCompactionConfig(config = {}) {
|
|
1132
|
+
const trigger = config.triggerThresholdPercent ?? DEFAULT_COMPACTION_CONFIG.triggerThresholdPercent;
|
|
1133
|
+
const target = config.targetPercent ?? DEFAULT_COMPACTION_CONFIG.targetPercent;
|
|
1134
|
+
if (target >= trigger) {
|
|
1135
|
+
console.warn(
|
|
1136
|
+
`[llmist/compaction] targetPercent (${target}) should be less than triggerThresholdPercent (${trigger}) to be effective.`
|
|
1137
|
+
);
|
|
1138
|
+
}
|
|
1139
|
+
const strategy = config.strategy ?? DEFAULT_COMPACTION_CONFIG.strategy;
|
|
1140
|
+
const strategyName = typeof strategy === "object" && "name" in strategy ? strategy.name : strategy;
|
|
1141
|
+
return {
|
|
1142
|
+
enabled: config.enabled ?? DEFAULT_COMPACTION_CONFIG.enabled,
|
|
1143
|
+
strategy: strategyName,
|
|
1144
|
+
triggerThresholdPercent: trigger,
|
|
1145
|
+
targetPercent: target,
|
|
1146
|
+
preserveRecentTurns: config.preserveRecentTurns ?? DEFAULT_COMPACTION_CONFIG.preserveRecentTurns,
|
|
1147
|
+
summarizationModel: config.summarizationModel,
|
|
1148
|
+
summarizationPrompt: config.summarizationPrompt ?? DEFAULT_SUMMARIZATION_PROMPT,
|
|
1149
|
+
onCompaction: config.onCompaction
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
var DEFAULT_COMPACTION_CONFIG, DEFAULT_SUMMARIZATION_PROMPT;
|
|
1153
|
+
var init_config = __esm({
|
|
1154
|
+
"src/agent/compaction/config.ts"() {
|
|
1155
|
+
"use strict";
|
|
1156
|
+
DEFAULT_COMPACTION_CONFIG = {
|
|
1157
|
+
enabled: true,
|
|
1158
|
+
strategy: "hybrid",
|
|
1159
|
+
triggerThresholdPercent: 80,
|
|
1160
|
+
targetPercent: 50,
|
|
1161
|
+
preserveRecentTurns: 5
|
|
1162
|
+
};
|
|
1163
|
+
DEFAULT_SUMMARIZATION_PROMPT = `Summarize this conversation history concisely, preserving:
|
|
1164
|
+
1. Key decisions made and their rationale
|
|
1165
|
+
2. Important facts and data discovered
|
|
1166
|
+
3. Errors encountered and how they were resolved
|
|
1167
|
+
4. Current task context and goals
|
|
1168
|
+
|
|
1169
|
+
Format as a brief narrative paragraph, not bullet points.
|
|
1170
|
+
Previous conversation:`;
|
|
1171
|
+
}
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
// src/agent/compaction/strategy.ts
|
|
1175
|
+
function groupIntoTurns(messages) {
|
|
1176
|
+
const turns = [];
|
|
1177
|
+
let currentTurn = [];
|
|
1178
|
+
for (const msg of messages) {
|
|
1179
|
+
if (msg.role === "user" && currentTurn.length > 0) {
|
|
1180
|
+
turns.push({
|
|
1181
|
+
messages: currentTurn,
|
|
1182
|
+
tokenEstimate: estimateTurnTokens(currentTurn)
|
|
1183
|
+
});
|
|
1184
|
+
currentTurn = [msg];
|
|
1185
|
+
} else {
|
|
1186
|
+
currentTurn.push(msg);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
if (currentTurn.length > 0) {
|
|
1190
|
+
turns.push({
|
|
1191
|
+
messages: currentTurn,
|
|
1192
|
+
tokenEstimate: estimateTurnTokens(currentTurn)
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
return turns;
|
|
1196
|
+
}
|
|
1197
|
+
function estimateTurnTokens(messages) {
|
|
1198
|
+
return Math.ceil(messages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4);
|
|
1199
|
+
}
|
|
1200
|
+
function flattenTurns(turns) {
|
|
1201
|
+
return turns.flatMap((turn) => turn.messages);
|
|
1202
|
+
}
|
|
1203
|
+
var init_strategy = __esm({
|
|
1204
|
+
"src/agent/compaction/strategy.ts"() {
|
|
1205
|
+
"use strict";
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
1208
|
+
|
|
1209
|
+
// src/agent/compaction/strategies/sliding-window.ts
|
|
1210
|
+
var TRUNCATION_MARKER_TEMPLATE, SlidingWindowStrategy;
|
|
1211
|
+
var init_sliding_window = __esm({
|
|
1212
|
+
"src/agent/compaction/strategies/sliding-window.ts"() {
|
|
1213
|
+
"use strict";
|
|
1214
|
+
init_strategy();
|
|
1215
|
+
TRUNCATION_MARKER_TEMPLATE = "[Previous conversation truncated. Removed {count} turn(s) to fit context window.]";
|
|
1216
|
+
SlidingWindowStrategy = class {
|
|
1217
|
+
name = "sliding-window";
|
|
1218
|
+
async compact(messages, config, context) {
|
|
1219
|
+
const turns = groupIntoTurns(messages);
|
|
1220
|
+
const preserveCount = Math.min(config.preserveRecentTurns, turns.length);
|
|
1221
|
+
if (turns.length <= preserveCount) {
|
|
1222
|
+
return {
|
|
1223
|
+
messages,
|
|
1224
|
+
strategyName: this.name,
|
|
1225
|
+
metadata: {
|
|
1226
|
+
originalCount: messages.length,
|
|
1227
|
+
compactedCount: messages.length,
|
|
1228
|
+
tokensBefore: context.currentTokens,
|
|
1229
|
+
tokensAfter: context.currentTokens
|
|
1230
|
+
}
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
const turnsToKeep = turns.slice(-preserveCount);
|
|
1234
|
+
const turnsRemoved = turns.length - preserveCount;
|
|
1235
|
+
const truncationMarker = {
|
|
1236
|
+
role: "user",
|
|
1237
|
+
content: TRUNCATION_MARKER_TEMPLATE.replace("{count}", turnsRemoved.toString())
|
|
1238
|
+
};
|
|
1239
|
+
const compactedMessages = [truncationMarker, ...flattenTurns(turnsToKeep)];
|
|
1240
|
+
const tokensAfter = Math.ceil(
|
|
1241
|
+
compactedMessages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4
|
|
1242
|
+
);
|
|
1243
|
+
return {
|
|
1244
|
+
messages: compactedMessages,
|
|
1245
|
+
strategyName: this.name,
|
|
1246
|
+
metadata: {
|
|
1247
|
+
originalCount: messages.length,
|
|
1248
|
+
compactedCount: compactedMessages.length,
|
|
1249
|
+
tokensBefore: context.currentTokens,
|
|
1250
|
+
tokensAfter
|
|
1251
|
+
}
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
// src/agent/compaction/strategies/summarization.ts
|
|
1259
|
+
var SummarizationStrategy;
|
|
1260
|
+
var init_summarization = __esm({
|
|
1261
|
+
"src/agent/compaction/strategies/summarization.ts"() {
|
|
1262
|
+
"use strict";
|
|
1263
|
+
init_strategy();
|
|
1264
|
+
SummarizationStrategy = class {
|
|
1265
|
+
name = "summarization";
|
|
1266
|
+
async compact(messages, config, context) {
|
|
1267
|
+
const turns = groupIntoTurns(messages);
|
|
1268
|
+
const preserveCount = Math.min(config.preserveRecentTurns, turns.length);
|
|
1269
|
+
if (turns.length <= preserveCount) {
|
|
1270
|
+
return {
|
|
1271
|
+
messages,
|
|
1272
|
+
strategyName: this.name,
|
|
1273
|
+
metadata: {
|
|
1274
|
+
originalCount: messages.length,
|
|
1275
|
+
compactedCount: messages.length,
|
|
1276
|
+
tokensBefore: context.currentTokens,
|
|
1277
|
+
tokensAfter: context.currentTokens
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
const turnsToSummarize = turns.slice(0, -preserveCount);
|
|
1282
|
+
const turnsToKeep = turns.slice(-preserveCount);
|
|
1283
|
+
const conversationToSummarize = this.formatTurnsForSummary(flattenTurns(turnsToSummarize));
|
|
1284
|
+
const summary = await this.generateSummary(conversationToSummarize, config, context);
|
|
1285
|
+
const summaryMessage = {
|
|
1286
|
+
role: "user",
|
|
1287
|
+
content: `[Previous conversation summary]
|
|
1288
|
+
${summary}
|
|
1289
|
+
[End of summary - conversation continues below]`
|
|
1290
|
+
};
|
|
1291
|
+
const compactedMessages = [summaryMessage, ...flattenTurns(turnsToKeep)];
|
|
1292
|
+
const tokensAfter = Math.ceil(
|
|
1293
|
+
compactedMessages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4
|
|
1294
|
+
);
|
|
1295
|
+
return {
|
|
1296
|
+
messages: compactedMessages,
|
|
1297
|
+
summary,
|
|
1298
|
+
strategyName: this.name,
|
|
1299
|
+
metadata: {
|
|
1300
|
+
originalCount: messages.length,
|
|
1301
|
+
compactedCount: compactedMessages.length,
|
|
1302
|
+
tokensBefore: context.currentTokens,
|
|
1303
|
+
tokensAfter
|
|
1304
|
+
}
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* Formats messages into a readable conversation format for summarization.
|
|
1309
|
+
*/
|
|
1310
|
+
formatTurnsForSummary(messages) {
|
|
1311
|
+
return messages.map((msg) => {
|
|
1312
|
+
const role = msg.role.charAt(0).toUpperCase() + msg.role.slice(1);
|
|
1313
|
+
return `${role}: ${msg.content}`;
|
|
1314
|
+
}).join("\n\n");
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Generates a summary using the configured LLM.
|
|
1318
|
+
*/
|
|
1319
|
+
async generateSummary(conversation, config, context) {
|
|
1320
|
+
const model = config.summarizationModel ?? context.model;
|
|
1321
|
+
const prompt = `${config.summarizationPrompt}
|
|
1322
|
+
|
|
1323
|
+
${conversation}`;
|
|
1324
|
+
const response = await context.client.complete(prompt, {
|
|
1325
|
+
model,
|
|
1326
|
+
temperature: 0.3
|
|
1327
|
+
// Low temperature for factual summarization
|
|
1328
|
+
});
|
|
1329
|
+
return response.trim();
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
});
|
|
1334
|
+
|
|
1335
|
+
// src/agent/compaction/strategies/hybrid.ts
|
|
1336
|
+
var MIN_TURNS_FOR_SUMMARIZATION, HybridStrategy;
|
|
1337
|
+
var init_hybrid = __esm({
|
|
1338
|
+
"src/agent/compaction/strategies/hybrid.ts"() {
|
|
1339
|
+
"use strict";
|
|
1340
|
+
init_strategy();
|
|
1341
|
+
init_sliding_window();
|
|
1342
|
+
init_summarization();
|
|
1343
|
+
MIN_TURNS_FOR_SUMMARIZATION = 3;
|
|
1344
|
+
HybridStrategy = class {
|
|
1345
|
+
name = "hybrid";
|
|
1346
|
+
slidingWindow = new SlidingWindowStrategy();
|
|
1347
|
+
summarization = new SummarizationStrategy();
|
|
1348
|
+
async compact(messages, config, context) {
|
|
1349
|
+
const turns = groupIntoTurns(messages);
|
|
1350
|
+
const preserveCount = Math.min(config.preserveRecentTurns, turns.length);
|
|
1351
|
+
if (turns.length <= preserveCount) {
|
|
1352
|
+
return {
|
|
1353
|
+
messages,
|
|
1354
|
+
strategyName: this.name,
|
|
1355
|
+
metadata: {
|
|
1356
|
+
originalCount: messages.length,
|
|
1357
|
+
compactedCount: messages.length,
|
|
1358
|
+
tokensBefore: context.currentTokens,
|
|
1359
|
+
tokensAfter: context.currentTokens
|
|
1360
|
+
}
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
const turnsToSummarize = turns.length - preserveCount;
|
|
1364
|
+
if (turnsToSummarize < MIN_TURNS_FOR_SUMMARIZATION) {
|
|
1365
|
+
return this.slidingWindow.compact(messages, config, context);
|
|
1366
|
+
}
|
|
1367
|
+
return this.summarization.compact(messages, config, context);
|
|
1368
|
+
}
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
|
|
1373
|
+
// src/agent/compaction/strategies/index.ts
|
|
1374
|
+
var init_strategies = __esm({
|
|
1375
|
+
"src/agent/compaction/strategies/index.ts"() {
|
|
1376
|
+
"use strict";
|
|
1377
|
+
init_sliding_window();
|
|
1378
|
+
init_summarization();
|
|
1379
|
+
init_hybrid();
|
|
1380
|
+
}
|
|
1381
|
+
});
|
|
1382
|
+
|
|
1383
|
+
// src/agent/compaction/manager.ts
|
|
1384
|
+
function createStrategy(name) {
|
|
1385
|
+
switch (name) {
|
|
1386
|
+
case "sliding-window":
|
|
1387
|
+
return new SlidingWindowStrategy();
|
|
1388
|
+
case "summarization":
|
|
1389
|
+
return new SummarizationStrategy();
|
|
1390
|
+
case "hybrid":
|
|
1391
|
+
return new HybridStrategy();
|
|
1392
|
+
default:
|
|
1393
|
+
throw new Error(`Unknown compaction strategy: ${name}`);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
var CompactionManager;
|
|
1397
|
+
var init_manager = __esm({
|
|
1398
|
+
"src/agent/compaction/manager.ts"() {
|
|
1399
|
+
"use strict";
|
|
1400
|
+
init_config();
|
|
1401
|
+
init_strategies();
|
|
1402
|
+
CompactionManager = class {
|
|
1403
|
+
client;
|
|
1404
|
+
model;
|
|
1405
|
+
config;
|
|
1406
|
+
strategy;
|
|
1407
|
+
modelLimits;
|
|
1408
|
+
// Statistics
|
|
1409
|
+
totalCompactions = 0;
|
|
1410
|
+
totalTokensSaved = 0;
|
|
1411
|
+
lastTokenCount = 0;
|
|
1412
|
+
constructor(client, model, config = {}) {
|
|
1413
|
+
this.client = client;
|
|
1414
|
+
this.model = model;
|
|
1415
|
+
this.config = resolveCompactionConfig(config);
|
|
1416
|
+
if (typeof config.strategy === "object" && "compact" in config.strategy) {
|
|
1417
|
+
this.strategy = config.strategy;
|
|
1418
|
+
} else {
|
|
1419
|
+
this.strategy = createStrategy(this.config.strategy);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* Check if compaction is needed and perform it if so.
|
|
1424
|
+
*
|
|
1425
|
+
* @param conversation - The conversation manager to compact
|
|
1426
|
+
* @param iteration - Current agent iteration (for event metadata)
|
|
1427
|
+
* @returns CompactionEvent if compaction was performed, null otherwise
|
|
1428
|
+
*/
|
|
1429
|
+
async checkAndCompact(conversation, iteration) {
|
|
1430
|
+
if (!this.config.enabled) {
|
|
1431
|
+
return null;
|
|
1432
|
+
}
|
|
1433
|
+
if (!this.modelLimits) {
|
|
1434
|
+
this.modelLimits = this.client.modelRegistry.getModelLimits(this.model);
|
|
1435
|
+
if (!this.modelLimits) {
|
|
1436
|
+
return null;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
if (!this.client.countTokens) {
|
|
1440
|
+
return null;
|
|
1441
|
+
}
|
|
1442
|
+
const messages = conversation.getMessages();
|
|
1443
|
+
const currentTokens = await this.client.countTokens(this.model, messages);
|
|
1444
|
+
this.lastTokenCount = currentTokens;
|
|
1445
|
+
const usagePercent = currentTokens / this.modelLimits.contextWindow * 100;
|
|
1446
|
+
if (usagePercent < this.config.triggerThresholdPercent) {
|
|
1447
|
+
return null;
|
|
1448
|
+
}
|
|
1449
|
+
const historyMessages = conversation.getHistoryMessages();
|
|
1450
|
+
const baseMessages = conversation.getBaseMessages();
|
|
1451
|
+
const historyTokens = await this.client.countTokens(this.model, historyMessages);
|
|
1452
|
+
const baseTokens = await this.client.countTokens(this.model, baseMessages);
|
|
1453
|
+
return this.compact(conversation, iteration, {
|
|
1454
|
+
historyMessages,
|
|
1455
|
+
baseMessages,
|
|
1456
|
+
historyTokens,
|
|
1457
|
+
baseTokens,
|
|
1458
|
+
currentTokens: historyTokens + baseTokens
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
/**
|
|
1462
|
+
* Force compaction regardless of threshold.
|
|
1463
|
+
*
|
|
1464
|
+
* @param conversation - The conversation manager to compact
|
|
1465
|
+
* @param iteration - Current agent iteration (for event metadata). Use -1 for manual compaction.
|
|
1466
|
+
* @param precomputed - Optional pre-computed token counts (passed from checkAndCompact for efficiency)
|
|
1467
|
+
* @returns CompactionEvent with compaction details
|
|
1468
|
+
*/
|
|
1469
|
+
async compact(conversation, iteration, precomputed) {
|
|
1470
|
+
if (!this.modelLimits) {
|
|
1471
|
+
this.modelLimits = this.client.modelRegistry.getModelLimits(this.model);
|
|
1472
|
+
if (!this.modelLimits) {
|
|
1473
|
+
return null;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
const historyMessages = precomputed?.historyMessages ?? conversation.getHistoryMessages();
|
|
1477
|
+
const baseMessages = precomputed?.baseMessages ?? conversation.getBaseMessages();
|
|
1478
|
+
const historyTokens = precomputed?.historyTokens ?? await this.client.countTokens(this.model, historyMessages);
|
|
1479
|
+
const baseTokens = precomputed?.baseTokens ?? await this.client.countTokens(this.model, baseMessages);
|
|
1480
|
+
const currentTokens = precomputed?.currentTokens ?? historyTokens + baseTokens;
|
|
1481
|
+
const targetTotalTokens = Math.floor(
|
|
1482
|
+
this.modelLimits.contextWindow * this.config.targetPercent / 100
|
|
1483
|
+
);
|
|
1484
|
+
const targetHistoryTokens = Math.max(0, targetTotalTokens - baseTokens);
|
|
1485
|
+
const result = await this.strategy.compact(historyMessages, this.config, {
|
|
1486
|
+
currentTokens: historyTokens,
|
|
1487
|
+
targetTokens: targetHistoryTokens,
|
|
1488
|
+
modelLimits: this.modelLimits,
|
|
1489
|
+
client: this.client,
|
|
1490
|
+
model: this.config.summarizationModel ?? this.model
|
|
1491
|
+
});
|
|
1492
|
+
conversation.replaceHistory(result.messages);
|
|
1493
|
+
const afterTokens = await this.client.countTokens(this.model, conversation.getMessages());
|
|
1494
|
+
const tokensSaved = currentTokens - afterTokens;
|
|
1495
|
+
this.totalCompactions++;
|
|
1496
|
+
this.totalTokensSaved += tokensSaved;
|
|
1497
|
+
this.lastTokenCount = afterTokens;
|
|
1498
|
+
const event = {
|
|
1499
|
+
strategy: result.strategyName,
|
|
1500
|
+
tokensBefore: currentTokens,
|
|
1501
|
+
tokensAfter: afterTokens,
|
|
1502
|
+
messagesBefore: historyMessages.length + baseMessages.length,
|
|
1503
|
+
messagesAfter: result.messages.length + baseMessages.length,
|
|
1504
|
+
summary: result.summary,
|
|
1505
|
+
iteration
|
|
1506
|
+
};
|
|
1507
|
+
if (this.config.onCompaction) {
|
|
1508
|
+
try {
|
|
1509
|
+
this.config.onCompaction(event);
|
|
1510
|
+
} catch (err) {
|
|
1511
|
+
console.warn("[llmist/compaction] onCompaction callback error:", err);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
return event;
|
|
1515
|
+
}
|
|
1516
|
+
/**
|
|
1517
|
+
* Get compaction statistics.
|
|
1518
|
+
*/
|
|
1519
|
+
getStats() {
|
|
1520
|
+
const contextWindow = this.modelLimits?.contextWindow ?? 0;
|
|
1521
|
+
return {
|
|
1522
|
+
totalCompactions: this.totalCompactions,
|
|
1523
|
+
totalTokensSaved: this.totalTokensSaved,
|
|
1524
|
+
currentUsage: {
|
|
1525
|
+
tokens: this.lastTokenCount,
|
|
1526
|
+
percent: contextWindow > 0 ? this.lastTokenCount / contextWindow * 100 : 0
|
|
1527
|
+
},
|
|
1528
|
+
contextWindow
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Check if compaction is enabled.
|
|
1533
|
+
*/
|
|
1534
|
+
isEnabled() {
|
|
1535
|
+
return this.config.enabled;
|
|
1536
|
+
}
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
});
|
|
1540
|
+
|
|
1119
1541
|
// src/agent/gadget-output-store.ts
|
|
1120
1542
|
import { randomBytes } from "node:crypto";
|
|
1121
1543
|
var GadgetOutputStore;
|
|
@@ -1206,10 +1628,16 @@ var init_conversation_manager = __esm({
|
|
|
1206
1628
|
baseMessages;
|
|
1207
1629
|
initialMessages;
|
|
1208
1630
|
historyBuilder;
|
|
1631
|
+
startPrefix;
|
|
1632
|
+
endPrefix;
|
|
1633
|
+
argPrefix;
|
|
1209
1634
|
constructor(baseMessages, initialMessages, options = {}) {
|
|
1210
1635
|
this.baseMessages = baseMessages;
|
|
1211
1636
|
this.initialMessages = initialMessages;
|
|
1212
1637
|
this.historyBuilder = new LLMMessageBuilder();
|
|
1638
|
+
this.startPrefix = options.startPrefix;
|
|
1639
|
+
this.endPrefix = options.endPrefix;
|
|
1640
|
+
this.argPrefix = options.argPrefix;
|
|
1213
1641
|
if (options.startPrefix && options.endPrefix) {
|
|
1214
1642
|
this.historyBuilder.withPrefixes(options.startPrefix, options.endPrefix, options.argPrefix);
|
|
1215
1643
|
}
|
|
@@ -1226,6 +1654,25 @@ var init_conversation_manager = __esm({
|
|
|
1226
1654
|
getMessages() {
|
|
1227
1655
|
return [...this.baseMessages, ...this.initialMessages, ...this.historyBuilder.build()];
|
|
1228
1656
|
}
|
|
1657
|
+
getHistoryMessages() {
|
|
1658
|
+
return this.historyBuilder.build();
|
|
1659
|
+
}
|
|
1660
|
+
getBaseMessages() {
|
|
1661
|
+
return [...this.baseMessages, ...this.initialMessages];
|
|
1662
|
+
}
|
|
1663
|
+
replaceHistory(newHistory) {
|
|
1664
|
+
this.historyBuilder = new LLMMessageBuilder();
|
|
1665
|
+
if (this.startPrefix && this.endPrefix) {
|
|
1666
|
+
this.historyBuilder.withPrefixes(this.startPrefix, this.endPrefix, this.argPrefix);
|
|
1667
|
+
}
|
|
1668
|
+
for (const msg of newHistory) {
|
|
1669
|
+
if (msg.role === "user") {
|
|
1670
|
+
this.historyBuilder.addUser(msg.content);
|
|
1671
|
+
} else if (msg.role === "assistant") {
|
|
1672
|
+
this.historyBuilder.addAssistant(msg.content);
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1229
1676
|
};
|
|
1230
1677
|
}
|
|
1231
1678
|
});
|
|
@@ -4545,6 +4992,7 @@ var init_agent = __esm({
|
|
|
4545
4992
|
init_model_shortcuts();
|
|
4546
4993
|
init_output_viewer();
|
|
4547
4994
|
init_logger();
|
|
4995
|
+
init_manager();
|
|
4548
4996
|
init_gadget_output_store();
|
|
4549
4997
|
init_agent_internal_key();
|
|
4550
4998
|
init_conversation_manager();
|
|
@@ -4575,6 +5023,8 @@ var init_agent = __esm({
|
|
|
4575
5023
|
outputStore;
|
|
4576
5024
|
outputLimitEnabled;
|
|
4577
5025
|
outputLimitCharLimit;
|
|
5026
|
+
// Context compaction
|
|
5027
|
+
compactionManager;
|
|
4578
5028
|
/**
|
|
4579
5029
|
* Creates a new Agent instance.
|
|
4580
5030
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -4634,6 +5084,14 @@ var init_agent = __esm({
|
|
|
4634
5084
|
if (options.userPrompt) {
|
|
4635
5085
|
this.conversation.addUserMessage(options.userPrompt);
|
|
4636
5086
|
}
|
|
5087
|
+
const compactionEnabled = options.compactionConfig?.enabled ?? true;
|
|
5088
|
+
if (compactionEnabled) {
|
|
5089
|
+
this.compactionManager = new CompactionManager(
|
|
5090
|
+
this.client,
|
|
5091
|
+
this.model,
|
|
5092
|
+
options.compactionConfig
|
|
5093
|
+
);
|
|
5094
|
+
}
|
|
4637
5095
|
}
|
|
4638
5096
|
/**
|
|
4639
5097
|
* Get the gadget registry for this agent.
|
|
@@ -4656,6 +5114,53 @@ var init_agent = __esm({
|
|
|
4656
5114
|
getRegistry() {
|
|
4657
5115
|
return this.registry;
|
|
4658
5116
|
}
|
|
5117
|
+
/**
|
|
5118
|
+
* Manually trigger context compaction.
|
|
5119
|
+
*
|
|
5120
|
+
* Forces compaction regardless of threshold. Useful for:
|
|
5121
|
+
* - Pre-emptive context management before expected long operations
|
|
5122
|
+
* - Testing compaction behavior
|
|
5123
|
+
*
|
|
5124
|
+
* @returns CompactionEvent if compaction was performed, null if not configured or no history
|
|
5125
|
+
*
|
|
5126
|
+
* @example
|
|
5127
|
+
* ```typescript
|
|
5128
|
+
* const agent = await LLMist.createAgent()
|
|
5129
|
+
* .withModel('sonnet')
|
|
5130
|
+
* .withCompaction()
|
|
5131
|
+
* .ask('...');
|
|
5132
|
+
*
|
|
5133
|
+
* // Manually compact before a long operation
|
|
5134
|
+
* const event = await agent.compact();
|
|
5135
|
+
* if (event) {
|
|
5136
|
+
* console.log(`Saved ${event.tokensBefore - event.tokensAfter} tokens`);
|
|
5137
|
+
* }
|
|
5138
|
+
* ```
|
|
5139
|
+
*/
|
|
5140
|
+
async compact() {
|
|
5141
|
+
if (!this.compactionManager) {
|
|
5142
|
+
return null;
|
|
5143
|
+
}
|
|
5144
|
+
return this.compactionManager.compact(this.conversation, -1);
|
|
5145
|
+
}
|
|
5146
|
+
/**
|
|
5147
|
+
* Get compaction statistics.
|
|
5148
|
+
*
|
|
5149
|
+
* @returns CompactionStats if compaction is enabled, null otherwise
|
|
5150
|
+
*
|
|
5151
|
+
* @example
|
|
5152
|
+
* ```typescript
|
|
5153
|
+
* const stats = agent.getCompactionStats();
|
|
5154
|
+
* if (stats) {
|
|
5155
|
+
* console.log(`Total compactions: ${stats.totalCompactions}`);
|
|
5156
|
+
* console.log(`Tokens saved: ${stats.totalTokensSaved}`);
|
|
5157
|
+
* console.log(`Current usage: ${stats.currentUsage.percent.toFixed(1)}%`);
|
|
5158
|
+
* }
|
|
5159
|
+
* ```
|
|
5160
|
+
*/
|
|
5161
|
+
getCompactionStats() {
|
|
5162
|
+
return this.compactionManager?.getStats() ?? null;
|
|
5163
|
+
}
|
|
4659
5164
|
/**
|
|
4660
5165
|
* Run the agent loop.
|
|
4661
5166
|
* Clean, simple orchestration - all complexity is in StreamProcessor.
|
|
@@ -4676,6 +5181,30 @@ var init_agent = __esm({
|
|
|
4676
5181
|
while (currentIteration < this.maxIterations) {
|
|
4677
5182
|
this.logger.debug("Starting iteration", { iteration: currentIteration });
|
|
4678
5183
|
try {
|
|
5184
|
+
if (this.compactionManager) {
|
|
5185
|
+
const compactionEvent = await this.compactionManager.checkAndCompact(
|
|
5186
|
+
this.conversation,
|
|
5187
|
+
currentIteration
|
|
5188
|
+
);
|
|
5189
|
+
if (compactionEvent) {
|
|
5190
|
+
this.logger.info("Context compacted", {
|
|
5191
|
+
strategy: compactionEvent.strategy,
|
|
5192
|
+
tokensBefore: compactionEvent.tokensBefore,
|
|
5193
|
+
tokensAfter: compactionEvent.tokensAfter
|
|
5194
|
+
});
|
|
5195
|
+
yield { type: "compaction", event: compactionEvent };
|
|
5196
|
+
await this.safeObserve(async () => {
|
|
5197
|
+
if (this.hooks.observers?.onCompaction) {
|
|
5198
|
+
await this.hooks.observers.onCompaction({
|
|
5199
|
+
iteration: currentIteration,
|
|
5200
|
+
event: compactionEvent,
|
|
5201
|
+
stats: this.compactionManager.getStats(),
|
|
5202
|
+
logger: this.logger
|
|
5203
|
+
});
|
|
5204
|
+
}
|
|
5205
|
+
});
|
|
5206
|
+
}
|
|
5207
|
+
}
|
|
4679
5208
|
let llmOptions = {
|
|
4680
5209
|
model: this.model,
|
|
4681
5210
|
messages: this.conversation.getMessages(),
|
|
@@ -4695,6 +5224,7 @@ var init_agent = __esm({
|
|
|
4695
5224
|
if (this.hooks.controllers?.beforeLLMCall) {
|
|
4696
5225
|
const context = {
|
|
4697
5226
|
iteration: currentIteration,
|
|
5227
|
+
maxIterations: this.maxIterations,
|
|
4698
5228
|
options: llmOptions,
|
|
4699
5229
|
logger: this.logger
|
|
4700
5230
|
};
|
|
@@ -4759,12 +5289,17 @@ var init_agent = __esm({
|
|
|
4759
5289
|
});
|
|
4760
5290
|
let finalMessage = result.finalMessage;
|
|
4761
5291
|
if (this.hooks.controllers?.afterLLMCall) {
|
|
5292
|
+
const gadgetCallCount = result.outputs.filter(
|
|
5293
|
+
(output) => output.type === "gadget_result"
|
|
5294
|
+
).length;
|
|
4762
5295
|
const context = {
|
|
4763
5296
|
iteration: currentIteration,
|
|
5297
|
+
maxIterations: this.maxIterations,
|
|
4764
5298
|
options: llmOptions,
|
|
4765
5299
|
finishReason: result.finishReason,
|
|
4766
5300
|
usage: result.usage,
|
|
4767
5301
|
finalMessage: result.finalMessage,
|
|
5302
|
+
gadgetCallCount,
|
|
4768
5303
|
logger: this.logger
|
|
4769
5304
|
};
|
|
4770
5305
|
const action = await this.hooks.controllers.afterLLMCall(context);
|
|
@@ -5026,6 +5561,7 @@ var init_builder = __esm({
|
|
|
5026
5561
|
defaultGadgetTimeoutMs;
|
|
5027
5562
|
gadgetOutputLimit;
|
|
5028
5563
|
gadgetOutputLimitPercent;
|
|
5564
|
+
compactionConfig;
|
|
5029
5565
|
constructor(client) {
|
|
5030
5566
|
this.client = client;
|
|
5031
5567
|
}
|
|
@@ -5421,6 +5957,57 @@ var init_builder = __esm({
|
|
|
5421
5957
|
this.gadgetOutputLimitPercent = percent;
|
|
5422
5958
|
return this;
|
|
5423
5959
|
}
|
|
5960
|
+
/**
|
|
5961
|
+
* Configure context compaction.
|
|
5962
|
+
*
|
|
5963
|
+
* Context compaction automatically manages conversation history to prevent
|
|
5964
|
+
* context window overflow in long-running agent conversations.
|
|
5965
|
+
*
|
|
5966
|
+
* @param config - Compaction configuration options
|
|
5967
|
+
* @returns This builder for chaining
|
|
5968
|
+
*
|
|
5969
|
+
* @example
|
|
5970
|
+
* ```typescript
|
|
5971
|
+
* // Custom thresholds
|
|
5972
|
+
* .withCompaction({
|
|
5973
|
+
* triggerThresholdPercent: 70,
|
|
5974
|
+
* targetPercent: 40,
|
|
5975
|
+
* preserveRecentTurns: 10,
|
|
5976
|
+
* })
|
|
5977
|
+
*
|
|
5978
|
+
* // Different strategy
|
|
5979
|
+
* .withCompaction({
|
|
5980
|
+
* strategy: 'sliding-window',
|
|
5981
|
+
* })
|
|
5982
|
+
*
|
|
5983
|
+
* // With callback
|
|
5984
|
+
* .withCompaction({
|
|
5985
|
+
* onCompaction: (event) => {
|
|
5986
|
+
* console.log(`Saved ${event.tokensBefore - event.tokensAfter} tokens`);
|
|
5987
|
+
* }
|
|
5988
|
+
* })
|
|
5989
|
+
* ```
|
|
5990
|
+
*/
|
|
5991
|
+
withCompaction(config) {
|
|
5992
|
+
this.compactionConfig = { ...config, enabled: config.enabled ?? true };
|
|
5993
|
+
return this;
|
|
5994
|
+
}
|
|
5995
|
+
/**
|
|
5996
|
+
* Disable context compaction.
|
|
5997
|
+
*
|
|
5998
|
+
* By default, compaction is enabled. Use this method to explicitly disable it.
|
|
5999
|
+
*
|
|
6000
|
+
* @returns This builder for chaining
|
|
6001
|
+
*
|
|
6002
|
+
* @example
|
|
6003
|
+
* ```typescript
|
|
6004
|
+
* .withoutCompaction() // Disable automatic compaction
|
|
6005
|
+
* ```
|
|
6006
|
+
*/
|
|
6007
|
+
withoutCompaction() {
|
|
6008
|
+
this.compactionConfig = { enabled: false };
|
|
6009
|
+
return this;
|
|
6010
|
+
}
|
|
5424
6011
|
/**
|
|
5425
6012
|
* Add a synthetic gadget call to the conversation history.
|
|
5426
6013
|
*
|
|
@@ -5536,7 +6123,8 @@ ${endPrefix}`
|
|
|
5536
6123
|
shouldContinueAfterError: this.shouldContinueAfterError,
|
|
5537
6124
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
5538
6125
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
5539
|
-
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent
|
|
6126
|
+
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
6127
|
+
compactionConfig: this.compactionConfig
|
|
5540
6128
|
};
|
|
5541
6129
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
5542
6130
|
}
|
|
@@ -5638,7 +6226,8 @@ ${endPrefix}`
|
|
|
5638
6226
|
shouldContinueAfterError: this.shouldContinueAfterError,
|
|
5639
6227
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
5640
6228
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
5641
|
-
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent
|
|
6229
|
+
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
6230
|
+
compactionConfig: this.compactionConfig
|
|
5642
6231
|
};
|
|
5643
6232
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
5644
6233
|
}
|
|
@@ -5903,9 +6492,11 @@ export {
|
|
|
5903
6492
|
init_schema_validator,
|
|
5904
6493
|
GadgetRegistry,
|
|
5905
6494
|
init_registry,
|
|
6495
|
+
DEFAULT_HINTS,
|
|
5906
6496
|
DEFAULT_PROMPTS,
|
|
5907
6497
|
resolvePromptTemplate,
|
|
5908
6498
|
resolveRulesTemplate,
|
|
6499
|
+
resolveHintTemplate,
|
|
5909
6500
|
init_prompt_config,
|
|
5910
6501
|
LLMMessageBuilder,
|
|
5911
6502
|
init_messages,
|
|
@@ -5920,6 +6511,16 @@ export {
|
|
|
5920
6511
|
init_create_gadget,
|
|
5921
6512
|
createGadgetOutputViewer,
|
|
5922
6513
|
init_output_viewer,
|
|
6514
|
+
DEFAULT_COMPACTION_CONFIG,
|
|
6515
|
+
DEFAULT_SUMMARIZATION_PROMPT,
|
|
6516
|
+
init_config,
|
|
6517
|
+
init_strategy,
|
|
6518
|
+
SlidingWindowStrategy,
|
|
6519
|
+
SummarizationStrategy,
|
|
6520
|
+
HybridStrategy,
|
|
6521
|
+
init_strategies,
|
|
6522
|
+
CompactionManager,
|
|
6523
|
+
init_manager,
|
|
5923
6524
|
GadgetOutputStore,
|
|
5924
6525
|
init_gadget_output_store,
|
|
5925
6526
|
ConversationManager,
|
|
@@ -5962,4 +6563,4 @@ export {
|
|
|
5962
6563
|
AgentBuilder,
|
|
5963
6564
|
init_builder
|
|
5964
6565
|
};
|
|
5965
|
-
//# sourceMappingURL=chunk-
|
|
6566
|
+
//# sourceMappingURL=chunk-RZTAKIDE.js.map
|