@memberjunction/ai-agents 2.111.1 → 2.113.0
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/agent-context-injector.d.ts +34 -0
- package/dist/agent-context-injector.d.ts.map +1 -0
- package/dist/agent-context-injector.js +170 -0
- package/dist/agent-context-injector.js.map +1 -0
- package/dist/agent-types/base-agent-type.d.ts.map +1 -1
- package/dist/agent-types/base-agent-type.js.map +1 -1
- package/dist/agent-types/flow-agent-type.d.ts.map +1 -1
- package/dist/agent-types/flow-agent-type.js +1 -4
- package/dist/agent-types/flow-agent-type.js.map +1 -1
- package/dist/base-agent.d.ts +32 -5
- package/dist/base-agent.d.ts.map +1 -1
- package/dist/base-agent.js +403 -55
- package/dist/base-agent.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/memory-manager-agent.d.ts +17 -0
- package/dist/memory-manager-agent.d.ts.map +1 -0
- package/dist/memory-manager-agent.js +344 -0
- package/dist/memory-manager-agent.js.map +1 -0
- package/package.json +10 -10
package/dist/base-agent.js
CHANGED
|
@@ -31,6 +31,7 @@ const global_1 = require("@memberjunction/global");
|
|
|
31
31
|
const aiengine_1 = require("@memberjunction/aiengine");
|
|
32
32
|
const actions_1 = require("@memberjunction/actions");
|
|
33
33
|
const ai_engine_base_1 = require("@memberjunction/ai-engine-base");
|
|
34
|
+
const agent_context_injector_1 = require("./agent-context-injector");
|
|
34
35
|
const ai_core_plus_1 = require("@memberjunction/ai-core-plus");
|
|
35
36
|
const AgentRunner_1 = require("./AgentRunner");
|
|
36
37
|
const PayloadManager_1 = require("./PayloadManager");
|
|
@@ -51,7 +52,10 @@ class BaseAgent {
|
|
|
51
52
|
this._subAgentRuns = [];
|
|
52
53
|
this._payloadManager = new PayloadManager_1.PayloadManager();
|
|
53
54
|
this._validationRetryCount = 0;
|
|
54
|
-
this.
|
|
55
|
+
this._contextRecoveryAttempts = 0;
|
|
56
|
+
this.MAX_RECOVERY_ATTEMPTS = 1;
|
|
57
|
+
this._memoryContext = '';
|
|
58
|
+
this._injectedMemory = { notes: [], examples: [] };
|
|
55
59
|
}
|
|
56
60
|
get AgentTypeState() {
|
|
57
61
|
return this._agentTypeState;
|
|
@@ -208,7 +212,7 @@ class BaseAgent {
|
|
|
208
212
|
await this.initializeAgentRun(wrappedParams);
|
|
209
213
|
this._validationRetryCount = 0;
|
|
210
214
|
this._generalValidationRetryCount = 0;
|
|
211
|
-
this.
|
|
215
|
+
this._contextRecoveryAttempts = 0;
|
|
212
216
|
this._messageLifecycleCallback = params.onMessageLifecycle;
|
|
213
217
|
await this.initializeEngines(params.contextUser);
|
|
214
218
|
if (params.cancellationToken?.aborted) {
|
|
@@ -237,6 +241,13 @@ class BaseAgent {
|
|
|
237
241
|
}
|
|
238
242
|
await this.preloadAgentData(wrappedParams);
|
|
239
243
|
await this.initializeAgentType(wrappedParams, config);
|
|
244
|
+
const userId = params.userId || params.contextUser?.ID;
|
|
245
|
+
const companyId = params.companyId;
|
|
246
|
+
const lastUserMessage = params.conversationMessages
|
|
247
|
+
.filter(m => m.role === 'user')
|
|
248
|
+
.pop();
|
|
249
|
+
const inputText = lastUserMessage?.content || '';
|
|
250
|
+
await this.InjectContextMemory(typeof inputText === 'string' ? inputText : '', params.agent, userId, companyId, params.contextUser, wrappedParams.conversationMessages);
|
|
240
251
|
this.logStatus(`🚀 Executing agent '${params.agent.Name}' internal logic`, true, params);
|
|
241
252
|
const executionResult = await this.executeAgentInternal(wrappedParams, config);
|
|
242
253
|
wrappedParams.onProgress?.({
|
|
@@ -298,6 +309,50 @@ class BaseAgent {
|
|
|
298
309
|
await aiengine_1.AIEngine.Instance.Config(false, contextUser);
|
|
299
310
|
await actions_1.ActionEngineServer.Instance.Config(false, contextUser);
|
|
300
311
|
}
|
|
312
|
+
async InjectContextMemory(input, agent, userId, companyId, contextUser, conversationMessages) {
|
|
313
|
+
if (!agent.InjectNotes && !agent.InjectExamples) {
|
|
314
|
+
return { notes: [], examples: [] };
|
|
315
|
+
}
|
|
316
|
+
const injector = new agent_context_injector_1.AgentContextInjector();
|
|
317
|
+
const notes = agent.InjectNotes
|
|
318
|
+
? await injector.GetNotesForContext({
|
|
319
|
+
agentId: agent.ID,
|
|
320
|
+
userId,
|
|
321
|
+
companyId,
|
|
322
|
+
currentInput: input,
|
|
323
|
+
strategy: agent.NoteInjectionStrategy,
|
|
324
|
+
maxNotes: agent.MaxNotesToInject || 5,
|
|
325
|
+
contextUser: contextUser
|
|
326
|
+
})
|
|
327
|
+
: [];
|
|
328
|
+
const examples = agent.InjectExamples
|
|
329
|
+
? await injector.GetExamplesForContext({
|
|
330
|
+
agentId: agent.ID,
|
|
331
|
+
userId,
|
|
332
|
+
companyId,
|
|
333
|
+
currentInput: input,
|
|
334
|
+
strategy: agent.ExampleInjectionStrategy,
|
|
335
|
+
maxExamples: agent.MaxExamplesToInject || 3,
|
|
336
|
+
contextUser: contextUser
|
|
337
|
+
})
|
|
338
|
+
: [];
|
|
339
|
+
if ((notes.length > 0 || examples.length > 0) && conversationMessages) {
|
|
340
|
+
const notesText = injector.FormatNotesForInjection(notes);
|
|
341
|
+
const examplesText = injector.FormatExamplesForInjection(examples);
|
|
342
|
+
this._memoryContext = '';
|
|
343
|
+
if (notesText)
|
|
344
|
+
this._memoryContext += notesText + '\n\n';
|
|
345
|
+
if (examplesText)
|
|
346
|
+
this._memoryContext += examplesText + '\n\n';
|
|
347
|
+
conversationMessages.unshift({
|
|
348
|
+
role: 'system',
|
|
349
|
+
content: this._memoryContext
|
|
350
|
+
});
|
|
351
|
+
this.logStatus(`💾 Injected ${notes.length} notes and ${examples.length} examples into conversation context`, true);
|
|
352
|
+
}
|
|
353
|
+
this._injectedMemory = { notes, examples };
|
|
354
|
+
return { notes, examples };
|
|
355
|
+
}
|
|
301
356
|
async validateRunChain(lastRunId, contextUser) {
|
|
302
357
|
const visitedRunIds = new Set();
|
|
303
358
|
visitedRunIds.add(this._agentRun.ID);
|
|
@@ -1073,55 +1128,242 @@ class BaseAgent {
|
|
|
1073
1128
|
originalLength
|
|
1074
1129
|
};
|
|
1075
1130
|
}
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
const
|
|
1131
|
+
recoveryStrategy_RemoveOldestActionResults(params, tokensToSave, currentStepCount, minAge = 5) {
|
|
1132
|
+
let tokensSaved = 0;
|
|
1133
|
+
const removedIndices = [];
|
|
1134
|
+
const candidates = params.conversationMessages
|
|
1135
|
+
.map((msg, index) => ({
|
|
1136
|
+
message: msg,
|
|
1137
|
+
index: index,
|
|
1138
|
+
age: msg.metadata?.turnAdded
|
|
1139
|
+
? currentStepCount - msg.metadata.turnAdded
|
|
1140
|
+
: 0,
|
|
1141
|
+
tokens: this.estimateTokens(msg.content),
|
|
1142
|
+
isActionResult: msg.metadata?.messageType === 'action-result'
|
|
1143
|
+
}))
|
|
1144
|
+
.filter(c => c.isActionResult && c.age >= minAge)
|
|
1145
|
+
.sort((a, b) => b.age - a.age);
|
|
1146
|
+
for (const candidate of candidates) {
|
|
1147
|
+
if (tokensSaved >= tokensToSave)
|
|
1148
|
+
break;
|
|
1149
|
+
removedIndices.push(candidate.index);
|
|
1150
|
+
tokensSaved += candidate.tokens;
|
|
1151
|
+
this.logStatus(`Removing action-result from ${candidate.age} turns ago (${candidate.tokens} tokens)`, true, params);
|
|
1152
|
+
}
|
|
1153
|
+
removedIndices.sort((a, b) => b - a).forEach(index => {
|
|
1154
|
+
const removed = params.conversationMessages.splice(index, 1)[0];
|
|
1155
|
+
this.emitMessageLifecycleEvent({
|
|
1156
|
+
type: 'message-removed',
|
|
1157
|
+
turn: currentStepCount,
|
|
1158
|
+
messageIndex: index,
|
|
1159
|
+
message: removed,
|
|
1160
|
+
reason: 'Context recovery - oldest action results',
|
|
1161
|
+
tokensSaved: this.estimateTokens(removed.content)
|
|
1162
|
+
});
|
|
1163
|
+
});
|
|
1164
|
+
return {
|
|
1165
|
+
tokensSaved,
|
|
1166
|
+
strategyName: `Removed ${removedIndices.length} old action-results (${minAge}+ turns)`
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
async recoveryStrategy_CompactOldActionResults(params, tokensToSave, currentStepCount, minAge = 3) {
|
|
1170
|
+
let tokensSaved = 0;
|
|
1171
|
+
let compactedCount = 0;
|
|
1172
|
+
const candidates = params.conversationMessages
|
|
1173
|
+
.map((msg, index) => ({
|
|
1174
|
+
message: msg,
|
|
1175
|
+
index: index,
|
|
1176
|
+
age: msg.metadata?.turnAdded
|
|
1177
|
+
? currentStepCount - msg.metadata.turnAdded
|
|
1178
|
+
: 0,
|
|
1179
|
+
tokens: this.estimateTokens(msg.content),
|
|
1180
|
+
isActionResult: msg.metadata?.messageType === 'action-result',
|
|
1181
|
+
alreadyCompacted: msg.metadata?.wasCompacted === true
|
|
1182
|
+
}))
|
|
1183
|
+
.filter(c => c.isActionResult && c.age >= minAge && !c.alreadyCompacted)
|
|
1184
|
+
.sort((a, b) => b.age - a.age);
|
|
1185
|
+
for (const candidate of candidates) {
|
|
1186
|
+
if (tokensSaved >= tokensToSave)
|
|
1187
|
+
break;
|
|
1188
|
+
const originalTokens = candidate.tokens;
|
|
1189
|
+
const originalMessage = candidate.message;
|
|
1190
|
+
const originalContent = typeof originalMessage.content === 'string'
|
|
1191
|
+
? originalMessage.content
|
|
1192
|
+
: JSON.stringify(originalMessage.content);
|
|
1193
|
+
const compactedContent = await this.compactMessage(originalMessage, {
|
|
1194
|
+
compactMode: 'First N Chars',
|
|
1195
|
+
compactLength: 500,
|
|
1196
|
+
compactPromptId: '',
|
|
1197
|
+
originalLength: originalContent.length
|
|
1198
|
+
}, params);
|
|
1199
|
+
const newTokens = this.estimateTokens(compactedContent);
|
|
1200
|
+
const saved = originalTokens - newTokens;
|
|
1201
|
+
if (saved > 0) {
|
|
1202
|
+
params.conversationMessages[candidate.index] = {
|
|
1203
|
+
...originalMessage,
|
|
1204
|
+
content: compactedContent,
|
|
1205
|
+
metadata: {
|
|
1206
|
+
...originalMessage.metadata,
|
|
1207
|
+
wasCompacted: true,
|
|
1208
|
+
originalLength: originalContent.length,
|
|
1209
|
+
tokensSaved: saved
|
|
1210
|
+
}
|
|
1211
|
+
};
|
|
1212
|
+
tokensSaved += saved;
|
|
1213
|
+
compactedCount++;
|
|
1214
|
+
this.logStatus(`Compacted action-result from ${candidate.age} turns ago (saved ${saved} tokens)`, true, params);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
return {
|
|
1218
|
+
tokensSaved,
|
|
1219
|
+
strategyName: `Compacted ${compactedCount} old action-results (${minAge}+ turns)`
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
async recoveryStrategy_CompactAllActionResults(params, tokensToSave) {
|
|
1223
|
+
let tokensSaved = 0;
|
|
1224
|
+
let compactedCount = 0;
|
|
1225
|
+
const candidates = params.conversationMessages
|
|
1226
|
+
.map((msg, index) => ({
|
|
1227
|
+
message: msg,
|
|
1228
|
+
index: index,
|
|
1229
|
+
tokens: this.estimateTokens(msg.content),
|
|
1230
|
+
isActionResult: msg.metadata?.messageType === 'action-result',
|
|
1231
|
+
alreadyCompacted: msg.metadata?.wasCompacted === true
|
|
1232
|
+
}))
|
|
1233
|
+
.filter(c => c.isActionResult && !c.alreadyCompacted && c.tokens > 200)
|
|
1234
|
+
.sort((a, b) => b.tokens - a.tokens);
|
|
1235
|
+
for (const candidate of candidates) {
|
|
1236
|
+
if (tokensSaved >= tokensToSave)
|
|
1237
|
+
break;
|
|
1238
|
+
const originalTokens = candidate.tokens;
|
|
1239
|
+
const originalMessage = candidate.message;
|
|
1240
|
+
const originalContent = typeof originalMessage.content === 'string'
|
|
1241
|
+
? originalMessage.content
|
|
1242
|
+
: JSON.stringify(originalMessage.content);
|
|
1243
|
+
const compactedContent = await this.compactMessage(originalMessage, {
|
|
1244
|
+
compactMode: 'First N Chars',
|
|
1245
|
+
compactLength: 200,
|
|
1246
|
+
compactPromptId: '',
|
|
1247
|
+
originalLength: originalContent.length
|
|
1248
|
+
}, params);
|
|
1249
|
+
const newTokens = this.estimateTokens(compactedContent);
|
|
1250
|
+
const saved = originalTokens - newTokens;
|
|
1251
|
+
if (saved > 0) {
|
|
1252
|
+
params.conversationMessages[candidate.index] = {
|
|
1253
|
+
...originalMessage,
|
|
1254
|
+
content: compactedContent,
|
|
1255
|
+
metadata: {
|
|
1256
|
+
...originalMessage.metadata,
|
|
1257
|
+
wasCompacted: true,
|
|
1258
|
+
originalLength: originalContent.length,
|
|
1259
|
+
tokensSaved: saved
|
|
1260
|
+
}
|
|
1261
|
+
};
|
|
1262
|
+
tokensSaved += saved;
|
|
1263
|
+
compactedCount++;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
return {
|
|
1267
|
+
tokensSaved,
|
|
1268
|
+
strategyName: `Aggressively compacted ${compactedCount} action-results`
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
recoveryStrategy_TrimLastUserMessage(params, tokensToSave) {
|
|
1079
1272
|
let lastUserMessageIndex = -1;
|
|
1080
|
-
for (let i =
|
|
1081
|
-
if (
|
|
1273
|
+
for (let i = params.conversationMessages.length - 1; i >= 0; i--) {
|
|
1274
|
+
if (params.conversationMessages[i].role === 'user') {
|
|
1082
1275
|
lastUserMessageIndex = i;
|
|
1083
1276
|
break;
|
|
1084
1277
|
}
|
|
1085
1278
|
}
|
|
1086
1279
|
if (lastUserMessageIndex === -1) {
|
|
1087
|
-
|
|
1280
|
+
return { tokensSaved: 0, strategyName: 'No user message to trim' };
|
|
1281
|
+
}
|
|
1282
|
+
const lastUserMessage = params.conversationMessages[lastUserMessageIndex];
|
|
1283
|
+
const originalTokens = this.estimateTokens(lastUserMessage.content);
|
|
1284
|
+
const contentString = this.contentToString(lastUserMessage.content);
|
|
1285
|
+
const targetChars = 1000;
|
|
1286
|
+
if (contentString.length <= targetChars) {
|
|
1287
|
+
return { tokensSaved: 0, strategyName: 'User message already short' };
|
|
1288
|
+
}
|
|
1289
|
+
const trimResult = this.smartTrimContent(contentString, targetChars);
|
|
1290
|
+
const newContent = trimResult.trimmed +
|
|
1291
|
+
'\n\n<CONTEXT_LIMIT_REACHED>\n' +
|
|
1292
|
+
'Note: The conversation has exceeded the model\'s context window. ' +
|
|
1293
|
+
'The remainder of your message was trimmed to fit within the limit. ' +
|
|
1294
|
+
'The beginning of your request (shown above) has been preserved.\n' +
|
|
1295
|
+
'</CONTEXT_LIMIT_REACHED>';
|
|
1296
|
+
const newTokens = this.estimateTokens(newContent);
|
|
1297
|
+
const saved = originalTokens - newTokens;
|
|
1298
|
+
if (saved > 0) {
|
|
1299
|
+
params.conversationMessages[lastUserMessageIndex] = {
|
|
1300
|
+
...lastUserMessage,
|
|
1301
|
+
content: newContent
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
return {
|
|
1305
|
+
tokensSaved: saved,
|
|
1306
|
+
strategyName: 'Preserved beginning of user message with context limit marker'
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
async attemptContextRecovery(params, payload, errorMessage, modelSelectionInfo) {
|
|
1310
|
+
this.logStatus(`⚠️ Context length exceeded - attempting recovery with multi-strategy approach`, true, params);
|
|
1311
|
+
const modelLimit = this.getModelContextLimit(modelSelectionInfo);
|
|
1312
|
+
const currentTokens = this.estimateConversationTokens(params.conversationMessages);
|
|
1313
|
+
const tokensToSave = currentTokens - Math.floor(modelLimit * 0.9);
|
|
1314
|
+
this.logStatus(`Need to save ~${tokensToSave} tokens (current: ${currentTokens}, limit: ${modelLimit})`, true, params);
|
|
1315
|
+
if (tokensToSave <= 0) {
|
|
1316
|
+
this.logStatus(`Already under context limit, retrying...`, true, params);
|
|
1317
|
+
return {
|
|
1318
|
+
step: 'Retry',
|
|
1319
|
+
retryReason: 'Context recovery - already under limit',
|
|
1320
|
+
retryInstructions: 'The context is now within limits.',
|
|
1321
|
+
terminate: false,
|
|
1322
|
+
previousPayload: payload,
|
|
1323
|
+
newPayload: payload
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
const currentStepCount = this._agentRun?.Steps?.length || 0;
|
|
1327
|
+
const strategies = [
|
|
1328
|
+
() => this.recoveryStrategy_RemoveOldestActionResults(params, tokensToSave, currentStepCount, 5),
|
|
1329
|
+
() => this.recoveryStrategy_CompactOldActionResults(params, tokensToSave, currentStepCount, 3),
|
|
1330
|
+
() => this.recoveryStrategy_RemoveOldestActionResults(params, tokensToSave, currentStepCount, 2),
|
|
1331
|
+
() => this.recoveryStrategy_CompactAllActionResults(params, tokensToSave),
|
|
1332
|
+
() => Promise.resolve(this.recoveryStrategy_TrimLastUserMessage(params, tokensToSave))
|
|
1333
|
+
];
|
|
1334
|
+
let tokensSaved = 0;
|
|
1335
|
+
const strategiesUsed = [];
|
|
1336
|
+
for (const strategy of strategies) {
|
|
1337
|
+
const result = await strategy();
|
|
1338
|
+
tokensSaved += result.tokensSaved;
|
|
1339
|
+
if (result.tokensSaved > 0) {
|
|
1340
|
+
strategiesUsed.push(result.strategyName);
|
|
1341
|
+
this.logStatus(`${result.strategyName}: saved ${result.tokensSaved} tokens`, true, params);
|
|
1342
|
+
}
|
|
1343
|
+
if (tokensSaved >= tokensToSave) {
|
|
1344
|
+
break;
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
if (tokensSaved < tokensToSave * 0.5) {
|
|
1348
|
+
this.logStatus(`❌ Context recovery insufficient: only saved ${tokensSaved}/${tokensToSave} tokens`, true, params);
|
|
1088
1349
|
return {
|
|
1089
|
-
errorMessage: `Context
|
|
1350
|
+
errorMessage: `Context recovery failed: only saved ${tokensSaved}/${tokensToSave} tokens. ${errorMessage}`,
|
|
1090
1351
|
step: 'Failed',
|
|
1091
1352
|
terminate: true,
|
|
1092
1353
|
previousPayload: payload,
|
|
1093
1354
|
newPayload: payload
|
|
1094
1355
|
};
|
|
1095
1356
|
}
|
|
1096
|
-
|
|
1097
|
-
const
|
|
1098
|
-
const trimResult = this.smartTrimContent(contentString, 1000);
|
|
1099
|
-
this.logStatus(`✂️ Trimmed message from ${trimResult.originalLength.toLocaleString()} to ${trimResult.trimmed.length.toLocaleString()} characters using ${trimResult.strategy}`, true, params);
|
|
1100
|
-
messages[lastUserMessageIndex] = {
|
|
1101
|
-
role: 'user',
|
|
1102
|
-
content: `⚠️ CONTEXT OVERFLOW RECOVERY ⚠️
|
|
1103
|
-
|
|
1104
|
-
The previous step returned a result that exceeded the context window (${(trimResult.originalLength - trimResult.trimmed.length).toLocaleString()} characters truncated).
|
|
1105
|
-
|
|
1106
|
-
Here is a PARTIAL result from the previous action:
|
|
1107
|
-
---
|
|
1108
|
-
${trimResult.trimmed}
|
|
1109
|
-
---
|
|
1110
|
-
|
|
1111
|
-
❗ THE ABOVE IS INCOMPLETE - the full result was too large for the context window.
|
|
1112
|
-
|
|
1113
|
-
RECOMMENDED ACTIONS:
|
|
1114
|
-
1. Use a different action with more specific filters to get smaller result sets
|
|
1115
|
-
2. Request data in batches or pages instead of all at once
|
|
1116
|
-
3. Ask the user to clarify scope to narrow the query
|
|
1117
|
-
4. If you need the full data, acknowledge the limitation and ask the user how to proceed
|
|
1118
|
-
|
|
1119
|
-
Please choose an alternative approach to complete your task.`
|
|
1120
|
-
};
|
|
1357
|
+
this.logStatus(`✅ Context recovery successful: saved ${tokensSaved} tokens using ${strategiesUsed.length} strategies`, true, params);
|
|
1358
|
+
const strategyDescriptions = strategiesUsed.map((strategy, index) => `${index + 1}. ${strategy}`).join('\n');
|
|
1121
1359
|
return {
|
|
1122
1360
|
step: 'Retry',
|
|
1123
|
-
retryReason:
|
|
1124
|
-
retryInstructions:
|
|
1361
|
+
retryReason: `Context recovery successful - ${tokensSaved} tokens freed`,
|
|
1362
|
+
retryInstructions: `The conversation exceeded the model's context limit (${modelLimit} tokens). I've applied the following context recovery strategies to free up ${tokensSaved} tokens:
|
|
1363
|
+
|
|
1364
|
+
${strategyDescriptions}
|
|
1365
|
+
|
|
1366
|
+
The context is now within limits. Please retry your request with the recovered context.`,
|
|
1125
1367
|
terminate: false,
|
|
1126
1368
|
previousPayload: payload,
|
|
1127
1369
|
newPayload: payload
|
|
@@ -1236,18 +1478,61 @@ Please choose an alternative approach to complete your task.`
|
|
|
1236
1478
|
throw new Error(`Error executing actions: ${error.message}`);
|
|
1237
1479
|
}
|
|
1238
1480
|
}
|
|
1481
|
+
prepareSubAgentMessages(params, subAgentRequest, subAgent, contextMessage) {
|
|
1482
|
+
const engine = aiengine_1.AIEngine.Instance;
|
|
1483
|
+
let messages = [];
|
|
1484
|
+
const relationship = engine.AgentRelationships.find(r => r.AgentID === params.agent.ID && r.SubAgentID === subAgent.ID);
|
|
1485
|
+
let messageMode = relationship?.MessageMode || subAgent.MessageMode || 'None';
|
|
1486
|
+
let maxMessages = relationship?.MaxMessages || subAgent.MaxMessages || null;
|
|
1487
|
+
switch (messageMode) {
|
|
1488
|
+
case 'None':
|
|
1489
|
+
break;
|
|
1490
|
+
case 'All':
|
|
1491
|
+
messages = [...params.conversationMessages];
|
|
1492
|
+
break;
|
|
1493
|
+
case 'Latest':
|
|
1494
|
+
if (maxMessages && maxMessages > 0) {
|
|
1495
|
+
messages = params.conversationMessages.slice(-maxMessages);
|
|
1496
|
+
}
|
|
1497
|
+
else {
|
|
1498
|
+
messages = [...params.conversationMessages];
|
|
1499
|
+
}
|
|
1500
|
+
break;
|
|
1501
|
+
case 'Bookend':
|
|
1502
|
+
if (maxMessages && maxMessages > 2 && params.conversationMessages.length > maxMessages) {
|
|
1503
|
+
const firstTwo = params.conversationMessages.slice(0, 2);
|
|
1504
|
+
const remaining = params.conversationMessages.slice(-(maxMessages - 2));
|
|
1505
|
+
const omittedCount = params.conversationMessages.length - maxMessages;
|
|
1506
|
+
messages = [
|
|
1507
|
+
...firstTwo,
|
|
1508
|
+
{
|
|
1509
|
+
role: 'system',
|
|
1510
|
+
content: `[${omittedCount} messages omitted for context management]`
|
|
1511
|
+
},
|
|
1512
|
+
...remaining
|
|
1513
|
+
];
|
|
1514
|
+
}
|
|
1515
|
+
else {
|
|
1516
|
+
messages = [...params.conversationMessages];
|
|
1517
|
+
}
|
|
1518
|
+
break;
|
|
1519
|
+
default:
|
|
1520
|
+
break;
|
|
1521
|
+
}
|
|
1522
|
+
if (contextMessage) {
|
|
1523
|
+
messages.push(contextMessage);
|
|
1524
|
+
}
|
|
1525
|
+
messages.push({
|
|
1526
|
+
role: 'user',
|
|
1527
|
+
content: subAgentRequest.message
|
|
1528
|
+
});
|
|
1529
|
+
return messages;
|
|
1530
|
+
}
|
|
1239
1531
|
async ExecuteSubAgent(params, subAgentRequest, subAgent, stepEntity, payload, contextMessage) {
|
|
1240
1532
|
try {
|
|
1241
1533
|
this.logStatus(`🤖 Executing sub-agent '${subAgentRequest.name}'`, true, params);
|
|
1242
1534
|
const runner = new AgentRunner_1.AgentRunner();
|
|
1243
|
-
const subAgentMessages =
|
|
1244
|
-
if (contextMessage) {
|
|
1245
|
-
subAgentMessages.push(contextMessage);
|
|
1246
|
-
}
|
|
1247
|
-
subAgentMessages.push({
|
|
1248
|
-
role: 'user',
|
|
1249
|
-
content: subAgentRequest.message
|
|
1250
|
-
});
|
|
1535
|
+
const subAgentMessages = this.prepareSubAgentMessages(params, subAgentRequest, subAgent, contextMessage);
|
|
1251
1536
|
this.logStatus(`📨 Sub-agent message: "${subAgentRequest.message}"`, true, params);
|
|
1252
1537
|
if (subAgentRequest.templateParameters) {
|
|
1253
1538
|
this.logStatus(`📎 Template parameters: ${JSON.stringify(subAgentRequest.templateParameters)}`, true, params);
|
|
@@ -1781,10 +2066,10 @@ Please choose an alternative approach to complete your task.`
|
|
|
1781
2066
|
await stepEntity.Save();
|
|
1782
2067
|
};
|
|
1783
2068
|
const promptResult = await this.executePrompt(promptParams);
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
}
|
|
2069
|
+
params.conversationMessages = params.conversationMessages.filter(m => {
|
|
2070
|
+
const metadata = m.metadata;
|
|
2071
|
+
return !metadata?._loopResults && !metadata?._subAgentResult;
|
|
2072
|
+
});
|
|
1788
2073
|
if (promptResult.promptRun?.ID) {
|
|
1789
2074
|
stepEntity.TargetLogID = promptResult.promptRun.ID;
|
|
1790
2075
|
stepEntity.PromptRun = promptResult.promptRun;
|
|
@@ -1794,10 +2079,10 @@ Please choose an alternative approach to complete your task.`
|
|
|
1794
2079
|
const isFatal = this.isFatalPromptError(promptResult);
|
|
1795
2080
|
const isContextOverflow = isFatal &&
|
|
1796
2081
|
promptResult.chatResult?.errorInfo?.errorType === 'ContextLengthExceeded';
|
|
1797
|
-
if (isContextOverflow &&
|
|
1798
|
-
this.
|
|
2082
|
+
if (isContextOverflow && this._contextRecoveryAttempts < this.MAX_RECOVERY_ATTEMPTS) {
|
|
2083
|
+
this._contextRecoveryAttempts++;
|
|
1799
2084
|
this.logStatus(`⚠️ Context length exceeded - attempting recovery by trimming conversation`, true, params);
|
|
1800
|
-
return await this.attemptContextRecovery(params, payload, promptResult.errorMessage || 'Context length exceeded');
|
|
2085
|
+
return await this.attemptContextRecovery(params, payload, promptResult.errorMessage || 'Context length exceeded', promptResult.modelSelectionInfo);
|
|
1801
2086
|
}
|
|
1802
2087
|
this.logStatus(`❌ Prompt execution failed: ${promptResult.errorMessage} (fatal: ${isFatal})`, true, params);
|
|
1803
2088
|
return {
|
|
@@ -2084,7 +2369,13 @@ Please choose an alternative approach to complete your task.`
|
|
|
2084
2369
|
: `Sub-agent failed:\n${JSON.stringify(subAgentSummary, null, 2)}`;
|
|
2085
2370
|
params.conversationMessages.push({
|
|
2086
2371
|
role: 'user',
|
|
2087
|
-
content: resultMessage
|
|
2372
|
+
content: resultMessage,
|
|
2373
|
+
metadata: {
|
|
2374
|
+
_temporary: true,
|
|
2375
|
+
_subAgentResult: true,
|
|
2376
|
+
subAgentName: subAgentRequest.name,
|
|
2377
|
+
subAgentId: subAgentEntity.ID
|
|
2378
|
+
}
|
|
2088
2379
|
});
|
|
2089
2380
|
if (stepEntity) {
|
|
2090
2381
|
stepEntity.PayloadAtEnd = JSON.stringify(mergedPayload);
|
|
@@ -3100,7 +3391,10 @@ Please choose an alternative approach to complete your task.`
|
|
|
3100
3391
|
success: finalStep.step === 'Success' || finalStep.step === 'Chat',
|
|
3101
3392
|
payload,
|
|
3102
3393
|
agentRun: this._agentRun,
|
|
3103
|
-
suggestedResponses: finalStep.suggestedResponses
|
|
3394
|
+
suggestedResponses: finalStep.suggestedResponses,
|
|
3395
|
+
memoryContext: this._injectedMemory.notes.length > 0 || this._injectedMemory.examples.length > 0
|
|
3396
|
+
? this._injectedMemory
|
|
3397
|
+
: undefined
|
|
3104
3398
|
};
|
|
3105
3399
|
}
|
|
3106
3400
|
calculateTokenStats() {
|
|
@@ -3353,11 +3647,65 @@ Please choose an alternative approach to complete your task.`
|
|
|
3353
3647
|
}
|
|
3354
3648
|
return prompt.ID;
|
|
3355
3649
|
}
|
|
3356
|
-
estimateTokens(content) {
|
|
3650
|
+
estimateTokens(content, modelName) {
|
|
3357
3651
|
const text = typeof content === 'string'
|
|
3358
3652
|
? content
|
|
3359
3653
|
: JSON.stringify(content);
|
|
3360
|
-
return
|
|
3654
|
+
return this.heuristicTokenCount(text);
|
|
3655
|
+
}
|
|
3656
|
+
heuristicTokenCount(text) {
|
|
3657
|
+
const charCount = text.length;
|
|
3658
|
+
const structuralChars = (text.match(/[\{\}\[\],:]/g) || []).length;
|
|
3659
|
+
const whitespaceChars = (text.match(/\s/g) || []).length;
|
|
3660
|
+
const effectiveChars = charCount - (whitespaceChars * 0.5);
|
|
3661
|
+
const baseTokens = effectiveChars / 4;
|
|
3662
|
+
const structuralTokens = structuralChars * 0.05;
|
|
3663
|
+
return Math.ceil(baseTokens + structuralTokens);
|
|
3664
|
+
}
|
|
3665
|
+
getModelContextLimit(modelSelectionInfo) {
|
|
3666
|
+
const DEFAULT_LIMIT = 8000;
|
|
3667
|
+
if (!modelSelectionInfo) {
|
|
3668
|
+
this.logStatus(`No model selection info available, using default limit: ${DEFAULT_LIMIT}`, true);
|
|
3669
|
+
return DEFAULT_LIMIT;
|
|
3670
|
+
}
|
|
3671
|
+
try {
|
|
3672
|
+
const modelSelected = modelSelectionInfo.modelSelected;
|
|
3673
|
+
const vendorSelected = modelSelectionInfo.vendorSelected;
|
|
3674
|
+
if (!modelSelected) {
|
|
3675
|
+
this.logStatus(`No model selected in model selection info, using default limit: ${DEFAULT_LIMIT}`, true);
|
|
3676
|
+
return DEFAULT_LIMIT;
|
|
3677
|
+
}
|
|
3678
|
+
if (!vendorSelected) {
|
|
3679
|
+
this.logStatus(`No vendor selected, using default limit: ${DEFAULT_LIMIT}`, true);
|
|
3680
|
+
return DEFAULT_LIMIT;
|
|
3681
|
+
}
|
|
3682
|
+
const modelVendors = modelSelected.ModelVendors;
|
|
3683
|
+
if (!modelVendors || modelVendors.length === 0) {
|
|
3684
|
+
this.logStatus(`No ModelVendors array found on model, using default limit: ${DEFAULT_LIMIT}`, true);
|
|
3685
|
+
return DEFAULT_LIMIT;
|
|
3686
|
+
}
|
|
3687
|
+
const vendorEntry = modelVendors.find((mv) => mv.VendorID === vendorSelected.ID);
|
|
3688
|
+
if (!vendorEntry) {
|
|
3689
|
+
this.logStatus(`No matching vendor entry found in ModelVendors, using default limit: ${DEFAULT_LIMIT}`, true);
|
|
3690
|
+
return DEFAULT_LIMIT;
|
|
3691
|
+
}
|
|
3692
|
+
const maxInputTokens = vendorEntry.MaxInputTokens;
|
|
3693
|
+
if (!maxInputTokens || maxInputTokens <= 0) {
|
|
3694
|
+
this.logStatus(`MaxInputTokens not set or invalid on vendor entry, using default limit: ${DEFAULT_LIMIT}`, true);
|
|
3695
|
+
return DEFAULT_LIMIT;
|
|
3696
|
+
}
|
|
3697
|
+
this.logStatus(`Using vendor-specific MaxInputTokens: ${maxInputTokens} (Model: ${modelSelected.Name}, Vendor: ${vendorSelected.Name})`, true);
|
|
3698
|
+
return maxInputTokens;
|
|
3699
|
+
}
|
|
3700
|
+
catch (error) {
|
|
3701
|
+
this.logStatus(`Error extracting model context limit: ${error}, using default limit: ${DEFAULT_LIMIT}`, true);
|
|
3702
|
+
return DEFAULT_LIMIT;
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3705
|
+
estimateConversationTokens(messages) {
|
|
3706
|
+
return messages.reduce((total, msg) => {
|
|
3707
|
+
return total + this.estimateTokens(msg.content);
|
|
3708
|
+
}, 0);
|
|
3361
3709
|
}
|
|
3362
3710
|
emitMessageLifecycleEvent(event) {
|
|
3363
3711
|
if (this._messageLifecycleCallback) {
|