diff --git a/public/scripts/extensions/memory/index.js b/public/scripts/extensions/memory/index.js index be805d379..90a4d5991 100644 --- a/public/scripts/extensions/memory/index.js +++ b/public/scripts/extensions/memory/index.js @@ -1,4 +1,4 @@ -import { getStringHash, debounce, waitUntilCondition } from "../../utils.js"; +import { getStringHash, debounce, waitUntilCondition, extractAllWords } from "../../utils.js"; import { getContext, getApiUrl, extension_settings, doExtrasFetch, modules } from "../../extensions.js"; import { eventSource, event_types, extension_prompt_types, generateQuietPrompt, is_send_press, saveSettingsDebounced, substituteParams } from "../../../script.js"; export { MODULE_NAME }; @@ -12,7 +12,21 @@ let lastMessageHash = null; let lastMessageId = null; let inApiCall = false; -const formatMemoryValue = (value) => value ? `Summary: ${value.trim()}` : ''; +const formatMemoryValue = function (value) { + if (!value) { + return ''; + } + + value = value.trim(); + + if (extension_settings.memory.template) { + let result = extension_settings.memory.template.replace(/{{summary}}/i, value); + return substituteParams(result); + } else { + return `Summary: ${value}`; + } +} + const saveChatDebounced = debounce(() => getContext().saveChat(), 2000); const summary_sources = { @@ -21,6 +35,7 @@ const summary_sources = { }; const defaultPrompt = '[Pause your roleplay. Summarize the most important facts and events that have happened in the chat so far. If a summary already exists in your memory, use that as a base and expand with new facts. Limit the summary to {{words}} words or less. Your response should include nothing but the summary.]'; +const defaultTemplate = '[Summary: {{summary}}]'; const defaultSettings = { minLongMemory: 16, @@ -46,6 +61,9 @@ const defaultSettings = { memoryFrozen: false, source: summary_sources.extras, prompt: defaultPrompt, + template: defaultTemplate, + position: extension_prompt_types.AFTER_SCENARIO, + depth: 2, promptWords: 200, promptMinWords: 25, promptMaxWords: 1000, @@ -54,6 +72,10 @@ const defaultSettings = { promptMinInterval: 1, promptMaxInterval: 100, promptIntervalStep: 1, + promptForceWords: 0, + promptForceWordsStep: 100, + promptMinForceWords: 0, + promptMaxForceWords: 10000, }; function loadSettings() { @@ -61,20 +83,10 @@ function loadSettings() { Object.assign(extension_settings.memory, defaultSettings); } - if (extension_settings.memory.source === undefined) { - extension_settings.memory.source = defaultSettings.source; - } - - if (extension_settings.memory.prompt === undefined) { - extension_settings.memory.prompt = defaultSettings.prompt; - } - - if (extension_settings.memory.promptWords === undefined) { - extension_settings.memory.promptWords = defaultSettings.promptWords; - } - - if (extension_settings.memory.promptInterval === undefined) { - extension_settings.memory.promptInterval = defaultSettings.promptInterval; + for (const key of Object.keys(defaultSettings)) { + if (extension_settings.memory[key] === undefined) { + extension_settings.memory[key] = defaultSettings[key]; + } } $('#summary_source').val(extension_settings.memory.source).trigger('change'); @@ -87,6 +99,10 @@ function loadSettings() { $('#memory_prompt').val(extension_settings.memory.prompt).trigger('input'); $('#memory_prompt_words').val(extension_settings.memory.promptWords).trigger('input'); $('#memory_prompt_interval').val(extension_settings.memory.promptInterval).trigger('input'); + $('#memory_template').val(extension_settings.memory.template).trigger('input'); + $('#memory_depth').val(extension_settings.memory.depth).trigger('input'); + $(`input[name="memory_position"][value="${extension_settings.memory.position}"]`).prop('checked', true).trigger('input'); + $('#memory_prompt_words_force').val(extension_settings.memory.promptForceWords).trigger('input'); } function onSummarySourceChange(event) { @@ -170,6 +186,31 @@ function onMemoryPromptInput() { saveSettingsDebounced(); } +function onMemoryTemplateInput() { + const value = $(this).val(); + extension_settings.memory.template = value; + saveSettingsDebounced(); +} + +function onMemoryDepthInput() { + const value = $(this).val(); + extension_settings.memory.depth = Number(value); + saveSettingsDebounced(); +} + +function onMemoryPositionChange(e) { + const value = e.target.value; + extension_settings.memory.position = value; + saveSettingsDebounced(); +} + +function onMemoryPromptWordsForceInput() { + const value = $(this).val(); + extension_settings.memory.promptForceWords = Number(value); + $('#memory_prompt_words_force_value').text(extension_settings.memory.promptForceWords); + saveSettingsDebounced(); +} + function saveLastValues() { const context = getContext(); lastGroupId = context.groupId; @@ -310,19 +351,30 @@ async function summarizeChatMain(context, force) { } let messagesSinceLastSummary = 0; + let wordsSinceLastSummary = 0; + let conditionSatisfied = false; for (let i = context.chat.length - 1; i >= 0; i--) { if (context.chat[i].extra && context.chat[i].extra.memory) { break; } messagesSinceLastSummary++; + wordsSinceLastSummary += extractAllWords(context.chat[i].mes).length; } - if (messagesSinceLastSummary < extension_settings.memory.promptInterval && !force) { - console.debug(`Not enough messages since last summary (messages: ${messagesSinceLastSummary}, interval: ${extension_settings.memory.promptInterval}`); + if (messagesSinceLastSummary >= extension_settings.memory.promptInterval) { + conditionSatisfied = true; + } + + if (extension_settings.memory.promptForceWords && wordsSinceLastSummary >= extension_settings.memory.promptForceWords) { + conditionSatisfied = true; + } + + if (!conditionSatisfied && !force) { + console.debug(`Summary conditions not satisfied (messages: ${messagesSinceLastSummary}, interval: ${extension_settings.memory.promptInterval}, words: ${wordsSinceLastSummary}, force words: ${extension_settings.memory.promptForceWords})`); return; } - console.log('Summarizing chat, messages since last summary: ' + messagesSinceLastSummary); + console.log('Summarizing chat, messages since last summary: ' + messagesSinceLastSummary, 'words since last summary: ' + wordsSinceLastSummary); const prompt = substituteParams(extension_settings.memory.prompt) .replace(/{{words}}/gi, extension_settings.memory.promptWords); @@ -458,9 +510,11 @@ function onMemoryContentInput() { function setMemoryContext(value, saveToMessage) { const context = getContext(); - context.setExtensionPrompt(MODULE_NAME, formatMemoryValue(value), extension_prompt_types.AFTER_SCENARIO); + context.setExtensionPrompt(MODULE_NAME, formatMemoryValue(value), extension_settings.memory.position, extension_settings.memory.depth); $('#memory_contents').val(value); - console.log('Memory set to: ' + value); + console.log('Summary set to: ' + value); + console.debug('Position: ' + extension_settings.memory.position); + console.debug('Depth: ' + extension_settings.memory.depth); if (saveToMessage && context.chat.length) { const idx = context.chat.length - 2; @@ -496,6 +550,21 @@ jQuery(function () { +
+ + +
+ +
+ + +
@@ -511,6 +580,9 @@ jQuery(function () { + + Set to 0 to disable +
@@ -542,6 +614,10 @@ jQuery(function () { $('#memory_prompt_interval').on('input', onMemoryPromptIntervalInput); $('#memory_prompt').on('input', onMemoryPromptInput); $('#memory_force_summarize').on('click', forceSummarizeChat); + $('#memory_template').on('input', onMemoryTemplateInput); + $('#memory_depth').on('input', onMemoryDepthInput); + $('input[name="memory_position"]').on('change', onMemoryPositionChange); + $('#memory_prompt_words_force').on('input', onMemoryPromptWordsForceInput); } addExtensionControls(); diff --git a/public/scripts/extensions/memory/style.css b/public/scripts/extensions/memory/style.css index f0c07834f..592892f0f 100644 --- a/public/scripts/extensions/memory/style.css +++ b/public/scripts/extensions/memory/style.css @@ -1,34 +1,26 @@ -#memory_settings { - display: flex; - flex-direction: column; -} - -#memory_settings textarea { - font-size: calc(var(--mainFontSize) * 0.9); - line-height: 1.2; -} - -#memory_settings input[type="range"] { - margin-bottom: 20px; -} - -#memory_settings label { - margin-bottom: 10px; -} - -label[for="memory_frozen"] { - display: flex; - align-items: center; - margin: 0 !important; -} - -label[for="memory_frozen"] input { - margin-right: 10px; -} - -.memory_contents_controls { - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; -} \ No newline at end of file +#memory_settings { + display: flex; + flex-direction: column; +} + +#memory_settings textarea { + font-size: calc(var(--mainFontSize) * 0.9); + line-height: 1.2; +} + +label[for="memory_frozen"] { + display: flex; + align-items: center; + margin: 0 !important; +} + +label[for="memory_frozen"] input { + margin-right: 10px; +} + +.memory_contents_controls { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; +} diff --git a/public/scripts/group-chats.js b/public/scripts/group-chats.js index 2a87e7c6d..3f03971ec 100644 --- a/public/scripts/group-chats.js +++ b/public/scripts/group-chats.js @@ -5,6 +5,7 @@ import { delay, isDataURL, createThumbnail, + extractAllWords, } from './utils.js'; import { RA_CountCharTokens, humanizedDateTime, dragElement } from "./RossAscends-mods.js"; import { sortCharactersList, sortGroupMembers, loadMovingUIState } from './power-user.js'; @@ -782,19 +783,6 @@ function activateNaturalOrder(members, input, lastMessage, allowSelfResponses, i return memberIds; } -function extractAllWords(value) { - const words = []; - - if (!value) { - return words; - } - - const matches = value.matchAll(/\b\w+\b/gim); - for (let match of matches) { - words.push(match[0].toLowerCase()); - } - return words; -} async function deleteGroup(id) { diff --git a/public/scripts/utils.js b/public/scripts/utils.js index ff667b8b8..3212458a3 100644 --- a/public/scripts/utils.js +++ b/public/scripts/utils.js @@ -470,6 +470,20 @@ export function getCharaFilename(chid) { } } +export function extractAllWords(value) { + const words = []; + + if (!value) { + return words; + } + + const matches = value.matchAll(/\b\w+\b/gim); + for (let match of matches) { + words.push(match[0].toLowerCase()); + } + return words; +} + export function escapeRegex(string) { return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&'); } diff --git a/public/style.css b/public/style.css index d2523f253..fda0ee5e3 100644 --- a/public/style.css +++ b/public/style.css @@ -1293,7 +1293,7 @@ body.charListGrid #rm_print_characters_block .tags_inline { } -.floating_prompt_radio_group { +.floating_prompt_radio_group, .radio_group { display: flex; flex-direction: column; }