mirror of
				https://github.com/SillyTavern/SillyTavern.git
				synced 2025-06-05 21:59:27 +02:00 
			
		
		
		
	Add auto-select for instruct presets. Re-organize instruct mode code. Prepare for preset manager integration
This commit is contained in:
		| @@ -2143,7 +2143,7 @@ | ||||
|                             </label> | ||||
|                             <div class="flex-container"> | ||||
|                                 <select id="instruct_presets" class="flex1 margin0"></select> | ||||
|                                 <div id="instruct_set_default" class="menu_button menu_button_icon margin0"> | ||||
|                                 <div id="instruct_set_default" class="menu_button menu_button_icon margin0" title="Auto-select this preset on API connection."> | ||||
|                                     <i class="fa-solid fa-xs fa-fw fa-heart"></i> | ||||
|                                     <span data-i18n="Default">Default</span> | ||||
|                                 </div> | ||||
|   | ||||
| @@ -66,9 +66,6 @@ import { | ||||
|     power_user, | ||||
|     pygmalion_options, | ||||
|     tokenizers, | ||||
|     formatInstructModeChat, | ||||
|     formatInstructStoryString, | ||||
|     formatInstructModePrompt, | ||||
|     persona_description_positions, | ||||
|     loadMovingUIState, | ||||
|     getCustomStoppingStrings, | ||||
| @@ -165,6 +162,13 @@ import { deviceInfo } from "./scripts/RossAscends-mods.js"; | ||||
| import { registerPromptManagerMigration } from "./scripts/PromptManager.js"; | ||||
| import { getRegexedString, regex_placement } from "./scripts/extensions/regex/engine.js"; | ||||
| import { FILTER_TYPES, FilterHelper } from "./scripts/filters.js"; | ||||
| import { | ||||
|     formatInstructModeChat, | ||||
|     formatInstructModePrompt, | ||||
|     getInstructStoppingSequences, | ||||
|     formatInstructStoryString, | ||||
|     autoSelectInstructPreset, | ||||
| } from "./scripts/instruct-mode.js"; | ||||
|  | ||||
| //exporting functions and vars for mods | ||||
| export { | ||||
| @@ -330,7 +334,6 @@ let scrollLock = false; | ||||
| const durationSaveEdit = 1000; | ||||
| const saveSettingsDebounced = debounce(() => saveSettings(), durationSaveEdit); | ||||
| export const saveCharacterDebounced = debounce(() => $("#create_button").trigger('click'), durationSaveEdit); | ||||
| const getStatusDebounced = debounce(() => getStatus(), 300_000); | ||||
| const saveChatDebounced = debounce(() => saveChatConditional(), durationSaveEdit); | ||||
|  | ||||
| const system_message_types = { | ||||
| @@ -884,10 +887,6 @@ async function getStatus() { | ||||
|                 const hordeStatus = await checkHordeStatus(); | ||||
|                 online_status = hordeStatus ? 'Connected' : 'no_connection'; | ||||
|                 resultCheckStatus(); | ||||
|  | ||||
|                 if (online_status !== "no_connection") { | ||||
|                     getStatusDebounced(); | ||||
|                 } | ||||
|             } | ||||
|             catch { | ||||
|                 online_status = "no_connection"; | ||||
| @@ -916,6 +915,10 @@ async function getStatus() { | ||||
|                 if (online_status == undefined) { | ||||
|                     online_status = "no_connection"; | ||||
|                 } | ||||
|  | ||||
|                 // Determine instruct mode preset | ||||
|                 autoSelectInstructPreset(online_status); | ||||
|  | ||||
|                 if ((online_status.toLowerCase().indexOf("pygmalion") != -1 && power_user.pygmalion_formatting == pygmalion_options.AUTO) | ||||
|                     || (online_status !== "no_connection" && power_user.pygmalion_formatting == pygmalion_options.ENABLED)) { | ||||
|                     is_pygmalion = true; | ||||
| @@ -937,9 +940,6 @@ async function getStatus() { | ||||
|  | ||||
|                 //console.log(online_status); | ||||
|                 resultCheckStatus(); | ||||
|                 if (online_status !== "no_connection") { | ||||
|                     getStatusDebounced(); | ||||
|                 } | ||||
|             }, | ||||
|             error: function (jqXHR, exception) { | ||||
|                 console.log(exception); | ||||
| @@ -1913,32 +1913,7 @@ function getStoppingStrings(isImpersonate, addSpace) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function addInstructSequence(sequence) { | ||||
|         // Cohee: oobabooga's textgen always appends newline before the sequence as a stopping string | ||||
|         // But it's a problem for Metharme which doesn't use newlines to separate them. | ||||
|         const wrap = (s) => power_user.instruct.wrap ? '\n' + s : s; | ||||
|         // Sequence must be a non-empty string | ||||
|         if (typeof sequence === 'string' && sequence.length > 0) { | ||||
|             // If sequence is just a whitespace or newline - we don't want to make it a stopping string | ||||
|             // User can always add it as a custom stop string if really needed | ||||
|             if (sequence.trim().length > 0) { | ||||
|                 const wrappedSequence = wrap(sequence); | ||||
|                 // Need to respect "insert macro" setting | ||||
|                 const stopString = power_user.instruct.macro ? substituteParams(wrappedSequence) : wrappedSequence; | ||||
|                 result.push(stopString); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (power_user.instruct.enabled) { | ||||
|         const input_sequence = power_user.instruct.input_sequence; | ||||
|         const output_sequence = power_user.instruct.output_sequence; | ||||
|         const last_output_sequence = power_user.instruct.last_output_sequence; | ||||
|  | ||||
|         const combined_sequence = `${input_sequence}\n${output_sequence}\n${last_output_sequence}`; | ||||
|  | ||||
|         combined_sequence.split('\n').filter((line, index, self) => self.indexOf(line) === index).forEach(addInstructSequence); | ||||
|     } | ||||
|     result.push(...getInstructStoppingSequences()); | ||||
|  | ||||
|     if (power_user.custom_stopping_strings) { | ||||
|         const customStoppingStrings = getCustomStoppingStrings(); | ||||
|   | ||||
| @@ -8,6 +8,8 @@ import { | ||||
| import { SECRET_KEYS, writeSecret } from "./secrets.js"; | ||||
| import { delay } from "./utils.js"; | ||||
| import { deviceInfo } from "./RossAscends-mods.js"; | ||||
| import { power_user } from "./power-user.js"; | ||||
| import { autoSelectInstructPreset } from "./instruct-mode.js"; | ||||
|  | ||||
| export { | ||||
|     horde_settings, | ||||
| @@ -226,19 +228,11 @@ async function showKudos() { | ||||
|  | ||||
| jQuery(function () { | ||||
|     $("#horde_model").on('mousedown change', async function (e) { | ||||
|         //desktop-only routine for multi-select without CTRL | ||||
|         /*if (deviceInfo.device.type === 'desktop') { | ||||
|             let hordeModelSelectScrollTop = null; | ||||
|             e.preventDefault(); | ||||
|             const option = $(e.target); | ||||
|             const selectElement = $(this)[0]; | ||||
|             hordeModelSelectScrollTop = selectElement.scrollTop; | ||||
|             option.prop('selected', !option.prop('selected')); | ||||
|             await delay(1); | ||||
|             selectElement.scrollTop = hordeModelSelectScrollTop; | ||||
|         }*/ | ||||
|         horde_settings.models = $('#horde_model').val(); | ||||
|         console.log('Updated Horde models', horde_settings.models); | ||||
|  | ||||
|         // Try select instruct preset | ||||
|         autoSelectInstructPreset(horde_settings.models.join(' ')); | ||||
|     }); | ||||
|  | ||||
|     $("#horde_auto_adjust_response_length").on("input", function () { | ||||
|   | ||||
							
								
								
									
										253
									
								
								public/scripts/instruct-mode.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								public/scripts/instruct-mode.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,253 @@ | ||||
| "use strict"; | ||||
|  | ||||
| import { name1, name2, saveSettingsDebounced, substituteParams } from "../script.js"; | ||||
| import { selected_group } from "./group-chats.js"; | ||||
| import { power_user } from "./power-user.js"; | ||||
|  | ||||
| export let instruct_presets = []; | ||||
|  | ||||
| /** | ||||
|  * Loads instruct mode settings from the given data object. | ||||
|  * @param {object} data Settings data object. | ||||
|  */ | ||||
| export function loadInstructMode(data) { | ||||
|     if (data.instruct !== undefined) { | ||||
|         instruct_presets = data.instruct; | ||||
|     } | ||||
|  | ||||
|     if (power_user.instruct.names_force_groups === undefined) { | ||||
|         power_user.instruct.names_force_groups = true; | ||||
|     } | ||||
|  | ||||
|     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_separator_sequence", property: "separator_sequence", isCheckbox: false }, | ||||
|         { id: "instruct_input_sequence", property: "input_sequence", isCheckbox: false }, | ||||
|         { id: "instruct_output_sequence", property: "output_sequence", isCheckbox: false }, | ||||
|         { id: "instruct_stop_sequence", property: "stop_sequence", isCheckbox: false }, | ||||
|         { id: "instruct_names", property: "names", isCheckbox: true }, | ||||
|         { id: "instruct_macro", property: "macro", isCheckbox: true }, | ||||
|         { id: "instruct_names_force_groups", property: "names_force_groups", isCheckbox: true }, | ||||
|         { id: "instruct_last_output_sequence", property: "last_output_sequence", isCheckbox: false }, | ||||
|         { id: "instruct_activation_regex", property: "activation_regex", isCheckbox: false }, | ||||
|     ]; | ||||
|  | ||||
|     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(); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     instruct_presets.forEach((preset) => { | ||||
|         const name = preset.name; | ||||
|         const option = document.createElement('option'); | ||||
|         option.value = name; | ||||
|         option.innerText = name; | ||||
|         option.selected = name === power_user.instruct.preset; | ||||
|         $('#instruct_presets').append(option); | ||||
|     }); | ||||
|  | ||||
|     function highlightDefaultPreset() { | ||||
|         $('#instruct_set_default').toggleClass('default', power_user.default_instruct === power_user.instruct.preset); | ||||
|     } | ||||
|  | ||||
|     $('#instruct_set_default').on('click', function () { | ||||
|         power_user.default_instruct = power_user.instruct.preset; | ||||
|         $(this).addClass('default'); | ||||
|         toastr.success(`Default instruct preset set to ${power_user.default_instruct}`); | ||||
|         saveSettingsDebounced(); | ||||
|     }); | ||||
|  | ||||
|     highlightDefaultPreset(); | ||||
|  | ||||
|     $('#instruct_presets').on('change', function () { | ||||
|         const name = $(this).find(':selected').val(); | ||||
|         const preset = instruct_presets.find(x => x.name === name); | ||||
|  | ||||
|         if (!preset) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         power_user.instruct.preset = name; | ||||
|         controls.forEach(control => { | ||||
|             if (preset[control.property] !== undefined) { | ||||
|                 power_user.instruct[control.property] = preset[control.property]; | ||||
|                 const $element = $(`#${control.id}`); | ||||
|  | ||||
|                 if (control.isCheckbox) { | ||||
|                     $element.prop('checked', power_user.instruct[control.property]).trigger('input'); | ||||
|                 } else { | ||||
|                     $element.val(power_user.instruct[control.property]).trigger('input'); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         highlightDefaultPreset(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Automatically select instruct preset based on model id. | ||||
|  * 1. Enables instruct mode if preset is found. | ||||
|  * 2. Otherwise, if default instruct preset is set, selects it. | ||||
|  * @param {string} modelId Model name reported by the API. | ||||
|  * @returns {boolean} True if instruct preset was activated by model id, false otherwise. | ||||
|  */ | ||||
| export function autoSelectInstructPreset(modelId) { | ||||
|     for (const preset of instruct_presets) { | ||||
|         // If activation regex is set, check if it matches the model id | ||||
|         if (preset.activation_regex) { | ||||
|             try { | ||||
|                 const modeState = power_user.instruct.enabled; | ||||
|                 const regex = new RegExp(preset.activation_regex, 'i'); | ||||
|  | ||||
|                 // If regex matches, select the preset and enable instruct mode | ||||
|                 // Should not enable instruct mode if it's already enabled and the preset is the same | ||||
|                 if (regex.test(modelId) && (power_user.instruct.preset !== preset.name || !modeState)) { | ||||
|                     $('#instruct_presets').val(preset.name).trigger('change'); | ||||
|                     $('#instruct_enabled').prop('checked', true).trigger('input'); | ||||
|                     console.log(`Instruct mode: preset "${preset.name}" auto-selected`); | ||||
|  | ||||
|                     // If instruct mode was disabled, show a notification | ||||
|                     if (!modeState) { | ||||
|                         toastr.info(`Instruct mode enabled by preset "${preset.name}" for model "${modelId}"`); | ||||
|                     } | ||||
|  | ||||
|                     return true; | ||||
|                 } | ||||
|             } catch { | ||||
|                 // If regex is invalid, ignore it | ||||
|                 console.warn(`Invalid instruct activation regex in preset "${preset.name}"`); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (power_user.instruct.enabled && power_user.default_instruct && power_user.instruct.preset !== power_user.default_instruct) { | ||||
|         if (instruct_presets.some(p => p.name === power_user.default_instruct)) { | ||||
|             console.log(`Instruct mode: default preset "${power_user.default_instruct}" selected`); | ||||
|             $('#instruct_presets').val(power_user.default_instruct).trigger('change'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Converts instruct mode sequences to an array of stopping strings. | ||||
|  * @returns {string[]} Array of instruct mode stopping strings. | ||||
|  */ | ||||
| export function getInstructStoppingSequences() { | ||||
|     function addInstructSequence(sequence) { | ||||
|         // Cohee: oobabooga's textgen always appends newline before the sequence as a stopping string | ||||
|         // But it's a problem for Metharme which doesn't use newlines to separate them. | ||||
|         const wrap = (s) => power_user.instruct.wrap ? '\n' + s : s; | ||||
|         // Sequence must be a non-empty string | ||||
|         if (typeof sequence === 'string' && sequence.length > 0) { | ||||
|             // If sequence is just a whitespace or newline - we don't want to make it a stopping string | ||||
|             // User can always add it as a custom stop string if really needed | ||||
|             if (sequence.trim().length > 0) { | ||||
|                 const wrappedSequence = wrap(sequence); | ||||
|                 // Need to respect "insert macro" setting | ||||
|                 const stopString = power_user.instruct.macro ? substituteParams(wrappedSequence) : wrappedSequence; | ||||
|                 result.push(stopString); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     const result = []; | ||||
|  | ||||
|     if (power_user.instruct.enabled) { | ||||
|         const input_sequence = power_user.instruct.input_sequence; | ||||
|         const output_sequence = power_user.instruct.output_sequence; | ||||
|         const last_output_sequence = power_user.instruct.last_output_sequence; | ||||
|  | ||||
|         const combined_sequence = `${input_sequence}\n${output_sequence}\n${last_output_sequence}`; | ||||
|  | ||||
|         combined_sequence.split('\n').filter((line, index, self) => self.indexOf(line) === index).forEach(addInstructSequence); | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Formats instruct mode chat message. | ||||
|  * @param {string} name Character name. | ||||
|  * @param {string} mes Message text. | ||||
|  * @param {boolean} isUser Is the message from the user. | ||||
|  * @param {boolean} isNarrator Is the message from the narrator. | ||||
|  * @param {string} forceAvatar Force avatar string. | ||||
|  * @param {string} name1 User name. | ||||
|  * @param {string} name2 Character name. | ||||
|  * @returns {string} Formatted instruct mode chat message. | ||||
|  */ | ||||
| export function formatInstructModeChat(name, mes, isUser, isNarrator, forceAvatar, name1, name2) { | ||||
|     let includeNames = isNarrator ? false : power_user.instruct.names; | ||||
|  | ||||
|     if (!isNarrator && power_user.instruct.names_force_groups && (selected_group || forceAvatar)) { | ||||
|         includeNames = true; | ||||
|     } | ||||
|  | ||||
|     let sequence = (isUser || isNarrator) ? power_user.instruct.input_sequence : power_user.instruct.output_sequence; | ||||
|  | ||||
|     if (power_user.instruct.macro) { | ||||
|         sequence = substituteParams(sequence, name1, name2); | ||||
|     } | ||||
|  | ||||
|     const separator = power_user.instruct.wrap ? '\n' : ''; | ||||
|     const separatorSequence = power_user.instruct.separator_sequence && !isUser | ||||
|         ? power_user.instruct.separator_sequence | ||||
|         : separator; | ||||
|     const textArray = includeNames ? [sequence, `${name}: ${mes}` + separatorSequence] : [sequence, mes + separatorSequence]; | ||||
|     const text = textArray.filter(x => x).join(separator); | ||||
|     return text; | ||||
| } | ||||
|  | ||||
| export function formatInstructStoryString(story, systemPrompt) { | ||||
|     // If the character has a custom system prompt AND user has it preferred, use that instead of the default | ||||
|     systemPrompt = power_user.prefer_character_prompt && systemPrompt ? systemPrompt : power_user.instruct.system_prompt; | ||||
|     const sequence = power_user.instruct.system_sequence || ''; | ||||
|     const prompt = substituteParams(systemPrompt, name1, name2, power_user.instruct.system_prompt) || ''; | ||||
|     const separator = power_user.instruct.wrap ? '\n' : ''; | ||||
|     const textArray = [sequence, prompt + '\n' + story]; | ||||
|     const text = textArray.filter(x => x).join(separator); | ||||
|     return text; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Formats instruct mode last prompt line. | ||||
|  * @param {string} name Character name. | ||||
|  * @param {boolean} isImpersonate Is generation in impersonation mode. | ||||
|  * @param {string} promptBias Prompt bias string. | ||||
|  * @param {string} name1 User name. | ||||
|  * @param {string} name2 Character name. | ||||
|  */ | ||||
| export function formatInstructModePrompt(name, isImpersonate, promptBias, name1, name2) { | ||||
|     const includeNames = power_user.instruct.names || (!!selected_group && power_user.instruct.names_force_groups); | ||||
|     const getOutputSequence = () => power_user.instruct.last_output_sequence || power_user.instruct.output_sequence; | ||||
|     let sequence = isImpersonate ? power_user.instruct.input_sequence : getOutputSequence(); | ||||
|  | ||||
|     if (power_user.instruct.macro) { | ||||
|         sequence = substituteParams(sequence, name1, name2); | ||||
|     } | ||||
|  | ||||
|     const separator = power_user.instruct.wrap ? '\n' : ''; | ||||
|     let text = includeNames ? (separator + sequence + separator + `${name}:`) : (separator + sequence); | ||||
|  | ||||
|     if (!isImpersonate && promptBias) { | ||||
|         text += (includeNames ? promptBias : (separator + promptBias)); | ||||
|     } | ||||
|  | ||||
|     return text.trimEnd() + (includeNames ? '' : separator); | ||||
| } | ||||
| @@ -12,8 +12,6 @@ import { | ||||
|     event_types, | ||||
|     getCurrentChatId, | ||||
|     printCharacters, | ||||
|     name1, | ||||
|     name2, | ||||
|     setCharacterId, | ||||
|     setEditedMessageId | ||||
| } from "../script.js"; | ||||
| @@ -21,8 +19,8 @@ import { isMobile, initMovingUI } from "./RossAscends-mods.js"; | ||||
| import { | ||||
|     groups, | ||||
|     resetSelectedGroup, | ||||
|     selected_group, | ||||
| } from "./group-chats.js"; | ||||
| import { loadInstructMode } from "./instruct-mode.js"; | ||||
|  | ||||
| import { registerSlashCommand } from "./slash-commands.js"; | ||||
|  | ||||
| @@ -206,7 +204,6 @@ let power_user = { | ||||
|  | ||||
| let themes = []; | ||||
| let movingUIPresets = []; | ||||
| let instruct_presets = []; | ||||
| let context_presets = []; | ||||
|  | ||||
| const storage_keys = { | ||||
| @@ -666,9 +663,6 @@ function loadPowerUserSettings(settings, data) { | ||||
|         movingUIPresets = data.movingUIPresets; | ||||
|     } | ||||
|  | ||||
|     if (data.instruct !== undefined) { | ||||
|         instruct_presets = data.instruct; | ||||
|     } | ||||
|  | ||||
|     if (data.context !== undefined) { | ||||
|         context_presets = data.context; | ||||
| @@ -804,7 +798,7 @@ function loadPowerUserSettings(settings, data) { | ||||
|  | ||||
|     $(`#character_sort_order option[data-order="${power_user.sort_order}"][data-field="${power_user.sort_field}"]`).prop("selected", true); | ||||
|     reloadMarkdownProcessor(power_user.render_formulas); | ||||
|     loadInstructMode(); | ||||
|     loadInstructMode(data); | ||||
|     loadContextSettings(); | ||||
|     loadMaxContextUnlocked(); | ||||
|     switchWaifuMode(); | ||||
| @@ -928,90 +922,6 @@ function loadContextSettings() { | ||||
|     }); | ||||
| } | ||||
|  | ||||
| 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_separator_sequence", property: "separator_sequence", isCheckbox: false }, | ||||
|         { id: "instruct_input_sequence", property: "input_sequence", isCheckbox: false }, | ||||
|         { id: "instruct_output_sequence", property: "output_sequence", isCheckbox: false }, | ||||
|         { id: "instruct_stop_sequence", property: "stop_sequence", isCheckbox: false }, | ||||
|         { id: "instruct_names", property: "names", isCheckbox: true }, | ||||
|         { id: "instruct_macro", property: "macro", isCheckbox: true }, | ||||
|         { id: "instruct_names_force_groups", property: "names_force_groups", isCheckbox: true }, | ||||
|         { id: "instruct_last_output_sequence", property: "last_output_sequence", isCheckbox: false }, | ||||
|         { id: "instruct_activation_regex", property: "activation_regex", isCheckbox: false }, | ||||
|     ]; | ||||
|  | ||||
|     if (power_user.instruct.names_force_groups === undefined) { | ||||
|         power_user.instruct.names_force_groups = 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(); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     instruct_presets.forEach((preset) => { | ||||
|         const name = preset.name; | ||||
|         const option = document.createElement('option'); | ||||
|         option.value = name; | ||||
|         option.innerText = name; | ||||
|         option.selected = name === power_user.instruct.preset; | ||||
|         $('#instruct_presets').append(option); | ||||
|     }); | ||||
|  | ||||
|     function highlightDefaultPreset() { | ||||
|         $('#instruct_set_default').toggleClass('default', power_user.default_instruct === power_user.instruct.preset); | ||||
|     } | ||||
|  | ||||
|     $('#instruct_set_default').on('click', function () { | ||||
|         power_user.default_instruct = power_user.instruct.preset; | ||||
|         $(this).addClass('default'); | ||||
|         toastr.success(`Default instruct preset set to ${power_user.default_instruct}`); | ||||
|         saveSettingsDebounced(); | ||||
|     }); | ||||
|  | ||||
|     highlightDefaultPreset(); | ||||
|  | ||||
|     $('#instruct_presets').on('change', function () { | ||||
|         const name = $(this).find(':selected').val(); | ||||
|         const preset = instruct_presets.find(x => x.name === name); | ||||
|  | ||||
|         if (!preset) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         power_user.instruct.preset = name; | ||||
|         controls.forEach(control => { | ||||
|             if (preset[control.property] !== undefined) { | ||||
|                 power_user.instruct[control.property] = preset[control.property]; | ||||
|                 const $element = $(`#${control.id}`); | ||||
|  | ||||
|                 if (control.isCheckbox) { | ||||
|                     $element.prop('checked', power_user.instruct[control.property]).trigger('input'); | ||||
|                 } else { | ||||
|                     $element.val(power_user.instruct[control.property]).trigger('input'); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         highlightDefaultPreset(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export function fuzzySearchCharacters(searchValue) { | ||||
|     const fuse = new Fuse(characters, { | ||||
|         keys: [ | ||||
| @@ -1068,58 +978,6 @@ export function renderStoryString(params) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| export function formatInstructModeChat(name, mes, isUser, isNarrator, forceAvatar, name1, name2) { | ||||
|     let includeNames = isNarrator ? false : power_user.instruct.names; | ||||
|  | ||||
|     if (!isNarrator && power_user.instruct.names_force_groups && (selected_group || forceAvatar)) { | ||||
|         includeNames = true; | ||||
|     } | ||||
|  | ||||
|     let sequence = (isUser || isNarrator) ? power_user.instruct.input_sequence : power_user.instruct.output_sequence; | ||||
|  | ||||
|     if (power_user.instruct.macro) { | ||||
|         sequence = substituteParams(sequence, name1, name2); | ||||
|     } | ||||
|  | ||||
|     const separator = power_user.instruct.wrap ? '\n' : ''; | ||||
|     const separatorSequence = power_user.instruct.separator_sequence && !isUser | ||||
|         ? power_user.instruct.separator_sequence | ||||
|         : separator; | ||||
|     const textArray = includeNames ? [sequence, `${name}: ${mes}` + separatorSequence] : [sequence, mes + separatorSequence]; | ||||
|     const text = textArray.filter(x => x).join(separator); | ||||
|     return text; | ||||
| } | ||||
|  | ||||
| export function formatInstructStoryString(story, systemPrompt) { | ||||
|     // If the character has a custom system prompt AND user has it preferred, use that instead of the default | ||||
|     systemPrompt = power_user.prefer_character_prompt && systemPrompt ? systemPrompt : power_user.instruct.system_prompt; | ||||
|     const sequence = power_user.instruct.system_sequence || ''; | ||||
|     const prompt = substituteParams(systemPrompt, name1, name2, power_user.instruct.system_prompt) || ''; | ||||
|     const separator = power_user.instruct.wrap ? '\n' : ''; | ||||
|     const textArray = [sequence, prompt + '\n' + story]; | ||||
|     const text = textArray.filter(x => x).join(separator); | ||||
|     return text; | ||||
| } | ||||
|  | ||||
| export function formatInstructModePrompt(name, isImpersonate, promptBias, name1, name2) { | ||||
|     const includeNames = power_user.instruct.names || (!!selected_group && power_user.instruct.names_force_groups); | ||||
|     const getOutputSequence = () => power_user.instruct.last_output_sequence || power_user.instruct.output_sequence; | ||||
|     let sequence = isImpersonate ? power_user.instruct.input_sequence : getOutputSequence(); | ||||
|  | ||||
|     if (power_user.instruct.macro) { | ||||
|         sequence = substituteParams(sequence, name1, name2); | ||||
|     } | ||||
|  | ||||
|     const separator = power_user.instruct.wrap ? '\n' : ''; | ||||
|     let text = includeNames ? (separator + sequence + separator + `${name}:`) : (separator + sequence); | ||||
|  | ||||
|     if (!isImpersonate && promptBias) { | ||||
|         text += (includeNames ? promptBias : (separator + promptBias)); | ||||
|     } | ||||
|  | ||||
|     return text.trimEnd() + (includeNames ? '' : separator); | ||||
| } | ||||
|  | ||||
| const sortFunc = (a, b) => power_user.sort_order == 'asc' ? compareFunc(a, b) : compareFunc(b, a); | ||||
| const compareFunc = (first, second) => { | ||||
|     if (power_user.sort_order == 'random') { | ||||
|   | ||||
| @@ -55,8 +55,10 @@ function autoSelectPreset() { | ||||
|     } | ||||
| } | ||||
|  | ||||
| function getPresetManager() { | ||||
|     const apiId = main_api == 'koboldhorde' ? 'kobold' : main_api; | ||||
| function getPresetManager(apiId) { | ||||
|     if (!apiId) { | ||||
|         apiId = main_api == 'koboldhorde' ? 'kobold' : main_api; | ||||
|     } | ||||
|  | ||||
|     if (!Object.keys(presetManagers).includes(apiId)) { | ||||
|         return null; | ||||
| @@ -289,9 +291,11 @@ jQuery(async () => { | ||||
|     eventSource.on(event_types.CHAT_CHANGED, autoSelectPreset); | ||||
|     registerPresetManagers(); | ||||
|     $(document).on("click", "[data-preset-manager-update]", async function () { | ||||
|         const presetManager = getPresetManager(); | ||||
|         const apiId = $(this).data("preset-manager-update"); | ||||
|         const presetManager = getPresetManager(apiId); | ||||
|  | ||||
|         if (!presetManager) { | ||||
|             console.warn(`Preset Manager not found for API: ${apiId}`); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -299,9 +303,11 @@ jQuery(async () => { | ||||
|     }); | ||||
|  | ||||
|     $(document).on("click", "[data-preset-manager-new]", async function () { | ||||
|         const presetManager = getPresetManager(); | ||||
|         const apiId = $(this).data("preset-manager-new"); | ||||
|         const presetManager = getPresetManager(apiId); | ||||
|  | ||||
|         if (!presetManager) { | ||||
|             console.warn(`Preset Manager not found for API: ${apiId}`); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -309,9 +315,11 @@ jQuery(async () => { | ||||
|     }); | ||||
|  | ||||
|     $(document).on("click", "[data-preset-manager-export]", async function () { | ||||
|         const presetManager = getPresetManager(); | ||||
|         const apiId = $(this).data("preset-manager-export"); | ||||
|         const presetManager = getPresetManager(apiId); | ||||
|  | ||||
|         if (!presetManager) { | ||||
|             console.warn(`Preset Manager not found for API: ${apiId}`); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -348,9 +356,11 @@ jQuery(async () => { | ||||
|     }); | ||||
|  | ||||
|     $(document).on("click", "[data-preset-manager-delete]", async function () { | ||||
|         const presetManager = getPresetManager(); | ||||
|         const apiId = $(this).data("preset-manager-delete"); | ||||
|         const presetManager = getPresetManager(apiId); | ||||
|  | ||||
|         if (!presetManager) { | ||||
|             console.warn(`Preset Manager not found for API: ${apiId}`); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user