mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
initial commit
This commit is contained in:
@@ -140,7 +140,7 @@
|
|||||||
<div data-preset-manager-import="novel" class="margin0 menu_button_icon menu_button" title="Import preset" data-i18n="[title]Import preset">
|
<div data-preset-manager-import="novel" class="margin0 menu_button_icon menu_button" title="Import preset" data-i18n="[title]Import preset">
|
||||||
<i class="fa-fw fa-solid fa-file-import"></i>
|
<i class="fa-fw fa-solid fa-file-import"></i>
|
||||||
</div>
|
</div>
|
||||||
<div data-preset-manager-export="novel" class="margin0 menu_button_icon menu_button" title="Export preset" data-i18n="[title]Export preset">
|
<div data-preset-manager-export="novel" class="margin0 menu_button_icon menu_button" title="Export preset" data-i18n="[title]Export preset">
|
||||||
<i class="fa-fw fa-solid fa-file-export"></i>
|
<i class="fa-fw fa-solid fa-file-export"></i>
|
||||||
</div>
|
</div>
|
||||||
<div data-preset-manager-delete="novel" class="margin0 menu_button_icon menu_button" title="Delete the preset" data-i18n="[title]Delete the preset">
|
<div data-preset-manager-delete="novel" class="margin0 menu_button_icon menu_button" title="Delete the preset" data-i18n="[title]Delete the preset">
|
||||||
@@ -663,7 +663,7 @@
|
|||||||
<div data-source="openrouter" class="range-block">
|
<div data-source="openrouter" class="range-block">
|
||||||
<div class="range-block-title" title="Allow compressing requests by removing messages from the middle of the prompt.">
|
<div class="range-block-title" title="Allow compressing requests by removing messages from the middle of the prompt.">
|
||||||
<span data-i18n="Middle-out Transform">Middle-out Transform</span>
|
<span data-i18n="Middle-out Transform">Middle-out Transform</span>
|
||||||
<a href="https://openrouter.ai/docs/transforms" target="_blank" rel="noreferrer noopener" class="note-link-span fa-solid fa-circle-question"></a>
|
<a href="https://openrouter.ai/docs/transforms" target="_blank" rel="noreferrer noopener" class="note-link-span fa-solid fa-circle-question"></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="wide100p">
|
<div class="wide100p">
|
||||||
<select id="openrouter_middleout" class="text_pole">
|
<select id="openrouter_middleout" class="text_pole">
|
||||||
@@ -3718,7 +3718,7 @@
|
|||||||
<input id="instruct_bind_to_context" type="checkbox" style="display:none;" />
|
<input id="instruct_bind_to_context" type="checkbox" style="display:none;" />
|
||||||
<small><i class="fa-solid fa-link menu_button margin0"></i></small>
|
<small><i class="fa-solid fa-link menu_button margin0"></i></small>
|
||||||
</label>
|
</label>
|
||||||
<label id="instruct_enabled_label"for="instruct_enabled" class="checkbox_label flex1" title="Enable Instruct Mode" data-i18n="[title]instruct_enabled">
|
<label id="instruct_enabled_label" for="instruct_enabled" class="checkbox_label flex1" title="Enable Instruct Mode" data-i18n="[title]instruct_enabled">
|
||||||
<input id="instruct_enabled" type="checkbox" style="display:none;" />
|
<input id="instruct_enabled" type="checkbox" style="display:none;" />
|
||||||
<small><i class="fa-solid fa-power-off menu_button togglable margin0"></i></small>
|
<small><i class="fa-solid fa-power-off menu_button togglable margin0"></i></small>
|
||||||
</label>
|
</label>
|
||||||
@@ -3762,6 +3762,10 @@
|
|||||||
<input id="instruct_skip_examples" type="checkbox" />
|
<input id="instruct_skip_examples" type="checkbox" />
|
||||||
<small data-i18n="Skip Example Dialogues Formatting">Skip Example Dialogues Formatting</small>
|
<small data-i18n="Skip Example Dialogues Formatting">Skip Example Dialogues Formatting</small>
|
||||||
</label>
|
</label>
|
||||||
|
<label for="instruct_skip_examples" class="checkbox_label">
|
||||||
|
<input id="instruct_collapse_same_entity" type="checkbox" />
|
||||||
|
<small data-i18n="Collapse Consecutive Same-Entity Sequences" title="If enabled, all consecutive messages with the same entity will be merged in prompt." data-i18n="[title]If enabled, all consecutive messages with the same entity will be merged in prompt.">Collapse Consecutive Same-Entity Sequences</small>
|
||||||
|
</label>
|
||||||
<div>
|
<div>
|
||||||
<small data-i18n="Include Names">
|
<small data-i18n="Include Names">
|
||||||
Include Names
|
Include Names
|
||||||
@@ -5166,7 +5170,7 @@
|
|||||||
<div class="flex-container wide100p alignitemscenter spaceBetween flexNoGap">
|
<div class="flex-container wide100p alignitemscenter spaceBetween flexNoGap">
|
||||||
<div class="flex-container alignItemsBaseline wide100p">
|
<div class="flex-container alignItemsBaseline wide100p">
|
||||||
<div class="flex1 flex-container alignItemsBaseline">
|
<div class="flex1 flex-container alignItemsBaseline">
|
||||||
<h3 class="margin0" >
|
<h3 class="margin0">
|
||||||
<span data-i18n="Persona Management">Persona Management</span>
|
<span data-i18n="Persona Management">Persona Management</span>
|
||||||
<a href="https://docs.sillytavern.app/usage/core-concepts/personas/" target="_blank">
|
<a href="https://docs.sillytavern.app/usage/core-concepts/personas/" target="_blank">
|
||||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||||
@@ -6576,7 +6580,7 @@
|
|||||||
<span id="completion_prompt_manager_popup_entry_source"></span>
|
<span id="completion_prompt_manager_popup_entry_source"></span>
|
||||||
</div>
|
</div>
|
||||||
<textarea id="completion_prompt_manager_popup_entry_form_prompt" class="text_pole" name="prompt" placeholder="The prompt to be sent." data-i18n="[placeholder]The prompt to be sent."></textarea>
|
<textarea id="completion_prompt_manager_popup_entry_form_prompt" class="text_pole" name="prompt" placeholder="The prompt to be sent." data-i18n="[placeholder]The prompt to be sent."></textarea>
|
||||||
</textarea>
|
</textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="completion_prompt_manager_popup_entry_form_footer">
|
<div class="completion_prompt_manager_popup_entry_form_footer">
|
||||||
<a id="completion_prompt_manager_popup_entry_form_close" title="close" data-i18n="[title]close" class="fa-solid fa-close menu_button"></a>
|
<a id="completion_prompt_manager_popup_entry_form_close" title="close" data-i18n="[title]close" class="fa-solid fa-close menu_button"></a>
|
||||||
@@ -7368,4 +7372,4 @@
|
|||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
158
public/script.js
158
public/script.js
@@ -3673,6 +3673,7 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy
|
|||||||
prompt = api == 'novel' ? adjustNovelInstructionPrompt(prompt) : prompt;
|
prompt = api == 'novel' ? adjustNovelInstructionPrompt(prompt) : prompt;
|
||||||
prompt = isInstruct ? formatInstructModeChat(name1, prompt, false, true, '', name1, name2, false) : prompt;
|
prompt = isInstruct ? formatInstructModeChat(name1, prompt, false, true, '', name1, name2, false) : prompt;
|
||||||
prompt = isInstruct ? (prompt + formatInstructModePrompt(name2, false, '', name1, name2, isQuiet, quietToLoud)) : (prompt + '\n');
|
prompt = isInstruct ? (prompt + formatInstructModePrompt(name2, false, '', name1, name2, isQuiet, quietToLoud)) : (prompt + '\n');
|
||||||
|
prompt = isInstruct && power_user.instruct.collapse_same_entity ? (collapseConsecutiveMessagesInPrompt(prompt, power_user.instruct)) : prompt;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (responseLengthCustomized) {
|
if (responseLengthCustomized) {
|
||||||
@@ -3872,6 +3873,159 @@ function removeLastMessage() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function collapseConsecutiveMessagesInPrompt(prompt, instruct) {
|
||||||
|
if (!instruct.enabled) {
|
||||||
|
console.warn('Instruct mode is disabled; returning original prompt');
|
||||||
|
return prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!instruct.collapse_same_entity) {
|
||||||
|
console.warn('collapse_same_entity is false; returning original prompt');
|
||||||
|
return prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn('Collapsing consecutive messages in prompt');
|
||||||
|
|
||||||
|
// Define prefixes and suffixes
|
||||||
|
const prefixes = {
|
||||||
|
system: instruct.system_sequence,
|
||||||
|
system_first: instruct.system_sequence_prefix,
|
||||||
|
user: instruct.input_sequence,
|
||||||
|
assistant: instruct.output_sequence,
|
||||||
|
};
|
||||||
|
const suffixes = {
|
||||||
|
system_first: instruct.system_sequence_suffix,
|
||||||
|
system: instruct.system_suffix,
|
||||||
|
user: instruct.input_suffix,
|
||||||
|
assistant: instruct.output_suffix,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Validate required settings
|
||||||
|
// Fail only if all prefixes and suffixes are empty strings
|
||||||
|
const allPrefixesEmpty = Object.values(prefixes).every(val => !val);
|
||||||
|
const allSuffixesEmpty = Object.values(suffixes).every(val => !val);
|
||||||
|
if (allPrefixesEmpty && allSuffixesEmpty) {
|
||||||
|
console.warn('All prefixes and suffixes are empty; returning original prompt', {
|
||||||
|
system_sequence: prefixes.system,
|
||||||
|
system_sequence_prefix: prefixes.system_first,
|
||||||
|
input_sequence: prefixes.user,
|
||||||
|
output_sequence: prefixes.assistant,
|
||||||
|
system_first_suffix: suffixes.system_first,
|
||||||
|
system_suffix: suffixes.system,
|
||||||
|
input_suffix: suffixes.user,
|
||||||
|
output_suffix: suffixes.assistant,
|
||||||
|
});
|
||||||
|
return prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape prefixes and suffixes for regex, handling empty strings
|
||||||
|
const escapeRegex = (str) => str ? str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') : '';
|
||||||
|
const prefixRegexStr = Object.values(prefixes)
|
||||||
|
.filter(val => val)
|
||||||
|
.map(escapeRegex)
|
||||||
|
.join('|') || '\\b';
|
||||||
|
const suffixRegexStr = Object.values(suffixes)
|
||||||
|
.filter(val => val)
|
||||||
|
.map(escapeRegex)
|
||||||
|
.join('|') || '\\b';
|
||||||
|
// Use lookahead to match content up to suffix or next prefix
|
||||||
|
const messageRegex = new RegExp(
|
||||||
|
`(${prefixRegexStr})([\\s\\S]*?)(?=(?:${suffixRegexStr}|${prefixRegexStr}|$))((?:${suffixRegexStr})?)`,
|
||||||
|
'g',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Parse the prompt into messages
|
||||||
|
const messages = [];
|
||||||
|
let match;
|
||||||
|
let lastIndex = 0;
|
||||||
|
|
||||||
|
console.warn('Parsing prompt with regex:', messageRegex);
|
||||||
|
while ((match = messageRegex.exec(prompt)) !== null) {
|
||||||
|
const [, prefix, content, suffix = ''] = match;
|
||||||
|
const prefixStart = match.index;
|
||||||
|
const gap = prompt.slice(lastIndex, prefixStart);
|
||||||
|
if (gap) {
|
||||||
|
console.warn(`Found gap between messages: ${JSON.stringify(gap)}`);
|
||||||
|
}
|
||||||
|
messages.push({ prefix, content: content.trim(), suffix, fullMatch: match[0], gap });
|
||||||
|
lastIndex = match.index + match[0].length;
|
||||||
|
console.warn(`Matched message: prefix=${prefix}, content=${content}, suffix=${suffix}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture any trailing text
|
||||||
|
if (lastIndex < prompt.length) {
|
||||||
|
const trailing = prompt.slice(lastIndex);
|
||||||
|
console.warn(`Found trailing text: ${JSON.stringify(trailing)}`);
|
||||||
|
// Check if trailing text starts with a prefix
|
||||||
|
const trailingMatch = trailing.match(new RegExp(`^(${prefixRegexStr})([\\s\\S]*?)(?=(?:${suffixRegexStr}|${prefixRegexStr}|$))((?:${suffixRegexStr})?)`));
|
||||||
|
if (trailingMatch) {
|
||||||
|
const [, prefix, content, suffix = ''] = trailingMatch;
|
||||||
|
messages.push({ prefix, content: content.trim(), suffix, fullMatch: trailingMatch[0], gap: '' });
|
||||||
|
console.warn(`Matched trailing message: prefix=${prefix}, content=${content}, suffix=${suffix}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messages.length === 0) {
|
||||||
|
console.warn('No messages parsed; returning original prompt');
|
||||||
|
return prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collapse consecutive messages with the same effective prefix
|
||||||
|
const collapsedMessages = [];
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
while (i < messages.length) {
|
||||||
|
const current = messages[i];
|
||||||
|
const effectivePrefix = (i === 0 && current.prefix === prefixes.system_first) ? prefixes.system : current.prefix;
|
||||||
|
let combinedContent = [current.content];
|
||||||
|
let currentSuffix = current.suffix;
|
||||||
|
let j = i + 1;
|
||||||
|
|
||||||
|
// Avoid collapsing the last complete assistant message if followed by an incomplete assistant message
|
||||||
|
const isLastCompleteAssistant = i === messages.length - 2 &&
|
||||||
|
messages[i].prefix === prefixes.assistant &&
|
||||||
|
messages[i + 1].prefix === prefixes.assistant;
|
||||||
|
|
||||||
|
if (!isLastCompleteAssistant) {
|
||||||
|
// Collect consecutive messages with the same effective prefix
|
||||||
|
while (j < messages.length) {
|
||||||
|
const next = messages[j];
|
||||||
|
const nextEffectivePrefix = (j === 0 && next.prefix === prefixes.system_first) ? prefixes.system : next.prefix;
|
||||||
|
|
||||||
|
if (nextEffectivePrefix !== effectivePrefix) {
|
||||||
|
console.warn(`Stopping collapse at index ${j}: effectivePrefix=${effectivePrefix}, nextEffectivePrefix=${nextEffectivePrefix}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
combinedContent.push(next.content);
|
||||||
|
currentSuffix = next.suffix; // Use the last suffix
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join contents with newlines
|
||||||
|
const joinedContent = combinedContent.reduce((acc, curr, idx) => {
|
||||||
|
if (idx === 0) return curr;
|
||||||
|
return acc + '\n' + curr;
|
||||||
|
}, '');
|
||||||
|
|
||||||
|
// Add newline after prefix if wrap is true and prefix doesn't end with newline
|
||||||
|
const prefixNewline = instruct.wrap && !current.prefix.match(/\n$/) ? '\n' : '';
|
||||||
|
|
||||||
|
// Create the collapsed message
|
||||||
|
const combinedMessage = `${current.prefix}${prefixNewline}${joinedContent}${currentSuffix}`;
|
||||||
|
collapsedMessages.push(combinedMessage);
|
||||||
|
console.warn(`Collapsed message at index ${i}: ${combinedMessage}`);
|
||||||
|
i = j; // Skip the combined messages
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reconstruct the prompt without extra newlines
|
||||||
|
const collapsedPrompt = collapsedMessages.join('');
|
||||||
|
console.warn('Before collapsing:', prompt);
|
||||||
|
console.warn('After collapsing:', collapsedPrompt);
|
||||||
|
return collapsedPrompt;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MARK:Generate()
|
* MARK:Generate()
|
||||||
* Runs a generation using the current chat context.
|
* Runs a generation using the current chat context.
|
||||||
@@ -4915,6 +5069,10 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
|
|||||||
|
|
||||||
showStopButton();
|
showStopButton();
|
||||||
|
|
||||||
|
if (isInstruct && power_user.instruct.collapse_same_entity) {
|
||||||
|
generate_data.prompt = collapseConsecutiveMessagesInPrompt(generate_data.prompt, power_user.instruct);
|
||||||
|
}
|
||||||
|
|
||||||
//set array object for prompt token itemization of this message
|
//set array object for prompt token itemization of this message
|
||||||
let currentArrayEntry = Number(thisPromptBits.length - 1);
|
let currentArrayEntry = Number(thisPromptBits.length - 1);
|
||||||
let additionalPromptStuff = {
|
let additionalPromptStuff = {
|
||||||
|
@@ -43,6 +43,7 @@ const controls = [
|
|||||||
{ id: 'instruct_derived', property: 'derived', isCheckbox: true },
|
{ id: 'instruct_derived', property: 'derived', isCheckbox: true },
|
||||||
{ id: 'instruct_bind_to_context', property: 'bind_to_context', isCheckbox: true },
|
{ id: 'instruct_bind_to_context', property: 'bind_to_context', isCheckbox: true },
|
||||||
{ id: 'instruct_skip_examples', property: 'skip_examples', isCheckbox: true },
|
{ id: 'instruct_skip_examples', property: 'skip_examples', isCheckbox: true },
|
||||||
|
{ id: 'instruct_collapse_same_entity', property: 'collapse_same_entity', isCheckbox: true },
|
||||||
{ id: 'instruct_names_behavior', property: 'names_behavior', isCheckbox: false },
|
{ id: 'instruct_names_behavior', property: 'names_behavior', isCheckbox: false },
|
||||||
{ id: 'instruct_system_same_as_user', property: 'system_same_as_user', isCheckbox: true, trigger: true },
|
{ id: 'instruct_system_same_as_user', property: 'system_same_as_user', isCheckbox: true, trigger: true },
|
||||||
];
|
];
|
||||||
|
Reference in New Issue
Block a user