Merge branch 'staging' of https://github.com/Cohee1207/SillyTavern into staging

This commit is contained in:
RossAscends
2023-10-26 13:20:49 +09:00
8 changed files with 254 additions and 47 deletions

View File

@ -78,6 +78,7 @@
#rm_group_members:empty {
width: 100%;
padding: 0.5em 0;
}
#rm_group_members:empty::before {

View File

@ -530,6 +530,36 @@
</div>
</div>
</div>
<div data-newbie-hidden class="range-block">
<div class="range-block-title" data-i18n="Frequency Penalty">
Frequency Penalty
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input type="range" id="freq_pen_textgenerationwebui" name="volume" min="-2" max="2" step="0.01" />
</div>
<div class="range-block-counter">
<div contenteditable="true" data-for="freq_pen_textgenerationwebui" id="freq_pen_counter_textgenerationwebui">
select
</div>
</div>
</div>
</div>
<div data-newbie-hidden class="range-block">
<div class="range-block-title" data-i18n="Presence Penalty">
Presence Penalty
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input type="range" id="presence_pen_textgenerationwebui" name="volume" min="-2" max="2" step="0.01" />
</div>
<div class="range-block-counter">
<div contenteditable="true" data-for="presence_pen_textgenerationwebui" id="presence_pen_counter_textgenerationwebui">
select
</div>
</div>
</div>
</div>
<div data-newbie-hidden class="range-block">
<div class="range-block-title" data-i18n="No Repeat Ngram Size">
No Repeat Ngram Size
@ -3529,7 +3559,12 @@
<div class="inline-drawer wide100p flexFlowColumn">
<div id="groupControlsToggle" class="inline-drawer-toggle inline-drawer-header">
<span>
<span data-i18n="Group Controls">Group Controls</span>
<a href="https://docs.sillytavern.app/usage/core-concepts/groupchats/" class="notes-link" target="_blank">
<span class="note-link-span">?</span>
</a>
</span>
<div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
</div>
<div class="inline-drawer-content">
@ -3545,7 +3580,7 @@
</div>
<div id="groupTagList" class="tags paddingTopBot5"></div>
</div>
<div id="rm_group_top_bar" class="flex-container alignitemscenter spaceBetween width100p">
<div id="rm_group_top_bar" class="flex-container alignitemscenter spaceBetween width100p fontsize80p">
<div>
<label class="add_avatar avatar flex-container justifyCenter" for="group_avatar_button" title="Click to select a new avatar for this group" data-i18n="[title]Click to select a new avatar for this group">
<div id="group_avatar_preview">
@ -3556,32 +3591,24 @@
<input hidden type="file" id="group_avatar_button" name="avatar" accept="image/png, image/jpeg, image/jpg, image/gif, image/bmp">
</label>
</div>
<div name="GroupStragegyAndOrder" id="rm_group_buttons" class="fontsize80p flex-container paddingLeftRight5 flex2">
<div class="">
<div name="GroupStragegyAndOrder" id="rm_group_buttons" class="flex-container paddingLeftRight5 flex2">
<div class="flex1 flexGap5">
<div class="flex-container flexnowrap width100p whitespacenowrap">
<span data-i18n="Group reply strategy">Group reply strategy</span>
<a href="https://docs.sillytavern.app/usage/core-concepts/groupchats/" class="notes-link" target="_blank">
<span class="note-link-span">?</span>
</a>
</div>
<label class="checkbox_label flexnowrap whitespacenowrap">
<input type="radio" name="rm_group_activation_strategy" value="0" />
<span data-i18n="Natural order">Natural order</span>
</label>
<label class="checkbox_label flexnowrap whitespacenowrap">
<input type="radio" name="rm_group_activation_strategy" value="1" />
<span data-i18n="List order">List order</span>
</label>
<select id="rm_group_activation_strategy">
<option value="0" data-i18n="Natural order">Natural order</option>
<option value="1" data-i18n="List order">List order</option>
</select>
</div>
<div class="">
<label class="checkbox_label whitespacenowrap">
<input id="rm_group_allow_self_responses" type="checkbox" />
<span data-i18n="Allow self responses">Allow self responses</span>
</label>
<label id="rm_group_automode_label" class="checkbox_label whitespacenowrap">
<input id="rm_group_automode" type="checkbox" />
<span data-i18n="Auto Mode">Auto Mode</span>
</label>
<div class="flex1 flexGap5">
<div class="flex-container flexnowrap width100p whitespacenowrap">
<span data-i18n="Group generation handling mode">Group generation handling mode</span>
</div>
<select id="rm_group_generation_mode">
<option value="0" data-i18n="Swap character cards">Swap character cards</option>
<option value="1" data-i18n="Join character cards">Join character cards</option>
</select>
</div>
</div>
<div id="GroupFavDelOkBack" class="flex-container flexGap5 spaceEvenly flex1">
@ -3592,6 +3619,17 @@
<div id="rm_group_submit" class="heightFitContent margin0 menu_button fa-solid fa-check" title="Create" data-i18n="[title]Create"></div>
<div id="rm_group_restore_avatar" class="heightFitContent margin0 menu_button fa-solid fa-images" title="Restore collage avatar" data-i18n="[title]Restore collage avatar"></div>
<div id="rm_group_delete" class="heightFitContent margin0 menu_button fa-solid fa-trash-can" title="Delete" data-i18n="[title]Delete"></div>
<div class="flex1">
<label class="checkbox_label whitespacenowrap">
<input id="rm_group_allow_self_responses" type="checkbox" />
<span data-i18n="Allow self responses">Allow self responses</span>
</label>
<label id="rm_group_automode_label" class="checkbox_label whitespacenowrap">
<input id="rm_group_automode" type="checkbox" />
<span data-i18n="Auto Mode">Auto Mode</span>
</label>
</div>
</div>
</div>

View File

@ -59,6 +59,8 @@ import {
importGroupChat,
getGroupBlock,
getGroupChatNames,
getGroupCharacterCards,
getGroupDepthPrompts,
} from "./scripts/group-chats.js";
import {
@ -598,7 +600,7 @@ function getCurrentChatId() {
}
const talkativeness_default = 0.5;
const depth_prompt_depth_default = 4;
export const depth_prompt_depth_default = 4;
const per_page_default = 50;
var is_advanced_char_open = false;
@ -2036,7 +2038,7 @@ function getExtensionPrompt(position = 0, depth = undefined, separator = "\n") {
return extension_prompt;
}
function baseChatReplace(value, name1, name2) {
export function baseChatReplace(value, name1, name2) {
if (value !== undefined && value.length > 0) {
value = substituteParams(value, name1, name2);
@ -2539,21 +2541,41 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
const scenarioText = chat_metadata['scenario'] || characters[this_chid].scenario;
let charDescription = baseChatReplace(characters[this_chid].description.trim(), name1, name2);
let charPersonality = baseChatReplace(characters[this_chid].personality.trim(), name1, name2);
let personaDescription = baseChatReplace(power_user.persona_description.trim(), name1, name2);
let Scenario = baseChatReplace(scenarioText.trim(), name1, name2);
let scenario = baseChatReplace(scenarioText.trim(), name1, name2);
let mesExamples = baseChatReplace(characters[this_chid].mes_example.trim(), name1, name2);
let systemPrompt = power_user.prefer_character_prompt ? baseChatReplace(characters[this_chid].data?.system_prompt?.trim(), name1, name2) : '';
let jailbreakPrompt = power_user.prefer_character_jailbreak ? baseChatReplace(characters[this_chid].data?.post_history_instructions?.trim(), name1, name2) : '';
let personaDescription = baseChatReplace(power_user.persona_description.trim(), name1, name2);
if (isInstruct) {
systemPrompt = power_user.prefer_character_prompt && systemPrompt ? systemPrompt : baseChatReplace(power_user.instruct.system_prompt, name1, name2);
systemPrompt = formatInstructModeSystemPrompt(substituteParams(systemPrompt, name1, name2, power_user.instruct.system_prompt));
}
if (selected_group) {
const groupCards = getGroupCharacterCards(selected_group, Number(this_chid));
if (groupCards) {
charDescription = groupCards.description;
charPersonality = groupCards.personality;
scenario = groupCards.scenario;
mesExamples = groupCards.mesExample;
}
}
// Depth prompt (character-specific A/N)
removeDepthPrompts();
const groupDepthPrompts = getGroupDepthPrompts(selected_group, Number(this_chid));
if (selected_group && Array.isArray(groupDepthPrompts) && groupDepthPrompts.length > 0) {
groupDepthPrompts.forEach((value, index) => {
setExtensionPrompt('DEPTH_PROMPT_' + index, value.text, extension_prompt_types.IN_CHAT, value.depth);
});
} else {
const depthPromptText = baseChatReplace(characters[this_chid].data?.extensions?.depth_prompt?.prompt?.trim(), name1, name2) || '';
const depthPromptDepth = characters[this_chid].data?.extensions?.depth_prompt?.depth ?? depth_prompt_depth_default;
setExtensionPrompt('DEPTH_PROMPT', depthPromptText, extension_prompt_types.IN_CHAT, depthPromptDepth);
}
// Parse example messages
if (!mesExamples.startsWith('<START>')) {
@ -2682,7 +2704,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
description: charDescription,
personality: charPersonality,
persona: personaDescription,
scenario: Scenario,
scenario: scenario,
system: isInstruct ? systemPrompt : '',
char: name2,
user: name1,
@ -3112,7 +3134,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
name2: name2,
charDescription: charDescription,
charPersonality: charPersonality,
Scenario: Scenario,
Scenario: scenario,
worldInfoBefore: worldInfoBefore,
worldInfoAfter: worldInfoAfter,
extensionPrompts: extension_prompts,
@ -5750,6 +5772,18 @@ export function setExtensionPrompt(key, value, position, depth) {
extension_prompts[key] = { value: String(value), position: Number(position), depth: Number(depth) };
}
/**
* Removes all char A/N prompt injections from the chat.
* To clean up when switching from groups to solo and vice versa.
*/
export function removeDepthPrompts() {
for (const key of Object.keys(extension_prompts)) {
if (key.startsWith('DEPTH_PROMPT')) {
delete extension_prompts[key];
}
}
}
/**
* Adds or updates the metadata for the currently active chat.
* @param {Object} newValues An object with collection of new values to be added into the metadata.
@ -5777,15 +5811,14 @@ export function setScenarioOverride() {
const isGroup = !!selected_group;
template.find('[data-group="true"]').toggle(isGroup);
template.find('[data-character="true"]').toggle(!isGroup);
template.find('.chat_scenario').text(metadataValue).on('input', onScenarioOverrideInput);
template.find('.chat_scenario').val(metadataValue).on('input', onScenarioOverrideInput);
template.find('.remove_scenario_override').on('click', onScenarioOverrideRemoveClick);
callPopup(template, 'text');
}
function onScenarioOverrideInput() {
const value = $(this).val();
const metadata = { scenario: value, };
updateChatMetadata(metadata, false);
const value = String($(this).val());
chat_metadata['scenario'] = value;
saveMetadataDebounced();
}

View File

@ -16,6 +16,10 @@
<input id="sd_expand" type="checkbox" />
Auto-enhance prompts
</label>
<small>
This option uses an additional GPT-2 text generation model to add more details to the prompt generated by the main API.
Works best for SDXL image models. May not work well with other models, it is recommended to manually edit prompts in this case.
</small>
<label for="sd_source">Source</label>
<select id="sd_source">
<option value="extras">Extras API (local / remote)</option>

View File

@ -66,6 +66,8 @@ import {
system_avatar,
isChatSaving,
setExternalAbortController,
baseChatReplace,
depth_prompt_depth_default,
} from "../script.js";
import { appendTagToList, createTagMapFromList, getTagsList, applyTagsOnCharacterSelect, tag_map, printTagFilters } from './tags.js';
import { FILTER_TYPES, FilterHelper } from './filters.js';
@ -101,6 +103,11 @@ export const group_activation_strategy = {
LIST: 1,
};
export const group_generation_mode = {
SWAP: 0,
APPEND: 1,
}
export const groupCandidatesFilter = new FilterHelper(debounce(printGroupCandidates, 100));
const groupAutoModeInterval = setInterval(groupChatAutoModeWorker, 5000);
const saveGroupDebounced = debounce(async (group, reload) => await _save(group, reload), 500);
@ -193,6 +200,104 @@ export async function getGroupChat(groupId) {
eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId());
}
/**
* Gets depth prompts for group members.
* @param {string} groupId Group ID
* @param {number} characterId Current Character ID
* @returns {{depth: number, text: string}[]} Array of depth prompts
*/
export function getGroupDepthPrompts(groupId, characterId) {
if (!groupId) {
return [];
}
console.debug('getGroupDepthPrompts entered for group: ', groupId);
const group = groups.find(x => x.id === groupId);
if (!group || !Array.isArray(group.members) || !group.members.length) {
return [];
}
if (group.generation_mode === group_generation_mode.SWAP) {
return [];
}
const depthPrompts = [];
for (const member of group.members) {
const index = characters.findIndex(x => x.avatar === member);
const character = characters[index];
if (index === -1 || !character) {
console.debug(`Skipping missing member: ${member}`);
continue;
}
if (group.disabled_members.includes(member) && characterId !== index) {
console.debug(`Skipping disabled group member: ${member}`);
continue;
}
const depthPromptText = baseChatReplace(character.data?.extensions?.depth_prompt?.prompt?.trim(), name1, character.name) || '';
const depthPromptDepth = character.data?.extensions?.depth_prompt?.depth ?? depth_prompt_depth_default;
if (depthPromptText) {
depthPrompts.push({ text: depthPromptText, depth: depthPromptDepth });
}
}
return depthPrompts;
}
/**
* Combines group members info a single string. Only for groups with generation mode set to APPEND.
* @param {string} groupId Group ID
* @param {number} characterId Current Character ID
* @returns {{description: string, personality: string, scenario: string, mesExample: string}} Group character cards combined
*/
export function getGroupCharacterCards(groupId, characterId) {
console.debug('getGroupCharacterCards entered for group: ', groupId);
const group = groups.find(x => x.id === groupId);
if (!group || group?.generation_mode !== group_generation_mode.APPEND || !Array.isArray(group.members) || !group.members.length) {
return null;
}
const scenarioOverride = chat_metadata['scenario'];
let descriptions = [];
let personalities = [];
let scenarios = [];
let mesExamples = [];
for (const member of group.members) {
const index = characters.findIndex(x => x.avatar === member);
const character = characters[index];
if (index === -1 || !character) {
console.debug(`Skipping missing member: ${member}`);
continue;
}
if (group.disabled_members.includes(member) && characterId !== index) {
console.debug(`Skipping disabled group member: ${member}`);
continue;
}
descriptions.push(baseChatReplace(character.description.trim(), name1, character.name));
personalities.push(baseChatReplace(character.personality.trim(), name1, character.name));
scenarios.push(baseChatReplace(character.scenario.trim(), name1, character.name));
mesExamples.push(baseChatReplace(character.mes_example.trim(), name1, character.name));
}
const description = descriptions.join('\n');
const personality = personalities.join('\n');
const scenario = scenarioOverride?.trim() || scenarios.join('\n');
const mesExample = mesExamples.join('\n');
return { description, personality, scenario, mesExample };
}
function getFirstCharacterMessage(character) {
let messageText = character.first_mes;
@ -922,6 +1027,14 @@ async function onGroupActivationStrategyInput(e) {
}
}
async function onGroupGenerationModeInput(e) {
if (openGroupId) {
let _thisGroup = groups.find((x) => x.id == openGroupId);
_thisGroup.generation_mode = Number(e.target.value);
await editGroup(openGroupId, false, false);
}
}
async function onGroupNameInput() {
if (openGroupId) {
let _thisGroup = groups.find((x) => x.id == openGroupId);
@ -1085,12 +1198,16 @@ function select_group_chats(groupId, skipAnimation) {
const group = openGroupId && groups.find((x) => x.id == openGroupId);
const groupName = group?.name ?? "";
const replyStrategy = Number(group?.activation_strategy ?? group_activation_strategy.NATURAL);
const generationMode = Number(group?.generation_mode ?? group_generation_mode.SWAP);
setMenuType(!!group ? 'group_edit' : 'group_create');
$("#group_avatar_preview").empty().append(getGroupAvatar(group));
$("#rm_group_restore_avatar").toggle(!!group && isValidImageUrl(group.avatar_url));
$("#rm_group_filter").val("").trigger("input");
$(`input[name="rm_group_activation_strategy"][value="${replyStrategy}"]`).prop('checked', true);
$("#rm_group_activation_strategy").val(replyStrategy);
$(`#rm_group_activation_strategy option[value="${replyStrategy}"]`).prop('selected', true);
$("#rm_group_generation_mode").val(generationMode);
$(`#rm_group_generation_mode option[value="${generationMode}"]`).prop('selected', true);
$("#rm_group_chat_name").val(groupName);
if (!skipAnimation) {
@ -1311,8 +1428,9 @@ function filterGroupMembers() {
async function createGroup() {
let name = $("#rm_group_chat_name").val();
let allow_self_responses = !!$("#rm_group_allow_self_responses").prop("checked");
let activation_strategy = $('input[name="rm_group_activation_strategy"]:checked').val() ?? group_activation_strategy.NATURAL;
let allowSelfResponses = !!$("#rm_group_allow_self_responses").prop("checked");
let activationStrategy = Number($('#rm_group_activation_strategy').find(':selected').val()) ?? group_activation_strategy.NATURAL;
let generationMode = Number($('#rm_group_generation_mode').find(':selected').val()) ?? group_generation_mode.SWAP;
const members = newGroupMembers;
const memberNames = characters.filter(x => members.includes(x.avatar)).map(x => x.name).join(", ");
@ -1332,8 +1450,9 @@ async function createGroup() {
name: name,
members: members,
avatar_url: isValidImageUrl(avatar_url) ? avatar_url : default_avatar,
allow_self_responses: allow_self_responses,
activation_strategy: activation_strategy,
allow_self_responses: allowSelfResponses,
activation_strategy: activationStrategy,
generation_mode: generationMode,
disabled_members: [],
chat_metadata: {},
fav: fav_grp_checked,
@ -1605,7 +1724,8 @@ jQuery(() => {
$("#rm_group_delete").off().on("click", onDeleteGroupClick);
$("#group_favorite_button").on('click', onFavoriteGroupClick);
$("#rm_group_allow_self_responses").on("input", onGroupSelfResponsesClick);
$('input[name="rm_group_activation_strategy"]').on("input", onGroupActivationStrategyInput);
$("#rm_group_activation_strategy").on("change", onGroupActivationStrategyInput);
$("#rm_group_generation_mode").on("change", onGroupGenerationModeInput);
$("#group_avatar_button").on("input", uploadGroupAvatar);
$("#rm_group_restore_avatar").on("click", restoreGroupAvatar);
$(document).on("click", ".group_member .right_menu_button", onGroupActionClick);

View File

@ -44,6 +44,8 @@ const textgenerationwebui_settings = {
length_penalty: 1,
min_length: 0,
encoder_rep_pen: 1,
freq_pen: 0,
presence_pen: 0,
do_sample: true,
early_stopping: false,
seed: -1,
@ -87,6 +89,8 @@ const setting_names = [
"length_penalty",
"min_length",
"encoder_rep_pen",
"freq_pen",
"presence_pen",
"do_sample",
"early_stopping",
"seed",
@ -415,6 +419,8 @@ export function getTextGenGenerationData(finalPrompt, this_amount_gen, isImperso
'repetition_penalty': textgenerationwebui_settings.rep_pen,
'repetition_penalty_range': textgenerationwebui_settings.rep_pen_range,
'encoder_repetition_penalty': textgenerationwebui_settings.encoder_rep_pen,
'frequency_penalty': textgenerationwebui_settings.freq_pen,
'presence_penalty': textgenerationwebui_settings.presence_pen,
'top_k': textgenerationwebui_settings.top_k,
'min_length': textgenerationwebui_settings.min_length,
'no_repeat_ngram_size': textgenerationwebui_settings.no_repeat_ngram_size,

View File

@ -1383,12 +1383,16 @@ async function checkWorldInfo(chat, maxContext) {
// Add the depth or AN if enabled
// Put this code here since otherwise, the chat reference is modified
if (extension_settings.note.allowWIScan) {
let depthPrompt = getExtensionPromptByName("DEPTH_PROMPT")
for (const key of Object.keys(context.extensionPrompts)) {
if (key.startsWith('DEPTH_PROMPT')) {
const depthPrompt = getExtensionPromptByName(key)
if (depthPrompt) {
textToScan = `${depthPrompt}\n${textToScan}`
}
}
}
let anPrompt = getExtensionPromptByName(NOTE_MODULE_NAME);
const anPrompt = getExtensionPromptByName(NOTE_MODULE_NAME);
if (anPrompt) {
textToScan = `${anPrompt}\n${textToScan}`
}

View File

@ -2573,6 +2573,7 @@ app.post('/creategroup', jsonParser, (request, response) => {
avatar_url: request.body.avatar_url,
allow_self_responses: !!request.body.allow_self_responses,
activation_strategy: request.body.activation_strategy ?? 0,
generation_mode: request.body.generation_mode ?? 0,
disabled_members: request.body.disabled_members ?? [],
chat_metadata: request.body.chat_metadata ?? {},
fav: request.body.fav,