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 = {
136 maxMessagesPerRequestMax: 250,136 maxMessagesPerRequestMax: 250,
137 maxMessagesPerRequestStep: 1,137 maxMessagesPerRequestStep: 1,
138 prompt_builder: prompt_builders.DEFAULT,138 prompt_builder: prompt_builders.DEFAULT,
139 summaryPromptRole: extension_prompt_roles.SYSTEM,
139};140};
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 */
147function getSummaryPromptRole() {
148 return Number(extension_settings.memory.summaryPromptRole) === extension_prompt_roles.USER
149 ? extension_prompt_roles.USER
150 : extension_prompt_roles.SYSTEM;
151}
152
141function loadSettings() {153function loadSettings() {
142 if (Object.keys(extension_settings.memory).length === 0) {154 if (Object.keys(extension_settings.memory).length === 0) {
143 Object.assign(extension_settings.memory, defaultSettings);155 Object.assign(extension_settings.memory, defaultSettings);
@@ -158,6 +170,7 @@ function loadSettings() {
158 $('#memory_template').val(extension_settings.memory.template).trigger('input');170 $('#memory_template').val(extension_settings.memory.template).trigger('input');
159 $('#memory_depth').val(extension_settings.memory.depth).trigger('input');171 $('#memory_depth').val(extension_settings.memory.depth).trigger('input');
160 $('#memory_role').val(extension_settings.memory.role).trigger('input');172 $('#memory_role').val(extension_settings.memory.role).trigger('input');
173 $('#memory_summary_prompt_role').val(extension_settings.memory.summaryPromptRole).trigger('input');
161 $(`input[name="memory_position"][value="${extension_settings.memory.position}"]`).prop('checked', true).trigger('input');174 $(`input[name="memory_position"][value="${extension_settings.memory.position}"]`).prop('checked', true).trigger('input');
162 $('#memory_prompt_words_force').val(extension_settings.memory.promptForceWords).trigger('input');175 $('#memory_prompt_words_force').val(extension_settings.memory.promptForceWords).trigger('input');
163 $(`input[name="memory_prompt_builder"][value="${extension_settings.memory.prompt_builder}"]`).prop('checked', true).trigger('input');176 $(`input[name="memory_prompt_builder"][value="${extension_settings.memory.prompt_builder}"]`).prop('checked', true).trigger('input');
@@ -314,6 +327,12 @@ function onMemoryRoleInput() {
314 saveSettingsDebounced();327 saveSettingsDebounced();
315}328}
316329
330function onMemorySummaryPromptRoleInput() {
331 const value = $(this).val();
332 extension_settings.memory.summaryPromptRole = Number(value);
333 saveSettingsDebounced();
334}
335
317function onMemoryPositionChange(e) {336function onMemoryPositionChange(e) {
318 const value = e.target.value;337 const value = e.target.value;
319 extension_settings.memory.position = value;338 extension_settings.memory.position = value;
@@ -515,15 +534,22 @@ async function summarizeCallback(args, text) {
515534
516 const source = args.source || extension_settings.memory.source;535 const source = args.source || extension_settings.memory.source;
517 const prompt = substituteParamsExtended((args.prompt || extension_settings.memory.prompt), { words: extension_settings.memory.promptWords });536 const prompt = substituteParamsExtended((args.prompt || extension_settings.memory.prompt), { words: extension_settings.memory.promptWords });
537 const useUserRole = getSummaryPromptRole() === extension_prompt_roles.USER;
518538
519 try {539 try {
520 switch (source) {540 switch (source) {
521 case summary_sources.extras:541 case summary_sources.extras:
522 return await callExtrasSummarizeAPI(text);542 return await callExtrasSummarizeAPI(text);
523 case summary_sources.main:543 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 }
525 case summary_sources.webllm: {550 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);
527 const params = extension_settings.memory.overrideResponseLength > 0 ? { max_tokens: extension_settings.memory.overrideResponseLength } : {};553 const params = extension_settings.memory.overrideResponseLength > 0 ? { max_tokens: extension_settings.memory.overrideResponseLength } : {};
528 return await generateWebLlmChatPrompt(messages, params);554 return await generateWebLlmChatPrompt(messages, params);
529 }555 }
@@ -646,8 +672,9 @@ async function summarizeChatWebLLM(context, force) {
646 return null;672 return null;
647 }673 }
648674
675 const promptRole = getSummaryPromptRole() === extension_prompt_roles.USER ? 'user' : 'system';
649 const messages = [676 const messages = [
650 { role: 'system', content: prompt },677 { role: promptRole, content: prompt },
651 { role: 'user', content: rawPrompt },678 { role: 'user', content: rawPrompt },
652 ];679 ];
653680
@@ -692,13 +719,30 @@ async function summarizeChatMain(context, force, skipWIAN) {
692 if (prompt_builders.DEFAULT === extension_settings.memory.prompt_builder) {719 if (prompt_builders.DEFAULT === extension_settings.memory.prompt_builder) {
693 try {720 try {
694 inApiCall = true;721 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 }
702 } finally {746 } finally {
703 inApiCall = false;747 inApiCall = false;
704 }748 }
@@ -722,10 +766,14 @@ async function summarizeChatMain(context, force, skipWIAN) {
722 return null;766 return null;
723 }767 }
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;
725 /** @type {import('../../../script.js').GenerateRawParams} */773 /** @type {import('../../../script.js').GenerateRawParams} */
726 const params = {774 const params = {
727 prompt: rawPrompt,775 prompt: useUserRole ? [prompt, rawPrompt].filter(x => x).join('\n\n') : rawPrompt,
728 systemPrompt: prompt,776 systemPrompt: useUserRole ? '' : prompt,
729 responseLength: extension_settings.memory.overrideResponseLength,777 responseLength: extension_settings.memory.overrideResponseLength,
730 };778 };
731 const rawSummary = await generateRaw(params);779 const rawSummary = await generateRaw(params);
@@ -1047,6 +1095,7 @@ function setupListeners() {
1047 $('#memory_template').off('input').on('input', onMemoryTemplateInput);1095 $('#memory_template').off('input').on('input', onMemoryTemplateInput);
1048 $('#memory_depth').off('input').on('input', onMemoryDepthInput);1096 $('#memory_depth').off('input').on('input', onMemoryDepthInput);
1049 $('#memory_role').off('input').on('input', onMemoryRoleInput);1097 $('#memory_role').off('input').on('input', onMemoryRoleInput);
1098 $('#memory_summary_prompt_role').off('input').on('input', onMemorySummaryPromptRoleInput);
1050 $('input[name="memory_position"]').off('change').on('change', onMemoryPositionChange);1099 $('input[name="memory_position"]').off('change').on('change', onMemoryPositionChange);
1051 $('#memory_prompt_words_force').off('input').on('input', onMemoryPromptWordsForceInput);1100 $('#memory_prompt_words_force').off('input').on('input', onMemoryPromptWordsForceInput);
1052 $('#memory_prompt_builder_default').off('input').on('input', onMemoryPromptBuilderInput);1101 $('#memory_prompt_builder_default').off('input').on('input', onMemoryPromptBuilderInput);
public/scripts/extensions/memory/settings.html+6 -0
@@ -69,6 +69,12 @@
69 </div>69 </div>
70 </label>70 </label>
71 <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>71 <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>
72 <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>78 <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>
73 <input id="memory_prompt_words" type="range" value="{{defaultSettings.promptWords}}" min="{{defaultSettings.promptMinWords}}" max="{{defaultSettings.promptMaxWords}}" step="{{defaultSettings.promptWordsStep}}" />79 <input id="memory_prompt_words" type="range" value="{{defaultSettings.promptWords}}" min="{{defaultSettings.promptMinWords}}" max="{{defaultSettings.promptMaxWords}}" step="{{defaultSettings.promptWordsStep}}" />
74 <label for="memory_override_response_length">80 <label for="memory_override_response_length">