Merge branch 'SillyTavern:staging' into staging

This commit is contained in:
Tony Ribeiro 2023-08-24 01:43:10 +02:00 committed by GitHub
commit 7437b23fa2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1549 additions and 242 deletions

View File

@ -128,14 +128,14 @@
"max_context_unlocked": false, "max_context_unlocked": false,
"prefer_character_prompt": true, "prefer_character_prompt": true,
"prefer_character_jailbreak": true, "prefer_character_jailbreak": true,
"continue_on_send": false, "quick_continue": false,
"trim_spaces": true, "trim_spaces": true,
"relaxed_api_urls": false, "relaxed_api_urls": false,
"default_instruct": "", "default_instruct": "",
"instruct": { "instruct": {
"enabled": false, "enabled": false,
"preset": "🧙 Roleplay", "preset": "Roleplay",
"system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.\n\nAvoid repetition, don't loop. Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions.\n\n### Input:", "system_prompt": "Avoid repetition, don't loop. Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions.",
"input_sequence": "\n### Instruction:", "input_sequence": "\n### Instruction:",
"output_sequence": "\n### Response:", "output_sequence": "\n### Response:",
"last_output_sequence": "\n### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):", "last_output_sequence": "\n### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):",
@ -150,7 +150,7 @@
}, },
"context": { "context": {
"preset": "Default", "preset": "Default",
"story_string": "{{#if system}}{{system}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}", "story_string": "{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
"chat_start": "***", "chat_start": "***",
"example_separator": "***" "example_separator": "***"
}, },

View File

@ -1,6 +1,6 @@
{ {
"name": "Default", "name": "Default",
"story_string": "{{#if system}}{{system}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}", "story_string": "{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
"chat_start": "***", "chat_start": "***",
"example_separator": "***" "example_separator": "***"
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "Pygmalion", "name": "Pygmalion",
"story_string": "{{#if system}}{{system}}\n{{/if}}{{#if description}}{{{char}}}'s Persona: {{description}}\n{{/if}}{{#if personality}}Personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}", "story_string": "{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{{char}}}'s Persona: {{description}}\n{{/if}}{{#if personality}}Personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
"chat_start": "<START>", "chat_start": "<START>",
"example_separator": "<START>" "example_separator": "<START>"
} }

View File

@ -0,0 +1,6 @@
{
"name": "Roleplay",
"story_string": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.\n\n{{#if system}}{{system}}\n\n{{/if}}### Input:\n{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
"chat_start": "### New Roleplay:",
"example_separator": "### New Roleplay:"
}

View File

@ -1,6 +1,6 @@
{ {
"name": "simple-proxy-for-tavern", "name": "simple-proxy-for-tavern",
"story_string": "## {{char}}\n- You're \"{{char}}\" in this never-ending roleplay with \"{{user}}\".\n### Input:\n{{#if system}}{{system}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}### Response:\n(OOC) Understood. I will take this info into account for the roleplay. (end OOC)", "story_string": "## {{char}}\n- You're \"{{char}}\" in this never-ending roleplay with \"{{user}}\".\n### Input:\n{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}### Response:\n(OOC) Understood. I will take this info into account for the roleplay. (end OOC)",
"chat_start": "### New Roleplay:", "chat_start": "### New Roleplay:",
"example_separator": "### New Roleplay:" "example_separator": "### New Roleplay:"
} }

File diff suppressed because it is too large Load Diff

View File

@ -2816,10 +2816,10 @@
<input id="prefer_character_jailbreak" type="checkbox" /> <input id="prefer_character_jailbreak" type="checkbox" />
<span data-i18n="Prefer Character Card Jailbreak">Prefer Char. JB</span> <span data-i18n="Prefer Character Card Jailbreak">Prefer Char. JB</span>
</label> </label>
<label class="checkbox_label" for="continue_on_send"> <label class="checkbox_label" for="quick_continue">
<input id="continue_on_send" type="checkbox" /> <input id="quick_continue" type="checkbox" />
<span data-i18n="Press Send to continue"> <span data-i18n="Press Send to continue">
Press "Send" to continue Show quick "Continue" button
</span> </span>
</label> </label>
<label class="checkbox_label" for="auto-load-chat-checkbox"> <label class="checkbox_label" for="auto-load-chat-checkbox">
@ -4202,7 +4202,8 @@
<div id="mes_stop" title="Abort request" class="mes_stop" data-i18n="[title]Abort request"> <div id="mes_stop" title="Abort request" class="mes_stop" data-i18n="[title]Abort request">
<i class="fa-solid fa-circle-stop"></i> <i class="fa-solid fa-circle-stop"></i>
</div> </div>
<div id="send_but" class="fa-solid fa-paper-plane" title="Send a message" data-i18n="[title]Send a message"></div> <div id="mes_continue" class="fa-fw fa-solid fa-arrow-right displayNone" title="Continue the last message" data-i18n="[title]Continue the last message"></div>
<div id="send_but" class="fa-solid fa-paper-plane displayNone" title="Send a message" data-i18n="[title]Send a message"></div>
</div> </div>
</form> </form>
</div> </div>

View File

@ -1,6 +1,6 @@
{ {
"name": "🧙 Roleplay", "name": "Roleplay",
"system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.\n\nAvoid repetition, don't loop. Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions.\n\n### Input:", "system_prompt": "Avoid repetition, don't loop. Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions.",
"input_sequence": "\n### Instruction:", "input_sequence": "\n### Instruction:",
"output_sequence": "\n### Response:", "output_sequence": "\n### Response:",
"last_output_sequence": "\n### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):", "last_output_sequence": "\n### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):",

View File

@ -129,6 +129,7 @@ import {
getCharaFilename, getCharaFilename,
isDigitsOnly, isDigitsOnly,
PAGINATION_TEMPLATE, PAGINATION_TEMPLATE,
waitUntilCondition,
} from "./scripts/utils.js"; } from "./scripts/utils.js";
import { extension_settings, getContext, loadExtensionSettings, processExtensionHelpers, registerExtensionHelper, runGenerationInterceptors, saveMetadataDebounced } from "./scripts/extensions.js"; import { extension_settings, getContext, loadExtensionSettings, processExtensionHelpers, registerExtensionHelper, runGenerationInterceptors, saveMetadataDebounced } from "./scripts/extensions.js";
@ -314,6 +315,8 @@ let safetychat = [
mes: "You deleted a character/chat and arrived back here for safety reasons! Pick another character!", mes: "You deleted a character/chat and arrived back here for safety reasons! Pick another character!",
}, },
]; ];
let chatSaveTimeout;
export let isChatSaving = false;
let chat_create_date = 0; let chat_create_date = 0;
let firstRun = false; let firstRun = false;
@ -350,7 +353,6 @@ let scrollLock = false;
const durationSaveEdit = 1000; const durationSaveEdit = 1000;
const saveSettingsDebounced = debounce(() => saveSettings(), durationSaveEdit); const saveSettingsDebounced = debounce(() => saveSettings(), durationSaveEdit);
export const saveCharacterDebounced = debounce(() => $("#create_button").trigger('click'), durationSaveEdit); export const saveCharacterDebounced = debounce(() => $("#create_button").trigger('click'), durationSaveEdit);
const saveChatDebounced = debounce(() => saveChatConditional(), durationSaveEdit);
const system_message_types = { const system_message_types = {
HELP: "help", HELP: "help",
@ -623,7 +625,6 @@ let is_api_button_press_novel = false;
let api_use_mancer_webui = false; let api_use_mancer_webui = false;
let is_send_press = false; //Send generation let is_send_press = false; //Send generation
let add_mes_without_animation = false;
let this_del_mes = 0; let this_del_mes = 0;
@ -821,6 +822,11 @@ export function selectCharacterById(id) {
return; return;
} }
if (isChatSaving) {
toastr.info("Please wait until the chat is saved before switching characters.", "Your chat is still saving...");
return;
}
if (selected_group && is_group_generating) { if (selected_group && is_group_generating) {
return; return;
} }
@ -1884,24 +1890,19 @@ function cleanGroupMessage(getMessage) {
return getMessage; return getMessage;
} }
function getPersonaDescription(storyString) { function addPersonaDescriptionExtensionPrompt() {
if (!power_user.persona_description) { if (!power_user.persona_description) {
return storyString; return;
} }
switch (power_user.persona_description_position) { const promptPositions = [persona_description_positions.BOTTOM_AN, persona_description_positions.TOP_AN];
case persona_description_positions.BEFORE_CHAR:
case persona_description_positions.AFTER_CHAR: if (promptPositions.includes(power_user.persona_description_position) && shouldWIAddPrompt) {
return storyString; const originalAN = extension_prompts[NOTE_MODULE_NAME].value
default: const ANWithDesc = power_user.persona_description_position === persona_description_positions.TOP_AN
if (shouldWIAddPrompt) { ? `${power_user.persona_description}\n${originalAN}`
const originalAN = extension_prompts[NOTE_MODULE_NAME].value : `${originalAN}\n${power_user.persona_description}`;
const ANWithDesc = power_user.persona_description_position === persona_description_positions.TOP_AN setExtensionPrompt(NOTE_MODULE_NAME, ANWithDesc, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth]);
? `${power_user.persona_description}\n${originalAN}`
: `${originalAN}\n${power_user.persona_description}`;
setExtensionPrompt(NOTE_MODULE_NAME, ANWithDesc, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth]);
}
return storyString;
} }
} }
@ -2287,6 +2288,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
setCharacterName(''); setCharacterName('');
} else { } else {
console.log('No enabled members found'); console.log('No enabled members found');
is_send_press = false;
return; return;
} }
} }
@ -2318,10 +2320,6 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
} }
} }
if (!type && !textareaText && power_user.continue_on_send && !selected_group && chat.length && !chat[chat.length - 1]['is_user']) {
type = 'continue';
}
const isContinue = type == 'continue'; const isContinue = type == 'continue';
if (!dryRun) { if (!dryRun) {
@ -2405,18 +2403,6 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
console.log(`Core/all messages: ${coreChat.length}/${chat.length}`); console.log(`Core/all messages: ${coreChat.length}/${chat.length}`);
const storyStringParams = {
description: charDescription,
personality: charPersonality,
persona: personaDescription,
scenario: Scenario,
system: isInstruct ? systemPrompt : '',
char: name2,
user: name1,
};
let storyString = renderStoryString(storyStringParams);
// kingbri MARK: - Make sure the prompt bias isn't the same as the user bias // kingbri MARK: - Make sure the prompt bias isn't the same as the user bias
if ((promptBias && !isUserPromptBias) || power_user.always_force_name2 || is_pygmalion) { if ((promptBias && !isUserPromptBias) || power_user.always_force_name2 || is_pygmalion) {
force_name2 = true; force_name2 = true;
@ -2437,10 +2423,15 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
break; break;
} }
chat2[i] = formatMessageHistoryItem(coreChat[j], isInstruct); chat2[i] = formatMessageHistoryItem(coreChat[j], isInstruct, false);
// Do not suffix the message for continuation // Do not suffix the message for continuation
if (i === 0 && isContinue) { if (i === 0 && isContinue) {
if (isInstruct) {
// Reformat with the last output line (if any)
chat2[i] = formatMessageHistoryItem(coreChat[j], isInstruct, true);
}
chat2[i] = chat2[i].slice(0, chat2[i].lastIndexOf(coreChat[j].mes) + coreChat[j].mes.length); chat2[i] = chat2[i].slice(0, chat2[i].lastIndexOf(coreChat[j].mes) + coreChat[j].mes.length);
continue_mag = coreChat[j].mes; continue_mag = coreChat[j].mes;
} }
@ -2462,22 +2453,32 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
} }
// Extension added strings // Extension added strings
// Set non-WI AN // Set non-WI AN
setFloatingPrompt(); setFloatingPrompt();
// Add WI to prompt (and also inject WI to AN value via hijack) // Add WI to prompt (and also inject WI to AN value via hijack)
let { worldInfoString, worldInfoBefore, worldInfoAfter } = await getWorldInfoPrompt(chat2, this_max_context); let { worldInfoString, worldInfoBefore, worldInfoAfter } = await getWorldInfoPrompt(chat2, this_max_context);
// Add persona description to prompt // Add persona description to prompt
storyString = getPersonaDescription(storyString); addPersonaDescriptionExtensionPrompt();
// Call combined AN into Generate // Call combined AN into Generate
let allAnchors = getAllExtensionPrompts(); let allAnchors = getAllExtensionPrompts();
const afterScenarioAnchor = getExtensionPrompt(extension_prompt_types.AFTER_SCENARIO); const afterScenarioAnchor = getExtensionPrompt(extension_prompt_types.AFTER_SCENARIO);
let zeroDepthAnchor = getExtensionPrompt(extension_prompt_types.IN_CHAT, 0, ' '); let zeroDepthAnchor = getExtensionPrompt(extension_prompt_types.IN_CHAT, 0, ' ');
// Pre-format the World Info into the story string const storyStringParams = {
if (main_api !== 'openai') { description: charDescription,
storyString = worldInfoBefore + storyString + worldInfoAfter; personality: charPersonality,
} persona: personaDescription,
scenario: Scenario,
system: isInstruct ? systemPrompt : '',
char: name2,
user: name1,
wiBefore: worldInfoBefore,
wiAfter: worldInfoAfter,
loreBefore: worldInfoBefore,
loreAfter: worldInfoAfter,
};
const storyString = renderStoryString(storyStringParams);
if (main_api === 'openai') { if (main_api === 'openai') {
message_already_generated = ''; // OpenAI doesn't have multigen message_already_generated = ''; // OpenAI doesn't have multigen
@ -2599,16 +2600,9 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
return; return;
} }
if (i === arrMes.length - 1 && !item.trim().startsWith(name1 + ":")) { // Cohee: I'm not even sure what this is for anymore
//if (textareaText == "") { if (i === arrMes.length - 1 && type !== 'continue') {
// Cohee: I think this was added to allow the model to continue
// where it left off by removing the trailing newline at the end
// that was added by chat2 generator. This causes problems with
// instruct mode that could not have a trailing newline. So we're
// removing a newline ONLY at the end of the string if it exists.
item = item.replace(/\n?$/, ''); item = item.replace(/\n?$/, '');
//item = item.substr(0, item.length - 1);
//}
} }
if (is_pygmalion && !isInstruct) { if (is_pygmalion && !isInstruct) {
if (item.trim().startsWith(name1)) { if (item.trim().startsWith(name1)) {
@ -2654,7 +2648,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
// Add quiet generation prompt at depth 0 // Add quiet generation prompt at depth 0
if (quiet_prompt && quiet_prompt.length) { if (quiet_prompt && quiet_prompt.length) {
const name = is_pygmalion ? 'You' : name1; const name = is_pygmalion ? 'You' : name1;
const quietAppend = isInstruct ? formatInstructModeChat(name, quiet_prompt, false, true, false, name1, name2) : `\n${name}: ${quiet_prompt}`; const quietAppend = isInstruct ? formatInstructModeChat(name, quiet_prompt, false, true, '', name1, name2, false) : `\n${name}: ${quiet_prompt}`;
lastMesString += quietAppend; lastMesString += quietAppend;
// Bail out early // Bail out early
return lastMesString; return lastMesString;
@ -2700,6 +2694,11 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
promptCache = promptCache.replace(promptBias, ''); promptCache = promptCache.replace(promptBias, '');
} }
// Add a space if prompt cache doesn't start with one
if (!/^\s/.test(promptCache) && !isInstruct) {
promptCache = ' ' + promptCache;
}
return promptCache; return promptCache;
} }
@ -2823,7 +2822,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
let finalPromt = getCombinedPrompt(false); let finalPromt = getCombinedPrompt(false);
// Include the entire guidance scale object // Include the entire guidance scale object
const cfgValues = cfgGuidanceScale && cfgGuidanceScale?.value !== 1 ? ({guidanceScale: cfgGuidanceScale, negativePrompt: negativePrompt }) : null; const cfgValues = cfgGuidanceScale && cfgGuidanceScale?.value !== 1 ? ({ guidanceScale: cfgGuidanceScale, negativePrompt: negativePrompt }) : null;
let this_amount_gen = Number(amount_gen); // how many tokens the AI will be requested to generate let this_amount_gen = Number(amount_gen); // how many tokens the AI will be requested to generate
let this_settings = koboldai_settings[koboldai_setting_names[preset_settings]]; let this_settings = koboldai_settings[koboldai_setting_names[preset_settings]];
@ -2880,6 +2879,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
cyclePrompt: cyclePrompt, cyclePrompt: cyclePrompt,
systemPromptOverride: systemPrompt, systemPromptOverride: systemPrompt,
jailbreakPromptOverride: jailbreakPrompt, jailbreakPromptOverride: jailbreakPrompt,
personaDescription: personaDescription
}, dryRun); }, dryRun);
generate_data = { prompt: prompt }; generate_data = { prompt: prompt };
@ -3216,7 +3216,12 @@ export function getBiasStrings(textareaText, type) {
return { messageBias, promptBias, isUserPromptBias }; return { messageBias, promptBias, isUserPromptBias };
} }
function formatMessageHistoryItem(chatItem, isInstruct) { /**
* @param {Object} chatItem Message history item.
* @param {boolean} isInstruct Whether instruct mode is enabled.
* @param {boolean} forceLastOutputSequence Whether to force the last output sequence for instruct mode.
*/
function formatMessageHistoryItem(chatItem, isInstruct, forceLastOutputSequence) {
const isNarratorType = chatItem?.extra?.type === system_message_types.NARRATOR; const isNarratorType = chatItem?.extra?.type === system_message_types.NARRATOR;
const characterName = (selected_group || chatItem.force_avatar) ? chatItem.name : name2; const characterName = (selected_group || chatItem.force_avatar) ? chatItem.name : name2;
const itemName = chatItem.is_user ? chatItem['name'] : characterName; const itemName = chatItem.is_user ? chatItem['name'] : characterName;
@ -3225,7 +3230,7 @@ function formatMessageHistoryItem(chatItem, isInstruct) {
let textResult = shouldPrependName ? `${itemName}: ${chatItem.mes}\n` : `${chatItem.mes}\n`; let textResult = shouldPrependName ? `${itemName}: ${chatItem.mes}\n` : `${chatItem.mes}\n`;
if (isInstruct) { if (isInstruct) {
textResult = formatInstructModeChat(itemName, chatItem.mes, chatItem.is_user, isNarratorType, chatItem.force_avatar, name1, name2); textResult = formatInstructModeChat(itemName, chatItem.mes, chatItem.is_user, isNarratorType, chatItem.force_avatar, name1, name2, forceLastOutputSequence);
} }
textResult = replaceBiasMarkup(textResult); textResult = replaceBiasMarkup(textResult);
@ -3965,14 +3970,16 @@ export function isMultigenEnabled() {
export function activateSendButtons() { export function activateSendButtons() {
is_send_press = false; is_send_press = false;
$("#send_but").css("display", "flex"); $("#send_but").removeClass("displayNone");
$("#mes_continue").removeClass("displayNone");
$("#send_textarea").attr("disabled", false); $("#send_textarea").attr("disabled", false);
$('.mes_buttons:last').show(); $('.mes_buttons:last').show();
hideStopButton(); hideStopButton();
} }
export function deactivateSendButtons() { export function deactivateSendButtons() {
$("#send_but").css("display", "none"); $("#send_but").addClass("displayNone");
$("#mes_continue").addClass("displayNone");
showStopButton(); showStopButton();
} }
@ -4139,6 +4146,32 @@ async function renamePastChats(newAvatar, newValue) {
} }
} }
function saveChatDebounced() {
const chid = this_chid;
const selectedGroup = selected_group;
if (chatSaveTimeout) {
console.debug('Clearing chat save timeout');
clearTimeout(chatSaveTimeout);
}
chatSaveTimeout = setTimeout(async () => {
if (selectedGroup !== selected_group) {
console.warn('Chat save timeout triggered, but group changed. Aborting.');
return;
}
if (chid !== this_chid) {
console.warn('Chat save timeout triggered, but chid changed. Aborting.');
return;
}
console.debug('Chat save timeout triggered');
await saveChatConditional();
console.debug('Chat saved');
}, 1000);
}
async function saveChat(chat_name, withMetadata, mesId) { async function saveChat(chat_name, withMetadata, mesId) {
const metadata = { ...chat_metadata, ...(withMetadata || {}) }; const metadata = { ...chat_metadata, ...(withMetadata || {}) };
let file_name = chat_name ?? characters[this_chid].chat; let file_name = chat_name ?? characters[this_chid].chat;
@ -4290,7 +4323,6 @@ async function getChat() {
chat_create_date = humanizedDateTime(); chat_create_date = humanizedDateTime();
} }
await getChatResult(); await getChatResult();
saveChatDebounced();
eventSource.emit('chatLoaded', { detail: { id: this_chid, character: characters[this_chid] } }); eventSource.emit('chatLoaded', { detail: { id: this_chid, character: characters[this_chid] } });
setTimeout(function () { setTimeout(function () {
@ -4306,26 +4338,9 @@ async function getChat() {
async function getChatResult() { async function getChatResult() {
name2 = characters[this_chid].name; name2 = characters[this_chid].name;
if (chat.length === 0) { if (chat.length === 0) {
const firstMes = characters[this_chid].first_mes || default_ch_mes; const message = getFirstMessage();
const alternateGreetings = characters[this_chid]?.data?.alternate_greetings; chat.push(message);
await saveChatConditional();
chat[0] = {
name: name2,
is_user: false,
is_name: true,
send_date: getMessageTimeStamp(),
mes: getRegexedString(firstMes, regex_placement.AI_OUTPUT),
};
if (Array.isArray(alternateGreetings) && alternateGreetings.length > 0) {
chat[0]['swipe_id'] = 0;
chat[0]['swipes'] = [chat[0]['mes']].concat(
alternateGreetings.map(
(greeting) => substituteParams(getRegexedString(greeting, regex_placement.AI_OUTPUT))
)
);
chat[0]['swipe_info'] = [];
}
} }
printMessages(); printMessages();
select_selected_character(this_chid); select_selected_character(this_chid);
@ -4338,6 +4353,30 @@ async function getChatResult() {
} }
} }
function getFirstMessage() {
const firstMes = characters[this_chid].first_mes || default_ch_mes;
const alternateGreetings = characters[this_chid]?.data?.alternate_greetings;
const message = {
name: name2,
is_user: false,
is_name: true,
send_date: getMessageTimeStamp(),
mes: getRegexedString(firstMes, regex_placement.AI_OUTPUT),
};
if (Array.isArray(alternateGreetings) && alternateGreetings.length > 0) {
message['swipe_id'] = 0;
message['swipes'] = message['mes'].concat(
alternateGreetings.map(
(greeting) => substituteParams(getRegexedString(greeting, regex_placement.AI_OUTPUT))
)
);
message['swipe_info'] = [];
}
return message;
}
async function openCharacterChat(file_name) { async function openCharacterChat(file_name) {
characters[this_chid]["chat"] = file_name; characters[this_chid]["chat"] = file_name;
clearChat(); clearChat();
@ -4872,7 +4911,7 @@ async function deleteUserAvatar() {
if (avatarId === chat_metadata['persona']) { if (avatarId === chat_metadata['persona']) {
toastr.warning('The locked persona was deleted. You will need to set a new persona for this chat.', 'Persona deleted'); toastr.warning('The locked persona was deleted. You will need to set a new persona for this chat.', 'Persona deleted');
delete chat_metadata['persona']; delete chat_metadata['persona'];
saveMetadata(); await saveMetadata();
} }
saveSettingsDebounced(); saveSettingsDebounced();
@ -4881,11 +4920,11 @@ async function deleteUserAvatar() {
} }
} }
function lockUserNameToChat() { async function lockUserNameToChat() {
if (chat_metadata['persona']) { if (chat_metadata['persona']) {
console.log(`Unlocking persona for this chat ${chat_metadata['persona']}`); console.log(`Unlocking persona for this chat ${chat_metadata['persona']}`);
delete chat_metadata['persona']; delete chat_metadata['persona'];
saveMetadata(); await saveMetadata();
if (power_user.persona_show_notifications) { if (power_user.persona_show_notifications) {
toastr.info('User persona is now unlocked for this chat. Click the "Lock" again to revert.', 'Persona unlocked'); toastr.info('User persona is now unlocked for this chat. Click the "Lock" again to revert.', 'Persona unlocked');
} }
@ -4907,7 +4946,7 @@ function lockUserNameToChat() {
} }
chat_metadata['persona'] = user_avatar; chat_metadata['persona'] = user_avatar;
saveMetadata(); await saveMetadata();
saveSettingsDebounced(); saveSettingsDebounced();
console.log(`Locking persona for this chat ${user_avatar}`); console.log(`Locking persona for this chat ${user_avatar}`);
if (power_user.persona_show_notifications) { if (power_user.persona_show_notifications) {
@ -6006,23 +6045,38 @@ function hideSwipeButtons() {
async function saveMetadata() { async function saveMetadata() {
if (selected_group) { if (selected_group) {
await editGroup(selected_group, false, false); await editGroup(selected_group, true, false);
} }
else { else {
saveChatDebounced(); await saveChatConditional();
} }
} }
export async function saveChatConditional() { export async function saveChatConditional() {
if (selected_group) { try {
await saveGroupChat(selected_group, true); await waitUntilCondition(() => !isChatSaving, durationSaveEdit, 100);
} } catch {
else { console.warn('Timeout waiting for chat to save');
await saveChat(); return;
} }
// Save token cache to IndexedDB storage try {
await saveTokenCache(); isChatSaving = true;
if (selected_group) {
await saveGroupChat(selected_group, true);
}
else {
await saveChat();
}
// Save token cache to IndexedDB storage
saveTokenCache();
} catch (error) {
console.error('Error saving chat', error);
} finally {
isChatSaving = false;
}
} }
async function importCharacterChat(formData) { async function importCharacterChat(formData) {
@ -6454,58 +6508,6 @@ async function createOrEditCharacter(e) {
contentType: false, contentType: false,
processData: false, processData: false,
success: async function (html) { success: async function (html) {
if (chat.length === 1 && !selected_group) {
var this_ch_mes = default_ch_mes;
if ($("#firstmessage_textarea").val() != "") {
this_ch_mes = $("#firstmessage_textarea").val();
}
if (
this_ch_mes !=
$.trim(
$("#chat")
.children(".mes")
.children(".mes_block")
.children(".mes_text")
.text()
)
) {
// MARK - kingbri: Regex on character greeting message
// May need to be placed somewhere else
this_ch_mes = getRegexedString(this_ch_mes, regex_placement.AI_OUTPUT);
clearChat();
chat.length = 0;
chat[0] = {};
chat[0]["name"] = name2;
chat[0]["is_user"] = false;
chat[0]["is_name"] = true;
chat[0]["mes"] = this_ch_mes;
chat[0]["extra"] = {};
chat[0]["send_date"] = getMessageTimeStamp();
const alternateGreetings = characters[this_chid]?.data?.alternate_greetings;
if (Array.isArray(alternateGreetings) && alternateGreetings.length > 0) {
chat[0]['swipe_id'] = 0;
chat[0]['swipes'] = [];
chat[0]['swipe_info'] = [];
chat[0]['swipes'][0] = chat[0]['mes'];
for (let i = 0; i < alternateGreetings.length; i++) {
const alternateGreeting = getRegexedString(alternateGreetings[i], regex_placement.AI_OUTPUT);
chat[0]['swipes'].push(substituteParams(alternateGreeting));
}
}
add_mes_without_animation = true;
//console.log('form create submission calling addOneMessage');
await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1));
addOneMessage(chat[0]);
await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, (chat.length - 1));
}
}
$("#create_button").removeAttr("disabled"); $("#create_button").removeAttr("disabled");
await getOneCharacter(formData.get('avatar_url')); await getOneCharacter(formData.get('avatar_url'));
@ -6516,6 +6518,17 @@ async function createOrEditCharacter(e) {
$("#create_button").attr("value", "Save"); $("#create_button").attr("value", "Save");
crop_data = undefined; crop_data = undefined;
eventSource.emit(event_types.CHARACTER_EDITED, { detail: { id: this_chid, character: characters[this_chid] } }); eventSource.emit(event_types.CHARACTER_EDITED, { detail: { id: this_chid, character: characters[this_chid] } });
if (chat.length === 1 && !selected_group) {
const firstMessage = getFirstMessage();
chat[0] = firstMessage;
await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1));
clearChat();
printMessages();
await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, (chat.length - 1));
await saveChatConditional();
}
}, },
error: function (jqXHR, exception) { error: function (jqXHR, exception) {
$("#create_button").removeAttr("disabled"); $("#create_button").removeAttr("disabled");
@ -7153,7 +7166,7 @@ $(document).ready(function () {
S_TAPreviouslyFocused = true; S_TAPreviouslyFocused = true;
}); });
$('#send_textarea').on('focusout blur', () => S_TAFocused = false); $('#send_textarea').on('focusout blur', () => S_TAFocused = false);
$('#options_button, #send_but, #option_regenerate').on('click', () => { $('#options_button, #send_but, #option_regenerate, #option_continue, #mes_continue').on('click', () => {
if (S_TAPreviouslyFocused) { if (S_TAPreviouslyFocused) {
$('#send_textarea').focus(); $('#send_textarea').focus();
S_TAFocused = true; S_TAFocused = true;
@ -7161,8 +7174,8 @@ $(document).ready(function () {
}); });
$(document).click(event => { $(document).click(event => {
if ($(':focus').attr('id') !== 'send_textarea') { if ($(':focus').attr('id') !== 'send_textarea') {
var validIDs = ["options_button", "send_but", "send_textarea", "option_regenerate"]; var validIDs = ["options_button", "send_but", "mes_continue", "send_textarea", "option_regenerate", "option_continue"];
if ($(event.target).attr('id') !== validIDs) { if (!validIDs.includes($(event.target).attr('id'))) {
S_TAFocused = false; S_TAFocused = false;
S_TAPreviouslyFocused = false; S_TAPreviouslyFocused = false;
} }
@ -7196,7 +7209,11 @@ $(document).ready(function () {
entitiesFilter.setFilterData(FILTER_TYPES.SEARCH, searchValue); entitiesFilter.setFilterData(FILTER_TYPES.SEARCH, searchValue);
}); });
$("#send_but").click(function () { $("#mes_continue").on('click', function () {
$("#option_continue").trigger('click');
});
$("#send_but").on('click', function () {
if (is_send_press == false) { if (is_send_press == false) {
is_send_press = true; is_send_press = true;
Generate(); Generate();
@ -7471,14 +7488,15 @@ $(document).ready(function () {
chat.length = 0; chat.length = 0;
if (selected_group) { if (selected_group) {
createNewGroupChat(selected_group); await createNewGroupChat(selected_group);
} }
else { else {
//RossAscends: added character name to new chat filenames and replaced Date.now() with humanizedDateTime; //RossAscends: added character name to new chat filenames and replaced Date.now() with humanizedDateTime;
chat_metadata = {}; chat_metadata = {};
characters[this_chid].chat = name2 + " - " + humanizedDateTime(); characters[this_chid].chat = name2 + " - " + humanizedDateTime();
$("#selected_chat_pole").val(characters[this_chid].chat); $("#selected_chat_pole").val(characters[this_chid].chat);
getChat(); await getChat();
await createOrEditCharacter();
} }
} }
@ -7630,11 +7648,11 @@ $(document).ready(function () {
else { else {
if (characters[this_chid].chat == old_filename) { if (characters[this_chid].chat == old_filename) {
characters[this_chid].chat = newName; characters[this_chid].chat = newName;
saveCharacterDebounced(); await createOrEditCharacter();
} }
} }
reloadCurrentChat(); await reloadCurrentChat();
await delay(250); await delay(250);
$("#option_select_chat").trigger('click'); $("#option_select_chat").trigger('click');
@ -8008,6 +8026,32 @@ $(document).ready(function () {
////////////////// OPTIMIZED RANGE SLIDER LISTENERS//////////////// ////////////////// OPTIMIZED RANGE SLIDER LISTENERS////////////////
var sliderLocked = true;
var sliderTimer;
$("input[type='range']").on("touchstart", function () {
// Unlock the slider after 500ms
sliderTimer = setTimeout(function () {
sliderLocked = false;
}, 500);
});
$("input[type='range']").on("touchend", function () {
clearTimeout(sliderTimer);
$(this).css('background-color', '')
sliderLocked = true
});
$("input[type='range']").on("touchmove", function (event) {
if (sliderLocked) {
event.preventDefault();
}
else {
$(this).css('background-color', 'var(--SmartThemeQuoteColor)')
}
});
const sliders = [ const sliders = [
{ {
sliderId: "#amount_gen", sliderId: "#amount_gen",

View File

@ -1772,6 +1772,12 @@ const chatCompletionDefaultPrompts = {
"system_prompt": true, "system_prompt": true,
"marker": true, "marker": true,
}, },
{
"identifier": "personaDescription",
"name": "Persona Description",
"system_prompt": true,
"marker": true,
},
] ]
}; };
@ -1788,6 +1794,10 @@ const promptManagerDefaultPromptOrder = [
"identifier": "worldInfoBefore", "identifier": "worldInfoBefore",
"enabled": true "enabled": true
}, },
{
"identifier": "personaDescription",
"enabled": true
},
{ {
"identifier": "charDescription", "identifier": "charDescription",
"enabled": true "enabled": true

View File

@ -315,7 +315,8 @@ function RA_checkOnlineStatus() {
if (online_status == "no_connection") { if (online_status == "no_connection") {
$("#send_textarea").attr("placeholder", "Not connected to API!"); //Input bar placeholder tells users they are not connected $("#send_textarea").attr("placeholder", "Not connected to API!"); //Input bar placeholder tells users they are not connected
$("#send_form").addClass('no-connection'); //entire input form area is red when not connected $("#send_form").addClass('no-connection'); //entire input form area is red when not connected
$("#send_but").css("display", "none"); //send button is hidden when not connected; $("#send_but").addClass("displayNone"); //send button is hidden when not connected;
$("#mes_continue").addClass("displayNone"); //continue button is hidden when not connected;
$("#API-status-top").removeClass("fa-plug"); $("#API-status-top").removeClass("fa-plug");
$("#API-status-top").addClass("fa-plug-circle-exclamation redOverlayGlow"); $("#API-status-top").addClass("fa-plug-circle-exclamation redOverlayGlow");
connection_made = false; connection_made = false;
@ -330,7 +331,8 @@ function RA_checkOnlineStatus() {
RA_AC_retries = 1; RA_AC_retries = 1;
if (!is_send_press && !(selected_group && is_group_generating)) { if (!is_send_press && !(selected_group && is_group_generating)) {
$("#send_but").css("display", "flex"); //on connect, send button shows $("#send_but").removeClass("displayNone"); //on connect, send button shows
$("#mes_continue").removeClass("displayNone"); //continue button is shown when connected
} }
} }
} }

View File

@ -14,7 +14,37 @@ export {
let extensionNames = []; let extensionNames = [];
let manifests = {}; let manifests = {};
const defaultUrl = "http://localhost:5100"; const defaultUrl = "http://localhost:5100";
export const saveMetadataDebounced = debounce(async () => await getContext().saveMetadata(), 1000);
let saveMetadataTimeout = null;
export function saveMetadataDebounced() {
const context = getContext();
const groupId = context.groupId;
const characterId = context.characterId;
if (saveMetadataTimeout) {
console.debug('Clearing save metadata timeout');
clearTimeout(saveMetadataTimeout);
}
saveMetadataTimeout = setTimeout(async () => {
const newContext = getContext();
if (groupId !== newContext.groupId) {
console.warn('Group changed, not saving metadata');
return;
}
if (characterId !== newContext.characterId) {
console.warn('Character changed, not saving metadata');
return;
}
console.debug('Saving metadata...');
newContext.saveMetadata();
console.debug('Saved metadata...');
}, 1000);
}
export const extensionsHandlebars = Handlebars.create(); export const extensionsHandlebars = Handlebars.create();

View File

@ -164,7 +164,7 @@ function getNextIncompleteTaskRecurse(task){
} }
// Set a task in extensionPrompt context. Defaults to first incomplete // Set a task in extensionPrompt context. Defaults to first incomplete
function setCurrentTask(taskId = null) { function setCurrentTask(taskId = null, skipSave = false) {
const context = getContext(); const context = getContext();
// TODO: Should probably null this rather than set empty object // TODO: Should probably null this rather than set empty object
@ -202,7 +202,10 @@ function setCurrentTask(taskId = null) {
console.info(`No current task`); console.info(`No current task`);
} }
saveState(); // Save state if not skipping
if (!skipSave) {
saveState();
}
} }
function getHighestTaskIdRecurse(task) { function getHighestTaskIdRecurse(task) {
@ -731,7 +734,7 @@ function loadSettings() {
$('#objective-check-frequency').val(chat_metadata['objective'].checkFrequency) $('#objective-check-frequency').val(chat_metadata['objective'].checkFrequency)
$('#objective-hide-tasks').prop('checked', chat_metadata['objective'].hideTasks) $('#objective-hide-tasks').prop('checked', chat_metadata['objective'].hideTasks)
$('#objective-tasks').prop('hidden', $('#objective-hide-tasks').prop('checked')) $('#objective-tasks').prop('hidden', $('#objective-hide-tasks').prop('checked'))
setCurrentTask() setCurrentTask(null, true)
} }
function addManualTaskCheckUi() { function addManualTaskCheckUi() {

View File

@ -818,7 +818,7 @@ function addSDGenButtons() {
$(document).on('click touchend', function (e) { $(document).on('click touchend', function (e) {
const target = $(e.target); const target = $(e.target);
if (target.is(dropdown)) return; if (target.is(dropdown)) return;
if (target.is(button) && !dropdown.is(":visible") && $("#send_but").css('display') === 'flex') { if (target.is(button) && !dropdown.is(":visible") && $("#send_but").is(":visible")) {
e.preventDefault(); e.preventDefault();
dropdown.fadeIn(250); dropdown.fadeIn(250);

View File

@ -1,3 +1,5 @@
import { deepClone } from "../../utils.js";
export { ElevenLabsTtsProvider } export { ElevenLabsTtsProvider }
class ElevenLabsTtsProvider { class ElevenLabsTtsProvider {
@ -47,18 +49,20 @@ class ElevenLabsTtsProvider {
loadSettings(settings) { loadSettings(settings) {
// Pupulate Provider UI given input settings // Pupulate Provider UI given input settings
if (Object.keys(settings).length == 0) { if (!settings || Object.keys(settings).length == 0) {
console.info("Using default TTS Provider settings") console.info("Using default TTS Provider settings")
} }
// Only accept keys defined in defaultSettings // Only accept keys defined in defaultSettings
this.settings = this.defaultSettings this.settings = deepClone(this.defaultSettings);
for (const key in settings){ if (settings) {
if (key in this.settings){ for (const key in settings) {
this.settings[key] = settings[key] if (key in this.settings) {
} else { this.settings[key] = settings[key]
throw `Invalid setting passed to TTS Provider: ${key}` } else {
throw `Invalid setting passed to TTS Provider: ${key}`
}
} }
} }

View File

@ -63,6 +63,7 @@ import {
setScenarioOverride, setScenarioOverride,
getCropPopup, getCropPopup,
system_avatar, system_avatar,
isChatSaving,
} from "../script.js"; } from "../script.js";
import { appendTagToList, createTagMapFromList, getTagsList, applyTagsOnCharacterSelect, tag_map, printTagFilters } from './tags.js'; import { appendTagToList, createTagMapFromList, getTagsList, applyTagsOnCharacterSelect, tag_map, printTagFilters } from './tags.js';
import { FILTER_TYPES, FilterHelper } from './filters.js'; import { FILTER_TYPES, FilterHelper } from './filters.js';
@ -1272,6 +1273,11 @@ function updateFavButtonState(state) {
} }
export async function openGroupById(groupId) { export async function openGroupById(groupId) {
if (isChatSaving) {
toastr.info("Please wait until the chat is saved before switching characters.", "Your chat is still saving...");
return;
}
if (!groups.find(x => x.id === groupId)) { if (!groups.find(x => x.id === groupId)) {
console.log('Group not found', groupId); console.log('Group not found', groupId);
return; return;

View File

@ -2,7 +2,10 @@
import { saveSettingsDebounced, substituteParams } from "../script.js"; import { saveSettingsDebounced, substituteParams } from "../script.js";
import { selected_group } from "./group-chats.js"; import { selected_group } from "./group-chats.js";
import { power_user } from "./power-user.js"; import {
power_user,
context_presets,
} from "./power-user.js";
/** /**
* @type {any[]} Instruct mode presets. * @type {any[]} Instruct mode presets.
@ -69,6 +72,48 @@ function highlightDefaultPreset() {
$('#instruct_set_default').toggleClass('default', power_user.default_instruct === power_user.instruct.preset); $('#instruct_set_default').toggleClass('default', power_user.default_instruct === power_user.instruct.preset);
} }
/**
* Select context template if not already selected.
* @param {string} preset Preset name.
*/
function selectContextPreset(preset) {
// If context template is not already selected, select it
if (preset !== power_user.context.preset) {
$('#context_presets').val(preset).trigger('change');
toastr.info(`Context Template: preset "${preset}" auto-selected`);
}
// If instruct mode is disabled, enable it, except for default context template
if (!power_user.instruct.enabled && preset !== 'Default') {
power_user.instruct.enabled = true;
$('#instruct_enabled').prop('checked', true).trigger('change');
toastr.info(`Instruct Mode enabled`);
}
saveSettingsDebounced();
}
/**
* Select instruct preset if not already selected.
* @param {string} preset Preset name.
*/
export function selectInstructPreset(preset) {
// If instruct preset is not already selected, select it
if (preset !== power_user.instruct.preset) {
$('#instruct_presets').val(preset).trigger('change');
toastr.info(`Instruct Mode: preset "${preset}" auto-selected`);
}
// If instruct mode is disabled, enable it
if (!power_user.instruct.enabled) {
power_user.instruct.enabled = true;
$('#instruct_enabled').prop('checked', true).trigger('change');
toastr.info(`Instruct Mode enabled`);
}
saveSettingsDebounced();
}
/** /**
* Automatically select instruct preset based on model id. * Automatically select instruct preset based on model id.
* Otherwise, if default instruct preset is set, selects it. * Otherwise, if default instruct preset is set, selects it.
@ -81,33 +126,42 @@ export function autoSelectInstructPreset(modelId) {
return false; return false;
} }
for (const preset of instruct_presets) { // Select matching instruct preset
// If activation regex is set, check if it matches the model id let foundMatch = false;
if (preset.activation_regex) { for (const instruct_preset of instruct_presets) {
try { // If instruct preset matches the context template
const regex = new RegExp(preset.activation_regex, 'i'); if (instruct_preset.name === power_user.context.preset) {
foundMatch = true;
selectInstructPreset(instruct_preset.name);
break;
}
}
// If no match was found, auto-select instruct preset
if (!foundMatch) {
for (const preset of instruct_presets) {
// If activation regex is set, check if it matches the model id
if (preset.activation_regex) {
try {
const regex = new RegExp(preset.activation_regex, 'i');
// Stop on first match so it won't cycle back and forth between presets if multiple regexes match // Stop on first match so it won't cycle back and forth between presets if multiple regexes match
if (regex.test(modelId)) { if (regex.test(modelId)) {
// If preset is not already selected, select it selectInstructPreset(preset.name);
if (power_user.instruct.preset !== preset.name) {
$('#instruct_presets').val(preset.name).trigger('change');
toastr.info(`Instruct mode: preset "${preset.name}" auto-selected`);
return true; return true;
} }
} catch {
// If regex is invalid, ignore it
console.warn(`Invalid instruct activation regex in preset "${preset.name}"`);
} }
} catch {
// If regex is invalid, ignore it
console.warn(`Invalid instruct activation regex in preset "${preset.name}"`);
} }
} }
}
if (power_user.default_instruct && power_user.instruct.preset !== power_user.default_instruct) { if (power_user.default_instruct && power_user.instruct.preset !== power_user.default_instruct) {
if (instruct_presets.some(p => p.name === 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`); console.log(`Instruct mode: default preset "${power_user.default_instruct}" selected`);
$('#instruct_presets').val(power_user.default_instruct).trigger('change'); $('#instruct_presets').val(power_user.default_instruct).trigger('change');
}
} }
} }
@ -165,9 +219,10 @@ export function getInstructStoppingSequences() {
* @param {string} forceAvatar Force avatar string. * @param {string} forceAvatar Force avatar string.
* @param {string} name1 User name. * @param {string} name1 User name.
* @param {string} name2 Character name. * @param {string} name2 Character name.
* @param {boolean} forceLastOutputSequence Force to use last outline sequence (if configured).
* @returns {string} Formatted instruct mode chat message. * @returns {string} Formatted instruct mode chat message.
*/ */
export function formatInstructModeChat(name, mes, isUser, isNarrator, forceAvatar, name1, name2) { export function formatInstructModeChat(name, mes, isUser, isNarrator, forceAvatar, name1, name2, forceLastOutputSequence) {
let includeNames = isNarrator ? false : power_user.instruct.names; let includeNames = isNarrator ? false : power_user.instruct.names;
if (!isNarrator && power_user.instruct.names_force_groups && (selected_group || forceAvatar)) { if (!isNarrator && power_user.instruct.names_force_groups && (selected_group || forceAvatar)) {
@ -176,6 +231,10 @@ export function formatInstructModeChat(name, mes, isUser, isNarrator, forceAvata
let sequence = (isUser || isNarrator) ? power_user.instruct.input_sequence : power_user.instruct.output_sequence; let sequence = (isUser || isNarrator) ? power_user.instruct.input_sequence : power_user.instruct.output_sequence;
if (sequence === power_user.instruct.output_sequence && forceLastOutputSequence && power_user.instruct.last_output_sequence) {
sequence = power_user.instruct.last_output_sequence;
}
if (power_user.instruct.macro) { if (power_user.instruct.macro) {
sequence = substituteParams(sequence, name1, name2); sequence = substituteParams(sequence, name1, name2);
} }
@ -273,6 +332,16 @@ jQuery(() => {
saveSettingsDebounced(); saveSettingsDebounced();
}); });
$('#instruct_enabled').on('change', function () {
// When instruct mode gets enabled, select context template matching selected instruct preset
if (power_user.instruct.enabled) {
$('#instruct_presets').trigger('change');
// When instruct mode gets disabled, select default context preset
} else {
selectContextPreset('Default');
}
});
$('#instruct_presets').on('change', function () { $('#instruct_presets').on('change', function () {
const name = $(this).find(':selected').val(); const name = $(this).find(':selected').val();
const preset = instruct_presets.find(x => x.name === name); const preset = instruct_presets.find(x => x.name === name);
@ -295,6 +364,21 @@ jQuery(() => {
} }
}); });
// Select matching context template
let foundMatch = false;
for (const context_preset of context_presets) {
// If context template matches the instruct preset
if (context_preset.name === name) {
foundMatch = true;
selectContextPreset(context_preset.name);
break;
}
}
if (!foundMatch) {
// If no match was found, select default context preset
selectContextPreset('Default');
}
highlightDefaultPreset(); highlightDefaultPreset();
}); });
}); });

View File

@ -339,7 +339,7 @@ function setupChatCompletionPromptManager(openAiSettings) {
}, },
promptOrder: { promptOrder: {
strategy: 'global', strategy: 'global',
dummyId: 100000 dummyId: 100001
}, },
}; };
@ -577,6 +577,7 @@ function populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, ty
addToChatCompletion('charDescription'); addToChatCompletion('charDescription');
addToChatCompletion('charPersonality'); addToChatCompletion('charPersonality');
addToChatCompletion('scenario'); addToChatCompletion('scenario');
addToChatCompletion('personaDescription')
// Collection of control prompts that will always be positioned last // Collection of control prompts that will always be positioned last
const controlPrompts = new MessageCollection('controlPrompts'); const controlPrompts = new MessageCollection('controlPrompts');
@ -626,34 +627,6 @@ function populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, ty
if (true === afterScenario) chatCompletion.insert(authorsNote, 'scenario'); if (true === afterScenario) chatCompletion.insert(authorsNote, 'scenario');
} }
// Persona Description
if (power_user.persona_description) {
const personaDescription = Message.fromPrompt(prompts.get('personaDescription'));
try {
switch (power_user.persona_description_position) {
case persona_description_positions.BEFORE_CHAR:
chatCompletion.insertAtStart(personaDescription, 'charDescription');
break;
case persona_description_positions.AFTER_CHAR:
chatCompletion.insertAtEnd(personaDescription, 'charDescription');
break;
case persona_description_positions.TOP_AN:
chatCompletion.insertAtStart(personaDescription, 'authorsNote');
break;
case persona_description_positions.BOTTOM_AN:
chatCompletion.insertAtEnd(personaDescription, 'authorsNote');
break;
}
} catch (error) {
if (error instanceof IdentifierNotFoundError) {
// Error is acceptable in this context
} else {
throw error;
}
}
}
// Decide whether dialogue examples should always be added // Decide whether dialogue examples should always be added
if (power_user.pin_examples) { if (power_user.pin_examples) {
populateDialogueExamples(prompts, chatCompletion); populateDialogueExamples(prompts, chatCompletion);
@ -679,10 +652,12 @@ function populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, ty
* @param {string} quietPrompt - The quiet prompt to be used in the conversation. * @param {string} quietPrompt - The quiet prompt to be used in the conversation.
* @param {string} bias - The bias to be added in the conversation. * @param {string} bias - The bias to be added in the conversation.
* @param {Object} extensionPrompts - An object containing additional prompts. * @param {Object} extensionPrompts - An object containing additional prompts.
* * @param {string} systemPromptOverride
* @param {string} jailbreakPromptOverride
* @param {string} personaDescription
* @returns {Object} prompts - The prepared and merged system and user-defined prompts. * @returns {Object} prompts - The prepared and merged system and user-defined prompts.
*/ */
function preparePromptsForChatCompletion(Scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts, systemPromptOverride, jailbreakPromptOverride) { function preparePromptsForChatCompletion({Scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts, systemPromptOverride, jailbreakPromptOverride, personaDescription} = {}) {
const scenarioText = Scenario ? `[Circumstances and context of the dialogue: ${Scenario}]` : ''; const scenarioText = Scenario ? `[Circumstances and context of the dialogue: ${Scenario}]` : '';
const charPersonalityText = charPersonality ? `[${name2}'s personality: ${charPersonality}]` : '' const charPersonalityText = charPersonality ? `[${name2}'s personality: ${charPersonality}]` : ''
const groupNudge = `[Write the next reply only as ${name2}]`; const groupNudge = `[Write the next reply only as ${name2}]`;
@ -695,6 +670,7 @@ function preparePromptsForChatCompletion(Scenario, charPersonality, name2, world
{ role: 'system', content: charDescription, identifier: 'charDescription' }, { role: 'system', content: charDescription, identifier: 'charDescription' },
{ role: 'system', content: charPersonalityText, identifier: 'charPersonality' }, { role: 'system', content: charPersonalityText, identifier: 'charPersonality' },
{ role: 'system', content: scenarioText, identifier: 'scenario' }, { role: 'system', content: scenarioText, identifier: 'scenario' },
{ role: 'system', content: personaDescription, identifier: 'personaDescription' },
// Unordered prompts without marker // Unordered prompts without marker
{ role: 'system', content: oai_settings.nsfw_avoidance_prompt, identifier: 'nsfwAvoidance' }, { role: 'system', content: oai_settings.nsfw_avoidance_prompt, identifier: 'nsfwAvoidance' },
{ role: 'system', content: oai_settings.impersonation_prompt, identifier: 'impersonate' }, { role: 'system', content: oai_settings.impersonation_prompt, identifier: 'impersonate' },
@ -793,6 +769,7 @@ function prepareOpenAIMessages({
cyclePrompt, cyclePrompt,
systemPromptOverride, systemPromptOverride,
jailbreakPromptOverride, jailbreakPromptOverride,
personaDescription
} = {}, dryRun) { } = {}, dryRun) {
// Without a character selected, there is no way to accurately calculate tokens // Without a character selected, there is no way to accurately calculate tokens
if (!promptManager.activeCharacter && dryRun) return [null, false]; if (!promptManager.activeCharacter && dryRun) return [null, false];
@ -805,7 +782,19 @@ function prepareOpenAIMessages({
try { try {
// Merge markers and ordered user prompts with system prompts // Merge markers and ordered user prompts with system prompts
const prompts = preparePromptsForChatCompletion(Scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts, systemPromptOverride, jailbreakPromptOverride); const prompts = preparePromptsForChatCompletion({
Scenario,
charPersonality,
name2,
worldInfoBefore,
worldInfoAfter,
charDescription,
quietPrompt,
bias,
extensionPrompts,
systemPromptOverride,
jailbreakPromptOverride,
personaDescription});
// Fill the chat completion with as much context as the budget allows // Fill the chat completion with as much context as the budget allows
populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, type, cyclePrompt }); populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, type, cyclePrompt });
@ -2548,7 +2537,7 @@ function getMaxContextWindowAI(value) {
async function onModelChange() { async function onModelChange() {
biasCache = undefined; biasCache = undefined;
let value = String($(this).val()); let value = String($(this).val() || '');
if ($(this).is('#model_claude_select')) { if ($(this).is('#model_claude_select')) {
console.log('Claude model changed to', value); console.log('Claude model changed to', value);

View File

@ -20,7 +20,11 @@ import {
groups, groups,
resetSelectedGroup, resetSelectedGroup,
} from "./group-chats.js"; } from "./group-chats.js";
import { loadInstructMode } from "./instruct-mode.js"; import {
instruct_presets,
loadInstructMode,
selectInstructPreset,
} from "./instruct-mode.js";
import { registerSlashCommand } from "./slash-commands.js"; import { registerSlashCommand } from "./slash-commands.js";
import { tokenizers } from "./tokenizers.js"; import { tokenizers } from "./tokenizers.js";
@ -147,7 +151,7 @@ let power_user = {
max_context_unlocked: false, max_context_unlocked: false,
prefer_character_prompt: true, prefer_character_prompt: true,
prefer_character_jailbreak: true, prefer_character_jailbreak: true,
continue_on_send: false, quick_continue: false,
trim_spaces: true, trim_spaces: true,
relaxed_api_urls: false, relaxed_api_urls: false,
@ -193,7 +197,7 @@ let power_user = {
let themes = []; let themes = [];
let movingUIPresets = []; let movingUIPresets = [];
let context_presets = []; export let context_presets = [];
const storage_keys = { const storage_keys = {
fast_ui_mode: "TavernAI_fast_ui_mode", fast_ui_mode: "TavernAI_fast_ui_mode",
@ -704,7 +708,8 @@ function loadPowerUserSettings(settings, data) {
$('#relaxed_api_urls').prop("checked", power_user.relaxed_api_urls); $('#relaxed_api_urls').prop("checked", power_user.relaxed_api_urls);
$('#trim_spaces').prop("checked", power_user.trim_spaces); $('#trim_spaces').prop("checked", power_user.trim_spaces);
$('#continue_on_send').prop("checked", power_user.continue_on_send); $('#quick_continue').prop("checked", power_user.quick_continue);
$('#mes_continue').css('display', power_user.quick_continue ? '' : 'none');
$('#auto_swipe').prop("checked", power_user.auto_swipe); $('#auto_swipe').prop("checked", power_user.auto_swipe);
$('#auto_swipe_minimum_length').val(power_user.auto_swipe_minimum_length); $('#auto_swipe_minimum_length').val(power_user.auto_swipe_minimum_length);
$('#auto_swipe_blacklist').val(power_user.auto_swipe_blacklist.join(", ")); $('#auto_swipe_blacklist').val(power_user.auto_swipe_blacklist.join(", "));
@ -920,6 +925,15 @@ function loadContextSettings() {
} }
} }
}); });
// Select matching instruct preset
for (const instruct_preset of instruct_presets) {
// If instruct preset matches the context template
if (instruct_preset.name === name) {
selectInstructPreset(instruct_preset.name);
break;
}
}
}); });
} }
@ -1954,9 +1968,10 @@ $(document).ready(() => {
saveSettingsDebounced(); saveSettingsDebounced();
}); });
$("#continue_on_send").on("input", function () { $("#quick_continue").on("input", function () {
const value = !!$(this).prop('checked'); const value = !!$(this).prop('checked');
power_user.continue_on_send = value; power_user.quick_continue = value;
$("#mes_continue").css('display', value ? '' : 'none');
saveSettingsDebounced(); saveSettingsDebounced();
}); });

View File

@ -1156,8 +1156,8 @@ async function checkWorldInfo(chat, maxContext) {
} }
}); });
const worldInfoBefore = WIBeforeEntries.length ? `${WIBeforeEntries.join("\n")}\n` : ''; const worldInfoBefore = WIBeforeEntries.length ? WIBeforeEntries.join("\n") : '';
const worldInfoAfter = WIAfterEntries.length ? `${WIAfterEntries.join("\n")}\n` : ''; const worldInfoAfter = WIAfterEntries.length ? WIAfterEntries.join("\n") : '';
if (shouldWIAddPrompt) { if (shouldWIAddPrompt) {
const originalAN = context.extensionPrompts[NOTE_MODULE_NAME].value; const originalAN = context.extensionPrompts[NOTE_MODULE_NAME].value;

View File

@ -543,13 +543,16 @@ hr {
} }
#send_but { #send_but {
display: none;
order: 99999; order: 99999;
} }
#mes_continue {
order: 99998;
}
.mes_stop { .mes_stop {
display: none; display: none;
order: 99998; order: 99997;
} }
#options_button { #options_button {