diff --git a/public/index.html b/public/index.html index d8c1e1ebc..f37ba0d3e 100644 --- a/public/index.html +++ b/public/index.html @@ -1150,7 +1150,7 @@
-
+

AutoFormat Overrides

-
-

- Anchors Order - - ? - -

- -
- - +
+

Instruct mode

+ + + + +
+
+ +
+ +
+ +
+ +
+ +
-
+

Context Formatting

Tokenizer @@ -1268,6 +1285,26 @@

+
+

+ Anchors Order + + ? + +

+ +
+ + +
+
diff --git a/public/script.js b/public/script.js index 48a30dad6..2bcb1fdcd 100644 --- a/public/script.js +++ b/public/script.js @@ -60,6 +60,9 @@ import { power_user, pygmalion_options, tokenizers, + formatInstructModeChat, + formatInstructStoryString, + formatInstructModePrompt, } from "./scripts/power-user.js"; import { @@ -1218,6 +1221,15 @@ function getStoppingStrings(isImpersonate, addSpace) { } } + if (power_user.instruct.enabled) { + if (power_user.instruct.input_sequence) { + result.push(`\n${power_user.instruct.input_sequence}`); + } + if (power_user.instruct.output_sequence) { + result.push(`\n${power_user.instruct.output_sequence}`); + } + } + return addSpace ? result.map(x => `${x} `) : result; } @@ -1542,6 +1554,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject, generation_started = new Date(); const isImpersonate = type == "impersonate"; + const isInstruct = power_user.instruct.enabled; message_already_generated = isImpersonate ? `${name1}: ` : `${name2}: `; const interruptedByCommand = processCommands($("#send_textarea").val(), type); @@ -1720,6 +1733,10 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject, force_name2 = false; } + if (isInstruct) { + storyString = formatInstructStoryString(storyString); + } + ////////////////////////////////// let chat2 = []; @@ -1733,7 +1750,6 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject, let charName = selected_group ? coreChat[j].name : name2; let this_mes_ch_name = ''; if (coreChat[j]['is_user']) { - //this_mes_ch_name = name1; this_mes_ch_name = coreChat[j]['name']; } else { this_mes_ch_name = charName; @@ -1744,10 +1760,12 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject, chat2[i] = coreChat[j]['mes'] + '\n'; } + if (isInstruct) { + chat2[i] = formatInstructModeChat(this_mes_ch_name, coreChat[j]['mes'], coreChat[j]['is_user']); + } + // replace bias markup - //chat2[i] = (chat2[i] ?? '').replace(/{.*}/g, ''); chat2[i] = (chat2[i] ?? '').replace(/{{(\*?.*\*?)}}/g, ''); - //console.log('replacing chat2 {}s'); } //chat2 = chat2.reverse(); @@ -1904,7 +1922,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject, item += anchorBottom + "\n"; } } - if (is_pygmalion) { + if (is_pygmalion && !isInstruct) { if (i === arrMes.length - 1 && item.trim().startsWith(name1 + ":")) {//for add name2 when user sent item = item + name2 + ":"; } @@ -1951,9 +1969,14 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject, mesExmString = pinExmString ?? mesExamplesArray.slice(0, count_exm_add).join(''); mesSendString = ''; for (let j = 0; j < mesSend.length; j++) { - + const isBottom = j === mesSend.length - 1; mesSendString += mesSend[j]; - if (isImpersonate && j === mesSend.length - 1 && tokens_already_generated === 0) { + + if (isInstruct && isBottom && tokens_already_generated === 0) { + mesSendString += formatInstructModePrompt(isImpersonate); + } + + if (!isInstruct && isImpersonate && isBottom && tokens_already_generated === 0) { const name = is_pygmalion ? 'You' : name1; if (!mesSendString.endsWith('\n')) { mesSendString += '\n'; @@ -1961,7 +1984,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject, mesSendString += name + ':'; } - if (force_name2 && j === mesSend.length - 1 && tokens_already_generated === 0) { + if (force_name2 && isBottom && tokens_already_generated === 0) { if (!mesSendString.endsWith('\n')) { mesSendString += '\n'; } diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index 6ac313808..7c1ebd665 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -7,9 +7,11 @@ import { reloadMarkdownProcessor, reloadCurrentChat, getRequestHeaders, + substituteParams, } from "../script.js"; import { groups, + selected_group, } from "./group-chats.js"; export { @@ -109,6 +111,16 @@ let power_user = { allow_name2_display: false, hotswap_enabled: true, timer_enabled: true, + + instruct: { + enabled: false, + wrap: true, + names: false, + system_prompt: "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\nWrite {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}. Write 1 reply only.", + system_sequence: '', + input_sequence: '### Instruction:', + output_sequence: '### Response:', + } }; let themes = []; @@ -514,6 +526,59 @@ function loadPowerUserSettings(settings, data) { $(`#character_sort_order option[data-order="${power_user.sort_order}"][data-field="${power_user.sort_field}"]`).prop("selected", true); sortCharactersList(); reloadMarkdownProcessor(power_user.render_formulas); + loadInstructMode(); +} + +function loadInstructMode() { + const controls = [ + { id: "instruct_enabled", property: "enabled", isCheckbox: true }, + { id: "instruct_wrap", property: "wrap", isCheckbox: true }, + { id: "instruct_system_prompt", property: "system_prompt", isCheckbox: false }, + { id: "instruct_system_sequence", property: "system_sequence", isCheckbox: false }, + { id: "instruct_input_sequence", property: "input_sequence", isCheckbox: false }, + { id: "instruct_output_sequence", property: "output_sequence", isCheckbox: false }, + { id: "instruct_names", property: "names", isCheckbox: true }, + ]; + + controls.forEach(control => { + const $element = $(`#${control.id}`); + + if (control.isCheckbox) { + $element.prop('checked', power_user.instruct[control.property]); + } else { + $element.val(power_user.instruct[control.property]); + } + + $element.on('input', function () { + power_user.instruct[control.property] = control.isCheckbox ? $(this).prop('checked') : $(this).val(); + saveSettingsDebounced(); + }); + }); +} + +export function formatInstructModeChat(name, mes, isUser) { + const includeNames = power_user.instruct.names || (selected_group && !isUser); + const sequence = isUser ? power_user.instruct.input_sequence : power_user.instruct.output_sequence; + const separator = power_user.instruct.wrap ? '\n' : ''; + const textArray = includeNames ? [sequence, name, ': ', mes, separator] : [sequence, mes, separator]; + const text = textArray.filter(x => x).join(separator); + return text; +} + +export function formatInstructStoryString(story) { + const sequence = power_user.instruct.system_sequence || ''; + const prompt = substituteParams(power_user.instruct.system_prompt) || ''; + const separator = power_user.instruct.wrap ? '\n' : ''; + const textArray = [sequence, prompt, story, separator]; + const text = textArray.filter(x => x).join(separator); + return text; +} + +export function formatInstructModePrompt(isImpersonate) { + const sequence = isImpersonate ? power_user.instruct.input_sequence : power_user.instruct.output_sequence; + const separator = power_user.instruct.wrap ? '\n' : ''; + const text = separator + sequence; + return text; } const sortFunc = (a, b) => power_user.sort_order == 'asc' ? compareFunc(a, b) : compareFunc(b, a);