Summarize: configurable system/user role for the summary prompt Reviewed-by: auto-review

d52580592541ecaef1873a8a81e59876a4634ed5

permissionBRICK <permissionBRICK@gmail.com>

2 files changed, +68 -13Ignore whitespace
public/scripts/extensions/memory/index.js+62 -13
@@ -136,8 +136,20 @@ const defaultSettings = {
136136 maxMessagesPerRequestMax: 250,
137137 maxMessagesPerRequestStep: 1,
138138 prompt_builder: prompt_builders.DEFAULT,
139+ summaryPromptRole: extension_prompt_roles.SYSTEM,
139140};
140141
142+/**
143+ * Resolve the configured role for the summarization request prompt.
144+ * Only SYSTEM and USER are supported; anything else falls back to SYSTEM.
145+ * @returns {number} One of extension_prompt_roles.SYSTEM or extension_prompt_roles.USER
146+ */
147+function getSummaryPromptRole() {
148+ return Number(extension_settings.memory.summaryPromptRole) === extension_prompt_roles.USER
149+ ? extension_prompt_roles.USER
150+ : extension_prompt_roles.SYSTEM;
151+}
152+
141153function loadSettings() {
142154 if (Object.keys(extension_settings.memory).length === 0) {
143155 Object.assign(extension_settings.memory, defaultSettings);
@@ -158,6 +170,7 @@ function loadSettings() {
158170 $('#memory_template').val(extension_settings.memory.template).trigger('input');
159171 $('#memory_depth').val(extension_settings.memory.depth).trigger('input');
160172 $('#memory_role').val(extension_settings.memory.role).trigger('input');
173+ $('#memory_summary_prompt_role').val(extension_settings.memory.summaryPromptRole).trigger('input');
161174 $(`input[name="memory_position"][value="${extension_settings.memory.position}"]`).prop('checked', true).trigger('input');
162175 $('#memory_prompt_words_force').val(extension_settings.memory.promptForceWords).trigger('input');
163176 $(`input[name="memory_prompt_builder"][value="${extension_settings.memory.prompt_builder}"]`).prop('checked', true).trigger('input');
@@ -314,6 +327,12 @@ function onMemoryRoleInput() {
314327 saveSettingsDebounced();
315328}
316329
330+function onMemorySummaryPromptRoleInput() {
331+ const value = $(this).val();
332+ extension_settings.memory.summaryPromptRole = Number(value);
333+ saveSettingsDebounced();
334+}
335+
317336function onMemoryPositionChange(e) {
318337 const value = e.target.value;
319338 extension_settings.memory.position = value;
@@ -515,15 +534,22 @@ async function summarizeCallback(args, text) {
515534
516535 const source = args.source || extension_settings.memory.source;
517536 const prompt = substituteParamsExtended((args.prompt || extension_settings.memory.prompt), { words: extension_settings.memory.promptWords });
537+ const useUserRole = getSummaryPromptRole() === extension_prompt_roles.USER;
518538
519539 try {
520540 switch (source) {
521541 case summary_sources.extras:
522542 return await callExtrasSummarizeAPI(text);
523543 case summary_sources.main: {
524- return removeReasoningFromString(await generateRaw({ prompt: text, systemPrompt: prompt, responseLength: extension_settings.memory.overrideResponseLength }));
544+ // When the instruction should be a USER message, combine it into the
545+ // user content and clear the system prompt. Otherwise keep it as system.
546+ const rawPrompt = useUserRole ? [prompt, text].filter(x => x).join('\n\n') : text;
547+ const systemPrompt = useUserRole ? '' : prompt;
548+ return removeReasoningFromString(await generateRaw({ prompt: rawPrompt, systemPrompt: systemPrompt, responseLength: extension_settings.memory.overrideResponseLength }));
549+ }
525550 case summary_sources.webllm: {
526- const messages = [{ role: 'system', content: prompt }, { role: 'user', content: text }].filter(m => m.content);
551+ const promptRole = useUserRole ? 'user' : 'system';
552+ const messages = [{ role: promptRole, content: prompt }, { role: 'user', content: text }].filter(m => m.content);
527553 const params = extension_settings.memory.overrideResponseLength > 0 ? { max_tokens: extension_settings.memory.overrideResponseLength } : {};
528554 return await generateWebLlmChatPrompt(messages, params);
529555 }
@@ -646,8 +672,9 @@ async function summarizeChatWebLLM(context, force) {
646672 return null;
647673 }
648674
675+ const promptRole = getSummaryPromptRole() === extension_prompt_roles.USER ? 'user' : 'system';
649676 const messages = [
650677 { role: 'system'promptRole, content: prompt },
651678 { role: 'user', content: rawPrompt },
652679 ];
653680
@@ -692,13 +719,30 @@ async function summarizeChatMain(context, force, skipWIAN) {
692719 if (prompt_builders.DEFAULT === extension_settings.memory.prompt_builder) {
693720 try {
694721 inApiCall = true;
695- /** @type {import('../../../script.js').GenerateQuietPromptParams} */
722+ // generateQuietPrompt always injects the instruction as a SYSTEM message
696- const params = {
723+ // (the QUIET_PROMPT extension prompt has no exposed role parameter).
697- quietPrompt: prompt,
724+ // When a USER role is requested, assemble the chat transcript ourselves
698- skipWIAN: skipWIAN,
725+ // (same source as the raw builder) and deliver the instruction as USER
699- responseLength: extension_settings.memory.overrideResponseLength,
726+ // content via generateRaw, so the model still receives the chat to
700- };
727+ // summarize instead of just the bare instruction.
701- summary = await generateQuietPrompt(params);
728+ if (getSummaryPromptRole() === extension_prompt_roles.USER) {
729+ const { rawPrompt } = await getRawSummaryPrompt(context, prompt);
730+ /** @type {import('../../../script.js').GenerateRawParams} */
731+ const params = {
732+ prompt: [prompt, rawPrompt].filter(x => x).join('\n\n'),
733+ systemPrompt: '',
734+ responseLength: extension_settings.memory.overrideResponseLength,
735+ };
736+ summary = removeReasoningFromString(await generateRaw(params));
737+ } else {
738+ /** @type {import('../../../script.js').GenerateQuietPromptParams} */
739+ const params = {
740+ quietPrompt: prompt,
741+ skipWIAN: skipWIAN,
742+ responseLength: extension_settings.memory.overrideResponseLength,
743+ };
744+ summary = await generateQuietPrompt(params);
745+ }
702746 } finally {
703747 inApiCall = false;
704748 }
@@ -722,10 +766,14 @@ async function summarizeChatMain(context, force, skipWIAN) {
722766 return null;
723767 }
724768
769+ // When the summary instruction should be a USER message, deliver it as
770+ // user content and leave the system prompt empty. Otherwise keep the
771+ // existing behavior of sending it as the system prompt.
772+ const useUserRole = getSummaryPromptRole() === extension_prompt_roles.USER;
725773 /** @type {import('../../../script.js').GenerateRawParams} */
726774 const params = {
727- prompt: rawPrompt,
775+ prompt: useUserRole ? [prompt, rawPrompt].filter(x => x).join('\n\n') : rawPrompt,
728776 systemPrompt: useUserRole ? '' : prompt,
729777 responseLength: extension_settings.memory.overrideResponseLength,
730778 };
731779 const rawSummary = await generateRaw(params);
@@ -1047,6 +1095,7 @@ function setupListeners() {
10471095 $('#memory_template').off('input').on('input', onMemoryTemplateInput);
10481096 $('#memory_depth').off('input').on('input', onMemoryDepthInput);
10491097 $('#memory_role').off('input').on('input', onMemoryRoleInput);
1098+ $('#memory_summary_prompt_role').off('input').on('input', onMemorySummaryPromptRoleInput);
10501099 $('input[name="memory_position"]').off('change').on('change', onMemoryPositionChange);
10511100 $('#memory_prompt_words_force').off('input').on('input', onMemoryPromptWordsForceInput);
10521101 $('#memory_prompt_builder_default').off('input').on('input', onMemoryPromptBuilderInput);
public/scripts/extensions/memory/settings.html+6 -0
@@ -69,6 +69,12 @@
6969 </div>
7070 </label>
7171 <textarea id="memory_prompt" class="text_pole textarea_compact" rows="6" data-i18n="[placeholder]ext_sum_prompt_placeholder" placeholder="This prompt will be sent to AI to request the summary generation. &lcub;&lcub;words&rcub;&rcub; will resolve to the 'Number of words' parameter."></textarea>
72+ <label for="memory_summary_prompt_role" data-i18n="ext_sum_prompt_role">Summary Prompt Role</label>
73+ <select id="memory_summary_prompt_role" class="text_pole">
74+ <option value="0" data-i18n="System">System</option>
75+ <option value="1" data-i18n="User">User</option>
76+ </select>
77+ <small data-i18n="ext_sum_prompt_role_desc">The role used to send the summary request instruction to the model.</small>
7278 <label for="memory_prompt_words"><span data-i18n="ext_sum_target_length_1">Target summary length</span> <span data-i18n="ext_sum_target_length_2">(</span><span id="memory_prompt_words_value"></span><span data-i18n="ext_sum_target_length_3"> words)</span></label>
7379 <input id="memory_prompt_words" type="range" value="{{defaultSettings.promptWords}}" min="{{defaultSettings.promptMinWords}}" max="{{defaultSettings.promptMaxWords}}" step="{{defaultSettings.promptWordsStep}}" />
7480 <label for="memory_override_response_length">