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

174c178485d352e246336300113241c956e82d66

RossAscends <124905043+RossAscends@users.noreply.github.com>

9 files changed, +137 -75Ignore whitespace
public/index.html+26 -2
@@ -5,7 +5,7 @@
55 <title>SillyTavern</title>
66 <base href="/">
77 <meta charset="utf-8">
88 <meta name="viewport" content="width=device-width, viewport-fit=cover, initial-scale=1, maximum-scale=1.0, user-scalable=no, interactive-widget=resizes-content">
99 <meta name="apple-mobile-web-app-capable" content="yes">
1010 <meta name="darkreader-lock">
1111 <meta name="robots" content="noindex, nofollow" />
@@ -1278,6 +1278,28 @@
12781278 </div>
12791279 </div>
12801280 </div>
1281+
1282+ <div data-newbie-hidden data-tg-type="koboldcpp" id="xtc_block" class="wide100p">
1283+ <h4 class="wide100p textAlignCenter">
1284+ <label data-i18n="Exclude Top Choices (XTC)">Exclude Top Choices (XTC)</label>
1285+ <a href="https://github.com/oobabooga/text-generation-webui/pull/6335" target="_blank">
1286+ <div class=" fa-solid fa-circle-info opacity50p"></div>
1287+ </a>
1288+ </h4>
1289+ <div class="flex-container flexFlowRow gap10px flexShrink">
1290+ <div class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
1291+ <small data-i18n="Threshold">Threshold</small>
1292+ <input class="neo-range-slider" type="range" id="xtc_threshold_textgenerationwebui" name="volume" min="0" max="0.5" step="0.01" />
1293+ <input class="neo-range-input" type="number" min="0" max="0.5" step="0.01" data-for="xtc_threshold_textgenerationwebui" id="xtc_threshold_counter_textgenerationwebui">
1294+ </div>
1295+ <div class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
1296+ <small data-i18n="Probability">Probability</small>
1297+ <input class="neo-range-slider" type="range" id="xtc_probability_textgenerationwebui" name="volume" min="0" max="1" step="0.01" />
1298+ <input class="neo-range-input" type="number" min="0" max="1" step="0.01" data-for="xtc_probability_textgenerationwebui" id="xtc_probability_counter_textgenerationwebui">
1299+ </div>
1300+ </div>
1301+ </div>
1302+
12811303 <!-- Enable for llama.cpp when the PR is merged: https://github.com/ggerganov/llama.cpp/pull/6839 -->
12821304 <div data-newbie-hidden data-tg-type="ooba, koboldcpp" id="dryBlock" class="wide100p">
12831305 <h4 class="wide100p textAlignCenter" title="DRY penalizes tokens that would extend the end of the input into a sequence that has previously occurred in the input. Set multiplier to 0 to disable." data-i18n="[title]DRY_Repetition_Penalty_desc">
@@ -1823,7 +1845,7 @@
18231845 <div class="fa-solid fa-clock-rotate-left"></div>
18241846 </div>
18251847 </div>
18261848 <textarea id="claude_human_sysprompt_textarea" class="text_pole textarea_compact" rows="4" maxlength="10000" data-i18n="[placeholder]Human message" placeholder="Human message, instruction, etc.&#10;Adds nothing when empty, i.e. requires a new prompt with the role 'user'."></textarea>
18271849 </div>
18281850 </div>
18291851 </div>
@@ -2931,6 +2953,8 @@
29312953 <option value="command">command</option>
29322954 <option value="command-r">command-r</option>
29332955 <option value="command-r-plus">command-r-plus</option>
2956+ <option value="command-r-08-2024">command-r-08-2024</option>
2957+ <option value="command-r-plus-08-2024">command-r-plus-08-2024</option>
29342958 </optgroup>
29352959 <optgroup label="Nightly">
29362960 <option value="command-light-nightly">command-light-nightly</option>
public/script.js+57 -51
@@ -488,14 +488,6 @@ let default_user_name = 'User';
488488export let name1 = default_user_name;
489489export let name2 = 'SillyTavern System';
490490export let chat = [];
491-let safetychat = [
492- {
493- name: systemUserName,
494- is_user: false,
495- create_date: 0,
496- mes: 'You deleted a character/chat and arrived back here for safety reasons! Pick another character!',
497- },
498-];
499491let chatSaveTimeout;
500492let importFlashTimeout;
501493export let isChatSaving = false;
@@ -594,6 +586,17 @@ export const extension_prompt_roles = {
594586
595587export const MAX_INJECTION_DEPTH = 1000;
596588
589+const SAFETY_CHAT = [
590+ {
591+ name: systemUserName,
592+ force_avatar: system_avatar,
593+ is_system: true,
594+ is_user: false,
595+ create_date: 0,
596+ mes: 'You deleted a character/chat and arrived back here for safety reasons! Pick another character!',
597+ },
598+];
599+
597600export let system_messages = {};
598601
599602async function getSystemMessages() {
@@ -3760,7 +3763,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
37603763 }
37613764
37623765 let examplesString = '';
37633766 let chatString = addChatsPreamble(addChatsSeparator(''));
37643767 let cyclePrompt = '';
37653768
37663769 async function getMessagesTokenCount() {
@@ -3769,10 +3772,10 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
37693772 storyString,
37703773 afterScenarioAnchor,
37713774 examplesString,
3775+ userAlignmentMessage,
37723776 chatString,
3773- quiet_prompt,
3777+ modifyLastPromptLine(''),
37743778 cyclePrompt,
3775- userAlignmentMessage,
37763779 ].join('').replace(/\r/gm, '');
37773780 return getTokenCountAsync(encodeString, power_user.token_padding);
37783781 }
@@ -3803,8 +3806,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
38033806 }
38043807
38053808 tokenCount += await getTokenCountAsync(item.replace(/\r/gm, ''));
3806- chatString = item + chatString;
38073809 if (tokenCount < this_max_context) {
3810+ chatString = chatString + item;
38083811 arrMes[index] = item;
38093812 lastAddedIndex = Math.max(lastAddedIndex, index);
38103813 } else {
@@ -3830,8 +3833,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
38303833 }
38313834
38323835 tokenCount += await getTokenCountAsync(item.replace(/\r/gm, ''));
3833- chatString = item + chatString;
38343836 if (tokenCount < this_max_context) {
3837+ chatString = chatString + item;
38353838 arrMes[i] = item;
38363839 lastAddedIndex = Math.max(lastAddedIndex, i);
38373840 } else {
@@ -4028,15 +4031,16 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
40284031 async function checkPromptSize() {
40294032 console.debug('---checking Prompt size');
40304033 setPromptString();
4034+ const jointMessages = mesSend.map((e) => `${e.extensionPrompts.join('')}${e.message}`).join('');
40314035 const prompt = [
40324036 beforeScenarioAnchor,
40334037 storyString,
40344038 afterScenarioAnchor,
40354039 mesExmString,
4036- mesSend.map((e) => `${e.extensionPrompts.join('')}${e.message}`).join(''),
4040+ addChatsPreamble(addChatsSeparator(jointMessages)),
40374041 '\n',
4042+ modifyLastPromptLine(''),
40384043 generatedPromptCache,
4039- quiet_prompt,
40404044 ].join('').replace(/\r/gm, '');
40414045 let thisPromptContextSize = await getTokenCountAsync(prompt, power_user.token_padding);
40424046
@@ -5679,7 +5683,7 @@ export function resetChatState() {
56795683 // replaces deleted charcter name with system user since it will be displayed next.
56805684 name2 = systemUserName;
56815685 // sets up system user to tell user about having deleted a character
56825686 chat.splice(0, =chat.length, [...safetychat]SAFETY_CHAT);
56835687 // resets chat metadata
56845688 chat_metadata = {};
56855689 // resets the characters array, forcing getcharacters to reset
@@ -8840,72 +8844,74 @@ export async function handleDeleteCharacter(this_chid, delete_chats) {
88408844/**
88418845 * Deletes a character completely, including associated chats if specified
88428846 *
88438847 * @param {string|string[]} characterKey - The key (avatar) of the character to be deleted
88448848 * @param {Object} [options] - Optional parameters for the deletion
88458849 * @param {boolean} [options.deleteChats=true] - Whether to delete associated chats or not
88468850 * @return {Promise<void>} - A promise that resolves when the character is successfully deleted
88478851 */
88488852export async function deleteCharacter(characterKey, { deleteChats = true } = {}) {
8849- const character = characters.find(x => x.avatar == characterKey);
8853+ if (!Array.isArray(characterKey)) {
8850- if (!character) {
8854+ characterKey = [characterKey];
8851- toastr.warning(`Character ${characterKey} not found. Cannot be deleted.`);
8852- return;
88538855 }
88548856
8855- const chid = characters.indexOf(character);
8857+ for (const key of characterKey) {
8856- const pastChats = await getPastCharacterChats(chid);
8858+ const character = characters.find(x => x.avatar == key);
8859+ if (!character) {
8860+ toastr.warning(`Character ${key} not found. Skipping deletion.`);
8861+ continue;
8862+ }
88578863
8858- const msg = { avatar_url: character.avatar, delete_chats: deleteChats };
8864+ const chid = characters.indexOf(character);
8865+ const pastChats = await getPastCharacterChats(chid);
88598866
8860- const response = await fetch('/api/characters/delete', {
8867+ const msg = { avatar_url: character.avatar, delete_chats: deleteChats };
8861- method: 'POST',
8862- headers: getRequestHeaders(),
8863- body: JSON.stringify(msg),
8864- cache: 'no-cache',
8865- });
88668868
8867- if (!response.ok) {
8869+ const response = await fetch('/api/characters/delete', {
8868- throw new Error(`Failed to delete character: ${response.status} ${response.statusText}`);
8870+ method: 'POST',
8869- }
8871+ headers: getRequestHeaders(),
8872+ body: JSON.stringify(msg),
8873+ cache: 'no-cache',
8874+ });
8875+
8876+ if (!response.ok) {
8877+ toastr.error(`${response.status} ${response.statusText}`, 'Failed to delete character');
8878+ continue;
8879+ }
88708880
8871- await removeCharacterFromUI(character.name, character.avatar);
8881+ delete tag_map[character.avatar];
8882+ select_rm_info('char_delete', character.name);
88728883
88738884 if (deleteChats) {
88748885 for (const chat of pastChats) {
88758886 const name = chat.file_name.replace('.jsonl', '');
88768887 await eventSource.emit(event_types.CHAT_DELETED, name);
8888+ }
88778889 }
8890+
8891+ await eventSource.emit(event_types.CHARACTER_DELETED, { id: chid, character: character });
88788892 }
88798893
8880- eventSource.emit(event_types.CHARACTER_DELETED, { id: this_chid, character: characters[this_chid] });
8894+ await removeCharacterFromUI();
88818895}
88828896
88838897/**
88848898 * Function to delete a character from UI after character deletion API success.
88858899 * It manages necessary UI changes such as closing advanced editing popup, unsetting
88868900 * character ID, resetting characters array and chat metadata, deselecting character's tab
88878901 * panel, removing character name from navigation tabs, clearing chat, removingfetching character'supdated list of characters.
8888- * avatar from tag_map, fetching updated list of characters and updating the 'deleted
8889- * character' message.
88908902 * It also ensures to save the settings after all the operations.
8891- *
8892- * @param {string} name - The name of the character to be deleted.
8893- * @param {string} avatar - The avatar URL of the character to be deleted.
8894- * @param {boolean} reloadCharacters - Whether the character list should be refreshed after deletion.
88958903 */
88968904async function removeCharacterFromUI(name, avatar, reloadCharacters = true) {
88978905 await clearChat();
88988906 $('#character_cross').click();
88998907 this_chid = undefined;
89008908 characters.length = 0;
89018909 name2 = systemUserName;
89028910 chat.splice(0, =chat.length, [...safetychat]SAFETY_CHAT);
89038911 chat_metadata = {};
89048912 $(document.getElementById('rm_button_selected_ch')).children('h2').text('');
89058913 this_chid = undefined;
8906- delete tag_map[avatar];
8914+ await getCharacters();
8907- if (reloadCharacters) await getCharacters();
8908- select_rm_info('char_delete', name);
89098915 await printMessages();
89108916 saveSettingsDebounced();
89118917}
public/scripts/BulkEditOverlay.js+12 -12
@@ -108,14 +108,12 @@ class CharacterContextMenu {
108108 * Delete one or more characters,
109109 * opens a popup.
110110 *
111111 * @param {numberstring|string[]} characterIdcharacterKey
112112 * @param {boolean} [deleteChats]
113113 * @returns {Promise<void>}
114114 */
115115 static delete = async (characterIdcharacterKey, deleteChats = false) => {
116- const character = CharacterContextMenu.#getCharacter(characterId);
116+ await deleteCharacter(characterKey, { deleteChats: deleteChats });
117-
118- await deleteCharacter(character.avatar, { deleteChats: deleteChats });
119117 };
120118
121119 static #getCharacter = (characterId) => characters[characterId] ?? null;
@@ -344,7 +342,7 @@ class BulkTagPopupHandler {
344342 const mutualTags = this.getMutualTags();
345343
346344 for (const characterId of this.characterIds) {
347345 for (const tag of mutualTags) {
348346 removeTagFromMap(tag.id, characterId);
349347 }
350348 }
@@ -599,8 +597,7 @@ class BulkEditOverlay {
599597
600598 this.container.removeEventListener('mouseup', cancelHold);
601599 this.container.removeEventListener('touchend', cancelHold);
602600 }, BulkEditOverlay.longPressDelay);
603- BulkEditOverlay.longPressDelay);
604601 };
605602
606603 handleLongPressEnd = (event) => {
@@ -847,11 +844,14 @@ class BulkEditOverlay {
847844 const deleteChats = document.getElementById('del_char_checkbox').checked ?? false;
848845
849846 showLoader();
850847 const toast = toastr.info('We\'re deleting your characters, please wait...', 'Working on it');
851- return Promise.allSettled(characterIds.map(async characterId => CharacterContextMenu.delete(characterId, deleteChats)))
848+ const avatarList = characterIds.map(id => characters[id]?.avatar).filter(a => a);
852- .then(() => getCharacters())
849+ return CharacterContextMenu.delete(avatarList, deleteChats)
853850 .then(() => this.browseState())
854851 .finally(() => hideLoader());{
852+ toastr.clear(toast);
853+ hideLoader();
854+ });
855855 });
856856
857857 // At this moment the popup is already changed in the dom, but not yet closed/resolved. We build the avatar list here
public/scripts/nai-settings.js+16 -1
@@ -78,6 +78,19 @@ export function getKayraMaxContextTokens() {
7878 return null;
7979}
8080
81+export function getKayraMaxResponseTokens() {
82+ switch (novel_data?.tier) {
83+ case 1:
84+ return 100;
85+ case 2:
86+ return 100;
87+ case 3:
88+ return 150;
89+ }
90+
91+ return maximum_output_length;
92+}
93+
8194export function getNovelTier() {
8295 return nai_tiers[novel_data?.tier] ?? 'no_connection';
8396}
@@ -438,12 +451,14 @@ export function getNovelGenerationData(finalPrompt, settings, maxLength, isImper
438451 console.log(finalPrompt);
439452 }
440453
454+ const adjustedMaxLength = nai_settings.model_novel.includes('kayra') ? getKayraMaxResponseTokens() : maximum_output_length;
455+
441456 return {
442457 'input': finalPrompt,
443458 'model': nai_settings.model_novel,
444459 'use_string': true,
445460 'temperature': Number(nai_settings.temperature),
446461 'max_length': maxLength < maximum_output_lengthadjustedMaxLength ? maxLength : maximum_output_lengthadjustedMaxLength,
447462 'min_length': Number(nai_settings.min_length),
448463 'tail_free_sampling': Number(nai_settings.tail_free_sampling),
449464 'repetition_penalty': Number(nai_settings.repetition_penalty),
public/scripts/openai.js+2 -2
@@ -133,7 +133,7 @@ const max_2mil = 2000 * 1000;
133133const scale_max = 8191;
134134const claude_max = 9000; // We have a proper tokenizer, so theoretically could be larger (up to 9k)
135135const claude_100k_max = 99000;
136136const unlocked_max = max_200kmax_2mil;
137137const oai_max_temp = 2.0;
138138const claude_max_temp = 1.0;
139139const openrouter_website_model = 'OR_Website';
@@ -4191,7 +4191,7 @@ async function onModelChange() {
41914191 else if (['command-light-nightly', 'command-nightly'].includes(oai_settings.cohere_model)) {
41924192 $('#openai_max_context').attr('max', max_8k);
41934193 }
41944194 else if (['command-r', oai_settings.cohere_model.includes('command-r-plus'].includes(oai_settings.cohere_model)) {
41954195 $('#openai_max_context').attr('max', max_128k);
41964196 }
41974197 else if (['c4ai-aya-23'].includes(oai_settings.cohere_model)) {
public/scripts/textgen-settings.js+8 -0
@@ -188,6 +188,8 @@ const settings = {
188188 custom_model: '',
189189 bypass_status_check: false,
190190 openrouter_allow_fallbacks: true,
191+ xtc_threshold: 0.1,
192+ xtc_probability: 0,
191193};
192194
193195export let textgenerationwebui_banned_in_macros = [];
@@ -263,6 +265,8 @@ export const setting_names = [
263265 'custom_model',
264266 'bypass_status_check',
265267 'openrouter_allow_fallbacks',
268+ 'xtc_threshold',
269+ 'xtc_probability',
266270];
267271
268272const DYNATEMP_BLOCK = document.getElementById('dynatemp_block_ooba');
@@ -718,6 +722,8 @@ jQuery(function () {
718722 'dry_multiplier_textgenerationwebui': 0,
719723 'dry_base_textgenerationwebui': 1.75,
720724 'dry_penalty_last_n_textgenerationwebui': 0,
725+ 'xtc_threshold_textgenerationwebui': 0.1,
726+ 'xtc_probability_textgenerationwebui': 0,
721727 };
722728
723729 for (const [id, value] of Object.entries(inputs)) {
@@ -1156,6 +1162,8 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
11561162 'api_server': getTextGenServer(),
11571163 'legacy_api': settings.legacy_api && (settings.type === OOBA || settings.type === APHRODITE),
11581164 'sampler_order': settings.type === textgen_types.KOBOLDCPP ? settings.sampler_order : undefined,
1165+ 'xtc_threshold': settings.xtc_threshold,
1166+ 'xtc_probability': settings.xtc_probability,
11591167 };
11601168 const nonAphroditeParams = {
11611169 'rep_pen': settings.rep_pen,
public/scripts/world-info.js+6 -2
@@ -4777,8 +4777,10 @@ jQuery(() => {
47774777 world_info_min_activations = Number($(this).val());
47784778 $('#world_info_min_activations_counter').val(world_info_min_activations);
47794779
47804780 if (world_info_min_activations !== 0 && world_info_max_recursion_steps !== 0) {
47814781 $('#world_info_max_recursion_steps').val(0).trigger('input');
4782+ flashHighlight($('#world_info_max_recursion_steps').parent()); // flash the other control to show it has changed
4783+ console.info('[WI] Max recursion steps set to 0, as min activations is set to', world_info_min_activations);
47824784 } else {
47834785 saveSettings();
47844786 }
@@ -4840,8 +4842,10 @@ jQuery(() => {
48404842 $('#world_info_max_recursion_steps').on('input', function () {
48414843 world_info_max_recursion_steps = Number($(this).val());
48424844 $('#world_info_max_recursion_steps_counter').val(world_info_max_recursion_steps);
48434845 if (world_info_max_recursion_steps !== 0 && world_info_min_activations !== 0) {
48444846 $('#world_info_min_activations').val(0).trigger('input');
4847+ flashHighlight($('#world_info_min_activations').parent()); // flash the other control to show it has changed
4848+ console.info('[WI] Min activations set to 0, as max recursion steps is set to', world_info_max_recursion_steps);
48454849 } else {
48464850 saveSettings();
48474851 }
src/endpoints/backends/chat-completions.js+5 -0
@@ -572,6 +572,11 @@ async function sendCohereRequest(request, response) {
572572 search_queries_only: false,
573573 };
574574
575+ const canDoSafetyMode = String(request.body.model).endsWith('08-2024');
576+ if (canDoSafetyMode) {
577+ requestBody.safety_mode = 'NONE';
578+ }
579+
575580 console.log('Cohere request:', requestBody);
576581
577582 const config = {
src/endpoints/novelai.js+5 -5
@@ -6,6 +6,7 @@ const { readAllChunks, extractFileFromZipBuffer, forwardFetchResponse } = requir
66const { jsonParser } = require('../express-common');
77
88const API_NOVELAI = 'https://api.novelai.net';
9+const TEXT_NOVELAI = 'https://text.novelai.net';
910const IMAGE_NOVELAI = 'https://image.novelai.net';
1011
1112// Ban bracket generation, plus defaults
@@ -155,7 +156,7 @@ router.post('/generate', jsonParser, async function (req, res) {
155156 'repetition_penalty_slope': req.body.repetition_penalty_slope,
156157 'repetition_penalty_frequency': req.body.repetition_penalty_frequency,
157158 'repetition_penalty_presence': req.body.repetition_penalty_presence,
158159 'repetition_penalty_whitelist': isNewModel ? repPenaltyAllowList.flat() : null,
159160 'top_a': req.body.top_a,
160161 'top_p': req.body.top_p,
161162 'top_k': req.body.top_k,
@@ -178,9 +179,7 @@ router.post('/generate', jsonParser, async function (req, res) {
178179 };
179180
180181 // Tells the model to stop generation at '>'
181182 if ('theme_textadventure' === req.body.prefix && isNewModel) {
182- (true === req.body.model.includes('clio') ||
183- true === req.body.model.includes('kayra'))) {
184183 data.parameters.eos_token_id = 49405;
185184 }
186185
@@ -193,7 +192,8 @@ router.post('/generate', jsonParser, async function (req, res) {
193192 };
194193
195194 try {
196195 const urlbaseURL = req.body.streamingmodel.includes('kayra') ? `${API_NOVELAI}/ai/generate-stream`TEXT_NOVELAI : `${API_NOVELAI}/ai/generate`;
196+ const url = req.body.streaming ? `${baseURL}/ai/generate-stream` : `${baseURL}/ai/generate`;
197197 const response = await fetch(url, { method: 'POST', timeout: 0, ...args });
198198
199199 if (req.body.streaming) {