Skip to content

Commit 10cfc2e

Browse files
committed
refactor(getMessagesWithinTokenLimit): remove summary logic
refactor(handleContextStrategy): add usePrevSummary logic in case only summary was pruned refactor(loadHistory): initial message query will return all ordered messages but keep track of the latest summary refactor(getMessagesForConversation): use object for single param, edit jsdoc, edit all files using the method refactor(ChatGPTClient): order messages before buildPrompt is called, TODO: add convoSumBuffMemory logic
1 parent dfd09a6 commit 10cfc2e

File tree

7 files changed

+167
-75
lines changed

7 files changed

+167
-75
lines changed

api/app/clients/AnthropicClient.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,10 @@ class AnthropicClient extends BaseClient {
9191
}
9292

9393
async buildMessages(messages, parentMessageId) {
94-
const orderedMessages = this.constructor.getMessagesForConversation(messages, parentMessageId);
94+
const orderedMessages = this.constructor.getMessagesForConversation({
95+
messages,
96+
parentMessageId,
97+
});
9598
if (this.options.debug) {
9699
console.debug('AnthropicClient: orderedMessages', orderedMessages, parentMessageId);
97100
}

api/app/clients/BaseClient.js

Lines changed: 81 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ class BaseClient {
213213
userName: this.options?.name,
214214
assistantName: this.options?.chatGptLabel ?? this.options?.modelLabel,
215215
},
216-
previous_summary: messagesToRefine?.[0]?.summary,
216+
previous_summary: this.previous_summary.summary,
217217
});
218218

219219
const summaryTokenCount = this.getTokenCountForMessage(summaryMessage);
@@ -261,14 +261,7 @@ class BaseClient {
261261
if (currentTokenCount < remainingContextTokens) {
262262
while (messages.length > 0 && currentTokenCount < remainingContextTokens) {
263263
const poppedMessage = messages.pop();
264-
let { tokenCount, summary, summaryTokenCount } = poppedMessage;
265-
266-
if (this.shouldRefineContext && summary && summaryTokenCount) {
267-
tokenCount = summaryTokenCount;
268-
poppedMessage.text = summary;
269-
}
270-
271-
console.log('poppedMessage ID & token count', poppedMessage.messageId, tokenCount);
264+
const { tokenCount } = poppedMessage;
272265

273266
if (poppedMessage && currentTokenCount + tokenCount <= remainingContextTokens) {
274267
context.push(poppedMessage);
@@ -299,48 +292,62 @@ class BaseClient {
299292
let { context, remainingContextTokens, messagesToRefine, summaryIndex } =
300293
await this.getMessagesWithinTokenLimit(orderedWithInstructions);
301294

295+
this.options.debug &&
296+
console.debug(
297+
'remainingContextTokens, this.maxContextTokens (1/2)',
298+
remainingContextTokens,
299+
this.maxContextTokens,
300+
);
301+
302302
let summaryMessage;
303303
let summaryTokenCount;
304304

305305
// Calculate the difference in length to determine how many messages were discarded if any
306306
const { length } = payload;
307-
let diff = length - context.length;
307+
const diff = length - context.length;
308+
const firstMessage = orderedWithInstructions[0];
309+
const usePrevSummary =
310+
this.shouldRefineContext &&
311+
diff === 1 &&
312+
firstMessage?.summary &&
313+
this.previous_summary.messageId === firstMessage.messageId;
308314

309-
// If the difference is positive, slice the payload array
310315
if (diff > 0) {
311316
payload = payload.slice(diff);
312-
console.debug(
313-
`Difference between original payload (${length}) and context (${context.length}): ${diff}`,
314-
);
317+
this.options.debug &&
318+
console.debug(
319+
`Difference between original payload (${length}) and context (${context.length}): ${diff}`,
320+
);
315321
}
316322

317-
if (messagesToRefine.length > 0) {
323+
if (usePrevSummary) {
324+
summaryMessage = { role: 'system', content: firstMessage.summary };
325+
summaryTokenCount = firstMessage.summaryTokenCount;
326+
payload.unshift(summaryMessage);
327+
remainingContextTokens -= summaryTokenCount;
328+
} else if (messagesToRefine.length > 0) {
318329
({ summaryMessage, summaryTokenCount } = await this.refineMessages({
319330
messagesToRefine,
320331
remainingContextTokens,
321332
}));
322333
payload.unshift(summaryMessage);
323334
remainingContextTokens -= summaryTokenCount;
324-
} else if (this.shouldRefineContext && messagesToRefine.length === 0 && context?.[0]?.summary) {
325-
payload[0].role = 'system';
326-
payload[0].content = context[0].summary;
327335
}
328336

329-
if (this.options.debug) {
337+
this.options.debug &&
330338
console.debug(
331339
'remainingContextTokens, this.maxContextTokens (2/2)',
332340
remainingContextTokens,
333341
this.maxContextTokens,
334342
);
335-
}
336343

337344
let tokenCountMap = orderedWithInstructions.reduce((map, message, index) => {
338345
const { messageId } = message;
339346
if (!messageId) {
340347
return map;
341348
}
342349

343-
if (index === summaryIndex) {
350+
if (index === summaryIndex && !usePrevSummary) {
344351
map.summaryMessage = { ...summaryMessage, messageId, tokenCount: summaryTokenCount };
345352
}
346353

@@ -469,7 +476,30 @@ class BaseClient {
469476
mapMethod = this.getMessageMapMethod();
470477
}
471478

472-
return this.constructor.getMessagesForConversation(messages, parentMessageId, mapMethod);
479+
const orderedMessages = this.constructor.getMessagesForConversation({
480+
messages,
481+
parentMessageId,
482+
mapMethod,
483+
});
484+
485+
if (!this.shouldRefineContext) {
486+
return orderedMessages;
487+
}
488+
489+
// Find the latest message with a 'summary' property
490+
for (let i = orderedMessages.length - 1; i >= 0; i--) {
491+
if (orderedMessages[i]?.summary) {
492+
this.previous_summary = orderedMessages[i];
493+
break;
494+
}
495+
}
496+
497+
if (this.options.debug && this.previous_summary) {
498+
const { messageId, summary, tokenCount, summaryTokenCount } = this.previous_summary;
499+
console.debug('Previous summary:', { messageId, summary, tokenCount, summaryTokenCount });
500+
}
501+
502+
return orderedMessages;
473503
}
474504

475505
async saveMessageToDatabase(message, endpointOptions, user = null) {
@@ -490,20 +520,30 @@ class BaseClient {
490520
*
491521
* This function constructs a conversation thread by traversing messages from a given parentMessageId up to the root message.
492522
* It handles cyclic references by ensuring that a message is not processed more than once.
493-
* If a message has a 'summary' property, the traversal stops at that message.
523+
* If the 'summary' option is set to true and a message has a 'summary' property:
524+
* - The message's 'role' is set to 'system'.
525+
* - The message's 'text' is set to its 'summary'.
526+
* - If the message has a 'summaryTokenCount', the message's 'tokenCount' is set to 'summaryTokenCount'.
527+
* The traversal stops at the message with the 'summary' property.
494528
*
495529
* Each message object should have an 'id' or 'messageId' property and may have a 'parentMessageId' property.
496530
* The 'parentMessageId' is the ID of the message that the current message is a reply to.
497531
* If 'parentMessageId' is not present, null, or is '00000000-0000-0000-0000-000000000000',
498532
* the message is considered a root message.
499533
*
500-
* @param {Array} messages - An array of message objects. Each object should have either an 'id' or 'messageId' property, and may have a 'parentMessageId' property.
501-
* @param {string} parentMessageId - The ID of the parent message to start the traversal from.
502-
* @param {Function} [mapMethod] - An optional function to map over the ordered messages. If provided, it will be applied to each message in the resulting array.
503-
* @returns {Array} An array containing the messages in the order they should be displayed, starting with the most recent message with a 'summary' property if present, and ending with the message identified by 'parentMessageId'.
534+
* @param {Object} options - The options for the function.
535+
* @param {Array} options.messages - An array of message objects. Each object should have either an 'id' or 'messageId' property, and may have a 'parentMessageId' property.
536+
* @param {string} options.parentMessageId - The ID of the parent message to start the traversal from.
537+
* @param {Function} [options.mapMethod] - An optional function to map over the ordered messages. If provided, it will be applied to each message in the resulting array.
538+
* @param {boolean} [options.summary=false] - If set to true, the traversal modifies messages with 'summary' and 'summaryTokenCount' properties and stops at the message with a 'summary' property.
539+
* @returns {Array} An array containing the messages in the order they should be displayed, starting with the most recent message with a 'summary' property if the 'summary' option is true, and ending with the message identified by 'parentMessageId'.
504540
*/
505-
506-
static getMessagesForConversation(messages, parentMessageId, mapMethod = null) {
541+
static getMessagesForConversation({
542+
messages,
543+
parentMessageId,
544+
mapMethod = null,
545+
summary = false,
546+
}) {
507547
if (!messages || messages.length === 0) {
508548
return [];
509549
}
@@ -527,10 +567,21 @@ class BaseClient {
527567
break;
528568
}
529569

570+
if (summary && message.summary) {
571+
message.role = 'system';
572+
message.text = message.summary;
573+
}
574+
575+
if (summary && message.summaryTokenCount) {
576+
message.tokenCount = message.summaryTokenCount;
577+
}
578+
530579
orderedMessages.push(message);
531-
if (message.summary) {
580+
581+
if (summary && message.summary) {
532582
break;
533583
}
584+
534585
currentMessageId =
535586
message.parentMessageId === '00000000-0000-0000-0000-000000000000'
536587
? null

api/app/clients/ChatGPTClient.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -437,9 +437,7 @@ ${botMessage.message}
437437
return returnData;
438438
}
439439

440-
async buildPrompt(messages, parentMessageId, { isChatGptModel = false, promptPrefix = null }) {
441-
const orderedMessages = this.constructor.getMessagesForConversation(messages, parentMessageId);
442-
440+
async buildPrompt(messages, { isChatGptModel = false, promptPrefix = null }) {
443441
promptPrefix = (promptPrefix || this.options.promptPrefix || '').trim();
444442
if (promptPrefix) {
445443
// If the prompt prefix doesn't end with the end token, add it.
@@ -485,8 +483,8 @@ ${botMessage.message}
485483
// Iterate backwards through the messages, adding them to the prompt until we reach the max token count.
486484
// Do this within a recursive async function so that it doesn't block the event loop for too long.
487485
const buildPromptBody = async () => {
488-
if (currentTokenCount < maxTokenCount && orderedMessages.length > 0) {
489-
const message = orderedMessages.pop();
486+
if (currentTokenCount < maxTokenCount && messages.length > 0) {
487+
const message = messages.pop();
490488
const roleLabel =
491489
message?.isCreatedByUser || message?.role?.toLowerCase() === 'user'
492490
? this.userLabel

api/app/clients/OpenAIClient.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,13 @@ class OpenAIClient extends BaseClient {
268268
parentMessageId,
269269
{ isChatCompletion = false, promptPrefix = null },
270270
) {
271+
let orderedMessages = this.constructor.getMessagesForConversation({
272+
messages,
273+
parentMessageId,
274+
summary: this.shouldRefineContext,
275+
});
271276
if (!isChatCompletion) {
272-
return await this.buildPrompt(messages, parentMessageId, {
277+
return await this.buildPrompt(orderedMessages, {
273278
isChatGptModel: isChatCompletion,
274279
promptPrefix,
275280
});
@@ -279,7 +284,6 @@ class OpenAIClient extends BaseClient {
279284
let instructions;
280285
let tokenCountMap;
281286
let promptTokens;
282-
let orderedMessages = this.constructor.getMessagesForConversation(messages, parentMessageId);
283287

284288
promptPrefix = (promptPrefix || this.options.promptPrefix || '').trim();
285289
if (promptPrefix) {

0 commit comments

Comments
 (0)