This commit is contained in:
based 2023-08-20 03:44:06 +10:00
commit 3df439a224
68 changed files with 10549 additions and 1617 deletions

1
.gitignore vendored
View File

@ -10,6 +10,7 @@ public/css/bg_load.css
public/themes/
public/OpenAI Settings/
public/KoboldAI Settings/
public/NovelAI Settings/
public/TextGen Settings/
public/scripts/extensions/third-party/
public/stats.json

View File

@ -64,11 +64,6 @@
"collapse_newlines": false,
"pygmalion_formatting": 0,
"pin_examples": false,
"disable_description_formatting": false,
"disable_scenario_formatting": false,
"disable_personality_formatting": false,
"disable_examples_formatting": false,
"disable_start_formatting": false,
"trim_sentences": false,
"include_newline": false,
"always_force_name2": true,
@ -77,7 +72,6 @@
"multigen": false,
"multigen_first_chunk": 50,
"multigen_next_chunks": 30,
"custom_chat_separator": "",
"markdown_escape_strings": "",
"fast_ui_mode": false,
"avatar_style": 0,
@ -312,9 +306,6 @@
"None": {}
}
},
"context_settings": {
"selected_template": ""
},
"tags": [
{
"id": "1345561466591",
@ -334,21 +325,22 @@
]
},
"nai_settings": {
"temperature": 0.63,
"repetition_penalty": 1.148125,
"temperature": 1.5,
"repetition_penalty": 2.25,
"repetition_penalty_range": 2048,
"repetition_penalty_slope": 0.09,
"repetition_penalty_frequency": 0,
"repetition_penalty_presence": 0,
"repetition_penalty_presence": 0.005,
"tail_free_sampling": 0.975,
"top_k": 0,
"top_p": 0.975,
"top_a": 1,
"typical_p": 1,
"top_k": 10,
"top_p": 0.75,
"top_a": 0.08,
"typical_p": 0.975,
"min_length": 1,
"model_novel": "euterpe-v2",
"preset_settings_novel": "Classic-Euterpe",
"streaming_novel": false
"model_novel": "clio-v1",
"preset_settings_novel": "Talker-Chat-Clio",
"streaming_novel": true,
"order": [1, 5, 0, 2, 3, 4]
},
"kai_settings": {
"temp": 1,

32
package-lock.json generated
View File

@ -43,6 +43,7 @@
"simple-git": "^3.19.1",
"uniqolor": "^1.1.0",
"webp-converter": "2.3.2",
"write-file-atomic": "^5.0.1",
"ws": "^8.13.0",
"yargs": "^17.7.1",
"yauzl": "^2.10.0"
@ -1805,6 +1806,14 @@
"@types/node": "16.9.1"
}
},
"node_modules/imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"engines": {
"node": ">=0.8.19"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@ -3073,6 +3082,17 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
@ -3523,6 +3543,18 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
},
"node_modules/write-file-atomic": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz",
"integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==",
"dependencies": {
"imurmurhash": "^0.1.4",
"signal-exit": "^4.0.1"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/ws": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",

View File

@ -1,7 +1,8 @@
{
"dependencies": {
"@dqbd/tiktoken": "^1.0.2",
"@agnai/sentencepiece-js": "^1.1.1",
"@agnai/web-tokenizers": "^0.1.3",
"@dqbd/tiktoken": "^1.0.2",
"axios": "^1.4.0",
"command-exists": "^1.2.9",
"compression": "^1",
@ -30,10 +31,10 @@
"png-chunks-extract": "^1.0.0",
"response-time": "^2.3.2",
"sanitize-filename": "^1.6.3",
"@agnai/sentencepiece-js": "^1.1.1",
"simple-git": "^3.19.1",
"uniqolor": "^1.1.0",
"webp-converter": "2.3.2",
"write-file-atomic": "^5.0.1",
"ws": "^8.13.0",
"yargs": "^17.7.1",
"yauzl": "^2.10.0"

View File

@ -1,16 +1,16 @@
{
"order": [5, 0, 1, 3, 7],
"temperature": 1.35,
"max_length": 300,
"order": [5, 0, 1, 3],
"temperature": 1.16,
"max_length": 150,
"min_length": 1,
"top_k": 225,
"top_g": 8,
"typical_p": 0.975,
"tail_free_sampling": 0.984,
"repetition_penalty": 1.7,
"repetition_penalty_range": 3200,
"top_k": 175,
"typical_p": 0.96,
"tail_free_sampling": 0.994,
"repetition_penalty": 1.68,
"repetition_penalty_range": 2240,
"repetition_penalty_slope": 1.5,
"repetition_penalty_frequency": 0,
"repetition_penalty_presence": 0.02,
"repetition_penalty_presence": 0.005,
"use_cache": false,
"return_full_text": false,
"prefix": "vanilla",

View File

@ -1,7 +1,7 @@
{
"order": [6, 0, 1, 2, 3],
"temperature": 1,
"max_length": 300,
"max_length": 150,
"min_length": 1,
"top_k": 25,
"top_p": 1,

View File

@ -1,7 +1,7 @@
{
"order": [6, 2, 3, 1, 0],
"temperature": 1,
"max_length": 300,
"max_length": 150,
"min_length": 1,
"top_k": 0,
"top_p": 0.96,

View File

@ -1,7 +1,7 @@
{
"order": [2, 3, 0, 4, 1],
"temperature": 1.35,
"max_length": 300,
"max_length": 150,
"min_length": 1,
"top_k": 15,
"top_p": 0.85,

View File

@ -0,0 +1,20 @@
{
"order": [8, 6, 5, 0, 3],
"temperature": 0.9,
"max_length": 150,
"min_length": 1,
"typical_p": 0.95,
"tail_free_sampling": 0.92,
"mirostat_lr": 0.22,
"mirostat_tau": 4.95,
"repetition_penalty": 3,
"repetition_penalty_range": 4000,
"repetition_penalty_frequency": 0,
"repetition_penalty_presence": 0,
"use_cache": false,
"return_full_text": false,
"prefix": "vanilla",
"phrase_rep_pen": "off",
"cfg_scale": 1.48,
"max_context": 7800
}

View File

@ -1,7 +1,7 @@
{
"order": [4, 0, 5, 3, 2],
"temperature": 1.09,
"max_length": 300,
"max_length": 150,
"min_length": 1,
"top_p": 0.969,
"top_a": 0.09,

View File

@ -1,7 +1,7 @@
{
"order": [0, 1, 2, 3],
"temperature": 1,
"max_length": 300,
"max_length": 150,
"min_length": 1,
"top_k": 25,
"top_p": 1,

View File

@ -1,7 +1,7 @@
{
"order": [6, 0, 8, 5, 3],
"temperature": 1.5,
"max_length": 300,
"max_length": 150,
"min_length": 1,
"typical_p": 0.95,
"tail_free_sampling": 0.95,

View File

@ -1,7 +1,7 @@
{
"order": [6, 0, 4, 1, 2, 5, 3],
"temperature": 1.31,
"max_length": 300,
"max_length": 150,
"min_length": 1,
"top_k": 25,
"top_p": 0.97,

View File

@ -1,7 +1,7 @@
{
"order": [3, 4, 5, 0],
"temperature": 1.06,
"max_length": 300,
"max_length": 150,
"min_length": 1,
"top_a": 0.146,
"typical_p": 0.976,

View File

@ -1,17 +1,17 @@
{
"order": [3, 0, 5],
"temperature": 2.5,
"max_length": 300,
"max_length": 150,
"min_length": 1,
"typical_p": 0.966,
"tail_free_sampling": 0.933,
"typical_p": 0.969,
"tail_free_sampling": 0.941,
"repetition_penalty": 1,
"repetition_penalty_range": 2048,
"repetition_penalty_range": 1024,
"repetition_penalty_frequency": 0,
"repetition_penalty_presence": 0,
"use_cache": false,
"return_full_text": false,
"prefix": "vanilla",
"phrase_rep_pen": "aggressive",
"phrase_rep_pen": "medium",
"max_context": 7800
}

View File

@ -1,7 +1,7 @@
{
"order": [1, 5, 0, 2, 3, 4],
"temperature": 1.5,
"max_length": 300,
"max_length": 150,
"min_length": 1,
"top_k": 10,
"top_p": 0.75,

View File

@ -1,7 +1,7 @@
{
"order": [5, 0, 4],
"temperature": 1,
"max_length": 300,
"max_length": 150,
"min_length": 1,
"top_a": 0.017,
"typical_p": 0.975,

View File

@ -1,7 +1,7 @@
{
"order": [6, 0, 5],
"temperature": 0.895,
"max_length": 300,
"max_length": 150,
"min_length": 1,
"typical_p": 0.9,
"repetition_penalty": 2,

View File

@ -1,15 +1,15 @@
{
"order": [8, 0, 5, 3, 2, 4],
"temperature": 1.5,
"max_length": 300,
"max_length": 150,
"min_length": 1,
"top_a": 0.02,
"top_p": 0.95,
"typical_p": 0.95,
"tail_free_sampling": 0.95,
"mirostat_lr": 0.2,
"mirostat_lr": 0.25,
"mirostat_tau": 5,
"repetition_penalty": 1.6,
"repetition_penalty": 1.625,
"repetition_penalty_range": 2016,
"repetition_penalty_frequency": 0,
"repetition_penalty_presence": 0,

View File

@ -1,5 +0,0 @@
{
"name": "Classic",
"storyString": "{{instructSystemPrompt}}\n{{wiBeforeCharacter}}\n{{description}}\n{{char}}'s personality: {{personality}}\nCircumstances and context of the dialogue: {{scenario}}\n{{wiAfterCharacter}}\nThis is how {{char}} should talk\n{{mesExamples}}\nThen the roleplay chat between {{user}} and {{char}} begins\n",
"injections": []
}

View File

@ -0,0 +1,6 @@
{
"name": "Default",
"story_string": "{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}{{/if}}",
"chat_start": "***",
"example_separator": "***"
}

View File

@ -1,5 +1,6 @@
{
"name": "Pygmalion",
"storyString": "{{instructSystemPrompt}}\n{{wiBeforeCharacter}}\n{{char}}'s Persona: {{description}}\nPersonality: {{personality}}\nScenario: {{scenario}}\n{{wiAfterCharacter}}\n<START>\n{{mesExamples}}\n<START>\n",
"injections": []
"story_string": "{{#if description}}{{{char}}}'s Persona: {{description}}{{/if}}\n{{#if personality}}Personality: {{personality}}{{/if}}\n{{#if scenario}}Scenario: {{scenario}}{{/if}}",
"chat_start": "<START>",
"example_separator": "<START>"
}

View File

@ -0,0 +1,6 @@
{
"name": "Roleplay",
"story_string": "### Input:\n{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}{{/if}}",
"chat_start": "### New Roleplay:",
"example_separator": "### New Roleplay:"
}

View File

@ -0,0 +1,6 @@
{
"name": "simple-proxy-for-tavern",
"story_string": "### Input:\n{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}### Response:\n(OOC) Understood. I will take this info into account for the roleplay. (end OOC)",
"chat_start": "### New Roleplay:",
"example_separator": "### New Roleplay:"
}

14
public/img/openrouter.svg Normal file
View File

@ -0,0 +1,14 @@
<svg width="100%" height="100%" viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg" class="Navbar_logo__INhgK" aria-label="Logo">
<g clip-path="url(#clip0_205_3)">
<path d="M3 248.945C18 248.945 76 236 106 219C136 202 136 202 198 158C276.497 102.293 332 120.945 423 120.945" stroke-width="90"></path>
<path d="M511 121.5L357.25 210.268L357.25 32.7324L511 121.5Z"></path>
<path d="M0 249C15 249 73 261.945 103 278.945C133 295.945 133 295.945 195 339.945C273.497 395.652 329 377 420 377" stroke-width="90"></path>
<path d="M508 376.445L354.25 287.678L354.25 465.213L508 376.445Z"></path>
</g>
<defs>
<clipPath id="clip0_205_3">
<rect width="512" height="512"></rect>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 777 B

View File

@ -41,6 +41,8 @@
<script src="scripts/seedrandom.min.js"></script>
<script src="scripts/droll.js"></script>
<script src="scripts/localforage.min.js"></script>
<script src="scripts/handlebars.js"></script>
<script src="scripts/pagination.js"></script>
<script type="module" src="scripts/eventemitter.js"></script>
<script type="module" src="scripts/power-user.js"></script>
<script type="module" src="scripts/swiped-events.js"></script>
@ -49,7 +51,8 @@
<link rel="icon" type="image/x-icon" href="favicon.ico">
<script>
function applyLocale() {
var language = navigator.language || navigator.userLanguage;
const overrideLanguage = localStorage.getItem("language");
var language = overrideLanguage || navigator.language || navigator.userLanguage;
language = language.toLowerCase();
console.log(language)
//load the appropriate language file
@ -93,10 +96,10 @@
<script type="module" src="scripts/slash-commands.js"></script>
<script type="module" src="scripts/tags.js"></script>
<script type="module" src="scripts/secrets.js"></script>
<script type="module" src="scripts/context-template.js"></script>
<script type="module" src="scripts/extensions.js"></script>
<script type="module" src="scripts/authors-note.js"></script>
<script type="module" src="scripts/preset-manager.js"></script>
<script type="module" src="scripts/filters.js"></script>
<script type="text/javascript" src="scripts/toolcool-color-picker.js"></script>
<title>SillyTavern</title>
@ -159,9 +162,16 @@
<span class="note-link-span">?</span>
</a>
</h3>
<select id="settings_perset_novel" data-preset-manager-for="novel">
<option value="gui" data-i18n="default">Default</option>
</select>
<div class="preset_buttons">
<select id="settings_perset_novel" data-preset-manager-for="novel">
<option value="gui" data-i18n="default">Default</option>
</select>
<i data-preset-manager-update="novel" class="menu_button fa-solid fa-save" title="Update current preset" data-i18n="[title]Update current preset"></i>
<i data-preset-manager-new="novel" class="menu_button fa-solid fa-plus" title="Create new preset" data-i18n="[title]Create new preset"></i>
<i data-preset-manager-import="novel" class="menu_button fa-solid fa-upload" title="Import preset" data-i18n="[title]Import preset"></i>
<i data-preset-manager-export="novel" class="menu_button fa-solid fa-download" title="Export preset" data-i18n="[title]Export preset"></i>
<i data-preset-manager-delete="novel" class="menu_button fa-solid fa-trash-can" title="Delete the preset" data-i18n="[title]Delete the preset"></i>
</div>
</div>
<div id="openai_api-presets">
<div>
@ -200,8 +210,7 @@
<div class="range-block-title justifyLeft">
AI Module
</div>
<div class="toggle-description justifyLeft"
data-i18n="Changes the style of the generated text.">
<div class="toggle-description justifyLeft" data-i18n="Changes the style of the generated text.">
Changes the style of the generated text.
</div>
<select id="nai_prefix">
@ -831,7 +840,7 @@
Samplers will be applied in a top-down order.
Use with caution.
</div>
<div id="kobold_order">
<div id="kobold_order" class="prompt_order">
<div data-id="0">
<span data-i18n="Top K">Top K</span>
<small>0</small>
@ -849,7 +858,7 @@
<small>3</small>
</div>
<div data-id="4">
<span data-i18n="Typical Sampling">Typical Sampling</span>
<span data-i18n="Typical Sampling">Typical P Sampling</span>
<small>4</small>
</div>
<div data-id="5">
@ -876,7 +885,7 @@
</div>
</div>
<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
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" placeholder=""></textarea>
@ -893,6 +902,21 @@
<textarea id="nai_banned_tokens" class="text_pole textarea_compact" name="nai_banned_tokens" rows="2" placeholder=""></textarea>
</div>
</div>
<div class="range-block">
<div class="range-block-title title_restorable">
<span data-i18n="Logit Bias">Logit Bias</span>
<div id="novelai_logit_bias_new_entry" class="menu_button menu_button_icon">
<i class="fa-xs fa-solid fa-plus"></i>
<small data-i18n="Add">Add</small>
</div>
</div>
<div class="toggle-description justifyLeft" data-i18n="Helps to ban or reenforce the usage of certain words">
Helps to ban or reinforce the usage of certain tokens.
</div>
<div class="flex-container flexFlowColumn wide100p">
<div class="novelai_logit_bias_list"></div>
</div>
</div>
<hr>
<div class="range-block">
<div class="range-block-title justifyLeft" data-i18n="CFG Scale">
@ -914,7 +938,7 @@
<span data-i18n="Negative Prompt">Negative Prompt</span>
</div>
<div class="wide100p">
<textarea id="nai_cfg_uc" class="text_pole textarea_compact" name="cfg_uc" rows="2" data-i18n="[placeholder]Add text here that would make the AI generate things you don't want in your outputs." placeholder="Add text here that would make the AI generate things you don't want in your outputs."></textarea>
<textarea id="nai_cfg_uc" class="text_pole textarea_compact" name="cfg_uc" rows="2" data-i18n="[placeholder]Add text here that would make the AI generate things you don't want in your outputs." placeholder="Add text here that would make the AI generate things you don't want in your outputs."></textarea>
</div>
</div>
<small class="margin-bot-10px" data-i18n="Used if CFG Scale is unset globally, per chat or character">
@ -966,21 +990,6 @@
</div>
</div>
</div>
<div class="range-block">
<div class="range-block-title" data-i18n="Top G">
Top G
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input type="range" id="top_g_novel" name="volume" min="0" max="20" step="1">
</div>
<div class="range-block-counter">
<div contenteditable="true" data-for="top_g_novel" id="top_g_counter_novel">
select
</div>
</div>
</div>
</div>
<div class="range-block">
<div class="range-block-title" data-i18n="Mirostat Tau">
Mirostat Tau
@ -1041,6 +1050,57 @@
</div>
</div>
</div>
<div class="range-block flexFlowColumn">
<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="novel_order" class="prompt_order">
<div data-id="0">
<span data-i18n="Temperature">Temperature</span>
<small>0</small>
<div class="toggle_button right_menu_button"></div>
</div>
<div data-id="1">
<span data-i18n="Top K Sampling">Top K Sampling</span>
<small>1</small>
<div class="toggle_button right_menu_button"></div>
</div>
<div data-id="2">
<span data-i18n="Nucleus Sampling">Nucleus Sampling</span>
<small>2</small>
<div class="toggle_button right_menu_button"></div>
</div>
<div data-id="3">
<span data-i18n="Tail Free Sampling">Tail Free Sampling</span>
<small>3</small>
<div class="toggle_button right_menu_button"></div>
</div>
<div data-id="4">
<span data-i18n="Top A Sampling">Top A Sampling</span>
<small>4</small>
<div class="toggle_button right_menu_button"></div>
</div>
<div data-id="5">
<span data-i18n="Typical Sampling">Typical Sampling</span>
<small>5</small>
<div class="toggle_button right_menu_button"></div>
</div>
<div data-id="6">
<span data-i18n="CFG">CFG</span>
<small>6</small>
<div class="toggle_button right_menu_button"></div>
</div>
<!-- Yes, there is no 7. It was a removed Top G sampling. RIP. -->
<div data-id="8">
<span data-i18n="Mirostat">Mirostat</span>
<small>8</small>
<div class="toggle_button right_menu_button"></div>
</div>
</div>
</div>
</div>
<div id="textgenerationwebui_api-settings">
<div class="range-block">
@ -1207,7 +1267,7 @@
<span data-i18n="Negative Prompt">Negative Prompt</span>
</div>
<div class="wide100p">
<textarea id="negative_prompt_textgenerationwebui" class="text_pole textarea_compact" name="negative_prompt" rows="2" data-i18n="[placeholder]Add text here that would make the AI generate things you don't want in your outputs." placeholder="Add text here that would make the AI generate things you don't want in your outputs."></textarea>
<textarea id="negative_prompt_textgenerationwebui" class="text_pole textarea_compact" name="negative_prompt" rows="2" data-i18n="[placeholder]Add text here that would make the AI generate things you don't want in your outputs." placeholder="Add text here that would make the AI generate things you don't want in your outputs."></textarea>
</div>
</div>
<small class="margin-bot-10px" data-i18n="Used if CFG Scale is unset globally, per chat or character">
@ -1418,8 +1478,7 @@
<div class="fa-solid fa-clock-rotate-left"></div>
</div>
</div>
<div class="toggle-description justifyLeft"
data-i18n="Prompt that is used when the NSFW toggle is O">
<div class="toggle-description justifyLeft" data-i18n="Prompt that is used when the NSFW toggle is O">
Prompt that is used when the NSFW toggle is OFF
</div>
<div class="wide100p">
@ -1435,9 +1494,9 @@
</div>
</div>
<div class="toggle-description justifyLeft">
<span data-i18n="Set at the beginning of the chat history to indicate that a new chat is about to start.">
Set at the beginning of the chat history to indicate that a new chat is about to start.
</span>
<span data-i18n="Set at the beginning of the chat history to indicate that a new chat is about to start.">
Set at the beginning of the chat history to indicate that a new chat is about to start.
</span>
</div>
<div class="wide100p">
<textarea id="newchat_prompt_textarea" class="text_pole textarea_compact" name="new_chat" rows="3" placeholder=""></textarea>
@ -1451,9 +1510,9 @@
</div>
</div>
<div class="toggle-description justifyLeft">
<span data-i18n="Set at the beginning of the chat history to indicate that a new group chat is about to start.">
Set at the beginning of the chat history to indicate that a new group chat is about to start.
</span>
<span data-i18n="Set at the beginning of the chat history to indicate that a new group chat is about to start.">
Set at the beginning of the chat history to indicate that a new group chat is about to start.
</span>
</div>
<div class="wide100p">
<textarea id="newgroupchat_prompt_textarea" class="text_pole textarea_compact" name="new_group_chat" rows="3" placeholder=""></textarea>
@ -1467,9 +1526,9 @@
</div>
</div>
<div class="toggle-description justifyLeft">
<span data-i18n="Set at the beginning of Dialogue examples to indicate that a new example chat is about to start.">
Set at the beginning of Dialogue examples to indicate that a new example chat is about to start.
</span>
<span data-i18n="Set at the beginning of Dialogue examples to indicate that a new example chat is about to start.">
Set at the beginning of Dialogue examples to indicate that a new example chat is about to start.
</span>
</div>
<div class="wide100p">
<textarea id="newexamplechat_prompt_textarea" class="text_pole textarea_compact" name="new_example_chat" rows="3" placeholder=""></textarea>
@ -1483,9 +1542,9 @@
</div>
</div>
<div class="toggle-description justifyLeft">
<span data-i18n="Set at the beginning of the chat history to indicate that a new chat is about to start.">
Set at the end of the chat history when the continue button is pressed.
</span>
<span data-i18n="Set at the beginning of the chat history to indicate that a new chat is about to start.">
Set at the end of the chat history when the continue button is pressed.
</span>
</div>
<div class="wide100p">
<textarea id="continue_nudge_prompt_textarea" class="text_pole textarea_compact" name="continue_nudge" rows="3" placeholder=""></textarea>
@ -1496,9 +1555,9 @@
Replace empty message
</div>
<div class="toggle-description justifyLeft">
<span data-i18n="Send this text instead of nothing when the text box is empty.">
Send this text instead of nothing when the text box is empty.
</span>
<span data-i18n="Send this text instead of nothing when the text box is empty.">
Send this text instead of nothing when the text box is empty.
</span>
</div>
<div class="wide100p">
<textarea id="send_if_empty_textarea" class="text_pole textarea_compact" name="send_if_empty" rows="3" placeholder=""></textarea>
@ -1947,58 +2006,40 @@
</h3>
<div class="flex-container">
<div name="PygOverrides" class="flex1">
<h4><span data-i18n="AutoFormat Overrides">AutoFormat Overrides</span></h4>
<label class="checkbox_label" for="disable-description-formatting-checkbox">
<input id="disable-description-formatting-checkbox" type="checkbox" />
<span data-i18n="Disable description formatting">Disable description formatting</span>
</label>
<label class="checkbox_label" for="disable-scenario-formatting-checkbox">
<input id="disable-scenario-formatting-checkbox" type="checkbox" />
<span data-i18n="Disable scenario formatting"> Disable scenario formatting</span>
</label>
<label class="checkbox_label" for="disable-personality-formatting-checkbox">
<input id="disable-personality-formatting-checkbox" type="checkbox" />
<span data-i18n="Disable personality formatting"> Disable personality formatting</span>
</label>
<label class="checkbox_label" for="disable-examples-formatting-checkbox">
<input id="disable-examples-formatting-checkbox" type="checkbox" />
<span data-i18n="Disable example chats formatting">Disable example chats formatting</span>
</label>
<label class="checkbox_label" for="disable-start-formatting-checkbox">
<input id="disable-start-formatting-checkbox" type="checkbox" />
<span data-i18n="Disable chat start formatting">Disable chat start formatting</span>
</label>
<label class="checkbox_label" for="trim_spaces">
<input id="trim_spaces" type="checkbox" />
<span data-i18n="Trim spaces">Trim spaces</span>
</label>
<label class="checkbox_label" for="trim_sentences_checkbox">
<input id="trim_sentences_checkbox" type="checkbox" />
<span data-i18n="Trim Incomplete Sentences">Trim Incomplete Sentences</span>
</label>
<!-- Add margin since this is a child of above -->
<label class="checkbox_label indent20p" for="include_newline_checkbox">
<input id="include_newline_checkbox" type="checkbox" />
<span data-i18n="Include Newline">Include Newline</span>
</label>
<div>
<h4 data-i18n="Custom Chat Separator">
Custom Chat Separator
<h4 data-i18n="Context Template">
Context Template
</h4>
<div class="flex-container flexGap5">
<select id="context_presets" class="flex1 margin0">
</select>
</div>
<div>
<textarea id="custom_chat_separator" class="text_pole textarea_compact" type="text" placeholder="&lt;START&gt;" maxlength="500" rows="1"></textarea>
<label for="context_story_string" data-i18n="Story String">
Story String
</label>
<textarea id="context_story_string" class="text_pole textarea_compact" rows="3"></textarea>
<div class="flex-container">
<div class="flex1">
<label for="context_example_separator">
<span data-i18n="Example Separator">Example Separator</span>
</label>
<div>
<textarea id="context_example_separator" class="text_pole textarea_compact" maxlength="500" rows="1"></textarea>
</div>
</div>
<div class="flex1">
<label for="context_chat_start">
<span data-i18n="Chat Start">Chat Start</span>
</label>
<div>
<textarea id="context_chat_start" class="text_pole textarea_compact" maxlength="500" rows="1"></textarea>
</div>
</div>
</div>
</div>
</div>
<div>
<h4 data-i18n="Non-markdown strings">
Non-markdown strings
</h4>
<div>
<input id="markdown_escape_strings" class="text_pole textarea_compact" type="text" data-i18n="[placeholder]separate with commas w/o space between" placeholder="separate with commas w/o space between" maxlength="100" />
</div>
</div>
<div>
<h4 data-i18n="Instruct mode">Instruct mode
<a href="https://docs.sillytavern.app/usage/core-concepts/instructmode/" class="notes-link" target="_blank">
<span class="note-link-span">?</span>
@ -2040,18 +2081,26 @@
<div class="flex-container">
<div class="flex1">
<label for="instruct_input_sequence">
<span data-i18n="Input Sequence">Input Sequence</span>
<small data-i18n="Input Sequence">Input Sequence</small>
</label>
<div>
<textarea id="instruct_input_sequence" class="text_pole textarea_compact" type="text" maxlength="500" rows="1"></textarea>
<textarea id="instruct_input_sequence" class="text_pole textarea_compact" maxlength="500" rows="1"></textarea>
</div>
</div>
<div class="flex1">
<label for="instruct_output_sequence">
<span data-i18n="Output Sequence">Output Sequence</span>
<small data-i18n="Output Sequence">Output Sequence</small>
</label>
<div>
<textarea id="instruct_output_sequence" class="text_pole wide100p textarea_compact" type="text" maxlength="500" rows="1"></textarea>
<textarea id="instruct_output_sequence" class="text_pole wide100p textarea_compact" maxlength="500" rows="1"></textarea>
</div>
</div>
<div class="flex1">
<label for="instruct_last_output_sequence">
<small data-i18n="Last Sequence">Last Sequence</small>
</label>
<div>
<textarea id="instruct_last_output_sequence" class="text_pole wide100p textarea_compact" maxlength="500" rows="1"></textarea>
</div>
</div>
</div>
@ -2061,7 +2110,7 @@
<small data-i18n="System Sequence">System Sequence</small>
</label>
<div>
<textarea id="instruct_system_sequence" class="text_pole textarea_compact" type="text" maxlength="500" rows="1"></textarea>
<textarea id="instruct_system_sequence" class="text_pole textarea_compact" maxlength="500" rows="1"></textarea>
</div>
</div>
<div class="flex1">
@ -2069,7 +2118,7 @@
<small data-i18n="Stop Sequence">Stop Sequence</small>
</label>
<div>
<textarea id="instruct_stop_sequence" class="text_pole wide100p textarea_compact" type="text" maxlength="500" rows="1"></textarea>
<textarea id="instruct_stop_sequence" class="text_pole wide100p textarea_compact" maxlength="500" rows="1"></textarea>
</div>
</div>
<div class="flex1">
@ -2077,14 +2126,22 @@
<small data-i18n="Separator">Separator</small>
</label>
<div>
<textarea id="instruct_separator_sequence" class="text_pole wide100p textarea_compact" type="text" maxlength="500" rows="1"></textarea>
<textarea id="instruct_separator_sequence" class="text_pole wide100p textarea_compact" maxlength="500" rows="1"></textarea>
</div>
</div>
</div>
</div>
<div>
<h4 data-i18n="Non-markdown strings">
Non-markdown strings
</h4>
<div>
<input id="markdown_escape_strings" class="text_pole textarea_compact" type="text" data-i18n="[placeholder]separate with commas w/o space between" placeholder="separate with commas w/o space between" maxlength="100" />
</div>
</div>
</div>
<div name="ContextFormatting" class="flex1">
<h4><span data-i18n="Context Formatting">Context Formatting</span></h4>
<div>
<h4><span data-i18n="Tokenizer">Tokenizer</span>
<a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#tokenizer" class="notes-link" target="_blank">
@ -2128,19 +2185,19 @@
Remove Empty New Lines from Output
</span>
</label>
<div id="context-templates-block" class="template_element">
<h4>
Context Templates
</h4>
<div class="flex-container flexGap5">
<select id="context_template" class="flex1 margin0">
</select>
<div id="context_template_new" class="menu_button fa-solid fa-plus margin0" title="Create new" data-i18n="[title]Create New"></div>
<div id="context_template_edit" class="menu_button fa-solid fa-file-pen margin0" title="Edit" data-i18n="[title]Edit"></div>
<div id="context_template_rename" class="menu_button fa-solid fa-i-cursor margin0" title="Rename" data-i18n="[title]Rename"></div>
<div id="context_template_delete" class="menu_button fa-solid fa-trash-can margin0" title="Delete" data-i18n="[title]Delete"></div>
</div>
</div>
<label class="checkbox_label" for="trim_spaces">
<input id="trim_spaces" type="checkbox" />
<span data-i18n="Trim spaces">Trim spaces</span>
</label>
<label class="checkbox_label" for="trim_sentences_checkbox">
<input id="trim_sentences_checkbox" type="checkbox" />
<span data-i18n="Trim Incomplete Sentences">Trim Incomplete Sentences</span>
</label>
<!-- Add margin since this is a child of above -->
<label class="checkbox_label indent20p" for="include_newline_checkbox">
<input id="include_newline_checkbox" type="checkbox" />
<span data-i18n="Include Newline">Include Newline</span>
</label>
<div>
<h4>
<span data-i18n="Start Reply With">
@ -2484,6 +2541,19 @@
</div>
</div>
</div>
<div id="UI-language-block" class="flex-container flexFlowColumn">
<h4>
<span data-i18n="UI Language">UI Language</span>
</h4>
<div class="flex-container flexnowrap alignitemscenter">
<select id="ui_language_select" class="margin0 margin-r5">
<option value="" data-i18n="Browser default">
Browser default
</option>
<option value="en">en</option>
</select>
</div>
</div>
<div id="UI-presets-block" class="flex-container flexFlowColumn">
<h4>
<span data-i18n="UI Theme Preset">UI Theme Preset</span>
@ -2920,6 +2990,7 @@
<div id="rm_button_selected_ch">
<h2></h2>
</div>
<i id="hideCharPanelAvatarButton" class="fa-solid fa-eye right_menu_button"></i>
</div>
</div>
<!-- end group peeking cope structure-->
@ -2934,7 +3005,7 @@
<input id="character_name_pole" name="ch_name" class="text_pole" data-i18n="[placeholder]Name this character" placeholder="Name this character" maxlength="50" value="" autocomplete="off">
</div>
<div id="result_info" class="flex-container" title="Token counts may be inaccurate and provided just for reference." data-i18n="[title]Token counts may be inaccurate and provided just for reference.">&nbsp;</div>
<div id="avatar_div" class="avatar_div alignitemsflexstart justifySpaceBetween flexnowrap flexGap5">
<label id="avatar_div_div" class="add_avatar avatar" for="add_avatar_button" title="Click to select a new avatar for this character" data-i18n="[title]Click to select a new avatar for this character">
@ -3007,6 +3078,7 @@
<div id="description_div" class="marginBot5">
<span data-i18n="Character Description">Description</span>
<div id="result_info" class="flex-container" title="Token counts may be inaccurate and provided just for reference." data-i18n="[title]Token counts may be inaccurate and provided just for reference.">&nbsp;</div>
<a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#character-description" class="notes-link" target="_blank">
<span class="note-link-span">?</span>
</a>
@ -3129,6 +3201,7 @@
</div>
<div class="inline-drawer-content">
<div name="Current Group Members" class="flex-container flexFlowColumn overflowYAuto flex1">
<div id="rm_group_members_pagination" class="group_pagination"></div>
<div id="rm_group_members" class="overflowYAuto flex-container"></div>
</div>
</div>
@ -3146,6 +3219,7 @@
<div class="rm_tag_controls">
<div class="tags rm_tag_filter"></div>
</div>
<div id="rm_group_add_members_pagination" class="group_pagination"></div>
<div id="rm_group_add_members" class="overflowYAuto flex-container"></div>
</div>
</div>
@ -3185,14 +3259,12 @@
<div class="rm_tag_controls">
<div class="tags rm_tag_filter"></div>
</div>
<!-- a div containing a dynamically updated count of characters currently displayed -->
<div class="flex-container alignitemscenter">
<div id="rm_character_count"></div>
<i id="charListGridToggle" class="fa-solid fa-table-cells-large menu_button" title="Toggle character grid view"></i>
</div>
<hr>
</div>
<div id="rm_print_characters_pagination">
<i id="charListGridToggle" class="fa-solid fa-table-cells-large menu_button" title="Toggle character grid view"></i>
</div>
<div id="rm_print_characters_block" class="flexFlowColumn"></div>
</div>
@ -3331,6 +3403,7 @@
<div id="shadow_select_chat_popup">
<div id="select_chat_popup">
<input type="text" id="select_chat_search" placeholder="Search..." autocomplete="off">
<div id="select_chat_import"> <!-- import chat popup header -->
<div id="chat_import_button" class="fa-solid fa-file-arrow-up menu_button"></div>
<div id="selectChatPopupHeaderText" class="TxtLrgBoldCenter">
@ -3722,10 +3795,19 @@
<input class="openai_logit_bias_text text_pole" data-i18n="[placeholder]Type here..." placeholder="type here..." />
<input class="openai_logit_bias_value text_pole" type="number" min="-100" value="0" max="100" />
<i class="menu_button fa-solid fa-xmark openai_logit_bias_remove"></i>
</form>
</div>
</form>
</div>
</div>
</div>
<div id="novelai_logit_bias_template" class="template_element">
<div class="novelai_logit_bias_form">
<input class="novelai_logit_bias_text text_pole" data-i18n="[placeholder]Type here..." placeholder="type here..." />
<input class="novelai_logit_bias_value text_pole" type="number" min="-2" value="0" max="2" step="0.01" />
<i class="menu_button fa-solid fa-xmark novelai_logit_bias_remove"></i>
</div>
</div>
<div id="completion_prompt_manager_popup" class="drawer-content" style="display:none;">
<div id="completion_prompt_manager_popup_inspect">
<h3>Inspect</h3>
@ -3774,7 +3856,7 @@
<textarea id="completion_prompt_manager_popup_entry_form_prompt" class="text_pole" name="prompt">
</textarea>
</div>
<div class="completion_prompt_manager_popup_entry_form_footer" >
<div class="completion_prompt_manager_popup_entry_form_footer">
<a id="completion_prompt_manager_popup_entry_form_close" title="close" class="fa-solid fa-close menu_button"></a>
<a id="completion_prompt_manager_popup_entry_form_reset" title="reset" class="fa-solid fa-undo menu_button"></a>
<a id="completion_prompt_manager_popup_entry_form_save" title="save" class="fa-solid fa-save menu_button" data-pm-prompt=""></a>

View File

@ -5,6 +5,7 @@
"stop_sequence": "",
"input_sequence": "### Instruction:",
"output_sequence": "### Response:",
"last_output_sequence": "",
"separator_sequence": "",
"wrap": true
}
}

View File

@ -5,6 +5,7 @@
"stop_sequence": "",
"input_sequence": "USER: ",
"output_sequence": "GPT: ",
"last_output_sequence": "",
"separator_sequence": "</s>",
"wrap": false
}
}

View File

@ -5,6 +5,7 @@
"stop_sequence": "",
"input_sequence": "[INST]",
"output_sequence": "[/INST]",
"last_output_sequence": "",
"separator_sequence": "\n",
"wrap": false
}

View File

@ -5,6 +5,7 @@
"stop_sequence": "</s>",
"input_sequence": "<|user|>",
"output_sequence": "<|model|>",
"last_output_sequence": "",
"separator_sequence": "",
"wrap": false
}

View File

@ -4,6 +4,7 @@
"name": "OpenOrca/OpenChat",
"names": true,
"output_sequence": "<|end_of_turn|>\nAssistant: ",
"last_output_sequence": "",
"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",

View File

@ -1,12 +1,13 @@
{
"input_sequence": "### Instruction:",
"last_output_sequence": "### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):",
"macro": true,
"name": "Roleplay",
"names": true,
"output_sequence": "### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):",
"output_sequence": "### Response:",
"separator_sequence": "",
"stop_sequence": "",
"system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.\n\nAvoid repetition, don't loop. Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions.\n\n### Input:",
"system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.\n\nAvoid repetition, don't loop. Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions.",
"system_sequence": "",
"wrap": true
}

View File

@ -5,6 +5,7 @@
"stop_sequence": "",
"input_sequence": "### Human:",
"output_sequence": "### Assistant:",
"last_output_sequence": "",
"separator_sequence": "",
"wrap": true
}
}

View File

@ -5,6 +5,7 @@
"stop_sequence": "",
"input_sequence": "USER: ",
"output_sequence": "ASSISTANT: ",
"last_output_sequence": "",
"separator_sequence": "</s>",
"wrap": false
}
}

View File

@ -5,6 +5,7 @@
"stop_sequence": "",
"input_sequence": "USER: ",
"output_sequence": "ASSISTANT: ",
"last_output_sequence": "",
"separator_sequence": "",
"wrap": true
}

View File

@ -5,6 +5,7 @@
"stop_sequence": "",
"input_sequence": "",
"output_sequence": "### Response:",
"last_output_sequence": "",
"separator_sequence": "</s>",
"wrap": true
}
}

View File

@ -0,0 +1,13 @@
{
"input_sequence": "### Instruction:\n#### {{user}}:",
"last_output_sequence": "### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):\n#### {{char}}:",
"macro": true,
"name": "simple-proxy-for-tavern",
"names": false,
"output_sequence": "### Response:\n#### {{char}}:",
"separator_sequence": "",
"stop_sequence": "",
"system_prompt": "## {{char}}\n- You're \"{{char}}\" in this never-ending roleplay with \"{{user}}\".",
"system_sequence": "",
"wrap": true
}

File diff suppressed because it is too large Load Diff

View File

@ -172,7 +172,7 @@ function PromptManagerModule() {
strategy: 'global',
dummyId: 100000
},
draggable: true,
sortableDelay: 30,
warningTokenThreshold: 1500,
dangerTokenThreshold: 500,
defaultPrompts: {
@ -181,10 +181,7 @@ function PromptManagerModule() {
jailbreak: '',
enhanceDefinitions: ''
}
}
// Either 0 for done or 1 for rendering
this.renderState = 0;
};
// Chatcompletion configuration object
this.serviceSettings = null;
@ -591,22 +588,12 @@ PromptManagerModule.prototype.init = function (moduleConfiguration, serviceSetti
* @param afterTryGenerate - Whether a dry run should be attempted before rendering
*/
PromptManagerModule.prototype.render = function (afterTryGenerate = true) {
if (main_api !== 'openai' ||
null === this.activeCharacter ||
1 === this.renderState) return;
if (main_api !== 'openai') return;
this.renderState = 1;
if (null === this.activeCharacter) return;
this.error = null;
const stopPropagation = (event) => {
event.stopPropagation();
}
const configurationContainer = document.getElementById('ai_response_configuration');
try {
// Lock configuration during render
configurationContainer.addEventListener('click', stopPropagation, true);
waitUntilCondition(() => !is_send_press && !is_group_generating, 1024 * 1024, 100).then(() => {
if (true === afterTryGenerate) {
// Executed during dry-run for determining context composition
this.profileStart('filling context');
@ -626,15 +613,9 @@ PromptManagerModule.prototype.render = function (afterTryGenerate = true) {
this.makeDraggable();
this.profileEnd('render');
}
} catch (error) {
this.log('----- Unexpected error while rendering prompt manager -----');
this.log(error);
this.log(error.stack);
this.log('-----------------------------------------------------------');
} finally {
this.renderState = 0;
configurationContainer.removeEventListener('click', stopPropagation, true);
}
}).catch(() => {
console.log('Timeout while waiting for send press to be false');
});
}
/**
@ -889,7 +870,7 @@ PromptManagerModule.prototype.handleGroupSelected = function (event) {
*/
PromptManagerModule.prototype.getActiveGroupCharacters = function() {
// ToDo: Ideally, this should return the actual characters.
return (this.activeCharacter?.group?.members || []).map(member => member.substring(0, member.lastIndexOf('.')));
return (this.activeCharacter?.group?.members || []).map(member => member && member.substring(0, member.lastIndexOf('.')));
}
/**
@ -1588,6 +1569,7 @@ PromptManagerModule.prototype.getFormattedDate = function() {
*/
PromptManagerModule.prototype.makeDraggable = function () {
$(`#${this.configuration.prefix}prompt_manager_list`).sortable({
delay: this.configuration.sortableDelay,
items: `.${this.configuration.prefix}prompt_manager_prompt_draggable`,
update: ( event, ui ) => {
const promptOrder = this.getPromptOrderForCharacter(this.activeCharacter);

View File

@ -13,11 +13,13 @@ import {
menu_type,
max_context,
saveSettingsDebounced,
eventSource,
active_group,
active_character,
setActiveGroup,
setActiveCharacter,
getEntitiesList,
getThumbnailUrl,
selectCharacterById,
} from "../script.js";
import {
@ -348,12 +350,13 @@ async function RA_autoloadchat() {
// active character is the name, we should look it up in the character list and get the id
let active_character_id = Object.keys(characters).find(key => characters[key].avatar === active_character);
var charToAutoLoad = document.getElementById('CharID' + active_character_id);
let groupToAutoLoad = document.querySelector(`.group_select[grid="${active_group}"]`);
if (charToAutoLoad != null) {
$(charToAutoLoad).click();
if (active_character_id !== null) {
selectCharacterById(String(active_character_id));
}
else if (groupToAutoLoad != null) {
let groupToAutoLoad = document.querySelector(`.group_select[grid="${active_group}"]`);
if (groupToAutoLoad != null) {
$(groupToAutoLoad).click();
}
@ -362,53 +365,60 @@ async function RA_autoloadchat() {
}
export async function favsToHotswap() {
const selector = ['#rm_print_characters_block .character_select', '#rm_print_characters_block .group_select'].join(',');
const entities = getEntitiesList({ doFilter: false });
const container = $('#right-nav-panel .hotswap');
const template = $('#hotswap_template .hotswapAvatar');
container.empty();
const maxCount = 6;
let count = 0;
$(selector).sort(sortByCssOrder).each(function () {
if ($(this).hasClass('is_fav') && count < maxCount) {
const isCharacter = $(this).hasClass('character_select');
const isGroup = $(this).hasClass('group_select');
const grid = Number($(this).attr('grid'));
const chid = Number($(this).attr('chid'));
let thisHotSwapSlot = template.clone();
thisHotSwapSlot.toggleClass('character_select', isCharacter);
thisHotSwapSlot.toggleClass('group_select', isGroup);
thisHotSwapSlot.attr('grid', isGroup ? grid : '');
thisHotSwapSlot.attr('chid', isCharacter ? chid : '');
thisHotSwapSlot.data('id', isGroup ? grid : chid);
thisHotSwapSlot.attr('title', '');
if (isGroup) {
const group = groups.find(x => x.id === grid);
const avatar = getGroupAvatar(group);
$(thisHotSwapSlot).find('img').replaceWith(avatar);
}
if (isCharacter) {
const avatarUrl = $(this).find('img').attr('src');
$(thisHotSwapSlot).find('img').attr('src', avatarUrl);
}
$(thisHotSwapSlot).css('cursor', 'pointer');
container.append(thisHotSwapSlot);
count++;
for (const entity of entities) {
if (count >= maxCount) {
break;
}
});
//console.log('about to check for leftover selectors...')
const isFavorite = entity.item.fav || entity.item.fav == 'true';
if (!isFavorite) {
continue;
}
const isCharacter = entity.type === 'character';
const isGroup = entity.type === 'group';
const grid = isGroup ? entity.id : '';
const chid = isCharacter ? entity.id : '';
let slot = template.clone();
slot.toggleClass('character_select', isCharacter);
slot.toggleClass('group_select', isGroup);
slot.attr('grid', isGroup ? grid : '');
slot.attr('chid', isCharacter ? chid : '');
slot.data('id', isGroup ? grid : chid);
slot.attr('title', '');
if (isGroup) {
const group = groups.find(x => x.id === grid);
const avatar = getGroupAvatar(group);
$(slot).find('img').replaceWith(avatar);
}
if (isCharacter) {
const avatarUrl = getThumbnailUrl('avatar', entity.item.avatar);
$(slot).find('img').attr('src', avatarUrl);
}
$(slot).css('cursor', 'pointer');
container.append(slot);
count++;
}
// there are 6 slots in total,
if (count < maxCount) { //if any are left over
let leftOverSlots = maxCount - count;
for (let i = 1; i <= leftOverSlots; i++) {
container.append(template.clone());
}
} else {
//console.log(`count was ${count} so no need to knock off any selectors!`);
}
}

View File

@ -1,214 +0,0 @@
import {
callPopup,
getRequestHeaders,
saveSettingsDebounced,
} from '../script.js';
import { debounce } from './utils.js';
export let context_templates = [];
export let context_settings = {
selected_template: '',
};
const saveTemplateDebounced = debounce((name) => alert('implement me', name), 2000);
export function loadContextTemplatesFromSettings(data, settings) {
context_templates = data.context || [];
context_settings = Object.assign(context_settings, (settings.context_settings || {}));
const dropdown = $('#context_template');
dropdown.empty();
dropdown.append('<option value="">-- None --</option>')
for (const template of context_templates) {
const name = template.name;
const option = document.createElement('option');
option.innerText = name;
option.value = name;
option.selected = context_settings.selected_template == name;
dropdown.append(option);
}
}
function onContextTemplateChange() {
const value = $(this).find(':selected').val();
context_settings.selected_template = value;
saveSettingsDebounced();
}
function openContextTemplateEditor() {
const template = context_templates.find(x => x.name == context_settings.selected_template);
if (!template || !context_settings.selected_template) {
toastr.info('No context template selected');
return;
}
const editor = $('#context_editor_template .context_editor').clone();
const injectionsContainer = editor.find('.chat_injections_list');
editor.find('.template_name').text(template.name);
editor.find('.story_string_template').text(template.storyString).on('input', function () {
const value = $(this).val();
template.storyString = value;
saveTemplateDebounced(template.name);
});
editor.find('.chat_injection_add').on('click', function () {
const injection = { id: Date.now(), text: '', depth: 0 };
template.injections.push(injection);
addChatInjection(injectionsContainer, injection, template);
saveTemplateDebounced(template.name);
});
for (const injection of template.injections) {
addChatInjection(injectionsContainer, injection, template);
}
$('#dialogue_popup').addClass('large_dialogue_popup wide_dialogue_popup');
callPopup(editor, 'text');
}
async function onRenameContextTemplateClick() {
const oldName = context_settings.selected_template;
const newName = await inputTemplateName();
const template = context_templates.find(x => x.name === oldName);
if (!template || !newName || oldName === newName) {
return;
}
await saveContextTemplate(newName);
context_settings.selected_template = newName;
saveSettingsDebounced();
await deleteContextTemplate(oldName);
toastr.success('Context template renamed', newName);
}
async function deleteContextTemplate(name) {
const response = await fetch('/delete_context_template', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify({ name }),
});
if (!response.ok) {
throw new Error('Context template not deleted');
}
}
async function saveContextTemplate(name) {
const template = context_templates.find(x => x.name === name);
if (!template) {
throw new Error(`Context template not found: ${name}`);
}
const response = await fetch('/save_context_template', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify({ name, template }),
});
if (!response.ok) {
throw new Error('Context template not saved');
}
}
async function inputTemplateName() {
let name = await callPopup('Enter a template name:', 'input');
if (!name) {
return false;
}
name = DOMPurify.sanitize(name.trim());
if (context_templates.findIndex(x => x.name == name) > -1) {
toastr.warning('Template with that name already exists', 'Pick a unique name');
return false;
}
return name;
}
function addChatInjection(container, model, parent) {
const template = $('#chat_injection_template .chat_injection').clone();
template.attr('id', model.id);
template.find('.chat_injection_text').val(model.text).on('input', function () {
const value = $(this).val();
model.text = value;
saveTemplateDebounced(parent.name);
});
template.find('.chat_injection_depth').val(model.depth).on('input', function () {
const value = Math.abs(Number($(this).val()));
model.depth = value;
saveTemplateDebounced(parent.name);
});
template.find('.chat_injection_remove').on('click', function () {
if (!confirm('Are you sure?')) {
return;
}
const index = parent.injections.findIndex(x => x == model);
if (index === -1) {
console.error('Does not compute, injection index was lost');
return;
}
parent.injections.splice(index, 1);
template.remove();
saveTemplateDebounced(parent.name);
});
container.append(template);
}
function copyTemplateParameter(event) {
const text = $(event.target).text();
navigator.clipboard.writeText(text);
toastr.info('Copied!', '', { timeOut: 2000 });
}
async function onNewContextTemplateClick() {
const name = await inputTemplateName();
if (!name) {
return;
}
const template = { name: name, injections: [], storyString: '' };
context_templates.push(template);
const option = document.createElement('option');
option.innerText = name;
option.value = name;
option.selected = true;
$('#context_template').append(option).val(name).trigger('change');
saveTemplateDebounced(name);
}
async function onDeleteContextTemplateClick() {
const template = context_templates.find(x => x.name == context_settings.selected_template);
if (!template || !context_settings.selected_template) {
toastr.info('No context template selected');
return;
}
const confirm = await callPopup('Are you sure?', 'confirm');
if (!confirm) {
return;
}
await deleteContextTemplate(context_settings.selected_template);
$(`#context_template option[value="${context_settings.selected_template}"]`).remove();
$('#context_template').trigger('change');
}
jQuery(() => {
$('#context_template_edit').on('click', openContextTemplateEditor);
$('#context_template').on('change', onContextTemplateChange);
$('#context_template_new').on('click', onNewContextTemplateClick);
$('#context_template_rename').on('click', onRenameContextTemplateClick);
$('#context_template_delete').on('click', onDeleteContextTemplateClick);
$(document).on('pointerup', '.template_parameters_list code', copyTemplateParameter);
})

View File

@ -8,6 +8,11 @@ const MODULE_NAME = 'backgrounds';
const METADATA_KEY = 'custom_background';
const UPDATE_INTERVAL = 1000;
function forceSetBackground(background) {
saveBackgroundMetadata(background);
setCustomBackground();
}
async function moduleWorker() {
if (hasCustomBackground()) {
$('#unlock_background').show();
@ -167,4 +172,5 @@ $(document).ready(function () {
registerSlashCommand('lockbg', onLockBackgroundClick, ['bglock'], " locks a background for the currently selected chat", true, true);
registerSlashCommand('unlockbg', onUnlockBackgroundClick, ['bgunlock'], ' unlocks a background for the currently selected chat', true, true);
registerSlashCommand('autobg', autoBackgroundCommand, ['bgauto'], ' automatically changes the background based on the chat context using the AI request prompt', true, true);
window['forceSetBackground'] = forceSetBackground;
});

View File

@ -48,7 +48,7 @@
z-index: 2;
overflow: hidden;
resize: both;
display: flex;
}
img.expression {
@ -56,7 +56,6 @@ img.expression {
min-height: 100px;
max-height: 90vh;
max-width: 90vh;
width: 100%;
top: 0;
bottom: 0;
padding: 0;
@ -181,4 +180,4 @@ img.expression.default {
div.expression {
display: none;
}
}
}

View File

@ -597,8 +597,8 @@ function doAutoAdjust(chat, maxContext) {
console.debug('CHROMADB: Mean message length (tokens): %o', meanMessageLengthTokens);
// Get number of messages in context
const contextMessages = Math.max(1, Math.ceil(maxContext / meanMessageLengthTokens));
// Round up to nearest 10
const contextMessagesRounded = Math.ceil(contextMessages / 10) * 10;
// Round up to nearest 5
const contextMessagesRounded = Math.ceil(contextMessages / 5) * 5;
console.debug('CHROMADB: Estimated context messages (rounded): %o', contextMessagesRounded);
// Messages to keep (proportional, rounded to nearest 5, minimum 5, maximum 500)
const messagesToKeep = Math.min(defaultSettings.keep_context_max, Math.max(5, Math.floor(contextMessagesRounded * extension_settings.chromadb.keep_context_proportion / 5) * 5));

View File

@ -1,6 +1,6 @@
import { callPopup, eventSource, event_types, getCurrentChatId, reloadCurrentChat, saveSettingsDebounced } from "../../../script.js";
import { callPopup, getCurrentChatId, reloadCurrentChat, saveSettingsDebounced } from "../../../script.js";
import { extension_settings } from "../../extensions.js";
import { uuidv4, waitUntilCondition } from "../../utils.js";
import { getSortableDelay, uuidv4 } from "../../utils.js";
import { regex_placement } from "./engine.js";
async function saveRegexScript(regexScript, existingScriptIndex) {
@ -236,6 +236,7 @@ jQuery(async () => {
});
$('#saved_regex_scripts').sortable({
delay: getSortableDelay(),
stop: function () {
let newScripts = [];
$('#saved_regex_scripts').children().each(function () {

View File

@ -1,65 +1,40 @@
/*
TODO:
- Allow to upload RVC model to extras server ?
- Settings per characters ?
- load RVC models list from extras
- Settings per characters
*/
import { saveSettingsDebounced } from "../../../script.js";
import { getContext, getApiUrl, extension_settings, doExtrasFetch } from "../../extensions.js";
export { MODULE_NAME, rvcVoiceConversion};
import { getContext, getApiUrl, extension_settings, doExtrasFetch, ModuleWorkerWrapper, modules } from "../../extensions.js";
export { MODULE_NAME, rvcVoiceConversion };
const MODULE_NAME = 'RVC';
const DEBUG_PREFIX = "<RVC module> "
const UPDATE_INTERVAL = 1000
// Send an audio file to RVC to convert voice
async function rvcVoiceConversion(response, character) {
let apiResult
let charactersList = [] // Updated with module worker
let rvcModelsList = [] // Initialized only once
let rvcModelsReceived = false;
// 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.", DEBUG_PREFIX+'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;
function updateVoiceMapText() {
let voiceMapText = ""
for (let i in extension_settings.rvc.voiceMap) {
const voice_settings = extension_settings.rvc.voiceMap[i];
voiceMapText += i + ":"
+ voice_settings["modelName"] + "("
+ voice_settings["pitchExtraction"] + ","
+ voice_settings["pitchOffset"] + ","
+ voice_settings["indexRate"] + ","
+ voice_settings["filterRadius"] + ","
+ voice_settings["rmsMixRate"] + ","
+ voice_settings["protect"]
+ "),\n"
}
// Load model if different from currently loaded
//if (currentModel === null | currentModel != extension_settings.rvc.voiceMap[character])
// await rvcLoadModel(extension_settings.rvc.voiceMap[character]);
extension_settings.rvc.voiceMapText = voiceMapText;
$('#rvc_voice_map').val(voiceMapText);
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, DEBUG_PREFIX+' RVC Voice Conversion Failed', { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
}
return apiResult;
console.debug(DEBUG_PREFIX, "Updated voice map debug text to\n", voiceMapText)
}
//#############################//
@ -68,13 +43,13 @@ async function rvcVoiceConversion(response, character) {
const defaultSettings = {
enabled: false,
model:"",
pitchOffset:0,
pitchExtraction:"dio",
indexRate:0.88,
filterRadius:3,
//rmsMixRate:1,
protect:0.33,
model: "",
pitchOffset: 0,
pitchExtraction: "dio",
indexRate: 0.88,
filterRadius: 3,
rmsMixRate: 1,
protect: 0.33,
voicMapText: "",
voiceMap: {}
}
@ -83,12 +58,9 @@ 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_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);
@ -98,42 +70,17 @@ function loadSettings() {
$('#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_pitch_offset').val(extension_settings.rvc.pitchOffset);
$('#rvc_pitch_offset_value').text(extension_settings.rvc.pitchOffset);
$('#rvc_rms_mix_rate').val(extension_settings.rvc.rmsMixRate);
$("#rvc_rms_mix_rate_value").text(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);
toastr.error("no ':' found in: '"+text+"'", DEBUG_PREFIX+' RVC Voice map error', { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
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);
toastr.info("New map:\n"+JSON.stringify(extension_settings.rvc.voiceMap).substring(0,200)+"...", DEBUG_PREFIX+"Updated message mapping", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
extension_settings.rvc.voiceMapText = $('#rvc_voice_map').val();
saveSettingsDebounced();
}
}
async function onEnabledClick() {
@ -164,12 +111,93 @@ async function onPitchOffsetChange() {
saveSettingsDebounced()
}
async function onRmsMixRateChange() {
extension_settings.rvc.rmsMixRate = Number($('#rvc_rms_mix_rate').val());
$("#rvc_rms_mix_rate_value").text(extension_settings.rvc.rmsMixRate)
saveSettingsDebounced()
}
async function onProtectChange() {
extension_settings.rvc.protect = Number($('#rvc_protect').val());
$("#rvc_protect_value").text(extension_settings.rvc.protect)
saveSettingsDebounced()
}
async function onApplyClick() {
let error = false;
const character = $("#rvc_character_select").val();
const model_name = $("#rvc_model_select").val();
const pitchExtraction = $("#rvc_pitch_extraction").val();
const indexRate = $("#rvc_index_rate").val();
const filterRadius = $("#rvc_filter_radius").val();
const pitchOffset = $("#rvc_pitch_offset").val();
const rmsMixRate = $("#rvc_rms_mix_rate").val();
const protect = $("#rvc_protect").val();
if (character === "none") {
toastr.error("Character not selected.", DEBUG_PREFIX + " voice mapping apply", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
return;
}
if (model_name == "none") {
toastr.error("Model not selected.", DEBUG_PREFIX + " voice mapping apply", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
return;
}
extension_settings.rvc.voiceMap[character] = {
"modelName": model_name,
"pitchExtraction": pitchExtraction,
"indexRate": indexRate,
"filterRadius": filterRadius,
"pitchOffset": pitchOffset,
"rmsMixRate": rmsMixRate,
"protect": protect
}
updateVoiceMapText();
console.debug(DEBUG_PREFIX, "Updated settings of ", character, ":", extension_settings.rvc.voiceMap[character])
saveSettingsDebounced();
}
async function onDeleteClick() {
const character = $("#rvc_character_select").val();
if (character === "none") {
toastr.error("Character not selected.", DEBUG_PREFIX + " voice mapping delete", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
return;
}
delete extension_settings.rvc.voiceMap[character];
console.debug(DEBUG_PREFIX, "Deleted settings of ", character);
updateVoiceMapText();
saveSettingsDebounced();
}
async function onClickUpload() {
const url = new URL(getApiUrl());
const inputFiles = $("#rvc_model_upload_file").get(0).files;
let formData = new FormData();
for (const file of inputFiles)
formData.append(file.name, file);
console.debug(DEBUG_PREFIX, "Sending files:", formData);
url.pathname = '/api/voice-conversion/rvc/upload-models';
const apiResult = await doExtrasFetch(url, {
method: 'POST',
body: formData
});
if (!apiResult.ok) {
toastr.error(apiResult.statusText, DEBUG_PREFIX + ' Check extras console for errors log');
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
}
alert('The file has been uploaded successfully.');
}
$(document).ready(function () {
function addExtensionControls() {
const settingsHtml = `
@ -185,8 +213,28 @@ $(document).ready(function () {
<input type="checkbox" id="rvc_enabled" name="rvc_enabled">
<small>Enabled</small>
</label>
<label>Voice Map (debug infos)</label>
<textarea id="rvc_voice_map" type="text" class="text_pole textarea_compact" rows="4"
placeholder="Voice map will appear here for debug purpose"></textarea>
</div>
<div>
<label for="rvc_character_select">Character:</label>
<select id="rvc_character_select">
<!-- Populated by JS -->
</select>
<label for="rvc_model_select">Voice:</label>
<select id="rvc_model_select">
<!-- Populated by JS -->
</select>
<div>
<label for="rvc_model_upload_file">Select models to upload (zip files)</label>
<input
type="file"
id="rvc_model_upload_file"
accept=".zip,.rar,.7zip,.7z" multiple />
<button id="rvc_model_upload_button"> Upload </button>
<button id="rvc_model_refresh_button"> Refresh Voices </button>
</div>
<span>Select Pitch Extraction</span> </br>
<select id="rvc_pitch_extraction">
<option value="dio">dio</option>
@ -194,9 +242,8 @@ $(document).ready(function () {
<option value="harvest">harvest</option>
<option value="torchcrepe">torchcrepe</option>
<option value="rmvpe">rmvpe</option>
<option value="">None</option>
</select>
</div>
<div>
<label for="rvc_index_rate">
Index rate for feature retrieval (<span id="rvc_index_rate_value"></span>)
</label>
@ -208,15 +255,17 @@ $(document).ready(function () {
<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_rms_mix_rate">Mix rate (<span id="rvc_rms_mix_rate_value"></span>)</label>
<input id="rvc_rms_mix_rate" type="range" min="0" max="1" step="0.01" value="1" />
<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" />
<input id="rvc_delete" class="menu_button" type="submit" value="Delete" />
</div>
</div>
</div>
@ -225,14 +274,157 @@ $(document).ready(function () {
`;
$('#extensions_settings').append(settingsHtml);
$("#rvc_enabled").on("click", onEnabledClick);
$("#rvc_voice_map").attr("disabled", "disabled");;
$('#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_rms_mix_rate').on('input', onRmsMixRateChange);
$('#rvc_protect').on('input', onProtectChange);
$("#rvc_apply").on("click", onApplyClick);
$("#rvc_delete").on("click", onDeleteClick);
$("#rvc_model_upload_file").show();
$("#rvc_model_upload_button").on("click", onClickUpload);
$("#rvc_model_refresh_button").on("click", refreshVoiceList);
}
addExtensionControls(); // No init dependencies
loadSettings(); // Depends on Extension Controls
const wrapper = new ModuleWorkerWrapper(moduleWorker);
setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL);
moduleWorker();
})
//#############################//
// API Calls //
//#############################//
/*
Check model installation state, return one of ["installed", "corrupted", "absent"]
*/
async function get_models_list(model_id) {
const url = new URL(getApiUrl());
url.pathname = '/api/voice-conversion/rvc/get-models-list';
const apiResult = await doExtrasFetch(url, {
method: 'POST'
});
if (!apiResult.ok) {
toastr.error(apiResult.statusText, DEBUG_PREFIX + ' Check model state request failed');
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
}
return apiResult
}
/*
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.", DEBUG_PREFIX+'RVC Voice map error', { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
console.info(DEBUG_PREFIX, "No RVC model assign in voice map for current character " + character);
return response;
}
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)
const voice_settings = extension_settings.rvc.voiceMap[character];
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": voice_settings["modelName"],
"pitchExtraction": voice_settings["pitchExtraction"],
"pitchOffset": voice_settings["pitchOffset"],
"indexRate": voice_settings["indexRate"],
"filterRadius": voice_settings["filterRadius"],
"rmsMixRate": voice_settings["rmsMixRate"],
"protect": voice_settings["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, DEBUG_PREFIX + ' RVC Voice Conversion Failed', { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
}
return apiResult;
}
//#############################//
// Module Worker //
//#############################//
async function refreshVoiceList() {
let result = await get_models_list();
result = await result.json();
rvcModelsList = result["models_list"]
$('#rvc_model_select')
.find('option')
.remove()
.end()
.append('<option value="none">Select Voice</option>')
.val('none')
for (const modelName of rvcModelsList) {
$("#rvc_model_select").append(new Option(modelName, modelName));
}
rvcModelsReceived = true
console.debug(DEBUG_PREFIX, "Updated model list to:", rvcModelsList);
}
async function moduleWorker() {
updateCharactersList();
if (modules.includes('rvc') && !rvcModelsReceived) {
refreshVoiceList();
}
}
function updateCharactersList() {
let currentcharacters = new Set();
for (const i of getContext().characters) {
currentcharacters.add(i.name);
}
currentcharacters = Array.from(currentcharacters)
if (JSON.stringify(charactersList) !== JSON.stringify(currentcharacters)) {
charactersList = currentcharacters
$('#rvc_character_select')
.find('option')
.remove()
.end()
.append('<option value="none">Select Character</option>')
.val('none')
for (const charName of charactersList) {
$("#rvc_character_select").append(new Option(charName, charName));
}
console.debug(DEBUG_PREFIX, "Updated character list to:", charactersList);
}
}

View File

@ -40,6 +40,7 @@ const generationMode = {
NOW: 4,
FACE: 5,
FREE: 6,
BACKGROUND: 7,
}
const modeLabels = {
@ -49,6 +50,7 @@ const modeLabels = {
[generationMode.SCENARIO]: 'Scenario ("The Whole Story")',
[generationMode.NOW]: 'Last Message',
[generationMode.RAW_LAST]: 'Raw Last Message',
[generationMode.BACKGROUND]: 'Background',
}
const triggerWords = {
@ -58,6 +60,7 @@ const triggerWords = {
[generationMode.RAW_LAST]: ['raw_last'],
[generationMode.NOW]: ['last'],
[generationMode.FACE]: ['face'],
[generationMode.BACKGROUND]: ['background'],
}
const promptTemplates = {
@ -94,6 +97,7 @@ const promptTemplates = {
'(location),(character list by gender),(primary action), (relative character position) POV, (character 1's description and actions), (character 2's description and actions)']`,
[generationMode.RAW_LAST]: "[Pause your roleplay and provide ONLY the last chat message string back to me verbatim. Do not write anything after the string. Do not roleplay at all in your response. Do not continue the roleplay story.]",
[generationMode.BACKGROUND]: "[Pause your roleplay and provide a detailed description of {{char}}'s surroundings in the form of a comma-delimited list of keywords and phrases. The list must include all of the following items in this order: location, time of day, weather, lighting, and any other relevant details. Do not include descriptions of characters and non-visual qualities such as names, personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase 'background,'. Ignore the rest of the story when crafting this description. Do not roleplay as {{user}} when writing this description, and do not attempt to continue the story.]",
}
const helpString = [
@ -105,6 +109,7 @@ const helpString = [
`<li>${m(j(triggerWords[generationMode.SCENARIO]))} visual recap of the whole chat scenario</li>`,
`<li>${m(j(triggerWords[generationMode.NOW]))} visual recap of the last chat message</li>`,
`<li>${m(j(triggerWords[generationMode.RAW_LAST]))} visual recap of the last chat message with no summary</li>`,
`<li>${m(j(triggerWords[generationMode.BACKGROUND]))} generate a background for this chat based on the chat's context</li>`,
'</ul>',
`Anything else would trigger a "free mode" to make SD generate whatever you prompted.<Br>
example: '/sd apple tree' would generate a picture of an apple tree.`,
@ -159,6 +164,13 @@ async function loadSettings() {
extension_settings.sd.prompts = promptTemplates;
}
// Insert missing templates
for (const [key, value] of Object.entries(promptTemplates)) {
if (extension_settings.sd.prompts[key] === undefined) {
extension_settings.sd.prompts[key] = value;
}
}
if (extension_settings.sd.character_prompts === undefined) {
extension_settings.sd.character_prompts = {};
}
@ -554,9 +566,35 @@ async function generatePicture(_, trigger, message, callback) {
const context = getContext();
const prevSDHeight = extension_settings.sd.height;
if (generationType == generationMode.FACE) {
const prevSDWidth = extension_settings.sd.width;
const aspectRatio = extension_settings.sd.width / extension_settings.sd.height;
// Face images are always portrait (pun intended)
if (generationType == generationMode.FACE && aspectRatio >= 1) {
// Round to nearest multiple of 64
extension_settings.sd.height = Math.round(extension_settings.sd.height * 1.5 / 64) * 64;
extension_settings.sd.height = Math.round(extension_settings.sd.width * 1.5 / 64) * 64;
}
// Background images are always landscape
if (generationType == generationMode.BACKGROUND && aspectRatio <= 1) {
// Round to nearest multiple of 64
extension_settings.sd.width = Math.round(extension_settings.sd.height * 1.8 / 64) * 64;
const callbackOriginal = callback;
callback = function (prompt, base64Image) {
const imgUrl = `url(${base64Image})`;
if ('forceSetBackground' in window) {
forceSetBackground(imgUrl);
} else {
toastr.info('Background image will not be preserved.', '"Chat backgrounds" extension is disabled.');
$('#bg_custom').css('background-image', imgUrl);
}
if (typeof callbackOriginal === 'function') {
callbackOriginal(prompt, base64Image);
} else {
sendMessage(prompt, base64Image);
}
}
}
try {
@ -566,13 +604,14 @@ async function generatePicture(_, trigger, message, callback) {
context.deactivateSendButtons();
hideSwipeButtons();
await sendGenerationRequest(prompt, callback);
await sendGenerationRequest(generationType, prompt, callback);
} catch (err) {
console.trace(err);
throw new Error('SD prompt text generation failed.')
}
finally {
extension_settings.sd.height = prevSDHeight;
extension_settings.sd.width = prevSDWidth;
context.activateSendButtons();
showSwipeButtons();
}
@ -605,16 +644,20 @@ async function generatePrompt(quiet_prompt) {
return processReply(reply);
}
async function sendGenerationRequest(prompt, callback) {
async function sendGenerationRequest(generationType, prompt, callback) {
const prefix = generationType !== generationMode.BACKGROUND
? combinePrefixes(extension_settings.sd.prompt_prefix, getCharacterPrefix())
: extension_settings.sd.prompt_prefix;
if (extension_settings.sd.horde) {
await generateHordeImage(prompt, callback);
await generateHordeImage(prompt, prefix, callback);
} else {
await generateExtrasImage(prompt, callback);
await generateExtrasImage(prompt, prefix, callback);
}
}
async function generateExtrasImage(prompt, callback) {
console.log(extension_settings.sd);
async function generateExtrasImage(prompt, prefix, callback) {
console.debug(extension_settings.sd);
const url = new URL(getApiUrl());
url.pathname = '/api/image';
const result = await doExtrasFetch(url, {
@ -627,7 +670,7 @@ async function generateExtrasImage(prompt, callback) {
scale: extension_settings.sd.scale,
width: extension_settings.sd.width,
height: extension_settings.sd.height,
prompt_prefix: combinePrefixes(extension_settings.sd.prompt_prefix, getCharacterPrefix()),
prompt_prefix: prefix,
negative_prompt: extension_settings.sd.negative_prompt,
restore_faces: !!extension_settings.sd.restore_faces,
enable_hr: !!extension_settings.sd.enable_hr,
@ -644,7 +687,7 @@ async function generateExtrasImage(prompt, callback) {
}
}
async function generateHordeImage(prompt, callback) {
async function generateHordeImage(prompt, prefix, callback) {
const result = await fetch('/horde_generateimage', {
method: 'POST',
headers: getRequestHeaders(),
@ -655,7 +698,7 @@ async function generateHordeImage(prompt, callback) {
scale: extension_settings.sd.scale,
width: extension_settings.sd.width,
height: extension_settings.sd.height,
prompt_prefix: combinePrefixes(extension_settings.sd.prompt_prefix, getCharacterPrefix()),
prompt_prefix: prefix,
negative_prompt: extension_settings.sd.negative_prompt,
model: extension_settings.sd.model,
nsfw: extension_settings.sd.horde_nsfw,
@ -680,6 +723,7 @@ async function sendMessage(prompt, image) {
name: context.groupId ? systemUserName : context.name2,
is_system: context.groupId ? true : false,
is_user: false,
is_system: true,
is_name: true,
send_date: timestampToMoment(Date.now()).format('LL LT'),
mes: context.groupId ? p(messageText) : messageText,
@ -715,6 +759,7 @@ function addSDGenButtons() {
<li class="list-group-item" id="sd_world" data-value="world">The Whole Story</li>
<li class="list-group-item" id="sd_last" data-value="last">The Last Message</li>
<li class="list-group-item" id="sd_raw_last" data-value="raw_last">Raw Last Message</li>
<li class="list-group-item" id="sd_background" data-value="background">Background</li>
</ul>
</div>`;
@ -797,7 +842,7 @@ async function sdMessageButton(e) {
message.extra.title = prompt;
console.log('Regenerating an image, using existing prompt:', prompt);
await sendGenerationRequest(prompt, saveGeneratedImage);
await sendGenerationRequest(generationMode.FREE, prompt, saveGeneratedImage);
}
else {
console.log("doing /sd raw last");
@ -828,36 +873,22 @@ async function sdMessageButton(e) {
};
$("#sd_dropdown [id]").on("click", function () {
var id = $(this).attr("id");
if (id == "sd_you") {
console.log("doing /sd you");
generatePicture('sd', 'you');
}
const id = $(this).attr("id");
const idParamMap = {
"sd_you": "you",
"sd_face": "face",
"sd_me": "me",
"sd_world": "scene",
"sd_last": "last",
"sd_raw_last": "raw_last",
"sd_background": "background"
};
else if (id == "sd_face") {
console.log("doing /sd face");
generatePicture('sd', 'face');
const param = idParamMap[id];
}
else if (id == "sd_me") {
console.log("doing /sd me");
generatePicture('sd', 'me');
}
else if (id == "sd_world") {
console.log("doing /sd scene");
generatePicture('sd', 'scene');
}
else if (id == "sd_last") {
console.log("doing /sd last");
generatePicture('sd', 'last');
}
else if (id == "sd_raw_last") {
console.log("doing /sd raw last");
generatePicture('sd', 'raw_last');
if (param) {
console.log("doing /sd " + param)
generatePicture('sd', param);
}
});

View File

@ -4,17 +4,19 @@ TODO:
- Delete useless call
*/
import { saveSettingsDebounced } from "../../../script.js"
import { doExtrasFetch, extension_settings, getApiUrl, getContext, modules, ModuleWorkerWrapper } from "../../extensions.js"
export { CoquiTtsProvider }
const DEBUG_PREFIX = "<Coqui TTS module> "
const UPDATE_INTERVAL = 1000
const DEBUG_PREFIX = "<Coqui TTS module> ";
const UPDATE_INTERVAL = 1000;
let inApiCall = false
let charactersList = [] // Updated with module worker
let coquiApiModels = {} // Initialized only once
let inApiCall = false;
let charactersList = []; // Updated with module worker
let coquiApiModels = {}; // Initialized only once
let coquiApiModelsFull = {}; // Initialized only once
let coquiLocalModels = []; // Initialized only once
let coquiLocalModelsReceived = false;
/*
coquiApiModels format [language][dataset][name]:coqui-api-model-id, example:
{
@ -32,24 +34,19 @@ coquiApiModels format [language][dataset][name]:coqui-api-model-id, example:
*/
const languageLabels = {
"multilingual": "Multilingual",
"en" : "English",
"fr" : "French",
"es" : "Spanish",
"ja" : "Japanese"
"en": "English",
"fr": "French",
"es": "Spanish",
"ja": "Japanese"
}
function throwIfModuleMissing() {
if (!modules.includes('coqui-tts')) {
toastr.error(`Add coqui-tts to enable-modules and restart the Extras API.`, "Coqui TTS module not loaded.", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
throw new Error(DEBUG_PREFIX,`Coqui TTS module not loaded.`);
throw new Error(DEBUG_PREFIX, `Coqui TTS module not loaded.`);
}
}
function throwLocalOrigin() {
toastr.info("coming soon, ready when ready, etc", DEBUG_PREFIX+' Custom models not supported yet', { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
throw new Error(DEBUG_PREFIX,`requesting feature not implemented yet.`);
}
function resetModelSettings() {
$("#coqui_api_model_settings_language").val("none");
$("#coqui_api_model_settings_speaker").val("none");
@ -62,7 +59,7 @@ function updateCharactersList() {
}
currentcharacters = Array.from(currentcharacters)
if (JSON.stringify(charactersList) !== JSON.stringify(currentcharacters)) {
charactersList = currentcharacters
@ -72,12 +69,12 @@ function updateCharactersList() {
.end()
.append('<option value="none">Select Character</option>')
.val('none')
for(const charName of charactersList) {
$("#coqui_character_select").append(new Option(charName,charName));
for (const charName of charactersList) {
$("#coqui_character_select").append(new Option(charName, charName));
}
console.debug(DEBUG_PREFIX,"Updated character list to:", charactersList);
console.debug(DEBUG_PREFIX, "Updated character list to:", charactersList);
}
}
@ -90,7 +87,7 @@ class CoquiTtsProvider {
defaultSettings = {
voiceMap: "",
voiceMapDict : {}
voiceMapDict: {}
}
get settingsHtml() {
@ -102,11 +99,15 @@ class CoquiTtsProvider {
<select id="coqui_character_select">
<!-- Populated by JS -->
</select>
<input id="coqui_remove_char_mapping" class="menu_button" type="button" value="Remove from Voice Map" />
<label for="coqui_model_origin">Models:</label>
<select id="coqui_model_origin">gpu_mode
<option value="none">Select Origin</option>
<option value="coqui-api">Coqui TTS</option>
<option value="local">My models</option>
<option value="coqui-api">Coqui API (Tested)</option>
<option value="coqui-api-full">Coqui API (Experimental)</option>
<option value="local">My Models</option>
</select>
<div id="coqui_api_model_div">
@ -129,6 +130,13 @@ class CoquiTtsProvider {
<span id="coqui_api_model_install_status">Model installed on extras server</span>
<input id="coqui_api_model_install_button" class="menu_button" type="button" value="Install" />
</div>
<div id="coqui_local_model_div">
<select id="coqui_local_model_name">
<!-- Populated by JS and request -->
</select>
</div>
</div>
</div>
</div>
@ -140,25 +148,31 @@ class CoquiTtsProvider {
// Only accept keys defined in defaultSettings
this.settings = this.defaultSettings
for (const key in settings){
if (key in this.settings){
for (const key in settings) {
if (key in this.settings) {
this.settings[key] = settings[key]
} else {
throw DEBUG_PREFIX+`Invalid setting passed to extension: ${key}`
throw DEBUG_PREFIX + `Invalid setting passed to extension: ${key}`
}
}
this.updateVoiceMap(); // Overide any manual modification
$("#coqui_api_model_div").hide();
$("#coqui_local_model_div").hide();
$("#coqui_api_language").show();
$("#coqui_api_model_name").hide();
$("#coqui_api_model_settings").hide();
$("#coqui_api_model_install_status").hide();
$("#coqui_api_model_install_button").hide();
$("#coqui_model_origin").on("change",this.onModelOriginChange);
$("#coqui_api_language").on("change",this.onModelLanguageChange);
$("#coqui_api_model_name").on("change",this.onModelNameChange);
let that = this
$("#coqui_model_origin").on("change", function () { that.onModelOriginChange() });
$("#coqui_api_language").on("change", function () { that.onModelLanguageChange() });
$("#coqui_api_model_name").on("change", function () { that.onModelNameChange() });
$("#coqui_remove_char_mapping").on("click", function () { that.onRemoveClick() });
// Load characters list
$('#coqui_character_select')
@ -167,9 +181,9 @@ class CoquiTtsProvider {
.end()
.append('<option value="none">Select Character</option>')
.val('none')
for(const charName of charactersList) {
$("#coqui_character_select").append(new Option(charName,charName));
for (const charName of charactersList) {
$("#coqui_character_select").append(new Option(charName, charName));
}
// Load coqui-api settings from json file
@ -178,7 +192,7 @@ class CoquiTtsProvider {
.then(json => {
coquiApiModels = json;
console.debug(DEBUG_PREFIX,"initialized coqui-api model list to", coquiApiModels);
/*
$('#coqui_api_language')
.find('option')
.remove()
@ -189,13 +203,33 @@ class CoquiTtsProvider {
for(let language in coquiApiModels) {
$("#coqui_api_language").append(new Option(languageLabels[language],language));
console.log(DEBUG_PREFIX,"added language",language);
}
}*/
});
// Load coqui-api FULL settings from json file
fetch("/scripts/extensions/tts/coqui_api_models_settings_full.json")
.then(response => response.json())
.then(json => {
coquiApiModelsFull = json;
console.debug(DEBUG_PREFIX,"initialized coqui-api full model list to", coquiApiModelsFull);
/*
$('#coqui_api_full_language')
.find('option')
.remove()
.end()
.append('<option value="none">Select model language</option>')
.val('none');
for(let language in coquiApiModelsFull) {
$("#coqui_api_full_language").append(new Option(languageLabels[language],language));
console.log(DEBUG_PREFIX,"added language",language);
}*/
});
}
updateVoiceMap(){
updateVoiceMap() {
this.settings.voiceMap = "";
for(let i in this.settings.voiceMapDict) {
for (let i in this.settings.voiceMapDict) {
const voice_settings = this.settings.voiceMapDict[i];
this.settings.voiceMap += i + ":" + voice_settings["model_id"];
@ -210,9 +244,9 @@ class CoquiTtsProvider {
$("#tts_voice_map").val(this.settings.voiceMap);
extension_settings.tts.Coqui = this.settings;
}
onSettingsChange() {
console.debug(DEBUG_PREFIX,"Settings changes",this.settings);
console.debug(DEBUG_PREFIX, "Settings changes", this.settings);
extension_settings.tts.Coqui = this.settings;
}
@ -228,99 +262,168 @@ class CoquiTtsProvider {
let model_setting_language = $("#coqui_api_model_settings_language").val();
let model_setting_speaker = $("#coqui_api_model_settings_speaker").val();
if (character === "none") {
toastr.error(`Character not selected, please select one.`, DEBUG_PREFIX+" voice mapping character", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
toastr.error(`Character not selected, please select one.`, DEBUG_PREFIX + " voice mapping character", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
this.updateVoiceMap(); // Overide any manual modification
return;
}
if (model_origin == "none") {
toastr.error(`Origin not selected, please select one.`, DEBUG_PREFIX+" voice mapping origin", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
toastr.error(`Origin not selected, please select one.`, DEBUG_PREFIX + " voice mapping origin", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
this.updateVoiceMap(); // Overide any manual modification
return;
}
if (model_origin == "local") {
throwLocalOrigin();
const model_id = $("#coqui_local_model_name").val();
if (model_name == "none") {
toastr.error(`Model not selected, please select one.`, DEBUG_PREFIX + " voice mapping model", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
this.updateVoiceMap(); // Overide any manual modification
return;
}
this.settings.voiceMapDict[character] = { model_type: "local", model_id: "local/" + model_id };
console.debug(DEBUG_PREFIX, "Registered new voice map: ", character, ":", this.settings.voiceMapDict[character]);
this.updateVoiceMap(); // Overide any manual modification
return;
}
if (model_language == "none") {
toastr.error(`Language not selected, please select one.`, DEBUG_PREFIX+" voice mapping language", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
toastr.error(`Language not selected, please select one.`, DEBUG_PREFIX + " voice mapping language", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
this.updateVoiceMap(); // Overide any manual modification
return;
}
if (model_name == "none") {
toastr.error(`Model not selected, please select one.`, DEBUG_PREFIX+" voice mapping model", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
toastr.error(`Model not selected, please select one.`, DEBUG_PREFIX + " voice mapping model", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
this.updateVoiceMap(); // Overide any manual modification
return;
}
if (model_setting_language == "none")
model_setting_language = null;
if (model_setting_speaker == "none")
model_setting_speaker = null;
const tokens = $('#coqui_api_model_name').val().split("/");
const model_dataset = tokens[0];
const model_label = tokens[1];
const model_id = "tts_models/" + model_language + "/" + model_dataset + "/" + model_label
if (model_setting_language == null & "languages" in coquiApiModels[model_language][model_dataset][model_label]) {
let modelDict = coquiApiModels
if (model_origin == "coqui-api-full")
modelDict = coquiApiModelsFull
if (model_setting_language == null & "languages" in modelDict[model_language][model_dataset][model_label]) {
toastr.error(`Model language not selected, please select one.`, DEBUG_PREFIX+" voice mapping model language", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
return;
}
if (model_setting_speaker == null & "speakers" in coquiApiModels[model_language][model_dataset][model_label]) {
if (model_setting_speaker == null & "speakers" in modelDict[model_language][model_dataset][model_label]) {
toastr.error(`Model speaker not selected, please select one.`, DEBUG_PREFIX+" voice mapping model speaker", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
return;
}
console.debug(DEBUG_PREFIX,"Current voice map: ",this.settings.voiceMap);
console.debug(DEBUG_PREFIX, "Current voice map: ", this.settings.voiceMap);
this.settings.voiceMapDict[character] = {model_id: model_id, model_language:model_setting_language, model_speaker:model_setting_speaker};
this.settings.voiceMapDict[character] = { model_type: "coqui-api", model_id: model_id, model_language: model_setting_language, model_speaker: model_setting_speaker };
console.debug(DEBUG_PREFIX,"Registered new voice map: ",character,":",this.settings.voiceMapDict[character]);
console.debug(DEBUG_PREFIX, "Registered new voice map: ", character, ":", this.settings.voiceMapDict[character]);
this.updateVoiceMap();
let successMsg = character+":"+model_id;
let successMsg = character + ":" + model_id;
if (model_setting_language != null)
successMsg += "[" + model_setting_language + "]";
if (model_setting_speaker != null)
successMsg += "[" + model_setting_speaker + "]";
toastr.info(successMsg, DEBUG_PREFIX+" voice map updated", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
toastr.info(successMsg, DEBUG_PREFIX + " voice map updated", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
return
}
// DBG: assume voiceName is correct
// TODO: check voice is correct
async getVoice(voiceName) {
console.log(DEBUG_PREFIX,"getVoice",voiceName);
const output = {voice_id: voiceName};
console.log(DEBUG_PREFIX, "getVoice", voiceName);
const output = { voice_id: voiceName };
return output;
}
async onRemoveClick() {
const character = $("#coqui_character_select").val();
if (character === "none") {
toastr.error(`Character not selected, please select one.`, DEBUG_PREFIX + " voice mapping character", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
return;
}
// Todo erase from voicemap
delete (this.settings.voiceMapDict[character]);
this.updateVoiceMap(); // TODO
}
async onModelOriginChange() {
throwIfModuleMissing()
resetModelSettings();
const model_origin = $('#coqui_model_origin').val();
console.debug(model_origin);
if (model_origin == "none") {
$("#coqui_local_model_div").hide();
$("#coqui_api_model_div").hide();
}
// TODO: show coqui model list
// show coqui model selected list (SAFE)
if (model_origin == "coqui-api") {
$("#coqui_local_model_div").hide();
$('#coqui_api_language')
.find('option')
.remove()
.end()
.append('<option value="none">Select model language</option>')
.val('none');
for(let language in coquiApiModels) {
let languageLabel = language
if (language in languageLabels)
languageLabel = languageLabels[language]
$("#coqui_api_language").append(new Option(languageLabel,language));
console.log(DEBUG_PREFIX,"added language",languageLabel,"(",language,")");
}
$("#coqui_api_model_div").show();
}
else
$("#coqui_api_model_div").hide();
// TODO show local model list
// show coqui model full list (UNSAFE)
if (model_origin == "coqui-api-full") {
$("#coqui_local_model_div").hide();
$('#coqui_api_language')
.find('option')
.remove()
.end()
.append('<option value="none">Select model language</option>')
.val('none');
for(let language in coquiApiModelsFull) {
let languageLabel = language
if (language in languageLabels)
languageLabel = languageLabels[language]
$("#coqui_api_language").append(new Option(languageLabel,language));
console.log(DEBUG_PREFIX,"added language",languageLabel,"(",language,")");
}
$("#coqui_api_model_div").show();
}
// show local model list
if (model_origin == "local") {
throwLocalOrigin();
$("#coqui_api_model_div").hide();
$("#coqui_local_model_div").show();
}
}
@ -331,12 +434,12 @@ class CoquiTtsProvider {
const model_origin = $('#coqui_model_origin').val();
const model_language = $('#coqui_api_language').val();
console.debug(model_language);
if (model_language == "none") {
$("#coqui_api_model_name").hide();
return;
}
$("#coqui_api_model_name").show();
$('#coqui_api_model_name')
.find('option')
@ -345,18 +448,23 @@ class CoquiTtsProvider {
.append('<option value="none">Select model</option>')
.val('none');
for(let model_dataset in coquiApiModels[model_language])
for(let model_name in coquiApiModels[model_language][model_dataset]) {
let modelDict = coquiApiModels
if (model_origin == "coqui-api-full")
modelDict = coquiApiModelsFull
for(let model_dataset in modelDict[model_language])
for(let model_name in modelDict[model_language][model_dataset]) {
const model_id = model_dataset + "/" + model_name
const model_label = model_name + " ("+model_dataset+" dataset)"
$("#coqui_api_model_name").append(new Option(model_label,model_id));
}
const model_label = model_name + " (" + model_dataset + " dataset)"
$("#coqui_api_model_name").append(new Option(model_label, model_id));
}
}
async onModelNameChange() {
throwIfModuleMissing();
resetModelSettings();
$("#coqui_api_model_settings").hide();
const model_origin = $('#coqui_model_origin').val();
// No model selected
if ($('#coqui_api_model_name').val() == "none") {
@ -371,21 +479,25 @@ class CoquiTtsProvider {
const model_dataset = tokens[0];
const model_name = tokens[1];
const model_settings = coquiApiModels[model_language][model_dataset][model_name]
let modelDict = coquiApiModels
if (model_origin == "coqui-api-full")
modelDict = coquiApiModelsFull
const model_settings = modelDict[model_language][model_dataset][model_name]
if ("languages" in model_settings) {
$("#coqui_api_model_settings").show();
$("#coqui_api_model_settings_language").show();
$('#coqui_api_model_settings_language')
.find('option')
.remove()
.end()
.append('<option value="none">Select language</option>')
.val('none');
.find('option')
.remove()
.end()
.append('<option value="none">Select language</option>')
.val('none');
for(var i = 0; i < model_settings["languages"].length; i++) {
const language_label = JSON.stringify(model_settings["languages"][i]).replaceAll("\"","");
$("#coqui_api_model_settings_language").append(new Option(language_label,i));
for (var i = 0; i < model_settings["languages"].length; i++) {
const language_label = JSON.stringify(model_settings["languages"][i]).replaceAll("\"", "");
$("#coqui_api_model_settings_language").append(new Option(language_label, i));
}
}
else {
@ -396,15 +508,15 @@ class CoquiTtsProvider {
$("#coqui_api_model_settings").show();
$("#coqui_api_model_settings_speaker").show();
$('#coqui_api_model_settings_speaker')
.find('option')
.remove()
.end()
.append('<option value="none">Select speaker</option>')
.val('none');
.find('option')
.remove()
.end()
.append('<option value="none">Select speaker</option>')
.val('none');
for(var i = 0; i < model_settings["speakers"].length;i++) {
const speaker_label = JSON.stringify(model_settings["speakers"][i]).replaceAll("\"","");
$("#coqui_api_model_settings_speaker").append(new Option(speaker_label,i));
for (var i = 0; i < model_settings["speakers"].length; i++) {
const speaker_label = JSON.stringify(model_settings["speakers"][i]).replaceAll("\"", "");
$("#coqui_api_model_settings_speaker").append(new Option(speaker_label, i));
}
}
else {
@ -415,13 +527,13 @@ class CoquiTtsProvider {
$("#coqui_api_model_install_status").show();
// Check if already installed and propose to do it otherwise
const model_id = coquiApiModels[model_language][model_dataset][model_name]["id"]
const model_id = modelDict[model_language][model_dataset][model_name]["id"]
console.debug(DEBUG_PREFIX,"Check if model is already installed",model_id);
let result = await CoquiTtsProvider.checkmodel_state(model_id);
result = await result.json();
const model_state = result["model_state"];
console.debug(DEBUG_PREFIX," Model state:", model_state)
console.debug(DEBUG_PREFIX, " Model state:", model_state)
if (model_state == "installed") {
$("#coqui_api_model_install_status").text("Model already installed on extras server");
@ -435,13 +547,13 @@ class CoquiTtsProvider {
$("#coqui_api_model_install_status").text("Model found but incomplete try install again (maybe still downloading)"); // (remove and download again)
}
else {
toastr.info("Click download button to install the model "+$("#coqui_api_model_name").find(":selected").text(), DEBUG_PREFIX+" model not installed", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
toastr.info("Click download button to install the model " + $("#coqui_api_model_name").find(":selected").text(), DEBUG_PREFIX + " model not installed", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
$("#coqui_api_model_install_status").text("Model not found on extras server");
}
const onModelNameChange_pointer = this.onModelNameChange;
$("#coqui_api_model_install_button").off("click").on("click", async function (){
$("#coqui_api_model_install_button").off("click").on("click", async function () {
try {
$("#coqui_api_model_install_status").text("Downloading model...");
$("#coqui_api_model_install_button").hide();
@ -449,33 +561,33 @@ class CoquiTtsProvider {
let apiResult = await CoquiTtsProvider.installModel(model_id, action);
apiResult = await apiResult.json();
console.debug(DEBUG_PREFIX,"Response:",apiResult);
console.debug(DEBUG_PREFIX, "Response:", apiResult);
if (apiResult["status"] == "done") {
$("#coqui_api_model_install_status").text("Model installed and ready to use!");
$("#coqui_api_model_install_button").hide();
onModelNameChange_pointer();
}
if (apiResult["status"] == "downloading") {
toastr.error("Check extras console for progress", DEBUG_PREFIX+" already downloading", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
toastr.error("Check extras console for progress", DEBUG_PREFIX + " already downloading", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
$("#coqui_api_model_install_status").text("Already downloading a model, check extras console!");
$("#coqui_api_model_install_button").show();
}
} catch (error) {
console.error(error)
toastr.error(error, DEBUG_PREFIX+" error with model download", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
toastr.error(error, DEBUG_PREFIX + " error with model download", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
onModelNameChange_pointer();
}
// will refresh model status
// will refresh model status
});
$("#coqui_api_model_install_button").show();
return;
}
}
//#############################//
// API Calls //
@ -501,7 +613,7 @@ class CoquiTtsProvider {
});
if (!apiResult.ok) {
toastr.error(apiResult.statusText, DEBUG_PREFIX+' Check model state request failed');
toastr.error(apiResult.statusText, DEBUG_PREFIX + ' Check model state request failed');
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
}
@ -526,14 +638,40 @@ class CoquiTtsProvider {
});
if (!apiResult.ok) {
toastr.error(apiResult.statusText, DEBUG_PREFIX+' Install model '+model_id+' request failed');
toastr.error(apiResult.statusText, DEBUG_PREFIX + ' Install model ' + model_id + ' request failed');
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
}
return apiResult
}
// Get speakers
/*
Retrieve user custom models
*/
static async getLocalModelList() {
throwIfModuleMissing()
const url = new URL(getApiUrl());
url.pathname = '/api/text-to-speech/coqui/local/get-models';
const apiResult = await doExtrasFetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
},
body: JSON.stringify({
"model_id": "model_id",
"action": "action"
})
})
if (!apiResult.ok) {
toastr.error(apiResult.statusText, DEBUG_PREFIX + ' Get local model list request failed');
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
}
return apiResult
}
// Expect voiceId format to be like:
@ -547,10 +685,10 @@ class CoquiTtsProvider {
let language = "none"
let speaker = "none"
const tokens = voiceId.replaceAll("]","").replaceAll("\"","").split("[");
const tokens = voiceId.replaceAll("]", "").replaceAll("\"", "").split("[");
const model_id = tokens[0]
console.debug(DEBUG_PREFIX,"Preparing TTS request for",tokens)
console.debug(DEBUG_PREFIX, "Preparing TTS request for", tokens)
// First option
if (tokens.length > 1) {
@ -590,12 +728,12 @@ class CoquiTtsProvider {
// Dirty hack to say not implemented
async fetchTtsVoiceIds() {
return [{name:"Voice samples not implemented for coqui TTS yet, search for the model samples online", voice_id:"",lang:"",}]
return [{ name: "Voice samples not implemented for coqui TTS yet, search for the model samples online", voice_id: "", lang: "", }]
}
// Do nothing
previewTtsVoice(id) {
return
return
}
async fetchTtsFromHistory(history_item_id) {
@ -609,10 +747,34 @@ class CoquiTtsProvider {
async function moduleWorker() {
updateCharactersList();
if (!modules.includes('coqui-tts'))
return
// Initialized local model once
if (!coquiLocalModelsReceived) {
let result = await CoquiTtsProvider.getLocalModelList();
result = await result.json();
coquiLocalModels = result["models_list"];
$("#coqui_local_model_name").show();
$('#coqui_local_model_name')
.find('option')
.remove()
.end()
.append('<option value="none">Select model</option>')
.val('none');
for (const model_dataset of coquiLocalModels)
$("#coqui_local_model_name").append(new Option(model_dataset, model_dataset));
coquiLocalModelsReceived = true;
}
}
$(document).ready(function () {
const wrapper = new ModuleWorkerWrapper(moduleWorker);
setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL);
moduleWorker();
})
})

View File

@ -27,11 +27,11 @@
"glow-tts": {
"id": "tts_models/en/ljspeech/glow-tts"
},
"vits": {
"id": "tts_models/en/ljspeech/vits"
},
"speedy-speech": {
"id": "tts_models/en/ljspeech/speedy-speech"
},
"vits": {
"id": "tts_models/en/ljspeech/vits"
}
},
"vctk": {

View File

@ -0,0 +1,870 @@
{
"multilingual": {
"multi-dataset": {
"your_tts": {
"id": "tts_models/multilingual/multi-dataset/your_tts",
"languages": [
"en",
"fr-fr",
"pt-br"
],
"speakers": [
"female-en-5",
"female-en-5\n",
"female-pt-4\n",
"male-en-2",
"male-en-2\n",
"male-pt-3\n"
]
},
"bark": {
"id": "tts_models/multilingual/multi-dataset/bark"
}
}
},
"bg": {
"cv": {
"vits": {
"id": "tts_models/bg/cv/vits"
}
}
},
"cs": {
"cv": {
"vits": {
"id": "tts_models/cs/cv/vits"
}
}
},
"da": {
"cv": {
"vits": {
"id": "tts_models/da/cv/vits"
}
}
},
"et": {
"cv": {
"vits": {
"id": "tts_models/et/cv/vits"
}
}
},
"ga": {
"cv": {
"vits": {
"id": "tts_models/ga/cv/vits"
}
}
},
"en": {
"ek1": {
"tacotron2": {
"id": "tts_models/en/ek1/tacotron2"
}
},
"ljspeech": {
"tacotron2-DDC": {
"id": "tts_models/en/ljspeech/tacotron2-DDC"
},
"tacotron2-DDC_ph": {
"id": "tts_models/en/ljspeech/tacotron2-DDC_ph"
},
"glow-tts": {
"id": "tts_models/en/ljspeech/glow-tts"
},
"speedy-speech": {
"id": "tts_models/en/ljspeech/speedy-speech"
},
"tacotron2-DCA": {
"id": "tts_models/en/ljspeech/tacotron2-DCA"
},
"vits": {
"id": "tts_models/en/ljspeech/vits"
},
"vits--neon": {
"id": "tts_models/en/ljspeech/vits--neon"
},
"fast_pitch": {
"id": "tts_models/en/ljspeech/fast_pitch"
},
"overflow": {
"id": "tts_models/en/ljspeech/overflow"
},
"neural_hmm": {
"id": "tts_models/en/ljspeech/neural_hmm"
}
},
"vctk": {
"vits": {
"id": "tts_models/en/vctk/vits",
"speakers": [
"ED\n",
"p225",
"p226",
"p227",
"p228",
"p229",
"p230",
"p231",
"p232",
"p233",
"p234",
"p236",
"p237",
"p238",
"p239",
"p240",
"p241",
"p243",
"p244",
"p245",
"p246",
"p247",
"p248",
"p249",
"p250",
"p251",
"p252",
"p253",
"p254",
"p255",
"p256",
"p257",
"p258",
"p259",
"p260",
"p261",
"p262",
"p263",
"p264",
"p265",
"p266",
"p267",
"p268",
"p269",
"p270",
"p271",
"p272",
"p273",
"p274",
"p275",
"p276",
"p277",
"p278",
"p279",
"p280",
"p281",
"p282",
"p283",
"p284",
"p285",
"p286",
"p287",
"p288",
"p292",
"p293",
"p294",
"p295",
"p297",
"p298",
"p299",
"p300",
"p301",
"p302",
"p303",
"p304",
"p305",
"p306",
"p307",
"p308",
"p310",
"p311",
"p312",
"p313",
"p314",
"p316",
"p317",
"p318",
"p323",
"p326",
"p329",
"p330",
"p333",
"p334",
"p335",
"p336",
"p339",
"p340",
"p341",
"p343",
"p345",
"p347",
"p351",
"p360",
"p361",
"p362",
"p363",
"p364",
"p374",
"p376"
]
},
"fast_pitch": {
"id": "tts_models/en/vctk/fast_pitch",
"speakers": [
"VCTK_p225",
"VCTK_p226",
"VCTK_p227",
"VCTK_p228",
"VCTK_p229",
"VCTK_p230",
"VCTK_p231",
"VCTK_p232",
"VCTK_p233",
"VCTK_p234",
"VCTK_p236",
"VCTK_p237",
"VCTK_p238",
"VCTK_p239",
"VCTK_p240",
"VCTK_p241",
"VCTK_p243",
"VCTK_p244",
"VCTK_p245",
"VCTK_p246",
"VCTK_p247",
"VCTK_p248",
"VCTK_p249",
"VCTK_p250",
"VCTK_p251",
"VCTK_p252",
"VCTK_p253",
"VCTK_p254",
"VCTK_p255",
"VCTK_p256",
"VCTK_p257",
"VCTK_p258",
"VCTK_p259",
"VCTK_p260",
"VCTK_p261",
"VCTK_p262",
"VCTK_p263",
"VCTK_p264",
"VCTK_p265",
"VCTK_p266",
"VCTK_p267",
"VCTK_p268",
"VCTK_p269",
"VCTK_p270",
"VCTK_p271",
"VCTK_p272",
"VCTK_p273",
"VCTK_p274",
"VCTK_p275",
"VCTK_p276",
"VCTK_p277",
"VCTK_p278",
"VCTK_p279",
"VCTK_p280",
"VCTK_p281",
"VCTK_p282",
"VCTK_p283",
"VCTK_p284",
"VCTK_p285",
"VCTK_p286",
"VCTK_p287",
"VCTK_p288",
"VCTK_p292",
"VCTK_p293",
"VCTK_p294",
"VCTK_p295",
"VCTK_p297",
"VCTK_p298",
"VCTK_p299",
"VCTK_p300",
"VCTK_p301",
"VCTK_p302",
"VCTK_p303",
"VCTK_p304",
"VCTK_p305",
"VCTK_p306",
"VCTK_p307",
"VCTK_p308",
"VCTK_p310",
"VCTK_p311",
"VCTK_p312",
"VCTK_p313",
"VCTK_p314",
"VCTK_p316",
"VCTK_p317",
"VCTK_p318",
"VCTK_p323",
"VCTK_p326",
"VCTK_p329",
"VCTK_p330",
"VCTK_p333",
"VCTK_p334",
"VCTK_p335",
"VCTK_p336",
"VCTK_p339",
"VCTK_p340",
"VCTK_p341",
"VCTK_p343",
"VCTK_p345",
"VCTK_p347",
"VCTK_p351",
"VCTK_p360",
"VCTK_p361",
"VCTK_p362",
"VCTK_p363",
"VCTK_p364",
"VCTK_p374",
"VCTK_p376"
]
}
},
"sam": {
"tacotron-DDC": {
"id": "tts_models/en/sam/tacotron-DDC"
}
},
"blizzard2013": {
"capacitron-t2-c50": {
"id": "tts_models/en/blizzard2013/capacitron-t2-c50"
},
"capacitron-t2-c150_v2": {
"id": "tts_models/en/blizzard2013/capacitron-t2-c150_v2"
}
},
"multi-dataset": {
"tortoise-v2": {
"id": "tts_models/en/multi-dataset/tortoise-v2"
}
},
"jenny": {
"jenny": {
"id": "tts_models/en/jenny/jenny"
}
}
},
"es": {
"mai": {
"tacotron2-DDC": {
"id": "tts_models/es/mai/tacotron2-DDC"
}
},
"css10": {
"vits": {
"id": "tts_models/es/css10/vits"
}
}
},
"fr": {
"mai": {
"tacotron2-DDC": {
"id": "tts_models/fr/mai/tacotron2-DDC"
}
},
"css10": {
"vits": {
"id": "tts_models/fr/css10/vits"
}
}
},
"uk": {
"mai": {
"glow-tts": {
"id": "tts_models/uk/mai/glow-tts"
},
"vits": {
"id": "tts_models/uk/mai/vits"
}
}
},
"zh-CN": {
"baker": {
"tacotron2-DDC-GST": {
"id": "tts_models/zh-CN/baker/tacotron2-DDC-GST"
}
}
},
"nl": {
"mai": {
"tacotron2-DDC": {
"id": "tts_models/nl/mai/tacotron2-DDC"
}
},
"css10": {
"vits": {
"id": "tts_models/nl/css10/vits"
}
}
},
"de": {
"thorsten": {
"tacotron2-DCA": {
"id": "tts_models/de/thorsten/tacotron2-DCA"
},
"vits": {
"id": "tts_models/de/thorsten/vits"
},
"tacotron2-DDC": {
"id": "tts_models/de/thorsten/tacotron2-DDC"
}
},
"css10": {
"vits-neon": {
"id": "tts_models/de/css10/vits-neon"
}
}
},
"ja": {
"kokoro": {
"tacotron2-DDC": {
"id": "tts_models/ja/kokoro/tacotron2-DDC"
}
}
},
"tr": {
"common-voice": {
"glow-tts": {
"id": "tts_models/tr/common-voice/glow-tts"
}
}
},
"it": {
"mai_female": {
"glow-tts": {
"id": "tts_models/it/mai_female/glow-tts"
},
"vits": {
"id": "tts_models/it/mai_female/vits"
}
},
"mai_male": {
"glow-tts": {
"id": "tts_models/it/mai_male/glow-tts"
},
"vits": {
"id": "tts_models/it/mai_male/vits"
}
}
},
"ewe": {
"openbible": {
"vits": {
"id": "tts_models/ewe/openbible/vits"
}
}
},
"hau": {
"openbible": {
"vits": {
"id": "tts_models/hau/openbible/vits"
}
}
},
"lin": {
"openbible": {
"vits": {
"id": "tts_models/lin/openbible/vits"
}
}
},
"tw_akuapem": {
"openbible": {
"vits": {
"id": "tts_models/tw_akuapem/openbible/vits"
}
}
},
"tw_asante": {
"openbible": {
"vits": {
"id": "tts_models/tw_asante/openbible/vits"
}
}
},
"yor": {
"openbible": {
"vits": {
"id": "tts_models/yor/openbible/vits"
}
}
},
"hu": {
"css10": {
"vits": {
"id": "tts_models/hu/css10/vits"
}
}
},
"el": {
"cv": {
"vits": {
"id": "tts_models/el/cv/vits"
}
}
},
"fi": {
"css10": {
"vits": {
"id": "tts_models/fi/css10/vits"
}
}
},
"hr": {
"cv": {
"vits": {
"id": "tts_models/hr/cv/vits"
}
}
},
"lt": {
"cv": {
"vits": {
"id": "tts_models/lt/cv/vits"
}
}
},
"lv": {
"cv": {
"vits": {
"id": "tts_models/lv/cv/vits"
}
}
},
"mt": {
"cv": {
"vits": {
"id": "tts_models/mt/cv/vits"
}
}
},
"pl": {
"mai_female": {
"vits": {
"id": "tts_models/pl/mai_female/vits"
}
}
},
"pt": {
"cv": {
"vits": {
"id": "tts_models/pt/cv/vits"
}
}
},
"ro": {
"cv": {
"vits": {
"id": "tts_models/ro/cv/vits"
}
}
},
"sk": {
"cv": {
"vits": {
"id": "tts_models/sk/cv/vits"
}
}
},
"sl": {
"cv": {
"vits": {
"id": "tts_models/sl/cv/vits"
}
}
},
"sv": {
"cv": {
"vits": {
"id": "tts_models/sv/cv/vits"
}
}
},
"ca": {
"custom": {
"vits": {
"id": "tts_models/ca/custom/vits",
"speakers": [
"00236e350cc84b94a6684f182acf96e68963d7fa1164d4fa56da20f46f210b2dd3ecf189e97fb3c94113a54c12dc20550508f5b7b9b37e1873898d58a308feb5",
"00459",
"00762",
"00983a845f95493fb27125b114c635f3b40060efaee167d32d8a3dd040c877713446c7bd3e6944641227bdb4165ecb8d684ec2ef66c817e65e77c52cc50e62ed",
"01591",
"02452",
"02689",
"02992",
"02f7d61edf5063ca42953b1068539f1572985aa9448555cfd8d7667121eeedc72c912d95cf33abf61a1f9620f2a01be4251a53aa5440d15849003fb31210d830",
"03115",
"03386",
"03655",
"03944",
"04247",
"04484",
"04787",
"04910",
"05147",
"056d7638d714a7dc1efe1c47d390d0659fbfdfc7df5249e8bfe10ba346cc76d5cda93fc8ecbeadffd4924c4f9cfb6b32c1739c8af1e2d58d7cec88b2cf18795f",
"05739",
"06008",
"06042",
"06279",
"06311",
"06582",
"06705",
"06942",
"06c6d2e093624103c268e2cba37466147fd564bff1312a78d1c5be9ba168af4cf4819c7a91d5321d7aa9bd20ad6c702ca2cb005496dd20c45d293200b2b8a7b9",
"07140",
"07245",
"07803",
"08001",
"08106",
"085503e68b0772f1b3aa4de86a57bb26e3750660e7929a14a653c729787a110cc8b3704f8ea09842f72be46b6ffbb35bdb3732308b31dceefc3b33e5ad3f7975",
"08664",
"08935",
"08967",
"09204",
"09598",
"09901",
"0befb1084ad00d656f45a87ad83f074c61e3b3767cf6f5463fd5bc199ab7fd4733c5f02e3a100359e953977cc2a2689bd4824ef6e3178a7108cb45a0204fb3cb",
"0c6bf67821762116d753c9b48ebed8a2ccfa0a956d5dbf19feb0ac0bc2096154ca288ae7c5e324a3092db395cc24c64c6a4e4fb0e01429b7343cafc7ac1b2e13",
"0d0a943d348b4f0948da443c4d020b2e690731955ce8c318c0fb72663cfec3cd3458488ff9ff9cee6d221c85771b8eb83cc087dda37d4109bbb1614039e5f565",
"0da83aed14276e120e2581be32891bb088a22c272feb6f03b4bac1b827cccdbc8fee277a885f58e98931819e0d6171526c5fa7b2e788f68a2852e4d5314f613b",
"0ff19536d6147f61b24d50c0c993a7a687df4d253c2052e4fa30b1624c87e60075649d888f51ce71318fb8789cb378879091aba020256d66ac19f024833c3e63",
"125d9d1721de26a9b89d0e4f4d386e07458d287ebb2f338879e8886847abe6b3209f79e2bc335bcfc437350184df5a7d9e1a08ffb5239674edfd1cf95a9d1e24",
"1378866a4d2b6965c03eed8e48e03fffd089638acdf1fa82ed20a9856406e083f0c0e1f5043c4d3bf67dbc383f7cd28b602eff1d8bd8bf8c1a0191dc98540322",
"14bc32c10eb26503a4e799c3a762bfe5949d7a232074e854ecfad8139acaa4257c563a502e06a263f2d5fa8337114a9741d4a634a0f914adae74e5f9a80f145f",
"151fcb1168f41a51c49e20b426605109e1d9dd50be2926aa9878040fe325eba2f2c470357a735e9e24b7412e78e2550a8fbd0ea77fed80e4d8f50a21f2064948",
"1610e29603954ee12f408eaeb83a5c99781b5efe5f64fdf2e3c5e0ba9756b5b11bfeefda40a787842f7d5da653135043e3f43e7f786499cb51ad6181ef8acc9d",
"1887c37f4187a4c3213ba4b58d0ef15f903a3720ee94d5ce59c33db193f60db416a4d9607f213ae44ca3eafc7217fb9871e2ee1837ad4cc0f34794e5c543f9f5",
"1add23d44d2d913f0ea6e061fe292b0563653fa15b4ecc9cce0c2b83dc5a743d6d3dd4625efa112f751852c348a667a9456ef3486ae7e8c5954dedc69f998ea2",
"1b7fc0c4e437188bdf1b03ed21d45b780b525fd0dc3900b9759d0755e34bc25e31d64e69c5bd547ed0eda67d104fc0d658b8ec78277810830167c53ef8ced24b",
"1b8354b1fe9255578225b3d2255d5e781eed7d13ab61e84bc08be5d6465ec468c533563137ca756fcb7d3759af0ac2c0b4d00873782c7bf47ea72fd9be2f9e8c",
"1be6c773da6334cc73e23312689bc8a5915529c905e1d5289dbfe00332a7dcb9ae97efe209635e2e5040783777409155926d231a5a5f76357494671512d2b1a4",
"1c7af1cc1357fd63bd9ffe915745e20c34588438e1e0d85fdc8c9de4b3bd41d3d61b318b6a69862c6d64dd41f15ef3d994a6bb6c9a9dac69c891308b09ab16a5",
"1c7f19a7fa0b166c700bac583b6858ce7adbe19566d66e530953273aae59776757aebaeb30c20a58d74bc50ce1345516af5bbd36168f443fed809bf54c02f63c",
"1c80e9d982aa0c12db8498e8275b2281e638e8e5c684a752e19f8f9842979b70a3624785d0c30e0e57112950cae5e892bf554c295c74cf8c82c8ec33c732d8c6",
"2256cc5ee6c617347af9a1abd97dfe80f55e6691eb0a042321d46a1bd7ce0baf1c7a4c8ea3fe4184f8638b1c3d9e83b6aa193bd6f9b49d5358523f1fe324cd92",
"238532dddf77923ce93cf2e9ed809d088094106a1aad327e8a7b229ce24a339771e59478f7d5162efc1da6f347b44cbb2273ac9154aa3a76c7a8fc458470cc2b",
"241ca4fdf2124f550657446301fb8dfc8bdef46d3888ce39bf9d8622c2bbec7e06b198f5e33fadbf30e477fdb37435cea36d10341af1a7d3a80d0ad1caa94bf9",
"2421aa51a089ecfe45250bf284d5690a9994a9eb03f2ba4f43d2ad73fe78783ae5f3d3088d772e01fd1d747b2ecd6bb1bfae5feb10a72130d3952ba7304d5c53",
"24d967d0e8b84beb3652417724be81ab83c7834afaa7b7d3d7d9591b1a2a7bb75f9b25be548a200570ebd6cc34e91306b675af510ef91cd34a77060b65b9faaa",
"25911630ab15956e81427d3e990cf37f79490e305914a15ca7dd7b95dd4d4feb15fd94549cc005376801ce68d637eab6e19ee36017dba5c01bd0f206e5e8dc3b",
"26099adbc4db8fcf000e2c7d1da3399662281f9af03831808d29c602431af4fc13f21b38c5c42c5ac4f77ece48448eef99f735d92cdaed857d19da7dd2b888ad",
"28e2fe1944a593c44c3de0dc52971f040f0b8901fced2057025bdbafa1fe3b042be19618044ae085d7364e3cb38601e9ce4030329f15af7a0898f9d4c2c5014f",
"2b59e9f830e5ea00c500b63eff4e72553e0c2608f5741d35c226e733400412014d7697bd6efb67bc61b19fd61e40f9ed70fde2589fe0b5498915eebc1c8b5d93",
"2bc2a177bf56dcc98e05501e7bc6eebd3d1662114764299a4f9e6b060a48095b8ec95d20a5814f71343d65ee3cd2e7f42a80faa51a148005242cc5073e605ba4",
"2ce84c6ea6aae52c449b6d34cbc095b2f2c3e6fa20d0e48b2f7d223724ae01375e92a8ad106b029f0562ee735de36d9bca6cd167257c3f68796bd8b1a0ab600f",
"2d84f39c2cca33dd28fee650caa022c9a06407462342fae8dc256af7904cdd114af5b4cc883181407b8dcf2dc4a93d45c62c83a317d84e876cf710a521f20d80",
"2e6ccdf9f0a7bf0df6fd0572bbb53f25378fc5333b352bc885b3a0d01e5dd672156cf697c127cc998ac546d122c547c421970a6f23dccb60bf2c841146fa6576",
"2f92b4704080216aa80b1b39cfa223feeb9ed7c909f5b77be1b6e45fdc8827463bc4c4cd98263f02b57e653ecc2ccc7192aedd92990113433077e7ed44eb1e0a",
"2fb95c3b786fa65215534207266e034b294317b2327ee0928be3436258e42db8f4479e86e6006979ec4438dbaa9daa05be21ddc66717d30dc43e36ead349965e",
"30b1f81c579755895581259d79a8a5a3ca45b908b0bd14ad1c6418f39aa1e2f47cb4749c69b5440cdb92e3bafb772e19e7bc2b16d196b061addd173a1309e491",
"31535cb2ece4710d08fdbeefb6f8f75ed093fee4cf8573bd601d960f8c6156f0fd0a85712761691e86e31160b993ee0eacb10c4c8aed000cc394cf7c7d207a7e",
"31e6f3a011661320b2e59b6f8be43f6db2243e9feabc2b9787c1413788e13eb0e5810bed983bf7ff66e46417d183a91ed50b3b9be9d89e4f51aada72293b9881",
"32550810ba55b9a67a25d308f0ede521f12cbf6076472ff5bd60a8f5e951c481b784e2f04194fb96116c4f001d84b3993b2c580879671de46333d5f212ff2ca5",
"336f82b4645b80c99137018e69bb6f8138a9c8dc05a510e36922503120648625674e1414cd90d0cf46f28cbd5993ae0eaedc9994b72e8eb5242737ddefc0bfb2",
"35b962b08846ed7d8a4cc47582a4e607f5ff4136042ce0b1adb55d1e8d58e2dc1abc5807b3601a7f7be1ada5939e1771e128fc916c1b5d39ed3619e251707952",
"3637902e0d19f0080313c14d2c9dcde800ec6b71d493459c2f3b2cebf186f028ea289dd59ba1fd4705e53891216f7f4c36dbcb8938aeeaf142317b441b20a837",
"3723bd65a05afc7411c2bfca904742062b7b0c081ba126e68c65d28eaa6122f69196f4959fc1795fe03f8e49ec7364863911f9b659684a06b3a126c6f1729551",
"373d86f9fa3a127372dd913b7571ed318bfea42173b2b7daebde93c742f3224fb7fe5306085e836d20bfee4201bfe070b4c6b36510f5c9f379f6a3b610f36cd0",
"379d321bff71ebcd34792e8f4552d341f30a006b4765f8c6de4fa98d3ca416def88adbcb0253f5849f51793b3d7ca7e53700ec70b5a97e84ccd1f35a2a1fb6e5",
"37c12c700c95dc0028b3b82c4cf1fb922d68680c35fe84585bf22674e71c4dc53bd9233ce8b71bd31e9c9b0e000d01d195a3572b9055a73fbec891b1ade250cf",
"3a4a32c7cff18f1896e7cbff2c19b4e6f91a95c1e7aab616722600ceb36a86b07fb0e1e0c70cc285dfb6192b53cb67826698b7f3f652549e27a969bde0177fa7",
"404ecea5ae8e5f4ec3d2c48494cf7f1d559268542d8f1f7928da2fcde55c9fa3f491ba632f555ec69e8c9e819072df450add7e5886cf5527f446b11544af7d05",
"41e5e21b3a3b0c8df01ba5b3c3e6224cc4082f41ca87679344b0273e2216cc272e19426c160f5a9580915c057a3e4000788be6cc7a6f5f346cad5068c7884ce7",
"464d9ac63f7958200bc09a141171355bf4f3631d66dc4bbfabd497619a8f055c034c0752987944b2102e02d4b435bcd3ce0527962871112049e1d26865b776e9",
"4869d94d4936ab700c5e5bc7b666177b53220082f5f221774b5625d7275cd4f117482dcd1498674b7f885fa41d86f99b8d00b6a6f641829780946651f561fc22",
"496b66c9cb705a46cdfef9eeaf29c9d738a4b70b601270985a7df5a06f9e1d6c56be0982995c8cc06902d0ee89bae201c37a91f568331ffe28ad2d150e183fed",
"49a7654071536ed5882b8b6e6d2e3558ef796ecd8aab8ceaa24ad8bc9f3420b528ef1413696584c11facd6d5bccd37fe8e274b8c3d139dc251ffb11c3a503aaa",
"4b6c7e4e9bde35c471cbf5e2e93b2eb8bbba52b710acecf99910af08b3b35365f24d883ddfdd9825918c31477a5f3fc48f075080c4e97e80fecd6e1936bc92eb",
"4bce212aca40bd1834bf741e47954526a8817ecbff8fedda854dbfc2d033a2567bd34b84fa02c3d07855f3dcf413590ae75ad6edf261d66bffb84d77803a7b76",
"4cedaa8d96436fd0d2ebdb61d616790a3cb3737d0a93d2ae41d588137c0d3339999d991b7b3c452704be1f5f512ce5a08c0971898fad0ad77f18fd623411cd7f",
"4d7e2548403c7e04d809030aa25015c9706e773517e1f72b81bdda22213aeb8f542cc62156bc5ef1c1622e99227fedbcc9c1b3e5e147b854e3b629f8f78bd158",
"4de9f262eee7ee7d24ef8933af4610a1c5b97ff055c4fd0f97868e338a017308d460f4b003b74bd2aba7789153593f3b986b814fd93f2e4dfa5b55594fb17c55",
"4e5e58a6ec7d9cac969f99b817f981ab7f8d2cbd9ab9dd0a37e45c70a8a8ca3b8e1c43b2013082062ffc1f4f3b268ea78ebb88d613d026a6312f40a6867a1d0b",
"4ec8f1e81d7abd9d2dcb3dbd4be86b615f643386f3b1098c37a02a103fe6b36239c05bff6746ce568ca81765b285c1c271af4fb1fd99120341cae2851b776bbb",
"4f57d1abde3364d91128e682ba724e6d3bc2ed6b112d2cd679739e478ea6bd671c527edff64c6a7b5c1173f68e02a410f09c2256356fde7d517908310c118382",
"503dbbe83f0154e9bba4bc685bf1c1fbdd27293d0e4f837947910e4d320bc4d5bed1ade67a45b541013189a2c133f6f9f6cbc3566fad220c0635f286feec74c5",
"51795e8ea8faa28e88f02559f6bdd47d9a0735589d47dd0f2e057b8b01fd3667fd9fd29e2613f200174af1d4b2d3d0860704cebebf2b6e79f1724d6782d7a270",
"52cfac480c0cbc60068305d983adbf98814d2cfddb8be0ccfeb7c7f95bdaf31a5f70da944cc2453e6a5fbb9bb4092e36b662e838762855fb016f55e6e3d957c1",
"537e815df93312978a9ba479ff2dffc9975c875950a203e8a1b7ffe4cb06625964f59dde1a06b87921a2a91702cc6bab04e159aec7cb2e2fc576cbe25838df2b",
"547dd49c2cbe113b60c9df4a8e8b83a532f0da054cea8f1d23db66cc2638f7b5edfee820b4764646be10dbcd05caa5d71483477718a73ce8dfc752204807d9e5",
"54f344faa37da0c9ab1ab563735c532ab81fcd1c8431cd8eca4ab7a8774f194e1ffe922547ea42bd1fad36e7493761992eca4821138ded1a9580e9fa38685291",
"56071bfe30e977f201fa4d6808f8d7c2f3e6788ba68d12e2eb18386ac2507bb2bbe3c14bab90370066bbf6e2af42afcf1e45b362dba958d38fbc69cdea3874ea",
"57e5f7cc5fac058f7c772eb41f8d49bd0fe3070c41eef445b1c073abf9b1cec451aa22764490b7da4c5bcacf4ee453c3153158cb1569f2f9447807cb14dc1126",
"5a9a6481f1365def2919871790a95fdccbae145640f3b4b5e11d1d1370ed35c5a4c31e402b3b438892a6bfd9dfbbe2fc97056d2cc24f2ac412b3a5e1adb7003a",
"5ba168675a3f2ea8d6d51896c5db84ee59ca65359b1b97e6d79543a6c918fe427f8b6cec79037c452eb086debe1d57049c25481d61a873f0503703266bf0cb84",
"5da56ed896575439b7bbca20981f0b50618958d94f08b8f47d13774dca3990d4c571be1f4aa2786bf8fcbd1a594336be49cc26d972d5fb0c0682ab4d5b59d19e",
"5ebf04dfec6c9b10a6fe7fd03725901973565a13530c20d02b4332670cce9beab185dd0b0f61f4a87f9c3dacc307d06e062d640f6550d9443a4c06a114e5bca5",
"620b0d4c3be90f5f77f0cf9f976e5d7f067689884dc857f2b26a6edb40ef4fd2826213b5028900b168e853d036f1741600b236e04d8dcae5fa26cd2b8975ac04",
"6323ec0401b28c1b06afa76760b478535101ae48c6c9367491087143287d9ff76b9c00f39dc838cdb20d65eab16622dc85143f5845791bf85705cb4f20975bbe",
"633e7303eae41420e558e186308510783f5c234e9c639c0e6f5b6d37fca6bab766c5d475b2f330910bac93cef6982124e73a1b6bab1a2e99a2e5d797f8547c6d",
"6688b60c24d068e19487c0b88a8b0a256854d8090ebebfa9a462fe49a77b8e9f303aa02042069cb0d6f227932cb48863758d0b57a18d53125ad39953bac543aa",
"6745c47d0bd557c3dbce201697e8a2fbaff9ca52744d6007a636a237b82d1167795a0c0e2e5eb71b7460ed16e3fdcdac1dba1b7a2910d5168416e236c93ccb76",
"6892c6ba9f66d0d7aa0445139081dc82a76d9ef8c7bb049a8eaf090f76c06cb4f1db05739038d7e04167569bb6d0fea55fc15343f7c77cd5a3e2d4c5ed068290",
"689a213fd2d66b9d3634c9165b316e49ec53ac96131be42226d462ef1bc3ba38651e94698fd6e6f5c6d6c834d2b9a6732be54a8d6273c1025511d795326ffd3e",
"696e8808717101399ab7ff16382db411adfadcd60c6a525539b0f8f88d84b448662fbca212b175379ff78ce7b2e64aa4b4e96d1820ade8eb2f742295f744db7c",
"6bdec6b6f7e6b5a187feb6537101d90cca1043e34d53e347f2f0b14e701585361fc4a4cd81577b6d4588844fc8bdba8af66155d9eb6c2eefd461e23d0b2b87e4",
"6e5948f904b3048511677d23d3cc9bd678739b234170302e1556c1bd1db8cee4243bf5e012a1320b4c50f6276e05cc5f620c461f640ac7413c23524f63f4aac3",
"7115c00371f891d0094a716083b978948431509a16d5a9598e78ec12712db46d46f1674312cd31339e2d6118cca5f7a3f82ec25dce861a059ee31d832cd6dcda",
"71b67ba5ec75978632136441a25426dbd48d4c0a55c1a5fc91f0f952b6bac06ab0d9709f0a7bb5a05393499135b76e4d722c7065fb636a227ba58c7fb86438eb",
"72a3d5bde83f60653937232cf4d29218ff5988533855fdbc804d9bea7e94eb14a8afbe36a8f8ee576a3ed2345632d4ad36df52efdd9adbdb60da6f890074c6b6",
"73d3685f3e78183724e3362f6c4288d522b54a8d2722197dc5ff5006974c1529dc562c1cbb05f023da4922cfc04340eb83b887c5343041febed1ddc44b22f9ed",
"74a679bf6c4a1b5856a25780496812416383f0567afcbd9b411ae9a0abab47d466741bd925b03decd7da586c6ea9589c8f40208ac2a22fa4413d4ea6e1a6f0dd",
"7638395f7d47fbf631633e2b899044e82e7ce0e07305114921cb0696551966b09993766782aaad70fa40a0f7362be31940381653c659fb73d3e1fd1fa45c257d",
"76383f56d9979837d4b3348f9f28877dd1ba58c1bd0ea839bdabf021428c2edfba46ff25558004c5183a73575eb126d4e0746a40e22ab15154d5d6f238a48ca5",
"77cd12af0a3d1d8cb64dd577bd2d50ac057d816694e8bc04089a6adb90e53ad6cbe9fe6aaf52596450e0c8178d8f9b88a545b27adcbc89bddf4d7c4bc4dd31fd",
"7834da277192e9434b0c039272ee6b3f1b225d2f975aa175fee762fb0d5f16b1edba2e0dbf11d8aa2bab5984482f703f88f0e9d1786a7687710ea2688f307ccd",
"79a830901c1bb0e27663dbe14d13df91d887daf0eabb6d3eee7f09768212afcd9cdde458d13042a9d2aa099f390c79ab94f2c1ca47fe0321f6c18973e437cdd6",
"7b7593f44cc6f9f7b21495bca6f3d564f73f36b97ee15d51a783da8141463834022996c55e494800d21304079aefa8a5fe64350c9273e0d36453b097b2dcc5f4",
"7c7d917d97412c24b76af336086469a43013d1d6b27298aa82c4e99b3f3b6c5a82014428a6a14b080a834382d9b0f178e405fb10170bcd340957955087698e19",
"7d19dccf48114d3ec00c45fe80581300faca042157d6c9458ec439c300d8c7b1190aa70eecf19f8b1d5af8c7291f3da08fc635a7fd6acc7c5b203d1e226589f5",
"7d8d6fa22ff724d823b82499686732b7fbf32f7c1f35dd5733ec3b65fa9625cf2d49bba86e6e0132252bea64074f35ff96a77bfa44441aed3fc1765b13cbc526",
"7e36be2204fe367a3798e1b2ff988779890591e5c997b1f6025ec8ee1fef3eb19e81b74bb8657874f5a990d5062d6c849621ce363c4a9c2c5a63c0966be6140c",
"7ff908cc2a18ec5a80e74fb4a2f12b406f0b7456ac797d35091d618c7ee991baa88edf62200817aa27732b03d9109cbdc6603092822b2e13a575953045b1cd0a",
"8154716e77acd0f5e912887facffc7b2c9889891e863a39fcfed1e5637e47328a4a3bf40bbac1e740629d3013304ada88cf24dbf3735a7aa2d4b855f813c8fd1",
"8162d651b6211f06f655a69cd7fdd383d6b4287e9ba132b9898ef9ac8687349e777626333d23bed93f9264aae965efb14ed650cb64fd0ad90494aff903eaef11",
"8348c81a253096a9def0b472a8499fc03ef8c6c6d3cc9b4a018f142501ebd04c2479008b88895e033eb83978e7d71e52a91a2e324ca869ed8f2724dfdcef269e",
"84b101db8d076398c1d624a8b38b22fbddbfa8fcc43ade44619f5a9b6e70daf1c963d6dc09ea039cca94ec56ccfd04a1689e806c970c0bd32cc9e56b73c7bd7b",
"853fb95e0f017c203e08312e3ccf45c0419928e08313b1bb0444aa4ff089550546e67fdd3434a22cae3f67603437051e49be5c4d8fc5583b1aa6a1ae36f0a911",
"85c9e13ccfc0d67de10281b04257d8ac0c256d2f9415e54148fb59954c0d43f66d3cbea43ea6389f8407a8bda8b1b1becd30e41dfbb3dd9bebbe69816d096fa9",
"85ea0b349a8df04283c62efb571d2947e7264b566883e300501086733b08efa42ced215bc47951c8198626f86ca8c0df730cdc35f4d99ffff958599884b68e51",
"88673d4f24d039e89c15d9ede6b653e41e42ca8bd7a8cb7e92a4f235e9b56cbad6200f8dc313c644e9a7d8d1dbc2b7e988da93bc0765499701bca6bc86d8fe3d",
"88ec4ff5a1b0ffdabfe62d068286c851ee64c428883e56f32af14b59756d5846be9d46e5a777c4c22f2dec9596a1a44ed3aa75f1fb0231923cbd4ab59f1f9c47",
"892bf89bd3a008a7d982de0d278349e654c713efacf965e88e46a12398375cd8502711378e378c39c33b2f995f47799760c6e6e05948b93c0d2b9fd427854ffc",
"894bd433b4b06514195a604961c871649e108d210a41d5cbebe76f78cb6270b7708d1c59d6cb88807f882bb154be1c9058a753e2b6a95c3f4ac9e27a02036f12",
"896256329fbeb5b8116349c31d8a39a7d36d5f970d48558e1db5417d611e240e4dbf473f6e49137f7aa6116394b7deabb0bbec4a014896cdc9484ee91458117d",
"897c3401b4a35d8fad5966bf8c4dce6d94837c76e46e8131a8bd70527f6e1d8c9f59053d0a56425d7dee71939280ac3c38df14e976f613cb906d7187d6141297",
"89e6f6a865ab743936a9b29d53b67bf4b68660ccbe834d4a11fa9011edb535e3b7b4d7a238c84971d4cb5f06ef7398bfecc4f2b786200fee67d7307f242da565",
"8b707d4f8f32c80709d880fb257873915033c7d5bce9589a80ca9437618262c55dbe8eec2e8c82469bc335a84a8f16f89afcc53b8329dced5407a513927efc4c",
"8e98d00c5d110856943461cd85305b0a817abb457c2afc8e89edb32e502d0060081c8e667d9fceb63a2f8efbcb6e193e9b0231afcc05ecb2303d7f742f304396",
"90bb7c91281bb6625a0700c1ee2f3cee488cb9c1864ccf2e24699c5d957b1b7b686574d11acb37572fdf18a15f272fd44009b6cfce9b6cdf9025dd5002869d30",
"911c26cf828319df5123a9cf38641704961a6b894aa6ee2b0d13409996a93d89f4868b91e0eb1efea907a70a14cf3a3bd8935033aeb03bd8555f2dea857a48bd",
"92862e616dce7469bafc507ab8fbb47bb6f5ca8b96b05e9fbf39a259d1d4c4cac97b0472f713db2e5fbff0d3e587e7b34bedff80cc2a70c446becf9b488370d9",
"92a15e2cbd0c89fbce36b05e3b282255097bb5492fc11f0d2b0a08c4311621a41ec35df201de51523b62189a3b44bb3eb1cbdf64e80f4a543d0d9f9a99f9bd3f",
"97679def7032179662646816abc12f74fc693fb02c43675a2d5407e58be6dacd1eb483d1bf46f66c5103de3a649211c29e1127dca473e13b02dcd5e7df719cc0",
"97e29f9edfe712b059203de5af236569e2c41fae8cddfc7b486204d6e30c411ec605c757fa5a1a151646092bd5d71de18a5f2d8b6fb74b9a28a7c7226a4f641b",
"9b5f9ebc961424b8a6b7def59a86ad6fa6e45fc9ad5fb251c15d4d09202e6d3f63bb37b80faa4fdfe3997182079988d78556a9ebf7db535951a1e3cba0c0f6c9",
"9b847b5006ea1b47dc0ec366d09aec4a67aec747c55af554c094994fe8c8625b09cfd5322958c816bea74f725abb3d1403f2e9336007db3b257949401b1fef03",
"9cdf4ab91c8ef6148dfd724f2a2c644cc00df44f5eea5035e760ac59ec79078ffaf3d97a9c5a9747c04895a3dc666339f82cd17e40095b9fd055df3ff07da6d6",
"9fb127fbe4659174b52ef61778a705cc5a96c8f136445bd28c10ac79398ab9ea291852b627e285e828fe37aa23d05b13cf202f3f0cb4c272aba94dd1806802c9",
"9fe6ba948da2f4e4aa0e1b0d3e1aec1f093335f8097d7dd3d6b5217cd539f5c41735ef7a615d8210f2e6b777b7198f151264ade172be7dbbf5d442bf91843e8f",
"a1afb2eae49546bf59e6f9a1968287add54dd6e336ec795037090a435f736b6d8ba2076e05e27034979a8caaeafce05fc6d9d5541f4e5a4321e64106dabd1549",
"a2b06b54679145e65ce10a8356285efcadbacd41be817d2e0858ddba59e638775b79f76cb9e4ac5859627b67ebf227c55b51cc48e6d0d7ef41c9845d96ded68a",
"a2b503bc78bd0b68fcdc3e3b68e3c68cf3da8d2d48d91f09313c7cdc11b43dd4d4de3a8a2c4b526809adf9879427c4818db72cffdbc2f0015a9fa5ade83bd400",
"a359c15185b6d2a402dacfb7b3dc2e3ce5fd80a1add892b2dcf8e23bebe57f16680eebf7a851c3a870d3ba9932c4e42bad937c4676931d849c62f021ba812860",
"a35dea43a67cbd18b705cf2b28114652686eb409c1ae1e56c04256fd902ba9ab52c7343bb8b162522bf3442da42431246644432c70f819ba8617a723abcce836",
"a4b1eb406ff2c349437a5634148365fd0eecad5a264036e3af171d0f6769a7129590a0a3e09592038baa8bc1292af2bbdbfb74e3b1a685844e263532a87baef6",
"a4b8fa949865e0aa45147a27f0a034a26e34745d624dcf0603dd25fd1ce279eaf2d073a853d67e6432447d5e06708d71a9cddac0d2918876d2d3498af3ae0892",
"a6bc3c6beffd4335228c3b4857365215f0c4bc5197a5b0eca95334af33dba19ebf8d513f6c75359d7cb678b051d96579d73ebdaa5b6906e3b6eab35005bee13e",
"aabfdbdc21150ac70f9bb1a34f4d7de570a72ba7e1afd8c08d64c85e00c12e6ca1f2ffe60dbd16a871987bd7aa47182baf57e7f68daaf0bac7fc3b907c8ef4c0",
"af506d21ee140905c125e61c19d04599354fe84fe211502c9c766951387f6ff79e80db0658392af173f37ef7c92d7815ac9214e8ba4c6ade3e7a7ef014e5cc08",
"b04a1d5062f2921f39074e4f5c00675269195834a0a9c0bcce10b1427bf8a6499bdd7d8c6717f220aa4ec9f590bb04b290673018528a60dd819ce9798b0a33b4",
"b0a3c5148905a3e7e18c773684026e4ccd8811c3c62f6fcfc23135686a8db9c2caa6de7b14775e29b7cdeb360ae25ea626381c7689ade892c3fb72f82e2daa89",
"b1a0cbb91459433ff6de32b189783a734c2ada4c04d7dd164de449ce79c749d382aff10aa9ed7b4449af3390da51585123ef88719ecf7cfea9c24223023a23dd",
"b47a96b489f4dd851c364dee278699905f1ed933ba3a98a6660160463a8decef830bb91ac0a1b4f9b742df2dfbdc9625ec27133a69f6cf3cb81ed298183764e7",
"b52e493e5049e86223385546f3407f5924fd75311a0a11af38423b7bb7c02c3f085fd1d9188515c7b43c59fbf168c23126456dba98dc9c0d29b7a3edee159015",
"b5419f6ea89dc32431a7671df1ebf934647bba5b27db54235fb1e47d691b70c3160bf8019653d5faad616b169adfea5d8e7077e9820d9294144354133d45ee16",
"b570d19edbda421e0975056b5fdf4cefbc3825b840aacaa337567ec1aa151a81633eb645a86c8c1c22b23e7f916c60c20cb115de29b670511fb9413611e8cc3b",
"baff09432cffceac6ecd395a8ed5c947fdafe6c30c1c0f3b83c4ddfaa2ca9d57b21876153ac2b82067d7d37bc6789e2f68558f1f26fbcd53fd6a500124f80655",
"bc0b544f1c13cc1d0fe15b0eab96e89e6d4dfc8919de1fb757ef97a7d5de9efff5e520def5a8471b75480fd49d410d222ed9332089bd527946c74070e8ad1934",
"bc3886ba087d3fd637a4fa85adf33170e23b369c0c6eca422ddb26c73c04ae467e2b95ed73bead19013001af65bf2cf0d686a6e702b458a77068184c8b17dfb5",
"bd609b6955a6a35a5580a6e19e173b02fa6d4ed880b6cba8fb5d2fd91309dc753326a824a47ee6148b3d6a01b9b49ce7c1122b1e30b6ea181bd257bbc38c2940",
"bet",
"bf64f21ff129fae4bf3ff795c39df0a4a6dc40ece1d71747a913dd84af2e4cac4e1b84213e23cb1397b3299f26b1b6302a3cdbd41da8baea2505febd6e1803ce",
"bfe8d96ce71f9cce7bd16b5282041c66773405f1a11f4f0c8d3b6e81646f262bdac0cb3ee8f54e13175ba9ed7da38407e8a9aeff20972271f0c62c0b19f8b644",
"c088e98f02d33581ac0d79c37a101e4273e0750a5691cffd96a09c38742617dae948cbc4affbff4ece1d611e44ea5539f0597eef33ef39f7f0e3ec2a5edf75eb",
"c1bafe50eb70a1b65188fac549c6bbe7f641b672fbe9fd08cb64ed1f176efbedeca88f5c295d508e2dbf9b495fe0040bbbfbc4776af0d6cad6576a997db3e4cc",
"c1e166044d7731207ce8b838011eae84814857a8ddb63b8a393d2497bdcd7e96d045aa229a7978533646cf9f9ea99a619943599d47a1558073690601fb486ad5",
"c21ee36416076c1929dd93af7e936e371d4fe263662a2deb8fd6b0e5cd5b8cd86437b4afb2faa8813bd7b8689c7f56a63729a1e666684d8303f469faad669e54",
"c3f1018eb1f7b5e5c0210deab309d06d3e8e9e15ec7dd41d2dbcf863c39e36955b2034fe44af5a4983285b8fc6c0d92b092f95383f8989c1d75a40a4bcdd3d83",
"c4d740361d5f6bdcf408abc029d8adceb35f06c332c46fc290d187d96562992a8d6caa562eaa21643c346d44c9e706cd991ba986e53cfe37b41a0e048d14d6e0",
"c5d4c712e06053bc35bc6cef173daaaae7fd47db5ac812b95a2f0f08374432ffeaa2b49a0f10cb60f38405d2459489df0e43fb73b48bdb6caadcb4405915c33e",
"c777d3358a0aff067b64f254ac462fa223a1650af20ce2af341de610eebbb55a128a1dc43c91da7a1844848b5920b7dd5c5e0a1e8651d6442a2418709dad8c87",
"c96c4e97012d25add2fe69513a5b1f941fc36c837737780c443203c72182b808a129982ebd64aaffb8eda4ba3c8787fd98ca55fd33f060f63917567446417574",
"c9774fae6c0a30b456a21005abf026799f370a12fbcbc098e81bac2456955320ec6e712f1d6f9d59a50d615f81c6284785292180364598987a7990ae83c0f0c9",
"cb557116fa7b3b6da35024b539795d9e255c111c06edbf0e77ba728dd352353182c96918c649fb9327bbb4fea1bb25affcade9b5069676b191611062941356e7",
"cc3b30ba0f733abfe64667838f620c4f542db4665fa68e4d945b75ac0d2c435e6529e6541c4ac8ca18dec753b10e3a5c4614cfbc658dc951ab6cab357e6ef363",
"ccd85fb40538f948396a4c2bf381ea591927a7cde9330ecab883cad5bd59db56f0c983362f9d0a8e88a67d3f2bae2182bc8ea94b4e3adc721c782ca5c801e2af",
"cd1226e73c8275de15f2edb3744a413277fc76a4ebb7842fb743215c14b405b96c4e64bc8324feafe58937da218a1b0aeb9451d5781672ced1ad68c31eb54ba1",
"cdc5df38351edbdf7afdb3aaf0b4f53253cedbf3f43d662548a432f86389505fd6f2f64f51f951355f4fcfc5718a98dd782e1472246556c87f0bbaacebb38cb8",
"ce31dc5dfa61834e3ab67925ff5f24baf04b4aee6e35cd8ffa524f87b2e2e094999f85c68cc7a1c0e9b19016d050c1755406d02f7116ef85afa355c65a9a5855",
"cefa12e7ac99a5d11df487ab6521837b11165246d1c3cdb2108770532cb1429c2dcba5262a4dbd9a37686bb76ad1c48ddecf473d807c2e552534b24bb78ee30d",
"cf5b890eb74b4ac647d011a989a92a413c23c0db580c87057fc5afba2d83dd861f2a8640fb952381d090328d6278dbe56713d516020ce95cfb6d4fecf63b89e7",
"cf8c583b1282449a97b72e317e56d5a4d1432e5420148a21ba8fd8bb2a172c7832379f30cd6582bd6674b548deb8517c8915c5c4b423bd3e73903f71b8862380",
"d0cd44fcdae652efb0dd428cd1b8f1911e6eb2ca3469a1f2d6f9faf97a9d05e30f28387dfb81bfb4c97eba64187a0c047c85bf06998ccaec58781f3982626bb6",
"d15bfc3278de168872744ebec8fc7a07678bd04b7557e89749eeedc7087fe0a36cb8b094e978e979d67feba46c4a2741f0fab18010796b5ff436836a5fc67e88",
"d3d64ab67746fcb7b4a37d6b6b80c9d4b11afd9e15d81a60b3fde53e4f99267a63b50cfb2184c7c84c9f0dd4345c0d929160a7df52698a82603c112e0bf8ab8e",
"d647b73602a3a0c1b06f282a612c29eefc6a7e372bc8af212a41f481843c23a975b41ca402f06ecb7dc660d4dd22a814f7659b48da7dfd28c02a319032394da1",
"d98d182c89b465adb0fdd1cc5c2bcb22b81fcc4eb941977b667de22927ccc9a7876033008118957d803c83afb95595986bcc076e77483dd55dca91ce253ba010",
"dafd89491990553f5e22021f96344b3bc92be6a419c919ba78860876f226e51e668dbabcb11cf9500f3bd05582b387907ea007b5e8f37c78fb71ac819b9bc20e",
"db6932752693a1b2e7ef9af4adbf6fc8a299f21965ff9ff52b141563a471600df9308a89562af7b664b7fe14da134b4f44beafcf910f8794652e16dc475796b5",
"db8eecd1ac9b20918e31f04331e46007f367c1f6365c9c4abb7af70eb1d2ea12174375fb95d1d11c46e03c81976de6d68f70693e1ea7f2096aecf06307a17d29",
"dbe9efadf636bdd82f3ac2b3710653421e7cefca01b74012824b73f7368469fc4dd7e788b047920d4b3b7e4a486c732872ca11a75a89d1323337191ac2bc899e",
"dca1aa77f919ef1000d91291ba68800340332c299e3c4c6bcabb41fd2305f36db353211d6ac691c37d16889e3c3ffc1efb7c621e8040cb77b7249e264af44768",
"dee065b956b99b10db4763759d64c41791af1a7e77f1864f90a2b0847a12633dcf9bc108db7eaf73cc8d0e750f5c37383a56cd77cc2276d3960104c6bebe6346",
"df52eb2c24a6c35b977a1d0fab336ab5c21cd84f78f685d5f0bea9ebaa7c078c0ca69717455e29f17bcd9282a1af9cbbe2d3e608c62cecf868419da081e2d810",
"dfc8721858bd56b846473eb6123420a2735fc69cd77a92a1d2c623c51eab3ac664d61a890d305c6fe77ec48f2759248744e9d56689f6c22317bbaa316c848fbc",
"e249989b0c397ac03583594a3911c9e9222ccce620921170bb39b8ab6fdaf136b164f3c9fcd8b4f750fc469c9cd69f144c2ca2dd918fcb778148fbf9751a869b",
"e364856fe22a5c80cc8d13ee445473a0eb7204bad6972fc4c116ea1551b50da43a01577ef0487f2afb7aaee4b4155d61b1ff2b83dc502363929de76af0226818",
"e37d85b60af58cc03e9b36e09dee5e8308368f44f91b28455e7f645a13fe29902e7f7d594ccb600e02caf4202a05d15477d4ea5191c7b97038ea06d73ce93c33",
"e41b679ec1446821bf0a80fa7003fb90ac66b79d09c00dccf702a1b254f9ea85a68b0643ecd81d999413d5814b06b9998afd9876062067f51a63747533921d08",
"e61565e75d632748413d51997cabb00613355f0a94cf6b2f929fdfa351490d2afc9bad72c7fa67595d9d9c7adc9454e8d1b05527991a17258424b14ec4e9a1d5",
"e6a64aa839b95caeb74d810677a33b747e23907213719dd9706af7364b4cacf204b09f9b26686a70cd6d416a6b590f87103cc683685529968ea0edd75107f649",
"e751d2f83310990aedc7392b54f827afac1873e9f8861e625814a8d1d15776160864742d557796d07a612479b2886287b417273cc9f7718889216c2ec3b3b7ed",
"e7847a5814b865bc043600fee7d810b9815da389278fdfdd412114ab8f87b1536f4b63f3f7c3d3eeb097486abc152043eefdae6fd12c2f8743dac1cb668ab136",
"e82ba384934ac4780595261c43eeceb3df29a047087870f5da13c7acae782b4b97857b98852ce235428b4bc24aa4ddcdcd7297acf683421201eff1c3fbcab84e",
"e9da05b6d590dcf94addabd168c543be41a2275ddf44f6f44db1e3698f0bf7dd67f2e93b66679e0a0d42a2f39f3bc6a389f0e6b362431d0cb197fe46f9dd6606",
"ea8456e0667e1cce6273cb333b7e6982f9aa0f260c7c103e04eb0076a73fe3497070b1a8f0c45b097dc3100a30254095a1c63e9514367655e9a378344ed25d1d",
"eb415e110eaff48bdbc03b5ab719f64593f222b4a1d872b552e4fc48d338e532d1954f76e94813e44a6cd030425b4076cd7b9bf388b870a31344545d092dfa1a",
"eb5078bcb64f9595d6d8589ad60502b2870f16942fbb4cbd2483c817c7fa460faeda90b82bcf531ac96be8c1d6825953ab85ab0bd46ea477615e71e50386ffe0",
"ed5c9e654bfb28e9d4131b3805597ee9fa14fe72c6e2a6d503ec2e47faf396bbfa15ce49e6fe83bd97da1d441138545d388a329ae888c1f1ea44fc62996d787f",
"edba91511ccf8ab01de2e2cef34c47d8430f8a2f4c62cd66c42ecb62da52d396e909aef7da067eedc58e1eb58a1fc3697939371e6a36c931af5987a50509854b",
"ee216d2d13cba1a951445b061771ab0c97eb3c250003e16008debd85fa0317a508f923db79c796dc29de18c83baad5b15651f80db1cf7aee854e6da28853b742",
"eli",
"eva",
"f1812dbb566edaa2ac92121641e5ae504d647bec835a02ed5d7c7f90424d0e8fe202846a599c2f74c49ec9b86181d3d6c50ac0688baa9b4c28608d592becdfdb",
"f26a63e5171e2935e13015fbb755f04bff87fb1767ac91aa3481b9fe13b54cc75f772b41dfdc634829dd9b44c7b08798ed114046ef981d454889c41d4f6408d9",
"f2f359ea473c07070fd1e50d2fcfe3dc4f624f01678c35920b079660b2d5b9c1743259ae6129992cd3b99ec2cdda94a45e8710888488b196c6cd9c853e86e454",
"f35ce011f75fc01d153a94339aad24ae4fd5f181af55916a5ca0153cd5220ed199b98459eb88e9f4f3a4f8fbcf5c272bafdca35ddaca0827c4b480f79e7db1d6",
"f4df4a067fec667827901fb55acb16acc4650f24eeaa588af1a103e5009e9166f753c7cd313d0d3dec79abb82a13c43fd2059db5ac0307b78369ca318001c4e7",
"f56a47b89ebd2d22f869e2260b55f70d7ae0d499fc3fd4dbcb0e6e507f12513f29c004b9426e428696df0d434e4ad467f143bc620a2f661a54608de9e2c265d5",
"f61bdd3abb2d03f07e33bfb0b9fba46069468cefd9eda04e77cdc5c2f13a417716d3e60ca91c39de1a480b72112ef0e6143e927fad45410ee252cfce9034f0b1",
"f62196a11f50362b35eb1ed830b03c18bb187e4d07014a3d1b238756fe836f254afa923184170512a0c6d990032b4b1edb25dd2b74f6fc15f6ef6b51b6f82dd4",
"f8e4bf2dd4f93dd473b055ebf2dfa6081703014fddca40a0efb6bd5dcb702244a30a2d3edcd6597ea4118c20258da575a0bc69a895356519d8400a5ad3b2bf58",
"f980d152d5c14c6e7557f13fe26305ed0105dbb23177d455372e5529a5d3333e203070e87352d985a136f5ce3976a16b97070a4343fb4cb9d0760d9bcd5c7677",
"fa8641fb64db60e7299f070f6497678dee0bfdeefcc22a51ca328da34b33fdd6c31b882d97fc32cfcdeee4fcb72b05d7eae43b10b531db161b7e8dcfc2775ebf",
"fdde8cdd2fa5689aec75121e3c0778ca8c37238fd6a64706d85a4156d7735c482f1db74cefd023e94587b64a56d4a06e3b7fecf5c85978a4c777c9eaa5c633fd",
"jan",
"mar",
"ona",
"pau",
"pep",
"pol",
"teo"
]
}
}
},
"fa": {
"custom": {
"glow-tts": {
"id": "tts_models/fa/custom/glow-tts"
}
}
},
"bn": {
"custom": {
"vits-male": {
"id": "tts_models/bn/custom/vits-male"
},
"vits-female": {
"id": "tts_models/bn/custom/vits-female"
}
}
}
}

View File

@ -651,6 +651,7 @@ function onTtsProviderSettingsInput() {
ttsProvider.onSettingsChange()
// Persist changes to SillyTavern tts extension settings
extension_settings.tts[ttsProviderName] = ttsProvider.settings
saveSettingsDebounced()
console.info(`Saved settings ${ttsProviderName} ${JSON.stringify(ttsProvider.settings)}`)

View File

@ -4,7 +4,8 @@
"requires": [],
"optional": [
"silero-tts",
"edge-tts"
"edge-tts",
"coqui-tts"
],
"js": "index.js",
"css": "style.css",

129
public/scripts/filters.js Normal file
View File

@ -0,0 +1,129 @@
import { fuzzySearchCharacters, fuzzySearchGroups, power_user } from "./power-user.js";
import { tag_map } from "./tags.js";
export const FILTER_TYPES = {
SEARCH: 'search',
TAG: 'tag',
FAV: 'fav',
GROUP: 'group',
};
export class FilterHelper {
constructor(onDataChanged) {
this.onDataChanged = onDataChanged;
}
filterFunctions = {
[FILTER_TYPES.SEARCH]: this.searchFilter.bind(this),
[FILTER_TYPES.GROUP]: this.groupFilter.bind(this),
[FILTER_TYPES.FAV]: this.favFilter.bind(this),
[FILTER_TYPES.TAG]: this.tagFilter.bind(this),
}
filterData = {
[FILTER_TYPES.SEARCH]: '',
[FILTER_TYPES.GROUP]: false,
[FILTER_TYPES.FAV]: false,
[FILTER_TYPES.TAG]: { excluded: [], selected: [] },
}
tagFilter(data) {
const TAG_LOGIC_AND = true; // switch to false to use OR logic for combining tags
const { selected, excluded } = this.filterData[FILTER_TYPES.TAG];
if (!selected.length && !excluded.length) {
return data;
}
function isElementTagged(entity, tagId) {
const isCharacter = entity.type === 'character';
const lookupValue = isCharacter ? entity.item.avatar : String(entity.id);
const isTagged = Array.isArray(tag_map[lookupValue]) && tag_map[lookupValue].includes(tagId);
return isTagged;
}
function getIsTagged(entity) {
const tagFlags = selected.map(tagId => isElementTagged(entity, tagId));
const trueFlags = tagFlags.filter(x => x);
const isTagged = TAG_LOGIC_AND ? tagFlags.length === trueFlags.length : trueFlags.length > 0;
const excludedTagFlags = excluded.map(tagId => isElementTagged(entity, tagId));
const isExcluded = excludedTagFlags.includes(true);
if (isExcluded) {
return false;
} else if (selected.length > 0 && !isTagged) {
return false;
} else {
return true;
}
}
return data.filter(entity => getIsTagged(entity));
}
favFilter(data) {
if (!this.filterData[FILTER_TYPES.FAV]) {
return data;
}
return data.filter(entity => entity.item.fav || entity.item.fav == "true");
}
groupFilter(data) {
if (!this.filterData[FILTER_TYPES.GROUP]) {
return data;
}
return data.filter(entity => entity.type === 'group');
}
searchFilter(data) {
if (!this.filterData[FILTER_TYPES.SEARCH]) {
return data;
}
const searchValue = this.filterData[FILTER_TYPES.SEARCH].trim().toLowerCase();
const fuzzySearchCharactersResults = power_user.fuzzy_search ? fuzzySearchCharacters(searchValue) : [];
const fuzzySearchGroupsResults = power_user.fuzzy_search ? fuzzySearchGroups(searchValue) : [];
function getIsValidSearch(entity) {
const isGroup = entity.type === 'group';
const isCharacter = entity.type === 'character';
if (power_user.fuzzy_search) {
if (isCharacter) {
return fuzzySearchCharactersResults.includes(parseInt(entity.id));
} else if (isGroup) {
return fuzzySearchGroupsResults.includes(String(entity.id));
} else {
return false;
}
}
else {
return entity.item?.name?.toLowerCase()?.includes(searchValue) || false;
}
}
return data.filter(entity => getIsValidSearch(entity));
}
setFilterData(filterType, data) {
const oldData = this.filterData[filterType];
this.filterData[filterType] = data;
// only trigger a data change if the data actually changed
if (JSON.stringify(oldData) !== JSON.stringify(data)) {
this.onDataChanged();
}
}
getFilterData(filterType) {
return this.filterData[filterType];
}
applyFilters(data) {
return Object.values(this.filterFunctions)
.reduce((data, fn) => fn(data), data);
}
}

View File

@ -7,8 +7,8 @@ import {
createThumbnail,
extractAllWords,
} from './utils.js';
import { RA_CountCharTokens, humanizedDateTime, dragElement } from "./RossAscends-mods.js";
import { sortCharactersList, sortGroupMembers, loadMovingUIState } from './power-user.js';
import { RA_CountCharTokens, humanizedDateTime, dragElement, favsToHotswap } from "./RossAscends-mods.js";
import { loadMovingUIState, sortEntitiesList } from './power-user.js';
import {
chat,
@ -61,8 +61,10 @@ import {
getCurrentChatId,
setScenarioOverride,
getCropPopup,
system_avatar,
} from "../script.js";
import { appendTagToList, createTagMapFromList, getTagsList, applyTagsOnCharacterSelect, tag_map } from './tags.js';
import { FilterHelper } from './filters.js';
export {
selected_group,
@ -75,7 +77,6 @@ export {
deleteGroup,
getGroupAvatar,
getGroups,
printGroups,
regenerateGroup,
resetSelectedGroup,
select_group_chats,
@ -87,15 +88,17 @@ let groups = [];
let selected_group = null;
let group_generation_id = null;
let fav_grp_checked = false;
let fav_filter_on = false;
let openGroupId = null;
let newGroupMembers = [];
export const group_activation_strategy = {
NATURAL: 0,
LIST: 1,
};
export const groupCandidatesFilter = new FilterHelper(debounce(printGroupCandidates, 100));
const groupAutoModeInterval = setInterval(groupChatAutoModeWorker, 5000);
const saveGroupDebounced = debounce(async (group) => await _save(group), 500);
const saveGroupDebounced = debounce(async (group, reload) => await _save(group, reload), 500);
async function _save(group, reload = true) {
await fetch("/editgroup", {
@ -226,9 +229,8 @@ async function saveGroupChat(groupId, shouldSaveGroup) {
});
if (shouldSaveGroup && response.ok) {
await editGroup(groupId);
await editGroup(groupId, false, false);
}
sortCharactersList();
}
export async function renameGroupMember(oldAvatar, newAvatar, newName) {
@ -245,7 +247,7 @@ export async function renameGroupMember(oldAvatar, newAvatar, newName) {
// Replace group member avatar id and save the changes
group.members[memberIndex] = newAvatar;
await editGroup(group.id, true);
await editGroup(group.id, true, false);
console.log(`Renamed character ${newName} in group: ${group.name}`)
// Load all chats from this group
@ -330,8 +332,7 @@ async function getGroups() {
}
}
function printGroups() {
for (let group of groups) {
export function getGroupBlock(group) {
const template = $("#group_list_template .group_select").clone();
template.data("id", group.id);
template.attr("grid", group.id);
@ -345,17 +346,20 @@ function printGroups() {
const tagsElement = template.find('.tags');
tags.forEach(tag => appendTagToList(tagsElement, tag, {}));
$("#rm_print_characters_block").prepend(template);
updateGroupAvatar(group);
}
const avatar = getGroupAvatar(group);
if (avatar) {
$(template).find(".avatar").replaceWith(avatar);
}
return template;
}
function updateGroupAvatar(group) {
$("#rm_print_characters_block .group_select").each(function () {
$("#group_avatar_preview").empty().append(getGroupAvatar(group));
$(".group_select").each(function () {
if ($(this).data("id") == group.id) {
const avatar = getGroupAvatar(group);
if (avatar) {
$(this).find(".avatar").replaceWith(avatar);
}
$(this).find(".avatar").replaceWith(getGroupAvatar(group));
}
});
}
@ -397,7 +401,7 @@ function getGroupAvatar(group) {
// default avatar
const groupAvatar = $("#group_avatars_template .collage_1").clone();
groupAvatar.find(".img_1").attr("src", group.avatar_url);
groupAvatar.find(".img_1").attr("src", group.avatar_url || system_avatar);
return groupAvatar;
}
@ -783,8 +787,6 @@ function activateNaturalOrder(members, input, lastMessage, allowSelfResponses, i
return memberIds;
}
async function deleteGroup(id) {
const response = await fetch("/deletegroup", {
method: "POST",
@ -823,7 +825,7 @@ export async function editGroup(id, immediately, reload = true) {
return await _save(group, reload);
}
saveGroupDebounced(group);
saveGroupDebounced(group, reload);
}
let groupAutoModeAbortController = null;
@ -849,104 +851,223 @@ async function groupChatAutoModeWorker() {
async function modifyGroupMember(chat_id, groupMember, isDelete) {
const id = groupMember.data("id");
const template = groupMember.clone();
let _thisGroup = groups.find((x) => x.id == chat_id);
template.data("id", id);
const thisGroup = groups.find((x) => x.id == chat_id);
const membersArray = thisGroup?.members ?? newGroupMembers;
if (isDelete) {
$("#rm_group_add_members").prepend(template);
} else {
$("#rm_group_members").prepend(template);
}
if (_thisGroup) {
if (isDelete) {
const index = _thisGroup.members.findIndex((x) => x === id);
if (index !== -1) {
_thisGroup.members.splice(index, 1);
}
} else {
_thisGroup.members.push(id);
template.css({ 'order': _thisGroup.members.length });
const index = membersArray.findIndex((x) => x === id);
if (index !== -1) {
membersArray.splice(membersArray.indexOf(id), 1);
}
await editGroup(selected_group);
updateGroupAvatar(_thisGroup);
}
else {
template.css({ 'order': 'unset' });
} else {
membersArray.unshift(id);
}
groupMember.remove();
const groupHasMembers = !!$("#rm_group_members").children().length;
if (openGroupId) {
await editGroup(openGroupId, false, false);
updateGroupAvatar(thisGroup);
}
printGroupCandidates();
printGroupMembers();
const groupHasMembers = getGroupCharacters({ doFilter: false, onlyMembers: true }).length > 0;
$("#rm_group_submit").prop("disabled", !groupHasMembers);
}
async function reorderGroupMember(chat_id, groupMember, direction) {
const id = groupMember.data("id");
const group = groups.find((x) => x.id == chat_id);
const thisGroup = groups.find((x) => x.id == chat_id);
const memberArray = thisGroup?.members ?? newGroupMembers;
const indexOf = memberArray.indexOf(id);
if (direction == 'down') {
const next = memberArray[indexOf + 1];
if (next) {
memberArray[indexOf + 1] = memberArray[indexOf];
memberArray[indexOf] = next;
}
}
if (direction == 'up') {
const prev = memberArray[indexOf - 1];
if (prev) {
memberArray[indexOf - 1] = memberArray[indexOf];
memberArray[indexOf] = prev;
}
}
printGroupMembers();
// Existing groups need to modify members list
if (group && group.members.length > 1) {
const indexOf = group.members.indexOf(id);
if (direction == 'down') {
const next = group.members[indexOf + 1];
if (next) {
group.members[indexOf + 1] = group.members[indexOf];
group.members[indexOf] = next;
}
}
if (direction == 'up') {
const prev = group.members[indexOf - 1];
if (prev) {
group.members[indexOf - 1] = group.members[indexOf];
group.members[indexOf] = prev;
}
}
await editGroup(chat_id);
updateGroupAvatar(group);
// stupid but lifts the manual reordering
select_group_chats(chat_id, true);
if (openGroupId) {
await editGroup(chat_id, false, false);
updateGroupAvatar(thisGroup);
}
// New groups just can't be DOM-ordered
else {
if (direction == 'down') {
groupMember.insertAfter(groupMember.next());
}
if (direction == 'up') {
groupMember.insertBefore(groupMember.prev());
}
}
async function onGroupActivationStrategyInput(e) {
if (openGroupId) {
let _thisGroup = groups.find((x) => x.id == openGroupId);
_thisGroup.activation_strategy = Number(e.target.value);
await editGroup(openGroupId, false, false);
}
}
async function onGroupNameInput() {
if (openGroupId) {
let _thisGroup = groups.find((x) => x.id == openGroupId);
_thisGroup.name = $(this).val();
$("#rm_button_selected_ch").children("h2").text(_thisGroup.name);
await editGroup(openGroupId);
}
}
function isGroupMember(group, avatarId) {
if (group && Array.isArray(group.members)) {
return group.members.includes(avatarId);
} else {
return newGroupMembers.includes(avatarId);
}
}
function getGroupCharacters({ doFilter, onlyMembers } = {}) {
function sortMembersFn(a, b) {
const membersArray = thisGroup?.members ?? newGroupMembers;
const aIndex = membersArray.indexOf(a.item.avatar);
const bIndex = membersArray.indexOf(b.item.avatar);
return aIndex - bIndex;
}
const thisGroup = openGroupId && groups.find((x) => x.id == openGroupId);
let candidates = characters
.filter((x) => isGroupMember(thisGroup, x.avatar) == onlyMembers)
.map((x, index) => ({ item: x, id: index, type: 'character' }));
if (onlyMembers) {
candidates.sort(sortMembersFn);
} else {
sortEntitiesList(candidates);
}
if (doFilter) {
candidates = groupCandidatesFilter.applyFilters(candidates);
}
return candidates;
}
function printGroupCandidates() {
$("#rm_group_add_members_pagination").pagination({
dataSource: getGroupCharacters({ doFilter: true, onlyMembers: false }),
pageSize: 5,
pageRange: 1,
position: 'top',
showPageNumbers: false,
showSizeChanger: false,
prevText: '<',
nextText: '>',
showNavigator: true,
callback: function (data) {
$("#rm_group_add_members").empty();
for (const i of data) {
$("#rm_group_add_members").append(getGroupCharacterBlock(i.item));
}
},
});
}
function printGroupMembers() {
$("#rm_group_members_pagination").pagination({
dataSource: getGroupCharacters({ doFilter: false, onlyMembers: true }),
pageSize: 5,
pageRange: 1,
position: 'top',
showPageNumbers: false,
showSizeChanger: false,
prevText: '<',
nextText: '>',
showNavigator: true,
callback: function (data) {
$("#rm_group_members").empty();
for (const i of data) {
$("#rm_group_members").append(getGroupCharacterBlock(i.item));
}
},
});
}
function getGroupCharacterBlock(character) {
const avatar = getThumbnailUrl('avatar', character.avatar);
const template = $("#group_member_template .group_member").clone();
const isFav = character.fav || character.fav == 'true';
template.data("id", character.avatar);
template.find(".avatar img").attr({ "src": avatar, "title": character.avatar });
template.find(".ch_name").text(character.name);
template.attr("chid", characters.indexOf(character));
template.find('.ch_fav').val(isFav);
template.toggleClass('is_fav', isFav);
template.toggleClass('disabled', isGroupMemberDisabled(character.avatar));
// Display inline tags
const tags = getTagsList(character.avatar);
const tagsElement = template.find('.tags');
tags.forEach(tag => appendTagToList(tagsElement, tag, {}));
if (!openGroupId) {
template.find('[data-action="speak"]').hide();
template.find('[data-action="enable"]').hide();
template.find('[data-action="disable"]').hide();
}
return template;
}
function isGroupMemberDisabled(avatarId) {
const thisGroup = openGroupId && groups.find((x) => x.id == openGroupId);
return Boolean(thisGroup && thisGroup.disabled_members.includes(avatarId));
}
function onDeleteGroupClick() {
if (is_group_generating) {
toastr.warning('Not so fast! Wait for the characters to stop typing before deleting the group.');
return;
}
$("#dialogue_popup").data("group_id", openGroupId);
callPopup('<h3>Delete the group?</h3><p>This will also delete all your chats with that group. If you want to delete a single conversation, select a "View past chats" option in the lower left menu.</p>', "del_group");
}
async function onFavoriteGroupClick() {
updateFavButtonState(!fav_grp_checked);
if (openGroupId) {
let _thisGroup = groups.find((x) => x.id == openGroupId);
_thisGroup.fav = fav_grp_checked;
await editGroup(openGroupId, false, false);
favsToHotswap();
}
}
async function onGroupSelfResponsesClick() {
if (openGroupId) {
let _thisGroup = groups.find((x) => x.id == openGroupId);
const value = $(this).prop("checked");
_thisGroup.allow_self_responses = value;
await editGroup(openGroupId, false, false);
}
}
function select_group_chats(groupId, skipAnimation) {
const group = groupId && groups.find((x) => x.id == groupId);
openGroupId = groupId;
newGroupMembers = [];
const group = openGroupId && groups.find((x) => x.id == openGroupId);
const groupName = group?.name ?? "";
const replyStrategy = Number(group?.activation_strategy ?? group_activation_strategy.NATURAL);
setMenuType(!!group ? 'group_edit' : 'group_create');
$("#group_avatar_preview").empty().append(getGroupAvatar(group));
$("#rm_group_restore_avatar").toggle(!!group && isDataURL(group.avatar_url));
$("#rm_group_chat_name").val(groupName);
$("#rm_group_chat_name").off();
$("#rm_group_chat_name").on("input", async function () {
if (groupId) {
let _thisGroup = groups.find((x) => x.id == groupId);
_thisGroup.name = $(this).val();
$("#rm_button_selected_ch").children("h2").text(_thisGroup.name);
await editGroup(groupId);
}
});
$("#rm_group_filter").val("").trigger("input");
$('input[name="rm_group_activation_strategy"]').off();
$('input[name="rm_group_activation_strategy"]').on("input", async function (e) {
if (groupId) {
let _thisGroup = groups.find((x) => x.id == groupId);
_thisGroup.activation_strategy = Number(e.target.value);
await editGroup(groupId);
}
});
const replyStrategy = Number(group?.activation_strategy ?? group_activation_strategy.NATURAL);
$(`input[name="rm_group_activation_strategy"][value="${replyStrategy}"]`).prop('checked', true);
if (!skipAnimation) {
@ -954,53 +1075,15 @@ function select_group_chats(groupId, skipAnimation) {
}
// render characters list
$("#rm_group_add_members").empty();
$("#rm_group_members").empty();
for (let character of characters) {
const avatar =
character.avatar != "none"
? getThumbnailUrl('avatar', character.avatar)
: default_avatar;
const template = $("#group_member_template .group_member").clone();
const isFav = character.fav || character.fav == 'true';
template.data("id", character.avatar);
template.find(".avatar img").attr("src", avatar);
template.find(".avatar img").attr("title", character.avatar);
template.find(".ch_name").text(character.name);
template.attr("chid", characters.indexOf(character));
template.find('.ch_fav').val(isFav);
template.toggleClass('is_fav', isFav);
// Display inline tags
const tags = getTagsList(character.avatar);
const tagsElement = template.find('.tags');
tags.forEach(tag => appendTagToList(tagsElement, tag, {}));
if (!group) {
template.find('[data-action="speak"]').hide();
}
if (
group &&
Array.isArray(group.members) &&
group.members.includes(character.avatar)
) {
template.css({ 'order': group.members.indexOf(character.avatar) });
template.toggleClass('disabled', group.disabled_members.includes(character.avatar));
$("#rm_group_members").append(template);
} else {
$("#rm_group_add_members").append(template);
}
}
sortGroupMembers("#rm_group_add_members .group_member");
printGroupCandidates();
printGroupMembers();
const groupHasMembers = !!$("#rm_group_members").children().length;
$("#rm_group_submit").prop("disabled", !groupHasMembers);
$("#rm_group_allow_self_responses").prop("checked", group && group.allow_self_responses);
// bottom buttons
if (groupId) {
if (openGroupId) {
$("#rm_group_submit").hide();
$("#rm_group_delete").show();
$("#rm_group_scenario").show();
@ -1013,39 +1096,8 @@ function select_group_chats(groupId, skipAnimation) {
$("#rm_group_scenario").hide();
}
$("#rm_group_delete").off();
$("#rm_group_delete").on("click", function () {
if (is_group_generating) {
toastr.warning('Not so fast! Wait for the characters to stop typing before deleting the group.');
return;
}
$("#dialogue_popup").data("group_id", groupId);
callPopup('<h3>Delete the group?</h3><p>This will also delete all your chats with that group. If you want to delete a single conversation, select a "View past chats" option in the lower left menu.</p>', "del_group");
});
updateFavButtonState(group?.fav ?? false);
$("#group_favorite_button").off('click');
$("#group_favorite_button").on('click', async function () {
updateFavButtonState(!fav_grp_checked);
if (group) {
let _thisGroup = groups.find((x) => x.id == groupId);
_thisGroup.fav = fav_grp_checked;
await editGroup(groupId);
}
});
$("#rm_group_allow_self_responses").off();
$("#rm_group_allow_self_responses").on("input", async function () {
if (group) {
let _thisGroup = groups.find((x) => x.id == groupId);
const value = $(this).prop("checked");
_thisGroup.allow_self_responses = value;
await editGroup(groupId);
}
});
// top bar
if (group) {
$("#rm_group_automode_label").show();
@ -1056,118 +1108,112 @@ function select_group_chats(groupId, skipAnimation) {
$("#rm_group_automode_label").hide();
}
$("#group_avatar_button").off('input').on("input", uploadGroupAvatar);
$("#rm_group_restore_avatar").off('click').on("click", restoreGroupAvatar);
eventSource.emit('groupSelected', {detail: {id: openGroupId, group: group}});
}
async function uploadGroupAvatar(event) {
const file = event.target.files[0];
async function uploadGroupAvatar(event) {
const file = event.target.files[0];
if (!file) {
return;
}
const e = await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = resolve;
reader.onerror = reject;
reader.readAsDataURL(file);
});
$('#dialogue_popup').addClass('large_dialogue_popup wide_dialogue_popup');
const croppedImage = await callPopup(getCropPopup(e.target.result), 'avatarToCrop');
if (!croppedImage) {
return;
}
const thumbnail = await createThumbnail(croppedImage, 96, 144);
if (!groupId) {
$('#group_avatar_preview img').attr('src', thumbnail);
$('#rm_group_restore_avatar').show();
return;
}
let _thisGroup = groups.find((x) => x.id == groupId);
_thisGroup.avatar_url = thumbnail;
$("#group_avatar_preview").empty().append(getGroupAvatar(_thisGroup));
$("#rm_group_restore_avatar").show();
await editGroup(groupId, true, true);
if (!file) {
return;
}
async function restoreGroupAvatar() {
const confirm = await callPopup('<h3>Are you sure you want to restore the group avatar?</h3> Your custom image will be deleted, and a collage will be used instead.', 'confirm');
if (!confirm) {
return;
}
if (!groupId) {
$("#group_avatar_preview img").attr("src", default_avatar);
$("#rm_group_restore_avatar").hide();
return;
}
let _thisGroup = groups.find((x) => x.id == groupId);
_thisGroup.avatar_url = '';
$("#group_avatar_preview").empty().append(getGroupAvatar(_thisGroup));
$("#rm_group_restore_avatar").hide();
await editGroup(groupId, true, true);
}
$(document).off("click", ".group_member .right_menu_button");
$(document).on("click", ".group_member .right_menu_button", async function (event) {
event.stopPropagation();
const action = $(this).data('action');
const member = $(this).closest('.group_member');
if (action === 'remove') {
await modifyGroupMember(groupId, member, true);
}
if (action === 'add') {
await modifyGroupMember(groupId, member, false);
}
if (action === 'enable') {
member.removeClass('disabled');
const _thisGroup = groups.find(x => x.id === groupId);
const index = _thisGroup.disabled_members.indexOf(member.data('id'));
if (index !== -1) {
_thisGroup.disabled_members.splice(index, 1);
}
await editGroup(groupId);
}
if (action === 'disable') {
member.addClass('disabled');
const _thisGroup = groups.find(x => x.id === groupId);
_thisGroup.disabled_members.push(member.data('id'));
await editGroup(groupId);
}
if (action === 'up' || action === 'down') {
await reorderGroupMember(groupId, member, action);
}
if (action === 'view') {
openCharacterDefinition(member);
}
if (action === 'speak') {
const chid = Number(member.attr('chid'));
if (Number.isInteger(chid)) {
Generate('normal', { force_chid: chid });
}
}
sortGroupMembers("#rm_group_add_members .group_member");
await eventSource.emit(event_types.GROUP_UPDATED);
const e = await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = resolve;
reader.onerror = reject;
reader.readAsDataURL(file);
});
eventSource.emit('groupSelected', {detail: {id: groupId, group: group}});
$('#dialogue_popup').addClass('large_dialogue_popup wide_dialogue_popup');
const croppedImage = await callPopup(getCropPopup(e.target.result), 'avatarToCrop');
if (!croppedImage) {
return;
}
const thumbnail = await createThumbnail(croppedImage, 96, 144);
if (!openGroupId) {
$('#group_avatar_preview img').attr('src', thumbnail);
$('#rm_group_restore_avatar').show();
return;
}
let _thisGroup = groups.find((x) => x.id == openGroupId);
_thisGroup.avatar_url = thumbnail;
$("#group_avatar_preview").empty().append(getGroupAvatar(_thisGroup));
$("#rm_group_restore_avatar").show();
await editGroup(openGroupId, true, true);
}
async function restoreGroupAvatar() {
const confirm = await callPopup('<h3>Are you sure you want to restore the group avatar?</h3> Your custom image will be deleted, and a collage will be used instead.', 'confirm');
if (!confirm) {
return;
}
if (!openGroupId) {
$("#group_avatar_preview img").attr("src", default_avatar);
$("#rm_group_restore_avatar").hide();
return;
}
let _thisGroup = groups.find((x) => x.id == openGroupId);
_thisGroup.avatar_url = '';
$("#group_avatar_preview").empty().append(getGroupAvatar(_thisGroup));
$("#rm_group_restore_avatar").hide();
await editGroup(openGroupId, true, true);
}
async function onGroupActionClick(event) {
event.stopPropagation();
const action = $(this).data('action');
const member = $(this).closest('.group_member');
if (action === 'remove') {
await modifyGroupMember(openGroupId, member, true);
}
if (action === 'add') {
await modifyGroupMember(openGroupId, member, false);
}
if (action === 'enable') {
member.removeClass('disabled');
const _thisGroup = groups.find(x => x.id === openGroupId);
const index = _thisGroup.disabled_members.indexOf(member.data('id'));
if (index !== -1) {
_thisGroup.disabled_members.splice(index, 1);
}
await editGroup(openGroupId, false, false);
}
if (action === 'disable') {
member.addClass('disabled');
const _thisGroup = groups.find(x => x.id === openGroupId);
_thisGroup.disabled_members.push(member.data('id'));
await editGroup(openGroupId, false, false);
}
if (action === 'up' || action === 'down') {
await reorderGroupMember(openGroupId, member, action);
}
if (action === 'view') {
openCharacterDefinition(member);
}
if (action === 'speak') {
const chid = Number(member.attr('chid'));
if (Number.isInteger(chid)) {
Generate('normal', { force_chid: chid });
}
}
await eventSource.emit(event_types.GROUP_UPDATED);
}
function updateFavButtonState(state) {
@ -1235,10 +1281,7 @@ 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;
const members = $("#rm_group_members .group_member")
.map((_, x) => $(x).data("id"))
.toArray();
const members = newGroupMembers;
const memberNames = characters.filter(x => members.includes(x.avatar)).map(x => x.name).join(", ");
if (!name) {
@ -1268,6 +1311,7 @@ async function createGroup() {
});
if (createGroupResponse.ok) {
newGroupMembers = [];
const data = await createGroupResponse.json();
createTagMapFromList("#groupTagList", data.id);
await getCharacters();
@ -1351,9 +1395,8 @@ export async function openGroupChat(groupId, chatId) {
group['date_last_chat'] = Date.now();
updateChatMetadata(group.chat_metadata, true);
await editGroup(groupId, true);
await editGroup(groupId, true, false);
await getGroupChat(groupId);
sortCharactersList();
}
export async function renameGroupChat(groupId, oldChatId, newChatId) {
@ -1445,7 +1488,7 @@ export async function saveGroupBookmarkChat(groupId, name, metadata, mesId) {
? chat.slice(0, parseInt(mesId) + 1)
: chat;
await editGroup(groupId, true);
await editGroup(groupId, true, false);
await fetch("/savegroupchat", {
method: "POST",
@ -1513,4 +1556,12 @@ jQuery(() => {
});
$("#send_textarea").on("keyup", onSendTextareaInput);
$("#groupCurrentMemberPopoutButton").on('click', doCurMemberListPopout);
$("#rm_group_chat_name").on("input", onGroupNameInput)
$("#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);
$("#group_avatar_button").on("input", uploadGroupAvatar);
$("#rm_group_restore_avatar").on("click", restoreGroupAvatar);
$(document).on("click", ".group_member .right_menu_button", onGroupActionClick);
});

5972
public/scripts/handlebars.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -7,6 +7,7 @@ import {
import {
power_user,
} from "./power-user.js";
import { getSortableDelay } from "./utils.js";
export {
kai_settings,
@ -243,7 +244,7 @@ function sortItemsByOrder(orderArray) {
}
}
$(document).ready(function () {
jQuery(function () {
sliders.forEach(slider => {
$(document).on("input", slider.sliderId, function () {
const value = $(this).val();
@ -267,6 +268,7 @@ $(document).ready(function () {
});
$('#kobold_order').sortable({
delay: getSortableDelay(),
stop: function () {
const order = [];
$('#kobold_order').children().each(function () {

View File

@ -1,12 +1,19 @@
import {
getRequestHeaders,
saveSettingsDebounced,
getStoppingStrings,
getTextTokens
getTextTokens,
max_context,
novelai_setting_names,
saveSettingsDebounced,
setGenerationParamsFromPreset
} from "../script.js";
import { getCfg } from "./extensions/cfg/util.js";
import { tokenizers } from "./power-user.js";
import { getStringHash } from "./utils.js";
import { MAX_CONTEXT_DEFAULT, tokenizers } from "./power-user.js";
import {
getSortableDelay,
getStringHash,
uuidv4,
} from "./utils.js";
export {
nai_settings,
@ -16,29 +23,37 @@ export {
};
const default_preamble = "[ Style: chat, complex, sensory, visceral ]";
const default_order = [1, 5, 0, 2, 3, 4];
const maximum_output_length = 150;
const default_presets = {
"euterpe-v2": "Classic-Euterpe",
"krake-v2": "Classic-Krake",
"clio-v1": "Talker-Chat-Clio",
"kayra-v1": "Carefree-Kayra"
}
const nai_settings = {
temperature: 0.5,
repetition_penalty: 1,
repetition_penalty_range: 100,
repetition_penalty_slope: 0,
temperature: 1.5,
repetition_penalty: 2.25,
repetition_penalty_range: 2048,
repetition_penalty_slope: 0.09,
repetition_penalty_frequency: 0,
repetition_penalty_presence: 0,
tail_free_sampling: 0.68,
top_k: 0,
top_p: 1,
top_a: 1,
top_g: 0,
typical_p: 1,
min_length: 0,
model_novel: "euterpe-v2",
preset_settings_novel: "Classic-Euterpe",
repetition_penalty_presence: 0.005,
tail_free_sampling: 0.975,
top_k: 10,
top_p: 0.75,
top_a: 0.08,
typical_p: 0.975,
min_length: 1,
model_novel: "clio-v1",
preset_settings_novel: "Talker-Chat-Clio",
streaming_novel: false,
nai_preamble: default_preamble,
preamble: default_preamble,
prefix: '',
cfg_uc: '',
banned_tokens: '',
order: default_order,
logit_bias: [],
};
const nai_tiers = {
@ -50,6 +65,7 @@ const nai_tiers = {
let novel_data = null;
let badWordsCache = {};
let biasCache = undefined;
export function setNovelData(data) {
novel_data = data;
@ -73,16 +89,17 @@ function getNovelTier(tier) {
}
function loadNovelPreset(preset) {
$("#amount_gen").val(preset.max_length);
$("#amount_gen_counter").text(`${preset.max_length}`);
if (((preset.max_context > 2048) && (!$("#max_context_unlocked")[0].checked)) ||
((preset.max_context <= 2048) && ($("#max_context_unlocked")[0].checked))) {
$("#max_context_unlocked").click();
if (preset.genamt === undefined) {
const needsUnlock = preset.max_context > MAX_CONTEXT_DEFAULT;
$("#amount_gen").val(preset.max_length).trigger('input');
$('#max_context_unlocked').prop('checked', needsUnlock).trigger('change');
$("#max_context").val(preset.max_context).trigger('input');
}
else {
setGenerationParamsFromPreset(preset);
}
$("#max_context").val(preset.max_context);
$("#max_context_counter").text(`${preset.max_context}`);
$("#rep_pen_size_novel").attr('max', preset.max_context);
$("#rep_pen_size_novel").attr('max', max_context);
nai_settings.temperature = preset.temperature;
nai_settings.repetition_penalty = preset.repetition_penalty;
nai_settings.repetition_penalty_range = preset.repetition_penalty_range;
@ -97,12 +114,14 @@ function loadNovelPreset(preset) {
nai_settings.min_length = preset.min_length;
nai_settings.cfg_scale = preset.cfg_scale;
nai_settings.phrase_rep_pen = preset.phrase_rep_pen;
nai_settings.top_g = preset.top_g;
nai_settings.mirostat_lr = preset.mirostat_lr;
nai_settings.mirostat_tau = preset.mirostat_tau;
nai_settings.prefix = preset.prefix;
nai_settings.cfg_uc = preset.cfg_uc || '';
nai_settings.banned_tokens = preset.banned_tokens || '';
nai_settings.order = preset.order || default_order;
nai_settings.logit_bias = preset.logit_bias || [];
nai_settings.preamble = preset.preamble || default_preamble;
loadNovelSettingsUi(nai_settings);
}
@ -112,7 +131,10 @@ 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.preamble = settings.nai_preamble;
if (settings.nai_preamble !== undefined) {
nai_settings.preamble = settings.nai_preamble;
delete settings.nai_preamble;
}
nai_settings.preset_settings_novel = settings.preset_settings_novel;
nai_settings.temperature = settings.temperature;
nai_settings.repetition_penalty = settings.repetition_penalty;
@ -128,7 +150,6 @@ function loadNovelSettings(settings) {
nai_settings.min_length = settings.min_length;
nai_settings.phrase_rep_pen = settings.phrase_rep_pen;
nai_settings.cfg_scale = settings.cfg_scale;
nai_settings.top_g = settings.top_g;
nai_settings.mirostat_lr = settings.mirostat_lr;
nai_settings.mirostat_tau = settings.mirostat_tau;
nai_settings.streaming_novel = !!settings.streaming_novel;
@ -136,6 +157,8 @@ function loadNovelSettings(settings) {
nai_settings.prefix = settings.prefix;
nai_settings.cfg_uc = settings.cfg_uc || '';
nai_settings.banned_tokens = settings.banned_tokens || '';
nai_settings.order = settings.order || default_order;
nai_settings.logit_bias = settings.logit_bias || [];
loadNovelSettingsUi(nai_settings);
}
@ -145,6 +168,7 @@ function loadNovelSettingsUi(ui_settings) {
$("#rep_pen_novel").val(ui_settings.repetition_penalty);
$("#rep_pen_counter_novel").text(Number(ui_settings.repetition_penalty).toFixed(2));
$("#rep_pen_size_novel").val(ui_settings.repetition_penalty_range);
$("#rep_pen_size_novel").attr('max', max_context);
$("#rep_pen_size_counter_novel").text(Number(ui_settings.repetition_penalty_range).toFixed(0));
$("#rep_pen_slope_novel").val(ui_settings.repetition_penalty_slope);
$("#rep_pen_slope_counter_novel").text(Number(`${ui_settings.repetition_penalty_slope}`).toFixed(2));
@ -165,20 +189,20 @@ function loadNovelSettingsUi(ui_settings) {
$("#cfg_scale_novel").val(ui_settings.cfg_scale);
$("#cfg_scale_counter_novel").text(Number(ui_settings.cfg_scale).toFixed(2));
$("#phrase_rep_pen_novel").val(ui_settings.phrase_rep_pen || "off");
$("#top_g_novel").val(ui_settings.top_g);
$("#top_g_counter_novel").text(Number(ui_settings.top_g).toFixed(0));
$("#mirostat_lr_novel").val(ui_settings.mirostat_lr);
$("#mirostat_lr_counter_novel").text(Number(ui_settings.mirostat_lr).toFixed(2));
$("#mirostat_tau_novel").val(ui_settings.mirostat_tau);
$("#mirostat_tau_counter_novel").text(Number(ui_settings.mirostat_tau).toFixed(2));
$("#min_length_novel").val(ui_settings.min_length);
$("#min_length_counter_novel").text(Number(ui_settings.min_length).toFixed(0));
$('#nai_preamble_textarea').val(ui_settings.nai_preamble);
$('#nai_preamble_textarea').val(ui_settings.preamble);
$('#nai_prefix').val(ui_settings.prefix || "vanilla");
$('#nai_cfg_uc').val(ui_settings.cfg_uc || "");
$('#nai_banned_tokens').val(ui_settings.banned_tokens || "");
$("#streaming_novel").prop('checked', ui_settings.streaming_novel);
sortItemsByOrder(ui_settings.order);
displayLogitBias(ui_settings.logit_bias);
}
const sliders = [
@ -248,12 +272,6 @@ const sliders = [
format: (val) => Number(val).toFixed(2),
setValue: (val) => { nai_settings.typical_p = Number(val).toFixed(2); },
},
{
sliderId: "#top_g_novel",
counterId: "#top_g_counter_novel",
format: (val) => Number(val).toFixed(0),
setValue: (val) => { nai_settings.top_g = Number(val).toFixed(0); },
},
{
sliderId: "#mirostat_tau_novel",
counterId: "#mirostat_tau_counter_novel",
@ -394,6 +412,12 @@ export function getNovelGenerationData(finalPrompt, this_settings, this_amount_g
const prefix = selectPrefix(nai_settings.prefix, finalPrompt);
const cfgSettings = getCfg();
let logitBias = [];
if (tokenizerType !== tokenizers.NONE && Array.isArray(nai_settings.logit_bias) && nai_settings.logit_bias.length) {
logitBias = biasCache || calculateLogitBias();
biasCache = logitBias;
}
return {
"input": finalPrompt,
"model": nai_settings.model_novel,
@ -411,21 +435,20 @@ export function getNovelGenerationData(finalPrompt, this_settings, this_amount_g
"top_p": parseFloat(nai_settings.top_p),
"top_k": parseInt(nai_settings.top_k),
"typical_p": parseFloat(nai_settings.typical_p),
"top_g": parseFloat(nai_settings.top_g),
"mirostat_lr": parseFloat(nai_settings.mirostat_lr),
"mirostat_tau": parseFloat(nai_settings.mirostat_tau),
"cfg_scale": cfgSettings?.guidanceScale ?? parseFloat(nai_settings.cfg_scale),
"cfg_uc": cfgSettings?.negativePrompt ?? nai_settings.cfg_uc ?? "",
"cfg_uc": cfgSettings?.negativePrompt ?? nai_settings.cfg_uc ?? "",
"phrase_rep_pen": nai_settings.phrase_rep_pen,
"stop_sequences": stopSequences,
"bad_words_ids": badWordIds,
"logit_bias_exp": logitBias,
"generate_until_sentence": true,
"use_cache": false,
"use_string": true,
"return_full_text": false,
"prefix": prefix,
"order": this_settings.order,
"streaming": nai_settings.streaming_novel,
"order": nai_settings.order || this_settings.order || default_order,
};
}
@ -446,7 +469,129 @@ function selectPrefix(selected_prefix, finalPromt) {
return "vanilla";
}
// Sort the samplers by the order array
function sortItemsByOrder(orderArray) {
console.debug('Preset samplers order: ' + orderArray);
const $draggableItems = $("#novel_order");
// Sort the items by the order array
for (let i = 0; i < orderArray.length; i++) {
const index = orderArray[i];
const $item = $draggableItems.find(`[data-id="${index}"]`).detach();
$draggableItems.append($item);
}
// Update the disabled class for each sampler
$draggableItems.children().each(function () {
const isEnabled = orderArray.includes(parseInt($(this).data('id')));
$(this).toggleClass('disabled', !isEnabled);
// If the sampler is disabled, move it to the bottom of the list
if (!isEnabled) {
const item = $(this).detach();
$draggableItems.append(item);
}
});
}
function saveSamplingOrder() {
const order = [];
$('#novel_order').children().each(function () {
const isEnabled = !$(this).hasClass('disabled');
if (isEnabled) {
order.push($(this).data('id'));
}
});
nai_settings.order = order;
console.log('Samplers reordered:', nai_settings.order);
saveSettingsDebounced();
}
function displayLogitBias(logit_bias) {
if (!Array.isArray(logit_bias)) {
console.log('Logit bias set not found');
return;
}
$('.novelai_logit_bias_list').empty();
for (const entry of logit_bias) {
if (entry) {
createLogitBiasListItem(entry);
}
}
biasCache = undefined;
}
function createNewLogitBiasEntry() {
const entry = { id: uuidv4(), text: '', value: 0 };
nai_settings.logit_bias.push(entry);
biasCache = undefined;
createLogitBiasListItem(entry);
saveSettingsDebounced();
}
function createLogitBiasListItem(entry) {
const id = entry.id;
const template = $('#novelai_logit_bias_template .novelai_logit_bias_form').clone();
template.data('id', id);
template.find('.novelai_logit_bias_text').val(entry.text).on('input', function () {
entry.text = $(this).val();
biasCache = undefined;
saveSettingsDebounced();
});
template.find('.novelai_logit_bias_value').val(entry.value).on('input', function () {
entry.value = Number($(this).val());
biasCache = undefined;
saveSettingsDebounced();
});
template.find('.novelai_logit_bias_remove').on('click', function () {
$(this).closest('.novelai_logit_bias_form').remove();
const index = nai_settings.logit_bias.indexOf(entry);
if (index > -1) {
nai_settings.logit_bias.splice(index, 1);
}
biasCache = undefined;
saveSettingsDebounced();
});
$('.novelai_logit_bias_list').prepend(template);
}
function calculateLogitBias() {
const bias_preset = nai_settings.logit_bias;
if (!Array.isArray(bias_preset) || bias_preset.length === 0) {
return [];
}
const clio = nai_settings.model_novel.includes('clio');
const kayra = nai_settings.model_novel.includes('kayra');
const tokenizerType = kayra ? tokenizers.NERD2 : (clio ? tokenizers.NERD : tokenizers.NONE);
return bias_preset.filter(b => b.text?.length > 0).map(bias => ({
bias: bias.value,
ensure_sequence_finish: false,
generate_once: false,
sequence: getTextTokens(tokenizerType, bias.text)
}));
}
/**
* Transforms instruction into compatible format for Novel AI.
* 1. Instruction must begin and end with curly braces followed and preceded by a space.
* 2. Instruction must not contain square brackets as it serves different purpose in NAI.
* @param {string} prompt Original instruction prompt
* @returns Processed prompt
*/
export function adjustNovelInstructionPrompt(prompt) {
const stripedPrompt = prompt.replace(/[\[\]]/g, '').trim();
return `{ ${stripedPrompt} }`;
}
export async function generateNovelWithStreaming(generate_data, signal) {
generate_data.streaming = nai_settings.streaming_novel;
const response = await fetch('/generate_novelai', {
headers: getRequestHeaders(),
body: JSON.stringify(generate_data),
@ -499,14 +644,13 @@ $("#nai_preamble_restore").on('click', function () {
saveSettingsDebounced();
});
$(document).ready(function () {
jQuery(function () {
sliders.forEach(slider => {
$(document).on("input", slider.sliderId, function () {
const value = $(this).val();
const formattedValue = slider.format(value);
slider.setValue(value);
$(slider.counterId).text(formattedValue);
console.log('saving');
saveSettingsDebounced();
});
});
@ -520,6 +664,12 @@ $(document).ready(function () {
$("#model_novel_select").change(function () {
nai_settings.model_novel = $("#model_novel_select").find(":selected").val();
saveSettingsDebounced();
// Update the selected preset to something appropriate
const default_preset = default_presets[nai_settings.model_novel];
$(`#settings_perset_novel`).val(novelai_setting_names[default_preset]);
$(`#settings_perset_novel option[value=${novelai_setting_names[default_preset]}]`).attr("selected", "true")
$(`#settings_perset_novel`).trigger("change");
});
$("#nai_prefix").on('change', function () {
@ -531,4 +681,19 @@ $(document).ready(function () {
nai_settings.phrase_rep_pen = $("#phrase_rep_pen_novel").find(":selected").val();
saveSettingsDebounced();
});
$('#novel_order').sortable({
delay: getSortableDelay(),
stop: saveSamplingOrder,
});
$('#novel_order .toggle_button').on('click', function () {
const $item = $(this).closest('[data-id]');
const isEnabled = !$item.hasClass('disabled');
$item.toggleClass('disabled', isEnabled);
console.log('Sampler toggled:', $item.data('id'), !isEnabled);
saveSamplingOrder();
});
$("#novelai_logit_bias_new_entry").on("click", createNewLogitBiasEntry);
});

View File

@ -47,7 +47,7 @@ import {
import {
delay,
download,
getFileText,
getFileText, getSortableDelay,
getStringHash,
parseJsonFile,
stringFormat,
@ -341,7 +341,7 @@ function setupChatCompletionPromptManager(openAiSettings) {
containerIdentifier: 'completion_prompt_manager',
listIdentifier: 'completion_prompt_manager_list',
toggleDisabled: ['main'],
draggable: true,
sortableDelay: getSortableDelay(),
defaultPrompts: {
main: default_main_prompt,
nsfw: default_nsfw_prompt,

1190
public/scripts/pagination.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,6 @@ import {
reloadCurrentChat,
getRequestHeaders,
substituteParams,
updateVisibleDivs,
eventSource,
event_types,
getCurrentChatId,
@ -18,7 +17,7 @@ import {
setCharacterId,
setEditedMessageId
} from "../script.js";
import { favsToHotswap, isMobile, initMovingUI } from "./RossAscends-mods.js";
import { isMobile, initMovingUI } from "./RossAscends-mods.js";
import {
groups,
resetSelectedGroup,
@ -34,8 +33,7 @@ export {
loadMovingUIState,
collapseNewlines,
playMessageSound,
sortGroupMembers,
sortCharactersList,
sortEntitiesList,
fixMarkdown,
power_user,
pygmalion_options,
@ -46,6 +44,12 @@ export {
export const MAX_CONTEXT_DEFAULT = 4096;
const MAX_CONTEXT_UNLOCKED = 65536;
const defaultStoryString = `{{#if description}}{{description}}{{/if}}
{{#if personality}}{{personality}}{{/if}}
{{#if scenario}}Scenario: {{scenario}}{{/if}}`;
const defaultExampleSeparator = '***';
const defaultChatStart = '***';
const avatar_styles = {
ROUND: 0,
RECTANGULAR: 1,
@ -93,11 +97,6 @@ let power_user = {
collapse_newlines: false,
pygmalion_formatting: pygmalion_options.AUTO,
pin_examples: false,
disable_description_formatting: false,
disable_scenario_formatting: false,
disable_personality_formatting: false,
disable_examples_formatting: false,
disable_start_formatting: false,
trim_sentences: false,
include_newline: false,
always_force_name2: false,
@ -106,7 +105,6 @@ let power_user = {
multigen: false,
multigen_first_chunk: 50,
multigen_next_chunks: 30,
custom_chat_separator: '',
markdown_escape_strings: '',
fast_ui_mode: true,
@ -176,12 +174,20 @@ let power_user = {
stop_sequence: '',
input_sequence: '### Instruction:',
output_sequence: '### Response:',
last_output_sequence: '',
preset: 'Alpaca',
separator_sequence: '',
macro: false,
names_force_groups: true,
},
context: {
preset: 'Default',
story_string: defaultStoryString,
chat_start: defaultChatStart,
example_separator: defaultExampleSeparator,
},
personas: {},
default_persona: null,
persona_descriptions: {},
@ -200,8 +206,10 @@ let power_user = {
let themes = [];
let movingUIPresets = [];
let instruct_presets = [];
let context_presets = [];
const storage_keys = {
ui_language: "language",
fast_ui_mode: "TavernAI_fast_ui_mode",
avatar_style: "TavernAI_avatar_style",
chat_display: "TavernAI_chat_display",
@ -661,6 +669,10 @@ function loadPowerUserSettings(settings, data) {
instruct_presets = data.instruct;
}
if (data.context !== undefined) {
context_presets = data.context;
}
// These are still local storage
const fastUi = localStorage.getItem(storage_keys.fast_ui_mode);
const movingUI = localStorage.getItem(storage_keys.movingUI);
@ -719,16 +731,10 @@ function loadPowerUserSettings(settings, data) {
$("#spoiler_free_mode").prop("checked", power_user.spoiler_free_mode);
$("#collapse-newlines-checkbox").prop("checked", power_user.collapse_newlines);
$("#pin-examples-checkbox").prop("checked", power_user.pin_examples);
$("#disable-description-formatting-checkbox").prop("checked", power_user.disable_description_formatting);
$("#disable-scenario-formatting-checkbox").prop("checked", power_user.disable_scenario_formatting);
$("#disable-personality-formatting-checkbox").prop("checked", power_user.disable_personality_formatting);
$("#always-force-name2-checkbox").prop("checked", power_user.always_force_name2);
$("#disable-examples-formatting-checkbox").prop("checked", power_user.disable_examples_formatting);
$('#disable-start-formatting-checkbox').prop("checked", power_user.disable_start_formatting);
$("#trim_sentences_checkbox").prop("checked", power_user.trim_sentences);
$("#include_newline_checkbox").prop("checked", power_user.include_newline);
$('#render_formulas').prop("checked", power_user.render_formulas);
$("#custom_chat_separator").val(power_user.custom_chat_separator);
$("#markdown_escape_strings").val(power_user.markdown_escape_strings);
$("#fast_ui_mode").prop("checked", power_user.fast_ui_mode);
$("#waifuMode").prop("checked", power_user.waifuMode);
@ -795,15 +801,14 @@ function loadPowerUserSettings(settings, data) {
$(`#character_sort_order option[data-order="${power_user.sort_order}"][data-field="${power_user.sort_field}"]`).prop("selected", true);
sortCharactersList();
reloadMarkdownProcessor(power_user.render_formulas);
loadInstructMode();
loadContextSettings();
loadMaxContextUnlocked();
switchWaifuMode();
switchSpoilerMode();
loadMovingUIState();
loadCharListState();
}
async function loadCharListState() {
@ -866,6 +871,61 @@ function switchMaxContextSize() {
}
}
function loadContextSettings() {
const controls = [
{ id: "context_story_string", property: "story_string", isCheckbox: false },
{ id: "context_example_separator", property: "example_separator", isCheckbox: false },
{ id: "context_chat_start", property: "chat_start", isCheckbox: false },
];
controls.forEach(control => {
const $element = $(`#${control.id}`);
if (control.isCheckbox) {
$element.prop('checked', power_user.context[control.property]);
} else {
$element.val(power_user.context[control.property]);
}
$element.on('input', function () {
power_user.context[control.property] = control.isCheckbox ? !!$(this).prop('checked') : $(this).val();
saveSettingsDebounced();
});
});
context_presets.forEach((preset) => {
const name = preset.name;
const option = document.createElement('option');
option.value = name;
option.innerText = name;
option.selected = name === power_user.context.preset;
$('#context_presets').append(option);
});
$('#context_presets').on('change', function () {
const name = $(this).find(':selected').val();
const preset = context_presets.find(x => x.name === name);
if (!preset) {
return;
}
power_user.context.preset = name;
controls.forEach(control => {
if (preset[control.property] !== undefined) {
power_user.context[control.property] = preset[control.property];
const $element = $(`#${control.id}`);
if (control.isCheckbox) {
$element.prop('checked', power_user.context[control.property]).trigger('input');
} else {
$element.val(power_user.context[control.property]).trigger('input');
}
}
});
});
}
function loadInstructMode() {
const controls = [
{ id: "instruct_enabled", property: "enabled", isCheckbox: true },
@ -879,6 +939,7 @@ function loadInstructMode() {
{ id: "instruct_names", property: "names", isCheckbox: true },
{ id: "instruct_macro", property: "macro", isCheckbox: true },
{ id: "instruct_names_force_groups", property: "names_force_groups", isCheckbox: true },
{ id: "instruct_last_output_sequence", property: "last_output_sequence", isCheckbox: false },
];
if (power_user.instruct.names_force_groups === undefined) {
@ -975,6 +1036,20 @@ export function fuzzySearchGroups(searchValue) {
return ids;
}
export function renderStoryString(params) {
try {
const compiledTemplate = Handlebars.compile(power_user.context.story_string, { noEscape: true });
let output = compiledTemplate(params);
output = substituteParams(output, params.user, params.char);
output = `${output.trim()}\n`; // add a newline to the end
return output;
} catch (e) {
toastr.error('Check the story string template for validity', 'Error rendering story string');
console.error('Error rendering story string', e);
throw e;
}
}
export function formatInstructModeChat(name, mes, isUser, isNarrator, forceAvatar, name1, name2) {
let includeNames = isNarrator ? false : power_user.instruct.names;
@ -991,8 +1066,8 @@ export function formatInstructModeChat(name, mes, isUser, isNarrator, forceAvata
const separator = power_user.instruct.wrap ? '\n' : '';
const separatorSequence = power_user.instruct.separator_sequence && !isUser
? power_user.instruct.separator_sequence
: (power_user.instruct.wrap ? '\n' : '');
const textArray = includeNames ? [sequence, `${name}: ${mes}`, separatorSequence] : [sequence, mes, separatorSequence];
: separator;
const textArray = includeNames ? [sequence, `${name}: ${mes}` + separatorSequence] : [sequence, mes + separatorSequence];
const text = textArray.filter(x => x).join(separator);
return text;
}
@ -1010,7 +1085,8 @@ export function formatInstructStoryString(story, systemPrompt) {
export function formatInstructModePrompt(name, isImpersonate, promptBias, name1, name2) {
const includeNames = power_user.instruct.names || (!!selected_group && power_user.instruct.names_force_groups);
let sequence = isImpersonate ? power_user.instruct.input_sequence : power_user.instruct.output_sequence;
const getOutputSequence = () => power_user.instruct.last_output_sequence || power_user.instruct.output_sequence;
let sequence = isImpersonate ? power_user.instruct.input_sequence : getOutputSequence();
if (power_user.instruct.macro) {
sequence = substituteParams(sequence, name1, name2);
@ -1049,46 +1125,13 @@ const compareFunc = (first, second) => {
}
};
function sortCharactersList() {
const arr1 = groups.map(x => ({
item: x,
id: x.id,
selector: '.group_select',
attribute: 'grid',
}))
const arr2 = characters.map((x, index) => ({
item: x,
id: index,
selector: '.character_select',
attribute: 'chid',
}));
const array = [...arr1, ...arr2];
if (power_user.sort_field == undefined || array.length === 0) {
function sortEntitiesList(entities) {
if (power_user.sort_field == undefined || entities.length === 0) {
return;
}
let orderedList = array.slice().sort((a, b) => sortFunc(a.item, b.item));
for (const item of array) {
$(`${item.selector}[${item.attribute}="${item.id}"]`).css({ 'order': orderedList.indexOf(item) });
}
updateVisibleDivs('#rm_print_characters_block', true);
entities.sort((a, b) => sortFunc(a.item, b.item));
}
function sortGroupMembers(selector) {
if (power_user.sort_field == undefined || characters.length === 0) {
return;
}
let orderedList = characters.slice().sort(sortFunc);
for (let i = 0; i < characters.length; i++) {
$(`${selector}[chid="${i}"]`).css({ 'order': orderedList.indexOf(characters[i]) });
}
}
async function saveTheme() {
const name = await callPopup('Enter a theme preset name:', 'input');
@ -1339,8 +1382,25 @@ function doResetPanels() {
$("#movingUIreset").trigger('click');
}
function addLanguagesToDropdown() {
$.getJSON('i18n.json', function (data) {
if (!Array.isArray(data?.lang)) {
return;
}
for (const lang of data.lang) {
const option = document.createElement('option');
option.value = lang;
option.innerText = lang;
$('#ui_language_select').append(option);
}
const selectedLanguage = localStorage.getItem(storage_keys.ui_language);
if (selectedLanguage) {
$('#ui_language_select').val(selectedLanguage);
}
});
}
function setAvgBG() {
const bgimg = new Image();
@ -1621,31 +1681,6 @@ $(document).ready(() => {
saveSettingsDebounced();
});
$("#disable-description-formatting-checkbox").change(function () {
power_user.disable_description_formatting = !!$(this).prop('checked');
saveSettingsDebounced();
})
$("#disable-scenario-formatting-checkbox").change(function () {
power_user.disable_scenario_formatting = !!$(this).prop('checked');
saveSettingsDebounced();
});
$("#disable-personality-formatting-checkbox").change(function () {
power_user.disable_personality_formatting = !!$(this).prop('checked');
saveSettingsDebounced();
});
$("#disable-examples-formatting-checkbox").change(function () {
power_user.disable_examples_formatting = !!$(this).prop('checked');
saveSettingsDebounced();
})
$("#disable-start-formatting-checkbox").change(function () {
power_user.disable_start_formatting = !!$(this).prop('checked');
saveSettingsDebounced();
});
// include newline is the child of trim sentences
// if include newline is checked, trim sentences must be checked
// if trim sentences is unchecked, include newline must be unchecked
@ -1672,12 +1707,6 @@ $(document).ready(() => {
saveSettingsDebounced();
});
$("#custom_chat_separator").on('input', function () {
power_user.custom_chat_separator = $(this).val();
saveSettingsDebounced();
reloadMarkdownProcessor(power_user.render_formulas);
});
$("#markdown_escape_strings").on('input', function () {
power_user.markdown_escape_strings = $(this).val();
saveSettingsDebounced();
@ -1834,6 +1863,7 @@ $(document).ready(() => {
power_user.never_resize_avatars = !!$(this).prop('checked');
saveSettingsDebounced();
});
$("#show_card_avatar_urls").on('input', function () {
power_user.show_card_avatar_urls = !!$(this).prop('checked');
printCharacters();
@ -1859,8 +1889,7 @@ $(document).ready(() => {
power_user.sort_field = $(this).find(":selected").data('field');
power_user.sort_order = $(this).find(":selected").data('order');
power_user.sort_rule = $(this).find(":selected").data('rule');
sortCharactersList();
favsToHotswap();
printCharacters();
saveSettingsDebounced();
});
@ -1972,12 +2001,6 @@ $(document).ready(() => {
saveSettingsDebounced();
});
/* $("#removeXML").on("input", function () {
power_user.removeXML = !!$(this).prop('checked');
reloadCurrentChat();
saveSettingsDebounced();
}); */
$("#token_padding").on("input", function () {
power_user.token_padding = Number($(this).val());
saveSettingsDebounced();
@ -2090,6 +2113,18 @@ $(document).ready(() => {
saveSettingsDebounced();
});
$('#ui_language_select').on('change', async function () {
const language = $(this).val();
if (language) {
localStorage.setItem(storage_keys.ui_language, language);
} else {
localStorage.removeItem(storage_keys.ui_language);
}
location.reload();
});
$(window).on('focus', function () {
browser_has_focus = true;
});
@ -2105,4 +2140,5 @@ $(document).ready(() => {
registerSlashCommand('cut', doMesCut, [], ' <span class="monospace">(requred number)</span> cuts the specified message from the chat', true, true);
registerSlashCommand('resetpanels', doResetPanels, ['resetui'], ' resets UI panels to original state.', true, true);
registerSlashCommand('bgcol', setAvgBG, [], ' WIP test of auto-bg avg coloring', true, true);
addLanguagesToDropdown();
});

View File

@ -116,6 +116,12 @@ class PresetManager {
<h3>Preset name:</h3>
<h4>Hint: Use a character/group name to bind preset to a specific chat.</h4>`;
const name = await callPopup(popupText, "input");
if (!name) {
console.log('Preset name not provided');
return;
}
await this.savePreset(name);
toastr.success('Preset saved');
}
@ -214,7 +220,16 @@ class PresetManager {
}
}
const filteredKeys = ['preset', 'streaming_url', 'stopping_strings', 'use_stop_sequence'];
const filteredKeys = [
'preset',
'streaming_url',
'stopping_strings',
'use_stop_sequence',
'preset_settings_novel',
'streaming_novel',
'nai_preamble',
'model_novel',
];
const settings = Object.assign({}, getSettingsByApiId(this.apiId));
for (const key of filteredKeys) {

View File

@ -8,8 +8,12 @@ export const markdownExclusionExt = () => {
}
let combinedExcludeString = '';
if (power_user.custom_chat_separator) {
combinedExcludeString += `${power_user.custom_chat_separator},`;
if (power_user.context.chat_start) {
combinedExcludeString += `${power_user.context.chat_start},`;
}
if (power_user.context.example_separator) {
combinedExcludeString += `${power_user.context.example_separator},`;
}
if (power_user.markdown_escape_strings) {

View File

@ -4,19 +4,19 @@ import {
this_chid,
callPopup,
menu_type,
updateVisibleDivs,
getCharacters,
updateCharacterCount,
entitiesFilter,
} from "../script.js";
import { FILTER_TYPES } from "./filters.js";
import { selected_group } from "./group-chats.js";
import { groupCandidatesFilter, selected_group } from "./group-chats.js";
import { uuidv4 } from "./utils.js";
export {
tags,
tag_map,
loadTagsSettings,
printTagFilters,
isElementTagged,
getTagsList,
appendTagToList,
createTagMapFromList,
@ -24,19 +24,12 @@ export {
importTags,
};
const random_id = () => Math.round(Date.now() * Math.random()).toString();
const TAG_LOGIC_AND = true; // switch to false to use OR logic for combining tags
const CHARACTER_SELECTOR = '#rm_print_characters_block > div';
const GROUP_MEMBER_SELECTOR = '#rm_group_add_members > div';
const random_id = () => uuidv4();
const CHARACTER_FILTER_SELECTOR = '#rm_characters_block .rm_tag_filter';
const GROUP_FILTER_SELECTOR = '#rm_group_chats_block .rm_tag_filter';
function getCharacterSelector(listSelector) {
if ($(listSelector).is(GROUP_FILTER_SELECTOR)) {
return GROUP_MEMBER_SELECTOR;
}
return CHARACTER_SELECTOR;
function getFilterHelper(listSelector) {
return $(listSelector).is(GROUP_FILTER_SELECTOR) ? groupCandidatesFilter : entitiesFilter;
}
export const tag_filter_types = {
@ -67,37 +60,20 @@ const DEFAULT_TAGS = [
let tags = [];
let tag_map = {};
function applyFavFilter(characterSelector) {
function applyFavFilter(filterHelper) {
const isSelected = $(this).hasClass('selected');
const displayFavoritesOnly = !isSelected;
$(this).toggleClass('selected', displayFavoritesOnly);
$(characterSelector).removeClass('hiddenByFav');
$(characterSelector).each(function () {
if (displayFavoritesOnly) {
if ($(this).find(".ch_fav").length !== 0) {
const shouldBeDisplayed = $(this).find(".ch_fav").val().toLowerCase().includes(true);
$(this).toggleClass('hiddenByFav', !shouldBeDisplayed);
}
}
});
updateCharacterCount(characterSelector);
updateVisibleDivs('#rm_print_characters_block', true);
filterHelper.setFilterData(FILTER_TYPES.FAV, displayFavoritesOnly);
}
function filterByGroups(characterSelector) {
function filterByGroups(filterHelper) {
const isSelected = $(this).hasClass('selected');
const displayGroupsOnly = !isSelected;
$(this).toggleClass('selected', displayGroupsOnly);
$(characterSelector).removeClass('hiddenByGroup');
$(characterSelector).each((_, element) => {
$(element).toggleClass('hiddenByGroup', displayGroupsOnly && !$(element).hasClass('group_select'));
});
updateCharacterCount(characterSelector);
updateVisibleDivs('#rm_print_characters_block', true);
filterHelper.setFilterData(FILTER_TYPES.GROUP, displayGroupsOnly);
}
function loadTagsSettings(settings) {
@ -290,7 +266,6 @@ function appendTagToList(listElement, tag, { removable, selectable, action, isGe
return;
}
const characterSelector = getCharacterSelector($(listElement));
let tagElement = $('#tag_template .tag').clone();
tagElement.attr('id', tag.id);
@ -315,11 +290,12 @@ function appendTagToList(listElement, tag, { removable, selectable, action, isGe
}
if (selectable) {
tagElement.on('click', () => onTagFilterClick.bind(tagElement)(listElement, characterSelector));
tagElement.on('click', () => onTagFilterClick.bind(tagElement)(listElement));
}
if (action) {
tagElement.on('click', () => action.bind(tagElement)(characterSelector));
const filter = getFilterHelper($(listElement));
tagElement.on('click', () => action.bind(tagElement)(filter));
tagElement.addClass('actionable');
}
if (action && tag.id === 2) {
@ -329,7 +305,7 @@ function appendTagToList(listElement, tag, { removable, selectable, action, isGe
$(listElement).append(tagElement);
}
function onTagFilterClick(listElement, characterSelector) {
function onTagFilterClick(listElement) {
let excludeTag;
if ($(this).hasClass('selected')) {
$(this).removeClass('selected');
@ -355,44 +331,10 @@ function onTagFilterClick(listElement, characterSelector) {
}
}
// TODO: Overhaul this somehow to use settings tag IDs instead
const tagIds = [...($(listElement).find(".tag.selected:not(.actionable)").map((_, el) => $(el).attr("id")))];
const excludedTagIds = [...($(listElement).find(".tag.excluded:not(.actionable)").map((_, el) => $(el).attr("id")))];
$(characterSelector).each((_, element) => applyFilterToElement(tagIds, excludedTagIds, element));
updateCharacterCount(characterSelector);
updateVisibleDivs('#rm_print_characters_block', true);
}
function applyFilterToElement(tagIds, excludedTagIds, element) {
const tagFlags = tagIds.map(tagId => isElementTagged(element, tagId));
const trueFlags = tagFlags.filter(x => x);
const isTagged = TAG_LOGIC_AND ? tagFlags.length === trueFlags.length : trueFlags.length > 0;
const excludedTagFlags = excludedTagIds.map(tagId => isElementTagged(element, tagId));
const isExcluded = excludedTagFlags.includes(true);
if (isExcluded) {
$(element).addClass('hiddenByTag');
} else if (tagIds.length > 0 && !isTagged) {
$(element).addClass('hiddenByTag');
} else {
$(element).removeClass('hiddenByTag');
}
}
function isElementTagged(element, tagId) {
const isGroup = $(element).hasClass('group_select');
const isCharacter = $(element).hasClass('character_select') || $(element).hasClass('group_member');
const idAttr = isGroup ? 'grid' : 'chid';
const elementId = $(element).attr(idAttr);
const lookupValue = isCharacter ? characters[elementId].avatar : elementId;
const isTagged = Array.isArray(tag_map[lookupValue]) && tag_map[lookupValue].includes(tagId);
return isTagged;
}
function clearTagsFilter(characterSelector) {
$('.rm_tag_filter .tag').removeClass('selected');
$(characterSelector).removeClass('hiddenByTag');
const filterHelper = getFilterHelper($(listElement));
filterHelper.setFilterData(FILTER_TYPES.TAG, { excluded: excludedTagIds, selected: tagIds });
}
function printTagFilters(type = tag_filter_types.character) {

View File

@ -8,6 +8,11 @@ export function isDigitsOnly(str) {
return /^\d+$/.test(str);
}
// Increase delay on touch screens
export function getSortableDelay() {
return navigator.maxTouchPoints > 0 ? 750 : 100;
}
export function shuffle(array) {
let currentIndex = array.length,
randomIndex;

View File

@ -1,5 +1,5 @@
import { saveSettings, callPopup, substituteParams, getTokenCount, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types } from "../script.js";
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, delay, getCharaFilename, deepClone } from "./utils.js";
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, deepClone, getSortableDelay, escapeRegex } from "./utils.js";
import { getContext } from "./extensions.js";
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from "./authors-note.js";
import { registerSlashCommand } from "./slash-commands.js";
@ -302,6 +302,7 @@ function displayWorldEntries(name, data) {
}
$("#world_popup_entries_list").sortable({
delay: getSortableDelay(),
handle: ".drag-handle",
stop: async function (event, ui) {
$('#world_popup_entries_list .world_entry').each(function (index) {
@ -1129,7 +1130,7 @@ function matchKeys(haystack, needle) {
return haystack.includes(transformedString);
}
else {
const regex = new RegExp(`\\b${transformedString}\\b`);
const regex = new RegExp(`\\b${escapeRegex(transformedString)}\\b`);
if (regex.test(haystack)) {
return true;
}

View File

@ -1270,18 +1270,18 @@ input[type="file"] {
filter: brightness(150%);
}
#rm_character_count {
padding: 5px;
font-size: calc(var(--mainFontSize) * .8);
font-weight: bold;
#rm_print_characters_pagination {
display: flex;
flex-direction: row;
gap: 5px;
justify-content: center;
}
#rm_print_characters_block {
/* padding: 5px 0; */
overflow-y: auto;
flex-grow: 1;
display: flex;
/* row-gap: 5px; */
height: 100%;
}
body.charListGrid #rm_print_characters_block {
@ -1501,6 +1501,7 @@ select option:not(:checked) {
display: flex;
overflow-y: auto;
flex-direction: column;
height: 100%;
}
#rm_characters_block .right_menu_button {
@ -2216,7 +2217,7 @@ grammarly-extension {
background-color: red;
}
#kobold_order {
.prompt_order {
display: flex;
flex-direction: column;
align-items: center;
@ -2226,7 +2227,7 @@ grammarly-extension {
padding: 5px;
}
#kobold_order>div {
.prompt_order>div {
padding: 5px;
padding-left: 30px;
width: 100%;
@ -2241,18 +2242,40 @@ grammarly-extension {
display: flex;
align-items: center;
justify-content: space-between;
column-gap: 10px;
}
#kobold_order>div:hover {
.prompt_order>div>span:first-child {
flex-grow: 1;
}
.prompt_order>div:hover {
background-color: var(--grey30a);
}
#kobold_order>div::after {
.prompt_order>div::after {
content: "☰";
left: 8px;
position: absolute;
}
.prompt_order .disabled {
opacity: 0.5;
filter: grayscale(0.5);
}
.prompt_order .toggle_button {
padding-right: 0;
}
.prompt_order .toggle_button::after {
content: '☑';
}
.prompt_order .disabled .toggle_button::after {
content: '☐';
}
/* ------ online status indicators and texts. 2 = kobold AI, 3 = Novel AI ----------*/
#online_status2,
#online_status_horde,
@ -3211,6 +3234,12 @@ h5 {
display: none;
}
.group_pagination {
display: flex;
justify-content: center;
align-items: center;
}
#rm_group_chats_block .tag.filterByGroups {
display: none;
}
@ -4568,6 +4597,41 @@ toolcool-color-picker {
height: 100%;
}
.novelai_logit_bias_form {
display: flex;
flex-direction: row;
column-gap: 10px;
align-items: center;
}
.novelai_logit_bias_text,
.novelai_logit_bias_value {
flex: 1;
}
.novelai_logit_bias_list {
display: flex;
flex-direction: column;
gap: 10px;
}
.novelai_logit_bias_list:empty {
width: 100%;
height: 100%;
}
.novelai_logit_bias_list:empty::before {
display: flex;
align-items: center;
justify-content: center;
content: "No items";
font-weight: bolder;
width: 100%;
height: 100%;
opacity: 0.8;
min-height: 2.5rem;
}
/*used to fix smallness of certain FontAwesome glyph which break button squareness*/
/*currently used on: CharList Import*/
@ -5559,3 +5623,77 @@ body.waifuMode .zoomed_avatar {
vertical-align: middle;
/* To align with adjacent text */
}
.paginationjs {
display: flex;
align-items: center;
flex-direction: row;
}
/* Pagination */
.paginationsjs-pages {
margin: 0.5em 0;
display: flex;
justify-content: center;
align-items: center;
}
.paginationjs-pages ul {
list-style-type: none;
margin: 0.25em;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
gap: 5px;
}
.paginationjs-size-changer select {
width: unset;
margin: 0;
}
.paginationjs-pages ul li a {
padding: 0.05em 0.5em;
text-decoration: none;
color: var(--SmartThemeBodyColor);
border: 1px solid var(--white30a);
border-radius: 5px;
transition: opacity 0.2s;
opacity: 0.8;
cursor: pointer;
}
.paginationjs-pages ul li a:hover {
opacity: 1;
}
.paginationjs-pages ul li.active a {
color: var(--SmartThemeQuoteColor);
border-color: var(--SmartThemeQuoteColor);
opacity: 1;
}
.paginationjs-pages ul li.disabled a {
opacity: 0.5;
cursor: not-allowed;
}
.paginationjs-nav {
padding: 5px;
font-size: calc(var(--mainFontSize) * .8);
font-weight: bold;
}
#select_chat_search {
background-color: transparent;
border: none;
outline: none;
color: white;
display: inline-block; /* Change display to inline-block */
vertical-align: middle; /* Align to middle if there's a height discrepancy */
width: 200px;
font-size: 16px;
z-index: 10;
margin-left: 10px; /* Give some space between the button and search box */
}

161
server.js
View File

@ -68,6 +68,7 @@ app.use(compression());
app.use(responseTime());
const fs = require('fs');
const writeFileAtomicSync = require('write-file-atomic').sync;
const readline = require('readline');
const open = require('open');
@ -551,6 +552,7 @@ app.post("/generate", jsonParser, async function (request, response_generate = r
}
const data = await response.json();
console.log("Endpoint response:", data);
return response_generate.send(data);
}
} catch (error) {
@ -600,18 +602,14 @@ app.post("/generate_textgenerationwebui", jsonParser, async function (request, r
const websocket = new WebSocket(streamingUrl);
websocket.on('open', async function () {
console.log('websocket open');
websocket.send(JSON.stringify(request.body));
});
websocket.on('error', (err) => {
console.error(err);
websocket.close();
console.log('WebSocket opened');
const combined_args = Object.assign(request.body.use_mancer ? get_mancer_headers() : {}, request.body);
websocket.send(JSON.stringify(combined_args));
});
websocket.on('close', (code, buffer) => {
const reason = new TextDecoder().decode(buffer)
console.log(reason);
console.log("WebSocket closed (reason: %o)", reason);
});
while (true) {
@ -620,8 +618,27 @@ app.post("/generate_textgenerationwebui", jsonParser, async function (request, r
websocket.close();
return;
}
let rawMessage = null;
try {
// This lunacy is because the websocket can fail to connect AFTER we're awaiting 'message'... so 'message' never triggers.
// So instead we need to look for 'error' at the same time to reject the promise. And then remove the listener if we resolve.
// This is awful.
// Welcome to the shenanigan shack.
rawMessage = await new Promise(function (resolve, reject) {
websocket.once('error', reject);
websocket.once('message', (data, isBinary) => {
websocket.removeListener('error', reject);
resolve(data, isBinary);
});
});
} catch(err) {
console.error("Socket error:", err);
websocket.close();
yield "[SillyTavern] Streaming failed:\n" + err;
return;
}
const rawMessage = await new Promise(resolve => websocket.once('message', resolve));
const message = json5.parse(rawMessage);
switch (message.event) {
@ -672,11 +689,11 @@ app.post("/generate_textgenerationwebui", jsonParser, async function (request, r
try {
const data = await postAsync(api_server + "/v1/generate", args);
console.log(data);
console.log("Endpoint response:", data);
return response_generate.send(data);
} catch (error) {
retval = { error: true, status: error.status, response: error.statusText };
console.log(error);
console.log("Endpoint error:", error);
try {
retval.response = await error.json();
retval.response = retval.response.result;
@ -692,7 +709,7 @@ app.post("/savechat", jsonParser, function (request, response) {
var dir_name = String(request.body.avatar_url).replace('.png', '');
let chat_data = request.body.chat;
let jsonlData = chat_data.map(JSON.stringify).join('\n');
fs.writeFileSync(`${chatsPath + sanitize(dir_name)}/${sanitize(String(request.body.file_name))}.jsonl`, jsonlData, 'utf8');
writeFileAtomicSync(`${chatsPath + sanitize(dir_name)}/${sanitize(String(request.body.file_name))}.jsonl`, jsonlData, 'utf8');
return response.send({ result: "ok" });
} catch (error) {
response.send(error);
@ -1217,7 +1234,7 @@ async function charaWrite(img_url, data, target_img, response = undefined, mes =
chunks.splice(-1, 0, PNGtext.encode('chara', base64EncodedData));
//chunks.splice(-1, 0, text.encode('lorem', 'ipsum'));
fs.writeFileSync(charactersPath + target_img + '.png', new Buffer.from(encode(chunks)));
writeFileAtomicSync(charactersPath + target_img + '.png', new Buffer.from(encode(chunks)));
if (response !== undefined) response.send(mes);
return true;
} catch (err) {
@ -1428,18 +1445,16 @@ app.post('/deleteuseravatar', jsonParser, function (request, response) {
});
app.post("/setbackground", jsonParser, function (request, response) {
var bg = "#bg1 {background-image: url('../backgrounds/" + request.body.bg + "');}";
fs.writeFile('public/css/bg_load.css', bg, 'utf8', function (err) {
if (err) {
response.send(err);
return console.log(err);
} else {
//response.redirect("/");
response.send({ result: 'ok' });
}
});
try {
const bg = `#bg1 {background-image: url('../backgrounds/${request.body.bg}');}`;
writeFileAtomicSync('public/css/bg_load.css', bg, 'utf8');
response.send({ result: 'ok' });
} catch (err) {
console.log(err);
response.send(err);
}
});
app.post("/delbackground", jsonParser, function (request, response) {
if (!request.body) return response.sendStatus(400);
@ -1530,25 +1545,13 @@ app.post("/downloadbackground", urlencodedParser, function (request, response) {
});
app.post("/savesettings", jsonParser, function (request, response) {
fs.writeFile('public/settings.json', JSON.stringify(request.body, null, 4), 'utf8', function (err) {
if (err) {
response.send(err);
console.log(err);
} else {
response.send({ result: "ok" });
}
});
/*fs.writeFile('public/settings.json', JSON.stringify(request.body), 'utf8', function (err) {
if (err) {
response.send(err);
return console.log(err);
//response.send(err);
} else {
//response.redirect("/");
response.send({ result: "ok" });
}
});*/
try {
writeFileAtomicSync('public/settings.json', JSON.stringify(request.body, null, 4), 'utf8');
response.send({ result: "ok" });
} catch (err) {
console.log(err);
response.send(err);
}
});
function getCharaCardV2(jsonObject) {
@ -1714,7 +1717,7 @@ app.post('/savetheme', jsonParser, (request, response) => {
}
const filename = path.join(directories.themes, sanitize(request.body.name) + '.json');
fs.writeFileSync(filename, JSON.stringify(request.body, null, 4), 'utf8');
writeFileAtomicSync(filename, JSON.stringify(request.body, null, 4), 'utf8');
return response.sendStatus(200);
});
@ -1725,7 +1728,7 @@ app.post('/savemovingui', jsonParser, (request, response) => {
}
const filename = path.join(directories.movingUI, sanitize(request.body.name) + '.json');
fs.writeFileSync(filename, JSON.stringify(request.body, null, 4), 'utf8');
writeFileAtomicSync(filename, JSON.stringify(request.body, null, 4), 'utf8');
return response.sendStatus(200);
});
@ -1736,7 +1739,7 @@ app.post('/savequickreply', jsonParser, (request, response) => {
}
const filename = path.join(directories.quickreplies, sanitize(request.body.name) + '.json');
fs.writeFileSync(filename, JSON.stringify(request.body, null, 4), 'utf8');
writeFileAtomicSync(filename, JSON.stringify(request.body, null, 4), 'utf8');
return response.sendStatus(200);
});
@ -1853,7 +1856,7 @@ app.post("/generate_novelai", jsonParser, async function (request, response_gene
const novelai = require('./src/novelai');
const isNewModel = (request.body.model.includes('clio') || request.body.model.includes('kayra'));
const isKrake = request.body.model.includes('krake');
const badWordsList = isNewModel ? novelai.badWordsList : (isKrake ? novelai.krakeBadWordsList : novelai.euterpeBadWordsList);
const badWordsList = (isNewModel ? novelai.badWordsList : (isKrake ? novelai.krakeBadWordsList : novelai.euterpeBadWordsList)).slice();
// Add customized bad words for Clio and Kayra
if (isNewModel && Array.isArray(request.body.bad_words_ids)) {
@ -1864,6 +1867,13 @@ app.post("/generate_novelai", jsonParser, async function (request, response_gene
}
}
// Add default biases for dinkus and asterism
const logit_bias_exp = isNewModel ? novelai.logitBiasExp.slice() : null;
if (Array.isArray(logit_bias_exp) && Array.isArray(request.body.logit_bias_exp)) {
logit_bias_exp.push(...request.body.logit_bias_exp);
}
const data = {
"input": request.body.input,
"model": request.body.model,
@ -1883,16 +1893,14 @@ app.post("/generate_novelai", jsonParser, async function (request, response_gene
"top_p": request.body.top_p,
"top_k": request.body.top_k,
"typical_p": request.body.typical_p,
"top_g": request.body.top_g,
"mirostat_lr": request.body.mirostat_lr,
"mirostat_tau": request.body.mirostat_tau,
"cfg_scale": request.body.cfg_scale,
"cfg_uc": request.body.cfg_uc,
"phrase_rep_pen": request.body.phrase_rep_pen,
"stop_sequences": request.body.stop_sequences,
//"stop_sequences": {{187}},
"bad_words_ids": badWordsList,
"logit_bias_exp": isNewModel ? novelai.logitBiasExp : null,
"logit_bias_exp": logit_bias_exp,
//generate_until_sentence = true;
"use_cache": request.body.use_cache,
"use_string": true,
@ -2325,7 +2333,7 @@ app.post("/exportcharacter", jsonParser, async function (request, response) {
},
};
const exifString = exif.dump(metadata);
fs.writeFileSync(metadataPath, exifString, 'binary');
writeFileAtomicSync(metadataPath, exifString, 'binary');
await webp.cwebp(filename, inputWebpPath, '-q 95');
await webp.webpmux_add(inputWebpPath, outputWebpPath, metadataPath, 'exif');
@ -2407,13 +2415,17 @@ app.post("/importchat", urlencodedParser, function (request, response) {
});
const errors = [];
newChats.forEach(chat => fs.writeFile(
`${chatsPath + avatar_url}/${ch_name} - ${humanizedISO8601DateTime()} imported.jsonl`,
chat.map(JSON.stringify).join('\n'),
'utf8',
(err) => err ?? errors.push(err)
)
);
for (const chat of newChats) {
const filePath = `${chatsPath + avatar_url}/${ch_name} - ${humanizedISO8601DateTime()} imported.jsonl`;
const fileContent = chat.map(tryParse).filter(x => x).join('\n');
try {
writeFileAtomicSync(filePath, fileContent, 'utf8');
} catch (err) {
errors.push(err);
}
}
if (0 < errors.length) {
response.send('Errors occurred while writing character files. Errors: ' + JSON.stringify(errors));
@ -2451,7 +2463,7 @@ app.post("/importchat", urlencodedParser, function (request, response) {
}
}
fs.writeFileSync(`${chatsPath + avatar_url}/${ch_name} - ${humanizedISO8601DateTime()} imported.jsonl`, chat.map(JSON.stringify).join('\n'), 'utf8');
writeFileAtomicSync(`${chatsPath + avatar_url}/${ch_name} - ${humanizedISO8601DateTime()} imported.jsonl`, chat.map(JSON.stringify).join('\n'), 'utf8');
response.send({ res: true });
} else {
@ -2520,7 +2532,7 @@ app.post('/importworldinfo', urlencodedParser, (request, response) => {
return response.status(400).send('World file must have a name');
}
fs.writeFileSync(pathToNewFile, fileContents);
writeFileAtomicSync(pathToNewFile, fileContents);
return response.send({ name: worldName });
});
@ -2544,7 +2556,7 @@ app.post('/editworldinfo', jsonParser, (request, response) => {
const filename = `${sanitize(request.body.name)}.json`;
const pathToFile = path.join(directories.worlds, filename);
fs.writeFileSync(pathToFile, JSON.stringify(request.body.data, null, 4));
writeFileAtomicSync(pathToFile, JSON.stringify(request.body.data, null, 4));
return response.send({ ok: true });
});
@ -2565,7 +2577,7 @@ app.post('/uploaduseravatar', urlencodedParser, async (request, response) => {
const filename = request.body.overwrite_name || `${Date.now()}.png`;
const pathToNewFile = path.join(directories.avatars, filename);
fs.writeFileSync(pathToNewFile, image);
writeFileAtomicSync(pathToNewFile, image);
fs.rmSync(pathToUpload);
return response.send({ path: filename });
} catch (err) {
@ -2642,7 +2654,7 @@ app.post('/creategroup', jsonParser, (request, response) => {
fs.mkdirSync(directories.groups);
}
fs.writeFileSync(pathToFile, fileData);
writeFileAtomicSync(pathToFile, fileData);
return response.send(groupMetadata);
});
@ -2654,7 +2666,7 @@ app.post('/editgroup', jsonParser, (request, response) => {
const pathToFile = path.join(directories.groups, `${id}.json`);
const fileData = JSON.stringify(request.body);
fs.writeFileSync(pathToFile, fileData);
writeFileAtomicSync(pathToFile, fileData);
return response.send({ ok: true });
});
@ -2708,7 +2720,7 @@ app.post('/savegroupchat', jsonParser, (request, response) => {
let chat_data = request.body.chat;
let jsonlData = chat_data.map(JSON.stringify).join('\n');
fs.writeFileSync(pathToFile, jsonlData, 'utf8');
writeFileAtomicSync(pathToFile, jsonlData, 'utf8');
return response.send({ ok: true });
});
@ -2907,7 +2919,7 @@ async function generateThumbnail(type, file) {
buffer = fs.readFileSync(pathToOriginalFile);
}
fs.writeFileSync(pathToCachedFile, buffer);
writeFileAtomicSync(pathToCachedFile, buffer);
}
catch (outer) {
return null;
@ -3468,7 +3480,7 @@ app.post("/save_preset", jsonParser, function (request, response) {
}
const fullpath = path.join(directory, filename);
fs.writeFileSync(fullpath, JSON.stringify(request.body.preset, null, 4), 'utf-8');
writeFileAtomicSync(fullpath, JSON.stringify(request.body.preset, null, 4), 'utf-8');
return response.send({ name });
});
@ -3503,7 +3515,7 @@ app.post("/savepreset_openai", jsonParser, function (request, response) {
const filename = `${name}.settings`;
const fullpath = path.join(directories.openAI_Settings, filename);
fs.writeFileSync(fullpath, JSON.stringify(request.body, null, 4), 'utf-8');
writeFileAtomicSync(fullpath, JSON.stringify(request.body, null, 4), 'utf-8');
return response.send({ name });
});
@ -3637,7 +3649,8 @@ const setupTasks = async function () {
console.log('Launching...');
if (autorun) open(autorunUrl.toString());
console.log('SillyTavern is listening on: ' + tavernUrl);
console.log('\x1b[32mSillyTavern is listening on: ' + tavernUrl + '\x1b[0m');
if (listen) {
console.log('\n0.0.0.0 means SillyTavern is listening on all network interfaces (Wi-Fi, LAN, localhost). If you want to limit it only to internal localhost (127.0.0.1), change the setting in config.conf to “listen=false”\n');
@ -3805,7 +3818,7 @@ function migrateSecrets() {
if (modified) {
console.log('Writing updated settings.json...');
const settingsContent = JSON.stringify(settings);
fs.writeFileSync(SETTINGS_FILE, settingsContent, "utf-8");
writeFileAtomicSync(SETTINGS_FILE, settingsContent, "utf-8");
}
}
catch (error) {
@ -4176,7 +4189,7 @@ app.post('/upload_sprite_pack', urlencodedParser, async (request, response) => {
// Write sprite buffer to disk
const pathToSprite = path.join(spritesPath, filename);
fs.writeFileSync(pathToSprite, buffer);
writeFileAtomicSync(pathToSprite, buffer);
}
// Remove uploaded ZIP file
@ -4401,7 +4414,7 @@ function importRisuSprites(data) {
const filename = label + '.png';
const pathToFile = path.join(spritesPath, filename);
fs.writeFileSync(pathToFile, fileBase64, { encoding: 'base64' });
writeFileAtomicSync(pathToFile, fileBase64, { encoding: 'base64' });
}
// Remove additionalAssets and emotions from data (they are now in the sprites folder)
@ -4415,13 +4428,13 @@ function importRisuSprites(data) {
function writeSecret(key, value) {
if (!fs.existsSync(SECRETS_FILE)) {
const emptyFile = JSON.stringify({});
fs.writeFileSync(SECRETS_FILE, emptyFile, "utf-8");
writeFileAtomicSync(SECRETS_FILE, emptyFile, "utf-8");
}
const fileContents = fs.readFileSync(SECRETS_FILE);
const secrets = JSON.parse(fileContents);
secrets[key] = value;
fs.writeFileSync(SECRETS_FILE, JSON.stringify(secrets), "utf-8");
writeFileAtomicSync(SECRETS_FILE, JSON.stringify(secrets), "utf-8");
}
function readSecret(key) {

View File

@ -9,7 +9,8 @@
const fs = require("fs");
const path = require("path");
const util = require("util");
const writeFile = util.promisify(fs.writeFile);
const writeFileAtomic = require("write-file-atomic");
const writeFile = util.promisify(writeFileAtomic);
const readFile = util.promisify(fs.readFile);
const readdir = util.promisify(fs.readdir);
const crypto = require("crypto");