Merge branch 'staging' of https://github.com/SillyLossy/TavernAI into staging

This commit is contained in:
Cohee 2023-08-05 15:44:18 +03:00
commit ef1f6b3143
14 changed files with 232 additions and 74 deletions

View File

@ -1,6 +1,8 @@
[English](readme.md) | 中文
![image](https://github.com/SillyTavern/SillyTavern/assets/18619528/8c41a061-7f72-4d2b-9d54-e6d058209e7b)
移动设备界面友好多种人工智能服务或模型支持KoboldAI/CPP, Horde, NovelAI, Ooba, OpenAI+proxies, WindowAI(Claude!)),类似 Galgame 的 老 婆 模 式Horde SD文本系统语音生成世界信息Lorebooks可定制的界面自动翻译和比你所需要的更多的 Prompt。附带扩展服务支持文本绘画生成与语音生成和基于向量数据库 ChromaDB 的聊天信息总结。
移动设备界面友好多种人工智能服务或模型支持KoboldAI/CPP, Horde, NovelAI, Ooba, OpenAI, OpenRouter, Claude, Scale),类似 Galgame 的 老 婆 模 式Horde SD文本系统语音生成世界信息Lorebooks可定制的界面自动翻译和比你所需要的更多的 Prompt。附带扩展服务支持文本绘画生成与语音生成和基于向量数据库 ChromaDB 的聊天信息总结。
基于 TavernAI 1.2.8 的分叉版本
@ -282,25 +284,26 @@ SillyTavern 会将 API 密钥保存在目录中的 `secrets.json` 文件内。
## 许可证和贡献
** 发布本程序是希望它能有所帮助,但不做任何保证;甚至没有明示的性能、稳定性和其他任何特定用途的可用性保证。更多详情,请参阅 GNU Affero 通用公共许可证。 **
**发布本程序是希望它能有所帮助,但不做任何保证;甚至没有明示的性能、稳定性和其他任何特定用途的可用性保证。更多详情,请参阅 GNU Affero 通用公共许可证。**
** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. **
**This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.**
* TAI Base by Humi: Unknown license
* Cohee's modifications and derived code: AGPL v3
* RossAscends' additions: AGPL v3
* Portions of CncAnon's TavernAITurbo mod: Unknown license
* kingbri's various commits and suggestions (https://github.com/bdashore3)
* StefanDanielSchwarz's various commits and bug reports (https://github.com/StefanDanielSchwarz)
* Waifu mode inspired by the work of PepperTaco (https://github.com/peppertaco/Tavern/)
* kingbri's various commits and suggestions (<https://github.com/bdashore3>)
* StefanDanielSchwarz's various commits and bug reports (<https://github.com/StefanDanielSchwarz>)
* Waifu mode inspired by the work of PepperTaco (<https://github.com/peppertaco/Tavern/>)
* Thanks Pygmalion University for being awesome testers and suggesting cool features!
* Thanks oobabooga for compiling presets for TextGen
* KoboldAI Presets from KAI Lite: https://lite.koboldai.net/
* KoboldAI Presets from KAI Lite: <https://lite.koboldai.net/>
* Noto Sans font by Google (OFL license)
* Icon theme by Font Awesome https://fontawesome.com (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* AI Horde client library by ZeldaFan0225: https://github.com/ZeldaFan0225/ai_horde
* Icon theme by Font Awesome <https://fontawesome.com> (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* AI Horde client library by ZeldaFan0225: <https://github.com/ZeldaFan0225/ai_horde>
* Linux startup script by AlpinDale
* Thanks paniphons for providing a FAQ document
* 10K Discord Users Celebratory Background by @kallmeflocc
* Default content (characters and lore books) provided by @OtisAlejandro, @RossAscends and @kallmeflocc
* Korean translation by @doloroushyeonse
* 中文翻译由 [@XXpE3](https://github.com/XXpE3) 完成,中文 ISSUES 可以联系 @XXpE3

7
.github/readme.md vendored
View File

@ -1,6 +1,8 @@
English | [中文](readme-zh_cn.md)
![image](https://github.com/SillyTavern/SillyTavern/assets/18619528/8c41a061-7f72-4d2b-9d54-e6d058209e7b)
Mobile-friendly, Multi-API (KoboldAI/CPP, Horde, NovelAI, Ooba, OpenAI+proxies, WindowAI(Claude!)), VN-like Waifu Mode, Horde SD, System TTS, WorldInfo (lorebooks), customizable UI, auto-translate, and more prompt options than you'd ever want or need. Optional Extras server for more SD/TTS options + ChromaDB/Summarize.
Mobile-friendly, Multi-API (KoboldAI/CPP, Horde, NovelAI, Ooba, OpenAI, OpenRouter, Claude, Scale), VN-like Waifu Mode, Horde SD, System TTS, WorldInfo (lorebooks), customizable UI, auto-translate, and more prompt options than you'd ever want or need. Optional Extras server for more SD/TTS options + ChromaDB/Summarize.
Based on a fork of TavernAI 1.2.8
@ -65,7 +67,7 @@ Get in touch with the developers directly:
* Chat bookmarks / branching (duplicates the dialogue in its current state)
* Advanced KoboldAI / TextGen generation settings with a lot of community-made presets
* World Info support: create rich lore or save tokens on your character card
* Window AI browser extension support (run models like Claude, GPT 4): <https://windowai.io/>
* [OpenRouter](https://openrouter.ai) connection for various APIs (Claude, GPT-4/3.5 and more)
* [Oobabooga's TextGen WebUI](https://github.com/oobabooga/text-generation-webui) API connection
* [AI Horde](https://horde.koboldai.net/) connection
* Prompt generation formatting tweaking
@ -307,3 +309,4 @@ GNU Affero General Public License for more details.**
* Default content (characters and lore books) provided by @OtisAlejandro, @RossAscends and @kallmeflocc
* Korean translation by @doloroushyeonse
* k_euler_a support for Horde by <https://github.com/Teashrock>
* Chinese translation by [@XXpE3](https://github.com/XXpE3), 中文 ISSUES 可以联系 @XXpE3

View File

@ -59,7 +59,7 @@
"trusted_workers_only": false
},
"power_user": {
"tokenizer": 3,
"tokenizer": 99,
"token_padding": 64,
"collapse_newlines": false,
"pygmalion_formatting": 0,
@ -147,6 +147,7 @@
"persona_description": "",
"persona_description_position": 0,
"custom_stopping_strings": "",
"custom_stopping_strings_macro": true,
"fuzzy_search": false
},
"extension_settings": {

View File

@ -328,7 +328,7 @@
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input type="range" id="temp_novel" name="volume" min="0.1" max="2.0" step="0.01">
<input type="range" id="temp_novel" name="volume" min="0.1" max="2.50" step="0.01">
</div>
<div class="range-block-counter">
<div contenteditable="true" data-for="temp_novel" id="temp_counter_novel">
@ -342,7 +342,7 @@
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input type="range" id="rep_pen_novel" name="volume" min="1" max="1.5" step="0.01">
<input type="range" id="rep_pen_novel" name="volume" min="1" max="8" step="0.05">
</div>
<div class="range-block-counter">
<div contenteditable="true" data-for="rep_pen_novel" id="rep_pen_counter_novel">
@ -387,7 +387,7 @@
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input type="range" id="rep_pen_freq_novel" name="volume" min="0" max="1" step="0.00001">
<input type="range" id="rep_pen_freq_novel" name="volume" min="0" max="1" step="0.01">
</div>
<div class="range-block-counter">
<div contenteditable="true" data-for="rep_pen_freq_novel" id="rep_pen_freq_counter_novel">
@ -402,7 +402,7 @@
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input type="range" id="rep_pen_presence_novel" name="volume" min="0" max="1" step="0.001">
<input type="range" id="rep_pen_presence_novel" name="volume" min="0" max="1" step="0.01">
</div>
<div class="range-block-counter">
<div contenteditable="true" data-for="rep_pen_presence_novel" id="rep_pen_presence_counter_novel">
@ -815,8 +815,8 @@
<div class="fa-solid fa-clock-rotate-left "></div>
</div>
</div>
<div class="toggle-description justifyLeft" data-i18n="Use style tags to improve the quality of the output">
Use style tags to improve the quality of the output
<div class="toggle-description justifyLeft" data-i18n="Use style tags to modify the writing style of the output">
Use style tags to modify the writing style of the output
</div>
<div class="wide100p">
<textarea id="nai_preamble_textarea" class="text_pole textarea_compact" name="nai_preamble" rows="2"
@ -1336,12 +1336,8 @@
</div>
</div>
<div class="range-block" data-source="claude">
<div class="range-block-title" data-i18n="Assistant Prefill">
Assistant Prefill
</div>
<div class="wide100p">
<input type="text" id="claude_assistant_prefill" class="text_pole" placeholder="Start Claude's answer with...">
</div>
<span data-i18n="Assistant Prefill">Assistant Prefill</span>
<textarea id="claude_assistant_prefill" class="text_pole textarea_compact" name="assistant_prefill" rows="3" maxlength="5000" placeholder="Start Claude's answer with..."></textarea>
</div>
<div class="inline-drawer wide100p">
@ -1556,11 +1552,11 @@
</div>
<div id="textgenerationwebui_api" style="display: none;position: relative;">
<form action="javascript:void(null);" method="post" enctype="multipart/form-data">
If you are using:
If you are using:
<div class="flex-container indent20p">
<a href="https://github.com/oobabooga/text-generation-webui" target="_blank">
oobabooga/text-generation-webui
</a>,
</a>
<span data-i18n="Make sure you run it with">
Make sure you run it with <tt>--api</tt> flag
</span>
@ -1568,7 +1564,7 @@
<div class="flex-container indent20p">
<a href="https://mancer.tech/" target="_blank">
Mancer AI
</a>,
</a>
<label class="checkbox_label" for="use-mancer-api-checkbox">
<span data-i18n="Use API key (Only required for Mancer)">
Click this box (and add your API key!):
@ -1961,6 +1957,7 @@
</a>
</h4>
<select id="tokenizer">
<option value="99">Best match (recommended)</option>
<option value="0">None / Estimated</option>
<option value="1">GPT-3 (OpenAI)</option>
<option value="2">GPT-3 (Alternative / Classic)</option>
@ -2037,6 +2034,12 @@
<div>
<textarea id="custom_stopping_strings" rows="2" class="text_pole textarea_compact" placeholder="[&quot;Ford&quot;, &quot;BMW&quot;, &quot;Fiat&quot;]"></textarea>
</div>
<label class="checkbox_label" for="custom_stopping_strings_macro">
<input id="custom_stopping_strings_macro" type="checkbox" checked>
<span data-i18n="Replace Macro in Custom Stopping Strings">
Replace Macro in Custom Stopping Strings
</span>
</label>
<h4>
<span data-i18n="Pygmalion Formatting">
Pygmalion Formatting

View File

@ -0,0 +1,12 @@
{
"input_sequence": "User: ",
"macro": true,
"name": "OpenOrca/OpenChat",
"names": true,
"output_sequence": "<|end_of_turn|>\nAssistant: ",
"separator_sequence": "<|end_of_turn|>\n",
"stop_sequence": "",
"system_prompt": "You are a helpful assistant. Please answer truthfully and write out your thinking step by step to be sure you get the right answer. If you make a mistake or encounter an error in your thinking, say so out loud and attempt to correct it. If you don't know or aren't sure about something, say so clearly. You will act as a professional logician, mathematician, and physicist. You will also act as the most appropriate type of expert to answer any particular question or solve the relevant problem; state which expert type your are, if so. Also think of any particular named expert that would be ideal to answer the relevant question or solve the relevant problem; name and act as them, if appropriate.\n",
"system_sequence": "",
"wrap": false
}

View File

@ -1,5 +1,4 @@
{
"enabled": true,
"input_sequence": "### Instruction:",
"macro": true,
"name": "Roleplay",

View File

@ -546,6 +546,25 @@ async function getClientVersion() {
}
}
function getTokenizerBestMatch() {
if (main_api === 'novel') {
if (nai_settings.model_novel.includes('krake') || nai_settings.model_novel.includes('euterpe')) {
return tokenizers.CLASSIC;
}
if (nai_settings.model_novel.includes('clio')) {
return tokenizers.NERD;
}
if (nai_settings.model_novel.includes('kayra')) {
return tokenizers.NERD2;
}
}
if (main_api === 'kobold' || main_api === 'textgenerationwebui' || main_api === 'koboldhorde') {
return tokenizers.LLAMA;
}
return power_user.NONE;
}
function getTokenCount(str, padding = undefined) {
if (typeof str !== 'string') {
return 0;
@ -563,6 +582,10 @@ function getTokenCount(str, padding = undefined) {
}
}
if (tokenizerType === tokenizers.BEST_MATCH) {
tokenizerType = getTokenizerBestMatch();
}
if (padding === undefined) {
padding = 0;
}
@ -1229,8 +1252,8 @@ function messageFormatting(mes, ch_name, isSystem, isUser) {
mes = fixMarkdown(mes);
}
if (this_chid != undefined && !isSystem)
mes = mes.replaceAll("<", "&lt;").replaceAll(">", "&gt;"); //for welcome message
//if (this_chid != undefined && !isSystem)
// mes = mes.replaceAll("<", "&lt;").replaceAll(">", "&gt;"); //for welcome message
if ((this_chid === undefined || this_chid === "invalid-safety-id") && !selected_group) {
mes = mes
.replace(/\*\*(.+?)\*\*/g, "<b>$1</b>")
@ -1727,7 +1750,11 @@ function getStoppingStrings(isImpersonate, addSpace) {
if (power_user.custom_stopping_strings) {
const customStoppingStrings = getCustomStoppingStrings();
result.push(...customStoppingStrings);
if (power_user.custom_stopping_strings_macro) {
result.push(...customStoppingStrings.map(x => substituteParams(x, name1, name2)));
} else {
result.push(...customStoppingStrings);
}
}
return addSpace ? result.map(x => `${x} `) : result;
@ -2669,8 +2696,11 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
setPromtString();
}
// add chat preamble
mesSendString = addChatsPreamble(mesSendString);
// add a custom dingus (if defined)
mesSendString = adjustChatsSeparator(mesSendString);
mesSendString = addChatsSeparator(mesSendString);
let finalPromt =
storyString +
@ -3156,16 +3186,13 @@ function parseTokenCounts(counts, thisPromptBits) {
});
}
function adjustChatsSeparator(mesSendString) {
if (main_api === 'novel') {
let preamble = "\n***\n" + nai_settings.nai_preamble;
if (!preamble.endsWith('\n')) {
preamble += '\n';
}
mesSendString = preamble + mesSendString;
}
function addChatsPreamble(mesSendString) {
const preamble = main_api === 'novel' ? nai_settings.preamble : "";
return preamble + '\n' + mesSendString;
}
else if (power_user.custom_chat_separator && power_user.custom_chat_separator.length) {
function addChatsSeparator(mesSendString) {
if (power_user.custom_chat_separator && power_user.custom_chat_separator.length) {
mesSendString = power_user.custom_chat_separator + '\n' + mesSendString;
}
@ -3174,6 +3201,10 @@ function adjustChatsSeparator(mesSendString) {
mesSendString = mesSendString;
}
else if (main_api === 'novel') {
mesSendString = '\n***\n' + mesSendString;
}
// add non-pygma dingus
else if (!is_pygmalion) {
mesSendString = '\nThen the roleplay chat between ' + name1 + ' and ' + name2 + ' begins.\n' + mesSendString;
@ -7005,13 +7036,13 @@ function doCloseChat() {
*
* @param {string} popup_type - The type of popup currently active.
* @param {string} this_chid - The character ID to be deleted.
* @param {boolean} delete_chats - Whether to delete chats or not.
*/
export async function handleDeleteCharacter(popup_type, this_chid) {
export async function handleDeleteCharacter(popup_type, this_chid, delete_chats) {
if (popup_type !== "del_ch") {
return;
}
const delete_chats = !!$("#del_char_checkbox").prop("checked");
const avatar = characters[this_chid].avatar;
const name = characters[this_chid].name;
@ -7449,7 +7480,8 @@ $(document).ready(function () {
}, 200);
}
if (popup_type == "del_ch") {
handleDeleteCharacter(popup_type, this_chid, characters);
const deleteChats = !!$("#del_char_checkbox").prop("checked");
await handleDeleteCharacter(popup_type, this_chid, deleteChats);
}
if (popup_type == "alternate_greeting" && menu_type !== "create") {
createOrEditCharacter();

View File

@ -1,4 +1,4 @@
import { characters, getCharacters, saveSettingsDebounced, handleDeleteCharacter } from "../../../script.js";
import { characters, getCharacters, handleDeleteCharacter, callPopup } from "../../../script.js";
let is_bulk_edit = false;
@ -27,7 +27,7 @@ function onEditButtonClick() {
* @param {string} this_chid - The chid of the character to delete.
*/
async function deleteCharacter(this_chid) {
await handleDeleteCharacter("del_ch", this_chid);
await handleDeleteCharacter("del_ch", this_chid, false);
}
/**
@ -45,6 +45,13 @@ async function onDeleteButtonClick() {
toDelete.push(avatar);
});
const confirm = await callPopup('<h3>Are you sure you want to delete these characters?</h3>You would need to delete the chat files manually.<br>', 'confirm');
if (!confirm) {
console.log('User cancelled delete');
return;
}
// Delete the characters
for (const avatar of toDelete) {
console.log(`Deleting character with avatar ${avatar}`);
@ -78,7 +85,7 @@ function addButtons() {
* Enables bulk selection by adding a checkbox next to each character.
*/
function enableBulkSelect() {
$(".character_select").each((i, el) => {
$("#rm_print_characters_block .character_select").each((i, el) => {
const character = $(el).text();
const checkbox = $("<input type='checkbox' class='bulk_select_checkbox'>");
checkbox.on("change", () => {
@ -86,6 +93,7 @@ function enableBulkSelect() {
});
$(el).prepend(checkbox);
});
$("#rm_print_characters_block").addClass("bulk_select");
// We also need to disable the default click event for the character_select divs
$(document).on("click", ".bulk_select_checkbox", function (event) {
event.stopImmediatePropagation();
@ -97,6 +105,7 @@ function enableBulkSelect() {
*/
function disableBulkSelect() {
$(".bulk_select_checkbox").remove();
$("#rm_print_characters_block").removeClass("bulk_select");
}
/**

View File

@ -0,0 +1,7 @@
.bulk_select_checkbox {
align-self: center;
}
#rm_print_characters_block .wide100pLess70px {
width: calc(100% - 85px);
}

View File

@ -45,8 +45,6 @@ let lastCharacter = undefined;
let lastMessage = null;
let spriteCache = {};
let inApiCall = false;
let previousSrc = null;
function isVisualNovelMode() {
return Boolean(!isMobile() && power_user.waifuMode && getContext().groupId);
@ -395,6 +393,21 @@ function onExpressionsShowDefaultInput() {
}
}
async function unloadLiveChar() {
try {
const url = new URL(getApiUrl());
url.pathname = '/api/live2d/unload';
const loadResponse = await doExtrasFetch(url);
if (!loadResponse.ok) {
throw new Error(loadResponse.statusText);
}
const loadResponseText = await loadResponse.text();
//console.log(`Response: ${loadResponseText}`);
} catch (error) {
//console.error(`Error unloading - ${error}`);
}
}
async function loadLiveChar() {
if (!modules.includes('live2d')) {
console.debug('live2d module is disabled');
@ -456,7 +469,6 @@ function handleImageChange() {
}
if (extension_settings.expressions.live2d) {
previousSrc = imgElement.src;
// Method get IP of endpoint
const live2dResultFeedSrc = `${getApiUrl()}/api/live2d/result_feed`;
$('#expression-holder').css({ display: '' });
@ -496,7 +508,6 @@ async function moduleWorker() {
if (context.groupId !== lastCharacter && context.characterId !== lastCharacter) {
removeExpression();
spriteCache = {};
previousSrc = null;
//clear expression
let imgElement = document.getElementById('expression-image');
@ -611,12 +622,62 @@ async function moduleWorker() {
}
}
async function live2dcheck() {
const context = getContext();
let spriteFolderName = context.name2;
const message = getLastCharacterMessage();
const avatarFileName = getSpriteFolderName(message);
const expressionOverride = extension_settings.expressionOverrides.find((e) =>
e.name == avatarFileName
);
if (expressionOverride && expressionOverride.path) {
spriteFolderName = expressionOverride.path;
}
try {
await validateImages(spriteFolderName);
let live2dObj = spriteCache[spriteFolderName].find(obj => obj.label === 'live2d');
let live2dPath_f = live2dObj ? live2dObj.path : null;
if(live2dPath_f != null){
//console.log("live2dPath_f " + live2dPath_f);
return true;
} else {
//console.log("live2dPath_f is null");
unloadLiveChar();
return false;
}
} catch (err) {
return err;
}
}
function setLive2dState(switch_var){
extension_settings.expressions.live2d = switch_var; // Store setting
saveSettingsDebounced();
if (extension_settings.expressions.live2d) { loadLiveChar(); } // load char as needed
handleImageChange(switch_var); // Change image as needed
live2dcheck().then(result => {
if (result) {
//console.log("Live2d exists!");
if (extension_settings.expressions.live2d) {
loadLiveChar();
} else {
unloadLiveChar();
}
handleImageChange(switch_var); // Change image as needed
} else {
//console.log("Live2d does not exist.");
}
});
}
function getSpriteFolderName(message) {
@ -917,13 +978,25 @@ async function setExpression(character, expression, force) {
document.getElementById("expression-holder").style.display = '';
} else {
// Find the <img> element with id="expression-image" and class="expression"
const imgElement = document.querySelector('img#expression-image.expression');
//console.log("searching");
if (imgElement) {
console.log("setting value");
imgElement.src = getApiUrl() + '/api/live2d/result_feed';
live2dcheck().then(result => {
if (result) {
// Find the <img> element with id="expression-image" and class="expression"
const imgElement = document.querySelector('img#expression-image.expression');
//console.log("searching");
if (imgElement) {
//console.log("setting value");
imgElement.src = getApiUrl() + '/api/live2d/result_feed';
}
} else {
//console.log("The fetch failed!");
}
});
}
}

View File

@ -79,7 +79,7 @@ function loadNovelSettings(settings) {
$(`#model_novel_select option[value=${nai_settings.model_novel}]`).attr("selected", true);
$('#model_novel_select').val(nai_settings.model_novel);
if (settings.nai_preamble !== undefined) nai_settings.nai_preamble = settings.nai_preamble;
if (settings.nai_preamble !== undefined) nai_settings.preamble = settings.nai_preamble;
nai_settings.preset_settings_novel = settings.preset_settings_novel;
nai_settings.temperature = settings.temperature;
nai_settings.repetition_penalty = settings.repetition_penalty;
@ -347,13 +347,13 @@ export async function generateNovelWithStreaming(generate_data, signal) {
}
$("#nai_preamble_textarea").on('input', function () {
nai_settings.nai_preamble = $('#nai_preamble_textarea').val();
nai_settings.preamble = $('#nai_preamble_textarea').val();
saveSettingsDebounced();
});
$("#nai_preamble_restore").on('click', function () {
nai_settings.nai_preamble = default_preamble;
$('#nai_preamble_textarea').val(nai_settings.nai_preamble);
nai_settings.preamble = default_preamble;
$('#nai_preamble_textarea').val(nai_settings.preamble);
saveSettingsDebounced();
});

View File

@ -71,6 +71,7 @@ const tokenizers = {
NERD: 4,
NERD2: 5,
API: 6,
BEST_MATCH: 99,
}
const send_on_enter_options = {
@ -87,7 +88,7 @@ export const persona_description_positions = {
}
let power_user = {
tokenizer: tokenizers.CLASSIC,
tokenizer: tokenizers.BEST_MATCH,
token_padding: 64,
collapse_newlines: false,
pygmalion_formatting: pygmalion_options.AUTO,
@ -187,6 +188,7 @@ let power_user = {
persona_show_notifications: true,
custom_stopping_strings: '',
custom_stopping_strings_macro: true,
fuzzy_search: false,
};
@ -678,6 +680,7 @@ function loadPowerUserSettings(settings, data) {
$('#auto_swipe_blacklist').val(power_user.auto_swipe_blacklist.join(", "));
$('#auto_swipe_blacklist_threshold').val(power_user.auto_swipe_blacklist_threshold);
$('#custom_stopping_strings').val(power_user.custom_stopping_strings);
$("#custom_stopping_strings_macro").prop("checked", power_user.custom_stopping_strings_macro);
$('#fuzzy_search_checkbox').prop("checked", power_user.fuzzy_search);
$('#persona_show_notifications').prop("checked", power_user.persona_show_notifications);
@ -1995,6 +1998,11 @@ $(document).ready(() => {
saveSettingsDebounced();
});
$("#custom_stopping_strings_macro").change(function () {
power_user.custom_stopping_strings_macro = !!$(this).prop("checked");
saveSettingsDebounced();
});
$('#fuzzy_search_checkbox').on('input', function () {
power_user.fuzzy_search = !!$(this).prop('checked');
saveSettingsDebounced();

View File

@ -223,6 +223,12 @@ table.responsiveTable {
font-weight: 500;
}
.mes_text blockquote {
border-left: 3px solid var(--SmartThemeQuoteColor);
padding-left: 10px;
background-color: var(--black30a);
}
.mes_text strong em,
.mes_text strong,
.mes_text h2,
@ -1293,7 +1299,8 @@ body.charListGrid #rm_print_characters_block .tags_inline {
}
.floating_prompt_radio_group, .radio_group {
.floating_prompt_radio_group,
.radio_group {
display: flex;
flex-direction: column;
}
@ -3050,12 +3057,13 @@ h5 {
/* HEINOUS */
@keyframes infinite-spinning {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
#export_character_div {
@ -5461,4 +5469,4 @@ body.waifuMode .zoomed_avatar {
background-color: var(--SmartThemeBlurTintColor);
text-align: center;
line-height: 14px;
}
}

View File

@ -1169,7 +1169,7 @@ app.post("/deletecharacter", jsonParser, async function (request, response) {
return response.sendStatus(403);
}
if (request.body.delete_chats == 'true') {
if (request.body.delete_chats == true) {
try {
await fs.promises.rm(path.join(chatsPath, sanitize(dir_name)), { recursive: true, force: true })
} catch (err) {
@ -3475,7 +3475,7 @@ app.post("/tokenize_via_api", jsonParser, async function (request, response) {
body: JSON.stringify({ "prompt": text }),
headers: { "Content-Type": "application/json" }
};
if (main_api == 'textgenerationwebui' && request.body.use_mancer) {
args.headers = Object.assign(args.headers, get_mancer_headers());
}