diff --git a/public/css/promptmanager.css b/public/css/promptmanager.css index 501094d0a..485cf3a01 100644 --- a/public/css/promptmanager.css +++ b/public/css/promptmanager.css @@ -112,6 +112,10 @@ flex-direction: column; } +.completion_prompt_manager_popup_entry_form .select2-container { + margin: 5px 0; +} + #completion_prompt_manager_popup .completion_prompt_manager_popup_entry_form_control:has(#completion_prompt_manager_popup_entry_form_prompt) { flex: 1; display: flex; diff --git a/public/index.html b/public/index.html index 882512189..5983321f3 100644 --- a/public/index.html +++ b/public/index.html @@ -6720,6 +6720,22 @@
To whom this message will be attributed.
+
+ + +
+ Filter to specific generation types. +
+
diff --git a/public/script.js b/public/script.js index 3a89646a9..ab595274b 100644 --- a/public/script.js +++ b/public/script.js @@ -3540,7 +3540,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro 'impersonate', 'quiet', 'continue', - 'ask_command', ]; //for normal messages sent from user.. if ((textareaText != '' || (hasPendingFileAttachment() && !noAttachTypes.includes(type))) && !automatic_trigger && type !== 'quiet' && !dryRun) { @@ -4380,7 +4379,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro cyclePrompt: cyclePrompt, systemPromptOverride: system, jailbreakPromptOverride: jailbreak, - personaDescription: persona, messages: oaiMessages, messageExamples: oaiMessageExamples, }, dryRun); diff --git a/public/scripts/PromptManager.js b/public/scripts/PromptManager.js index 486989586..bcb59d148 100644 --- a/public/scripts/PromptManager.js +++ b/public/scripts/PromptManager.js @@ -6,7 +6,7 @@ import { event_types, eventSource, is_send_press, main_api, substituteParams } f import { is_group_generating } from './group-chats.js'; import { Message, MessageCollection, TokenHandler } from './openai.js'; import { power_user } from './power-user.js'; -import { debounce, waitUntilCondition, escapeHtml } from './utils.js'; +import { debounce, waitUntilCondition, escapeHtml, uuidv4 } from './utils.js'; import { debounce_timeout } from './constants.js'; import { renderTemplateAsync } from './templates.js'; import { Popup } from './popup.js'; @@ -29,6 +29,7 @@ function debouncePromise(func, delay) { } const DEFAULT_DEPTH = 4; +const DEFAULT_ORDER = 100; /** * @enum {number} @@ -77,25 +78,108 @@ const registerPromptManagerMigration = () => { * Represents a prompt. */ class Prompt { - identifier; role; content; name; system_prompt; position; injection_position; injection_depth; injection_order; forbid_overrides; extension; + /** + * Indicates if the prompt is enabled. + * @type {boolean} + */ + enabled; + + /** + * Unique identifier for the prompt. + * @type {string} + */ + identifier; + + /** + * Role of the prompt, e.g., 'system', 'user', etc. + * @type {string} + */ + role; + + /** + * Content of the prompt. + * @type {string} + */ + content; + + /** + * Display name of the prompt. + * @type {string} + */ + name; + + /** + * Indicates if the prompt is a system prompt. + * @type {boolean} + */ + system_prompt; + + /** + * Position of the prompt in the prompt list. + * @type {string|number} + */ + position; + + /** + * Inject position of the prompt (relative = 0 or in-chat = 1) + * @type {number} + */ + injection_position; + + /** + * Depth of the prompt in the chat. + * @type {number} + */ + injection_depth; + + /** + * Order of the prompt in the chat. + * @type {number} + */ + injection_order; + + /** + * Indicates if the prompt should not be overridden. + * @type {boolean} + */ + forbid_overrides; + + /** + * Prompt is added by an extension. + * @type {boolean} + */ + extension; + + /** + * A list of generation type triggers for the prompt injection. + * @type {string[]} + */ + injection_trigger; + + /** + * Indicates if the prompt is a marker prompt. + * @type {boolean} + */ + marker; /** * Create a new Prompt instance. * - * @param {Object} param0 - Object containing the properties of the prompt. - * @param {string} param0.identifier - The unique identifier of the prompt. - * @param {string} param0.role - The role associated with the prompt. - * @param {string} param0.content - The content of the prompt. + * @param {Object} [param0] - Object containing the properties of the prompt. + * @param {string} [param0.identifier] - The unique identifier of the prompt. + * @param {string} [param0.role] - The role associated with the prompt. + * @param {string} [param0.content] - The content of the prompt. * @param {string} [param0.name] - The name of the prompt. * @param {boolean} [param0.system_prompt] - Indicates if the prompt is a system prompt. - * @param {string} [param0.position] - The position of the prompt in the prompt list. + * @param {string|number} [param0.position] - The position of the prompt in the prompt list. * @param {number} [param0.injection_position] - The insert position of the prompt. * @param {number} [param0.injection_depth] - The depth of the prompt in the chat. * @param {number} [param0.injection_order] - The order of the prompt in the chat. + * @param {string[]} [param0.injection_trigger] - The generation type trigger for the prompt injection. * @param {boolean} [param0.forbid_overrides] - Indicates if the prompt should not be overridden. * @param {boolean} [param0.extension] - Prompt is added by an extension. */ - constructor({ identifier, role, content, name, system_prompt, position, injection_depth, injection_position, forbid_overrides, extension, injection_order } = {}) { + constructor({ identifier, role, content, name, system_prompt, position, injection_depth, injection_position, forbid_overrides, extension, injection_order, injection_trigger } = {}) { this.identifier = identifier; this.role = role; this.content = content; @@ -106,7 +190,8 @@ class Prompt { this.injection_position = injection_position; this.forbid_overrides = forbid_overrides; this.extension = extension ?? false; - this.injection_order = injection_order ?? 100; + this.injection_order = injection_order ?? DEFAULT_ORDER; + this.injection_trigger = injection_trigger ?? []; } } @@ -114,7 +199,16 @@ class Prompt { * Representing a collection of prompts. */ export class PromptCollection { + /** + * List of Prompts in the collection. + * @type {Prompt[]} + */ collection = []; + + /** + * List of identifiers of prompts that have been overridden. + * @type {string[]} + */ overriddenPrompts = []; /** @@ -129,7 +223,7 @@ export class PromptCollection { /** * Checks if the provided instances are of the Prompt class. * - * @param {...any} prompts - Instances to check. + * @param {...Prompt} prompts - Instances to check. * @throws Will throw an error if one or more instances are not of the Prompt class. */ checkPromptInstance(...prompts) { @@ -191,6 +285,12 @@ export class PromptCollection { return this.index(identifier) !== -1; } + /** + * Overrides a prompt at a specific position in the collection. + * + * @param {Prompt} prompt - The Prompt instance to override. + * @param {number} position - The position in the collection to override the Prompt instance. + */ override(prompt, position) { this.set(prompt, position); this.overriddenPrompts.push(prompt.identifier); @@ -274,7 +374,7 @@ class PromptManager { this.tryGenerate = async () => { }; /** Called to persist the configuration, must return a promise */ - this.saveServiceSettings = () => { }; + this.saveServiceSettings = () => { return Promise.resolve(); }; /** Toggle prompt button click */ this.handleToggle = () => { }; @@ -444,32 +544,51 @@ class PromptManager { break; } - document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_name').value = prompt.name; - document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_role').value = 'system'; - document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt').value = prompt.content ?? ''; - document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position').value = prompt.injection_position ?? 0; - document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth').value = prompt.injection_depth ?? DEFAULT_DEPTH; - document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_order').value = prompt.injection_order ?? 100; - document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block').style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden'; - document.getElementById(this.configuration.prefix + 'prompt_manager_order_block').style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden'; - document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides').checked = prompt.forbid_overrides ?? false; - document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block').style.visibility = this.overridablePrompts.includes(prompt.identifier) ? 'visible' : 'hidden'; - document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt').disabled = prompt.marker ?? false; - document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source_block').style.display = isPulledPrompt ? '' : 'none'; + const nameField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_name')); + const roleField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_role')); + const promptField = /** @type {HTMLTextAreaElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt')); + const injectionPositionField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position')); + const injectionDepthField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth')); + const injectionOrderField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_order')); + const injectionTriggerField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_trigger')); + const depthBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block')); + const orderBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_order_block')); + const forbidOverridesField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides')); + const forbidOverridesBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block')); + const entrySourceBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source_block')); + const entrySource = /** @type {HTMLSpanElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source')); + + nameField.value = prompt.name; + roleField.value = 'system'; + promptField.value = prompt.content ?? ''; + injectionPositionField.value = (prompt.injection_position ?? 0).toString(); + injectionDepthField.value = (prompt.injection_depth ?? DEFAULT_DEPTH).toString(); + injectionOrderField.value = (prompt.injection_order ?? DEFAULT_ORDER).toString(); + Array.from(injectionTriggerField.options).forEach(option => { + option.selected = false; + }); + injectionTriggerField.dispatchEvent(new Event('change', { bubbles: true })); + depthBlock.style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden'; + orderBlock.style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden'; + forbidOverridesField.checked = prompt.forbid_overrides ?? false; + forbidOverridesBlock.style.visibility = this.overridablePrompts.includes(prompt.identifier) ? 'visible' : 'hidden'; + promptField.disabled = prompt.marker ?? false; + entrySourceBlock.style.display = isPulledPrompt ? '' : 'none'; if (isPulledPrompt) { const sourceName = this.promptSources[promptId]; - document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source').textContent = sourceName; + entrySource.textContent = sourceName; } if (!this.systemPrompts.includes(promptId)) { - document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position').removeAttribute('disabled'); + injectionPositionField.removeAttribute('disabled'); } }; // Append prompt to selected character this.handleAppendPrompt = (event) => { - const promptID = document.getElementById(this.configuration.prefix + 'prompt_manager_footer_append_prompt').value; + const appendPromptFooter = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_footer_append_prompt')); + const promptID = appendPromptFooter.value; const prompt = this.getPromptById(promptID); if (prompt) { @@ -483,7 +602,8 @@ class PromptManager { this.handleDeletePrompt = async (event) => { Popup.show.confirm(t`Are you sure you want to delete this prompt?`, null).then((userChoice) => { if (!userChoice) return; - const promptID = document.getElementById(this.configuration.prefix + 'prompt_manager_footer_append_prompt').value; + const appendPromptFooter = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_footer_append_prompt')); + const promptID = appendPromptFooter.value; const prompt = this.getPromptById(promptID); if (prompt && true === this.isPromptDeletionAllowed(prompt)) { @@ -566,6 +686,7 @@ class PromptManager { fileOpener.accept = '.json'; fileOpener.addEventListener('change', (event) => { + if (!(event.target instanceof HTMLInputElement)) return; const file = event.target.files[0]; if (!file) return; @@ -575,7 +696,7 @@ class PromptManager { const fileContent = event.target.result; try { - const data = JSON.parse(fileContent); + const data = JSON.parse(fileContent.toString()); this.import(data); } catch (err) { toastr.error(t`An error occurred while importing prompts. More info available in console.`); @@ -615,7 +736,7 @@ class PromptManager { // Update edit form if present // @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent - const popupEditFormPrompt = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt'); + const popupEditFormPrompt = /** @type {HTMLTextAreaElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt')); if (popupEditFormPrompt.offsetParent) { popupEditFormPrompt.value = prompt.content; } @@ -673,6 +794,7 @@ class PromptManager { // Trigger re-render when token settings are changed document.getElementById('openai_max_context').addEventListener('change', (event) => { + if (!(event.target instanceof HTMLInputElement)) return; this.serviceSettings.openai_max_context = event.target.value; if (this.activeCharacter) this.renderDebounced(); }); @@ -778,23 +900,33 @@ class PromptManager { /** * Update a prompt with the values from the HTML form. - * @param {object} prompt - The prompt to be updated. + * @param {Partial} prompt - The prompt to be updated. * @returns {void} */ updatePromptWithPromptEditForm(prompt) { - prompt.name = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_name').value; - prompt.role = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_role').value; - prompt.content = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt').value; - prompt.injection_position = Number(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position').value); - prompt.injection_depth = Number(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth').value); - prompt.injection_order = Number(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_order').value); - prompt.forbid_overrides = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides').checked; + const nameField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_name')); + const roleField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_role')); + const promptField = /** @type {HTMLTextAreaElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt')); + const injectionPositionField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position')); + const injectionDepthField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth')); + const injectionOrderField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_order')); + const injectionTriggerField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_trigger')); + const forbidOverridesField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides')); + + prompt.name = nameField.value; + prompt.role = roleField.value; + prompt.content = promptField.value; + prompt.injection_position = Number(injectionPositionField.value); + prompt.injection_depth = Number(injectionDepthField.value); + prompt.injection_order = Number(injectionOrderField.value); + prompt.injection_trigger = Array.from(injectionTriggerField.selectedOptions).map(option => option.value); + prompt.forbid_overrides = forbidOverridesField.checked; } /** * Find a prompt by its identifier and update it with the provided object. * @param {string} identifier - The identifier of the prompt. - * @param {object} updatePrompt - An object with properties to be updated in the prompt. + * @param {Prompt} updatePrompt - An object with properties to be updated in the prompt. * @returns {void} */ updatePromptByIdentifier(identifier, updatePrompt) { @@ -804,7 +936,7 @@ class PromptManager { /** * Iterate over an array of prompts, find each one by its identifier, and update them with the provided data. - * @param {object[]} prompts - An array of prompt updates. + * @param {Prompt[]} prompts - An array of prompt updates. * @returns {void} */ updatePrompts(prompts) { @@ -826,7 +958,7 @@ class PromptManager { /** * Add a prompt to the current character's prompt list. - * @param {object} prompt - The prompt to be added. + * @param {Prompt} prompt - The prompt to be added. * @param {object} character - The character whose prompt list will be updated. * @returns {void} */ @@ -839,7 +971,7 @@ class PromptManager { /** * Remove a prompt from the current character's prompt list. - * @param {object} prompt - The prompt to be removed. + * @param {Prompt} prompt - The prompt to be removed. * @param {object} character - The character whose prompt list will be updated. * @returns {void} */ @@ -853,7 +985,7 @@ class PromptManager { /** * Create a new prompt and add it to the list of prompts. - * @param {object} prompt - The prompt to be added. + * @param {Partial} prompt - The prompt to be added. * @param {string} identifier - The identifier for the new prompt. * @returns {void} */ @@ -931,7 +1063,7 @@ class PromptManager { /** * Check whether a prompt can be inspected. - * @param {object} prompt - The prompt to check. + * @param {Prompt} prompt - The prompt to check. * @returns {boolean} True if the prompt is a marker, false otherwise. */ isPromptInspectionAllowed(prompt) { @@ -940,7 +1072,7 @@ class PromptManager { /** * Check whether a prompt can be deleted. System prompts cannot be deleted. - * @param {object} prompt - The prompt to check. + * @param {Prompt} prompt - The prompt to check. * @returns {boolean} True if the prompt can be deleted, false otherwise. */ isPromptDeletionAllowed(prompt) { @@ -949,7 +1081,7 @@ class PromptManager { /** * Check whether a prompt can be edited. - * @param {object} prompt - The prompt to check. + * @param {Prompt} prompt - The prompt to check. * @returns {boolean} True if the prompt can be edited, false otherwise. */ isPromptEditAllowed(prompt) { @@ -966,7 +1098,7 @@ class PromptManager { /** * Check whether a prompt can be toggled on or off. - * @param {object} prompt - The prompt to check. + * @param {Prompt} prompt - The prompt to check. * @returns {boolean} True if the prompt can be deleted, false otherwise. */ isPromptToggleAllowed(prompt) { @@ -1062,7 +1194,7 @@ class PromptManager { /** * Get the prompts for a specific character. Can be filtered to only include enabled prompts. - * @returns {object[]} The prompts for the character. + * @returns {Prompt[]} The prompts for the character. * @param character * @param onlyEnabled */ @@ -1075,7 +1207,7 @@ class PromptManager { /** * Get the order of prompts for a specific character. If no character is specified or the character doesn't have a prompt list, an empty array is returned. * @param {object|null} character - The character to get the prompt list for. - * @returns {object[]} The prompt list for the character, or an empty array. + * @returns {Partial[]} The prompt list for the character, or an empty array. */ getPromptOrderForCharacter(character) { return !character ? [] : (this.serviceSettings.prompt_order.find(list => String(list.character_id) === String(character.id))?.order ?? []); @@ -1083,7 +1215,7 @@ class PromptManager { /** * Set the prompts for the manager. - * @param {object[]} prompts - The prompts to be set. + * @param {Partial[]} prompts - The prompts to be set. * @returns {void} */ setPrompts(prompts) { @@ -1125,7 +1257,7 @@ class PromptManager { /** * Finds and returns a prompt by its identifier. * @param {string} identifier - Identifier of the prompt - * @returns {Object|null} The prompt object, or null if not found + * @returns {Prompt|null} The prompt object, or null if not found */ getPromptById(identifier) { return this.serviceSettings.prompts.find(item => item && item.identifier === identifier) ?? null; @@ -1143,9 +1275,9 @@ class PromptManager { /** * Enriches a generic object, creating a new prompt object in the process * - * @param {Object} prompt - Prompt object + * @param {Partial} prompt - Prompt object * @param original - * @returns {Object} An object with "role" and "content" properties + * @returns {Prompt} An object with "role" and "content" properties */ preparePrompt(prompt, original = null) { const groupMembers = this.getActiveGroupCharacters(); @@ -1184,7 +1316,7 @@ class PromptManager { const debouncedSaveServiceSettings = debouncePromise(() => this.saveServiceSettings(), 300); - const textarea = document.getElementById(textareaIdentifier); + const textarea = /** @type {HTMLTextAreaElement} */(document.getElementById(textareaIdentifier)); textarea.addEventListener('blur', () => { prompt.content = textarea.value; this.updatePromptByIdentifier(identifier, prompt); @@ -1193,9 +1325,15 @@ class PromptManager { } + /** + * Updates the quick edit textarea for a specific prompt. + * @param {string} identifier - The identifier of the prompt. + * @param {Prompt} prompt - The updated prompt object. + * @returns {string} The ID of the updated textarea element. + */ updateQuickEdit(identifier, prompt) { const elementId = `${identifier}_prompt_quick_edit_textarea`; - const textarea = document.getElementById(elementId); + const textarea = /** @type {HTMLTextAreaElement} */(document.getElementById(elementId)); textarea.value = prompt.content; return elementId; @@ -1220,30 +1358,35 @@ class PromptManager { /** * Loads a given prompt into the edit form fields. - * @param {Object} prompt - Prompt object with properties 'name', 'role', 'content', and 'system_prompt' + * @param {Partial} prompt - Prompt object with properties 'name', 'role', 'content', and 'system_prompt' */ loadPromptIntoEditForm(prompt) { - const nameField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_name'); - const roleField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_role'); - const promptField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt'); - const injectionPositionField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position'); - const injectionDepthField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth'); - const injectionOrderField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_order'); - const injectionDepthBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block'); - const injectionOrderBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_order_block'); - const forbidOverridesField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides'); - const forbidOverridesBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block'); - const entrySourceBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source_block'); - const entrySource = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source'); + const nameField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_name')); + const roleField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_role')); + const promptField = /** @type {HTMLTextAreaElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt')); + const injectionPositionField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position')); + const injectionDepthField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth')); + const injectionOrderField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_order')); + const injectionTriggerField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_trigger')); + const injectionDepthBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block')); + const injectionOrderBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_order_block')); + const forbidOverridesField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides')); + const forbidOverridesBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block')); + const entrySourceBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source_block')); + const entrySource = /** @type {HTMLSpanElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source')); const isPulledPrompt = Object.keys(this.promptSources).includes(prompt.identifier); nameField.value = prompt.name ?? ''; roleField.value = prompt.role || 'system'; promptField.value = prompt.content ?? ''; promptField.disabled = prompt.marker ?? false; - injectionPositionField.value = prompt.injection_position ?? INJECTION_POSITION.RELATIVE; - injectionDepthField.value = prompt.injection_depth ?? DEFAULT_DEPTH; - injectionOrderField.value = prompt.injection_order ?? 100; + injectionPositionField.value = (prompt.injection_position ?? INJECTION_POSITION.RELATIVE).toString(); + injectionDepthField.value = (prompt.injection_depth ?? DEFAULT_DEPTH).toString(); + injectionOrderField.value = (prompt.injection_order ?? DEFAULT_ORDER).toString(); + Array.from(injectionTriggerField.options).forEach(option => { + option.selected = Array.isArray(prompt.injection_trigger) && prompt.injection_trigger.includes(option.value); + }); + injectionTriggerField.dispatchEvent(new Event('change', { bubbles: true })); injectionDepthBlock.style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden'; injectionOrderBlock.style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden'; injectionPositionField.removeAttribute('disabled'); @@ -1335,17 +1478,19 @@ class PromptManager { const editArea = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_edit'); editArea.style.display = 'none'; - const nameField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_name'); - const roleField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_role'); - const promptField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt'); - const injectionPositionField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position'); - const injectionDepthField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth'); - const injectionDepthBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block'); - const injectionOrderBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_order_block'); - const forbidOverridesField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides'); - const forbidOverridesBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block'); - const entrySourceBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source_block'); - const entrySource = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source'); + const nameField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_name')); + const roleField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_role')); + const promptField = /** @type {HTMLTextAreaElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt')); + const injectionPositionField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position')); + const injectionDepthField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth')); + const injectionDepthBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block')); + const injectionOrderBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_order_block')); + const injectionOrderField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_order')); + const injectionTriggerField = /** @type {HTMLSelectElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_trigger')); + const forbidOverridesField = /** @type {HTMLInputElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides')); + const forbidOverridesBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block')); + const entrySourceBlock = /** @type {HTMLDivElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source_block')); + const entrySource = /** @type {HTMLSpanElement} */(document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_source')); nameField.value = ''; roleField.selectedIndex = 0; @@ -1353,7 +1498,9 @@ class PromptManager { promptField.disabled = false; injectionPositionField.selectedIndex = 0; injectionPositionField.removeAttribute('disabled'); - injectionDepthField.value = DEFAULT_DEPTH; + injectionDepthField.value = DEFAULT_DEPTH.toString(); + injectionOrderField.value = DEFAULT_ORDER.toString(); + injectionTriggerField.value = ''; injectionDepthBlock.style.visibility = 'unset'; injectionOrderBlock.style.visibility = 'unset'; forbidOverridesBlock.style.visibility = 'unset'; @@ -1373,28 +1520,48 @@ class PromptManager { /** * Returns a full list of prompts whose content markers have been substituted. + * @param {string} generationType - The type of generation, e.g., 'continue' or 'quiet'. * @returns {PromptCollection} A PromptCollection object */ - getPromptCollection() { + getPromptCollection(generationType) { + generationType = String(generationType || 'normal').toLowerCase().trim(); + const promptCollection = new PromptCollection(); const promptOrder = this.getPromptOrderForCharacter(this.activeCharacter); - const promptCollection = new PromptCollection(); promptOrder.forEach(entry => { - if (true === entry.enabled) { - const prompt = this.getPromptById(entry.identifier); - if (prompt) promptCollection.add(this.preparePrompt(prompt)); - } else if (!entry.enabled && entry.identifier === 'main') { + const prompt = this.getPromptById(entry.identifier); + const allowedTrigger = entry.enabled && this.shouldTrigger(prompt, generationType); + + if (!prompt) { + return; + } + + if (allowedTrigger) { + promptCollection.add(this.preparePrompt(prompt)); + } else if (entry.identifier === 'main') { // Some extensions require main prompt to be present for relative inserts. // So we make a GMO-free vegan replacement. - const prompt = structuredClone(this.getPromptById(entry.identifier)); - prompt.content = ''; - if (prompt) promptCollection.add(this.preparePrompt(prompt)); + const replacementPrompt = structuredClone(prompt); + replacementPrompt.content = ''; + promptCollection.add(this.preparePrompt(replacementPrompt)); } }); return promptCollection; } + /** + * Checks if a prompt should be triggered based on its injection triggers. + * @param {Prompt} prompt - The prompt to check. + * @param {string} generationType - The type of generation to check against. + * @returns {boolean} True if the prompt should be triggered, false otherwise. + */ + shouldTrigger(prompt, generationType) { + if (!Array.isArray(prompt?.injection_trigger)) return true; + if (!prompt.injection_trigger.length) return true; + return prompt.injection_trigger.includes(generationType); + } + /** * Setter for messages property * @@ -1807,11 +1974,7 @@ class PromptManager { * @returns {string} A string representation of an uuid4 */ getUuidv4() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { - let r = Math.random() * 16 | 0, - v = c === 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); + return uuidv4(); } /** diff --git a/public/scripts/group-chats.js b/public/scripts/group-chats.js index 91f0b11a7..bbfce4eaa 100644 --- a/public/scripts/group-chats.js +++ b/public/scripts/group-chats.js @@ -921,7 +921,6 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) { for (const chId of activatedMembers) { throwIfAborted(); deactivateSendButtons(); - const generateType = type == 'swipe' || type == 'impersonate' || type == 'quiet' || type == 'continue' ? type : 'group_chat'; setCharacterId(chId); setCharacterName(characters[chId].name); if (power_user.show_group_chat_queue) { @@ -930,6 +929,7 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) { await eventSource.emit(event_types.GROUP_MEMBER_DRAFTED, chId); // Wait for generation to finish + const generateType = ['swipe', 'impersonate', 'quiet', 'continue'].includes(type) ? type : 'normal'; textResult = await Generate(generateType, { automatic_trigger: by_auto_mode, ...(params || {}) }); let messageChunk = textResult?.messageChunk; diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 32a232a0b..d27d54140 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -1255,12 +1255,12 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm * @param {string} options.quietPrompt - The quiet prompt to be used in the conversation. * @param {string} options.bias - The bias to be added in the conversation. * @param {Object} options.extensionPrompts - An object containing additional prompts. - * @param {string} options.systemPromptOverride - * @param {string} options.jailbreakPromptOverride - * @param {string} options.personaDescription + * @param {string} options.systemPromptOverride - Character card override of the main prompt + * @param {string} options.jailbreakPromptOverride - Character card override of the PHI + * @param {string} options.type - The type of generation that triggered the prompt * @returns {Promise} prompts - The prepared and merged system and user-defined prompts. */ -async function preparePromptsForChatCompletion({ scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts, systemPromptOverride, jailbreakPromptOverride, personaDescription }) { +async function preparePromptsForChatCompletion({ scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts, systemPromptOverride, jailbreakPromptOverride, type }) { const scenarioText = scenario && oai_settings.scenario_format ? substituteParams(oai_settings.scenario_format) : (scenario || ''); const charPersonalityText = charPersonality && oai_settings.personality_format ? substituteParams(oai_settings.personality_format) : (charPersonality || ''); const groupNudge = substituteParams(oai_settings.group_nudge_prompt); @@ -1363,7 +1363,7 @@ async function preparePromptsForChatCompletion({ scenario, charPersonality, name } // This is the prompt order defined by the user - const prompts = promptManager.getPromptCollection(); + const prompts = promptManager.getPromptCollection(type); // Merge system prompts with prompt manager prompts systemPrompts.forEach(prompt => { @@ -1429,7 +1429,6 @@ async function preparePromptsForChatCompletion({ scenario, charPersonality, name * @param {string} content.cyclePrompt - The last prompt used for chat message continuation. * @param {string} content.systemPromptOverride - The system prompt override. * @param {string} content.jailbreakPromptOverride - The jailbreak prompt override. - * @param {string} content.personaDescription - The persona description. * @param {object} content.extensionPrompts - An array of additional prompts. * @param {object[]} content.messages - An array of messages to be used as chat history. * @param {string[]} content.messageExamples - An array of messages to be used as dialogue examples. @@ -1451,7 +1450,6 @@ export async function prepareOpenAIMessages({ cyclePrompt, systemPromptOverride, jailbreakPromptOverride, - personaDescription, messages, messageExamples, }, dryRun) { @@ -1478,7 +1476,7 @@ export async function prepareOpenAIMessages({ extensionPrompts, systemPromptOverride, jailbreakPromptOverride, - personaDescription, + type, }); // Fill the chat completion with as much context as the budget allows @@ -6429,6 +6427,11 @@ export function initOpenAI() { width: '100%', templateResult: getAimlapiModelTemplate, }); + $('#completion_prompt_manager_popup_entry_form_injection_trigger').select2({ + placeholder: t`All types (default)`, + width: '100%', + closeOnSelect: false, + }); } $('#openrouter_providers_chat').on('change', function () { diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index b805e5b5d..7029af034 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -3947,7 +3947,7 @@ async function askCharacter(args, text) { try { eventSource.once(event_types.MESSAGE_RECEIVED, restoreCharacter); toastr.info(`Asking ${name} something...`); - askResult = await Generate('ask_command'); + askResult = await Generate('normal'); } catch (error) { restoreCharacter(); console.error('Error running /ask command', error);