mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
#790 Simplify local prompt formatting. Use handlebars to render story string.
This commit is contained in:
@ -1,214 +0,0 @@
|
||||
import {
|
||||
callPopup,
|
||||
getRequestHeaders,
|
||||
saveSettingsDebounced,
|
||||
} from '../script.js';
|
||||
import { debounce } from './utils.js';
|
||||
|
||||
export let context_templates = [];
|
||||
export let context_settings = {
|
||||
selected_template: '',
|
||||
};
|
||||
|
||||
const saveTemplateDebounced = debounce((name) => alert('implement me', name), 2000);
|
||||
|
||||
export function loadContextTemplatesFromSettings(data, settings) {
|
||||
context_templates = data.context || [];
|
||||
context_settings = Object.assign(context_settings, (settings.context_settings || {}));
|
||||
|
||||
const dropdown = $('#context_template');
|
||||
dropdown.empty();
|
||||
dropdown.append('<option value="">-- None --</option>')
|
||||
|
||||
for (const template of context_templates) {
|
||||
const name = template.name;
|
||||
const option = document.createElement('option');
|
||||
option.innerText = name;
|
||||
option.value = name;
|
||||
option.selected = context_settings.selected_template == name;
|
||||
dropdown.append(option);
|
||||
}
|
||||
}
|
||||
|
||||
function onContextTemplateChange() {
|
||||
const value = $(this).find(':selected').val();
|
||||
context_settings.selected_template = value;
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function openContextTemplateEditor() {
|
||||
const template = context_templates.find(x => x.name == context_settings.selected_template);
|
||||
|
||||
if (!template || !context_settings.selected_template) {
|
||||
toastr.info('No context template selected');
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = $('#context_editor_template .context_editor').clone();
|
||||
const injectionsContainer = editor.find('.chat_injections_list');
|
||||
editor.find('.template_name').text(template.name);
|
||||
editor.find('.story_string_template').text(template.storyString).on('input', function () {
|
||||
const value = $(this).val();
|
||||
template.storyString = value;
|
||||
saveTemplateDebounced(template.name);
|
||||
});
|
||||
editor.find('.chat_injection_add').on('click', function () {
|
||||
const injection = { id: Date.now(), text: '', depth: 0 };
|
||||
template.injections.push(injection);
|
||||
addChatInjection(injectionsContainer, injection, template);
|
||||
saveTemplateDebounced(template.name);
|
||||
});
|
||||
|
||||
for (const injection of template.injections) {
|
||||
addChatInjection(injectionsContainer, injection, template);
|
||||
}
|
||||
|
||||
$('#dialogue_popup').addClass('large_dialogue_popup wide_dialogue_popup');
|
||||
callPopup(editor, 'text');
|
||||
}
|
||||
|
||||
async function onRenameContextTemplateClick() {
|
||||
const oldName = context_settings.selected_template;
|
||||
const newName = await inputTemplateName();
|
||||
const template = context_templates.find(x => x.name === oldName);
|
||||
|
||||
if (!template || !newName || oldName === newName) {
|
||||
return;
|
||||
}
|
||||
|
||||
await saveContextTemplate(newName);
|
||||
context_settings.selected_template = newName;
|
||||
saveSettingsDebounced();
|
||||
await deleteContextTemplate(oldName);
|
||||
toastr.success('Context template renamed', newName);
|
||||
}
|
||||
|
||||
async function deleteContextTemplate(name) {
|
||||
const response = await fetch('/delete_context_template', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ name }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Context template not deleted');
|
||||
}
|
||||
}
|
||||
|
||||
async function saveContextTemplate(name) {
|
||||
const template = context_templates.find(x => x.name === name);
|
||||
|
||||
if (!template) {
|
||||
throw new Error(`Context template not found: ${name}`);
|
||||
}
|
||||
|
||||
const response = await fetch('/save_context_template', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ name, template }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Context template not saved');
|
||||
}
|
||||
}
|
||||
|
||||
async function inputTemplateName() {
|
||||
let name = await callPopup('Enter a template name:', 'input');
|
||||
|
||||
if (!name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
name = DOMPurify.sanitize(name.trim());
|
||||
|
||||
if (context_templates.findIndex(x => x.name == name) > -1) {
|
||||
toastr.warning('Template with that name already exists', 'Pick a unique name');
|
||||
return false;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
function addChatInjection(container, model, parent) {
|
||||
const template = $('#chat_injection_template .chat_injection').clone();
|
||||
template.attr('id', model.id);
|
||||
template.find('.chat_injection_text').val(model.text).on('input', function () {
|
||||
const value = $(this).val();
|
||||
model.text = value;
|
||||
saveTemplateDebounced(parent.name);
|
||||
});
|
||||
template.find('.chat_injection_depth').val(model.depth).on('input', function () {
|
||||
const value = Math.abs(Number($(this).val()));
|
||||
model.depth = value;
|
||||
saveTemplateDebounced(parent.name);
|
||||
});
|
||||
template.find('.chat_injection_remove').on('click', function () {
|
||||
if (!confirm('Are you sure?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const index = parent.injections.findIndex(x => x == model);
|
||||
|
||||
if (index === -1) {
|
||||
console.error('Does not compute, injection index was lost');
|
||||
return;
|
||||
}
|
||||
|
||||
parent.injections.splice(index, 1);
|
||||
template.remove();
|
||||
saveTemplateDebounced(parent.name);
|
||||
});
|
||||
container.append(template);
|
||||
}
|
||||
|
||||
function copyTemplateParameter(event) {
|
||||
const text = $(event.target).text();
|
||||
navigator.clipboard.writeText(text);
|
||||
toastr.info('Copied!', '', { timeOut: 2000 });
|
||||
}
|
||||
|
||||
async function onNewContextTemplateClick() {
|
||||
const name = await inputTemplateName();
|
||||
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const template = { name: name, injections: [], storyString: '' };
|
||||
context_templates.push(template);
|
||||
const option = document.createElement('option');
|
||||
option.innerText = name;
|
||||
option.value = name;
|
||||
option.selected = true;
|
||||
$('#context_template').append(option).val(name).trigger('change');
|
||||
saveTemplateDebounced(name);
|
||||
}
|
||||
|
||||
async function onDeleteContextTemplateClick() {
|
||||
const template = context_templates.find(x => x.name == context_settings.selected_template);
|
||||
|
||||
if (!template || !context_settings.selected_template) {
|
||||
toastr.info('No context template selected');
|
||||
return;
|
||||
}
|
||||
|
||||
const confirm = await callPopup('Are you sure?', 'confirm');
|
||||
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
|
||||
await deleteContextTemplate(context_settings.selected_template);
|
||||
$(`#context_template option[value="${context_settings.selected_template}"]`).remove();
|
||||
$('#context_template').trigger('change');
|
||||
}
|
||||
|
||||
jQuery(() => {
|
||||
$('#context_template_edit').on('click', openContextTemplateEditor);
|
||||
$('#context_template').on('change', onContextTemplateChange);
|
||||
$('#context_template_new').on('click', onNewContextTemplateClick);
|
||||
$('#context_template_rename').on('click', onRenameContextTemplateClick);
|
||||
$('#context_template_delete').on('click', onDeleteContextTemplateClick);
|
||||
$(document).on('pointerup', '.template_parameters_list code', copyTemplateParameter);
|
||||
})
|
5972
public/scripts/handlebars.js
Normal file
5972
public/scripts/handlebars.js
Normal file
File diff suppressed because one or more lines are too long
@ -644,7 +644,6 @@ $(document).ready(function () {
|
||||
const formattedValue = slider.format(value);
|
||||
slider.setValue(value);
|
||||
$(slider.counterId).text(formattedValue);
|
||||
console.log('saving');
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
});
|
||||
|
@ -46,6 +46,12 @@ export {
|
||||
export const MAX_CONTEXT_DEFAULT = 4096;
|
||||
const MAX_CONTEXT_UNLOCKED = 65536;
|
||||
|
||||
const defaultStoryString = `{{#if description}}{{{description}}}{{/if}}
|
||||
{{#if personality}}{{{personality}}}{{/if}}
|
||||
{{#if scenario}}Scenario: {{{scenario}}}{{/if}}`;
|
||||
const defaultExampleSeparator = '***';
|
||||
const defaultChatStart = '***';
|
||||
|
||||
const avatar_styles = {
|
||||
ROUND: 0,
|
||||
RECTANGULAR: 1,
|
||||
@ -93,11 +99,6 @@ let power_user = {
|
||||
collapse_newlines: false,
|
||||
pygmalion_formatting: pygmalion_options.AUTO,
|
||||
pin_examples: false,
|
||||
disable_description_formatting: false,
|
||||
disable_scenario_formatting: false,
|
||||
disable_personality_formatting: false,
|
||||
disable_examples_formatting: false,
|
||||
disable_start_formatting: false,
|
||||
trim_sentences: false,
|
||||
include_newline: false,
|
||||
always_force_name2: false,
|
||||
@ -106,7 +107,6 @@ let power_user = {
|
||||
multigen: false,
|
||||
multigen_first_chunk: 50,
|
||||
multigen_next_chunks: 30,
|
||||
custom_chat_separator: '',
|
||||
markdown_escape_strings: '',
|
||||
|
||||
fast_ui_mode: true,
|
||||
@ -182,6 +182,13 @@ let power_user = {
|
||||
names_force_groups: true,
|
||||
},
|
||||
|
||||
context: {
|
||||
preset: 'Default',
|
||||
story_string: defaultStoryString,
|
||||
chat_start: defaultChatStart,
|
||||
example_separator: defaultExampleSeparator,
|
||||
},
|
||||
|
||||
personas: {},
|
||||
default_persona: null,
|
||||
persona_descriptions: {},
|
||||
@ -200,6 +207,7 @@ let power_user = {
|
||||
let themes = [];
|
||||
let movingUIPresets = [];
|
||||
let instruct_presets = [];
|
||||
let context_presets = [];
|
||||
|
||||
const storage_keys = {
|
||||
ui_language: "language",
|
||||
@ -662,6 +670,10 @@ function loadPowerUserSettings(settings, data) {
|
||||
instruct_presets = data.instruct;
|
||||
}
|
||||
|
||||
if (data.context !== undefined) {
|
||||
context_presets = data.context;
|
||||
}
|
||||
|
||||
// These are still local storage
|
||||
const fastUi = localStorage.getItem(storage_keys.fast_ui_mode);
|
||||
const movingUI = localStorage.getItem(storage_keys.movingUI);
|
||||
@ -720,16 +732,10 @@ function loadPowerUserSettings(settings, data) {
|
||||
$("#spoiler_free_mode").prop("checked", power_user.spoiler_free_mode);
|
||||
$("#collapse-newlines-checkbox").prop("checked", power_user.collapse_newlines);
|
||||
$("#pin-examples-checkbox").prop("checked", power_user.pin_examples);
|
||||
$("#disable-description-formatting-checkbox").prop("checked", power_user.disable_description_formatting);
|
||||
$("#disable-scenario-formatting-checkbox").prop("checked", power_user.disable_scenario_formatting);
|
||||
$("#disable-personality-formatting-checkbox").prop("checked", power_user.disable_personality_formatting);
|
||||
$("#always-force-name2-checkbox").prop("checked", power_user.always_force_name2);
|
||||
$("#disable-examples-formatting-checkbox").prop("checked", power_user.disable_examples_formatting);
|
||||
$('#disable-start-formatting-checkbox').prop("checked", power_user.disable_start_formatting);
|
||||
$("#trim_sentences_checkbox").prop("checked", power_user.trim_sentences);
|
||||
$("#include_newline_checkbox").prop("checked", power_user.include_newline);
|
||||
$('#render_formulas').prop("checked", power_user.render_formulas);
|
||||
$("#custom_chat_separator").val(power_user.custom_chat_separator);
|
||||
$("#markdown_escape_strings").val(power_user.markdown_escape_strings);
|
||||
$("#fast_ui_mode").prop("checked", power_user.fast_ui_mode);
|
||||
$("#waifuMode").prop("checked", power_user.waifuMode);
|
||||
@ -799,6 +805,7 @@ function loadPowerUserSettings(settings, data) {
|
||||
sortCharactersList();
|
||||
reloadMarkdownProcessor(power_user.render_formulas);
|
||||
loadInstructMode();
|
||||
loadContextSettings();
|
||||
loadMaxContextUnlocked();
|
||||
switchWaifuMode();
|
||||
switchSpoilerMode();
|
||||
@ -867,6 +874,61 @@ function switchMaxContextSize() {
|
||||
}
|
||||
}
|
||||
|
||||
function loadContextSettings() {
|
||||
const controls = [
|
||||
{ id: "context_story_string", property: "story_string", isCheckbox: false },
|
||||
{ id: "context_example_separator", property: "example_separator", isCheckbox: false },
|
||||
{ id: "context_chat_start", property: "chat_start", isCheckbox: false },
|
||||
];
|
||||
|
||||
controls.forEach(control => {
|
||||
const $element = $(`#${control.id}`);
|
||||
|
||||
if (control.isCheckbox) {
|
||||
$element.prop('checked', power_user.context[control.property]);
|
||||
} else {
|
||||
$element.val(power_user.context[control.property]);
|
||||
}
|
||||
|
||||
$element.on('input', function () {
|
||||
power_user.context[control.property] = control.isCheckbox ? !!$(this).prop('checked') : $(this).val();
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
});
|
||||
|
||||
context_presets.forEach((preset) => {
|
||||
const name = preset.name;
|
||||
const option = document.createElement('option');
|
||||
option.value = name;
|
||||
option.innerText = name;
|
||||
option.selected = name === power_user.context.preset;
|
||||
$('#context_presets').append(option);
|
||||
});
|
||||
|
||||
$('#context_presets').on('change', function () {
|
||||
const name = $(this).find(':selected').val();
|
||||
const preset = context_presets.find(x => x.name === name);
|
||||
|
||||
if (!preset) {
|
||||
return;
|
||||
}
|
||||
|
||||
power_user.context.preset = name;
|
||||
controls.forEach(control => {
|
||||
if (preset[control.property] !== undefined) {
|
||||
power_user.context[control.property] = preset[control.property];
|
||||
const $element = $(`#${control.id}`);
|
||||
|
||||
if (control.isCheckbox) {
|
||||
$element.prop('checked', power_user.context[control.property]).trigger('input');
|
||||
} else {
|
||||
$element.val(power_user.context[control.property]).trigger('input');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadInstructMode() {
|
||||
const controls = [
|
||||
{ id: "instruct_enabled", property: "enabled", isCheckbox: true },
|
||||
@ -976,6 +1038,20 @@ export function fuzzySearchGroups(searchValue) {
|
||||
return ids;
|
||||
}
|
||||
|
||||
export function renderStoryString(params) {
|
||||
try {
|
||||
const compiledTemplate = Handlebars.compile(power_user.context.story_string);
|
||||
let output = compiledTemplate(params);
|
||||
output = substituteParams(output, params.user, params.char);
|
||||
output = `${output.trim()}\n`; // add a newline to the end
|
||||
return output;
|
||||
} catch (e) {
|
||||
toastr.error('Check the story string template for validity', 'Error rendering story string');
|
||||
console.error('Error rendering story string', e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
export function formatInstructModeChat(name, mes, isUser, isNarrator, forceAvatar, name1, name2) {
|
||||
let includeNames = isNarrator ? false : power_user.instruct.names;
|
||||
|
||||
@ -1639,31 +1715,6 @@ $(document).ready(() => {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$("#disable-description-formatting-checkbox").change(function () {
|
||||
power_user.disable_description_formatting = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
})
|
||||
|
||||
$("#disable-scenario-formatting-checkbox").change(function () {
|
||||
power_user.disable_scenario_formatting = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$("#disable-personality-formatting-checkbox").change(function () {
|
||||
power_user.disable_personality_formatting = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$("#disable-examples-formatting-checkbox").change(function () {
|
||||
power_user.disable_examples_formatting = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
})
|
||||
|
||||
$("#disable-start-formatting-checkbox").change(function () {
|
||||
power_user.disable_start_formatting = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
// include newline is the child of trim sentences
|
||||
// if include newline is checked, trim sentences must be checked
|
||||
// if trim sentences is unchecked, include newline must be unchecked
|
||||
@ -1690,12 +1741,6 @@ $(document).ready(() => {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$("#custom_chat_separator").on('input', function () {
|
||||
power_user.custom_chat_separator = $(this).val();
|
||||
saveSettingsDebounced();
|
||||
reloadMarkdownProcessor(power_user.render_formulas);
|
||||
});
|
||||
|
||||
$("#markdown_escape_strings").on('input', function () {
|
||||
power_user.markdown_escape_strings = $(this).val();
|
||||
saveSettingsDebounced();
|
||||
|
@ -8,8 +8,12 @@ export const markdownExclusionExt = () => {
|
||||
}
|
||||
|
||||
let combinedExcludeString = '';
|
||||
if (power_user.custom_chat_separator) {
|
||||
combinedExcludeString += `${power_user.custom_chat_separator},`;
|
||||
if (power_user.context.chat_start) {
|
||||
combinedExcludeString += `${power_user.context.chat_start},`;
|
||||
}
|
||||
|
||||
if (power_user.context.example_separator) {
|
||||
combinedExcludeString += `${power_user.context.example_separator},`;
|
||||
}
|
||||
|
||||
if (power_user.markdown_escape_strings) {
|
||||
|
Reference in New Issue
Block a user