Add at-depth position for custom Prompt Manager prompts

This commit is contained in:
Cohee 2023-10-11 16:03:36 +03:00
parent 6b5aa9d06e
commit abb78d1d6b
3 changed files with 117 additions and 37 deletions

View File

@ -4318,23 +4318,44 @@
<h3>Edit</h3>
<div class="completion_prompt_manager_popup_entry">
<form class="completion_prompt_manager_popup_entry_form">
<div class="completion_prompt_manager_popup_entry_form_control">
<label for="completion_prompt_manager_popup_entry_form_name">
<span>Name</span>
</label>
<div class="text_muted">A name for this prompt.</div>
<input id="completion_prompt_manager_popup_entry_form_name" class="text_pole" type="text" name="name" />
<div class="flex-container gap10px">
<div class="completion_prompt_manager_popup_entry_form_control flex1">
<label for="completion_prompt_manager_popup_entry_form_name">
<span>Name</span>
</label>
<div class="text_muted">A name for this prompt.</div>
<input id="completion_prompt_manager_popup_entry_form_name" class="text_pole" type="text" name="name" />
</div>
<div class="completion_prompt_manager_popup_entry_form_control flex1">
<label for="completion_prompt_manager_popup_entry_form_role">
<span>Role</span>
</label>
<div class="text_muted">To whom this message will be attributed.</div>
<select id="completion_prompt_manager_popup_entry_form_role" class="text_pole" name="role">
<option value="system">System</option>
<option value="user">User</option>
<option value="assistant">AI Assistant</option>
</select>
</div>
</div>
<div class="completion_prompt_manager_popup_entry_form_control">
<label for="completion_prompt_manager_popup_entry_form_role">
<span>Role</span>
</label>
<div class="text_muted">To whom this message will be attributed.</div>
<select id="completion_prompt_manager_popup_entry_form_role" class="text_pole" name="role">
<option value="system">System</option>
<option value="user">User</option>
<option value="assistant">AI Assistant</option>
</select>
<div class="flex-container gap10px">
<div class="completion_prompt_manager_popup_entry_form_control flex1">
<label for="completion_prompt_manager_popup_entry_form_injection_position">
<span>Position</span>
</label>
<div class="text_muted">Injection position. Next to other prompts (relative) or in-chat (absolute).</div>
<select id="completion_prompt_manager_popup_entry_form_injection_position" class="text_pole" name="injection_position">
<option value="0">Relative</option>
<option value="1">Absolute</option>
</select>
</div>
<div class="completion_prompt_manager_popup_entry_form_control flex1">
<label for="completion_prompt_manager_popup_entry_form_injection_depth">
<span>Depth</span>
</label>
<div class="text_muted">Injection depth. 0 = after the last message, 1 = before the last message, etc.</div>
<input id="completion_prompt_manager_popup_entry_form_injection_depth" class="text_pole" type="number" name="injection_depth" min="0" max="999" value="4" />
</div>
</div>
<div class="completion_prompt_manager_popup_entry_form_control">
<label for="completion_prompt_manager_popup_entry_form_prompt">

View File

@ -21,6 +21,16 @@ function debouncePromise(func, delay) {
};
}
const DEFAULT_DEPTH = 4;
/**
* @enum {number}
*/
export const INJECTION_POSITION ={
RELATIVE: 0,
ABSOLUTE: 1,
}
/**
* Register migrations for the prompt manager when settings are loaded or an Open AI preset is loaded.
*/
@ -60,7 +70,7 @@ const registerPromptManagerMigration = () => {
* Represents a prompt.
*/
class Prompt {
identifier; role; content; name; system_prompt; position;
identifier; role; content; name; system_prompt; position; injection_position; injection_depth;
/**
* Create a new Prompt instance.
@ -72,14 +82,18 @@ class 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 {number} param0.injection_position - The insert position of the prompt.
* @param {number} param0.injection_depth - The depth of the prompt in the chat.
*/
constructor({ identifier, role, content, name, system_prompt, position } = {}) {
constructor({ identifier, role, content, name, system_prompt, position, injection_depth, injection_position } = {}) {
this.identifier = identifier;
this.role = role;
this.content = content;
this.name = name;
this.system_prompt = system_prompt;
this.position = position;
this.injection_depth = injection_depth;
this.injection_position = injection_position;
}
}
@ -381,6 +395,8 @@ PromptManagerModule.prototype.init = function (moduleConfiguration, serviceSetti
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;
}
// Append prompt to selected character
@ -673,6 +689,8 @@ PromptManagerModule.prototype.updatePromptWithPromptEditForm = function (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);
}
/**
@ -1085,10 +1103,14 @@ PromptManagerModule.prototype.loadPromptIntoEditForm = function (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');
nameField.value = prompt.name ?? '';
roleField.value = prompt.role ?? '';
promptField.value = prompt.content ?? '';
injectionPositionField.value = prompt.injection_position ?? INJECTION_POSITION.RELATIVE;
injectionDepthField.value = prompt.injection_depth ?? DEFAULT_DEPTH;
const resetPromptButton = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_reset');
if (true === prompt.system_prompt) {
@ -1152,10 +1174,14 @@ PromptManagerModule.prototype.clearEditForm = function () {
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');
nameField.value = '';
roleField.selectedIndex = 0;
promptField.value = '';
injectionPositionField.selectedIndex = 0;
injectionDepthField.value = DEFAULT_DEPTH;
roleField.disabled = false;
}
@ -1435,13 +1461,18 @@ PromptManagerModule.prototype.renderPromptManagerListItems = function () {
}
const encodedName = escapeHtml(prompt.name);
const isSystemPrompt = !prompt.marker && prompt.system_prompt && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE;
const isUserPrompt = !prompt.marker && !prompt.system_prompt && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE;
const isInjectionPrompt = !prompt.marker && prompt.injection_position === INJECTION_POSITION.ABSOLUTE;
listItemHtml += `
<li class="${prefix}prompt_manager_prompt ${draggableClass} ${enabledClass} ${markerClass}" data-pm-identifier="${prompt.identifier}">
<span class="${prefix}prompt_manager_prompt_name" data-pm-name="${encodedName}">
${prompt.marker ? '<span class="fa-solid fa-thumb-tack" title="Marker"></span>' : ''}
${!prompt.marker && prompt.system_prompt ? '<span class="fa-solid fa-square-poll-horizontal" title="Global Prompt"></span>' : ''}
${!prompt.marker && !prompt.system_prompt ? '<span class="fa-solid fa-user" title="User Prompt"></span>' : ''}
${isSystemPrompt ? '<span class="fa-solid fa-square-poll-horizontal" title="Global Prompt"></span>' : ''}
${isUserPrompt ? '<span class="fa-solid fa-user" title="User Prompt"></span>' : ''}
${isInjectionPrompt ? `<span class="fa-solid fa-syringe" title="In-Chat Injection"></span>` : ''}
${this.isPromptInspectionAllowed(prompt) ? `<a class="prompt-manager-inspect-action">${encodedName}</a>` : encodedName}
${isInjectionPrompt ? `<small class="prompt-manager-injection-depth">@ ${prompt.injection_depth}</small>` : ''}
</span>
<span>
<span class="prompt_manager_prompt_controls">

View File

@ -31,7 +31,8 @@ import { groups, selected_group } from "./group-chats.js";
import {
promptManagerDefaultPromptOrders,
chatCompletionDefaultPrompts, Prompt,
PromptManagerModule as PromptManager
PromptManagerModule as PromptManager,
INJECTION_POSITION,
} from "./PromptManager.js";
import {
@ -321,15 +322,6 @@ function setOpenAIMessages(chat) {
openai_msgs[i] = { "role": role, "content": content, name: name };
j++;
}
// Add chat injections, 100 = maximum depth of injection. (Why would you ever need more?)
for (let i = MAX_INJECTION_DEPTH; i >= 0; i--) {
const anchor = getExtensionPrompt(extension_prompt_types.IN_CHAT, i);
if (anchor && anchor.length) {
openai_msgs.splice(i, 0, { "role": 'system', 'content': anchor.trim() });
}
}
}
function setOpenAIMessageExamples(mesExamplesArray) {
@ -468,6 +460,34 @@ function formatWorldInfo(value) {
return stringFormat(oai_settings.wi_format, value);
}
/**
* This function populates the injections in the conversation.
*
* @param {Prompt[]} prompts - Array containing injection prompts.
*/
function populationInjectionPrompts(prompts) {
for (let i = MAX_INJECTION_DEPTH; i >= 0; i--) {
// Get prompts for current depth
const depthPrompts = prompts.filter(prompt => prompt.injection_depth === i && prompt.content);
// Order of priority (most important go lower)
const roles = ['system', 'user', 'assistant'];
for (const role of roles) {
// Get prompts for current role
const rolePrompts = depthPrompts.filter(prompt => prompt.role === role).map(x => x.content).join('\n');
// Get extension prompt (only for system role)
const extensionPrompt = role === 'system' ? getExtensionPrompt(extension_prompt_types.IN_CHAT, i) : '';
const jointPrompt = [rolePrompts, extensionPrompt].filter(x => x).map(x => x.trim()).join('\n');
if (jointPrompt && jointPrompt.length) {
openai_msgs.splice(i, 0, { "role": role, 'content': jointPrompt });
}
}
}
}
/**
* Populates the chat history of the conversation.
*
@ -477,7 +497,6 @@ function formatWorldInfo(value) {
* @param cyclePrompt
*/
function populateChatHistory(prompts, chatCompletion, type = null, cyclePrompt = null) {
// Chat History
chatCompletion.add(new MessageCollection('chatHistory'), prompts.index('chatHistory'));
let names = (selected_group && groups.find(x => x.id === selected_group)?.members.map(member => characters.find(c => c.avatar === member)?.name).filter(Boolean).join(', ')) || '';
@ -646,14 +665,20 @@ function populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, ty
// Add ordered system and user prompts
const systemPrompts = ['nsfw', 'jailbreak'];
const userPrompts = prompts.collection
.filter((prompt) => false === prompt.system_prompt)
const userRelativePrompts = prompts.collection
.filter((prompt) => false === prompt.system_prompt && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE)
.reduce((acc, prompt) => {
acc.push(prompt.identifier)
return acc;
}, []);
const userAbsolutePrompts = prompts.collection
.filter((prompt) => false === prompt.system_prompt && prompt.injection_position === INJECTION_POSITION.ABSOLUTE)
.reduce((acc, prompt) => {
acc.push(prompt)
return acc;
}, []);
[...systemPrompts, ...userPrompts].forEach(identifier => addToChatCompletion(identifier));
[...systemPrompts, ...userRelativePrompts].forEach(identifier => addToChatCompletion(identifier));
// Add enhance definition instruction
if (prompts.has('enhanceDefinitions')) addToChatCompletion('enhanceDefinitions');
@ -697,6 +722,9 @@ function populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, ty
}
}
// Add in-chat injections
populationInjectionPrompts(userAbsolutePrompts);
// Decide whether dialogue examples should always be added
if (power_user.pin_examples) {
populateDialogueExamples(prompts, chatCompletion);
@ -1714,7 +1742,7 @@ class ChatCompletion {
*
* @param {Message} message - The message to insert.
* @param {string} identifier - The identifier of the collection where to insert the message.
* @param {string} position - The position at which to insert the message ('start' or 'end').
* @param {string|number} position - The position at which to insert the message ('start' or 'end').
*/
insert(message, identifier, position = 'end') {
this.validateMessage(message);
@ -1723,7 +1751,8 @@ class ChatCompletion {
const index = this.findMessageIndex(identifier);
if (message.content) {
if ('start' === position) this.messages.collection[index].collection.unshift(message);
else if ('end' === position) this.messages.collection[index].collection.push(message);
else if ('end' === position) this.messages.collection[index].collection.push(message)
else if (typeof position === 'number') this.messages.collection[index].collection.splice(position, 0, message);
this.decreaseTokenBudgetBy(message.getTokens());
@ -1731,7 +1760,6 @@ class ChatCompletion {
}
}
/**
* Remove the last item of the collection
*