live2d -> talking head
This commit is contained in:
commit
5feebd4897
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "sillytavern",
|
||||
"version": "1.9.6",
|
||||
"version": "1.9.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sillytavern",
|
||||
"version": "1.9.6",
|
||||
"version": "1.9.7",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@dqbd/tiktoken": "^1.0.2",
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
"type": "git",
|
||||
"url": "https://github.com/SillyTavern/SillyTavern.git"
|
||||
},
|
||||
"version": "1.9.6",
|
||||
"version": "1.9.7",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"start-multi": "node server.js --disableCsrf",
|
||||
|
|
|
@ -608,6 +608,10 @@
|
|||
<input type="number" id="openai_max_tokens" name="openai_max_tokens" class="text_pole" min="50" max="1000">
|
||||
</div>
|
||||
</div>
|
||||
<div data-source="openrouter">
|
||||
Max prompt cost: <span id="openrouter_max_prompt_cost">Unknown</span>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="range-block" data-source="openai,claude,windowai,openrouter">
|
||||
<div class="range-block-title" data-i18n="Temperature">
|
||||
Temperature
|
||||
|
@ -787,21 +791,45 @@
|
|||
</label>
|
||||
</div>
|
||||
<div class="range-block flexFlowColumn">
|
||||
<div class="range-block-title" data-i18n="Samplers Order">
|
||||
Samplers Order
|
||||
<div class="range-block-title">
|
||||
<span data-i18n="Samplers Order">Samplers Order</span>
|
||||
</div>
|
||||
<div class="toggle-description" data-i18n="Samplers will be applied in a top-down order. Use with caution.">
|
||||
Samplers will be applied in a top-down order.
|
||||
Use with caution.
|
||||
</div>
|
||||
<div id="kobold_order">
|
||||
<div data-id="0" data-i18n="Top K">Top K</div>
|
||||
<div data-id="1" data-i18n="Top A">Top A</div>
|
||||
<div data-id="2" data-i18n="Top P">Top P</div>
|
||||
<div data-id="3" data-i18n="Tail Free Sampling">Tail Free Sampling</div>
|
||||
<div data-id="4" data-i18n="Typical Sampling">Typical Sampling</div>
|
||||
<div data-id="5" data-i18n="Temperature">Temperature</div>
|
||||
<div data-id="6" data-i18n="Repetition Penalty">Repetition Penalty</div>
|
||||
<div data-id="0">
|
||||
<span data-i18n="Top K">Top K</span>
|
||||
<small>0</small>
|
||||
</div>
|
||||
<div data-id="1">
|
||||
<span data-i18n="Top A">Top A</span>
|
||||
<small>1</small>
|
||||
</div>
|
||||
<div data-id="2">
|
||||
<span data-i18n="Top P">Top P</span>
|
||||
<small>2</small>
|
||||
</div>
|
||||
<div data-id="3">
|
||||
<span data-i18n="Tail Free Sampling">Tail Free Sampling</span>
|
||||
<small>3</small>
|
||||
</div>
|
||||
<div data-id="4">
|
||||
<span data-i18n="Typical Sampling">Typical Sampling</span>
|
||||
<small>4</small>
|
||||
</div>
|
||||
<div data-id="5">
|
||||
<span data-i18n="Temperature">Temperature</span>
|
||||
<small>5</small>
|
||||
</div>
|
||||
<div data-id="6">
|
||||
<span data-i18n="Repetition Penalty">Repetition Penalty</span>
|
||||
<small>6</small>
|
||||
</div>
|
||||
</div>
|
||||
<div id="samplers_order_recommended" class="menu_button menu_button_icon">
|
||||
<span data-i18n="Load koboldcpp order">Load koboldcpp order</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2113,7 +2141,7 @@
|
|||
<div class="justifyContentSpaceAround wi-settings flex-container gap10px alignitemscenter">
|
||||
<div id="WIMultiSelector" class="flex2 flex alignSelfStart range-block">
|
||||
<div class="range-block-title justifyLeft">
|
||||
<span data-i18n="Active World(s)"><small>Active World(s)</small></span>
|
||||
<span data-i18n="Active World(s) for all chats"><small>Active World(s) for all chats</small></span>
|
||||
</div>
|
||||
<div class="range-block-range">
|
||||
<select id="world_info" multiple>
|
||||
|
@ -2158,7 +2186,7 @@
|
|||
|
||||
<div class="flex1 gap5px range-block">
|
||||
<div class="wide10pMinFit">
|
||||
<small data-i18n="Token Budget">Context %</small>
|
||||
<small data-i18n="Context %">Context %</small>
|
||||
</div>
|
||||
<div class="range-block-range-and-counter ">
|
||||
<div class="range-block-range paddingLeftRight5">
|
||||
|
@ -2171,6 +2199,25 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex1 gap5px range-block">
|
||||
<div class="wide10pMinFit">
|
||||
<small data-i18n="Budget Cap">Budget Cap</small>
|
||||
</div>
|
||||
<div class="range-block-range-and-counter ">
|
||||
<div class="range-block-range paddingLeftRight5">
|
||||
<input type="range" id="world_info_budget_cap" name="volume" min="0" max="8192" step="256">
|
||||
</div>
|
||||
<div class="range-block-counter margin0">
|
||||
<div contenteditable="true" data-for="world_info_budget_cap" id="world_info_budget_cap_counter">
|
||||
0
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="budget_cap_note">
|
||||
<small data-i18n="(0 = disabled)">(0 = disabled)</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -2479,11 +2526,11 @@
|
|||
<div id="power-user-options-block" class="flex-container drawer33pWidth">
|
||||
<div id="power-user-option-checkboxes">
|
||||
<h4 data-i18n="Power User Options">Power User Options</h4>
|
||||
<label for="swipes-checkbox">
|
||||
<label class="checkbox_label" for="swipes-checkbox">
|
||||
<input id="swipes-checkbox" type="checkbox" />
|
||||
<span data-i18n="Swipes">Swipes</span>
|
||||
</label>
|
||||
<label for="fuzzy_search_checkbox">
|
||||
<label class="checkbox_label" for="fuzzy_search_checkbox">
|
||||
<input id="fuzzy_search_checkbox" type="checkbox" />
|
||||
<span data-i18n="Advanced Character Search">Advanced Character Search</span>
|
||||
</label>
|
||||
|
@ -2501,13 +2548,15 @@
|
|||
Press "Send" to continue
|
||||
</span>
|
||||
</label>
|
||||
<label for="auto-load-chat-checkbox"><input id="auto-load-chat-checkbox" type="checkbox" />
|
||||
<label class="checkbox_label" for="auto-load-chat-checkbox">
|
||||
<input id="auto-load-chat-checkbox" type="checkbox" />
|
||||
<span data-i18n="Auto-load Last Chat">Auto-load Last Chat</span>
|
||||
</label>
|
||||
<label for="auto_save_msg_edits"><input id="auto_save_msg_edits" type="checkbox" />
|
||||
<label class="checkbox_label" for="auto_save_msg_edits">
|
||||
<input id="auto_save_msg_edits" type="checkbox" />
|
||||
<span data-i18n="Auto-save Message Edits">Auto-save Message Edits</span>
|
||||
</label>
|
||||
<label for="auto_fix_generated_markdown">
|
||||
<label class="checkbox_label" for="auto_fix_generated_markdown">
|
||||
<input id="auto_fix_generated_markdown" type="checkbox" />
|
||||
<span data-i18n="Auto-fix Markdown">Auto-fix Markdown</span>
|
||||
</label>
|
||||
|
@ -2519,37 +2568,43 @@
|
|||
<input id="allow_name1_display" type="checkbox" />
|
||||
<span data-i18n="Allow {{user}}: in bot messages">Show {{user}}: in responses</span>
|
||||
</label>
|
||||
<!-- <label class="checkbox_label" for="removeXML">
|
||||
<input id="removeXML" type="checkbox" />
|
||||
<span data-i18n="Remove XML/HTML tags from responses">Remove <tags> from responses</span>
|
||||
</label> -->
|
||||
<label for="console_log_prompts">
|
||||
<label class="checkbox_label" for="encode_tags">
|
||||
<input id="encode_tags" type="checkbox" />
|
||||
<span data-i18n="Show tags in responses">Show <tags> in responses</span>
|
||||
</label>
|
||||
<label class="checkbox_label" for="console_log_prompts">
|
||||
<input id="console_log_prompts" type="checkbox" />
|
||||
<span data-i18n="Log prompts to console">Log prompts to console</span>
|
||||
</label>
|
||||
<label for="render_formulas">
|
||||
<label class="checkbox_label" for="render_formulas">
|
||||
<input id="render_formulas" type="checkbox" />
|
||||
<span data-i18n="Render Formulas">Render Formulas</span>
|
||||
<a href="https://docs.sillytavern.app/usage/core-concepts/uicustomization/#formulas-rendering" class="notes-link" target="_blank">
|
||||
<span class="note-link-span">?</span>
|
||||
</a>
|
||||
</label>
|
||||
<label for="never_resize_avatars"><input id="never_resize_avatars" type="checkbox" />
|
||||
<label class="checkbox_label" for="never_resize_avatars">
|
||||
<input id="never_resize_avatars" type="checkbox" />
|
||||
<span data-i18n="Never resize avatars">Never resize avatars</span>
|
||||
</label>
|
||||
<label for="show_card_avatar_urls"><input id="show_card_avatar_urls" type="checkbox" />
|
||||
<label class="checkbox_label" for="show_card_avatar_urls">
|
||||
<input id="show_card_avatar_urls" type="checkbox" />
|
||||
<span data-i18n="Show avatar filenames">Show avatar filenames</span>
|
||||
</label>
|
||||
<label for="import_card_tags"><input id="import_card_tags" type="checkbox" />
|
||||
<label class="checkbox_label" for="import_card_tags">
|
||||
<input id="import_card_tags" type="checkbox" />
|
||||
<span data-i18n="Import Card Tags">Import Card Tags</span>
|
||||
</label>
|
||||
<label for="confirm_message_delete"><input id="confirm_message_delete" type="checkbox" />
|
||||
<label class="checkbox_label" for="confirm_message_delete">
|
||||
<input id="confirm_message_delete" type="checkbox" />
|
||||
<span data-i18n="Confirm message deletion">Confirm message deletion</span>
|
||||
</label>
|
||||
<label for="spoiler_free_mode"><input id="spoiler_free_mode" type="checkbox" />
|
||||
<label class="checkbox_label" for="spoiler_free_mode">
|
||||
<input id="spoiler_free_mode" type="checkbox" />
|
||||
<span data-i18n="Spoiler Free Mode">Spoiler Free Mode</span>
|
||||
</label>
|
||||
<label for="relaxed_api_urls" title="Reduce the formatting requirements on API URLS"><input id="relaxed_api_urls" type="checkbox" />
|
||||
<label class="checkbox_label" for="relaxed_api_urls" title="Reduce the formatting requirements on API URLS">
|
||||
<input id="relaxed_api_urls" type="checkbox" />
|
||||
<span data-i18n="Relaxed API URLS">Relaxed API URLS</span>
|
||||
</label>
|
||||
|
||||
|
|
|
@ -21,17 +21,11 @@ import {
|
|||
} from "./scripts/textgen-settings.js";
|
||||
|
||||
import {
|
||||
world_info_budget,
|
||||
world_info_depth,
|
||||
world_info,
|
||||
getWorldInfoPrompt,
|
||||
getWorldInfoSettings,
|
||||
setWorldInfoSettings,
|
||||
world_info_recursive,
|
||||
world_info_overflow_alert,
|
||||
world_info_case_sensitive,
|
||||
world_info_match_whole_words,
|
||||
world_names,
|
||||
world_info_character_strategy,
|
||||
importEmbeddedWorldInfo,
|
||||
checkEmbeddedWorld,
|
||||
setWorldInfoButtonClass,
|
||||
|
@ -102,10 +96,12 @@ import {
|
|||
import {
|
||||
generateNovelWithStreaming,
|
||||
getNovelGenerationData,
|
||||
getNovelMaxContextTokens,
|
||||
getNovelTier,
|
||||
loadNovelPreset,
|
||||
loadNovelSettings,
|
||||
nai_settings,
|
||||
setNovelData,
|
||||
} from "./scripts/nai-settings.js";
|
||||
|
||||
import {
|
||||
|
@ -679,6 +675,7 @@ function reloadMarkdownProcessor(render_formulas = false) {
|
|||
converter = new showdown.Converter({
|
||||
emoji: "true",
|
||||
underline: "true",
|
||||
tables: "true",
|
||||
parseImgDimensions: "true",
|
||||
extensions: [
|
||||
showdownKatex(
|
||||
|
@ -696,6 +693,7 @@ function reloadMarkdownProcessor(render_formulas = false) {
|
|||
emoji: "true",
|
||||
literalMidWordUnderscores: "true",
|
||||
parseImgDimensions: "true",
|
||||
tables: "true",
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -795,7 +793,6 @@ let extension_prompts = {};
|
|||
|
||||
var main_api;// = "kobold";
|
||||
//novel settings
|
||||
let novel_tier;
|
||||
export let novelai_settings;
|
||||
export let novelai_setting_names;
|
||||
let abortController;
|
||||
|
@ -1269,8 +1266,10 @@ function messageFormatting(mes, ch_name, isSystem, isUser) {
|
|||
mes = fixMarkdown(mes);
|
||||
}
|
||||
|
||||
//if (this_chid != undefined && !isSystem)
|
||||
// mes = mes.replaceAll("<", "<").replaceAll(">", ">"); //for welcome message
|
||||
if (!isSystem && power_user.encode_tags) {
|
||||
mes = mes.replaceAll("<", "<").replaceAll(">", ">");
|
||||
}
|
||||
|
||||
if ((this_chid === undefined || this_chid === "invalid-safety-id") && !selected_group) {
|
||||
mes = mes
|
||||
.replace(/\*\*(.+?)\*\*/g, "<b>$1</b>")
|
||||
|
@ -2958,7 +2957,8 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
|
|||
}
|
||||
|
||||
//Formating
|
||||
getMessage = cleanUpMessage(getMessage, isImpersonate, isContinue);
|
||||
const displayIncomplete = type == 'quiet';
|
||||
getMessage = cleanUpMessage(getMessage, isImpersonate, isContinue, displayIncomplete);
|
||||
|
||||
let this_mes_is_name;
|
||||
({ this_mes_is_name, getMessage } = extractNameFromMessage(getMessage, force_name2, isImpersonate));
|
||||
|
@ -3171,14 +3171,22 @@ function getMaxContextSize() {
|
|||
this_max_context = Number(max_context);
|
||||
if (nai_settings.model_novel == 'krake-v2' || nai_settings.model_novel == 'euterpe-v2') {
|
||||
// Krake and Euterpe have a max context of 2048
|
||||
// Should be used with nerdstash tokenizer for best results
|
||||
// Should be used with classic gpt tokenizer for best results
|
||||
this_max_context = Math.min(max_context, 2048);
|
||||
}
|
||||
if (nai_settings.model_novel == 'clio-v1' || nai_settings.model_novel == 'kayra-v1') {
|
||||
// Clio and Kayra has a max context of 8192
|
||||
// Clio and Kayra have a max context of 8192
|
||||
// Should be used with nerdstash / nerdstash_v2 tokenizer for best results
|
||||
this_max_context = Math.min(max_context, 8192);
|
||||
}
|
||||
|
||||
const subscriptionLimit = getNovelMaxContextTokens();
|
||||
if (typeof subscriptionLimit === "number" && this_max_context > subscriptionLimit) {
|
||||
this_max_context = subscriptionLimit;
|
||||
console.log(`NovelAI subscription limit reached. Max context size is now ${this_max_context}`);
|
||||
}
|
||||
|
||||
this_max_context = this_max_context - amount_gen;
|
||||
}
|
||||
if (main_api == 'openai') {
|
||||
this_max_context = oai_settings.openai_max_context;
|
||||
|
@ -5149,7 +5157,7 @@ async function getSettings(type) {
|
|||
api_server = settings.api_server;
|
||||
$("#api_url_text").val(api_server);
|
||||
|
||||
setWorldInfoSettings(settings, data);
|
||||
setWorldInfoSettings(settings.world_info_settings ?? settings, data);
|
||||
|
||||
api_server_textgenerationwebui = settings.api_server_textgenerationwebui;
|
||||
$("#textgenerationwebui_api_url_text").val(
|
||||
|
@ -5197,14 +5205,7 @@ async function saveSettings(type) {
|
|||
amount_gen: amount_gen,
|
||||
max_context: max_context,
|
||||
main_api: main_api,
|
||||
world_info: world_info,
|
||||
world_info_depth: world_info_depth,
|
||||
world_info_budget: world_info_budget,
|
||||
world_info_recursive: world_info_recursive,
|
||||
world_info_overflow_alert: world_info_overflow_alert,
|
||||
world_info_case_sensitive: world_info_case_sensitive,
|
||||
world_info_match_whole_words: world_info_match_whole_words,
|
||||
world_info_character_strategy: world_info_character_strategy,
|
||||
world_info_settings: getWorldInfoSettings(),
|
||||
textgenerationwebui_settings: textgenerationwebui_settings,
|
||||
swipes: swipes,
|
||||
horde_settings: horde_settings,
|
||||
|
@ -5471,8 +5472,8 @@ async function getStatusNovel() {
|
|||
contentType: "application/json",
|
||||
success: function (data) {
|
||||
if (data.error != true) {
|
||||
novel_tier = data.tier;
|
||||
online_status = getNovelTier(novel_tier);
|
||||
setNovelData(data);
|
||||
online_status = `${getNovelTier(data.tier)} (${getNovelMaxContextTokens()} context tokens)`;
|
||||
}
|
||||
resultCheckStatusNovel();
|
||||
},
|
||||
|
|
|
@ -74,6 +74,7 @@ const extension_settings = {
|
|||
enabled: false,
|
||||
},
|
||||
speech_recognition: {},
|
||||
rvc: {},
|
||||
};
|
||||
|
||||
let modules = [];
|
||||
|
|
|
@ -515,7 +515,7 @@ async function moduleWorker() {
|
|||
|
||||
//set checkbox to global var
|
||||
$('#image_type_toggle').prop('checked', extension_settings.expressions.talkinghead);
|
||||
if(extension_settings.expressions.talkinghead == true){
|
||||
if (extension_settings.expressions.talkinghead) {
|
||||
settalkingheadState(extension_settings.expressions.talkinghead);
|
||||
}
|
||||
}
|
||||
|
@ -641,11 +641,11 @@ async function talkingheadcheck() {
|
|||
let talkingheadObj = spriteCache[spriteFolderName].find(obj => obj.label === 'talkinghead');
|
||||
let talkingheadPath_f = talkingheadObj ? talkingheadObj.path : null;
|
||||
|
||||
if(talkingheadPath_f != null){
|
||||
if (talkingheadPath_f != null) {
|
||||
//console.log("talkingheadPath_f " + talkingheadPath_f);
|
||||
return true;
|
||||
} else {
|
||||
//console.log("talkingheadPath_f is null");
|
||||
} else {
|
||||
//console.log("talkingheadPath_f is null");
|
||||
unloadLiveChar();
|
||||
return false;
|
||||
}
|
||||
|
@ -654,7 +654,7 @@ async function talkingheadcheck() {
|
|||
}
|
||||
}
|
||||
|
||||
function settalkingheadState(switch_var){
|
||||
function settalkingheadState(switch_var) {
|
||||
extension_settings.expressions.talkinghead = switch_var; // Store setting
|
||||
saveSettingsDebounced();
|
||||
|
||||
|
@ -662,22 +662,18 @@ function settalkingheadState(switch_var){
|
|||
if (result) {
|
||||
//console.log("talkinghead exists!");
|
||||
|
||||
if (extension_settings.expressions.talkinghead) {
|
||||
loadLiveChar();
|
||||
} else {
|
||||
unloadLiveChar();
|
||||
}
|
||||
handleImageChange(switch_var); // Change image as needed
|
||||
if (extension_settings.expressions.talkinghead) {
|
||||
loadLiveChar();
|
||||
} else {
|
||||
unloadLiveChar();
|
||||
}
|
||||
handleImageChange(switch_var); // Change image as needed
|
||||
|
||||
|
||||
} else {
|
||||
//console.log("talkinghead does not exist.");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
function getSpriteFolderName(message) {
|
||||
|
@ -867,8 +863,7 @@ async function getExpressionsList() {
|
|||
}
|
||||
|
||||
async function setExpression(character, expression, force) {
|
||||
if (extension_settings.expressions.talkinghead == false) {
|
||||
|
||||
if (!extension_settings.expressions.talkinghead) {
|
||||
console.debug('entered setExpressions');
|
||||
await validateImages(character);
|
||||
const img = $('img.expression');
|
||||
|
@ -961,10 +956,11 @@ async function setExpression(character, expression, force) {
|
|||
setDefault();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (extension_settings.expressions.showDefault) {
|
||||
setDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (extension_settings.expressions.showDefault) {
|
||||
setDefault();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -982,14 +978,14 @@ async function setExpression(character, expression, force) {
|
|||
|
||||
talkingheadcheck().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/talkinghead/result_feed';
|
||||
}
|
||||
|
||||
// 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/talkinghead/result_feed';
|
||||
}
|
||||
|
||||
} else {
|
||||
//console.log("The fetch failed!");
|
||||
}
|
||||
|
@ -1254,14 +1250,15 @@ function setExpressionOverrideHtml(forceClear = false) {
|
|||
</div>
|
||||
|
||||
<div class="inline-drawer-content">
|
||||
<!-- Toggle button for aituber/static images -->
|
||||
<!-- Toggle button for aituber/static images -->
|
||||
<div class="toggle_button">
|
||||
<label class="switch">
|
||||
<label class="switch">
|
||||
<input id="image_type_toggle" type="checkbox">
|
||||
<span class="slider round"></span>
|
||||
<label for="image_type_toggle">Image Type - talkinghead (extras)</label>
|
||||
</div>
|
||||
<div class="offline_mode">
|
||||
<label for="image_type_toggle">Image Type - talkinghead (extras)</label>
|
||||
</label>
|
||||
</div>
|
||||
<div class="offline_mode">
|
||||
<small>You are in offline mode. Click on the image below to set the expression.</small>
|
||||
</div>
|
||||
<div class="flex-container flexnowrap">
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
TODO:
|
||||
- Allow to upload RVC model to extras server ?
|
||||
- Settings per characters ?
|
||||
*/
|
||||
|
||||
import { saveSettingsDebounced } from "../../../script.js";
|
||||
import { getContext, getApiUrl, extension_settings, doExtrasFetch } from "../../extensions.js";
|
||||
export { MODULE_NAME, rvcVoiceConversion};
|
||||
|
||||
const MODULE_NAME = 'RVC';
|
||||
const DEBUG_PREFIX = "<RVC module> "
|
||||
|
||||
// Send an audio file to RVC to convert voice
|
||||
async function rvcVoiceConversion(response, character) {
|
||||
let apiResult
|
||||
|
||||
// Check voice map
|
||||
if (extension_settings.rvc.voiceMap[character] === undefined) {
|
||||
toastr.error("No model is assigned to character '"+character+"', check RVC voice map in the extension menu.", 'RVC Voice map error', { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
||||
console.error("No RVC model assign in voice map for current character "+character);
|
||||
return response;
|
||||
}
|
||||
|
||||
// Load model if different from currently loaded
|
||||
//if (currentModel === null | currentModel != extension_settings.rvc.voiceMap[character])
|
||||
// await rvcLoadModel(extension_settings.rvc.voiceMap[character]);
|
||||
|
||||
const audioData = await response.blob()
|
||||
if (!audioData.type in ['audio/mpeg', 'audio/wav', 'audio/x-wav', 'audio/wave', 'audio/webm']) {
|
||||
throw `TTS received HTTP response with invalid data format. Expecting audio/mpeg, got ${audioData.type}`
|
||||
}
|
||||
console.log("Audio type received:",audioData.type)
|
||||
|
||||
console.log("Sending tts audio data to RVC on extras server")
|
||||
|
||||
var requestData = new FormData();
|
||||
requestData.append('AudioFile', audioData, 'record');
|
||||
requestData.append("json", JSON.stringify({
|
||||
"modelName": extension_settings.rvc.voiceMap[character],
|
||||
"pitchOffset": extension_settings.rvc.pitchOffset,
|
||||
"pitchExtraction": extension_settings.rvc.pitchExtraction,
|
||||
"indexRate": extension_settings.rvc.indexRate,
|
||||
"filterRadius": extension_settings.rvc.filterRadius,
|
||||
//"rmsMixRate": extension_settings.rvc.rmsMixRate,
|
||||
"protect": extension_settings.rvc.protect
|
||||
}));
|
||||
|
||||
const url = new URL(getApiUrl());
|
||||
url.pathname = '/api/voice-conversion/rvc/process-audio';
|
||||
|
||||
apiResult = await doExtrasFetch(url, {
|
||||
method: 'POST',
|
||||
body: requestData,
|
||||
});
|
||||
|
||||
if (!apiResult.ok) {
|
||||
toastr.error(apiResult.statusText, 'RVC Voice Conversion Failed', { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
||||
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
|
||||
}
|
||||
|
||||
return apiResult;
|
||||
}
|
||||
|
||||
//#############################//
|
||||
// Extension UI and Settings //
|
||||
//#############################//
|
||||
|
||||
const defaultSettings = {
|
||||
enabled: false,
|
||||
model:"",
|
||||
pitchOffset:0,
|
||||
pitchExtraction:"dio",
|
||||
indexRate:0.88,
|
||||
filterRadius:3,
|
||||
//rmsMixRate:1,
|
||||
protect:0.33,
|
||||
voicMapText: "",
|
||||
voiceMap: {}
|
||||
}
|
||||
|
||||
function loadSettings() {
|
||||
if (Object.keys(extension_settings.rvc).length === 0) {
|
||||
Object.assign(extension_settings.rvc, defaultSettings)
|
||||
}
|
||||
$('#rvc_enabled').prop('checked',extension_settings.rvc.enabled);
|
||||
$('#rvc_model').val(extension_settings.rvc.model);
|
||||
|
||||
$('#rvc_pitch_offset').val(extension_settings.rvc.pitchOffset);
|
||||
$('#rvc_pitch_offset_value').text(extension_settings.rvc.pitchOffset);
|
||||
|
||||
$('#rvc_pitch_extraction').val(extension_settings.rvc.pitchExtraction);
|
||||
$('#rvc_pitch_extractiont_value').text(extension_settings.rvc.pitchExtraction);
|
||||
|
||||
$('#rvc_index_rate').val(extension_settings.rvc.indexRate);
|
||||
$('#rvc_index_rate_value').text(extension_settings.rvc.indexRate);
|
||||
|
||||
$('#rvc_filter_radius').val(extension_settings.rvc.filterRadius);
|
||||
$("#rvc_filter_radius_value").text(extension_settings.rvc.filterRadius);
|
||||
|
||||
//$('#rvc_mix_rate').val(extension_settings.rvc.rmsMixRate);
|
||||
$('#rvc_protect').val(extension_settings.rvc.protect);
|
||||
$("#rvc_protect_value").text(extension_settings.rvc.protect);
|
||||
|
||||
$('#rvc_voice_map').val(extension_settings.rvc.voiceMapText);
|
||||
}
|
||||
|
||||
async function onApplyClick() {
|
||||
let error = false;
|
||||
let array = $('#rvc_voice_map').val().split(",");
|
||||
array = array.map(element => {return element.trim();});
|
||||
array = array.filter((str) => str !== '');
|
||||
extension_settings.rvc.voiceMap = {};
|
||||
for (const text of array) {
|
||||
if (text.includes("=")) {
|
||||
const pair = text.split("=")
|
||||
extension_settings.rvc.voiceMap[pair[0].trim()] = pair[1].trim()
|
||||
console.debug(DEBUG_PREFIX+"Added mapping", pair[0],"=>", extension_settings.rvc.voiceMap[pair[0]]);
|
||||
}
|
||||
else {
|
||||
$("#rvc_status").text("Voice map is invalid, check console for errors");
|
||||
$("#rvc_status").css("color", "red");
|
||||
console.error(DEBUG_PREFIX+"Wrong syntax for message mapping, no '=' found in:", text);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
$("#rvc_status").text("Successfully applied settings");
|
||||
$("#rvc_status").css("color", "green");
|
||||
console.debug(DEBUG_PREFIX+"Updated message mapping", extension_settings.rvc.voiceMap);
|
||||
extension_settings.rvc.voiceMapText = $('#rvc_voice_map').val();
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
}
|
||||
|
||||
async function onEnabledClick() {
|
||||
extension_settings.rvc.enabled = $('#rvc_enabled').is(':checked');
|
||||
saveSettingsDebounced()
|
||||
}
|
||||
|
||||
async function onPitchExtractionChange() {
|
||||
extension_settings.rvc.pitchExtraction = $('#rvc_pitch_extraction').val();
|
||||
saveSettingsDebounced()
|
||||
}
|
||||
|
||||
async function onIndexRateChange() {
|
||||
extension_settings.rvc.indexRate = Number($('#rvc_index_rate').val());
|
||||
$("#rvc_index_rate_value").text(extension_settings.rvc.indexRate)
|
||||
saveSettingsDebounced()
|
||||
}
|
||||
|
||||
async function onFilterRadiusChange() {
|
||||
extension_settings.rvc.filterRadius = Number($('#rvc_filter_radius').val());
|
||||
$("#rvc_filter_radius_value").text(extension_settings.rvc.filterRadius)
|
||||
saveSettingsDebounced()
|
||||
}
|
||||
|
||||
async function onPitchOffsetChange() {
|
||||
extension_settings.rvc.pitchOffset = Number($('#rvc_pitch_offset').val());
|
||||
$("#rvc_pitch_offset_value").text(extension_settings.rvc.pitchOffset)
|
||||
saveSettingsDebounced()
|
||||
}
|
||||
|
||||
async function onProtectChange() {
|
||||
extension_settings.rvc.protect = Number($('#rvc_protect').val());
|
||||
$("#rvc_protect_value").text(extension_settings.rvc.protect)
|
||||
saveSettingsDebounced()
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
function addExtensionControls() {
|
||||
const settingsHtml = `
|
||||
<div id="rvc_settings">
|
||||
<div class="inline-drawer">
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>RVC</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<div>
|
||||
<label class="checkbox_label" for="rvc_enabled">
|
||||
<input type="checkbox" id="rvc_enabled" name="rvc_enabled">
|
||||
<small>Enabled</small>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<span>Select Pitch Extraction</span> </br>
|
||||
<select id="rvc_pitch_extraction">
|
||||
<option value="dio">dio</option>
|
||||
<option value="pm">pm</option>
|
||||
<option value="harvest">harvest</option>
|
||||
<option value="torchcrepe">torchcrepe</option>
|
||||
<option value="rmvpe">rmvpe</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="rvc_index_rate">
|
||||
Index rate for feature retrieval (<span id="rvc_index_rate_value"></span>)
|
||||
</label>
|
||||
<input id="rvc_index_rate" type="range" min="0" max="1" step="0.01" value="0.5" />
|
||||
|
||||
<label for="rvc_filter_radius">Filter radius (<span id="rvc_filter_radius_value"></span>)</label>
|
||||
<input id="rvc_filter_radius" type="range" min="0" max="7" step="1" value="3" />
|
||||
|
||||
<label for="rvc_pitch_offset">Pitch offset (<span id="rvc_pitch_offset_value"></span>)</label>
|
||||
<input id="rvc_pitch_offset" type="range" min="-100" max="100" step="1" value="0" />
|
||||
|
||||
<label for="rvc_protect">Protect amount (<span id="rvc_protect_value"></span>)</label>
|
||||
<input id="rvc_protect" type="range" min="0" max="1" step="0.01" value="0.33" />
|
||||
<label>Voice Map</label>
|
||||
<textarea id="rvc_voice_map" type="text" class="text_pole textarea_compact" rows="4"
|
||||
placeholder="Enter comma separated map of charName:rvcModel. Example: \nAqua:Bella,\nYou:Josh,"></textarea>
|
||||
<div id="rvc_status">
|
||||
</div>
|
||||
<div class="rvc_buttons">
|
||||
<input id="rvc_apply" class="menu_button" type="submit" value="Apply" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
$('#extensions_settings').append(settingsHtml);
|
||||
$("#rvc_enabled").on("click", onEnabledClick);
|
||||
$('#rvc_pitch_extraction').on('change', onPitchExtractionChange);
|
||||
$('#rvc_index_rate').on('input', onIndexRateChange);
|
||||
$('#rvc_filter_radius').on('input', onFilterRadiusChange);
|
||||
$('#rvc_pitch_offset').on('input', onPitchOffsetChange);
|
||||
$('#rvc_protect').on('input', onProtectChange);
|
||||
$("#rvc_apply").on("click", onApplyClick);
|
||||
|
||||
}
|
||||
addExtensionControls(); // No init dependencies
|
||||
loadSettings(); // Depends on Extension Controls
|
||||
})
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"display_name": "RVC",
|
||||
"loading_order": 13,
|
||||
"requires": ["rvc"],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "style.css",
|
||||
"author": "Keij#6799",
|
||||
"version": "0.1.0",
|
||||
"homePage": "https://github.com/SillyTavern/SillyTavern"
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.speech-toggle {
|
||||
display: flex;
|
||||
}
|
|
@ -8,6 +8,7 @@ import { CoquiTtsProvider } from './coquitts.js'
|
|||
import { SystemTtsProvider } from './system.js'
|
||||
import { NovelTtsProvider } from './novel.js'
|
||||
import { power_user } from '../../power-user.js'
|
||||
import { rvcVoiceConversion } from "../rvc/index.js"
|
||||
|
||||
const UPDATE_INTERVAL = 1000
|
||||
|
||||
|
@ -399,8 +400,13 @@ function saveLastValues() {
|
|||
)
|
||||
}
|
||||
|
||||
async function tts(text, voiceId) {
|
||||
const response = await ttsProvider.generateTts(text, voiceId)
|
||||
async function tts(text, voiceId, char) {
|
||||
let response = await ttsProvider.generateTts(text, voiceId)
|
||||
|
||||
// RVC injection
|
||||
if (extension_settings.rvc.enabled)
|
||||
response = await rvcVoiceConversion(response, char)
|
||||
|
||||
addAudioJob(response)
|
||||
completeTtsJob()
|
||||
}
|
||||
|
@ -450,7 +456,7 @@ async function processTtsQueue() {
|
|||
toastr.error(`Specified voice for ${char} was not found. Check the TTS extension settings.`)
|
||||
throw `Unable to attain voiceId for ${char}`
|
||||
}
|
||||
tts(text, voiceId)
|
||||
tts(text, voiceId, char)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
currentTtsJob = null
|
||||
|
@ -567,6 +573,7 @@ function onEnableClick() {
|
|||
saveSettingsDebounced()
|
||||
}
|
||||
|
||||
|
||||
function onAutoGenerationClick() {
|
||||
extension_settings.tts.auto_generation = $('#tts_auto_generation').prop('checked');
|
||||
saveSettingsDebounced()
|
||||
|
|
|
@ -35,6 +35,7 @@ const kai_settings = {
|
|||
|
||||
const MIN_STOP_SEQUENCE_VERSION = '1.2.2';
|
||||
const MIN_STREAMING_KCPPVERSION = '1.30';
|
||||
const KOBOLDCPP_ORDER = [6, 0, 1, 3, 4, 2, 5];
|
||||
|
||||
function formatKoboldUrl(value) {
|
||||
try {
|
||||
|
@ -143,7 +144,7 @@ export async function generateKoboldWithStreaming(generate_data, signal) {
|
|||
|
||||
if (done) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -276,4 +277,10 @@ $(document).ready(function () {
|
|||
saveSettingsDebounced();
|
||||
},
|
||||
});
|
||||
|
||||
$('#samplers_order_recommended').on('click', function () {
|
||||
kai_settings.sampler_order = KOBOLDCPP_ORDER;
|
||||
sortItemsByOrder(kai_settings.sampler_order);
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -41,6 +41,16 @@ const nai_tiers = {
|
|||
3: 'Opus',
|
||||
};
|
||||
|
||||
let novel_data = null;
|
||||
|
||||
export function setNovelData(data) {
|
||||
novel_data = data;
|
||||
}
|
||||
|
||||
export function getNovelMaxContextTokens() {
|
||||
return novel_data?.perks?.contextTokens;
|
||||
}
|
||||
|
||||
function getNovelTier(tier) {
|
||||
return nai_tiers[tier] ?? 'no_connection';
|
||||
}
|
||||
|
|
|
@ -692,18 +692,43 @@ function getChatCompletionModel() {
|
|||
}
|
||||
}
|
||||
|
||||
function calculateOpenRouterCost() {
|
||||
if (oai_settings.chat_completion_source !== chat_completion_sources.OPENROUTER) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cost = 'Unknown';
|
||||
const model = model_list.find(x => x.id === oai_settings.openrouter_model);
|
||||
|
||||
if (model?.pricing) {
|
||||
const completionCost = Number(model.pricing.completion);
|
||||
const promptCost = Number(model.pricing.prompt);
|
||||
const completionTokens = oai_settings.openai_max_tokens;
|
||||
const promptTokens = (oai_settings.openai_max_context - completionTokens);
|
||||
const totalCost = (completionCost * completionTokens) + (promptCost * promptTokens);
|
||||
if (!isNaN(totalCost)) {
|
||||
cost = '$' + totalCost.toFixed(3);
|
||||
}
|
||||
}
|
||||
|
||||
$('#openrouter_max_prompt_cost').text(cost);
|
||||
}
|
||||
|
||||
function saveModelList(data) {
|
||||
model_list = data.map((model) => ({ id: model.id, context_length: model.context_length }));
|
||||
model_list = data.map((model) => ({ id: model.id, context_length: model.context_length, pricing: model.pricing }));
|
||||
model_list.sort((a, b) => a?.id && b?.id && a.id.localeCompare(b.id));
|
||||
|
||||
if (oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER) {
|
||||
$('#model_openrouter_select').empty();
|
||||
$('#model_openrouter_select').append($('<option>', { value: openrouter_website_model, text: 'Use OpenRouter website setting' }));
|
||||
model_list.forEach((model) => {
|
||||
let tokens_dollar = parseFloat(1 / (1000 * model.pricing.prompt));
|
||||
let tokens_rounded = (Math.round(tokens_dollar * 1000) / 1000).toFixed(0);
|
||||
let model_description = `${model.id} | ${tokens_rounded}k t/$ | ${model.context_length} ctx`;
|
||||
$('#model_openrouter_select').append(
|
||||
$('<option>', {
|
||||
value: model.id,
|
||||
text: model.id,
|
||||
text: model_description,
|
||||
}));
|
||||
});
|
||||
$('#model_openrouter_select').val(oai_settings.openrouter_model).trigger('change');
|
||||
|
@ -1801,6 +1826,8 @@ async function onModelChange() {
|
|||
oai_settings.temp_openai = Math.min(oai_max_temp, oai_settings.temp_openai);
|
||||
$('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
|
||||
}
|
||||
|
||||
calculateOpenRouterCost();
|
||||
}
|
||||
|
||||
if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
|
||||
|
@ -2038,11 +2065,13 @@ $(document).ready(function () {
|
|||
$(document).on('input', '#openai_max_context', function () {
|
||||
oai_settings.openai_max_context = parseInt($(this).val());
|
||||
$('#openai_max_context_counter').text(`${$(this).val()}`);
|
||||
calculateOpenRouterCost();
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$(document).on('input', '#openai_max_tokens', function () {
|
||||
oai_settings.openai_max_tokens = parseInt($(this).val());
|
||||
calculateOpenRouterCost();
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
|
|
|
@ -192,6 +192,7 @@ let power_user = {
|
|||
custom_stopping_strings: '',
|
||||
custom_stopping_strings_macro: true,
|
||||
fuzzy_search: false,
|
||||
encode_tags: false,
|
||||
};
|
||||
|
||||
let themes = [];
|
||||
|
@ -686,6 +687,7 @@ function loadPowerUserSettings(settings, data) {
|
|||
$("#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);
|
||||
$('#encode_tags').prop("checked", power_user.encode_tags);
|
||||
|
||||
$("#console_log_prompts").prop("checked", power_user.console_log_prompts);
|
||||
$('#auto_fix_generated_markdown').prop("checked", power_user.auto_fix_generated_markdown);
|
||||
|
@ -2032,6 +2034,12 @@ $(document).ready(() => {
|
|||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#encode_tags').on('input', async function () {
|
||||
power_user.encode_tags = !!$(this).prop('checked');
|
||||
await reloadCurrentChat();
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$(window).on('focus', function () {
|
||||
browser_has_focus = true;
|
||||
});
|
||||
|
|
|
@ -14,6 +14,7 @@ export {
|
|||
world_info_case_sensitive,
|
||||
world_info_match_whole_words,
|
||||
world_info_character_strategy,
|
||||
world_info_budget_cap,
|
||||
world_names,
|
||||
checkWorldInfo,
|
||||
deleteWorldInfo,
|
||||
|
@ -37,6 +38,7 @@ let world_info_overflow_alert = false;
|
|||
let world_info_case_sensitive = false;
|
||||
let world_info_match_whole_words = false;
|
||||
let world_info_character_strategy = world_info_insertion_strategy.character_first;
|
||||
let world_info_budget_cap = 0;
|
||||
const saveWorldDebounced = debounce(async (name, data) => await _save(name, data), 1000);
|
||||
const saveSettingsDebounced = debounce(() => {
|
||||
Object.assign(world_info, { globalSelect: selected_world_info })
|
||||
|
@ -44,6 +46,20 @@ const saveSettingsDebounced = debounce(() => {
|
|||
}, 1000);
|
||||
const sortFn = (a, b) => b.order - a.order;
|
||||
|
||||
export function getWorldInfoSettings() {
|
||||
return {
|
||||
world_info,
|
||||
world_info_depth,
|
||||
world_info_budget,
|
||||
world_info_recursive,
|
||||
world_info_overflow_alert,
|
||||
world_info_case_sensitive,
|
||||
world_info_match_whole_words,
|
||||
world_info_character_strategy,
|
||||
world_info_budget_cap,
|
||||
}
|
||||
}
|
||||
|
||||
const world_info_position = {
|
||||
before: 0,
|
||||
after: 1,
|
||||
|
@ -80,6 +96,8 @@ function setWorldInfoSettings(settings, data) {
|
|||
world_info_match_whole_words = Boolean(settings.world_info_match_whole_words);
|
||||
if (settings.world_info_character_strategy !== undefined)
|
||||
world_info_character_strategy = Number(settings.world_info_character_strategy);
|
||||
if (settings.world_info_budget_cap !== undefined)
|
||||
world_info_budget_cap = Number(settings.world_info_budget_cap);
|
||||
|
||||
// Migrate old settings
|
||||
if (world_info_budget > 100) {
|
||||
|
@ -113,6 +131,9 @@ function setWorldInfoSettings(settings, data) {
|
|||
$(`#world_info_character_strategy option[value='${world_info_character_strategy}']`).prop('selected', true);
|
||||
$("#world_info_character_strategy").val(world_info_character_strategy);
|
||||
|
||||
$("#world_info_budget_cap").val(world_info_budget_cap);
|
||||
$("#world_info_budget_cap_counter").text(world_info_budget_cap);
|
||||
|
||||
world_names = data.world_names?.length ? data.world_names : [];
|
||||
|
||||
// Add to existing selected WI if it exists
|
||||
|
@ -922,8 +943,14 @@ async function checkWorldInfo(chat, maxContext) {
|
|||
let failedProbabilityChecks = new Set();
|
||||
let allActivatedText = '';
|
||||
|
||||
const budget = Math.round(world_info_budget * maxContext / 100) || 1;
|
||||
console.debug(`Context size: ${maxContext}; WI budget: ${budget} (${world_info_budget}%)`);
|
||||
let budget = Math.round(world_info_budget * maxContext / 100) || 1;
|
||||
|
||||
if (world_info_budget_cap > 0 && budget > world_info_budget_cap) {
|
||||
console.debug(`Budget ${budget} exceeds cap ${world_info_budget_cap}, using cap`);
|
||||
budget = world_info_budget_cap;
|
||||
}
|
||||
|
||||
console.debug(`Context size: ${maxContext}; WI budget: ${budget} (max% = ${world_info_budget}%, cap = ${world_info_budget_cap})`);
|
||||
const sortedEntries = await getSortedEntries();
|
||||
|
||||
if (sortedEntries.length === 0) {
|
||||
|
@ -1515,6 +1542,12 @@ jQuery(() => {
|
|||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#world_info_budget_cap').on('input', function () {
|
||||
world_info_budget_cap = Number($(this).val());
|
||||
$("#world_info_budget_cap_counter").text(world_info_budget_cap);
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#world_button').on('click', async function () {
|
||||
const chid = $('#set_character_world').data('chid');
|
||||
|
||||
|
|
|
@ -188,6 +188,18 @@ table.responsiveTable {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.mes_text table {
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.mes_text td,
|
||||
.mes_text th {
|
||||
border: 1px solid;
|
||||
border-collapse: collapse;
|
||||
padding: 0.25em;
|
||||
}
|
||||
|
||||
.mes_text p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
|
@ -222,6 +234,10 @@ table.responsiveTable {
|
|||
font-weight: 500;
|
||||
}
|
||||
|
||||
.mes_text rp {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mes_text blockquote {
|
||||
border-left: 3px solid var(--SmartThemeQuoteColor);
|
||||
padding-left: 10px;
|
||||
|
@ -2160,6 +2176,9 @@ grammarly-extension {
|
|||
transition: background-color 200ms ease-in-out;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#kobold_order>div:hover {
|
||||
|
@ -2240,6 +2259,11 @@ grammarly-extension {
|
|||
gap: 5px;
|
||||
}
|
||||
|
||||
.budget_cap_note {
|
||||
flex-basis: 100%;
|
||||
line-height: 0.1;
|
||||
}
|
||||
|
||||
#world_popup {
|
||||
min-height: 100px;
|
||||
min-width: 100px;
|
||||
|
@ -2772,7 +2796,6 @@ input[type="range"]::-webkit-slider-thumb {
|
|||
#power-user-option-checkboxes {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 5px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
|
|
18
server.js
18
server.js
|
@ -2964,8 +2964,22 @@ app.post("/getstatus_openai", jsonParser, function (request, response_getstatus_
|
|||
client.get(api_url + "/models", args, function (data, response) {
|
||||
if (response.statusCode == 200) {
|
||||
response_getstatus_openai.send(data);
|
||||
const modelIds = data?.data?.map(x => x.id)?.sort();
|
||||
console.log('Available OpenAI models:', modelIds);
|
||||
if (request.body.use_openrouter) {
|
||||
let models = [];
|
||||
data.data.forEach(model => {
|
||||
const context_length = model.context_length;
|
||||
const tokens_dollar = parseFloat(1 / (1000 * model.pricing.prompt));
|
||||
const tokens_rounded = (Math.round(tokens_dollar * 1000) / 1000).toFixed(0);
|
||||
models[model.id] = {
|
||||
tokens_per_dollar: tokens_rounded + 'k',
|
||||
context_length: context_length,
|
||||
};
|
||||
});
|
||||
console.log('Available OpenRouter models:', models);
|
||||
} else {
|
||||
const modelIds = data?.data?.map(x => x.id)?.sort();
|
||||
console.log('Available OpenAI models:', modelIds);
|
||||
}
|
||||
}
|
||||
if (response.statusCode == 401) {
|
||||
console.log('Access Token is incorrect.');
|
||||
|
|
|
@ -165,7 +165,7 @@ async function loadStatsFile(chatsPath, charactersPath) {
|
|||
*/
|
||||
async function saveStatsToFile() {
|
||||
if (charStats.timestamp > lastSaveTimestamp) {
|
||||
console.debug("Saving stats to file...");
|
||||
//console.debug("Saving stats to file...");
|
||||
await writeFile(statsFilePath, JSON.stringify(charStats));
|
||||
lastSaveTimestamp = Date.now();
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue