Merge branch 'staging' into feat/xtc

4dbed09505ee2f6c24a7acafb7b50304a3fd02c3

Cohee <18619528+Cohee1207@users.noreply.github.com>

72 files changed, +985 -688Showing whitespace changes
.github/readme.md+1 -2
@@ -246,7 +246,6 @@ You will need two mandatory directory mappings and a port mapping to allow Silly
246246
247##### Additional Settings247##### Additional Settings
248248
249- [TimeZone] - The timezone your instance should use. This is useful for making logs match your local time for easier troubleshooting. Use your TZ Identifier. (https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
250- [DockerNet] - The docker network that the container should be created with a connection to. If you don't know what it is, see the [official Docker documentation](https://docs.docker.com/reference/cli/docker/network/).249- [DockerNet] - The docker network that the container should be created with a connection to. If you don't know what it is, see the [official Docker documentation](https://docs.docker.com/reference/cli/docker/network/).
251- [version] - On the right-hand side of this GitHub page, you'll see "Packages". Select the "sillytavern" package and you'll see the image versions. The image tag "latest" will keep you up-to-date with the current release. You can also utilize "staging" and "release" tags that point to the nightly images of the respective branches, but this may not be appropriate, if you are utilizing extensions that could be broken, and may need time to update.250- [version] - On the right-hand side of this GitHub page, you'll see "Packages". Select the "sillytavern" package and you'll see the image versions. The image tag "latest" will keep you up-to-date with the current release. You can also utilize "staging" and "release" tags that point to the nightly images of the respective branches, but this may not be appropriate, if you are utilizing extensions that could be broken, and may need time to update.
252251
@@ -255,7 +254,7 @@ You will need two mandatory directory mappings and a port mapping to allow Silly
2551. Open your Command Line2541. Open your Command Line
2562. Run the following command2552. Run the following command
257256
258`docker create --name='sillytavern' --net='[DockerNet]' -e TZ="[TimeZone]" -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' 'ghcr.io/sillytavern/sillytavern:[version]'`257`docker create --name='sillytavern' --net='[DockerNet]' -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' 'ghcr.io/sillytavern/sillytavern:[version]'`
259258
260> Note that 8000 is a default listening port. Don't forget to use an appropriate port if you change it in the config.259> Note that 8000 is a default listening port. Don't forget to use an appropriate port if you change it in the config.
261260
Dockerfile+1 -1
@@ -1,4 +1,4 @@
1FROM node:lts-alpine3.181FROM node:lts-alpine3.19
22
3# Arguments3# Arguments
4ARG APP_HOME=/home/node/app4ARG APP_HOME=/home/node/app
default/config.yaml+4 -2
@@ -90,9 +90,11 @@ whitelistImportDomains:
90## headers:90## headers:
91## User-Agent: "Googlebot/2.1 (+http://www.google.com/bot.html)"91## User-Agent: "Googlebot/2.1 (+http://www.google.com/bot.html)"
92requestOverrides: []92requestOverrides: []
93# -- PLUGIN CONFIGURATION --93# -- EXTENSIONS CONFIGURATION --
94# Enable UI extensions94# Enable UI extensions
95enableExtensions: true95enableExtensions: true
96# Automatically update extensions when a release version changes
97enableExtensionsAutoUpdate: true
96# Extension settings98# Extension settings
97extras:99extras:
98 # Disables automatic model download from HuggingFace100 # Disables automatic model download from HuggingFace
@@ -126,7 +128,7 @@ ollama:
126 # Controls how long the model will stay loaded into memory following the request128 # Controls how long the model will stay loaded into memory following the request
127 # * -1: Keep the model loaded indefinitely129 # * -1: Keep the model loaded indefinitely
128 # * 0: Unload the model immediately after the request130 # * 0: Unload the model immediately after the request
129 # * 5m: Keep the model loaded for 5 minutes after the request. Accepts duration strings (e.g. 5h30m40s)131 # * N (any positive number): Keep the model loaded for N seconds after the request.
130 keepAlive: -1132 keepAlive: -1
131# -- ANTHROPIC CLAUDE API CONFIGURATION --133# -- ANTHROPIC CLAUDE API CONFIGURATION --
132claude:134claude:
default/content/presets/openai/Default.json+1 -4
@@ -8,7 +8,7 @@
8 "openrouter_force_instruct": false,8 "openrouter_force_instruct": false,
9 "openrouter_group_models": false,9 "openrouter_group_models": false,
10 "openrouter_sort_models": "alphabetically",10 "openrouter_sort_models": "alphabetically",
11 "ai21_model": "j2-ultra",11 "ai21_model": "jamba-1.5-large",
12 "mistralai_model": "mistral-large-latest",12 "mistralai_model": "mistral-large-latest",
13 "custom_model": "",13 "custom_model": "",
14 "custom_url": "",14 "custom_url": "",
@@ -19,7 +19,6 @@
19 "temperature": 1,19 "temperature": 1,
20 "frequency_penalty": 0,20 "frequency_penalty": 0,
21 "presence_penalty": 0,21 "presence_penalty": 0,
22 "count_penalty": 0,
23 "top_p": 1,22 "top_p": 1,
24 "top_k": 0,23 "top_k": 0,
25 "top_a": 0,24 "top_a": 0,
@@ -233,8 +232,6 @@
233 "assistant_prefill": "",232 "assistant_prefill": "",
234 "assistant_impersonation": "",233 "assistant_impersonation": "",
235 "human_sysprompt_message": "Let's get started. Please generate your response based on the information and instructions provided above.",234 "human_sysprompt_message": "Let's get started. Please generate your response based on the information and instructions provided above.",
236 "use_ai21_tokenizer": false,
237 "use_google_tokenizer": false,
238 "claude_use_sysprompt": false,235 "claude_use_sysprompt": false,
239 "use_alt_scale": false,236 "use_alt_scale": false,
240 "squash_system_messages": false,237 "squash_system_messages": false,
default/content/settings.json+12 -5
@@ -142,6 +142,7 @@
142 "timestamps_enabled": true,142 "timestamps_enabled": true,
143 "timestamp_model_icon": true,143 "timestamp_model_icon": true,
144 "mesIDDisplay_enabled": false,144 "mesIDDisplay_enabled": false,
145 "hideChatAvatars_enabled": false,
145 "max_context_unlocked": false,146 "max_context_unlocked": false,
146 "prefer_character_prompt": true,147 "prefer_character_prompt": true,
147 "prefer_character_jailbreak": true,148 "prefer_character_jailbreak": true,
@@ -195,7 +196,15 @@
195 "enableLabMode": false,196 "enableLabMode": false,
196 "enableZenSliders": false,197 "enableZenSliders": false,
197 "ui_mode": 1,198 "ui_mode": 1,
198 "forbid_external_media": true199 "forbid_external_media": true,
200 "stscript": {
201 "parser": {
202 "flags": {
203 "1": true,
204 "2": true
205 }
206 }
207 }
199 },208 },
200 "extension_settings": {209 "extension_settings": {
201 "apiUrl": "http://localhost:5100",210 "apiUrl": "http://localhost:5100",
@@ -451,7 +460,6 @@
451 "temp_openai": 1.0,460 "temp_openai": 1.0,
452 "freq_pen_openai": 0,461 "freq_pen_openai": 0,
453 "pres_pen_openai": 0,462 "pres_pen_openai": 0,
454 "count_pen": 0,
455 "top_p_openai": 1,463 "top_p_openai": 1,
456 "top_k_openai": 0,464 "top_k_openai": 0,
457 "stream_openai": true,465 "stream_openai": true,
@@ -613,7 +621,7 @@
613 "wi_format": "{0}",621 "wi_format": "{0}",
614 "openai_model": "gpt-4-turbo",622 "openai_model": "gpt-4-turbo",
615 "claude_model": "claude-3-5-sonnet-20240620",623 "claude_model": "claude-3-5-sonnet-20240620",
616 "ai21_model": "j2-ultra",624 "ai21_model": "jamba-1.5-large",
617 "windowai_model": "",625 "windowai_model": "",
618 "openrouter_model": "OR_Website",626 "openrouter_model": "OR_Website",
619 "jailbreak_system": true,627 "jailbreak_system": true,
@@ -624,7 +632,6 @@
624 "show_external_models": false,632 "show_external_models": false,
625 "proxy_password": "",633 "proxy_password": "",
626 "assistant_prefill": "",634 "assistant_prefill": "",
627 "assistant_impersonation": "",635 "assistant_impersonation": ""
628 "use_ai21_tokenizer": false
629 }636 }
630}637}
default/content/themes/Azure.json+1 -0
@@ -23,6 +23,7 @@
23 "timestamps_enabled": true,23 "timestamps_enabled": true,
24 "timestamp_model_icon": false,24 "timestamp_model_icon": false,
25 "mesIDDisplay_enabled": true,25 "mesIDDisplay_enabled": true,
26 "hideChatAvatars_enabled": false,
26 "message_token_count_enabled": false,27 "message_token_count_enabled": false,
27 "expand_message_actions": false,28 "expand_message_actions": false,
28 "enableZenSliders": false,29 "enableZenSliders": false,
default/content/themes/Cappuccino.json+1 -0
@@ -23,6 +23,7 @@
23 "timestamps_enabled": true,23 "timestamps_enabled": true,
24 "timestamp_model_icon": true,24 "timestamp_model_icon": true,
25 "mesIDDisplay_enabled": true,25 "mesIDDisplay_enabled": true,
26 "hideChatAvatars_enabled": false,
26 "message_token_count_enabled": false,27 "message_token_count_enabled": false,
27 "expand_message_actions": false,28 "expand_message_actions": false,
28 "enableZenSliders": false,29 "enableZenSliders": false,
default/content/themes/Dark Lite.json+1 -0
@@ -23,6 +23,7 @@
23 "timestamps_enabled": true,23 "timestamps_enabled": true,
24 "timestamp_model_icon": true,24 "timestamp_model_icon": true,
25 "mesIDDisplay_enabled": false,25 "mesIDDisplay_enabled": false,
26 "hideChatAvatars_enabled": false,
26 "message_token_count_enabled": false,27 "message_token_count_enabled": false,
27 "expand_message_actions": false,28 "expand_message_actions": false,
28 "enableZenSliders": "",29 "enableZenSliders": "",
default/content/themes/Dark V 1.0.json+0 -0
default/content/user-default.png+0 -0

Binary file

package-lock.json+2 -2
@@ -1,12 +1,12 @@
1{1{
2 "name": "sillytavern",2 "name": "sillytavern",
3 "version": "1.12.4",3 "version": "1.12.5",
4 "lockfileVersion": 3,4 "lockfileVersion": 3,
5 "requires": true,5 "requires": true,
6 "packages": {6 "packages": {
7 "": {7 "": {
8 "name": "sillytavern",8 "name": "sillytavern",
9 "version": "1.12.4",9 "version": "1.12.5",
10 "hasInstallScript": true,10 "hasInstallScript": true,
11 "license": "AGPL-3.0",11 "license": "AGPL-3.0",
12 "dependencies": {12 "dependencies": {
package.json+1 -1
@@ -71,7 +71,7 @@
71 "type": "git",71 "type": "git",
72 "url": "https://github.com/SillyTavern/SillyTavern.git"72 "url": "https://github.com/SillyTavern/SillyTavern.git"
73 },73 },
74 "version": "1.12.4",74 "version": "1.12.5",
75 "scripts": {75 "scripts": {
76 "start": "node server.js",76 "start": "node server.js",
77 "start:no-csrf": "node server.js --disableCsrf",77 "start:no-csrf": "node server.js --disableCsrf",
public/img/ai4.png+0 -0

Binary file

public/index.html+39 -46
@@ -5,7 +5,7 @@
5 <title>SillyTavern</title>5 <title>SillyTavern</title>
6 <base href="/">6 <base href="/">
7 <meta charset="utf-8">7 <meta charset="utf-8">
8 <meta name="viewport" content="width=device-width, viewport-fit=cover, initial-scale=1, maximum-scale=1.0, user-scalable=no">8 <meta name="viewport" content="width=device-width, viewport-fit=cover, initial-scale=1, maximum-scale=1.0, user-scalable=no, interactive-widget=resizes-content">
9 <meta name="apple-mobile-web-app-capable" content="yes">9 <meta name="apple-mobile-web-app-capable" content="yes">
10 <meta name="darkreader-lock">10 <meta name="darkreader-lock">
11 <meta name="robots" content="noindex, nofollow" />11 <meta name="robots" content="noindex, nofollow" />
@@ -436,7 +436,7 @@
436 </div>436 </div>
437 </div>437 </div>
438 </div>438 </div>
439 <div data-newbie-hidden class="range-block" data-source="openai,openrouter,ai21,custom,cohere,perplexity,groq">439 <div data-newbie-hidden class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq">
440 <div class="range-block-title" data-i18n="Frequency Penalty">440 <div class="range-block-title" data-i18n="Frequency Penalty">
441 Frequency Penalty441 Frequency Penalty
442 </div>442 </div>
@@ -449,7 +449,7 @@
449 </div>449 </div>
450 </div>450 </div>
451 </div>451 </div>
452 <div data-newbie-hidden class="range-block" data-source="openai,openrouter,ai21,custom,cohere,perplexity,groq">452 <div data-newbie-hidden class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq">
453 <div class="range-block-title" data-i18n="Presence Penalty">453 <div class="range-block-title" data-i18n="Presence Penalty">
454 Presence Penalty454 Presence Penalty
455 </div>455 </div>
@@ -462,20 +462,7 @@
462 </div>462 </div>
463 </div>463 </div>
464 </div>464 </div>
465 <div data-newbie-hidden class="range-block" data-source="ai21">465 <div data-newbie-hidden class="range-block" data-source="claude,openrouter,makersuite,cohere,perplexity">
466 <div class="range-block-title" data-i18n="Count Penalty">
467 Count Penalty
468 </div>
469 <div class="range-block-range-and-counter">
470 <div class="range-block-range">
471 <input type="range" id="count_pen" name="volume" min="0" max="1" step="0.01">
472 </div>
473 <div class="range-block-counter">
474 <input type="number" min="0" max="1" step="0.01" data-for="count_pen" id="count_pen_counter">
475 </div>
476 </div>
477 </div>
478 <div data-newbie-hidden class="range-block" data-source="claude,openrouter,ai21,makersuite,cohere,perplexity">
479 <div class="range-block-title" data-i18n="Top K">466 <div class="range-block-title" data-i18n="Top K">
480 Top K467 Top K
481 </div>468 </div>
@@ -1814,22 +1801,6 @@
1814 </select>1801 </select>
1815 </div>1802 </div>
1816 </div>1803 </div>
1817 <div class="range-block" data-source="ai21">
1818 <label for="use_ai21_tokenizer" title="Use AI21 Tokenizer" data-i18n="[title]Use AI21 Tokenizer" class="checkbox_label widthFreeExpand">
1819 <input id="use_ai21_tokenizer" type="checkbox" /><span data-i18n="Use AI21 Tokenizer">Use AI21 Tokenizer</span>
1820 </label>
1821 <div class="toggle-description justifyLeft">
1822 <span data-i18n="Use the appropriate tokenizer for Jurassic models, which is more efficient than GPT's.">Use the appropriate tokenizer for Jurassic models, which is more efficient than GPT's.</span>
1823 </div>
1824 </div>
1825 <div class="range-block" data-source="makersuite">
1826 <label for="use_google_tokenizer" title="Use Google Tokenizer" data-i18n="[title]Use Google Tokenizer" class="checkbox_label widthFreeExpand">
1827 <input id="use_google_tokenizer" type="checkbox" /><span data-i18n="Use Google Tokenizer">Use Google Tokenizer</span>
1828 </label>
1829 <div class="toggle-description justifyLeft">
1830 <span data-i18n="Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.">Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.</span>
1831 </div>
1832 </div>
1833 <div class="range-block" data-source="makersuite">1804 <div class="range-block" data-source="makersuite">
1834 <label for="use_makersuite_sysprompt" class="checkbox_label widthFreeExpand">1805 <label for="use_makersuite_sysprompt" class="checkbox_label widthFreeExpand">
1835 <input id="use_makersuite_sysprompt" type="checkbox" />1806 <input id="use_makersuite_sysprompt" type="checkbox" />
@@ -1875,7 +1846,7 @@
1875 <div class="fa-solid fa-clock-rotate-left"></div>1846 <div class="fa-solid fa-clock-rotate-left"></div>
1876 </div>1847 </div>
1877 </div>1848 </div>
1878 <textarea id="claude_human_sysprompt_textarea" class="text_pole textarea_compact" rows="4" maxlength="10000" data-i18n="[placeholder]Human message" placeholder="Human message, instruction, etc.&#10;Adds nothing when empty, i.e. requires a new prompt with the role 'user'."></textarea>1849 <textarea id="claude_human_sysprompt_textarea" class="text_pole textarea_compact" rows="4" data-i18n="[placeholder]Human message" placeholder="Human message, instruction, etc.&#10;Adds nothing when empty, i.e. requires a new prompt with the role 'user'."></textarea>
1879 </div>1850 </div>
1880 </div>1851 </div>
1881 </div>1852 </div>
@@ -2460,7 +2431,7 @@
2460 <option value="claude">Claude</option>2431 <option value="claude">Claude</option>
2461 <option value="cohere">Cohere</option>2432 <option value="cohere">Cohere</option>
2462 <option value="groq">Groq</option>2433 <option value="groq">Groq</option>
2463 <option value="makersuite">Google MakerSuite</option>2434 <option value="makersuite">Google AI Studio</option>
2464 <option value="mistralai">MistralAI</option>2435 <option value="mistralai">MistralAI</option>
2465 <option value="openrouter">OpenRouter</option>2436 <option value="openrouter">OpenRouter</option>
2466 <option value="perplexity">Perplexity</option>2437 <option value="perplexity">Perplexity</option>
@@ -2814,16 +2785,18 @@
2814 <div>2785 <div>
2815 <h4 data-i18n="AI21 Model">AI21 Model</h4>2786 <h4 data-i18n="AI21 Model">AI21 Model</h4>
2816 <select id="model_ai21_select">2787 <select id="model_ai21_select">
2817 <optgroup label="Latest">2788 <optgroup label="Jamba 1.5">
2818 <option value="j2-ultra">j2-ultra</option>2789 <option value="jamba-1.5-mini">jamba-1.5-mini</option>
2819 <option value="j2-mid">j2-mid</option>2790 <option value="jamba-1.5-large">jamba-1.5-large</option>
2820 <option value="j2-light">j2-light</option>2791 </optgroup>
2792 <optgroup label="Jamba-Instruct (Deprecated)">
2793 <option value="jamba-instruct-preview">jamba-instruct-preview</option>
2821 </optgroup>2794 </optgroup>
2822 </select>2795 </select>
2823 </div>2796 </div>
2824 </form>2797 </form>
2825 <form id="makersuite_form" data-source="makersuite" action="javascript:void(null);" method="post" enctype="multipart/form-data">2798 <form id="makersuite_form" data-source="makersuite" action="javascript:void(null);" method="post" enctype="multipart/form-data">
2826 <h4 data-i18n="MakerSuite API Key">MakerSuite API Key</h4>2799 <h4 data-i18n="Google AI Studio API Key">Google AI Studio API Key</h4>
2827 <div class="flex-container">2800 <div class="flex-container">
2828 <input id="api_key_makersuite" name="api_key_makersuite" class="text_pole flex1" maxlength="500" value="" type="text" autocomplete="off">2801 <input id="api_key_makersuite" name="api_key_makersuite" class="text_pole flex1" maxlength="500" value="" type="text" autocomplete="off">
2829 <div title="Clear your API key" data-i18n="[title]Clear your API key" class="menu_button fa-solid fa-circle-xmark clear-api-key" data-key="api_key_makersuite"></div>2802 <div title="Clear your API key" data-i18n="[title]Clear your API key" class="menu_button fa-solid fa-circle-xmark clear-api-key" data-key="api_key_makersuite"></div>
@@ -2847,8 +2820,11 @@
2847 </optgroup>2820 </optgroup>
2848 <optgroup label="Subversions">2821 <optgroup label="Subversions">
2849 <option value="gemini-1.5-pro-exp-0801">Gemini 1.5 Pro Experiment 2024-08-01</option>2822 <option value="gemini-1.5-pro-exp-0801">Gemini 1.5 Pro Experiment 2024-08-01</option>
2823 <option value="gemini-1.5-pro-exp-0827">Gemini 1.5 Pro Experiment 2024-08-27</option>
2850 <option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option>2824 <option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option>
2851 <option value="gemini-1.5-pro-001">Gemini 1.5 Pro [001]</option>2825 <option value="gemini-1.5-pro-001">Gemini 1.5 Pro [001]</option>
2826 <option value="gemini-1.5-flash-exp-0827">Gemini 1.5 Flash Experiment 2024-08-27</option>
2827 <option value="gemini-1.5-flash-8b-exp-0827">Gemini 1.5 Flash 8B Experiment 2024-08-27</option>
2852 <option value="gemini-1.5-flash-latest">Gemini 1.5 Flash [latest]</option>2828 <option value="gemini-1.5-flash-latest">Gemini 1.5 Flash [latest]</option>
2853 <option value="gemini-1.5-flash-001">Gemini 1.5 Flash [001]</option>2829 <option value="gemini-1.5-flash-001">Gemini 1.5 Flash [001]</option>
2854 <option value="gemini-1.0-pro-latest">Gemini 1.0 Pro [latest]</option>2830 <option value="gemini-1.0-pro-latest">Gemini 1.0 Pro [latest]</option>
@@ -2978,6 +2954,8 @@
2978 <option value="command">command</option>2954 <option value="command">command</option>
2979 <option value="command-r">command-r</option>2955 <option value="command-r">command-r</option>
2980 <option value="command-r-plus">command-r-plus</option>2956 <option value="command-r-plus">command-r-plus</option>
2957 <option value="command-r-08-2024">command-r-08-2024</option>
2958 <option value="command-r-plus-08-2024">command-r-plus-08-2024</option>
2981 </optgroup>2959 </optgroup>
2982 <optgroup label="Nightly">2960 <optgroup label="Nightly">
2983 <option value="command-light-nightly">command-light-nightly</option>2961 <option value="command-light-nightly">command-light-nightly</option>
@@ -3473,6 +3451,7 @@
3473 <option value="3">Llama 1/2</option>3451 <option value="3">Llama 1/2</option>
3474 <option value="12">Llama 3</option>3452 <option value="12">Llama 3</option>
3475 <option value="13">Gemma / Gemini</option>3453 <option value="13">Gemma / Gemini</option>
3454 <option value="14">Jamba</option>
3476 <option value="4">NerdStash (NovelAI Clio)</option>3455 <option value="4">NerdStash (NovelAI Clio)</option>
3477 <option value="5">NerdStash v2 (NovelAI Kayra)</option>3456 <option value="5">NerdStash v2 (NovelAI Kayra)</option>
3478 <option value="7">Mistral</option>3457 <option value="7">Mistral</option>
@@ -3645,6 +3624,7 @@
3645 <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink gap0 flexBasis48p" title="Scan chronologically until reached min entries or token budget." data-i18n="[title]Scan chronologically until reached min entries or token budget.">3624 <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink gap0 flexBasis48p" title="Scan chronologically until reached min entries or token budget." data-i18n="[title]Scan chronologically until reached min entries or token budget.">
3646 <small>3625 <small>
3647 <span data-i18n="Min Activations">Min Activations</span>3626 <span data-i18n="Min Activations">Min Activations</span>
3627 <div class="fa-solid fa-triangle-exclamation opacity50p" data-i18n="[title](disabled when max recursion steps are used)" title="(disabled when max recursion steps are used)"></div>
3648 </small>3628 </small>
3649 <input class="neo-range-slider" type="range" id="world_info_min_activations" name="world_info_min_activations" min="0" max="100" step="1">3629 <input class="neo-range-slider" type="range" id="world_info_min_activations" name="world_info_min_activations" min="0" max="100" step="1">
3650 <input class="neo-range-input" type="number" min="0" max="100" step="1" data-for="world_info_min_activations" id="world_info_min_activations_counter">3630 <input class="neo-range-input" type="number" min="0" max="100" step="1" data-for="world_info_min_activations" id="world_info_min_activations_counter">
@@ -3658,6 +3638,14 @@
3658 <input class="neo-range-slider" type="range" id="world_info_min_activations_depth_max" name="volume" min="0" max="100" step="1">3638 <input class="neo-range-slider" type="range" id="world_info_min_activations_depth_max" name="volume" min="0" max="100" step="1">
3659 <input class="neo-range-input" type="number" min="0" max="100" step="1" data-for="world_info_min_activations_depth_max" id="world_info_min_activations_depth_max_counter">3639 <input class="neo-range-input" type="number" min="0" max="100" step="1" data-for="world_info_min_activations_depth_max" id="world_info_min_activations_depth_max_counter">
3660 </div>3640 </div>
3641 <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink gap0 flexBasis48p" title="Cap the number of entry activation recursions" data-i18n="[title]Cap the number of entry activation recursions">
3642 <small>
3643 <span data-i18n="Max Recursion Steps">Max Recursion Steps</span>
3644 <div class="fa-solid fa-triangle-exclamation opacity50p" data-i18n="[title]0 = unlimited, 1 = scans once and doesn't recurse, 2 = scans once and recurses once, etc\n(disabled when min activations are used)" title="0 = unlimited, 1 = scans once and doesn't recurse, 2 = scans once and recurses once, etc&#10;(disabled when min activations are used)"></div>
3645 </small>
3646 <input class="neo-range-slider" type="range" id="world_info_max_recursion_steps" name="world_info_max_recursion_steps" min="0" max="10" step="1">
3647 <input class="neo-range-input" type="number" min="0" max="10" step="1" data-for="world_info_max_recursion_steps" id="world_info_max_recursion_steps_counter">
3648 </div>
36613649
3662 <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink flexBasis48p">3650 <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink flexBasis48p">
3663 <small data-i18n="Insertion Strategy">3651 <small data-i18n="Insertion Strategy">
@@ -3921,8 +3909,8 @@
3921 <span data-i18n="Font Scale">Font Scale</span>3909 <span data-i18n="Font Scale">Font Scale</span>
3922 <div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Font size" title="Font size"></div>3910 <div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Font size" title="Font size"></div>
3923 </small>3911 </small>
3924 <input class="neo-range-slider" type="range" id="font_scale" name="font_scale" min="0.8" max="1.2" step="0.01">3912 <input class="neo-range-slider" type="range" id="font_scale" name="font_scale" min="0.5" max="1.5" step="0.01">
3925 <input class="neo-range-input" type="number" min="0.8" max="1.2" step="0.01" data-for="font_scale" id="font_scale_counter">3913 <input class="neo-range-input" type="number" min="0.5" max="1.5" step="0.01" data-for="font_scale" id="font_scale_counter">
3926 </div>3914 </div>
39273915
3928 <div class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0">3916 <div class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
@@ -4134,7 +4122,7 @@
4134 <input id="movingUImode" type="checkbox" />4122 <input id="movingUImode" type="checkbox" />
4135 <small data-i18n="Movable UI Panels">MovingUI&nbsp;<i class="fa-solid fa-desktop"></i></small>4123 <small data-i18n="Movable UI Panels">MovingUI&nbsp;<i class="fa-solid fa-desktop"></i></small>
4136 </label>4124 </label>
4137 <div data-newbie-hidden id="movingUIreset" title="Reset MovingUI panel sizes/locations." class="menu_button margin0" data-i18n="[title]Reset MovingUI panel sizes/locations."><i class=" fa-solid fa-recycle margin-r5"></i> Reset</div>4125 <div data-newbie-hidden id="movingUIreset" title="Reset MovingUI panel sizes/locations." class="menu_button margin0" data-i18n="[title]Reset MovingUI panel sizes/locations."><i class=" fa-solid fa-recycle margin-r5"></i> <span data-i18n="mui_reset">Reset</span></div>
4138 </div>4126 </div>
4139 <div data-newbie-hidden id="MovingUI-presets-block" class="flex-container alignitemscenter">4127 <div data-newbie-hidden id="MovingUI-presets-block" class="flex-container alignitemscenter">
4140 <div class="flex-container alignItemsFlexEnd">4128 <div class="flex-container alignItemsFlexEnd">
@@ -4590,6 +4578,7 @@
4590 <div data-newbie-hidden>4578 <div data-newbie-hidden>
4591 <label for="persona_description_position" data-i18n="Position:">Position:</label>4579 <label for="persona_description_position" data-i18n="Position:">Position:</label>
4592 <select id="persona_description_position">4580 <select id="persona_description_position">
4581 <option value="9" data-i18n="None (disabled)">None (disabled)</option>
4593 <option value="0" data-i18n="In Story String / Prompt Manager">In Story String / Prompt Manager</option>4582 <option value="0" data-i18n="In Story String / Prompt Manager">In Story String / Prompt Manager</option>
4594 <option value="2" data-i18n="Top of Author's Note">Top of Author's Note</option>4583 <option value="2" data-i18n="Top of Author's Note">Top of Author's Note</option>
4595 <option value="3" data-i18n="Bottom of Author's Note">Bottom of Author's Note</option>4584 <option value="3" data-i18n="Bottom of Author's Note">Bottom of Author's Note</option>
@@ -5615,13 +5604,16 @@
5615 <div class="flex-container wide100p flexGap10">5604 <div class="flex-container wide100p flexGap10">
5616 <div class="flex4 flex-container flexFlowColumn flexNoGap">5605 <div class="flex4 flex-container flexFlowColumn flexNoGap">
5617 <div class="flex-container justifySpaceBetween">5606 <div class="flex-container justifySpaceBetween">
5618 <small for="characterFilter" data-i18n="Filter to Character(s)">5607 <small for="characterFilter" data-i18n="Filter to Characters or Tags">
5619 Filter to Character(s)5608 Filter to Characters or Tags
5620 </small>5609 </small>
5621 <label class="checkbox_label flexNoGap margin-r5" for="character_exclusion">5610 <label class="checkbox_label flexNoGap margin-r5" for="character_exclusion">
5622 <input type="checkbox" name="character_exclusion" />5611 <input type="checkbox" name="character_exclusion" />
5623 <span>5612 <span>
5624 <small data-i18n="Character Exclusion">Character Exclusion</small>5613 <small title="Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry" data-i18n="[title]Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry">
5614 <span data-i18n="Exclude">Exclude</span>
5615 <div class="fa-solid fa-circle-info opacity50p"></div>
5616 </small>
5625 </span>5617 </span>
5626 </label>5618 </label>
5627 </div>5619 </div>
@@ -6577,6 +6569,7 @@
6577 <script src="lib/jquery-cookie-1.4.1.min.js"></script>6569 <script src="lib/jquery-cookie-1.4.1.min.js"></script>
6578 <script src="lib/jquery.ui.touch-punch.min.js"></script>6570 <script src="lib/jquery.ui.touch-punch.min.js"></script>
6579 <script src="lib/showdown.min.js"></script>6571 <script src="lib/showdown.min.js"></script>
6572 <script src="lib/showdown-patch.js"></script>
6580 <script src="lib/showdown-katex.min.js"></script>6573 <script src="lib/showdown-katex.min.js"></script>
6581 <script src="lib/popper.js"></script>6574 <script src="lib/popper.js"></script>
6582 <script src="lib/purify.min.js"></script>6575 <script src="lib/purify.min.js"></script>
public/lib/showdown-patch.js+24 -0
@@ -0,0 +1,24 @@
1showdown.subParser('unhashHTMLSpans', function (text, options, globals) {
2 'use strict';
3 text = globals.converter._dispatch('unhashHTMLSpans.before', text, options, globals);
4
5 for (var i = 0; i < globals.gHtmlSpans.length; ++i) {
6 var repText = globals.gHtmlSpans[i],
7 // limiter to prevent infinite loop (assume 10 as limit for recurse)
8 limit = 0;
9
10 while (/¨C(\d+)C/.test(repText)) {
11 var num = RegExp.$1;
12 repText = repText.replace('¨C' + num + 'C', globals.gHtmlSpans[num]);
13 if (limit === 10000) {
14 console.error('maximum nesting of 10000 spans reached!!!');
15 break;
16 }
17 ++limit;
18 }
19 text = text.replace('¨C' + i + 'C', repText);
20 }
21
22 text = globals.converter._dispatch('unhashHTMLSpans.after', text, options, globals);
23 return text;
24});
public/locales/ar-sa.json+1 -1
@@ -390,7 +390,7 @@
390 "Alt Method": "طريقة بديلة",390 "Alt Method": "طريقة بديلة",
391 "AI21 API Key": "مفتاح API لـ AI21",391 "AI21 API Key": "مفتاح API لـ AI21",
392 "AI21 Model": "نموذج AI21",392 "AI21 Model": "نموذج AI21",
393 "MakerSuite API Key": "مفتاح واجهة برمجة تطبيقات MakerSuite",393 "Google AI Studio API Key": "مفتاح واجهة برمجة تطبيقات Google AI Studio",
394 "Google Model": "نموذج جوجل",394 "Google Model": "نموذج جوجل",
395 "MistralAI API Key": "مفتاح واجهة برمجة التطبيقات MistralAI",395 "MistralAI API Key": "مفتاح واجهة برمجة التطبيقات MistralAI",
396 "MistralAI Model": "نموذج ميسترال آي آي",396 "MistralAI Model": "نموذج ميسترال آي آي",
public/locales/de-de.json+1 -1
@@ -390,7 +390,7 @@
390 "Alt Method": "Alternative Methode",390 "Alt Method": "Alternative Methode",
391 "AI21 API Key": "AI21 API-Schlüssel",391 "AI21 API Key": "AI21 API-Schlüssel",
392 "AI21 Model": "AI21-Modell",392 "AI21 Model": "AI21-Modell",
393 "MakerSuite API Key": "MakerSuite API-Schlüssel",393 "Google AI Studio API Key": "Google AI Studio API-Schlüssel",
394 "Google Model": "Google-Modell",394 "Google Model": "Google-Modell",
395 "MistralAI API Key": "MistralAI API-Schlüssel",395 "MistralAI API Key": "MistralAI API-Schlüssel",
396 "MistralAI Model": "MistralAI-Modell",396 "MistralAI Model": "MistralAI-Modell",
public/locales/es-es.json+1 -1
@@ -390,7 +390,7 @@
390 "Alt Method": "Método alternativo",390 "Alt Method": "Método alternativo",
391 "AI21 API Key": "Clave API de AI21",391 "AI21 API Key": "Clave API de AI21",
392 "AI21 Model": "Modelo de AI21",392 "AI21 Model": "Modelo de AI21",
393 "MakerSuite API Key": "Clave API de MakerSuite",393 "Google AI Studio API Key": "Clave API de Google AI Studio",
394 "Google Model": "Modelo de Google",394 "Google Model": "Modelo de Google",
395 "MistralAI API Key": "Clave API de MistralAI",395 "MistralAI API Key": "Clave API de MistralAI",
396 "MistralAI Model": "Modelo MistralAI",396 "MistralAI Model": "Modelo MistralAI",
public/locales/fr-fr.json+1 -1
@@ -390,7 +390,7 @@
390 "Alt Method": "Méthode alternative",390 "Alt Method": "Méthode alternative",
391 "AI21 API Key": "Clé API AI21",391 "AI21 API Key": "Clé API AI21",
392 "AI21 Model": "Modèle AI21",392 "AI21 Model": "Modèle AI21",
393 "MakerSuite API Key": "Clé API MakerSuite",393 "Google AI Studio API Key": "Clé API Google AI Studio",
394 "Google Model": "Modèle Google",394 "Google Model": "Modèle Google",
395 "MistralAI API Key": "Clé API MistralAI",395 "MistralAI API Key": "Clé API MistralAI",
396 "MistralAI Model": "Modèle MistralAI",396 "MistralAI Model": "Modèle MistralAI",
public/locales/is-is.json+1 -1
@@ -390,7 +390,7 @@
390 "Alt Method": "Aðferð Bakmenn",390 "Alt Method": "Aðferð Bakmenn",
391 "AI21 API Key": "Lykill API fyrir AI21",391 "AI21 API Key": "Lykill API fyrir AI21",
392 "AI21 Model": "AI21 Módel",392 "AI21 Model": "AI21 Módel",
393 "MakerSuite API Key": "MakerSuite API lykill",393 "Google AI Studio API Key": "Google AI Studio API lykill",
394 "Google Model": "Google líkan",394 "Google Model": "Google líkan",
395 "MistralAI API Key": "MistralAI API lykill",395 "MistralAI API Key": "MistralAI API lykill",
396 "MistralAI Model": "MistralAI líkan",396 "MistralAI Model": "MistralAI líkan",
public/locales/it-it.json+1 -1
@@ -390,7 +390,7 @@
390 "Alt Method": "Metodo alternativo",390 "Alt Method": "Metodo alternativo",
391 "AI21 API Key": "Chiave API di AI21",391 "AI21 API Key": "Chiave API di AI21",
392 "AI21 Model": "Modello AI21",392 "AI21 Model": "Modello AI21",
393 "MakerSuite API Key": "Chiave API MakerSuite",393 "Google AI Studio API Key": "Chiave API Google AI Studio",
394 "Google Model": "Modello Google",394 "Google Model": "Modello Google",
395 "MistralAI API Key": "Chiave API MistralAI",395 "MistralAI API Key": "Chiave API MistralAI",
396 "MistralAI Model": "Modello MistralAI",396 "MistralAI Model": "Modello MistralAI",
public/locales/ja-jp.json+1 -1
@@ -390,7 +390,7 @@
390 "Alt Method": "代替手法",390 "Alt Method": "代替手法",
391 "AI21 API Key": "AI21のAPIキー",391 "AI21 API Key": "AI21のAPIキー",
392 "AI21 Model": "AI21モデル",392 "AI21 Model": "AI21モデル",
393 "MakerSuite API Key": "MakerSuite APIキー",393 "Google AI Studio API Key": "Google AI Studio APIキー",
394 "Google Model": "Google モデル",394 "Google Model": "Google モデル",
395 "MistralAI API Key": "MistralAI API キー",395 "MistralAI API Key": "MistralAI API キー",
396 "MistralAI Model": "MistralAI モデル",396 "MistralAI Model": "MistralAI モデル",
public/locales/ko-kr.json+1 -1
@@ -390,7 +390,7 @@
390 "Alt Method": "대체 방법",390 "Alt Method": "대체 방법",
391 "AI21 API Key": "AI21 API 키",391 "AI21 API Key": "AI21 API 키",
392 "AI21 Model": "AI21 모델",392 "AI21 Model": "AI21 모델",
393 "MakerSuite API Key": "MakerSuite API 키",393 "Google AI Studio API Key": "Google AI Studio API 키",
394 "Google Model": "구글 모델",394 "Google Model": "구글 모델",
395 "MistralAI API Key": "MistralAI API 키",395 "MistralAI API Key": "MistralAI API 키",
396 "MistralAI Model": "MistralAI 모델",396 "MistralAI Model": "MistralAI 모델",
public/locales/pt-pt.json+1 -1
@@ -390,7 +390,7 @@
390 "Alt Method": "Método Alternativo",390 "Alt Method": "Método Alternativo",
391 "AI21 API Key": "Chave da API AI21",391 "AI21 API Key": "Chave da API AI21",
392 "AI21 Model": "Modelo AI21",392 "AI21 Model": "Modelo AI21",
393 "MakerSuite API Key": "Chave API MakerSuite",393 "Google AI Studio API Key": "Chave API Google AI Studio",
394 "Google Model": "Modelo Google",394 "Google Model": "Modelo Google",
395 "MistralAI API Key": "Chave de API MistralAI",395 "MistralAI API Key": "Chave de API MistralAI",
396 "MistralAI Model": "Modelo MistralAI",396 "MistralAI Model": "Modelo MistralAI",
public/locales/ru-ru.json+55 -8
@@ -43,7 +43,7 @@
43 "Smoothing Factor": "Коэффициент сглаживания",43 "Smoothing Factor": "Коэффициент сглаживания",
44 "No Repeat Ngram Size": "Размер no_repeat_ngram",44 "No Repeat Ngram Size": "Размер no_repeat_ngram",
45 "Min Length": "Мин. длина",45 "Min Length": "Мин. длина",
46 "Alternative server URL (leave empty to use the default value).": "Альтернативный URL сервера (оставьте пустым для стандартного значения)",46 "Alternative server URL (leave empty to use the default value).": "URL альтернативного сервера (оставьте пустым для стандартного значения)",
47 "Remove your real OAI API Key from the API panel BEFORE typing anything into this box": "Удалите свой личный OAI API Key из панели API, и ТОЛЬКО ПОСЛЕ ЭТОГО вводите что-то сюда",47 "Remove your real OAI API Key from the API panel BEFORE typing anything into this box": "Удалите свой личный OAI API Key из панели API, и ТОЛЬКО ПОСЛЕ ЭТОГО вводите что-то сюда",
48 "We cannot provide support for problems encountered while using an unofficial OpenAI proxy": "Мы не сможем предоставить помощь с проблемами, с которыми вы столкнетесь при использовании неофициальных прокси для OpenAI",48 "We cannot provide support for problems encountered while using an unofficial OpenAI proxy": "Мы не сможем предоставить помощь с проблемами, с которыми вы столкнетесь при использовании неофициальных прокси для OpenAI",
49 "Context Size (tokens)": "Размер контекста (в токенах)",49 "Context Size (tokens)": "Размер контекста (в токенах)",
@@ -186,7 +186,7 @@
186 "Generate only one line per request": "Генерировать одну строку на запрос",186 "Generate only one line per request": "Генерировать одну строку на запрос",
187 "Misc. Settings": "Доп. настройки",187 "Misc. Settings": "Доп. настройки",
188 "Auto-Continue": "Авто-продолжение",188 "Auto-Continue": "Авто-продолжение",
189 "Collapse Consecutive Newlines": "Сворачивать последовательные новые строки",189 "Collapse Consecutive Newlines": "Сворачивать неск. новых строк в одну",
190 "Allow for Chat Completion APIs": "Разрешить для Chat Completion API",190 "Allow for Chat Completion APIs": "Разрешить для Chat Completion API",
191 "Target length (tokens)": "Целевая длина (в токенах)",191 "Target length (tokens)": "Целевая длина (в токенах)",
192 "World Info": "Информация о мире",192 "World Info": "Информация о мире",
@@ -373,7 +373,7 @@
373 "Prefer Character Card Prompt": "Приоритет промпту из карточки персонажа",373 "Prefer Character Card Prompt": "Приоритет промпту из карточки персонажа",
374 "Prefer Character Card Jailbreak": "Приоритет джейлбрейку из карточки персонажа",374 "Prefer Character Card Jailbreak": "Приоритет джейлбрейку из карточки персонажа",
375 "Press Send to continue": "Кнопка отправки продолжает сообщение",375 "Press Send to continue": "Кнопка отправки продолжает сообщение",
376 "Quick 'Continue' button": "Кнопка быстрого продолжения",376 "Quick 'Continue' button": "Быстрое продолжение",
377 "Log prompts to console": "Выводить промпты в консоль",377 "Log prompts to console": "Выводить промпты в консоль",
378 "Never resize avatars": "Не менять размер аватарок",378 "Never resize avatars": "Не менять размер аватарок",
379 "Show avatar filenames": "Показывать названия файлов аватарок",379 "Show avatar filenames": "Показывать названия файлов аватарок",
@@ -495,7 +495,7 @@
495 "(Write a comma-separated list of tags)": "(Список тегов через запятую)",495 "(Write a comma-separated list of tags)": "(Список тегов через запятую)",
496 "(A brief description of the personality)": "(Краткое описание личности)",496 "(A brief description of the personality)": "(Краткое описание личности)",
497 "(Circumstances and context of the interaction)": "(Обстоятельства и контекст этого диалога)",497 "(Circumstances and context of the interaction)": "(Обстоятельства и контекст этого диалога)",
498 "(Examples of chat dialog. Begin each example with START on a new line.)": "(Примеры диалога. Начинайте каждый пример с START или новой линией.)",498 "(Examples of chat dialog. Begin each example with START on a new line.)": "(Примеры диалога. Начинайте каждый пример с START или новой строкой.)",
499 "Type here...": "Пишите здесь...",499 "Type here...": "Пишите здесь...",
500 "Comma separated (required)": "Через запятую (обязательное поле)",500 "Comma separated (required)": "Через запятую (обязательное поле)",
501 "What this keyword should mean to the AI, sent verbatim": "Что это ключевое слово должно означать для ИИ, отправляется дословно",501 "What this keyword should mean to the AI, sent verbatim": "Что это ключевое слово должно означать для ИИ, отправляется дословно",
@@ -552,7 +552,7 @@
552 "Click to set a new User Name": "Нажмите, чтобы задать новое имя пользователя.",552 "Click to set a new User Name": "Нажмите, чтобы задать новое имя пользователя.",
553 "Click to lock your selected persona to the current chat. Click again to remove the lock.": "Нажмите, чтобы закрепить выбранную персону для текущего чата. Нажмите еще раз, чтобы открепить.",553 "Click to lock your selected persona to the current chat. Click again to remove the lock.": "Нажмите, чтобы закрепить выбранную персону для текущего чата. Нажмите еще раз, чтобы открепить.",
554 "Click to set user name for all messages": "Нажмите, чтобы задать имя пользователя для всех сообщений.",554 "Click to set user name for all messages": "Нажмите, чтобы задать имя пользователя для всех сообщений.",
555 "Create a dummy persona": "Создать персону-болванку",555 "Create a dummy persona": "Создать пустую персону",
556 "Character Management": "Управление персонажами",556 "Character Management": "Управление персонажами",
557 "Locked = Character Management panel will stay open": "Закреплено = Панель управление персонажами останется открытой",557 "Locked = Character Management panel will stay open": "Закреплено = Панель управление персонажами останется открытой",
558 "Select/Create Characters": "Выбрать/Создать персонажа",558 "Select/Create Characters": "Выбрать/Создать персонажа",
@@ -625,7 +625,7 @@
625 "Automatically select a background based on the chat context": "Автоматический выбор фона в зависимости от контекста чата",625 "Automatically select a background based on the chat context": "Автоматический выбор фона в зависимости от контекста чата",
626 "Filter": "Фильтр",626 "Filter": "Фильтр",
627 "Exclude message from prompts": "Исключить сообщение из промпта",627 "Exclude message from prompts": "Исключить сообщение из промпта",
628 "Include message in prompts": "Включить сообщение в подсказки",628 "Include message in prompts": "Включить сообщение в промпт",
629 "Create checkpoint": "Создать чекпоинт",629 "Create checkpoint": "Создать чекпоинт",
630 "Create Branch": "Создать ветку",630 "Create Branch": "Создать ветку",
631 "Embed file or image": "Вставить файл или изображение",631 "Embed file or image": "Вставить файл или изображение",
@@ -722,7 +722,7 @@
722 "Proxy Server URL": "Адрес прокси-сервера",722 "Proxy Server URL": "Адрес прокси-сервера",
723 "MistralAI Model": "Модель MistralAI",723 "MistralAI Model": "Модель MistralAI",
724 "MistralAI API Key": "Ключ от API MistralAI",724 "MistralAI API Key": "Ключ от API MistralAI",
725 "MakerSuite API Key": "Ключ от API MakerSuite",725 "Google AI Studio API Key": "Ключ от API Google AI Studio",
726 "Google Model": "Модель Google",726 "Google Model": "Модель Google",
727 "Cohere API Key": "Ключ от API Cohere",727 "Cohere API Key": "Ключ от API Cohere",
728 "Cohere Model": "Модель Cohere",728 "Cohere Model": "Модель Cohere",
@@ -1661,5 +1661,52 @@
1661 "ext_regex_ai_output_desc": "Сообщения, полученные от API",1661 "ext_regex_ai_output_desc": "Сообщения, полученные от API",
1662 "ext_regex_sts_desc": "Сообщения, отправленные с помощью команд STscript",1662 "ext_regex_sts_desc": "Сообщения, отправленные с помощью команд STscript",
1663 "ext_regex_wi_desc": "Содержимое лорбуков и миров. Для работы требует включения флажка \"Только промпт\"!",1663 "ext_regex_wi_desc": "Содержимое лорбуков и миров. Для работы требует включения флажка \"Только промпт\"!",
1664 "ext_regex_only_format_display_desc": "История чата не изменится, замена будет осуществляться только в отображаемом сообщении (в UI)"1664 "ext_regex_only_format_display_desc": "История чата не изменится, замена будет осуществляться только в отображаемом сообщении (в UI)",
1665 "Prefer Character Card Instructions": "Приоритет инструкциям из карточек",
1666 "If checked and the character card contains a Post-History Instructions override, use that instead": "Если в карточке персонажа имеется собственная инструкция после истории, в промпт попадёт именно она",
1667 "Auto-select Input Text": "Автовыделение вводимого текста",
1668 "Enable auto-select of input text in some text fields when clicking/selecting them. Applies to popup input textboxes, and possible other custom input fields.": "Автоматически выделять вводимый текст в некоторых текстовых полях при клике/выборе. Работает для вспл. окон и различных кастомных полей ввода.",
1669 "Reset MovingUI panel sizes/locations.": "Сбросить расположение и размеры панелей MovingUI.",
1670 "mui_reset": "Сброс",
1671 "Quick 'Impersonate' button": "Быстрое перевоплощение",
1672 "Show a button in the input area to ask the AI to impersonate your character for a single message": "Показать в поле ввода кнопку, по нажатии на которую ИИ сгенерирует одно сообщение от лица вашего персонажа.",
1673 "Separators as Stop Strings": "Разделители как стоп-строки",
1674 "Names as Stop Strings": "Имена как стоп-строки",
1675 "Add Character and User names to a list of stopping strings.": "Добавлять имена персонажа и пользователя в список стоп-строк.",
1676 "Allow Post-History Instructions": "Разрешить инструкции после истории",
1677 "context_allow_post_history_instructions": "Добавлять в конец промпта инструкции после истории. Работает только при наличии таких инструкций в карточке И при включенной опции ''Приоритет инструкциям из карточек''.\nНЕ РЕКОМЕНДУЕТСЯ ДЛЯ МОДЕЛЕЙ TEXT COMPLETION, МОЖЕТ ПОРТИТЬ ВЫХОДНОЙ ТЕКСТ.",
1678 "First User Prefix": "Первый префикс пользователя",
1679 "Inserted before the first User's message.": "Вставляется перед первым сообщением пользователя.",
1680 "Last User Prefix": "Последний префикс пользователя",
1681 "instruct_last_input_sequence": "Вставляется перед последним сообщением пользователя.",
1682 "Inserted before a User message and as a last prompt line when impersonating.": "Вставляется перед сообщением пользователя в качестве последней строки промпта при перевоплощении.",
1683 "Inserted before a System (added by slash commands or extensions) message.": "Вставляется перед сообщением системы (может быть добавлено слэш-командой или расширением).",
1684 "Load Asset List": "Загрузить список ресурсов",
1685 "Never add character names.": "Не вставлять имя персонажа.",
1686 "Don't add character names unless necessary.": "Вставлять имя персонажа только когда это необходимо.",
1687 "character_names_none": "Не добавлять имена персонажей в качестве префикса. Может повредить качеству ответов в групповых чатах, используйте с осторожностью.",
1688 "Auxiliary": "Вспомогательный",
1689 "Post-History Instructions": "Инструкции после истории",
1690 "Current persona updated": "Текущая персона изменена",
1691 "Your messages will now be sent as ${0}": "Ваши сообщения будут отправляться от лица ${0}",
1692 "Copied!": "Скопировано!",
1693 "Are you sure you want to delete this message?": "Вы точно хотите удалить это сообщение?",
1694 "Delete Message": "Удалить сообщение",
1695 "Delete Swipe": "Удалить свайп",
1696 "Could not get a reply from API. Check your connection settings / API key and try again.": "Не удалось получить ответ от API. Проверьте настройки соединения и API-ключ и повторите попытку.",
1697 "Connecting To Proxy": "Подключение к прокси",
1698 "Are you sure you want to connect to the following proxy URL?": "Вы точно хотите соединиться с прокси по этому адресу?",
1699 "API connection successful!": "Соединение с API установлено!",
1700 "Proxy Saved": "Прокси сохранена",
1701 "Proxy Deleted": "Прокси удалена",
1702 "Could not find proxy with name '${0}'": "Не удалось найти прокси с названием '${0}'",
1703 "Proxy preset '${0}' not found in proxies array.": "Пресет с названием '${0}' не найден в списке прокси.",
1704 "Please wait for the previous request to complete.": "Пожалуйста, дождитесь окончания обработки предыдущего запроса.",
1705 "Start new chat?": "Начать новый чат?",
1706 "If necessary, you can later restore this chat file from the /backups folder": "При необходимости этот чат можно будет восстановить из папки /backups",
1707 "Also delete the current chat file": "Также удалить текущий чат",
1708 "chat_rename_1": "Введите новое имя чата:",
1709 "chat_rename_2": "!!Не используйте имя уже существующего файла, это приведёт к ошибке!!",
1710 "chat_rename_3": "Будут разрушены связи между чатами-чекпоинтами.",
1711 "chat_rename_4": "Расширение '.jsonl' дописывать не нужно."
1665}1712}
public/locales/uk-ua.json+1 -1
@@ -390,7 +390,7 @@
390 "Alt Method": "Альтернативний метод",390 "Alt Method": "Альтернативний метод",
391 "AI21 API Key": "Ключ API для AI21",391 "AI21 API Key": "Ключ API для AI21",
392 "AI21 Model": "Модель AI21",392 "AI21 Model": "Модель AI21",
393 "MakerSuite API Key": "Ключ API MakerSuite",393 "Google AI Studio API Key": "Ключ API Google AI Studio",
394 "Google Model": "Модель Google",394 "Google Model": "Модель Google",
395 "MistralAI API Key": "Ключ API MistralAI",395 "MistralAI API Key": "Ключ API MistralAI",
396 "MistralAI Model": "Модель MistralAI",396 "MistralAI Model": "Модель MistralAI",
public/locales/vi-vn.json+1 -1
@@ -390,7 +390,7 @@
390 "Alt Method": "Phương pháp thay thế",390 "Alt Method": "Phương pháp thay thế",
391 "AI21 API Key": "Khóa API của AI21",391 "AI21 API Key": "Khóa API của AI21",
392 "AI21 Model": "Mô hình AI21",392 "AI21 Model": "Mô hình AI21",
393 "MakerSuite API Key": "Khóa API MakerSuite",393 "Google AI Studio API Key": "Khóa API Google AI Studio",
394 "Google Model": "Mô hình Google",394 "Google Model": "Mô hình Google",
395 "MistralAI API Key": "Khóa API MistralAI",395 "MistralAI API Key": "Khóa API MistralAI",
396 "MistralAI Model": "Mô hình MistralAI",396 "MistralAI Model": "Mô hình MistralAI",
public/locales/zh-cn.json+1 -1
@@ -406,7 +406,7 @@
406 "Alt Method": "备用方法",406 "Alt Method": "备用方法",
407 "AI21 API Key": "AI21 API 密钥",407 "AI21 API Key": "AI21 API 密钥",
408 "AI21 Model": "AI21 模型",408 "AI21 Model": "AI21 模型",
409 "MakerSuite API Key": "MakerSuite API 密钥",409 "Google AI Studio API Key": "Google AI Studio API 密钥",
410 "Google Model": "Google 模型",410 "Google Model": "Google 模型",
411 "MistralAI API Key": "MistralAI API 密钥",411 "MistralAI API Key": "MistralAI API 密钥",
412 "MistralAI Model": "MistralAI 模型",412 "MistralAI Model": "MistralAI 模型",
public/locales/zh-tw.json+1 -1
@@ -391,7 +391,7 @@
391 "Alt Method": "替代方法",391 "Alt Method": "替代方法",
392 "AI21 API Key": "AI21 API 金鑰",392 "AI21 API Key": "AI21 API 金鑰",
393 "AI21 Model": "AI21 模型",393 "AI21 Model": "AI21 模型",
394 "MakerSuite API Key": "MakerSuite API 金鑰",394 "Google AI Studio API Key": "Google AI Studio API 金鑰",
395 "Google Model": "Google 模型",395 "Google Model": "Google 模型",
396 "MistralAI API Key": "MistralAI API 金鑰",396 "MistralAI API Key": "MistralAI API 金鑰",
397 "MistralAI Model": "MistralAI 模型",397 "MistralAI Model": "MistralAI 模型",
public/script.js+113 -81
@@ -84,6 +84,7 @@ import {
84 context_presets,84 context_presets,
85 resetMovableStyles,85 resetMovableStyles,
86 forceCharacterEditorTokenize,86 forceCharacterEditorTokenize,
87 applyPowerUserSettings,
87} from './scripts/power-user.js';88} from './scripts/power-user.js';
8889
89import {90import {
@@ -242,7 +243,7 @@ import { DragAndDropHandler } from './scripts/dragdrop.js';
242import { INTERACTABLE_CONTROL_CLASS, initKeyboard } from './scripts/keyboard.js';243import { INTERACTABLE_CONTROL_CLASS, initKeyboard } from './scripts/keyboard.js';
243import { initDynamicStyles } from './scripts/dynamic-styles.js';244import { initDynamicStyles } from './scripts/dynamic-styles.js';
244import { SlashCommandEnumValue, enumTypes } from './scripts/slash-commands/SlashCommandEnumValue.js';245import { SlashCommandEnumValue, enumTypes } from './scripts/slash-commands/SlashCommandEnumValue.js';
245import { enumIcons } from './scripts/slash-commands/SlashCommandCommonEnumsProvider.js';246import { commonEnumProviders, enumIcons } from './scripts/slash-commands/SlashCommandCommonEnumsProvider.js';
246247
247//exporting functions and vars for mods248//exporting functions and vars for mods
248export {249export {
@@ -415,6 +416,7 @@ export const event_types = {
415 GENERATION_STOPPED: 'generation_stopped',416 GENERATION_STOPPED: 'generation_stopped',
416 GENERATION_ENDED: 'generation_ended',417 GENERATION_ENDED: 'generation_ended',
417 EXTENSIONS_FIRST_LOAD: 'extensions_first_load',418 EXTENSIONS_FIRST_LOAD: 'extensions_first_load',
419 EXTENSION_SETTINGS_LOADED: 'extension_settings_loaded',
418 SETTINGS_LOADED: 'settings_loaded',420 SETTINGS_LOADED: 'settings_loaded',
419 SETTINGS_UPDATED: 'settings_updated',421 SETTINGS_UPDATED: 'settings_updated',
420 GROUP_UPDATED: 'group_updated',422 GROUP_UPDATED: 'group_updated',
@@ -486,14 +488,6 @@ let default_user_name = 'User';
486export let name1 = default_user_name;488export let name1 = default_user_name;
487export let name2 = 'SillyTavern System';489export let name2 = 'SillyTavern System';
488export let chat = [];490export let chat = [];
489let safetychat = [
490 {
491 name: systemUserName,
492 is_user: false,
493 create_date: 0,
494 mes: 'You deleted a character/chat and arrived back here for safety reasons! Pick another character!',
495 },
496];
497let chatSaveTimeout;491let chatSaveTimeout;
498let importFlashTimeout;492let importFlashTimeout;
499export let isChatSaving = false;493export let isChatSaving = false;
@@ -592,6 +586,17 @@ export const extension_prompt_roles = {
592586
593export const MAX_INJECTION_DEPTH = 1000;587export const MAX_INJECTION_DEPTH = 1000;
594588
589const SAFETY_CHAT = [
590 {
591 name: systemUserName,
592 force_avatar: system_avatar,
593 is_system: true,
594 is_user: false,
595 create_date: 0,
596 mes: 'You deleted a character/chat and arrived back here for safety reasons! Pick another character!',
597 },
598];
599
595export let system_messages = {};600export let system_messages = {};
596601
597async function getSystemMessages() {602async function getSystemMessages() {
@@ -1879,7 +1884,12 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId) {
1879 }1884 }
18801885
1881 if (Number(messageId) === 0 && !isSystem && !isUser) {1886 if (Number(messageId) === 0 && !isSystem && !isUser) {
1887 const mesBeforeReplace = mes;
1888 const chatMessage = chat[messageId];
1882 mes = substituteParams(mes, undefined, ch_name);1889 mes = substituteParams(mes, undefined, ch_name);
1890 if (chatMessage && chatMessage.mes === mesBeforeReplace && chatMessage.extra?.display_text !== mesBeforeReplace) {
1891 chatMessage.mes = mes;
1892 }
1883 }1893 }
18841894
1885 mesForShowdownParse = mes;1895 mesForShowdownParse = mes;
@@ -2185,7 +2195,7 @@ export function addCopyToCodeBlocks(messageElement) {
2185 codeBlocks.get(i).appendChild(copyButton);2195 codeBlocks.get(i).appendChild(copyButton);
2186 copyButton.addEventListener('pointerup', function (event) {2196 copyButton.addEventListener('pointerup', function (event) {
2187 navigator.clipboard.writeText(codeBlocks.get(i).innerText);2197 navigator.clipboard.writeText(codeBlocks.get(i).innerText);
2188 toastr.info('Copied!', '', { timeOut: 2000 });2198 toastr.info(t`Copied!`, '', { timeOut: 2000 });
2189 });2199 });
2190 }2200 }
2191 }2201 }
@@ -2698,7 +2708,7 @@ function addPersonaDescriptionExtensionPrompt() {
2698 const INJECT_TAG = 'PERSONA_DESCRIPTION';2708 const INJECT_TAG = 'PERSONA_DESCRIPTION';
2699 setExtensionPrompt(INJECT_TAG, '', extension_prompt_types.IN_PROMPT, 0);2709 setExtensionPrompt(INJECT_TAG, '', extension_prompt_types.IN_PROMPT, 0);
27002710
2701 if (!power_user.persona_description) {2711 if (!power_user.persona_description || power_user.persona_description_position === persona_description_positions.NONE) {
2702 return;2712 return;
2703 }2713 }
27042714
@@ -2819,7 +2829,7 @@ export function getCharacterCardFields() {
2819}2829}
28202830
2821export function isStreamingEnabled() {2831export function isStreamingEnabled() {
2822 const noStreamSources = [chat_completion_sources.SCALE, chat_completion_sources.AI21];2832 const noStreamSources = [chat_completion_sources.SCALE];
2823 return ((main_api == 'openai' && oai_settings.stream_openai && !noStreamSources.includes(oai_settings.chat_completion_source) && !(oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE && oai_settings.google_model.includes('bison')))2833 return ((main_api == 'openai' && oai_settings.stream_openai && !noStreamSources.includes(oai_settings.chat_completion_source) && !(oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE && oai_settings.google_model.includes('bison')))
2824 || (main_api == 'kobold' && kai_settings.streaming_kobold && kai_flags.can_use_streaming)2834 || (main_api == 'kobold' && kai_settings.streaming_kobold && kai_flags.can_use_streaming)
2825 || (main_api == 'novel' && nai_settings.streaming_novel)2835 || (main_api == 'novel' && nai_settings.streaming_novel)
@@ -2839,7 +2849,14 @@ function hideStopButton() {
2839}2849}
28402850
2841class StreamingProcessor {2851class StreamingProcessor {
2842 constructor(type, force_name2, timeStarted, messageAlreadyGenerated) {2852 /**
2853 * Creates a new streaming processor.
2854 * @param {string} type Generation type
2855 * @param {boolean} forceName2 If true, force the use of name2
2856 * @param {Date} timeStarted Date when generation was started
2857 * @param {string} continueMessage Previous message if the type is 'continue'
2858 */
2859 constructor(type, forceName2, timeStarted, continueMessage) {
2843 this.result = '';2860 this.result = '';
2844 this.messageId = -1;2861 this.messageId = -1;
2845 this.messageDom = null;2862 this.messageDom = null;
@@ -2849,14 +2866,14 @@ class StreamingProcessor {
2849 /** @type {HTMLTextAreaElement} */2866 /** @type {HTMLTextAreaElement} */
2850 this.sendTextarea = document.querySelector('#send_textarea');2867 this.sendTextarea = document.querySelector('#send_textarea');
2851 this.type = type;2868 this.type = type;
2852 this.force_name2 = force_name2;2869 this.force_name2 = forceName2;
2853 this.isStopped = false;2870 this.isStopped = false;
2854 this.isFinished = false;2871 this.isFinished = false;
2855 this.generator = this.nullStreamingGeneration;2872 this.generator = this.nullStreamingGeneration;
2856 this.abortController = new AbortController();2873 this.abortController = new AbortController();
2857 this.firstMessageText = '...';2874 this.firstMessageText = '...';
2858 this.timeStarted = timeStarted;2875 this.timeStarted = timeStarted;
2859 this.messageAlreadyGenerated = messageAlreadyGenerated;2876 this.continueMessage = type === 'continue' ? continueMessage : '';
2860 this.swipes = [];2877 this.swipes = [];
2861 /** @type {import('./scripts/logprobs.js').TokenLogprobs[]} */2878 /** @type {import('./scripts/logprobs.js').TokenLogprobs[]} */
2862 this.messageLogprobs = [];2879 this.messageLogprobs = [];
@@ -3009,8 +3026,7 @@ class StreamingProcessor {
3009 await eventSource.emit(event_types.IMPERSONATE_READY, text);3026 await eventSource.emit(event_types.IMPERSONATE_READY, text);
3010 }3027 }
30113028
3012 const continueMsg = this.type === 'continue' ? this.messageAlreadyGenerated : undefined;3029 saveLogprobsForActiveMessage(this.messageLogprobs.filter(Boolean), this.continueMessage);
3013 saveLogprobsForActiveMessage(this.messageLogprobs.filter(Boolean), continueMsg);
3014 await saveChatConditional();3030 await saveChatConditional();
3015 unblockGeneration();3031 unblockGeneration();
3016 generatedPromptCache = '';3032 generatedPromptCache = '';
@@ -3106,7 +3122,7 @@ class StreamingProcessor {
3106 if (logprobs) {3122 if (logprobs) {
3107 this.messageLogprobs.push(...(Array.isArray(logprobs) ? logprobs : [logprobs]));3123 this.messageLogprobs.push(...(Array.isArray(logprobs) ? logprobs : [logprobs]));
3108 }3124 }
3109 await sw.tick(() => this.onProgressStreaming(this.messageId, this.messageAlreadyGenerated + text));3125 await sw.tick(() => this.onProgressStreaming(this.messageId, this.continueMessage + text));
3110 }3126 }
3111 const seconds = (timestamps[timestamps.length - 1] - timestamps[0]) / 1000;3127 const seconds = (timestamps[timestamps.length - 1] - timestamps[0]) / 1000;
3112 console.warn(`Stream stats: ${timestamps.length} tokens, ${seconds.toFixed(2)} seconds, rate: ${Number(timestamps.length / seconds).toFixed(2)} TPS`);3128 console.warn(`Stream stats: ${timestamps.length} tokens, ${seconds.toFixed(2)} seconds, rate: ${Number(timestamps.length / seconds).toFixed(2)} TPS`);
@@ -3299,8 +3315,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3299 const isInstruct = power_user.instruct.enabled && main_api !== 'openai';3315 const isInstruct = power_user.instruct.enabled && main_api !== 'openai';
3300 const isImpersonate = type == 'impersonate';3316 const isImpersonate = type == 'impersonate';
33013317
3302 let message_already_generated = isImpersonate ? `${name1}: ` : `${name2}: `;
3303
3304 if (!(dryRun || type == 'regenerate' || type == 'swipe' || type == 'quiet')) {3318 if (!(dryRun || type == 'regenerate' || type == 'swipe' || type == 'quiet')) {
3305 const interruptedByCommand = await processCommands(String($('#send_textarea').val()));3319 const interruptedByCommand = await processCommands(String($('#send_textarea').val()));
33063320
@@ -3715,7 +3729,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3715 const storyStringParams = {3729 const storyStringParams = {
3716 description: description,3730 description: description,
3717 personality: personality,3731 personality: personality,
3718 persona: persona,3732 persona: power_user.persona_description_position == persona_description_positions.IN_PROMPT ? persona : '',
3719 scenario: scenario,3733 scenario: scenario,
3720 system: isInstruct ? system : '',3734 system: isInstruct ? system : '',
3721 char: name2,3735 char: name2,
@@ -3739,7 +3753,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3739 let oaiMessageExamples = [];3753 let oaiMessageExamples = [];
37403754
3741 if (main_api === 'openai') {3755 if (main_api === 'openai') {
3742 message_already_generated = '';
3743 oaiMessages = setOpenAIMessages(coreChat);3756 oaiMessages = setOpenAIMessages(coreChat);
3744 oaiMessageExamples = setOpenAIMessageExamples(mesExamplesArray);3757 oaiMessageExamples = setOpenAIMessageExamples(mesExamplesArray);
3745 }3758 }
@@ -3750,7 +3763,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3750 }3763 }
37513764
3752 let examplesString = '';3765 let examplesString = '';
3753 let chatString = '';3766 let chatString = addChatsPreamble(addChatsSeparator(''));
3754 let cyclePrompt = '';3767 let cyclePrompt = '';
37553768
3756 async function getMessagesTokenCount() {3769 async function getMessagesTokenCount() {
@@ -3759,10 +3772,10 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3759 storyString,3772 storyString,
3760 afterScenarioAnchor,3773 afterScenarioAnchor,
3761 examplesString,3774 examplesString,
3775 userAlignmentMessage,
3762 chatString,3776 chatString,
3763 quiet_prompt,3777 modifyLastPromptLine(''),
3764 cyclePrompt,3778 cyclePrompt,
3765 userAlignmentMessage,
3766 ].join('').replace(/\r/gm, '');3779 ].join('').replace(/\r/gm, '');
3767 return getTokenCountAsync(encodeString, power_user.token_padding);3780 return getTokenCountAsync(encodeString, power_user.token_padding);
3768 }3781 }
@@ -3793,8 +3806,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3793 }3806 }
37943807
3795 tokenCount += await getTokenCountAsync(item.replace(/\r/gm, ''));3808 tokenCount += await getTokenCountAsync(item.replace(/\r/gm, ''));
3796 chatString = item + chatString;
3797 if (tokenCount < this_max_context) {3809 if (tokenCount < this_max_context) {
3810 chatString = chatString + item;
3798 arrMes[index] = item;3811 arrMes[index] = item;
3799 lastAddedIndex = Math.max(lastAddedIndex, index);3812 lastAddedIndex = Math.max(lastAddedIndex, index);
3800 } else {3813 } else {
@@ -3820,8 +3833,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3820 }3833 }
38213834
3822 tokenCount += await getTokenCountAsync(item.replace(/\r/gm, ''));3835 tokenCount += await getTokenCountAsync(item.replace(/\r/gm, ''));
3823 chatString = item + chatString;
3824 if (tokenCount < this_max_context) {3836 if (tokenCount < this_max_context) {
3837 chatString = chatString + item;
3825 arrMes[i] = item;3838 arrMes[i] = item;
3826 lastAddedIndex = Math.max(lastAddedIndex, i);3839 lastAddedIndex = Math.max(lastAddedIndex, i);
3827 } else {3840 } else {
@@ -3882,7 +3895,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3882 cyclePrompt += oai_settings.continue_postfix;3895 cyclePrompt += oai_settings.continue_postfix;
3883 continue_mag += oai_settings.continue_postfix;3896 continue_mag += oai_settings.continue_postfix;
3884 }3897 }
3885 message_already_generated = continue_mag;
3886 }3898 }
38873899
3888 const originalType = type;3900 const originalType = type;
@@ -4019,15 +4031,16 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4019 async function checkPromptSize() {4031 async function checkPromptSize() {
4020 console.debug('---checking Prompt size');4032 console.debug('---checking Prompt size');
4021 setPromptString();4033 setPromptString();
4034 const jointMessages = mesSend.map((e) => `${e.extensionPrompts.join('')}${e.message}`).join('');
4022 const prompt = [4035 const prompt = [
4023 beforeScenarioAnchor,4036 beforeScenarioAnchor,
4024 storyString,4037 storyString,
4025 afterScenarioAnchor,4038 afterScenarioAnchor,
4026 mesExmString,4039 mesExmString,
4027 mesSend.map((e) => `${e.extensionPrompts.join('')}${e.message}`).join(''),4040 addChatsPreamble(addChatsSeparator(jointMessages)),
4028 '\n',4041 '\n',
4042 modifyLastPromptLine(''),
4029 generatedPromptCache,4043 generatedPromptCache,
4030 quiet_prompt,
4031 ].join('').replace(/\r/gm, '');4044 ].join('').replace(/\r/gm, '');
4032 let thisPromptContextSize = await getTokenCountAsync(prompt, power_user.token_padding);4045 let thisPromptContextSize = await getTokenCountAsync(prompt, power_user.token_padding);
40334046
@@ -4293,7 +4306,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4293 padding: power_user.token_padding,4306 padding: power_user.token_padding,
4294 main_api: main_api,4307 main_api: main_api,
4295 instruction: isInstruct ? substituteParams(power_user.prefer_character_prompt && system ? system : power_user.instruct.system_prompt) : '',4308 instruction: isInstruct ? substituteParams(power_user.prefer_character_prompt && system ? system : power_user.instruct.system_prompt) : '',
4296 userPersona: (power_user.persona_description || ''),4309 userPersona: (power_user.persona_description_position == persona_description_positions.IN_PROMPT ? (persona || '') : ''),
4297 };4310 };
42984311
4299 //console.log(additionalPromptStuff);4312 //console.log(additionalPromptStuff);
@@ -4309,7 +4322,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4309 console.debug(`pushed prompt bits to itemizedPrompts array. Length is now: ${itemizedPrompts.length}`);4322 console.debug(`pushed prompt bits to itemizedPrompts array. Length is now: ${itemizedPrompts.length}`);
43104323
4311 if (isStreamingEnabled() && type !== 'quiet') {4324 if (isStreamingEnabled() && type !== 'quiet') {
4312 streamingProcessor = new StreamingProcessor(type, force_name2, generation_started, message_already_generated);4325 streamingProcessor = new StreamingProcessor(type, force_name2, generation_started, continue_mag);
4313 if (isContinue) {4326 if (isContinue) {
4314 // Save reply does add cycle text to the prompt, so it's not needed here4327 // Save reply does add cycle text to the prompt, so it's not needed here
4315 streamingProcessor.firstMessageText = '';4328 streamingProcessor.firstMessageText = '';
@@ -5071,7 +5084,7 @@ async function promptItemize(itemizedPrompts, requestedMesId) {
5071 }5084 }
50725085
5073 navigator.clipboard.writeText(rawPromptValues);5086 navigator.clipboard.writeText(rawPromptValues);
5074 toastr.info('Copied!');5087 toastr.info(t`Copied!`);
5075 });5088 });
50765089
5077 popup.dlg.querySelector('#showRawPrompt').addEventListener('click', function () {5090 popup.dlg.querySelector('#showRawPrompt').addEventListener('click', function () {
@@ -5337,17 +5350,10 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc
5337 // Regex uses vars, so add before formatting5350 // Regex uses vars, so add before formatting
5338 getMessage = getRegexedString(getMessage, isImpersonate ? regex_placement.USER_INPUT : regex_placement.AI_OUTPUT);5351 getMessage = getRegexedString(getMessage, isImpersonate ? regex_placement.USER_INPUT : regex_placement.AI_OUTPUT);
53395352
5340 if (!displayIncompleteSentences && power_user.trim_sentences) {
5341 getMessage = trimToEndSentence(getMessage, power_user.include_newline);
5342 }
5343
5344 if (power_user.collapse_newlines) {5353 if (power_user.collapse_newlines) {
5345 getMessage = collapseNewlines(getMessage);5354 getMessage = collapseNewlines(getMessage);
5346 }5355 }
53475356
5348 if (power_user.trim_spaces) {
5349 getMessage = getMessage.trim();
5350 }
5351 // trailing invisible whitespace before every newlines, on a multiline string5357 // trailing invisible whitespace before every newlines, on a multiline string
5352 // "trailing whitespace on newlines \nevery line of the string \n?sample text" ->5358 // "trailing whitespace on newlines \nevery line of the string \n?sample text" ->
5353 // "trailing whitespace on newlines\nevery line of the string\nsample text"5359 // "trailing whitespace on newlines\nevery line of the string\nsample text"
@@ -5439,6 +5445,14 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc
5439 getMessage = getMessage.trim();5445 getMessage = getMessage.trim();
5440 }5446 }
54415447
5448 if (!displayIncompleteSentences && power_user.trim_sentences) {
5449 getMessage = trimToEndSentence(getMessage, power_user.include_newline);
5450 }
5451
5452 if (power_user.trim_spaces) {
5453 getMessage = getMessage.trim();
5454 }
5455
5442 return getMessage;5456 return getMessage;
5443}5457}
54445458
@@ -5669,7 +5683,7 @@ export function resetChatState() {
5669 // replaces deleted charcter name with system user since it will be displayed next.5683 // replaces deleted charcter name with system user since it will be displayed next.
5670 name2 = systemUserName;5684 name2 = systemUserName;
5671 // sets up system user to tell user about having deleted a character5685 // sets up system user to tell user about having deleted a character
5672 chat = [...safetychat];5686 chat.splice(0, chat.length, ...SAFETY_CHAT);
5673 // resets chat metadata5687 // resets chat metadata
5674 chat_metadata = {};5688 chat_metadata = {};
5675 // resets the characters array, forcing getcharacters to reset5689 // resets the characters array, forcing getcharacters to reset
@@ -6300,7 +6314,7 @@ export function setUserName(value) {
6300 console.log(`User name changed to ${name1}`);6314 console.log(`User name changed to ${name1}`);
6301 $('#your_name').val(name1);6315 $('#your_name').val(name1);
6302 if (power_user.persona_show_notifications) {6316 if (power_user.persona_show_notifications) {
6303 toastr.success(`Your messages will now be sent as ${name1}`, 'Current persona updated');6317 toastr.success(t`Your messages will now be sent as ${name1}`, t`Current persona updated`);
6304 }6318 }
6305 saveSettingsDebounced();6319 saveSettingsDebounced();
6306}6320}
@@ -6452,6 +6466,8 @@ export async function getSettings() {
6452 // Load power user settings6466 // Load power user settings
6453 await loadPowerUserSettings(settings, data);6467 await loadPowerUserSettings(settings, data);
64546468
6469 applyPowerUserSettings();
6470
6455 // Load character tags6471 // Load character tags
6456 loadTagsSettings(settings);6472 loadTagsSettings(settings);
64576473
@@ -6506,9 +6522,10 @@ export async function getSettings() {
6506 selected_button = settings.selected_button;6522 selected_button = settings.selected_button;
65076523
6508 if (data.enable_extensions) {6524 if (data.enable_extensions) {
6525 const enableAutoUpdate = Boolean(data.enable_extensions_auto_update);
6509 const isVersionChanged = settings.currentVersion !== currentVersion;6526 const isVersionChanged = settings.currentVersion !== currentVersion;
6510 await loadExtensionSettings(settings, isVersionChanged);6527 await loadExtensionSettings(settings, isVersionChanged, enableAutoUpdate);
6511 eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED);6528 await eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED);
6512 }6529 }
65136530
6514 firstRun = !!settings.firstRun;6531 firstRun = !!settings.firstRun;
@@ -8379,6 +8396,12 @@ const CONNECT_API_MAP = {
8379 button: '#api_button_openai',8396 button: '#api_button_openai',
8380 source: chat_completion_sources.OPENAI,8397 source: chat_completion_sources.OPENAI,
8381 },8398 },
8399 // Google alias
8400 'google': {
8401 selected: 'openai',
8402 button: '#api_button_openai',
8403 source: chat_completion_sources.MAKERSUITE,
8404 },
8382 // OpenRouter special naming, to differentiate between chat comp and text comp8405 // OpenRouter special naming, to differentiate between chat comp and text comp
8383 'openrouter': {8406 'openrouter': {
8384 selected: 'openai',8407 selected: 'openai',
@@ -8464,7 +8487,7 @@ async function disableInstructCallback() {
8464/**8487/**
8465 * @param {string} text API name8488 * @param {string} text API name
8466 */8489 */
8467async function connectAPISlash(_, text) {8490async function connectAPISlash(args, text) {
8468 if (!text.trim()) {8491 if (!text.trim()) {
8469 for (const [key, config] of Object.entries(CONNECT_API_MAP)) {8492 for (const [key, config] of Object.entries(CONNECT_API_MAP)) {
8470 if (config.selected !== main_api) continue;8493 if (config.selected !== main_api) continue;
@@ -8487,12 +8510,15 @@ async function connectAPISlash(_, text) {
84878510
8488 return key;8511 return key;
8489 }8512 }
8513
8514 console.error('FIXME: The current API is not in the API map');
8515 return '';
8490 }8516 }
84918517
8492 const apiConfig = CONNECT_API_MAP[text.toLowerCase()];8518 const apiConfig = CONNECT_API_MAP[text.toLowerCase()];
8493 if (!apiConfig) {8519 if (!apiConfig) {
8494 toastr.error(`Error: ${text} is not a valid API`);8520 toastr.error(`Error: ${text} is not a valid API`);
8495 return;8521 return '';
8496 }8522 }
84978523
8498 $(`#main_api option[value='${apiConfig.selected || text}']`).prop('selected', true);8524 $(`#main_api option[value='${apiConfig.selected || text}']`).prop('selected', true);
@@ -8512,14 +8538,18 @@ async function connectAPISlash(_, text) {
8512 $(apiConfig.button).trigger('click');8538 $(apiConfig.button).trigger('click');
8513 }8539 }
85148540
8515 toastr.info(`API set to ${text}, trying to connect..`);8541 const quiet = isTrueBoolean(args?.quiet);
8542 const toast = quiet ? jQuery() : toastr.info(`API set to ${text}, trying to connect..`);
85168543
8517 try {8544 try {
8518 await waitUntilCondition(() => online_status !== 'no_connection', 10000, 100);8545 await waitUntilCondition(() => online_status !== 'no_connection', 10000, 100);
8519 console.log('Connection successful');8546 console.log('Connection successful');
8520 } catch {8547 } catch {
8521 console.log('Could not connect after 5 seconds, skipping.');8548 console.log('Could not connect after 10 seconds, skipping.');
8522 }8549 }
8550
8551 toastr.clear(toast);
8552 return text;
8523}8553}
85248554
8525/**8555/**
@@ -8814,16 +8844,21 @@ export async function handleDeleteCharacter(this_chid, delete_chats) {
8814/**8844/**
8815 * Deletes a character completely, including associated chats if specified8845 * Deletes a character completely, including associated chats if specified
8816 *8846 *
8817 * @param {string} characterKey - The key (avatar) of the character to be deleted8847 * @param {string|string[]} characterKey - The key (avatar) of the character to be deleted
8818 * @param {Object} [options] - Optional parameters for the deletion8848 * @param {Object} [options] - Optional parameters for the deletion
8819 * @param {boolean} [options.deleteChats=true] - Whether to delete associated chats or not8849 * @param {boolean} [options.deleteChats=true] - Whether to delete associated chats or not
8820 * @return {Promise<void>} - A promise that resolves when the character is successfully deleted8850 * @return {Promise<void>} - A promise that resolves when the character is successfully deleted
8821 */8851 */
8822export async function deleteCharacter(characterKey, { deleteChats = true } = {}) {8852export async function deleteCharacter(characterKey, { deleteChats = true } = {}) {
8823 const character = characters.find(x => x.avatar == characterKey);8853 if (!Array.isArray(characterKey)) {
8854 characterKey = [characterKey];
8855 }
8856
8857 for (const key of characterKey) {
8858 const character = characters.find(x => x.avatar == key);
8824 if (!character) {8859 if (!character) {
8825 toastr.warning(`Character ${characterKey} not found. Cannot be deleted.`);8860 toastr.warning(`Character ${key} not found. Skipping deletion.`);
8826 return;8861 continue;
8827 }8862 }
88288863
8829 const chid = characters.indexOf(character);8864 const chid = characters.indexOf(character);
@@ -8839,10 +8874,12 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
8839 });8874 });
88408875
8841 if (!response.ok) {8876 if (!response.ok) {
8842 throw new Error(`Failed to delete character: ${response.status} ${response.statusText}`);8877 toastr.error(`${response.status} ${response.statusText}`, 'Failed to delete character');
8878 continue;
8843 }8879 }
88448880
8845 await removeCharacterFromUI(character.name, character.avatar);8881 delete tag_map[character.avatar];
8882 select_rm_info('char_delete', character.name);
88468883
8847 if (deleteChats) {8884 if (deleteChats) {
8848 for (const chat of pastChats) {8885 for (const chat of pastChats) {
@@ -8851,35 +8888,30 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
8851 }8888 }
8852 }8889 }
88538890
8854 eventSource.emit(event_types.CHARACTER_DELETED, { id: this_chid, character: characters[this_chid] });8891 await eventSource.emit(event_types.CHARACTER_DELETED, { id: chid, character: character });
8892 }
8893
8894 await removeCharacterFromUI();
8855}8895}
88568896
8857/**8897/**
8858 * Function to delete a character from UI after character deletion API success.8898 * Function to delete a character from UI after character deletion API success.
8859 * It manages necessary UI changes such as closing advanced editing popup, unsetting8899 * It manages necessary UI changes such as closing advanced editing popup, unsetting
8860 * character ID, resetting characters array and chat metadata, deselecting character's tab8900 * character ID, resetting characters array and chat metadata, deselecting character's tab
8861 * panel, removing character name from navigation tabs, clearing chat, removing character's8901 * panel, removing character name from navigation tabs, clearing chat, fetching updated list of characters.
8862 * avatar from tag_map, fetching updated list of characters and updating the 'deleted
8863 * character' message.
8864 * It also ensures to save the settings after all the operations.8902 * It also ensures to save the settings after all the operations.
8865 *
8866 * @param {string} name - The name of the character to be deleted.
8867 * @param {string} avatar - The avatar URL of the character to be deleted.
8868 * @param {boolean} reloadCharacters - Whether the character list should be refreshed after deletion.
8869 */8903 */
8870async function removeCharacterFromUI(name, avatar, reloadCharacters = true) {8904async function removeCharacterFromUI() {
8871 await clearChat();8905 await clearChat();
8872 $('#character_cross').click();8906 $('#character_cross').click();
8873 this_chid = undefined;8907 this_chid = undefined;
8874 characters.length = 0;8908 characters.length = 0;
8875 name2 = systemUserName;8909 name2 = systemUserName;
8876 chat = [...safetychat];8910 chat.splice(0, chat.length, ...SAFETY_CHAT);
8877 chat_metadata = {};8911 chat_metadata = {};
8878 $(document.getElementById('rm_button_selected_ch')).children('h2').text('');8912 $(document.getElementById('rm_button_selected_ch')).children('h2').text('');
8879 this_chid = undefined;8913 this_chid = undefined;
8880 delete tag_map[avatar];8914 await getCharacters();
8881 if (reloadCharacters) await getCharacters();
8882 select_rm_info('char_delete', name);
8883 await printMessages();8915 await printMessages();
8884 saveSettingsDebounced();8916 saveSettingsDebounced();
8885}8917}
@@ -8978,6 +9010,15 @@ jQuery(async function () {
8978 name: 'api',9010 name: 'api',
8979 callback: connectAPISlash,9011 callback: connectAPISlash,
8980 returns: 'the current API',9012 returns: 'the current API',
9013 namedArgumentList: [
9014 SlashCommandNamedArgument.fromProps({
9015 name: 'quiet',
9016 description: 'Suppress the toast message on connection',
9017 typeList: [ARGUMENT_TYPE.BOOLEAN],
9018 defaultValue: 'false',
9019 enumList: commonEnumProviders.boolean('trueFalse')(),
9020 }),
9021 ],
8981 unnamedArgumentList: [9022 unnamedArgumentList: [
8982 SlashCommandArgument.fromProps({9023 SlashCommandArgument.fromProps({
8983 description: 'API to connect to',9024 description: 'API to connect to',
@@ -9497,11 +9538,7 @@ jQuery(async function () {
9497 const oldFileNameFull = $(this).closest('.select_chat_block_wrapper').find('.select_chat_block_filename').text();9538 const oldFileNameFull = $(this).closest('.select_chat_block_wrapper').find('.select_chat_block_filename').text();
9498 const oldFileName = oldFileNameFull.replace('.jsonl', '');9539 const oldFileName = oldFileNameFull.replace('.jsonl', '');
94999540
9500 const popupText = `<h3>Enter the new name for the chat:<h3>9541 const popupText = await renderTemplateAsync('chatRename');
9501 <small>!!Using an existing filename will produce an error!!<br>
9502 This will break the link between checkpoint chats.<br>
9503 No need to add '.jsonl' at the end.<br>
9504 </small>`;
9505 const newName = await callPopup(popupText, 'input', oldFileName);9542 const newName = await callPopup(popupText, 'input', oldFileName);
95069543
9507 if (!newName || newName == oldFileName) {9544 if (!newName || newName == oldFileName) {
@@ -9709,12 +9746,7 @@ jQuery(async function () {
9709 else if (id == 'option_start_new_chat') {9746 else if (id == 'option_start_new_chat') {
9710 if ((selected_group || this_chid !== undefined) && !is_send_press) {9747 if ((selected_group || this_chid !== undefined) && !is_send_press) {
9711 let deleteCurrentChat = false;9748 let deleteCurrentChat = false;
9712 const result = await Popup.show.confirm('Start new chat?', `9749 const result = await Popup.show.confirm(t`Start new chat?`, await renderTemplateAsync('newChatConfirm'), {
9713 <label for="del_chat_checkbox" class="checkbox_label justifyCenter"
9714 title="If necessary, you can later restore this chat file from the /backups folder">
9715 <input type="checkbox" id="del_chat_checkbox" />
9716 <small>Also delete the current chat file</small>
9717 </label>`, {
9718 onClose: () => deleteCurrentChat = !!$('#del_chat_checkbox').prop('checked'),9750 onClose: () => deleteCurrentChat = !!$('#del_chat_checkbox').prop('checked'),
9719 });9751 });
9720 if (!result) {9752 if (!result) {
@@ -10226,10 +10258,10 @@ jQuery(async function () {
1022610258
10227 let deleteOnlySwipe = false;10259 let deleteOnlySwipe = false;
10228 if (power_user.confirm_message_delete && fromSlashCommand !== true) {10260 if (power_user.confirm_message_delete && fromSlashCommand !== true) {
10229 const result = await callGenericPopup('Are you sure you want to delete this message?', POPUP_TYPE.CONFIRM, null, {10261 const result = await callGenericPopup(t`Are you sure you want to delete this message?`, POPUP_TYPE.CONFIRM, null, {
10230 okButton: canDeleteSwipe ? 'Delete Swipe' : 'Delete Message',10262 okButton: canDeleteSwipe ? t`Delete Swipe` : t`Delete Message`,
10231 cancelButton: 'Cancel',10263 cancelButton: 'Cancel',
10232 customButtons: canDeleteSwipe ? ['Delete Message'] : null,10264 customButtons: canDeleteSwipe ? [t`Delete Message`] : null,
10233 });10265 });
10234 if (!result) {10266 if (!result) {
10235 return;10267 return;
public/scripts/BulkEditOverlay.js+11 -11
@@ -108,14 +108,12 @@ class CharacterContextMenu {
108 * Delete one or more characters,108 * Delete one or more characters,
109 * opens a popup.109 * opens a popup.
110 *110 *
111 * @param {number} characterId111 * @param {string|string[]} characterKey
112 * @param {boolean} [deleteChats]112 * @param {boolean} [deleteChats]
113 * @returns {Promise<void>}113 * @returns {Promise<void>}
114 */114 */
115 static delete = async (characterId, deleteChats = false) => {115 static delete = async (characterKey, deleteChats = false) => {
116 const character = CharacterContextMenu.#getCharacter(characterId);116 await deleteCharacter(characterKey, { deleteChats: deleteChats });
117
118 await deleteCharacter(character.avatar, { deleteChats: deleteChats });
119 };117 };
120118
121 static #getCharacter = (characterId) => characters[characterId] ?? null;119 static #getCharacter = (characterId) => characters[characterId] ?? null;
@@ -599,8 +597,7 @@ class BulkEditOverlay {
599597
600 this.container.removeEventListener('mouseup', cancelHold);598 this.container.removeEventListener('mouseup', cancelHold);
601 this.container.removeEventListener('touchend', cancelHold);599 this.container.removeEventListener('touchend', cancelHold);
602 },600 }, BulkEditOverlay.longPressDelay);
603 BulkEditOverlay.longPressDelay);
604 };601 };
605602
606 handleLongPressEnd = (event) => {603 handleLongPressEnd = (event) => {
@@ -847,11 +844,14 @@ class BulkEditOverlay {
847 const deleteChats = document.getElementById('del_char_checkbox').checked ?? false;844 const deleteChats = document.getElementById('del_char_checkbox').checked ?? false;
848845
849 showLoader();846 showLoader();
850 toastr.info('We\'re deleting your characters, please wait...', 'Working on it');847 const toast = toastr.info('We\'re deleting your characters, please wait...', 'Working on it');
851 return Promise.allSettled(characterIds.map(async characterId => CharacterContextMenu.delete(characterId, deleteChats)))848 const avatarList = characterIds.map(id => characters[id]?.avatar).filter(a => a);
852 .then(() => getCharacters())849 return CharacterContextMenu.delete(avatarList, deleteChats)
853 .then(() => this.browseState())850 .then(() => this.browseState())
854 .finally(() => hideLoader());851 .finally(() => {
852 toastr.clear(toast);
853 hideLoader();
854 });
855 });855 });
856856
857 // At this moment the popup is already changed in the dom, but not yet closed/resolved. We build the avatar list here857 // At this moment the popup is already changed in the dom, but not yet closed/resolved. We build the avatar list here
public/scripts/extensions.js+33 -7
@@ -21,6 +21,7 @@ const defaultUrl = 'http://localhost:5100';
21let saveMetadataTimeout = null;21let saveMetadataTimeout = null;
2222
23let requiresReload = false;23let requiresReload = false;
24let stateChanged = false;
2425
25export function saveMetadataDebounced() {26export function saveMetadataDebounced() {
26 const context = getContext();27 const context = getContext();
@@ -238,6 +239,7 @@ function onEnableExtensionClick() {
238239
239async function enableExtension(name, reload = true) {240async function enableExtension(name, reload = true) {
240 extension_settings.disabledExtensions = extension_settings.disabledExtensions.filter(x => x !== name);241 extension_settings.disabledExtensions = extension_settings.disabledExtensions.filter(x => x !== name);
242 stateChanged = true;
241 await saveSettings();243 await saveSettings();
242 if (reload) {244 if (reload) {
243 location.reload();245 location.reload();
@@ -248,6 +250,7 @@ async function enableExtension(name, reload = true) {
248250
249async function disableExtension(name, reload = true) {251async function disableExtension(name, reload = true) {
250 extension_settings.disabledExtensions.push(name);252 extension_settings.disabledExtensions.push(name);
253 stateChanged = true;
251 await saveSettings();254 await saveSettings();
252 if (reload) {255 if (reload) {
253 location.reload();256 location.reload();
@@ -304,7 +307,7 @@ async function activateExtensions() {
304307
305 if (!isDisabled) {308 if (!isDisabled) {
306 const promise = Promise.all([addExtensionScript(name, manifest), addExtensionStyle(name, manifest)]);309 const promise = Promise.all([addExtensionScript(name, manifest), addExtensionStyle(name, manifest)]);
307 promise310 await promise
308 .then(() => activeExtensions.add(name))311 .then(() => activeExtensions.add(name))
309 .catch(err => console.log('Could not activate extension: ' + name, err));312 .catch(err => console.log('Could not activate extension: ' + name, err));
310 promises.push(promise);313 promises.push(promise);
@@ -657,7 +660,29 @@ async function showExtensionsDetails() {
657 await oldPopup.complete(POPUP_RESULT.CANCELLED);660 await oldPopup.complete(POPUP_RESULT.CANCELLED);
658 }661 }
659662
660 const popup = new Popup(html, POPUP_TYPE.TEXT, '', { okButton: 'Close', wide: true, large: true, customButtons: [updateAllButton], allowVerticalScrolling: true });663 let waitingForSave = false;
664
665 const popup = new Popup(html, POPUP_TYPE.TEXT, '', {
666 okButton: 'Close',
667 wide: true,
668 large: true,
669 customButtons: [updateAllButton],
670 allowVerticalScrolling: true,
671 onClosing: async () => {
672 if (waitingForSave) {
673 return false;
674 }
675 if (stateChanged) {
676 waitingForSave = true;
677 const toast = toastr.info('The page will be reloaded shortly...', 'Extensions state changed');
678 await saveSettings();
679 toastr.clear(toast);
680 waitingForSave = false;
681 requiresReload = true;
682 }
683 return true;
684 },
685 });
661 popupPromise = popup.show();686 popupPromise = popup.show();
662 } catch (error) {687 } catch (error) {
663 toastr.error('Error loading extensions. See browser console for details.');688 toastr.error('Error loading extensions. See browser console for details.');
@@ -797,16 +822,17 @@ export async function installExtension(url) {
797 const response = await request.json();822 const response = await request.json();
798 toastr.success(`Extension "${response.display_name}" by ${response.author} (version ${response.version}) has been installed successfully!`, 'Extension installation successful');823 toastr.success(`Extension "${response.display_name}" by ${response.author} (version ${response.version}) has been installed successfully!`, 'Extension installation successful');
799 console.debug(`Extension "${response.display_name}" has been installed successfully at ${response.extensionPath}`);824 console.debug(`Extension "${response.display_name}" has been installed successfully at ${response.extensionPath}`);
800 await loadExtensionSettings({}, false);825 await loadExtensionSettings({}, false, false);
801 eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED);826 await eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED);
802}827}
803828
804/**829/**
805 * Loads extension settings from the app settings.830 * Loads extension settings from the app settings.
806 * @param {object} settings App Settings831 * @param {object} settings App Settings
807 * @param {boolean} versionChanged Is this a version change?832 * @param {boolean} versionChanged Is this a version change?
833 * @param {boolean} enableAutoUpdate Enable auto-update
808 */834 */
809async function loadExtensionSettings(settings, versionChanged) {835async function loadExtensionSettings(settings, versionChanged, enableAutoUpdate) {
810 if (settings.extension_settings) {836 if (settings.extension_settings) {
811 Object.assign(extension_settings, settings.extension_settings);837 Object.assign(extension_settings, settings.extension_settings);
812 }838 }
@@ -817,11 +843,11 @@ async function loadExtensionSettings(settings, versionChanged) {
817 $('#extensions_notify_updates').prop('checked', extension_settings.notifyUpdates);843 $('#extensions_notify_updates').prop('checked', extension_settings.notifyUpdates);
818844
819 // Activate offline extensions845 // Activate offline extensions
820 eventSource.emit(event_types.EXTENSIONS_FIRST_LOAD);846 await eventSource.emit(event_types.EXTENSIONS_FIRST_LOAD);
821 extensionNames = await discoverExtensions();847 extensionNames = await discoverExtensions();
822 manifests = await getManifests(extensionNames);848 manifests = await getManifests(extensionNames);
823849
824 if (versionChanged) {850 if (versionChanged && enableAutoUpdate) {
825 await autoUpdateExtensions(false);851 await autoUpdateExtensions(false);
826 }852 }
827853
public/scripts/extensions/caption/settings.html+4 -1
@@ -20,7 +20,7 @@
20 <option value="zerooneai">01.AI (Yi)</option>20 <option value="zerooneai">01.AI (Yi)</option>
21 <option value="anthropic">Anthropic</option>21 <option value="anthropic">Anthropic</option>
22 <option value="custom" data-i18n="Custom (OpenAI-compatible)">Custom (OpenAI-compatible)</option>22 <option value="custom" data-i18n="Custom (OpenAI-compatible)">Custom (OpenAI-compatible)</option>
23 <option value="google">Google MakerSuite</option>23 <option value="google">Google AI Studio</option>
24 <option value="koboldcpp">KoboldCpp</option>24 <option value="koboldcpp">KoboldCpp</option>
25 <option value="llamacpp">llama.cpp</option>25 <option value="llamacpp">llama.cpp</option>
26 <option value="ollama">Ollama</option>26 <option value="ollama">Ollama</option>
@@ -45,8 +45,11 @@
45 <option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>45 <option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>
46 <option data-type="google" value="gemini-pro-vision">gemini-pro-vision</option>46 <option data-type="google" value="gemini-pro-vision">gemini-pro-vision</option>
47 <option data-type="google" value="gemini-1.5-flash-latest">gemini-1.5-flash-latest</option>47 <option data-type="google" value="gemini-1.5-flash-latest">gemini-1.5-flash-latest</option>
48 <option data-type="google" value="gemini-1.5-flash-exp-0827">gemini-1.5-flash-exp-0827</option>
49 <option data-type="google" value="gemini-1.5-flash-8b-exp-0827">gemini-1.5-flash-8b-exp-0827</option>
48 <option data-type="google" value="gemini-1.5-pro-latest">gemini-1.5-pro-latest</option>50 <option data-type="google" value="gemini-1.5-pro-latest">gemini-1.5-pro-latest</option>
49 <option data-type="google" value="gemini-1.5-pro-exp-0801">gemini-1.5-pro-exp-0801</option>51 <option data-type="google" value="gemini-1.5-pro-exp-0801">gemini-1.5-pro-exp-0801</option>
52 <option data-type="google" value="gemini-1.5-pro-exp-0827">gemini-1.5-pro-exp-0827</option>
50 <option data-type="openrouter" value="openai/gpt-4-vision-preview">openai/gpt-4-vision-preview</option>53 <option data-type="openrouter" value="openai/gpt-4-vision-preview">openai/gpt-4-vision-preview</option>
51 <option data-type="openrouter" value="openai/gpt-4o">openai/gpt-4o</option>54 <option data-type="openrouter" value="openai/gpt-4o">openai/gpt-4o</option>
52 <option data-type="openrouter" value="openai/gpt-4-turbo">openai/gpt-4-turbo</option>55 <option data-type="openrouter" value="openai/gpt-4-turbo">openai/gpt-4-turbo</option>
public/scripts/extensions/memory/index.js+20 -6
@@ -1,4 +1,4 @@
1import { getStringHash, debounce, waitUntilCondition, extractAllWords } from '../../utils.js';1import { getStringHash, debounce, waitUntilCondition, extractAllWords, isTrueBoolean } from '../../utils.js';
2import { getContext, getApiUrl, extension_settings, doExtrasFetch, modules, renderExtensionTemplateAsync } from '../../extensions.js';2import { getContext, getApiUrl, extension_settings, doExtrasFetch, modules, renderExtensionTemplateAsync } from '../../extensions.js';
3import {3import {
4 activateSendButtons,4 activateSendButtons,
@@ -26,6 +26,7 @@ import { SlashCommand } from '../../slash-commands/SlashCommand.js';
26import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';26import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';
27import { MacrosParser } from '../../macros.js';27import { MacrosParser } from '../../macros.js';
28import { countWebLlmTokens, generateWebLlmChatPrompt, getWebLlmContextSize, isWebLlmSupported } from '../shared.js';28import { countWebLlmTokens, generateWebLlmChatPrompt, getWebLlmContextSize, isWebLlmSupported } from '../shared.js';
29import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
29export { MODULE_NAME };30export { MODULE_NAME };
3031
31const MODULE_NAME = '1_memory';32const MODULE_NAME = '1_memory';
@@ -456,7 +457,12 @@ async function onChatEvent() {
456 }457 }
457}458}
458459
459async function forceSummarizeChat() {460/**
461 * Forces a summary generation for the current chat.
462 * @param {boolean} quiet If an informational toast should be displayed
463 * @returns {Promise<string>} Summarized text
464 */
465async function forceSummarizeChat(quiet) {
460 if (extension_settings.memory.source === summary_sources.extras) {466 if (extension_settings.memory.source === summary_sources.extras) {
461 toastr.warning('Force summarization is not supported for Extras API');467 toastr.warning('Force summarization is not supported for Extras API');
462 return;468 return;
@@ -471,7 +477,7 @@ async function forceSummarizeChat() {
471 return '';477 return '';
472 }478 }
473479
474 const toast = toastr.info('Summarizing chat...', 'Please wait', { timeOut: 0, extendedTimeOut: 0 });480 const toast = quiet ? jQuery() : toastr.info('Summarizing chat...', 'Please wait', { timeOut: 0, extendedTimeOut: 0 });
475 const value = extension_settings.memory.source === summary_sources.main481 const value = extension_settings.memory.source === summary_sources.main
476 ? await summarizeChatMain(context, true, skipWIAN)482 ? await summarizeChatMain(context, true, skipWIAN)
477 : await summarizeChatWebLLM(context, true);483 : await summarizeChatWebLLM(context, true);
@@ -494,9 +500,10 @@ async function forceSummarizeChat() {
494async function summarizeCallback(args, text) {500async function summarizeCallback(args, text) {
495 text = text.trim();501 text = text.trim();
496502
497 // Using forceSummarizeChat to summarize the current chat503 // Summarize the current chat if no text provided
498 if (!text) {504 if (!text) {
499 return await forceSummarizeChat();505 const quiet = isTrueBoolean(args.quiet);
506 return await forceSummarizeChat(quiet);
500 }507 }
501508
502 const source = args.source || extension_settings.memory.source;509 const source = args.source || extension_settings.memory.source;
@@ -1005,7 +1012,7 @@ function setupListeners() {
1005 $('#memory_prompt_words').off('click').on('input', onMemoryPromptWordsInput);1012 $('#memory_prompt_words').off('click').on('input', onMemoryPromptWordsInput);
1006 $('#memory_prompt_interval').off('click').on('input', onMemoryPromptIntervalInput);1013 $('#memory_prompt_interval').off('click').on('input', onMemoryPromptIntervalInput);
1007 $('#memory_prompt').off('click').on('input', onMemoryPromptInput);1014 $('#memory_prompt').off('click').on('input', onMemoryPromptInput);
1008 $('#memory_force_summarize').off('click').on('click', forceSummarizeChat);1015 $('#memory_force_summarize').off('click').on('click', () => forceSummarizeChat(false));
1009 $('#memory_template').off('click').on('input', onMemoryTemplateInput);1016 $('#memory_template').off('click').on('input', onMemoryTemplateInput);
1010 $('#memory_depth').off('click').on('input', onMemoryDepthInput);1017 $('#memory_depth').off('click').on('input', onMemoryDepthInput);
1011 $('#memory_role').off('click').on('input', onMemoryRoleInput);1018 $('#memory_role').off('click').on('input', onMemoryRoleInput);
@@ -1055,6 +1062,13 @@ jQuery(async function () {
1055 typeList: [ARGUMENT_TYPE.STRING],1062 typeList: [ARGUMENT_TYPE.STRING],
1056 defaultValue: '',1063 defaultValue: '',
1057 }),1064 }),
1065 SlashCommandNamedArgument.fromProps({
1066 name: 'quiet',
1067 description: 'suppress the toast message when summarizing the chat',
1068 typeList: [ARGUMENT_TYPE.BOOLEAN],
1069 defaultValue: 'false',
1070 enumList: commonEnumProviders.boolean('trueFalse')(),
1071 }),
1058 ],1072 ],
1059 unnamedArgumentList: [1073 unnamedArgumentList: [
1060 new SlashCommandArgument('text to summarize', [ARGUMENT_TYPE.STRING], false, false, ''),1074 new SlashCommandArgument('text to summarize', [ARGUMENT_TYPE.STRING], false, false, ''),
public/scripts/extensions/quick-reply/html/settings.html+3 -0
@@ -11,6 +11,9 @@
11 <label class="flex-container">11 <label class="flex-container">
12 <input type="checkbox" id="qr--isCombined"><span data-i18n="Combine Quick Replies">Combine Quick Replies</span>12 <input type="checkbox" id="qr--isCombined"><span data-i18n="Combine Quick Replies">Combine Quick Replies</span>
13 </label>13 </label>
14 <label class="flex-container">
15 <input type="checkbox" id="qr--showPopoutButton"><span data-i18n="Show Popout Button">Show Popout Button</span>
16 </label>
1417
15 <hr>18 <hr>
1619
public/scripts/extensions/quick-reply/src/QuickReplySettings.js+2 -0
@@ -16,6 +16,7 @@ export class QuickReplySettings {
16 /**@type {Boolean}*/ isEnabled = false;16 /**@type {Boolean}*/ isEnabled = false;
17 /**@type {Boolean}*/ isCombined = false;17 /**@type {Boolean}*/ isCombined = false;
18 /**@type {Boolean}*/ isPopout = false;18 /**@type {Boolean}*/ isPopout = false;
19 /**@type {Boolean}*/ showPopoutButton = true;
19 /**@type {QuickReplyConfig}*/ config;20 /**@type {QuickReplyConfig}*/ config;
20 /**@type {QuickReplyConfig}*/ _chatConfig;21 /**@type {QuickReplyConfig}*/ _chatConfig;
21 get chatConfig() {22 get chatConfig() {
@@ -79,6 +80,7 @@ export class QuickReplySettings {
79 isEnabled: this.isEnabled,80 isEnabled: this.isEnabled,
80 isCombined: this.isCombined,81 isCombined: this.isCombined,
81 isPopout: this.isPopout,82 isPopout: this.isPopout,
83 showPopoutButton: this.showPopoutButton,
82 config: this.config,84 config: this.config,
83 };85 };
84 }86 }
public/scripts/extensions/quick-reply/src/ui/ButtonUi.js+3 -0
@@ -69,6 +69,8 @@ export class ButtonUi {
69 root.id = 'qr--bar';69 root.id = 'qr--bar';
70 root.classList.add('flex-container');70 root.classList.add('flex-container');
71 root.classList.add('flexGap5');71 root.classList.add('flexGap5');
72 if (this.settings.showPopoutButton) {
73 root.classList.add('popoutVisible');
72 const popout = document.createElement('div'); {74 const popout = document.createElement('div'); {
73 popout.id = 'qr--popoutTrigger';75 popout.id = 'qr--popoutTrigger';
74 popout.classList.add('menu_button');76 popout.classList.add('menu_button');
@@ -81,6 +83,7 @@ export class ButtonUi {
81 });83 });
82 root.append(popout);84 root.append(popout);
83 }85 }
86 }
84 if (this.settings.isCombined) {87 if (this.settings.isCombined) {
85 const buttons = document.createElement('div'); {88 const buttons = document.createElement('div'); {
86 buttonHolder = buttons;89 buttonHolder = buttons;
public/scripts/extensions/quick-reply/src/ui/SettingsUi.js+10 -0
@@ -14,6 +14,7 @@ export class SettingsUi {
1414
15 /**@type {HTMLInputElement}*/ isEnabled;15 /**@type {HTMLInputElement}*/ isEnabled;
16 /**@type {HTMLInputElement}*/ isCombined;16 /**@type {HTMLInputElement}*/ isCombined;
17 /**@type {HTMLInputElement}*/ showPopoutButton;
1718
18 /**@type {HTMLElement}*/ globalSetList;19 /**@type {HTMLElement}*/ globalSetList;
1920
@@ -79,6 +80,10 @@ export class SettingsUi {
79 this.isCombined = this.dom.querySelector('#qr--isCombined');80 this.isCombined = this.dom.querySelector('#qr--isCombined');
80 this.isCombined.checked = this.settings.isCombined;81 this.isCombined.checked = this.settings.isCombined;
81 this.isCombined.addEventListener('click', ()=>this.onIsCombined());82 this.isCombined.addEventListener('click', ()=>this.onIsCombined());
83
84 this.showPopoutButton = this.dom.querySelector('#qr--showPopoutButton');
85 this.showPopoutButton.checked = this.settings.showPopoutButton;
86 this.showPopoutButton.addEventListener('click', ()=>this.onShowPopoutButton());
82 }87 }
8388
84 prepareGlobalSetList() {89 prepareGlobalSetList() {
@@ -235,6 +240,11 @@ export class SettingsUi {
235 this.settings.save();240 this.settings.save();
236 }241 }
237242
243 async onShowPopoutButton() {
244 this.settings.showPopoutButton = this.showPopoutButton.checked;
245 this.settings.save();
246 }
247
238 async onGlobalSetListSort() {248 async onGlobalSetListSort() {
239 this.settings.config.setList = Array.from(this.globalSetList.children).map((it,idx)=>{249 this.settings.config.setList = Array.from(this.globalSetList.children).map((it,idx)=>{
240 const set = this.settings.config.setList[Number(it.getAttribute('data-order'))];250 const set = this.settings.config.setList[Number(it.getAttribute('data-order'))];
public/scripts/extensions/quick-reply/style.css+3 -1
@@ -27,7 +27,6 @@
27 max-width: 100%;27 max-width: 100%;
28 overflow-x: auto;28 overflow-x: auto;
29 order: 1;29 order: 1;
30 padding-right: 2.5em;
31 position: relative;30 position: relative;
32}31}
33#qr--bar > #qr--popoutTrigger {32#qr--bar > #qr--popoutTrigger {
@@ -35,6 +34,9 @@
35 right: 0.25em;34 right: 0.25em;
36 top: 0;35 top: 0;
37}36}
37#qr--bar.popoutVisible {
38 padding-right: 2.5em;
39}
38#qr--popout {40#qr--popout {
39 display: flex;41 display: flex;
40 flex-direction: column;42 flex-direction: column;
public/scripts/extensions/quick-reply/style.less+3 -1
@@ -25,7 +25,6 @@
25 max-width: 100%;25 max-width: 100%;
26 overflow-x: auto;26 overflow-x: auto;
27 order: 1;27 order: 1;
28 padding-right: 2.5em;
29 position: relative;28 position: relative;
3029
31 >#qr--popoutTrigger {30 >#qr--popoutTrigger {
@@ -34,6 +33,9 @@
34 top: 0;33 top: 0;
35 }34 }
36}35}
36#qr--bar.popoutVisible {
37 padding-right: 2.5em;
38}
3739
38#qr--popout {40#qr--popout {
39 display: flex;41 display: flex;
public/scripts/extensions/shared.js+1 -1
@@ -141,7 +141,7 @@ function throwIfInvalidModel(useReverseProxy) {
141 }141 }
142142
143 if (extension_settings.caption.multimodal_api === 'google' && !secret_state[SECRET_KEYS.MAKERSUITE] && !useReverseProxy) {143 if (extension_settings.caption.multimodal_api === 'google' && !secret_state[SECRET_KEYS.MAKERSUITE] && !useReverseProxy) {
144 throw new Error('MakerSuite API key is not set.');144 throw new Error('Google AI Studio API key is not set.');
145 }145 }
146146
147 if (extension_settings.caption.multimodal_api === 'ollama' && !textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]) {147 if (extension_settings.caption.multimodal_api === 'ollama' && !textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]) {
public/scripts/extensions/stable-diffusion/index.js+63 -1
@@ -52,6 +52,7 @@ const sources = {
52 pollinations: 'pollinations',52 pollinations: 'pollinations',
53 stability: 'stability',53 stability: 'stability',
54 blockentropy: 'blockentropy',54 blockentropy: 'blockentropy',
55 huggingface: 'huggingface',
55};56};
5657
57const initiators = {58const initiators = {
@@ -454,6 +455,7 @@ async function loadSettings() {
454 $('#sd_command_visible').prop('checked', extension_settings.sd.command_visible);455 $('#sd_command_visible').prop('checked', extension_settings.sd.command_visible);
455 $('#sd_interactive_visible').prop('checked', extension_settings.sd.interactive_visible);456 $('#sd_interactive_visible').prop('checked', extension_settings.sd.interactive_visible);
456 $('#sd_stability_style_preset').val(extension_settings.sd.stability_style_preset);457 $('#sd_stability_style_preset').val(extension_settings.sd.stability_style_preset);
458 $('#sd_huggingface_model_id').val(extension_settings.sd.huggingface_model_id);
457459
458 for (const style of extension_settings.sd.styles) {460 for (const style of extension_settings.sd.styles) {
459 const option = document.createElement('option');461 const option = document.createElement('option');
@@ -1091,6 +1093,11 @@ function onComfyUrlInput() {
1091 saveSettingsDebounced();1093 saveSettingsDebounced();
1092}1094}
10931095
1096function onHFModelInput() {
1097 extension_settings.sd.huggingface_model_id = $('#sd_huggingface_model_id').val();
1098 saveSettingsDebounced();
1099}
1100
1094function onComfyWorkflowChange() {1101function onComfyWorkflowChange() {
1095 extension_settings.sd.comfy_workflow = $('#sd_comfy_workflow').find(':selected').val();1102 extension_settings.sd.comfy_workflow = $('#sd_comfy_workflow').find(':selected').val();
1096 saveSettingsDebounced();1103 saveSettingsDebounced();
@@ -1235,7 +1242,16 @@ async function onModelChange() {
1235 extension_settings.sd.model = $('#sd_model').find(':selected').val();1242 extension_settings.sd.model = $('#sd_model').find(':selected').val();
1236 saveSettingsDebounced();1243 saveSettingsDebounced();
12371244
1238 const cloudSources = [sources.horde, sources.novel, sources.openai, sources.togetherai, sources.pollinations, sources.stability, sources.blockentropy];1245 const cloudSources = [
1246 sources.horde,
1247 sources.novel,
1248 sources.openai,
1249 sources.togetherai,
1250 sources.pollinations,
1251 sources.stability,
1252 sources.blockentropy,
1253 sources.huggingface,
1254 ];
12391255
1240 if (cloudSources.includes(extension_settings.sd.source)) {1256 if (cloudSources.includes(extension_settings.sd.source)) {
1241 return;1257 return;
@@ -1450,6 +1466,9 @@ async function loadSamplers() {
1450 case sources.blockentropy:1466 case sources.blockentropy:
1451 samplers = ['N/A'];1467 samplers = ['N/A'];
1452 break;1468 break;
1469 case sources.huggingface:
1470 samplers = ['N/A'];
1471 break;
1453 }1472 }
14541473
1455 for (const sampler of samplers) {1474 for (const sampler of samplers) {
@@ -1639,6 +1658,9 @@ async function loadModels() {
1639 case sources.blockentropy:1658 case sources.blockentropy:
1640 models = await loadBlockEntropyModels();1659 models = await loadBlockEntropyModels();
1641 break;1660 break;
1661 case sources.huggingface:
1662 models = [{ value: '', text: '<Enter Model ID above>' }];
1663 break;
1642 }1664 }
16431665
1644 for (const model of models) {1666 for (const model of models) {
@@ -1986,6 +2008,9 @@ async function loadSchedulers() {
1986 case sources.blockentropy:2008 case sources.blockentropy:
1987 schedulers = ['N/A'];2009 schedulers = ['N/A'];
1988 break;2010 break;
2011 case sources.huggingface:
2012 schedulers = ['N/A'];
2013 break;
1989 }2014 }
19902015
1991 for (const scheduler of schedulers) {2016 for (const scheduler of schedulers) {
@@ -2065,6 +2090,9 @@ async function loadVaes() {
2065 case sources.blockentropy:2090 case sources.blockentropy:
2066 vaes = ['N/A'];2091 vaes = ['N/A'];
2067 break;2092 break;
2093 case sources.huggingface:
2094 vaes = ['N/A'];
2095 break;
2068 }2096 }
20692097
2070 for (const vae of vaes) {2098 for (const vae of vaes) {
@@ -2596,6 +2624,9 @@ async function sendGenerationRequest(generationType, prompt, additionalNegativeP
2596 case sources.blockentropy:2624 case sources.blockentropy:
2597 result = await generateBlockEntropyImage(prefixedPrompt, negativePrompt, signal);2625 result = await generateBlockEntropyImage(prefixedPrompt, negativePrompt, signal);
2598 break;2626 break;
2627 case sources.huggingface:
2628 result = await generateHuggingFaceImage(prefixedPrompt, signal);
2629 break;
2599 }2630 }
26002631
2601 if (!result.data) {2632 if (!result.data) {
@@ -3229,6 +3260,34 @@ async function generateComfyImage(prompt, negativePrompt, signal) {
3229 return { format: 'png', data: await promptResult.text() };3260 return { format: 'png', data: await promptResult.text() };
3230}3261}
32313262
3263
3264/**
3265 * Generates an image in Hugging Face Inference API using the provided prompt and configuration settings (model selected).
3266 * @param {string} prompt - The main instruction used to guide the image generation.
3267 * @param {AbortSignal} signal - An AbortSignal object that can be used to cancel the request.
3268 * @returns {Promise<{format: string, data: string}>} - A promise that resolves when the image generation and processing are complete.
3269 */
3270async function generateHuggingFaceImage(prompt, signal) {
3271 const result = await fetch('/api/sd/huggingface/generate', {
3272 method: 'POST',
3273 headers: getRequestHeaders(),
3274 signal: signal,
3275 body: JSON.stringify({
3276 model: extension_settings.sd.huggingface_model_id,
3277 prompt: prompt,
3278 }),
3279 });
3280
3281 if (result.ok) {
3282 const data = await result.json();
3283 return { format: 'jpg', data: data.image };
3284 } else {
3285 const text = await result.text();
3286 throw new Error(text);
3287 }
3288}
3289
3290
3232async function onComfyOpenWorkflowEditorClick() {3291async function onComfyOpenWorkflowEditorClick() {
3233 let workflow = await (await fetch('/api/sd/comfy/workflow', {3292 let workflow = await (await fetch('/api/sd/comfy/workflow', {
3234 method: 'POST',3293 method: 'POST',
@@ -3508,6 +3567,8 @@ function isValidState() {
3508 return secret_state[SECRET_KEYS.STABILITY];3567 return secret_state[SECRET_KEYS.STABILITY];
3509 case sources.blockentropy:3568 case sources.blockentropy:
3510 return secret_state[SECRET_KEYS.BLOCKENTROPY];3569 return secret_state[SECRET_KEYS.BLOCKENTROPY];
3570 case sources.huggingface:
3571 return secret_state[SECRET_KEYS.HUGGINGFACE];
3511 }3572 }
3512}3573}
35133574
@@ -3848,6 +3909,7 @@ jQuery(async () => {
3848 $('#sd_swap_dimensions').on('click', onSwapDimensionsClick);3909 $('#sd_swap_dimensions').on('click', onSwapDimensionsClick);
3849 $('#sd_stability_key').on('click', onStabilityKeyClick);3910 $('#sd_stability_key').on('click', onStabilityKeyClick);
3850 $('#sd_stability_style_preset').on('change', onStabilityStylePresetChange);3911 $('#sd_stability_style_preset').on('change', onStabilityStylePresetChange);
3912 $('#sd_huggingface_model_id').on('input', onHFModelInput);
38513913
3852 $('.sd_settings .inline-drawer-toggle').on('click', function () {3914 $('.sd_settings .inline-drawer-toggle').on('click', function () {
3853 initScrollHeight($('#sd_prompt_prefix'));3915 initScrollHeight($('#sd_prompt_prefix'));
public/scripts/extensions/stable-diffusion/settings.html+8 -1
@@ -29,7 +29,8 @@
29 </label>29 </label>
30 <label for="sd_expand" class="checkbox_label" data-i18n="[title]sd_expand" title="Automatically extend prompts using text generation model">30 <label for="sd_expand" class="checkbox_label" data-i18n="[title]sd_expand" title="Automatically extend prompts using text generation model">
31 <input id="sd_expand" type="checkbox" />31 <input id="sd_expand" type="checkbox" />
32 <span data-i18n="sd_expand_txt">Auto-enhance prompts</span>32 <span data-i18n="sd_expand_txt">Auto-extend prompts</span>
33 <span class="right_menu_button fa-solid fa-triangle-exclamation" data-i18n="[title]sd_expand_warning" title="May produce unexpected results. Manual prompt editing is recommended."></span>
33 </label>34 </label>
34 <label for="sd_snap" class="checkbox_label" data-i18n="[title]sd_snap" title="Snap generation requests with a forced aspect ratio (portraits, backgrounds) to the nearest known resolution, while trying to preserve the absolute pixel counts (recommended for SDXL).">35 <label for="sd_snap" class="checkbox_label" data-i18n="[title]sd_snap" title="Snap generation requests with a forced aspect ratio (portraits, backgrounds) to the nearest known resolution, while trying to preserve the absolute pixel counts (recommended for SDXL).">
35 <input id="sd_snap" type="checkbox" />36 <input id="sd_snap" type="checkbox" />
@@ -41,6 +42,7 @@
41 <option value="comfy">ComfyUI</option>42 <option value="comfy">ComfyUI</option>
42 <option value="drawthings">DrawThings HTTP API</option>43 <option value="drawthings">DrawThings HTTP API</option>
43 <option value="extras">Extras API (local / remote)</option>44 <option value="extras">Extras API (local / remote)</option>
45 <option value="huggingface">HuggingFace Inference API (serverless)</option>
44 <option value="novel">NovelAI Diffusion</option>46 <option value="novel">NovelAI Diffusion</option>
45 <option value="openai">OpenAI (DALL-E)</option>47 <option value="openai">OpenAI (DALL-E)</option>
46 <option value="pollinations">Pollinations</option>48 <option value="pollinations">Pollinations</option>
@@ -82,6 +84,11 @@
82 <!-- (Original Text)<b>Important:</b> run DrawThings app with HTTP API switch enabled in the UI! The server must be accessible from the SillyTavern host machine. -->84 <!-- (Original Text)<b>Important:</b> run DrawThings app with HTTP API switch enabled in the UI! The server must be accessible from the SillyTavern host machine. -->
83 <i><b data-i18n="Important:">Important:</b></i><i data-i18n="sd_drawthings_auth_txt"> run DrawThings app with HTTP API switch enabled in the UI! The server must be accessible from the SillyTavern host machine.</i>85 <i><b data-i18n="Important:">Important:</b></i><i data-i18n="sd_drawthings_auth_txt"> run DrawThings app with HTTP API switch enabled in the UI! The server must be accessible from the SillyTavern host machine.</i>
84 </div>86 </div>
87 <div data-sd-source="huggingface">
88 <i>Hint: Save an API key in the Hugging Face (Text Completion) API settings to use it here.</i>
89 <label for="sd_huggingface_model_id" data-i18n="Model ID">Model ID</label>
90 <input id="sd_huggingface_model_id" type="text" class="text_pole" data-i18n="[placeholder]e.g. black-forest-labs/FLUX.1-dev" placeholder="e.g. black-forest-labs/FLUX.1-dev" value="" />
91 </div>
85 <div data-sd-source="vlad">92 <div data-sd-source="vlad">
86 <label for="sd_vlad_url">SD.Next API URL</label>93 <label for="sd_vlad_url">SD.Next API URL</label>
87 <div class="flex-container flexnowrap">94 <div class="flex-container flexnowrap">
public/scripts/extensions/tts/index.js+6 -1
@@ -421,7 +421,7 @@ function completeTtsJob() {
421async function tts(text, voiceId, char) {421async function tts(text, voiceId, char) {
422 async function processResponse(response) {422 async function processResponse(response) {
423 // RVC injection423 // RVC injection
424 if (extension_settings.rvc.enabled && typeof window['rvcVoiceConversion'] === 'function')424 if (typeof window['rvcVoiceConversion'] === 'function' && extension_settings.rvc.enabled)
425 response = await window['rvcVoiceConversion'](response, char, text);425 response = await window['rvcVoiceConversion'](response, char, text);
426426
427 await addAudioJob(response, char);427 await addAudioJob(response, char);
@@ -755,6 +755,11 @@ async function onMessageEvent(messageId, lastCharIndex) {
755 const message = structuredClone(context.chat[messageId]);755 const message = structuredClone(context.chat[messageId]);
756 const hashNew = getStringHash(message?.mes ?? '');756 const hashNew = getStringHash(message?.mes ?? '');
757757
758 // Ignore prompt-hidden messages
759 if (message.is_system) {
760 return;
761 }
762
758 // if no new messages, or same message, or same message hash, do nothing763 // if no new messages, or same message, or same message hash, do nothing
759 if (hashNew === lastMessageHash) {764 if (hashNew === lastMessageHash) {
760 return;765 return;
public/scripts/extensions/vectors/settings.html+1 -1
@@ -12,7 +12,7 @@
12 <select id="vectors_source" class="text_pole">12 <select id="vectors_source" class="text_pole">
13 <option value="cohere">Cohere</option>13 <option value="cohere">Cohere</option>
14 <option value="extras">Extras</option>14 <option value="extras">Extras</option>
15 <option value="palm">Google MakerSuite</option>15 <option value="palm">Google AI Studio</option>
16 <option value="llamacpp">llama.cpp</option>16 <option value="llamacpp">llama.cpp</option>
17 <option value="transformers" data-i18n="Local (Transformers)">Local (Transformers)</option>17 <option value="transformers" data-i18n="Local (Transformers)">Local (Transformers)</option>
18 <option value="mistral">MistralAI</option>18 <option value="mistral">MistralAI</option>
public/scripts/nai-settings.js+16 -1
@@ -78,6 +78,19 @@ export function getKayraMaxContextTokens() {
78 return null;78 return null;
79}79}
8080
81export function getKayraMaxResponseTokens() {
82 switch (novel_data?.tier) {
83 case 1:
84 return 100;
85 case 2:
86 return 100;
87 case 3:
88 return 150;
89 }
90
91 return maximum_output_length;
92}
93
81export function getNovelTier() {94export function getNovelTier() {
82 return nai_tiers[novel_data?.tier] ?? 'no_connection';95 return nai_tiers[novel_data?.tier] ?? 'no_connection';
83}96}
@@ -438,12 +451,14 @@ export function getNovelGenerationData(finalPrompt, settings, maxLength, isImper
438 console.log(finalPrompt);451 console.log(finalPrompt);
439 }452 }
440453
454 const adjustedMaxLength = nai_settings.model_novel.includes('kayra') ? getKayraMaxResponseTokens() : maximum_output_length;
455
441 return {456 return {
442 'input': finalPrompt,457 'input': finalPrompt,
443 'model': nai_settings.model_novel,458 'model': nai_settings.model_novel,
444 'use_string': true,459 'use_string': true,
445 'temperature': Number(nai_settings.temperature),460 'temperature': Number(nai_settings.temperature),
446 'max_length': maxLength < maximum_output_length ? maxLength : maximum_output_length,461 'max_length': maxLength < adjustedMaxLength ? maxLength : adjustedMaxLength,
447 'min_length': Number(nai_settings.min_length),462 'min_length': Number(nai_settings.min_length),
448 'tail_free_sampling': Number(nai_settings.tail_free_sampling),463 'tail_free_sampling': Number(nai_settings.tail_free_sampling),
449 'repetition_penalty': Number(nai_settings.repetition_penalty),464 'repetition_penalty': Number(nai_settings.repetition_penalty),
public/scripts/openai.js+47 -113
@@ -74,6 +74,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashComma
74import { renderTemplateAsync } from './templates.js';74import { renderTemplateAsync } from './templates.js';
75import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';75import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
76import { Popup, POPUP_RESULT } from './popup.js';76import { Popup, POPUP_RESULT } from './popup.js';
77import { t } from './i18n.js';
7778
78export {79export {
79 openai_messages_count,80 openai_messages_count,
@@ -132,13 +133,9 @@ const max_2mil = 2000 * 1000;
132const scale_max = 8191;133const scale_max = 8191;
133const claude_max = 9000; // We have a proper tokenizer, so theoretically could be larger (up to 9k)134const claude_max = 9000; // We have a proper tokenizer, so theoretically could be larger (up to 9k)
134const claude_100k_max = 99000;135const claude_100k_max = 99000;
135let ai21_max = 9200; //can easily fit 9k gpt tokens because j2's tokenizer is efficient af136const unlocked_max = max_2mil;
136const unlocked_max = max_200k;
137const oai_max_temp = 2.0;137const oai_max_temp = 2.0;
138const claude_max_temp = 1.0; //same as j2138const claude_max_temp = 1.0;
139const j2_max_topk = 10.0;
140const j2_max_freq = 5.0;
141const j2_max_pres = 5.0;
142const openrouter_website_model = 'OR_Website';139const openrouter_website_model = 'OR_Website';
143const openai_max_stop_strings = 4;140const openai_max_stop_strings = 4;
144141
@@ -218,25 +215,11 @@ const sensitiveFields = [
218 'custom_include_headers',215 'custom_include_headers',
219];216];
220217
221function getPrefixMap() {
222 return selected_group ? {
223 assistant: '',
224 user: '',
225 system: 'OOC: ',
226 }
227 : {
228 assistant: '{{char}}:',
229 user: '{{user}}:',
230 system: '',
231 };
232}
233
234const default_settings = {218const default_settings = {
235 preset_settings_openai: 'Default',219 preset_settings_openai: 'Default',
236 temp_openai: 1.0,220 temp_openai: 1.0,
237 freq_pen_openai: 0,221 freq_pen_openai: 0,
238 pres_pen_openai: 0,222 pres_pen_openai: 0,
239 count_pen: 0.0,
240 top_p_openai: 1.0,223 top_p_openai: 1.0,
241 top_k_openai: 0,224 top_k_openai: 0,
242 min_p_openai: 0,225 min_p_openai: 0,
@@ -264,7 +247,7 @@ const default_settings = {
264 openai_model: 'gpt-4-turbo',247 openai_model: 'gpt-4-turbo',
265 claude_model: 'claude-3-5-sonnet-20240620',248 claude_model: 'claude-3-5-sonnet-20240620',
266 google_model: 'gemini-1.5-pro',249 google_model: 'gemini-1.5-pro',
267 ai21_model: 'j2-ultra',250 ai21_model: 'jamba-1.5-large',
268 mistralai_model: 'mistral-large-latest',251 mistralai_model: 'mistral-large-latest',
269 cohere_model: 'command-r-plus',252 cohere_model: 'command-r-plus',
270 perplexity_model: 'llama-3.1-70b-instruct',253 perplexity_model: 'llama-3.1-70b-instruct',
@@ -294,8 +277,6 @@ const default_settings = {
294 assistant_prefill: '',277 assistant_prefill: '',
295 assistant_impersonation: '',278 assistant_impersonation: '',
296 human_sysprompt_message: default_claude_human_sysprompt_message,279 human_sysprompt_message: default_claude_human_sysprompt_message,
297 use_ai21_tokenizer: false,
298 use_google_tokenizer: false,
299 claude_use_sysprompt: false,280 claude_use_sysprompt: false,
300 use_makersuite_sysprompt: true,281 use_makersuite_sysprompt: true,
301 use_alt_scale: false,282 use_alt_scale: false,
@@ -317,7 +298,6 @@ const oai_settings = {
317 temp_openai: 1.0,298 temp_openai: 1.0,
318 freq_pen_openai: 0,299 freq_pen_openai: 0,
319 pres_pen_openai: 0,300 pres_pen_openai: 0,
320 count_pen: 0.0,
321 top_p_openai: 1.0,301 top_p_openai: 1.0,
322 top_k_openai: 0,302 top_k_openai: 0,
323 min_p_openai: 0,303 min_p_openai: 0,
@@ -345,7 +325,7 @@ const oai_settings = {
345 openai_model: 'gpt-4-turbo',325 openai_model: 'gpt-4-turbo',
346 claude_model: 'claude-3-5-sonnet-20240620',326 claude_model: 'claude-3-5-sonnet-20240620',
347 google_model: 'gemini-1.5-pro',327 google_model: 'gemini-1.5-pro',
348 ai21_model: 'j2-ultra',328 ai21_model: 'jamba-1.5-large',
349 mistralai_model: 'mistral-large-latest',329 mistralai_model: 'mistral-large-latest',
350 cohere_model: 'command-r-plus',330 cohere_model: 'command-r-plus',
351 perplexity_model: 'llama-3.1-70b-instruct',331 perplexity_model: 'llama-3.1-70b-instruct',
@@ -375,8 +355,6 @@ const oai_settings = {
375 assistant_prefill: '',355 assistant_prefill: '',
376 assistant_impersonation: '',356 assistant_impersonation: '',
377 human_sysprompt_message: default_claude_human_sysprompt_message,357 human_sysprompt_message: default_claude_human_sysprompt_message,
378 use_ai21_tokenizer: false,
379 use_google_tokenizer: false,
380 claude_use_sysprompt: false,358 claude_use_sysprompt: false,
381 use_makersuite_sysprompt: true,359 use_makersuite_sysprompt: true,
382 use_alt_scale: false,360 use_alt_scale: false,
@@ -425,7 +403,7 @@ async function validateReverseProxy() {
425 const rememberKey = `Proxy_SkipConfirm_${getStringHash(oai_settings.reverse_proxy)}`;403 const rememberKey = `Proxy_SkipConfirm_${getStringHash(oai_settings.reverse_proxy)}`;
426 const skipConfirm = localStorage.getItem(rememberKey) === 'true';404 const skipConfirm = localStorage.getItem(rememberKey) === 'true';
427405
428 const confirmation = skipConfirm || await Popup.show.confirm('Connecting To Proxy', `<span>Are you sure you want to connect to the following proxy URL?</span><var>${DOMPurify.sanitize(oai_settings.reverse_proxy)}</var>`);406 const confirmation = skipConfirm || await Popup.show.confirm(t`Connecting To Proxy`, await renderTemplateAsync('proxyConnectionWarning', { proxyURL: DOMPurify.sanitize(oai_settings.reverse_proxy) }));
429407
430 if (!confirmation) {408 if (!confirmation) {
431 toastr.error('Update or remove your reverse proxy settings.');409 toastr.error('Update or remove your reverse proxy settings.');
@@ -1148,7 +1126,6 @@ function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, wor
1148 { role: 'system', content: charDescription, identifier: 'charDescription' },1126 { role: 'system', content: charDescription, identifier: 'charDescription' },
1149 { role: 'system', content: charPersonalityText, identifier: 'charPersonality' },1127 { role: 'system', content: charPersonalityText, identifier: 'charPersonality' },
1150 { role: 'system', content: scenarioText, identifier: 'scenario' },1128 { role: 'system', content: scenarioText, identifier: 'scenario' },
1151 { role: 'system', content: personaDescription, identifier: 'personaDescription' },
1152 // Unordered prompts without marker1129 // Unordered prompts without marker
1153 { role: 'system', content: impersonationPrompt, identifier: 'impersonate' },1130 { role: 'system', content: impersonationPrompt, identifier: 'impersonate' },
1154 { role: 'system', content: quietPrompt, identifier: 'quietPrompt' },1131 { role: 'system', content: quietPrompt, identifier: 'quietPrompt' },
@@ -1395,6 +1372,11 @@ function tryParseStreamingError(response, decoded) {
1395 toastr.error(data.error.message || response.statusText, 'Chat Completion API');1372 toastr.error(data.error.message || response.statusText, 'Chat Completion API');
1396 throw new Error(data);1373 throw new Error(data);
1397 }1374 }
1375
1376 if (data.message) {
1377 toastr.error(data.message, 'Chat Completion API');
1378 throw new Error(data);
1379 }
1398 }1380 }
1399 catch {1381 catch {
1400 // No JSON. Do nothing.1382 // No JSON. Do nothing.
@@ -1802,7 +1784,6 @@ async function sendOpenAIRequest(type, messages, signal) {
1802 const isClaude = oai_settings.chat_completion_source == chat_completion_sources.CLAUDE;1784 const isClaude = oai_settings.chat_completion_source == chat_completion_sources.CLAUDE;
1803 const isOpenRouter = oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER;1785 const isOpenRouter = oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER;
1804 const isScale = oai_settings.chat_completion_source == chat_completion_sources.SCALE;1786 const isScale = oai_settings.chat_completion_source == chat_completion_sources.SCALE;
1805 const isAI21 = oai_settings.chat_completion_source == chat_completion_sources.AI21;
1806 const isGoogle = oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE;1787 const isGoogle = oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE;
1807 const isOAI = oai_settings.chat_completion_source == chat_completion_sources.OPENAI;1788 const isOAI = oai_settings.chat_completion_source == chat_completion_sources.OPENAI;
1808 const isMistral = oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI;1789 const isMistral = oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI;
@@ -1815,7 +1796,7 @@ async function sendOpenAIRequest(type, messages, signal) {
1815 const isQuiet = type === 'quiet';1796 const isQuiet = type === 'quiet';
1816 const isImpersonate = type === 'impersonate';1797 const isImpersonate = type === 'impersonate';
1817 const isContinue = type === 'continue';1798 const isContinue = type === 'continue';
1818 const stream = oai_settings.stream_openai && !isQuiet && !isScale && !isAI21 && !(isGoogle && oai_settings.google_model.includes('bison'));1799 const stream = oai_settings.stream_openai && !isQuiet && !isScale && !(isGoogle && oai_settings.google_model.includes('bison'));
1819 const useLogprobs = !!power_user.request_token_probabilities;1800 const useLogprobs = !!power_user.request_token_probabilities;
1820 const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom);1801 const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom);
18211802
@@ -1824,15 +1805,6 @@ async function sendOpenAIRequest(type, messages, signal) {
1824 replaceItemizedPromptText(messageId, messages);1805 replaceItemizedPromptText(messageId, messages);
1825 }1806 }
18261807
1827 if (isAI21) {
1828 const joinedMsgs = messages.reduce((acc, obj) => {
1829 const prefix = getPrefixMap()[obj.role];
1830 return acc + (prefix ? (selected_group ? '\n' : prefix + ' ') : '') + obj.content + '\n';
1831 }, '');
1832 messages = substituteParams(joinedMsgs) + (isImpersonate ? `${name1}:` : `${name2}:`);
1833 replaceItemizedPromptText(messageId, messages);
1834 }
1835
1836 // If we're using the window.ai extension, use that instead1808 // If we're using the window.ai extension, use that instead
1837 // Doesn't support logit bias yet1809 // Doesn't support logit bias yet
1838 if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI) {1810 if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI) {
@@ -1931,12 +1903,6 @@ async function sendOpenAIRequest(type, messages, signal) {
1931 generate_data['use_makersuite_sysprompt'] = oai_settings.use_makersuite_sysprompt;1903 generate_data['use_makersuite_sysprompt'] = oai_settings.use_makersuite_sysprompt;
1932 }1904 }
19331905
1934 if (isAI21) {
1935 generate_data['top_k'] = Number(oai_settings.top_k_openai);
1936 generate_data['count_pen'] = Number(oai_settings.count_pen);
1937 generate_data['stop_tokens'] = [name1 + ':', substituteParams(oai_settings.new_chat_prompt), substituteParams(oai_settings.new_group_chat_prompt)];
1938 }
1939
1940 if (isMistral) {1906 if (isMistral) {
1941 generate_data['safe_prompt'] = false; // already defaults to false, but just incase they change that in the future.1907 generate_data['safe_prompt'] = false; // already defaults to false, but just incase they change that in the future.
1942 }1908 }
@@ -3008,7 +2974,6 @@ function loadOpenAISettings(data, settings) {
3008 oai_settings.temp_openai = settings.temp_openai ?? default_settings.temp_openai;2974 oai_settings.temp_openai = settings.temp_openai ?? default_settings.temp_openai;
3009 oai_settings.freq_pen_openai = settings.freq_pen_openai ?? default_settings.freq_pen_openai;2975 oai_settings.freq_pen_openai = settings.freq_pen_openai ?? default_settings.freq_pen_openai;
3010 oai_settings.pres_pen_openai = settings.pres_pen_openai ?? default_settings.pres_pen_openai;2976 oai_settings.pres_pen_openai = settings.pres_pen_openai ?? default_settings.pres_pen_openai;
3011 oai_settings.count_pen = settings.count_pen ?? default_settings.count_pen;
3012 oai_settings.top_p_openai = settings.top_p_openai ?? default_settings.top_p_openai;2977 oai_settings.top_p_openai = settings.top_p_openai ?? default_settings.top_p_openai;
3013 oai_settings.top_k_openai = settings.top_k_openai ?? default_settings.top_k_openai;2978 oai_settings.top_k_openai = settings.top_k_openai ?? default_settings.top_k_openai;
3014 oai_settings.top_a_openai = settings.top_a_openai ?? default_settings.top_a_openai;2979 oai_settings.top_a_openai = settings.top_a_openai ?? default_settings.top_a_openai;
@@ -3080,10 +3045,12 @@ function loadOpenAISettings(data, settings) {
3080 oai_settings.names_behavior = character_names_behavior.COMPLETION;3045 oai_settings.names_behavior = character_names_behavior.COMPLETION;
3081 }3046 }
30823047
3048 if (oai_settings.ai21_model.startsWith('j2-')) {
3049 oai_settings.ai21_model = 'jamba-1.5-large';
3050 }
3051
3083 if (settings.wrap_in_quotes !== undefined) oai_settings.wrap_in_quotes = !!settings.wrap_in_quotes;3052 if (settings.wrap_in_quotes !== undefined) oai_settings.wrap_in_quotes = !!settings.wrap_in_quotes;
3084 if (settings.openai_model !== undefined) oai_settings.openai_model = settings.openai_model;3053 if (settings.openai_model !== undefined) oai_settings.openai_model = settings.openai_model;
3085 if (settings.use_ai21_tokenizer !== undefined) { oai_settings.use_ai21_tokenizer = !!settings.use_ai21_tokenizer; oai_settings.use_ai21_tokenizer ? ai21_max = 8191 : ai21_max = 9200; }
3086 if (settings.use_google_tokenizer !== undefined) oai_settings.use_google_tokenizer = !!settings.use_google_tokenizer;
3087 if (settings.claude_use_sysprompt !== undefined) oai_settings.claude_use_sysprompt = !!settings.claude_use_sysprompt;3054 if (settings.claude_use_sysprompt !== undefined) oai_settings.claude_use_sysprompt = !!settings.claude_use_sysprompt;
3088 if (settings.use_makersuite_sysprompt !== undefined) oai_settings.use_makersuite_sysprompt = !!settings.use_makersuite_sysprompt;3055 if (settings.use_makersuite_sysprompt !== undefined) oai_settings.use_makersuite_sysprompt = !!settings.use_makersuite_sysprompt;
3089 if (settings.use_alt_scale !== undefined) { oai_settings.use_alt_scale = !!settings.use_alt_scale; updateScaleForm(); }3056 if (settings.use_alt_scale !== undefined) { oai_settings.use_alt_scale = !!settings.use_alt_scale; updateScaleForm(); }
@@ -3133,8 +3100,6 @@ function loadOpenAISettings(data, settings) {
3133 $('#jailbreak_system').prop('checked', oai_settings.jailbreak_system);3100 $('#jailbreak_system').prop('checked', oai_settings.jailbreak_system);
3134 $('#openai_show_external_models').prop('checked', oai_settings.show_external_models);3101 $('#openai_show_external_models').prop('checked', oai_settings.show_external_models);
3135 $('#openai_external_category').toggle(oai_settings.show_external_models);3102 $('#openai_external_category').toggle(oai_settings.show_external_models);
3136 $('#use_ai21_tokenizer').prop('checked', oai_settings.use_ai21_tokenizer);
3137 $('#use_google_tokenizer').prop('checked', oai_settings.use_google_tokenizer);
3138 $('#claude_use_sysprompt').prop('checked', oai_settings.claude_use_sysprompt);3103 $('#claude_use_sysprompt').prop('checked', oai_settings.claude_use_sysprompt);
3139 $('#use_makersuite_sysprompt').prop('checked', oai_settings.use_makersuite_sysprompt);3104 $('#use_makersuite_sysprompt').prop('checked', oai_settings.use_makersuite_sysprompt);
3140 $('#scale-alt').prop('checked', oai_settings.use_alt_scale);3105 $('#scale-alt').prop('checked', oai_settings.use_alt_scale);
@@ -3170,9 +3135,6 @@ function loadOpenAISettings(data, settings) {
3170 $('#pres_pen_openai').val(oai_settings.pres_pen_openai);3135 $('#pres_pen_openai').val(oai_settings.pres_pen_openai);
3171 $('#pres_pen_counter_openai').val(Number(oai_settings.pres_pen_openai).toFixed(2));3136 $('#pres_pen_counter_openai').val(Number(oai_settings.pres_pen_openai).toFixed(2));
31723137
3173 $('#count_pen').val(oai_settings.count_pen);
3174 $('#count_pen_counter').val(Number(oai_settings.count_pen).toFixed(2));
3175
3176 $('#top_p_openai').val(oai_settings.top_p_openai);3138 $('#top_p_openai').val(oai_settings.top_p_openai);
3177 $('#top_p_counter_openai').val(Number(oai_settings.top_p_openai).toFixed(2));3139 $('#top_p_counter_openai').val(Number(oai_settings.top_p_openai).toFixed(2));
31783140
@@ -3392,7 +3354,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3392 temperature: settings.temp_openai,3354 temperature: settings.temp_openai,
3393 frequency_penalty: settings.freq_pen_openai,3355 frequency_penalty: settings.freq_pen_openai,
3394 presence_penalty: settings.pres_pen_openai,3356 presence_penalty: settings.pres_pen_openai,
3395 count_penalty: settings.count_pen,
3396 top_p: settings.top_p_openai,3357 top_p: settings.top_p_openai,
3397 top_k: settings.top_k_openai,3358 top_k: settings.top_k_openai,
3398 top_a: settings.top_a_openai,3359 top_a: settings.top_a_openai,
@@ -3427,8 +3388,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3427 assistant_prefill: settings.assistant_prefill,3388 assistant_prefill: settings.assistant_prefill,
3428 assistant_impersonation: settings.assistant_impersonation,3389 assistant_impersonation: settings.assistant_impersonation,
3429 human_sysprompt_message: settings.human_sysprompt_message,3390 human_sysprompt_message: settings.human_sysprompt_message,
3430 use_ai21_tokenizer: settings.use_ai21_tokenizer,
3431 use_google_tokenizer: settings.use_google_tokenizer,
3432 claude_use_sysprompt: settings.claude_use_sysprompt,3391 claude_use_sysprompt: settings.claude_use_sysprompt,
3433 use_makersuite_sysprompt: settings.use_makersuite_sysprompt,3392 use_makersuite_sysprompt: settings.use_makersuite_sysprompt,
3434 use_alt_scale: settings.use_alt_scale,3393 use_alt_scale: settings.use_alt_scale,
@@ -3799,7 +3758,6 @@ function onSettingsPresetChange() {
3799 temperature: ['#temp_openai', 'temp_openai', false],3758 temperature: ['#temp_openai', 'temp_openai', false],
3800 frequency_penalty: ['#freq_pen_openai', 'freq_pen_openai', false],3759 frequency_penalty: ['#freq_pen_openai', 'freq_pen_openai', false],
3801 presence_penalty: ['#pres_pen_openai', 'pres_pen_openai', false],3760 presence_penalty: ['#pres_pen_openai', 'pres_pen_openai', false],
3802 count_penalty: ['#count_pen', 'count_pen', false],
3803 top_p: ['#top_p_openai', 'top_p_openai', false],3761 top_p: ['#top_p_openai', 'top_p_openai', false],
3804 top_k: ['#top_k_openai', 'top_k_openai', false],3762 top_k: ['#top_k_openai', 'top_k_openai', false],
3805 top_a: ['#top_a_openai', 'top_a_openai', false],3763 top_a: ['#top_a_openai', 'top_a_openai', false],
@@ -3856,8 +3814,6 @@ function onSettingsPresetChange() {
3856 assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],3814 assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],
3857 assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false],3815 assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false],
3858 human_sysprompt_message: ['#claude_human_sysprompt_textarea', 'human_sysprompt_message', false],3816 human_sysprompt_message: ['#claude_human_sysprompt_textarea', 'human_sysprompt_message', false],
3859 use_ai21_tokenizer: ['#use_ai21_tokenizer', 'use_ai21_tokenizer', true],
3860 use_google_tokenizer: ['#use_google_tokenizer', 'use_google_tokenizer', true],
3861 claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true],3817 claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true],
3862 use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true],3818 use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true],
3863 use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],3819 use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],
@@ -4028,6 +3984,11 @@ async function onModelChange() {
4028 }3984 }
40293985
4030 if ($(this).is('#model_ai21_select')) {3986 if ($(this).is('#model_ai21_select')) {
3987 if (value === '' || value.startsWith('j2-')) {
3988 value = 'jamba-1.5-large';
3989 $('#model_ai21_select').val(value);
3990 }
3991
4031 console.log('AI21 model changed to', value);3992 console.log('AI21 model changed to', value);
4032 oai_settings.ai21_model = value;3993 oai_settings.ai21_model = value;
4033 }3994 }
@@ -4230,7 +4191,7 @@ async function onModelChange() {
4230 else if (['command-light-nightly', 'command-nightly'].includes(oai_settings.cohere_model)) {4191 else if (['command-light-nightly', 'command-nightly'].includes(oai_settings.cohere_model)) {
4231 $('#openai_max_context').attr('max', max_8k);4192 $('#openai_max_context').attr('max', max_8k);
4232 }4193 }
4233 else if (['command-r', 'command-r-plus'].includes(oai_settings.cohere_model)) {4194 else if (oai_settings.cohere_model.includes('command-r')) {
4234 $('#openai_max_context').attr('max', max_128k);4195 $('#openai_max_context').attr('max', max_128k);
4235 }4196 }
4236 else if (['c4ai-aya-23'].includes(oai_settings.cohere_model)) {4197 else if (['c4ai-aya-23'].includes(oai_settings.cohere_model)) {
@@ -4305,33 +4266,13 @@ async function onModelChange() {
4305 if (oai_settings.chat_completion_source == chat_completion_sources.AI21) {4266 if (oai_settings.chat_completion_source == chat_completion_sources.AI21) {
4306 if (oai_settings.max_context_unlocked) {4267 if (oai_settings.max_context_unlocked) {
4307 $('#openai_max_context').attr('max', unlocked_max);4268 $('#openai_max_context').attr('max', unlocked_max);
4308 } else {4269 } else if (oai_settings.ai21_model.includes('jamba-1.5') || oai_settings.ai21_model.includes('jamba-instruct')) {
4309 $('#openai_max_context').attr('max', ai21_max);4270 $('#openai_max_context').attr('max', max_256k);
4310 }4271 }
43114272
4312 oai_settings.openai_max_context = Math.min(oai_settings.openai_max_context, Number($('#openai_max_context').attr('max')));4273 oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
4313 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');4274 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
43144275 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
4315 oai_settings.temp_openai = Math.min(claude_max_temp, oai_settings.temp_openai);
4316 $('#temp_openai').attr('max', claude_max_temp).val(oai_settings.temp_openai).trigger('input');
4317
4318 oai_settings.freq_pen_openai = Math.min(j2_max_freq, oai_settings.freq_pen_openai < 0 ? 0 : oai_settings.freq_pen_openai);
4319 $('#freq_pen_openai').attr('min', 0).attr('max', j2_max_freq).val(oai_settings.freq_pen_openai).trigger('input');
4320
4321 oai_settings.pres_pen_openai = Math.min(j2_max_pres, oai_settings.pres_pen_openai < 0 ? 0 : oai_settings.pres_pen_openai);
4322 $('#pres_pen_openai').attr('min', 0).attr('max', j2_max_pres).val(oai_settings.pres_pen_openai).trigger('input');
4323
4324 oai_settings.top_k_openai = Math.min(j2_max_topk, oai_settings.top_k_openai);
4325 $('#top_k_openai').attr('max', j2_max_topk).val(oai_settings.top_k_openai).trigger('input');
4326 } else if (oai_settings.chat_completion_source != chat_completion_sources.AI21) {
4327 oai_settings.freq_pen_openai = Math.min(2.0, oai_settings.freq_pen_openai);
4328 $('#freq_pen_openai').attr('min', -2.0).attr('max', 2.0).val(oai_settings.freq_pen_openai).trigger('input');
4329
4330 oai_settings.pres_pen_openai = Math.min(2.0, oai_settings.pres_pen_openai);
4331 $('#pres_pen_openai').attr('min', -2.0).attr('max', 2.0).val(oai_settings.pres_pen_openai).trigger('input');
4332
4333 oai_settings.top_k_openai = Math.min(200, oai_settings.top_k_openai);
4334 $('#top_k_openai').attr('max', 200).val(oai_settings.top_k_openai).trigger('input');
4335 }4276 }
43364277
4337 if (oai_settings.chat_completion_source == chat_completion_sources.CUSTOM) {4278 if (oai_settings.chat_completion_source == chat_completion_sources.CUSTOM) {
@@ -4388,6 +4329,16 @@ async function onModelChange() {
4388 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');4329 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
4389 }4330 }
43904331
4332 if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) {
4333 oai_settings.pres_pen_openai = Math.min(Math.max(0, oai_settings.pres_pen_openai), 1);
4334 $('#pres_pen_openai').attr('max', 1).attr('min', 0).val(oai_settings.pres_pen_openai).trigger('input');
4335 oai_settings.freq_pen_openai = Math.min(Math.max(0, oai_settings.freq_pen_openai), 1);
4336 $('#freq_pen_openai').attr('max', 1).attr('min', 0).val(oai_settings.freq_pen_openai).trigger('input');
4337 } else {
4338 $('#pres_pen_openai').attr('max', 2).attr('min', -2).val(oai_settings.pres_pen_openai).trigger('input');
4339 $('#freq_pen_openai').attr('max', 2).attr('min', -2).val(oai_settings.freq_pen_openai).trigger('input');
4340 }
4341
4391 $('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max')));4342 $('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max')));
43924343
4393 saveSettingsDebounced();4344 saveSettingsDebounced();
@@ -4473,7 +4424,7 @@ async function onConnectButtonClick(e) {
4473 }4424 }
44744425
4475 if (!secret_state[SECRET_KEYS.MAKERSUITE] && !oai_settings.reverse_proxy) {4426 if (!secret_state[SECRET_KEYS.MAKERSUITE] && !oai_settings.reverse_proxy) {
4476 console.log('No secret key saved for MakerSuite');4427 console.log('No secret key saved for Google AI Studio');
4477 return;4428 return;
4478 }4429 }
4479 }4430 }
@@ -4673,17 +4624,17 @@ function toggleChatCompletionForms() {
4673async function testApiConnection() {4624async function testApiConnection() {
4674 // Check if the previous request is still in progress4625 // Check if the previous request is still in progress
4675 if (is_send_press) {4626 if (is_send_press) {
4676 toastr.info('Please wait for the previous request to complete.');4627 toastr.info(t`Please wait for the previous request to complete.`);
4677 return;4628 return;
4678 }4629 }
46794630
4680 try {4631 try {
4681 const reply = await sendOpenAIRequest('quiet', [{ 'role': 'user', 'content': 'Hi' }]);4632 const reply = await sendOpenAIRequest('quiet', [{ 'role': 'user', 'content': 'Hi' }]);
4682 console.log(reply);4633 console.log(reply);
4683 toastr.success('API connection successful!');4634 toastr.success(t`API connection successful!`);
4684 }4635 }
4685 catch (err) {4636 catch (err) {
4686 toastr.error('Could not get a reply from API. Check your connection settings / API key and try again.');4637 toastr.error(t`Could not get a reply from API. Check your connection settings / API key and try again.`);
4687 }4638 }
4688}4639}
46894640
@@ -4752,11 +4703,14 @@ export function isImageInliningSupported() {
4752 'gemini-1.5-flash',4703 'gemini-1.5-flash',
4753 'gemini-1.5-flash-latest',4704 'gemini-1.5-flash-latest',
4754 'gemini-1.5-flash-001',4705 'gemini-1.5-flash-001',
4706 'gemini-1.5-flash-exp-0827',
4707 'gemini-1.5-flash-8b-exp-0827',
4755 'gemini-1.0-pro-vision-latest',4708 'gemini-1.0-pro-vision-latest',
4756 'gemini-1.5-pro',4709 'gemini-1.5-pro',
4757 'gemini-1.5-pro-latest',4710 'gemini-1.5-pro-latest',
4758 'gemini-1.5-pro-001',4711 'gemini-1.5-pro-001',
4759 'gemini-1.5-pro-exp-0801',4712 'gemini-1.5-pro-exp-0801',
4713 'gemini-1.5-pro-exp-0827',
4760 'gemini-pro-vision',4714 'gemini-pro-vision',
4761 'claude-3',4715 'claude-3',
4762 'claude-3-5',4716 'claude-3-5',
@@ -4837,7 +4791,7 @@ function onProxyPresetChange() {
4837 if (selectedPreset) {4791 if (selectedPreset) {
4838 setProxyPreset(selectedPreset.name, selectedPreset.url, selectedPreset.password);4792 setProxyPreset(selectedPreset.name, selectedPreset.url, selectedPreset.password);
4839 } else {4793 } else {
4840 console.error(`Proxy preset "${value}" not found in proxies array.`);4794 console.error(t`Proxy preset '${value}' not found in proxies array.`);
4841 }4795 }
4842 saveSettingsDebounced();4796 saveSettingsDebounced();
4843}4797}
@@ -4849,7 +4803,7 @@ $('#save_proxy').on('click', async function () {
48494803
4850 setProxyPreset(presetName, reverseProxy, proxyPassword);4804 setProxyPreset(presetName, reverseProxy, proxyPassword);
4851 saveSettingsDebounced();4805 saveSettingsDebounced();
4852 toastr.success('Proxy Saved');4806 toastr.success(t`Proxy Saved`);
4853 if ($('#openai_proxy_preset').val() !== presetName) {4807 if ($('#openai_proxy_preset').val() !== presetName) {
4854 const option = document.createElement('option');4808 const option = document.createElement('option');
4855 option.text = presetName;4809 option.text = presetName;
@@ -4883,9 +4837,9 @@ $('#delete_proxy').on('click', async function () {
48834837
4884 saveSettingsDebounced();4838 saveSettingsDebounced();
4885 $('#openai_proxy_preset').val(selected_proxy.name);4839 $('#openai_proxy_preset').val(selected_proxy.name);
4886 toastr.success('Proxy Deleted');4840 toastr.success(t`Proxy Deleted`);
4887 } else {4841 } else {
4888 toastr.error(`Could not find proxy with name "${presetName}"`);4842 toastr.error(t`Could not find proxy with name '${presetName}'`);
4889 }4843 }
4890});4844});
48914845
@@ -4953,12 +4907,6 @@ $(document).ready(async function () {
4953 saveSettingsDebounced();4907 saveSettingsDebounced();
4954 });4908 });
49554909
4956 $('#count_pen').on('input', function () {
4957 oai_settings.count_pen = Number($(this).val());
4958 $('#count_pen_counter').val(Number($(this).val()).toFixed(2));
4959 saveSettingsDebounced();
4960 });
4961
4962 $('#top_p_openai').on('input', function () {4910 $('#top_p_openai').on('input', function () {
4963 oai_settings.top_p_openai = Number($(this).val());4911 oai_settings.top_p_openai = Number($(this).val());
4964 $('#top_p_counter_openai').val(Number($(this).val()).toFixed(2));4912 $('#top_p_counter_openai').val(Number($(this).val()).toFixed(2));
@@ -5017,20 +4965,6 @@ $(document).ready(async function () {
5017 saveSettingsDebounced();4965 saveSettingsDebounced();
5018 });4966 });
50194967
5020 $('#use_ai21_tokenizer').on('change', function () {
5021 oai_settings.use_ai21_tokenizer = !!$('#use_ai21_tokenizer').prop('checked');
5022 oai_settings.use_ai21_tokenizer ? ai21_max = 8191 : ai21_max = 9200;
5023 oai_settings.openai_max_context = Math.min(ai21_max, oai_settings.openai_max_context);
5024 $('#openai_max_context').attr('max', ai21_max).val(oai_settings.openai_max_context).trigger('input');
5025 $('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max')));
5026 saveSettingsDebounced();
5027 });
5028
5029 $('#use_google_tokenizer').on('change', function () {
5030 oai_settings.use_google_tokenizer = !!$('#use_google_tokenizer').prop('checked');
5031 saveSettingsDebounced();
5032 });
5033
5034 $('#claude_use_sysprompt').on('change', function () {4968 $('#claude_use_sysprompt').on('change', function () {
5035 oai_settings.claude_use_sysprompt = !!$('#claude_use_sysprompt').prop('checked');4969 oai_settings.claude_use_sysprompt = !!$('#claude_use_sysprompt').prop('checked');
5036 $('#claude_human_sysprompt_message_block').toggle(oai_settings.claude_use_sysprompt);4970 $('#claude_human_sysprompt_message_block').toggle(oai_settings.claude_use_sysprompt);
public/scripts/popup.js+7 -7
@@ -40,8 +40,8 @@ export const POPUP_RESULT = {
40 * @property {POPUP_RESULT|number?} [defaultResult=POPUP_RESULT.AFFIRMATIVE] - The default result of this popup when Enter is pressed. Can be changed from `POPUP_RESULT.AFFIRMATIVE`.40 * @property {POPUP_RESULT|number?} [defaultResult=POPUP_RESULT.AFFIRMATIVE] - The default result of this popup when Enter is pressed. Can be changed from `POPUP_RESULT.AFFIRMATIVE`.
41 * @property {CustomPopupButton[]|string[]?} [customButtons=null] - Custom buttons to add to the popup. If only strings are provided, the buttons will be added with default options, and their result will be in order from `2` onward.41 * @property {CustomPopupButton[]|string[]?} [customButtons=null] - Custom buttons to add to the popup. If only strings are provided, the buttons will be added with default options, and their result will be in order from `2` onward.
42 * @property {CustomPopupInput[]?} [customInputs=null] - Custom inputs to add to the popup. The display below the content and the input box, one by one.42 * @property {CustomPopupInput[]?} [customInputs=null] - Custom inputs to add to the popup. The display below the content and the input box, one by one.
43 * @property {(popup: Popup) => boolean?} [onClosing=null] - Handler called before the popup closes, return `false` to cancel the close43 * @property {(popup: Popup) => Promise<boolean?>|boolean?} [onClosing=null] - Handler called before the popup closes, return `false` to cancel the close
44 * @property {(popup: Popup) => void?} [onClose=null] - Handler called after the popup closes, but before the DOM is cleaned up44 * @property {(popup: Popup) => Promise<void?>|void?} [onClose=null] - Handler called after the popup closes, but before the DOM is cleaned up
45 * @property {number?} [cropAspect=null] - Aspect ratio for the crop popup45 * @property {number?} [cropAspect=null] - Aspect ratio for the crop popup
46 * @property {string?} [cropImage=null] - Image URL to display in the crop popup46 * @property {string?} [cropImage=null] - Image URL to display in the crop popup
47 */47 */
@@ -138,8 +138,8 @@ export class Popup {
138 /** @readonly @type {CustomPopupButton[]|string[]?} */ customButtons;138 /** @readonly @type {CustomPopupButton[]|string[]?} */ customButtons;
139 /** @readonly @type {CustomPopupInput[]} */ customInputs;139 /** @readonly @type {CustomPopupInput[]} */ customInputs;
140140
141 /** @type {(popup: Popup) => boolean?} */ onClosing;141 /** @type {(popup: Popup) => Promise<boolean?>|boolean?} */ onClosing;
142 /** @type {(popup: Popup) => void?} */ onClose;142 /** @type {(popup: Popup) => Promise<void?>|void?} */ onClose;
143143
144 /** @type {POPUP_RESULT|number} */ result;144 /** @type {POPUP_RESULT|number} */ result;
145 /** @type {any} */ value;145 /** @type {any} */ value;
@@ -509,7 +509,7 @@ export class Popup {
509 this.result = result;509 this.result = result;
510510
511 if (this.onClosing) {511 if (this.onClosing) {
512 const shouldClose = this.onClosing(this);512 const shouldClose = await this.onClosing(this);
513 if (!shouldClose) {513 if (!shouldClose) {
514 this.#isClosingPrevented = true;514 this.#isClosingPrevented = true;
515 // Set values back if we cancel out of closing the popup515 // Set values back if we cancel out of closing the popup
@@ -547,13 +547,13 @@ export class Popup {
547 fixToastrForDialogs();547 fixToastrForDialogs();
548548
549 // After the dialog is actually completely closed, remove it from the DOM549 // After the dialog is actually completely closed, remove it from the DOM
550 runAfterAnimation(this.dlg, () => {550 runAfterAnimation(this.dlg, async () => {
551 // Call the close on the dialog551 // Call the close on the dialog
552 this.dlg.close();552 this.dlg.close();
553553
554 // Run a possible custom handler right before DOM removal554 // Run a possible custom handler right before DOM removal
555 if (this.onClose) {555 if (this.onClose) {
556 this.onClose(this);556 await this.onClose(this);
557 }557 }
558558
559 // Remove it from the dom559 // Remove it from the dom
public/scripts/power-user.js+32 -175
@@ -60,6 +60,7 @@ export {
60 power_user,60 power_user,
61 send_on_enter_options,61 send_on_enter_options,
62 getContextSettings,62 getContextSettings,
63 applyPowerUserSettings,
63};64};
6465
65export const MAX_CONTEXT_DEFAULT = 8192;66export const MAX_CONTEXT_DEFAULT = 8192;
@@ -106,6 +107,7 @@ export const persona_description_positions = {
106 TOP_AN: 2,107 TOP_AN: 2,
107 BOTTOM_AN: 3,108 BOTTOM_AN: 3,
108 AT_DEPTH: 4,109 AT_DEPTH: 4,
110 NONE: 9,
109};111};
110112
111let power_user = {113let power_user = {
@@ -301,45 +303,9 @@ let movingUIPresets = [];
301export let context_presets = [];303export let context_presets = [];
302304
303const storage_keys = {305const storage_keys = {
304 fast_ui_mode: 'TavernAI_fast_ui_mode',
305 avatar_style: 'TavernAI_avatar_style',
306 chat_display: 'TavernAI_chat_display',
307 chat_width: 'chat_width',
308 font_scale: 'TavernAI_font_scale',
309
310 main_text_color: 'TavernAI_main_text_color',
311 italics_text_color: 'TavernAI_italics_text_color',
312 underline_text_color: 'TavernAI_underline_text_color',
313 quote_text_color: 'TavernAI_quote_text_color',
314 blur_tint_color: 'TavernAI_blur_tint_color',
315 chat_tint_color: 'TavernAI_chat_tint_color',
316 user_mes_blur_tint_color: 'TavernAI_user_mes_blur_tint_color',
317 bot_mes_blur_tint_color: 'TavernAI_bot_mes_blur_tint_color',
318 blur_strength: 'TavernAI_blur_strength',
319 shadow_color: 'TavernAI_shadow_color',
320 shadow_width: 'TavernAI_shadow_width',
321 border_color: 'TavernAI_border_color',
322
323 custom_css: 'TavernAI_custom_css',
324
325 waifuMode: 'TavernAI_waifuMode',
326 movingUI: 'TavernAI_movingUI',
327 noShadows: 'TavernAI_noShadows',
328
329 hotswap_enabled: 'HotswapEnabled',
330 timer_enabled: 'TimerEnabled',
331 timestamps_enabled: 'TimestampsEnabled',
332 timestamp_model_icon: 'TimestampModelIcon',
333 mesIDDisplay_enabled: 'mesIDDisplayEnabled',
334 hideChatAvatars_enabled: 'hideChatAvatarsEnabled',
335 message_token_count_enabled: 'MessageTokenCountEnabled',
336 expand_message_actions: 'ExpandMessageActions',
337 enableZenSliders: 'enableZenSliders',
338 enableLabMode: 'enableLabMode',
339 reduced_motion: 'reduced_motion',
340 compact_input_area: 'compact_input_area',
341 auto_connect_legacy: 'AutoConnectEnabled',306 auto_connect_legacy: 'AutoConnectEnabled',
342 auto_load_chat_legacy: 'AutoLoadChatEnabled',307 auto_load_chat_legacy: 'AutoLoadChatEnabled',
308 hideChatAvatars_legacy: 'hideChatAvatarsEnabled',
343309
344 storyStringValidationCache: 'StoryStringValidationCache',310 storyStringValidationCache: 'StoryStringValidationCache',
345};311};
@@ -459,73 +425,47 @@ function fixMarkdown(text, forDisplay) {
459}425}
460426
461function switchHotswap() {427function switchHotswap() {
462 const value = localStorage.getItem(storage_keys.hotswap_enabled);
463 power_user.hotswap_enabled = value === null ? true : value == 'true';
464 $('body').toggleClass('no-hotswap', !power_user.hotswap_enabled);428 $('body').toggleClass('no-hotswap', !power_user.hotswap_enabled);
465 $('#hotswapEnabled').prop('checked', power_user.hotswap_enabled);429 $('#hotswapEnabled').prop('checked', power_user.hotswap_enabled);
466}430}
467431
468function switchTimer() {432function switchTimer() {
469 const value = localStorage.getItem(storage_keys.timer_enabled);
470 power_user.timer_enabled = value === null ? true : value == 'true';
471 $('body').toggleClass('no-timer', !power_user.timer_enabled);433 $('body').toggleClass('no-timer', !power_user.timer_enabled);
472 $('#messageTimerEnabled').prop('checked', power_user.timer_enabled);434 $('#messageTimerEnabled').prop('checked', power_user.timer_enabled);
473}435}
474436
475function switchTimestamps() {437function switchTimestamps() {
476 const value = localStorage.getItem(storage_keys.timestamps_enabled);
477 power_user.timestamps_enabled = value === null ? true : value == 'true';
478 $('body').toggleClass('no-timestamps', !power_user.timestamps_enabled);438 $('body').toggleClass('no-timestamps', !power_user.timestamps_enabled);
479 $('#messageTimestampsEnabled').prop('checked', power_user.timestamps_enabled);439 $('#messageTimestampsEnabled').prop('checked', power_user.timestamps_enabled);
480}440}
481441
482function switchIcons() {442function switchIcons() {
483 const value = localStorage.getItem(storage_keys.timestamp_model_icon);
484 power_user.timestamp_model_icon = value === null ? true : value == 'true';
485 $('body').toggleClass('no-modelIcons', !power_user.timestamp_model_icon);443 $('body').toggleClass('no-modelIcons', !power_user.timestamp_model_icon);
486 $('#messageModelIconEnabled').prop('checked', power_user.timestamp_model_icon);444 $('#messageModelIconEnabled').prop('checked', power_user.timestamp_model_icon);
487}445}
488446
489function switchTokenCount() {447function switchTokenCount() {
490 const value = localStorage.getItem(storage_keys.message_token_count_enabled);
491 power_user.message_token_count_enabled = value === null ? false : value == 'true';
492 $('body').toggleClass('no-tokenCount', !power_user.message_token_count_enabled);448 $('body').toggleClass('no-tokenCount', !power_user.message_token_count_enabled);
493 $('#messageTokensEnabled').prop('checked', power_user.message_token_count_enabled);449 $('#messageTokensEnabled').prop('checked', power_user.message_token_count_enabled);
494}450}
495451
496function switchMesIDDisplay() {452function switchMesIDDisplay() {
497 const value = localStorage.getItem(storage_keys.mesIDDisplay_enabled);
498 power_user.mesIDDisplay_enabled = value === null ? true : value == 'true';
499 /* console.log(`
500 localstorage value:${value},
501 poweruser before:${before},
502 poweruser after:${power_user.mesIDDisplay_enabled}`) */
503 $('body').toggleClass('no-mesIDDisplay', !power_user.mesIDDisplay_enabled);453 $('body').toggleClass('no-mesIDDisplay', !power_user.mesIDDisplay_enabled);
504 $('#mesIDDisplayEnabled').prop('checked', power_user.mesIDDisplay_enabled);454 $('#mesIDDisplayEnabled').prop('checked', power_user.mesIDDisplay_enabled);
505}455}
506456
507function switchHideChatAvatars() {457function switchHideChatAvatars() {
508 const value = localStorage.getItem(storage_keys.hideChatAvatars_enabled);
509 power_user.hideChatAvatars_enabled = value === null ? false : value == 'true';
510 /*console.log(`
511 localstorage value:${value},
512 poweruser after:${power_user.hideChatAvatars_enabled}`)
513 */
514 $('body').toggleClass('hideChatAvatars', power_user.hideChatAvatars_enabled);458 $('body').toggleClass('hideChatAvatars', power_user.hideChatAvatars_enabled);
515 $('#hideChatAvatarsEnabled').prop('checked', power_user.hideChatAvatars_enabled);459 $('#hideChatAvatarsEnabled').prop('checked', power_user.hideChatAvatars_enabled);
516}460}
517461
518function switchMessageActions() {462function switchMessageActions() {
519 const value = localStorage.getItem(storage_keys.expand_message_actions);
520 power_user.expand_message_actions = value === null ? false : value == 'true';
521 $('body').toggleClass('expandMessageActions', power_user.expand_message_actions);463 $('body').toggleClass('expandMessageActions', power_user.expand_message_actions);
522 $('#expandMessageActions').prop('checked', power_user.expand_message_actions);464 $('#expandMessageActions').prop('checked', power_user.expand_message_actions);
523 $('.extraMesButtons, .extraMesButtonsHint').removeAttr('style');465 $('.extraMesButtons, .extraMesButtonsHint').removeAttr('style');
524}466}
525467
526function switchReducedMotion() {468function switchReducedMotion() {
527 const value = localStorage.getItem(storage_keys.reduced_motion);
528 power_user.reduced_motion = value === null ? false : value == 'true';
529 jQuery.fx.off = power_user.reduced_motion;469 jQuery.fx.off = power_user.reduced_motion;
530 const overrideDuration = power_user.reduced_motion ? 0 : ANIMATION_DURATION_DEFAULT;470 const overrideDuration = power_user.reduced_motion ? 0 : ANIMATION_DURATION_DEFAULT;
531 setAnimationDuration(overrideDuration);471 setAnimationDuration(overrideDuration);
@@ -534,8 +474,6 @@ function switchReducedMotion() {
534}474}
535475
536function switchCompactInputArea() {476function switchCompactInputArea() {
537 const value = localStorage.getItem(storage_keys.compact_input_area);
538 power_user.compact_input_area = value === null ? true : value == 'true';
539 $('#send_form').toggleClass('compact', power_user.compact_input_area);477 $('#send_form').toggleClass('compact', power_user.compact_input_area);
540 $('#compact_input_area').prop('checked', power_user.compact_input_area);478 $('#compact_input_area').prop('checked', power_user.compact_input_area);
541}479}
@@ -551,8 +489,6 @@ async function switchLabMode() {
551 }489 }
552 */490 */
553 await delay(100);491 await delay(100);
554 const value = localStorage.getItem(storage_keys.enableLabMode);
555 power_user.enableLabMode = value === null ? false : value == 'true';
556 $('body').toggleClass('enableLabMode', power_user.enableLabMode);492 $('body').toggleClass('enableLabMode', power_user.enableLabMode);
557 $('#enableLabMode').prop('checked', power_user.enableLabMode);493 $('#enableLabMode').prop('checked', power_user.enableLabMode);
558494
@@ -599,8 +535,6 @@ async function switchLabMode() {
599535
600async function switchZenSliders() {536async function switchZenSliders() {
601 await delay(100);537 await delay(100);
602 const value = localStorage.getItem(storage_keys.enableZenSliders);
603 power_user.enableZenSliders = value === null ? false : value == 'true';
604 $('body').toggleClass('enableZenSliders', power_user.enableZenSliders);538 $('body').toggleClass('enableZenSliders', power_user.enableZenSliders);
605 $('#enableZenSliders').prop('checked', power_user.enableZenSliders);539 $('#enableZenSliders').prop('checked', power_user.enableZenSliders);
606540
@@ -987,8 +921,6 @@ async function CreateZenSliders(elmnt) {
987 }921 }
988}922}
989function switchUiMode() {923function switchUiMode() {
990 const fastUi = localStorage.getItem(storage_keys.fast_ui_mode);
991 power_user.fast_ui_mode = fastUi === null ? true : fastUi == 'true';
992 $('body').toggleClass('no-blur', power_user.fast_ui_mode);924 $('body').toggleClass('no-blur', power_user.fast_ui_mode);
993 $('#fast_ui_mode').prop('checked', power_user.fast_ui_mode);925 $('#fast_ui_mode').prop('checked', power_user.fast_ui_mode);
994 if (power_user.fast_ui_mode) {926 if (power_user.fast_ui_mode) {
@@ -1038,8 +970,6 @@ function switchMovingUI() {
1038 $('.drawer-content.maximized').each(function () {970 $('.drawer-content.maximized').each(function () {
1039 $(this).find('.inline-drawer-maximize').trigger('click');971 $(this).find('.inline-drawer-maximize').trigger('click');
1040 });972 });
1041 const movingUI = localStorage.getItem(storage_keys.movingUI);
1042 power_user.movingUI = movingUI === null ? false : movingUI == 'true';
1043 $('body').toggleClass('movingUI', power_user.movingUI);973 $('body').toggleClass('movingUI', power_user.movingUI);
1044 if (power_user.movingUI === true) {974 if (power_user.movingUI === true) {
1045 initMovingUI();975 initMovingUI();
@@ -1055,9 +985,7 @@ function switchMovingUI() {
1055 }985 }
1056}986}
1057987
1058function noShadows() {988function applyNoShadows() {
1059 const noShadows = localStorage.getItem(storage_keys.noShadows);
1060 power_user.noShadows = noShadows === null ? false : noShadows == 'true';
1061 $('body').toggleClass('noShadows', power_user.noShadows);989 $('body').toggleClass('noShadows', power_user.noShadows);
1062 $('#noShadowsmode').prop('checked', power_user.noShadows);990 $('#noShadowsmode').prop('checked', power_user.noShadows);
1063 if (power_user.noShadows) {991 if (power_user.noShadows) {
@@ -1071,12 +999,9 @@ function noShadows() {
1071}999}
10721000
1073function applyAvatarStyle() {1001function applyAvatarStyle() {
1074 power_user.avatar_style = Number(localStorage.getItem(storage_keys.avatar_style) ?? avatar_styles.ROUND);
1075 $('body').toggleClass('big-avatars', power_user.avatar_style === avatar_styles.RECTANGULAR);1002 $('body').toggleClass('big-avatars', power_user.avatar_style === avatar_styles.RECTANGULAR);
1076 $('body').toggleClass('square-avatars', power_user.avatar_style === avatar_styles.SQUARE);1003 $('body').toggleClass('square-avatars', power_user.avatar_style === avatar_styles.SQUARE);
1077 $('#avatar_style').val(power_user.avatar_style).prop('selected', true);1004 $('#avatar_style').val(power_user.avatar_style).prop('selected', true);
1078 //$(`input[name="avatar_style"][value="${power_user.avatar_style}"]`).prop("checked", true);
1079
1080}1005}
10811006
1082function applyChatDisplay() {1007function applyChatDisplay() {
@@ -1111,8 +1036,6 @@ function applyChatDisplay() {
1111}1036}
11121037
1113function applyChatWidth(type) {1038function applyChatWidth(type) {
1114 power_user.chat_width = Number(localStorage.getItem(storage_keys.chat_width) ?? 50);
1115
1116 if (type === 'forced') {1039 if (type === 'forced') {
1117 let r = document.documentElement;1040 let r = document.documentElement;
1118 r.style.setProperty('--sheldWidth', `${power_user.chat_width}vw`);1041 r.style.setProperty('--sheldWidth', `${power_user.chat_width}vw`);
@@ -1174,8 +1097,6 @@ async function applyThemeColor(type) {
1174}1097}
11751098
1176async function applyCustomCSS() {1099async function applyCustomCSS() {
1177 power_user.custom_css = String(localStorage.getItem(storage_keys.custom_css) ?? '');
1178
1179 $('#customCSS').val(power_user.custom_css);1100 $('#customCSS').val(power_user.custom_css);
1180 var styleId = 'custom-style';1101 var styleId = 'custom-style';
1181 var style = document.getElementById(styleId);1102 var style = document.getElementById(styleId);
@@ -1189,32 +1110,26 @@ async function applyCustomCSS() {
1189}1110}
11901111
1191async function applyBlurStrength() {1112async function applyBlurStrength() {
1192 power_user.blur_strength = Number(localStorage.getItem(storage_keys.blur_strength) ?? 1);1113 document.documentElement.style.setProperty('--blurStrength', String(power_user.blur_strength));
1193 document.documentElement.style.setProperty('--blurStrength', power_user.blur_strength);
1194 $('#blur_strength_counter').val(power_user.blur_strength);1114 $('#blur_strength_counter').val(power_user.blur_strength);
1195 $('#blur_strength').val(power_user.blur_strength);1115 $('#blur_strength').val(power_user.blur_strength);
1196
1197
1198}1116}
11991117
1200async function applyShadowWidth() {1118async function applyShadowWidth() {
1201 power_user.shadow_width = Number(localStorage.getItem(storage_keys.shadow_width) ?? 2);1119 document.documentElement.style.setProperty('--shadowWidth', String(power_user.shadow_width));
1202 document.documentElement.style.setProperty('--shadowWidth', power_user.shadow_width);
1203 $('#shadow_width_counter').val(power_user.shadow_width);1120 $('#shadow_width_counter').val(power_user.shadow_width);
1204 $('#shadow_width').val(power_user.shadow_width);1121 $('#shadow_width').val(power_user.shadow_width);
12051122
1206}1123}
12071124
1208async function applyFontScale(type) {1125async function applyFontScale(type) {
1209
1210 power_user.font_scale = Number(localStorage.getItem(storage_keys.font_scale) ?? 1);
1211 //this is to allow forced setting on page load, theme swap, etc1126 //this is to allow forced setting on page load, theme swap, etc
1212 if (type === 'forced') {1127 if (type === 'forced') {
1213 document.documentElement.style.setProperty('--fontScale', power_user.font_scale);1128 document.documentElement.style.setProperty('--fontScale', String(power_user.font_scale));
1214 } else {1129 } else {
1215 //this is to prevent the slider from updating page in real time1130 //this is to prevent the slider from updating page in real time
1216 $('#font_scale').off('mouseup touchend').on('mouseup touchend', () => {1131 $('#font_scale').off('mouseup touchend').on('mouseup touchend', () => {
1217 document.documentElement.style.setProperty('--fontScale', power_user.font_scale);1132 document.documentElement.style.setProperty('--fontScale', String(power_user.font_scale));
1218 });1133 });
1219 }1134 }
12201135
@@ -1243,64 +1158,55 @@ async function applyTheme(name) {
1243 {1158 {
1244 key: 'blur_strength',1159 key: 'blur_strength',
1245 action: async () => {1160 action: async () => {
1246 localStorage.setItem(storage_keys.blur_strength, power_user.blur_strength);
1247 await applyBlurStrength();1161 await applyBlurStrength();
1248 },1162 },
1249 },1163 },
1250 {1164 {
1251 key: 'custom_css',1165 key: 'custom_css',
1252 action: async () => {1166 action: async () => {
1253 localStorage.setItem(storage_keys.custom_css, power_user.custom_css);
1254 await applyCustomCSS();1167 await applyCustomCSS();
1255 },1168 },
1256 },1169 },
1257 {1170 {
1258 key: 'shadow_width',1171 key: 'shadow_width',
1259 action: async () => {1172 action: async () => {
1260 localStorage.setItem(storage_keys.shadow_width, power_user.shadow_width);
1261 await applyShadowWidth();1173 await applyShadowWidth();
1262 },1174 },
1263 },1175 },
1264 {1176 {
1265 key: 'font_scale',1177 key: 'font_scale',
1266 action: async () => {1178 action: async () => {
1267 localStorage.setItem(storage_keys.font_scale, power_user.font_scale);
1268 await applyFontScale('forced');1179 await applyFontScale('forced');
1269 },1180 },
1270 },1181 },
1271 {1182 {
1272 key: 'fast_ui_mode',1183 key: 'fast_ui_mode',
1273 action: async () => {1184 action: async () => {
1274 localStorage.setItem(storage_keys.fast_ui_mode, power_user.fast_ui_mode);
1275 switchUiMode();1185 switchUiMode();
1276 },1186 },
1277 },1187 },
1278 {1188 {
1279 key: 'waifuMode',1189 key: 'waifuMode',
1280 action: async () => {1190 action: async () => {
1281 localStorage.setItem(storage_keys.waifuMode, power_user.waifuMode);
1282 switchWaifuMode();1191 switchWaifuMode();
1283 },1192 },
1284 },1193 },
1285 {1194 {
1286 key: 'chat_display',1195 key: 'chat_display',
1287 action: async () => {1196 action: async () => {
1288 localStorage.setItem(storage_keys.chat_display, power_user.chat_display);
1289 applyChatDisplay();1197 applyChatDisplay();
1290 },1198 },
1291 },1199 },
1292 {1200 {
1293 key: 'avatar_style',1201 key: 'avatar_style',
1294 action: async () => {1202 action: async () => {
1295 localStorage.setItem(storage_keys.avatar_style, power_user.avatar_style);
1296 applyAvatarStyle();1203 applyAvatarStyle();
1297 },1204 },
1298 },1205 },
1299 {1206 {
1300 key: 'noShadows',1207 key: 'noShadows',
1301 action: async () => {1208 action: async () => {
1302 localStorage.setItem(storage_keys.noShadows, power_user.noShadows);1209 applyNoShadows();
1303 noShadows();
1304 },1210 },
1305 },1211 },
1306 {1212 {
@@ -1310,78 +1216,66 @@ async function applyTheme(name) {
1310 if (!power_user.chat_width) {1216 if (!power_user.chat_width) {
1311 power_user.chat_width = 50;1217 power_user.chat_width = 50;
1312 }1218 }
1313
1314 localStorage.setItem(storage_keys.chat_width, String(power_user.chat_width));
1315 applyChatWidth('forced');1219 applyChatWidth('forced');
1316 },1220 },
1317 },1221 },
1318 {1222 {
1319 key: 'timer_enabled',1223 key: 'timer_enabled',
1320 action: async () => {1224 action: async () => {
1321 localStorage.setItem(storage_keys.timer_enabled, Boolean(power_user.timer_enabled));
1322 switchTimer();1225 switchTimer();
1323 },1226 },
1324 },1227 },
1325 {1228 {
1326 key: 'timestamps_enabled',1229 key: 'timestamps_enabled',
1327 action: async () => {1230 action: async () => {
1328 localStorage.setItem(storage_keys.timestamps_enabled, Boolean(power_user.timestamps_enabled));
1329 switchTimestamps();1231 switchTimestamps();
1330 },1232 },
1331 },1233 },
1332 {1234 {
1333 key: 'timestamp_model_icon',1235 key: 'timestamp_model_icon',
1334 action: async () => {1236 action: async () => {
1335 localStorage.setItem(storage_keys.timestamp_model_icon, Boolean(power_user.timestamp_model_icon));
1336 switchIcons();1237 switchIcons();
1337 },1238 },
1338 },1239 },
1339 {1240 {
1340 key: 'message_token_count_enabled',1241 key: 'message_token_count_enabled',
1341 action: async () => {1242 action: async () => {
1342 localStorage.setItem(storage_keys.message_token_count_enabled, Boolean(power_user.message_token_count_enabled));
1343 switchTokenCount();1243 switchTokenCount();
1344 },1244 },
1345 },1245 },
1346 {1246 {
1347 key: 'mesIDDisplay_enabled',1247 key: 'mesIDDisplay_enabled',
1348 action: async () => {1248 action: async () => {
1349 localStorage.setItem(storage_keys.mesIDDisplay_enabled, Boolean(power_user.mesIDDisplay_enabled));
1350 switchMesIDDisplay();1249 switchMesIDDisplay();
1351 },1250 },
1352 },1251 },
1353 {1252 {
1354 key: 'hideChatAvatars_enabled',1253 key: 'hideChatAvatars_enabled',
1355 action: async () => {1254 action: async () => {
1356 localStorage.setItem(storage_keys.hideChatAvatars_enabled, Boolean(power_user.hideChatAvatars_enabled));
1357 switchHideChatAvatars();1255 switchHideChatAvatars();
1358 },1256 },
1359 },1257 },
1360 {1258 {
1361 key: 'expand_message_actions',1259 key: 'expand_message_actions',
1362 action: async () => {1260 action: async () => {
1363 localStorage.setItem(storage_keys.expand_message_actions, Boolean(power_user.expand_message_actions));
1364 switchMessageActions();1261 switchMessageActions();
1365 },1262 },
1366 },1263 },
1367 {1264 {
1368 key: 'enableZenSliders',1265 key: 'enableZenSliders',
1369 action: async () => {1266 action: async () => {
1370 localStorage.setItem(storage_keys.enableZenSliders, Boolean(power_user.enableZenSliders));
1371 switchMessageActions();1267 switchMessageActions();
1372 },1268 },
1373 },1269 },
1374 {1270 {
1375 key: 'enableLabMode',1271 key: 'enableLabMode',
1376 action: async () => {1272 action: async () => {
1377 localStorage.setItem(storage_keys.enableLabMode, Boolean(power_user.enableLabMode));
1378 switchMessageActions();1273 switchMessageActions();
1379 },1274 },
1380 },1275 },
1381 {1276 {
1382 key: 'hotswap_enabled',1277 key: 'hotswap_enabled',
1383 action: async () => {1278 action: async () => {
1384 localStorage.setItem(storage_keys.hotswap_enabled, Boolean(power_user.hotswap_enabled));
1385 switchHotswap();1279 switchHotswap();
1386 },1280 },
1387 },1281 },
@@ -1402,7 +1296,6 @@ async function applyTheme(name) {
1402 {1296 {
1403 key: 'reduced_motion',1297 key: 'reduced_motion',
1404 action: async () => {1298 action: async () => {
1405 localStorage.setItem(storage_keys.reduced_motion, String(power_user.reduced_motion));
1406 $('#reduced_motion').prop('checked', power_user.reduced_motion);1299 $('#reduced_motion').prop('checked', power_user.reduced_motion);
1407 switchReducedMotion();1300 switchReducedMotion();
1408 },1301 },
@@ -1410,7 +1303,6 @@ async function applyTheme(name) {
1410 {1303 {
1411 key: 'compact_input_area',1304 key: 'compact_input_area',
1412 action: async () => {1305 action: async () => {
1413 localStorage.setItem(storage_keys.compact_input_area, String(power_user.compact_input_area));
1414 $('#compact_input_area').prop('checked', power_user.compact_input_area);1306 $('#compact_input_area').prop('checked', power_user.compact_input_area);
1415 switchCompactInputArea();1307 switchCompactInputArea();
1416 },1308 },
@@ -1465,6 +1357,7 @@ async function showDebugMenu() {
1465 callGenericPopup(template, POPUP_TYPE.TEXT, '', { wide: true, large: true, allowVerticalScrolling: true });1357 callGenericPopup(template, POPUP_TYPE.TEXT, '', { wide: true, large: true, allowVerticalScrolling: true });
1466}1358}
14671359
1360function applyPowerUserSettings() {
1468 switchUiMode();1361 switchUiMode();
1469 applyFontScale('forced');1362 applyFontScale('forced');
1470 applyThemeColor();1363 applyThemeColor();
@@ -1474,7 +1367,7 @@ applyBlurStrength();
1474 applyShadowWidth();1367 applyShadowWidth();
1475 applyCustomCSS();1368 applyCustomCSS();
1476 switchMovingUI();1369 switchMovingUI();
1477noShadows();1370 applyNoShadows();
1478 switchHotswap();1371 switchHotswap();
1479 switchTimer();1372 switchTimer();
1480 switchTimestamps();1373 switchTimestamps();
@@ -1483,6 +1376,7 @@ switchMesIDDisplay();
1483 switchHideChatAvatars();1376 switchHideChatAvatars();
1484 switchTokenCount();1377 switchTokenCount();
1485 switchMessageActions();1378 switchMessageActions();
1379}
14861380
1487function getExampleMessagesBehavior() {1381function getExampleMessagesBehavior() {
1488 if (power_user.strip_examples) {1382 if (power_user.strip_examples) {
@@ -1545,20 +1439,10 @@ async function loadPowerUserSettings(settings, data) {
1545 context_presets = data.context;1439 context_presets = data.context;
1546 }1440 }
15471441
1548 // These are still local storage1442 // These are still local storage. Delete in 1.12.7
1549 const fastUi = localStorage.getItem(storage_keys.fast_ui_mode);
1550 const movingUI = localStorage.getItem(storage_keys.movingUI);
1551 const noShadows = localStorage.getItem(storage_keys.noShadows);
1552 const hotswap = localStorage.getItem(storage_keys.hotswap_enabled);
1553 const timer = localStorage.getItem(storage_keys.timer_enabled);
1554 const timestamps = localStorage.getItem(storage_keys.timestamps_enabled);
1555 const mesIDDisplay = localStorage.getItem(storage_keys.mesIDDisplay_enabled);
1556 const hideChatAvatars = localStorage.getItem(storage_keys.hideChatAvatars_enabled);
1557 const expandMessageActions = localStorage.getItem(storage_keys.expand_message_actions);
1558 const enableZenSliders = localStorage.getItem(storage_keys.enableZenSliders);
1559 const enableLabMode = localStorage.getItem(storage_keys.enableLabMode);
1560 const autoLoadChat = localStorage.getItem(storage_keys.auto_load_chat_legacy);1443 const autoLoadChat = localStorage.getItem(storage_keys.auto_load_chat_legacy);
1561 const autoConnect = localStorage.getItem(storage_keys.auto_connect_legacy);1444 const autoConnect = localStorage.getItem(storage_keys.auto_connect_legacy);
1445 const hideChatAvatars = localStorage.getItem(storage_keys.hideChatAvatars_legacy);
15621446
1563 if (autoLoadChat) {1447 if (autoLoadChat) {
1564 power_user.auto_load_chat = autoLoadChat === 'true';1448 power_user.auto_load_chat = autoLoadChat === 'true';
@@ -1570,22 +1454,10 @@ async function loadPowerUserSettings(settings, data) {
1570 localStorage.removeItem(storage_keys.auto_connect_legacy);1454 localStorage.removeItem(storage_keys.auto_connect_legacy);
1571 }1455 }
15721456
1573 power_user.fast_ui_mode = fastUi === null ? true : fastUi == 'true';1457 if (hideChatAvatars) {
1574 power_user.movingUI = movingUI === null ? false : movingUI == 'true';1458 power_user.hideChatAvatars_enabled = hideChatAvatars === 'true';
1575 power_user.noShadows = noShadows === null ? false : noShadows == 'true';1459 localStorage.removeItem(storage_keys.hideChatAvatars_legacy);
1576 power_user.hotswap_enabled = hotswap === null ? true : hotswap == 'true';1460 }
1577 power_user.timer_enabled = timer === null ? true : timer == 'true';
1578 power_user.timestamps_enabled = timestamps === null ? true : timestamps == 'true';
1579 power_user.mesIDDisplay_enabled = mesIDDisplay === null ? true : mesIDDisplay == 'true';
1580 power_user.hideChatAvatars_enabled = hideChatAvatars === null ? true : hideChatAvatars == 'true';
1581 power_user.expand_message_actions = expandMessageActions === null ? true : expandMessageActions == 'true';
1582 power_user.enableZenSliders = enableZenSliders === null ? false : enableZenSliders == 'true';
1583 power_user.enableLabMode = enableLabMode === null ? false : enableLabMode == 'true';
1584 power_user.avatar_style = Number(localStorage.getItem(storage_keys.avatar_style) ?? avatar_styles.ROUND);
1585 //power_user.chat_display = Number(localStorage.getItem(storage_keys.chat_display) ?? chat_styles.DEFAULT);
1586 power_user.chat_width = Number(localStorage.getItem(storage_keys.chat_width) ?? 50);
1587 power_user.font_scale = Number(localStorage.getItem(storage_keys.font_scale) ?? 1);
1588 power_user.blur_strength = Number(localStorage.getItem(storage_keys.blur_strength) ?? 10);
15891461
1590 if (power_user.chat_display === '') {1462 if (power_user.chat_display === '') {
1591 power_user.chat_display = chat_styles.DEFAULT;1463 power_user.chat_display = chat_styles.DEFAULT;
@@ -1672,7 +1544,7 @@ async function loadPowerUserSettings(settings, data) {
1672 $('#messageTimestampsEnabled').prop('checked', power_user.timestamps_enabled);1544 $('#messageTimestampsEnabled').prop('checked', power_user.timestamps_enabled);
1673 $('#messageModelIconEnabled').prop('checked', power_user.timestamp_model_icon);1545 $('#messageModelIconEnabled').prop('checked', power_user.timestamp_model_icon);
1674 $('#mesIDDisplayEnabled').prop('checked', power_user.mesIDDisplay_enabled);1546 $('#mesIDDisplayEnabled').prop('checked', power_user.mesIDDisplay_enabled);
1675 $('#hideChatAvatarsEndabled').prop('checked', power_user.hideChatAvatars_enabled);1547 $('#hideChatAvatarsEnabled').prop('checked', power_user.hideChatAvatars_enabled);
1676 $('#prefer_character_prompt').prop('checked', power_user.prefer_character_prompt);1548 $('#prefer_character_prompt').prop('checked', power_user.prefer_character_prompt);
1677 $('#prefer_character_jailbreak').prop('checked', power_user.prefer_character_jailbreak);1549 $('#prefer_character_jailbreak').prop('checked', power_user.prefer_character_jailbreak);
1678 $('#enableZenSliders').prop('checked', power_user.enableZenSliders).trigger('input');1550 $('#enableZenSliders').prop('checked', power_user.enableZenSliders).trigger('input');
@@ -3313,10 +3185,8 @@ $(document).ready(() => {
3313 saveSettingsDebounced();3185 saveSettingsDebounced();
3314 });3186 });
33153187
3316 // Settings that go to local storage
3317 $('#fast_ui_mode').change(function () {3188 $('#fast_ui_mode').change(function () {
3318 power_user.fast_ui_mode = $(this).prop('checked');3189 power_user.fast_ui_mode = $(this).prop('checked');
3319 localStorage.setItem(storage_keys.fast_ui_mode, power_user.fast_ui_mode);
3320 switchUiMode();3190 switchUiMode();
3321 saveSettingsDebounced();3191 saveSettingsDebounced();
3322 });3192 });
@@ -3329,22 +3199,19 @@ $(document).ready(() => {
33293199
3330 $('#customCSS').on('input', () => {3200 $('#customCSS').on('input', () => {
3331 power_user.custom_css = String($('#customCSS').val());3201 power_user.custom_css = String($('#customCSS').val());
3332 localStorage.setItem(storage_keys.custom_css, power_user.custom_css);
3333 saveSettingsDebounced();3202 saveSettingsDebounced();
3334 applyCustomCSS();3203 applyCustomCSS();
3335 });3204 });
33363205
3337 $('#movingUImode').change(function () {3206 $('#movingUImode').change(function () {
3338 power_user.movingUI = $(this).prop('checked');3207 power_user.movingUI = $(this).prop('checked');
3339 localStorage.setItem(storage_keys.movingUI, power_user.movingUI);
3340 switchMovingUI();3208 switchMovingUI();
3341 saveSettingsDebounced();3209 saveSettingsDebounced();
3342 });3210 });
33433211
3344 $('#noShadowsmode').change(function () {3212 $('#noShadowsmode').change(function () {
3345 power_user.noShadows = $(this).prop('checked');3213 power_user.noShadows = $(this).prop('checked');
3346 localStorage.setItem(storage_keys.noShadows, power_user.noShadows);3214 applyNoShadows();
3347 noShadows();
3348 saveSettingsDebounced();3215 saveSettingsDebounced();
3349 });3216 });
33503217
@@ -3353,7 +3220,6 @@ $(document).ready(() => {
3353 $('#avatar_style').on('change', function () {3220 $('#avatar_style').on('change', function () {
3354 const value = $(this).find(':selected').val();3221 const value = $(this).find(':selected').val();
3355 power_user.avatar_style = Number(value);3222 power_user.avatar_style = Number(value);
3356 localStorage.setItem(storage_keys.avatar_style, power_user.avatar_style);
3357 applyAvatarStyle();3223 applyAvatarStyle();
3358 saveSettingsDebounced();3224 saveSettingsDebounced();
3359 });3225 });
@@ -3361,17 +3227,15 @@ $(document).ready(() => {
3361 $('#chat_display').on('change', function () {3227 $('#chat_display').on('change', function () {
3362 const value = $(this).find(':selected').val();3228 const value = $(this).find(':selected').val();
3363 power_user.chat_display = Number(value);3229 power_user.chat_display = Number(value);
3364 localStorage.setItem(storage_keys.chat_display, power_user.chat_display);
3365 applyChatDisplay();3230 applyChatDisplay();
3366 saveSettingsDebounced();3231 saveSettingsDebounced();
3367
3368 });3232 });
33693233
3370 $('#chat_width_slider').on('input', function (e, data) {3234 $('#chat_width_slider').on('input', function (e, data) {
3371 const applyMode = data?.forced ? 'forced' : 'normal';3235 const applyMode = data?.forced ? 'forced' : 'normal';
3372 power_user.chat_width = Number(e.target.value);3236 power_user.chat_width = Number(e.target.value);
3373 localStorage.setItem(storage_keys.chat_width, power_user.chat_width);
3374 applyChatWidth(applyMode);3237 applyChatWidth(applyMode);
3238 saveSettingsDebounced();
3375 setHotswapsDebounced();3239 setHotswapsDebounced();
3376 });3240 });
33773241
@@ -3401,7 +3265,6 @@ $(document).ready(() => {
3401 const applyMode = data?.forced ? 'forced' : 'normal';3265 const applyMode = data?.forced ? 'forced' : 'normal';
3402 power_user.font_scale = Number(e.target.value);3266 power_user.font_scale = Number(e.target.value);
3403 $('#font_scale_counter').val(power_user.font_scale);3267 $('#font_scale_counter').val(power_user.font_scale);
3404 localStorage.setItem(storage_keys.font_scale, power_user.font_scale);
3405 await applyFontScale(applyMode);3268 await applyFontScale(applyMode);
3406 saveSettingsDebounced();3269 saveSettingsDebounced();
3407 });3270 });
@@ -3409,7 +3272,6 @@ $(document).ready(() => {
3409 $('input[name="blur_strength"]').on('input', async function (e) {3272 $('input[name="blur_strength"]').on('input', async function (e) {
3410 power_user.blur_strength = Number(e.target.value);3273 power_user.blur_strength = Number(e.target.value);
3411 $('#blur_strength_counter').val(power_user.blur_strength);3274 $('#blur_strength_counter').val(power_user.blur_strength);
3412 localStorage.setItem(storage_keys.blur_strength, power_user.blur_strength);
3413 await applyBlurStrength();3275 await applyBlurStrength();
3414 saveSettingsDebounced();3276 saveSettingsDebounced();
3415 });3277 });
@@ -3417,7 +3279,6 @@ $(document).ready(() => {
3417 $('input[name="shadow_width"]').on('input', async function (e) {3279 $('input[name="shadow_width"]').on('input', async function (e) {
3418 power_user.shadow_width = Number(e.target.value);3280 power_user.shadow_width = Number(e.target.value);
3419 $('#shadow_width_counter').val(power_user.shadow_width);3281 $('#shadow_width_counter').val(power_user.shadow_width);
3420 localStorage.setItem(storage_keys.shadow_width, power_user.shadow_width);
3421 await applyShadowWidth();3282 await applyShadowWidth();
3422 saveSettingsDebounced();3283 saveSettingsDebounced();
3423 });3284 });
@@ -3658,36 +3519,36 @@ $(document).ready(() => {
3658 $('#messageTimerEnabled').on('input', function () {3519 $('#messageTimerEnabled').on('input', function () {
3659 const value = !!$(this).prop('checked');3520 const value = !!$(this).prop('checked');
3660 power_user.timer_enabled = value;3521 power_user.timer_enabled = value;
3661 localStorage.setItem(storage_keys.timer_enabled, Boolean(power_user.timer_enabled));
3662 switchTimer();3522 switchTimer();
3523 saveSettingsDebounced();
3663 });3524 });
36643525
3665 $('#messageTimestampsEnabled').on('input', function () {3526 $('#messageTimestampsEnabled').on('input', function () {
3666 const value = !!$(this).prop('checked');3527 const value = !!$(this).prop('checked');
3667 power_user.timestamps_enabled = value;3528 power_user.timestamps_enabled = value;
3668 localStorage.setItem(storage_keys.timestamps_enabled, Boolean(power_user.timestamps_enabled));
3669 switchTimestamps();3529 switchTimestamps();
3530 saveSettingsDebounced();
3670 });3531 });
36713532
3672 $('#messageModelIconEnabled').on('input', function () {3533 $('#messageModelIconEnabled').on('input', function () {
3673 const value = !!$(this).prop('checked');3534 const value = !!$(this).prop('checked');
3674 power_user.timestamp_model_icon = value;3535 power_user.timestamp_model_icon = value;
3675 localStorage.setItem(storage_keys.timestamp_model_icon, Boolean(power_user.timestamp_model_icon));
3676 switchIcons();3536 switchIcons();
3537 saveSettingsDebounced();
3677 });3538 });
36783539
3679 $('#messageTokensEnabled').on('input', function () {3540 $('#messageTokensEnabled').on('input', function () {
3680 const value = !!$(this).prop('checked');3541 const value = !!$(this).prop('checked');
3681 power_user.message_token_count_enabled = value;3542 power_user.message_token_count_enabled = value;
3682 localStorage.setItem(storage_keys.message_token_count_enabled, Boolean(power_user.message_token_count_enabled));
3683 switchTokenCount();3543 switchTokenCount();
3544 saveSettingsDebounced();
3684 });3545 });
36853546
3686 $('#expandMessageActions').on('input', function () {3547 $('#expandMessageActions').on('input', function () {
3687 const value = !!$(this).prop('checked');3548 const value = !!$(this).prop('checked');
3688 power_user.expand_message_actions = value;3549 power_user.expand_message_actions = value;
3689 localStorage.setItem(storage_keys.expand_message_actions, Boolean(power_user.expand_message_actions));
3690 switchMessageActions();3550 switchMessageActions();
3551 saveSettingsDebounced();
3691 });3552 });
36923553
3693 $('#enableZenSliders').on('input', function () {3554 $('#enableZenSliders').on('input', function () {
@@ -3699,9 +3560,8 @@ $(document).ready(() => {
3699 return;3560 return;
3700 }3561 }
3701 power_user.enableZenSliders = value;3562 power_user.enableZenSliders = value;
3702 localStorage.setItem(storage_keys.enableZenSliders, Boolean(power_user.enableZenSliders));
3703 saveSettingsDebounced();
3704 switchZenSliders();3563 switchZenSliders();
3564 saveSettingsDebounced();
3705 });3565 });
37063566
3707 $('#enableLabMode').on('input', function () {3567 $('#enableLabMode').on('input', function () {
@@ -3714,30 +3574,29 @@ $(document).ready(() => {
3714 }3574 }
37153575
3716 power_user.enableLabMode = value;3576 power_user.enableLabMode = value;
3717 localStorage.setItem(storage_keys.enableLabMode, Boolean(power_user.enableLabMode));
3718 saveSettingsDebounced();
3719 switchLabMode();3577 switchLabMode();
3578 saveSettingsDebounced();
3720 });3579 });
37213580
3722 $('#mesIDDisplayEnabled').on('input', function () {3581 $('#mesIDDisplayEnabled').on('input', function () {
3723 const value = !!$(this).prop('checked');3582 const value = !!$(this).prop('checked');
3724 power_user.mesIDDisplay_enabled = value;3583 power_user.mesIDDisplay_enabled = value;
3725 localStorage.setItem(storage_keys.mesIDDisplay_enabled, Boolean(power_user.mesIDDisplay_enabled));
3726 switchMesIDDisplay();3584 switchMesIDDisplay();
3585 saveSettingsDebounced();
3727 });3586 });
37283587
3729 $('#hideChatAvatarsEnabled').on('input', function () {3588 $('#hideChatAvatarsEnabled').on('input', function () {
3730 const value = !!$(this).prop('checked');3589 const value = !!$(this).prop('checked');
3731 power_user.hideChatAvatars_enabled = value;3590 power_user.hideChatAvatars_enabled = value;
3732 localStorage.setItem(storage_keys.hideChatAvatars_enabled, Boolean(power_user.hideChatAvatars_enabled));
3733 switchHideChatAvatars();3591 switchHideChatAvatars();
3592 saveSettingsDebounced();
3734 });3593 });
37353594
3736 $('#hotswapEnabled').on('input', function () {3595 $('#hotswapEnabled').on('input', function () {
3737 const value = !!$(this).prop('checked');3596 const value = !!$(this).prop('checked');
3738 power_user.hotswap_enabled = value;3597 power_user.hotswap_enabled = value;
3739 localStorage.setItem(storage_keys.hotswap_enabled, Boolean(power_user.hotswap_enabled));
3740 switchHotswap();3598 switchHotswap();
3599 saveSettingsDebounced();
3741 });3600 });
37423601
3743 $('#prefer_character_prompt').on('input', function () {3602 $('#prefer_character_prompt').on('input', function () {
@@ -3845,8 +3704,8 @@ $(document).ready(() => {
3845 $('#ui_mode_select').on('change', function () {3704 $('#ui_mode_select').on('change', function () {
3846 const value = $(this).find(':selected').val();3705 const value = $(this).find(':selected').val();
3847 power_user.ui_mode = Number(value);3706 power_user.ui_mode = Number(value);
3848 saveSettingsDebounced();
3849 switchSimpleMode();3707 switchSimpleMode();
3708 saveSettingsDebounced();
3850 });3709 });
38513710
3852 $('#bogus_folders').on('input', function () {3711 $('#bogus_folders').on('input', function () {
@@ -3950,14 +3809,12 @@ $(document).ready(() => {
39503809
3951 $('#reduced_motion').on('input', function () {3810 $('#reduced_motion').on('input', function () {
3952 power_user.reduced_motion = !!$(this).prop('checked');3811 power_user.reduced_motion = !!$(this).prop('checked');
3953 localStorage.setItem(storage_keys.reduced_motion, String(power_user.reduced_motion));
3954 switchReducedMotion();3812 switchReducedMotion();
3955 saveSettingsDebounced();3813 saveSettingsDebounced();
3956 });3814 });
39573815
3958 $('#compact_input_area').on('input', function () {3816 $('#compact_input_area').on('input', function () {
3959 power_user.compact_input_area = !!$(this).prop('checked');3817 power_user.compact_input_area = !!$(this).prop('checked');
3960 localStorage.setItem(storage_keys.compact_input_area, String(power_user.compact_input_area));
3961 switchCompactInputArea();3818 switchCompactInputArea();
3962 saveSettingsDebounced();3819 saveSettingsDebounced();
3963 });3820 });
public/scripts/showdown-underscore.js+1 -1
@@ -8,7 +8,7 @@ export const markdownUnderscoreExt = () => {
88
9 return [{9 return [{
10 type: 'output',10 type: 'output',
11 regex: new RegExp('(<code>[\\s\\S]*?<\\/code>)|(?<!\\S)_(?!_)([^_\\n]+?)(?<!_)_(?!\\w)', 'g'),11 regex: new RegExp('(<code(?:\\s+[^>]*)?>[\\s\\S]*?<\\/code>)|\\b(?<!_)_(?!_)(.*?)(?<!_)_(?!_)\\b', 'g'),
12 replace: function(match, codeContent, italicContent) {12 replace: function(match, codeContent, italicContent) {
13 if (codeContent) {13 if (codeContent) {
14 // If it's inside <code> tags, return unchanged14 // If it's inside <code> tags, return unchanged
public/scripts/slash-commands.js+6 -5
@@ -2147,12 +2147,13 @@ async function generateRawCallback(args, value) {
2147 }2147 }
2148}2148}
21492149
2150/**
2151 * Callback for the /gen command
2152 * @param {object} args Named arguments
2153 * @param {string} value Unnamed argument
2154 * @returns {Promise<string>} The generated text
2155 */
2150async function generateCallback(args, value) {2156async function generateCallback(args, value) {
2151 if (!value) {
2152 console.warn('WARN: No argument provided for /gen command');
2153 return;
2154 }
2155
2156 // Prevent generate recursion2157 // Prevent generate recursion
2157 $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true }));2158 $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true }));
2158 const lock = isTrueBoolean(args?.lock);2159 const lock = isTrueBoolean(args?.lock);
public/scripts/tags.js+9 -1
@@ -445,7 +445,11 @@ export function getTagKeyForEntity(entityOrKey) {
445 }445 }
446446
447 // Next lets check if its a valid character or character id, so we can swith it to its tag447 // Next lets check if its a valid character or character id, so we can swith it to its tag
448 const character = characters.indexOf(x) >= 0 ? x : characters[x];448 let character;
449 if (!character && characters.indexOf(x) >= 0) character = x; // Check for char object
450 if (!character && !isNaN(parseInt(entityOrKey))) character = characters[x]; // check if its a char id
451 if (!character) character = characters.find(y => y.avatar === x); // check if its a char key
452
449 if (character) {453 if (character) {
450 x = character.avatar;454 x = character.avatar;
451 }455 }
@@ -722,7 +726,11 @@ async function importTags(character, { importSetting = null } = {}) {
722 const tagsToImport = tagNamesToImport.map(tag => getTag(tag, { createNew: true }));726 const tagsToImport = tagNamesToImport.map(tag => getTag(tag, { createNew: true }));
723 const added = addTagsToEntity(tagsToImport, character.avatar);727 const added = addTagsToEntity(tagsToImport, character.avatar);
724728
729 if (added) {
725 toastr.success(`Imported tags:<br />${tagsToImport.map(x => x.name).join(', ')}`, 'Importing Tags', { escapeHtml: false });730 toastr.success(`Imported tags:<br />${tagsToImport.map(x => x.name).join(', ')}`, 'Importing Tags', { escapeHtml: false });
731 } else {
732 toastr.error(`Couldn't import tags:<br />${tagsToImport.map(x => x.name).join(', ')}`, 'Importing Tags', { escapeHtml: false });
733 }
726734
727 return added;735 return added;
728}736}
public/scripts/templates/chatRename.html+6 -0
@@ -0,0 +1,6 @@
1<h3 data-i18n="chat_rename_1">Enter the new name for the chat:</h3>
2<small>
3 <span data-i18n="chat_rename_2">!!Using an existing filename will produce an error!!</span><br>
4 <span data-i18n="chat_rename_3">This will break the link between checkpoint chats.</span><br>
5 <span data-i18n="chat_rename_4">No need to add '.jsonl' at the end.</span><br>
6</small>
\ No newline at end of file6 \ No newline at end of file
public/scripts/templates/newChatConfirm.html+4 -0
@@ -0,0 +1,4 @@
1<label for="del_chat_checkbox" class="checkbox_label justifyCenter" data-i18n="[title]If necessary, you can later restore this chat file from the /backups folder" title="If necessary, you can later restore this chat file from the /backups folder">
2 <input type="checkbox" id="del_chat_checkbox" />
3 <small data-i18n="Also delete the current chat file">Also delete the current chat file</small>
4</label>
\ No newline at end of file4 \ No newline at end of file
public/scripts/templates/proxyConnectionWarning.html+1 -0
@@ -0,0 +1 @@
1<span data-i18n="Are you sure you want to connect to the following proxy URL?">Are you sure you want to connect to the following proxy URL?</span><var>{{proxyURL}}</var>
\ No newline at end of file1 \ No newline at end of file
public/scripts/tokenizers.js+27 -23
@@ -27,6 +27,7 @@ export const tokenizers = {
27 CLAUDE: 11,27 CLAUDE: 11,
28 LLAMA3: 12,28 LLAMA3: 12,
29 GEMMA: 13,29 GEMMA: 13,
30 JAMBA: 14,
30 BEST_MATCH: 99,31 BEST_MATCH: 99,
31};32};
3233
@@ -36,6 +37,7 @@ export const SENTENCEPIECE_TOKENIZERS = [
36 tokenizers.YI,37 tokenizers.YI,
37 tokenizers.LLAMA3,38 tokenizers.LLAMA3,
38 tokenizers.GEMMA,39 tokenizers.GEMMA,
40 tokenizers.JAMBA,
39 // uncomment when NovelAI releases Kayra and Clio weights, lol41 // uncomment when NovelAI releases Kayra and Clio weights, lol
40 //tokenizers.NERD,42 //tokenizers.NERD,
41 //tokenizers.NERD2,43 //tokenizers.NERD2,
@@ -98,6 +100,11 @@ const TOKENIZER_URLS = {
98 decode: '/api/tokenizers/gemma/decode',100 decode: '/api/tokenizers/gemma/decode',
99 count: '/api/tokenizers/gemma/encode',101 count: '/api/tokenizers/gemma/encode',
100 },102 },
103 [tokenizers.JAMBA]: {
104 encode: '/api/tokenizers/jamba/encode',
105 decode: '/api/tokenizers/jamba/decode',
106 count: '/api/tokenizers/jamba/encode',
107 },
101 [tokenizers.API_TEXTGENERATIONWEBUI]: {108 [tokenizers.API_TEXTGENERATIONWEBUI]: {
102 encode: '/api/tokenizers/remote/textgenerationwebui/encode',109 encode: '/api/tokenizers/remote/textgenerationwebui/encode',
103 count: '/api/tokenizers/remote/textgenerationwebui/encode',110 count: '/api/tokenizers/remote/textgenerationwebui/encode',
@@ -164,7 +171,7 @@ export function getAvailableTokenizers() {
164 tokenizerId: Number(tokenizerOption.value),171 tokenizerId: Number(tokenizerOption.value),
165 tokenizerKey: Object.entries(tokenizers).find(([_, value]) => value === Number(tokenizerOption.value))[0].toLocaleLowerCase(),172 tokenizerKey: Object.entries(tokenizers).find(([_, value]) => value === Number(tokenizerOption.value))[0].toLocaleLowerCase(),
166 tokenizerName: tokenizerOption.text,173 tokenizerName: tokenizerOption.text,
167 }))174 }));
168}175}
169176
170/**177/**
@@ -280,6 +287,12 @@ export function getTokenizerBestMatch(forApi) {
280 if (model.includes('gemma')) {287 if (model.includes('gemma')) {
281 return tokenizers.GEMMA;288 return tokenizers.GEMMA;
282 }289 }
290 if (model.includes('yi')) {
291 return tokenizers.YI;
292 }
293 if (model.includes('jamba')) {
294 return tokenizers.JAMBA;
295 }
283 }296 }
284297
285 return tokenizers.LLAMA;298 return tokenizers.LLAMA;
@@ -497,6 +510,7 @@ export function getTokenizerModel() {
497 const mistralTokenizer = 'mistral';510 const mistralTokenizer = 'mistral';
498 const yiTokenizer = 'yi';511 const yiTokenizer = 'yi';
499 const gemmaTokenizer = 'gemma';512 const gemmaTokenizer = 'gemma';
513 const jambaTokenizer = 'jamba';
500514
501 // Assuming no one would use it for different models.. right?515 // Assuming no one would use it for different models.. right?
502 if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) {516 if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) {
@@ -562,12 +576,19 @@ export function getTokenizerModel() {
562 else if (oai_settings.openrouter_model.includes('GPT-NeoXT')) {576 else if (oai_settings.openrouter_model.includes('GPT-NeoXT')) {
563 return gpt2Tokenizer;577 return gpt2Tokenizer;
564 }578 }
579 else if (oai_settings.openrouter_model.includes('jamba')) {
580 return jambaTokenizer;
581 }
565 }582 }
566583
567 if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) {584 if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) {
568 return gemmaTokenizer;585 return gemmaTokenizer;
569 }586 }
570587
588 if (oai_settings.chat_completion_source == chat_completion_sources.AI21) {
589 return jambaTokenizer;
590 }
591
571 if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {592 if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
572 return claudeTokenizer;593 return claudeTokenizer;
573 }594 }
@@ -626,16 +647,7 @@ export function getTokenizerModel() {
626 * @deprecated Use countTokensOpenAIAsync instead.647 * @deprecated Use countTokensOpenAIAsync instead.
627 */648 */
628export function countTokensOpenAI(messages, full = false) {649export function countTokensOpenAI(messages, full = false) {
629 const shouldTokenizeAI21 = oai_settings.chat_completion_source === chat_completion_sources.AI21 && oai_settings.use_ai21_tokenizer;650 const tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
630 const shouldTokenizeGoogle = oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE && oai_settings.use_google_tokenizer;
631 let tokenizerEndpoint = '';
632 if (shouldTokenizeAI21) {
633 tokenizerEndpoint = '/api/tokenizers/ai21/count';
634 } else if (shouldTokenizeGoogle) {
635 tokenizerEndpoint = `/api/tokenizers/google/count?model=${getTokenizerModel()}&reverse_proxy=${oai_settings.reverse_proxy}&proxy_password=${oai_settings.proxy_password}`;
636 } else {
637 tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
638 }
639 const cacheObject = getTokenCacheObject();651 const cacheObject = getTokenCacheObject();
640652
641 if (!Array.isArray(messages)) {653 if (!Array.isArray(messages)) {
@@ -647,7 +659,7 @@ export function countTokensOpenAI(messages, full = false) {
647 for (const message of messages) {659 for (const message of messages) {
648 const model = getTokenizerModel();660 const model = getTokenizerModel();
649661
650 if (model === 'claude' || shouldTokenizeAI21 || shouldTokenizeGoogle) {662 if (model === 'claude') {
651 full = true;663 full = true;
652 }664 }
653665
@@ -687,16 +699,7 @@ export function countTokensOpenAI(messages, full = false) {
687 * @returns {Promise<number>} Token count.699 * @returns {Promise<number>} Token count.
688 */700 */
689export async function countTokensOpenAIAsync(messages, full = false) {701export async function countTokensOpenAIAsync(messages, full = false) {
690 const shouldTokenizeAI21 = oai_settings.chat_completion_source === chat_completion_sources.AI21 && oai_settings.use_ai21_tokenizer;702 const tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
691 const shouldTokenizeGoogle = oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE && oai_settings.use_google_tokenizer;
692 let tokenizerEndpoint = '';
693 if (shouldTokenizeAI21) {
694 tokenizerEndpoint = '/api/tokenizers/ai21/count';
695 } else if (shouldTokenizeGoogle) {
696 tokenizerEndpoint = `/api/tokenizers/google/count?model=${getTokenizerModel()}`;
697 } else {
698 tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
699 }
700 const cacheObject = getTokenCacheObject();703 const cacheObject = getTokenCacheObject();
701704
702 if (!Array.isArray(messages)) {705 if (!Array.isArray(messages)) {
@@ -708,7 +711,7 @@ export async function countTokensOpenAIAsync(messages, full = false) {
708 for (const message of messages) {711 for (const message of messages) {
709 const model = getTokenizerModel();712 const model = getTokenizerModel();
710713
711 if (model === 'claude' || shouldTokenizeAI21 || shouldTokenizeGoogle) {714 if (model === 'claude') {
712 full = true;715 full = true;
713 }716 }
714717
@@ -838,6 +841,7 @@ function getTextgenAPITokenizationParams(str) {
838 url: getTextGenServer(),841 url: getTextGenServer(),
839 legacy_api: textgen_settings.legacy_api && (textgen_settings.type === OOBA || textgen_settings.type === APHRODITE),842 legacy_api: textgen_settings.legacy_api && (textgen_settings.type === OOBA || textgen_settings.type === APHRODITE),
840 vllm_model: textgen_settings.vllm_model,843 vllm_model: textgen_settings.vllm_model,
844 aphrodite_model: textgen_settings.aphrodite_model,
841 };845 };
842}846}
843847
public/scripts/world-info.js+30 -2
@@ -73,6 +73,7 @@ export let world_info_match_whole_words = false;
73export let world_info_use_group_scoring = false;73export let world_info_use_group_scoring = false;
74export let world_info_character_strategy = world_info_insertion_strategy.character_first;74export let world_info_character_strategy = world_info_insertion_strategy.character_first;
75export let world_info_budget_cap = 0;75export let world_info_budget_cap = 0;
76export let world_info_max_recursion_steps = 0;
76const saveWorldDebounced = debounce(async (name, data) => await _save(name, data), debounce_timeout.relaxed);77const saveWorldDebounced = debounce(async (name, data) => await _save(name, data), debounce_timeout.relaxed);
77const saveSettingsDebounced = debounce(() => {78const saveSettingsDebounced = debounce(() => {
78 Object.assign(world_info, { globalSelect: selected_world_info });79 Object.assign(world_info, { globalSelect: selected_world_info });
@@ -710,6 +711,7 @@ export function getWorldInfoSettings() {
710 world_info_character_strategy,711 world_info_character_strategy,
711 world_info_budget_cap,712 world_info_budget_cap,
712 world_info_use_group_scoring,713 world_info_use_group_scoring,
714 world_info_max_recursion_steps,
713 };715 };
714}716}
715717
@@ -796,6 +798,8 @@ export function setWorldInfoSettings(settings, data) {
796 world_info_budget_cap = Number(settings.world_info_budget_cap);798 world_info_budget_cap = Number(settings.world_info_budget_cap);
797 if (settings.world_info_use_group_scoring !== undefined)799 if (settings.world_info_use_group_scoring !== undefined)
798 world_info_use_group_scoring = Boolean(settings.world_info_use_group_scoring);800 world_info_use_group_scoring = Boolean(settings.world_info_use_group_scoring);
801 if (settings.world_info_max_recursion_steps !== undefined)
802 world_info_max_recursion_steps = Number(settings.world_info_max_recursion_steps);
799803
800 // Migrate old settings804 // Migrate old settings
801 if (world_info_budget > 100) {805 if (world_info_budget > 100) {
@@ -844,6 +848,9 @@ export function setWorldInfoSettings(settings, data) {
844 $('#world_info_budget_cap').val(world_info_budget_cap);848 $('#world_info_budget_cap').val(world_info_budget_cap);
845 $('#world_info_budget_cap_counter').val(world_info_budget_cap);849 $('#world_info_budget_cap_counter').val(world_info_budget_cap);
846850
851 $('#world_info_max_recursion_steps').val(world_info_max_recursion_steps);
852 $('#world_info_max_recursion_steps_counter').val(world_info_max_recursion_steps);
853
847 world_names = data.world_names?.length ? data.world_names : [];854 world_names = data.world_names?.length ? data.world_names : [];
848855
849 // Add to existing selected WI if it exists856 // Add to existing selected WI if it exists
@@ -2459,7 +2466,7 @@ async function getWorldEntry(name, data, entry) {
2459 if (!isMobile()) {2466 if (!isMobile()) {
2460 $(characterFilter).select2({2467 $(characterFilter).select2({
2461 width: '100%',2468 width: '100%',
2462 placeholder: 'All characters will pull from this entry.',2469 placeholder: 'Tie this entry to specific characters or characters with specific tags',
2463 allowClear: true,2470 allowClear: true,
2464 closeOnSelect: false,2471 closeOnSelect: false,
2465 });2472 });
@@ -3723,6 +3730,12 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
3723 console.debug(`[WI] --- SEARCHING ENTRIES (on ${sortedEntries.length} entries) ---`);3730 console.debug(`[WI] --- SEARCHING ENTRIES (on ${sortedEntries.length} entries) ---`);
37243731
3725 while (scanState) {3732 while (scanState) {
3733 //if world_info_max_recursion_steps is non-zero min activations are disabled, and vice versa
3734 if (world_info_max_recursion_steps && world_info_max_recursion_steps <= count) {
3735 console.debug('[WI] Search stopped by reaching max recursion steps', world_info_max_recursion_steps);
3736 break;
3737 }
3738
3726 // Track how many times the loop has run. May be useful for debugging.3739 // Track how many times the loop has run. May be useful for debugging.
3727 count++;3740 count++;
37283741
@@ -4762,8 +4775,13 @@ jQuery(() => {
47624775
4763 $('#world_info_min_activations').on('input', function () {4776 $('#world_info_min_activations').on('input', function () {
4764 world_info_min_activations = Number($(this).val());4777 world_info_min_activations = Number($(this).val());
4765 $('#world_info_min_activations_counter').val($(this).val());4778 $('#world_info_min_activations_counter').val(world_info_min_activations);
4779
4780 if (world_info_min_activations !== 0) {
4781 $('#world_info_max_recursion_steps').val(0).trigger('input');
4782 } else {
4766 saveSettings();4783 saveSettings();
4784 }
4767 });4785 });
47684786
4769 $('#world_info_min_activations_depth_max').on('input', function () {4787 $('#world_info_min_activations_depth_max').on('input', function () {
@@ -4819,6 +4837,16 @@ jQuery(() => {
4819 saveSettings();4837 saveSettings();
4820 });4838 });
48214839
4840 $('#world_info_max_recursion_steps').on('input', function () {
4841 world_info_max_recursion_steps = Number($(this).val());
4842 $('#world_info_max_recursion_steps_counter').val(world_info_max_recursion_steps);
4843 if (world_info_max_recursion_steps !== 0) {
4844 $('#world_info_min_activations').val(0).trigger('input');
4845 } else {
4846 saveSettings();
4847 }
4848 });
4849
4822 $('#world_button').on('click', async function (event) {4850 $('#world_button').on('click', async function (event) {
4823 const chid = $('#set_character_world').data('chid');4851 const chid = $('#set_character_world').data('chid');
48244852
server.js+2 -2
@@ -817,7 +817,7 @@ function createHttpsServer(url) {
817 }, app);817 }, app);
818 server.on('error', reject);818 server.on('error', reject);
819 server.on('listening', resolve);819 server.on('listening', resolve);
820 server.listen(url.port, url.hostname);820 server.listen(url.port || 443, url.hostname);
821 });821 });
822}822}
823823
@@ -832,7 +832,7 @@ function createHttpServer(url) {
832 const server = http.createServer(app);832 const server = http.createServer(app);
833 server.on('error', reject);833 server.on('error', reject);
834 server.on('listening', resolve);834 server.on('listening', resolve);
835 server.listen(url.port, url.hostname);835 server.listen(url.port || 80, url.hostname);
836 });836 });
837}837}
838838
src/cohere-stream.js+126 -0
@@ -0,0 +1,126 @@
1const DATA_PREFIX = 'data:';
2
3/**
4 * Borrowed from Cohere SDK (MIT License)
5 * https://github.com/cohere-ai/cohere-typescript/blob/main/src/core/streaming-fetcher/Stream.ts
6 * Copyright (c) 2021 Cohere
7 */
8class CohereStream {
9 /** @type {ReadableStream} */
10 stream;
11 /** @type {string} */
12 prefix;
13 /** @type {string} */
14 messageTerminator;
15 /** @type {string|undefined} */
16 streamTerminator;
17 /** @type {AbortController} */
18 controller = new AbortController();
19
20 constructor({ stream, eventShape }) {
21 this.stream = stream;
22 if (eventShape.type === 'sse') {
23 this.prefix = DATA_PREFIX;
24 this.messageTerminator = '\n';
25 this.streamTerminator = eventShape.streamTerminator;
26 } else {
27 this.messageTerminator = eventShape.messageTerminator;
28 }
29 }
30
31 async *iterMessages() {
32 const stream = readableStreamAsyncIterable(this.stream);
33 let buf = '';
34 let prefixSeen = false;
35 let parsedAnyMessages = false;
36 for await (const chunk of stream) {
37 buf += this.decodeChunk(chunk);
38
39 let terminatorIndex;
40 // Parse the chunk into as many messages as possible
41 while ((terminatorIndex = buf.indexOf(this.messageTerminator)) >= 0) {
42 // Extract the line from the buffer
43 let line = buf.slice(0, terminatorIndex + 1);
44 buf = buf.slice(terminatorIndex + 1);
45
46 // Skip empty lines
47 if (line.length === 0) {
48 continue;
49 }
50
51 // Skip the chunk until the prefix is found
52 if (!prefixSeen && this.prefix != null) {
53 const prefixIndex = line.indexOf(this.prefix);
54 if (prefixIndex === -1) {
55 continue;
56 }
57 prefixSeen = true;
58 line = line.slice(prefixIndex + this.prefix.length);
59 }
60
61 // If the stream terminator is present, return
62 if (this.streamTerminator != null && line.includes(this.streamTerminator)) {
63 return;
64 }
65
66 // Otherwise, yield message from the prefix to the terminator
67 const message = JSON.parse(line);
68 yield message;
69 prefixSeen = false;
70 parsedAnyMessages = true;
71 }
72 }
73
74 if (!parsedAnyMessages && buf.length > 0) {
75 try {
76 yield JSON.parse(buf);
77 } catch (e) {
78 console.error('Error parsing message:', e);
79 }
80 }
81 }
82
83 async *[Symbol.asyncIterator]() {
84 for await (const message of this.iterMessages()) {
85 yield message;
86 }
87 }
88
89 decodeChunk(chunk) {
90 const decoder = new TextDecoder('utf8');
91 return decoder.decode(chunk);
92 }
93}
94
95function readableStreamAsyncIterable(stream) {
96 if (stream[Symbol.asyncIterator]) {
97 return stream;
98 }
99
100 const reader = stream.getReader();
101 return {
102 async next() {
103 try {
104 const result = await reader.read();
105 if (result?.done) {
106 reader.releaseLock();
107 } // release lock when stream becomes closed
108 return result;
109 } catch (e) {
110 reader.releaseLock(); // release lock when stream becomes errored
111 throw e;
112 }
113 },
114 async return() {
115 const cancelPromise = reader.cancel();
116 reader.releaseLock();
117 await cancelPromise;
118 return { done: true, value: undefined };
119 },
120 [Symbol.asyncIterator]() {
121 return this;
122 },
123 };
124}
125
126module.exports = CohereStream;
src/endpoints/backends/chat-completions.js+63 -93
@@ -1,11 +1,11 @@
1const express = require('express');1const express = require('express');
2const fetch = require('node-fetch').default;2const fetch = require('node-fetch').default;
3const Readable = require('stream').Readable;
43
5const { jsonParser } = require('../../express-common');4const { jsonParser } = require('../../express-common');
6const { CHAT_COMPLETION_SOURCES, GEMINI_SAFETY, BISON_SAFETY, OPENROUTER_HEADERS } = require('../../constants');5const { CHAT_COMPLETION_SOURCES, GEMINI_SAFETY, BISON_SAFETY, OPENROUTER_HEADERS } = require('../../constants');
7const { forwardFetchResponse, getConfigValue, tryParse, uuidv4, mergeObjectWithYaml, excludeKeysByYaml, color } = require('../../util');6const { forwardFetchResponse, getConfigValue, tryParse, uuidv4, mergeObjectWithYaml, excludeKeysByYaml, color } = require('../../util');
8const { convertClaudeMessages, convertGooglePrompt, convertTextCompletionPrompt, convertCohereMessages, convertMistralMessages, convertCohereTools } = require('../../prompt-converters');7const { convertClaudeMessages, convertGooglePrompt, convertTextCompletionPrompt, convertCohereMessages, convertMistralMessages, convertCohereTools, convertAI21Messages } = require('../../prompt-converters');
8const CohereStream = require('../../cohere-stream');
99
10const { readSecret, SECRET_KEYS } = require('../secrets');10const { readSecret, SECRET_KEYS } = require('../secrets');
11const { getTokenizerModel, getSentencepiceTokenizer, getTiktokenTokenizer, sentencepieceTokenizers, TEXT_COMPLETION_MODELS } = require('../tokenizers');11const { getTokenizerModel, getSentencepiceTokenizer, getTiktokenTokenizer, sentencepieceTokenizers, TEXT_COMPLETION_MODELS } = require('../tokenizers');
@@ -19,6 +19,7 @@ const API_GROQ = 'https://api.groq.com/openai/v1';
19const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';19const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';
20const API_01AI = 'https://api.01.ai/v1';20const API_01AI = 'https://api.01.ai/v1';
21const API_BLOCKENTROPY = 'https://api.blockentropy.ai/v1';21const API_BLOCKENTROPY = 'https://api.blockentropy.ai/v1';
22const API_AI21 = 'https://api.ai21.com/studio/v1';
2223
23/**24/**
24 * Applies a post-processing step to the generated messages.25 * Applies a post-processing step to the generated messages.
@@ -40,52 +41,30 @@ function postProcessPrompt(messages, type, charName, userName) {
40/**41/**
41 * Ollama strikes back. Special boy #2's steaming routine.42 * Ollama strikes back. Special boy #2's steaming routine.
42 * Wrap this abomination into proper SSE stream, again.43 * Wrap this abomination into proper SSE stream, again.
43 * @param {import('node-fetch').Response} jsonStream JSON stream44 * @param {Response} jsonStream JSON stream
44 * @param {import('express').Request} request Express request45 * @param {import('express').Request} request Express request
45 * @param {import('express').Response} response Express response46 * @param {import('express').Response} response Express response
46 * @returns {Promise<any>} Nothing valuable47 * @returns {Promise<any>} Nothing valuable
47 */48 */
48async function parseCohereStream(jsonStream, request, response) {49async function parseCohereStream(jsonStream, request, response) {
49 try {50 try {
50 let partialData = '';51 const stream = new CohereStream({ stream: jsonStream.body, eventShape: { type: 'json', messageTerminator: '\n' } });
51 jsonStream.body.on('data', (data) => {52
52 const chunk = data.toString();53 for await (const json of stream.iterMessages()) {
53 partialData += chunk;
54 while (true) {
55 let json;
56 try {
57 json = JSON.parse(partialData);
58 } catch (e) {
59 break;
60 }
61 if (json.message) {54 if (json.message) {
62 const message = json.message || 'Unknown error';55 const message = json.message || 'Unknown error';
63 const chunk = { error: { message: message } };56 const chunk = { error: { message: message } };
64 response.write(`data: ${JSON.stringify(chunk)}\n\n`);57 response.write(`data: ${JSON.stringify(chunk)}\n\n`);
65 partialData = '';
66 break;
67 } else if (json.event_type === 'text-generation') {58 } else if (json.event_type === 'text-generation') {
68 const text = json.text || '';59 const text = json.text || '';
69 const chunk = { choices: [{ text }] };60 const chunk = { choices: [{ text }] };
70 response.write(`data: ${JSON.stringify(chunk)}\n\n`);61 response.write(`data: ${JSON.stringify(chunk)}\n\n`);
71 partialData = '';
72 } else {
73 partialData = '';
74 break;
75 }62 }
76 }63 }
77 });
7864
79 request.socket.on('close', function () {
80 if (jsonStream.body instanceof Readable) jsonStream.body.destroy();
81 response.end();
82 });
83
84 jsonStream.body.on('end', () => {
85 console.log('Streaming request finished');65 console.log('Streaming request finished');
86 response.write('data: [DONE]\n\n');66 response.write('data: [DONE]\n\n');
87 response.end();67 response.end();
88 });
89 } catch (error) {68 } catch (error) {
90 console.log('Error forwarding streaming response:', error);69 console.log('Error forwarding streaming response:', error);
91 if (!response.headersSent) {70 if (!response.headersSent) {
@@ -233,7 +212,7 @@ async function sendScaleRequest(request, response) {
233212
234 if (!generateResponse.ok) {213 if (!generateResponse.ok) {
235 console.log(`Scale API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);214 console.log(`Scale API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
236 return response.status(generateResponse.status).send({ error: true });215 return response.status(500).send({ error: true });
237 }216 }
238217
239 const generateResponseJson = await generateResponse.json();218 const generateResponseJson = await generateResponse.json();
@@ -259,7 +238,7 @@ async function sendMakerSuiteRequest(request, response) {
259 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);238 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);
260239
261 if (!request.body.reverse_proxy && !apiKey) {240 if (!request.body.reverse_proxy && !apiKey) {
262 console.log('MakerSuite API key is missing.');241 console.log('Google AI Studio API key is missing.');
263 return response.status(400).send({ error: true });242 return response.status(400).send({ error: true });
264 }243 }
265244
@@ -326,7 +305,7 @@ async function sendMakerSuiteRequest(request, response) {
326 }305 }
327306
328 const body = isGemini ? getGeminiBody() : getBisonBody();307 const body = isGemini ? getGeminiBody() : getBisonBody();
329 console.log('MakerSuite request:', body);308 console.log('Google AI Studio request:', body);
330309
331 try {310 try {
332 const controller = new AbortController();311 const controller = new AbortController();
@@ -362,7 +341,7 @@ async function sendMakerSuiteRequest(request, response) {
362 }341 }
363 } else {342 } else {
364 if (!generateResponse.ok) {343 if (!generateResponse.ok) {
365 console.log(`MakerSuite API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);344 console.log(`Google AI Studio API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
366 return response.status(generateResponse.status).send({ error: true });345 return response.status(generateResponse.status).send({ error: true });
367 }346 }
368347
@@ -370,7 +349,7 @@ async function sendMakerSuiteRequest(request, response) {
370349
371 const candidates = generateResponseJson?.candidates;350 const candidates = generateResponseJson?.candidates;
372 if (!candidates || candidates.length === 0) {351 if (!candidates || candidates.length === 0) {
373 let message = 'MakerSuite API returned no candidate';352 let message = 'Google AI Studio API returned no candidate';
374 console.log(message, generateResponseJson);353 console.log(message, generateResponseJson);
375 if (generateResponseJson?.promptFeedback?.blockReason) {354 if (generateResponseJson?.promptFeedback?.blockReason) {
376 message += `\nPrompt was blocked due to : ${generateResponseJson.promptFeedback.blockReason}`;355 message += `\nPrompt was blocked due to : ${generateResponseJson.promptFeedback.blockReason}`;
@@ -381,19 +360,19 @@ async function sendMakerSuiteRequest(request, response) {
381 const responseContent = candidates[0].content ?? candidates[0].output;360 const responseContent = candidates[0].content ?? candidates[0].output;
382 const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.[0]?.text;361 const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.[0]?.text;
383 if (!responseText) {362 if (!responseText) {
384 let message = 'MakerSuite Candidate text empty';363 let message = 'Google AI Studio Candidate text empty';
385 console.log(message, generateResponseJson);364 console.log(message, generateResponseJson);
386 return response.send({ error: { message } });365 return response.send({ error: { message } });
387 }366 }
388367
389 console.log('MakerSuite response:', responseText);368 console.log('Google AI Studio response:', responseText);
390369
391 // Wrap it back to OAI format370 // Wrap it back to OAI format
392 const reply = { choices: [{ 'message': { 'content': responseText } }] };371 const reply = { choices: [{ 'message': { 'content': responseText } }] };
393 return response.send(reply);372 return response.send(reply);
394 }373 }
395 } catch (error) {374 } catch (error) {
396 console.log('Error communicating with MakerSuite API: ', error);375 console.log('Error communicating with Google AI Studio API: ', error);
397 if (!response.headersSent) {376 if (!response.headersSent) {
398 return response.status(500).send({ error: true });377 return response.status(500).send({ error: true });
399 }378 }
@@ -413,6 +392,16 @@ async function sendAI21Request(request, response) {
413 request.socket.on('close', function () {392 request.socket.on('close', function () {
414 controller.abort();393 controller.abort();
415 });394 });
395 const convertedPrompt = convertAI21Messages(request.body.messages, request.body.char_name, request.body.user_name);
396 const body = {
397 messages: convertedPrompt,
398 model: request.body.model,
399 max_tokens: request.body.max_tokens,
400 temperature: request.body.temperature,
401 top_p: request.body.top_p,
402 stop: request.body.stop,
403 stream: request.body.stream,
404 };
416 const options = {405 const options = {
417 method: 'POST',406 method: 'POST',
418 headers: {407 headers: {
@@ -420,59 +409,35 @@ async function sendAI21Request(request, response) {
420 'content-type': 'application/json',409 'content-type': 'application/json',
421 Authorization: `Bearer ${readSecret(request.user.directories, SECRET_KEYS.AI21)}`,410 Authorization: `Bearer ${readSecret(request.user.directories, SECRET_KEYS.AI21)}`,
422 },411 },
423 body: JSON.stringify({412 body: JSON.stringify(body),
424 numResults: 1,
425 maxTokens: request.body.max_tokens,
426 minTokens: 0,
427 temperature: request.body.temperature,
428 topP: request.body.top_p,
429 stopSequences: request.body.stop_tokens,
430 topKReturn: request.body.top_k,
431 frequencyPenalty: {
432 scale: request.body.frequency_penalty * 100,
433 applyToWhitespaces: false,
434 applyToPunctuations: false,
435 applyToNumbers: false,
436 applyToStopwords: false,
437 applyToEmojis: false,
438 },
439 presencePenalty: {
440 scale: request.body.presence_penalty,
441 applyToWhitespaces: false,
442 applyToPunctuations: false,
443 applyToNumbers: false,
444 applyToStopwords: false,
445 applyToEmojis: false,
446 },
447 countPenalty: {
448 scale: request.body.count_pen,
449 applyToWhitespaces: false,
450 applyToPunctuations: false,
451 applyToNumbers: false,
452 applyToStopwords: false,
453 applyToEmojis: false,
454 },
455 prompt: request.body.messages,
456 }),
457 signal: controller.signal,413 signal: controller.signal,
458 };414 };
459415
460 fetch(`https://api.ai21.com/studio/v1/${request.body.model}/complete`, options)416 console.log('AI21 request:', body);
461 .then(r => r.json())417
462 .then(r => {418 try {
463 if (r.completions === undefined) {419 const generateResponse = await fetch(API_AI21 + '/chat/completions', options);
464 console.log(r);420 if (request.body.stream) {
421 forwardFetchResponse(generateResponse, response);
465 } else {422 } else {
466 console.log(r.completions[0].data.text);423 if (!generateResponse.ok) {
424 const errorText = await generateResponse.text();
425 console.log(`AI21 API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
426 const errorJson = tryParse(errorText) ?? { error: true };
427 return response.status(500).send(errorJson);
428 }
429 const generateResponseJson = await generateResponse.json();
430 console.log('AI21 response:', generateResponseJson);
431 return response.send(generateResponseJson);
432 }
433 } catch (error) {
434 console.log('Error communicating with AI21 API: ', error);
435 if (!response.headersSent) {
436 response.send({ error: true });
437 } else {
438 response.end();
439 }
467 }440 }
468 const reply = { choices: [{ 'message': { 'content': r.completions?.[0]?.data?.text } }] };
469 return response.send(reply);
470 })
471 .catch(err => {
472 console.error(err);
473 return response.send({ error: true });
474 });
475
476}441}
477442
478/**443/**
@@ -531,10 +496,10 @@ async function sendMistralAIRequest(request, response) {
531 forwardFetchResponse(generateResponse, response);496 forwardFetchResponse(generateResponse, response);
532 } else {497 } else {
533 if (!generateResponse.ok) {498 if (!generateResponse.ok) {
534 console.log(`MistralAI API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);499 const errorText = await generateResponse.text();
535 // a 401 unauthorized response breaks the frontend auth, so return a 500 instead. prob a better way of dealing with this.500 console.log(`MistralAI API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
536 // 401s are already handled by the streaming processor and dont pop up an error toast, that should probably be fixed too.501 const errorJson = tryParse(errorText) ?? { error: true };
537 return response.status(generateResponse.status === 401 ? 500 : generateResponse.status).send({ error: true });502 return response.status(500).send(errorJson);
538 }503 }
539 const generateResponseJson = await generateResponse.json();504 const generateResponseJson = await generateResponse.json();
540 console.log('MistralAI response:', generateResponseJson);505 console.log('MistralAI response:', generateResponseJson);
@@ -607,6 +572,11 @@ async function sendCohereRequest(request, response) {
607 search_queries_only: false,572 search_queries_only: false,
608 };573 };
609574
575 const canDoSafetyMode = String(request.body.model).endsWith('08-2024');
576 if (canDoSafetyMode) {
577 requestBody.safety_mode = 'NONE';
578 }
579
610 console.log('Cohere request:', requestBody);580 console.log('Cohere request:', requestBody);
611581
612 const config = {582 const config = {
@@ -623,15 +593,15 @@ async function sendCohereRequest(request, response) {
623 const apiUrl = API_COHERE + '/chat';593 const apiUrl = API_COHERE + '/chat';
624594
625 if (request.body.stream) {595 if (request.body.stream) {
626 const stream = await fetch(apiUrl, config);596 const stream = await global.fetch(apiUrl, config);
627 parseCohereStream(stream, request, response);597 parseCohereStream(stream, request, response);
628 } else {598 } else {
629 const generateResponse = await fetch(apiUrl, config);599 const generateResponse = await fetch(apiUrl, config);
630 if (!generateResponse.ok) {600 if (!generateResponse.ok) {
631 console.log(`Cohere API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);601 const errorText = await generateResponse.text();
632 // a 401 unauthorized response breaks the frontend auth, so return a 500 instead. prob a better way of dealing with this.602 console.log(`Cohere API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
633 // 401s are already handled by the streaming processor and dont pop up an error toast, that should probably be fixed too.603 const errorJson = tryParse(errorText) ?? { error: true };
634 return response.status(generateResponse.status === 401 ? 500 : generateResponse.status).send({ error: true });604 return response.status(500).send(errorJson);
635 }605 }
636 const generateResponseJson = await generateResponse.json();606 const generateResponseJson = await generateResponse.json();
637 console.log('Cohere response:', generateResponseJson);607 console.log('Cohere response:', generateResponseJson);
src/endpoints/google.js+1 -1
@@ -44,7 +44,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
4444
45 if (!result.ok) {45 if (!result.ok) {
46 const error = await result.json();46 const error = await result.json();
47 console.log(`MakerSuite API returned error: ${result.status} ${result.statusText}`, error);47 console.log(`Google AI Studio API returned error: ${result.status} ${result.statusText}`, error);
48 return response.status(result.status).send({ error: true });48 return response.status(result.status).send({ error: true });
49 }49 }
5050
src/endpoints/novelai.js+5 -5
@@ -6,6 +6,7 @@ const { readAllChunks, extractFileFromZipBuffer, forwardFetchResponse } = requir
6const { jsonParser } = require('../express-common');6const { jsonParser } = require('../express-common');
77
8const API_NOVELAI = 'https://api.novelai.net';8const API_NOVELAI = 'https://api.novelai.net';
9const TEXT_NOVELAI = 'https://text.novelai.net';
9const IMAGE_NOVELAI = 'https://image.novelai.net';10const IMAGE_NOVELAI = 'https://image.novelai.net';
1011
11// Ban bracket generation, plus defaults12// Ban bracket generation, plus defaults
@@ -155,7 +156,7 @@ router.post('/generate', jsonParser, async function (req, res) {
155 'repetition_penalty_slope': req.body.repetition_penalty_slope,156 'repetition_penalty_slope': req.body.repetition_penalty_slope,
156 'repetition_penalty_frequency': req.body.repetition_penalty_frequency,157 'repetition_penalty_frequency': req.body.repetition_penalty_frequency,
157 'repetition_penalty_presence': req.body.repetition_penalty_presence,158 'repetition_penalty_presence': req.body.repetition_penalty_presence,
158 'repetition_penalty_whitelist': isNewModel ? repPenaltyAllowList : null,159 'repetition_penalty_whitelist': isNewModel ? repPenaltyAllowList.flat() : null,
159 'top_a': req.body.top_a,160 'top_a': req.body.top_a,
160 'top_p': req.body.top_p,161 'top_p': req.body.top_p,
161 'top_k': req.body.top_k,162 'top_k': req.body.top_k,
@@ -178,9 +179,7 @@ router.post('/generate', jsonParser, async function (req, res) {
178 };179 };
179180
180 // Tells the model to stop generation at '>'181 // Tells the model to stop generation at '>'
181 if ('theme_textadventure' === req.body.prefix &&182 if ('theme_textadventure' === req.body.prefix && isNewModel) {
182 (true === req.body.model.includes('clio') ||
183 true === req.body.model.includes('kayra'))) {
184 data.parameters.eos_token_id = 49405;183 data.parameters.eos_token_id = 49405;
185 }184 }
186185
@@ -193,7 +192,8 @@ router.post('/generate', jsonParser, async function (req, res) {
193 };192 };
194193
195 try {194 try {
196 const url = req.body.streaming ? `${API_NOVELAI}/ai/generate-stream` : `${API_NOVELAI}/ai/generate`;195 const baseURL = req.body.model.includes('kayra') ? TEXT_NOVELAI : API_NOVELAI;
196 const url = req.body.streaming ? `${baseURL}/ai/generate-stream` : `${baseURL}/ai/generate`;
197 const response = await fetch(url, { method: 'POST', timeout: 0, ...args });197 const response = await fetch(url, { method: 'POST', timeout: 0, ...args });
198198
199 if (req.body.streaming) {199 if (req.body.streaming) {
src/endpoints/settings.js+2 -0
@@ -9,6 +9,7 @@ const { jsonParser } = require('../express-common');
9const { getAllUserHandles, getUserDirectories } = require('../users');9const { getAllUserHandles, getUserDirectories } = require('../users');
1010
11const ENABLE_EXTENSIONS = getConfigValue('enableExtensions', true);11const ENABLE_EXTENSIONS = getConfigValue('enableExtensions', true);
12const ENABLE_EXTENSIONS_AUTO_UPDATE = getConfigValue('enableExtensionsAutoUpdate', true);
12const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false);13const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false);
1314
14// 10 minutes15// 10 minutes
@@ -268,6 +269,7 @@ router.post('/get', jsonParser, (request, response) => {
268 instruct,269 instruct,
269 context,270 context,
270 enable_extensions: ENABLE_EXTENSIONS,271 enable_extensions: ENABLE_EXTENSIONS,
272 enable_extensions_auto_update: ENABLE_EXTENSIONS_AUTO_UPDATE,
271 enable_accounts: ENABLE_ACCOUNTS,273 enable_accounts: ENABLE_ACCOUNTS,
272 });274 });
273});275});
src/endpoints/stable-diffusion.js+41 -0
@@ -991,11 +991,52 @@ blockentropy.post('/generate', jsonParser, async (request, response) => {
991});991});
992992
993993
994const huggingface = express.Router();
995
996huggingface.post('/generate', jsonParser, async (request, response) => {
997 try {
998 const key = readSecret(request.user.directories, SECRET_KEYS.HUGGINGFACE);
999
1000 if (!key) {
1001 console.log('Hugging Face key not found.');
1002 return response.sendStatus(400);
1003 }
1004
1005 console.log('Hugging Face request:', request.body);
1006
1007 const result = await fetch(`https://api-inference.huggingface.co/models/${request.body.model}`, {
1008 method: 'POST',
1009 body: JSON.stringify({
1010 inputs: request.body.prompt,
1011 }),
1012 headers: {
1013 'Content-Type': 'application/json',
1014 'Authorization': `Bearer ${key}`,
1015 },
1016 });
1017
1018 if (!result.ok) {
1019 console.log('Hugging Face returned an error.');
1020 return response.sendStatus(500);
1021 }
1022
1023 const buffer = await result.buffer();
1024 return response.send({
1025 image: buffer.toString('base64'),
1026 });
1027 } catch (error) {
1028 console.log(error);
1029 return response.sendStatus(500);
1030 }
1031});
1032
1033
994router.use('/comfy', comfy);1034router.use('/comfy', comfy);
995router.use('/together', together);1035router.use('/together', together);
996router.use('/drawthings', drawthings);1036router.use('/drawthings', drawthings);
997router.use('/pollinations', pollinations);1037router.use('/pollinations', pollinations);
998router.use('/stability', stability);1038router.use('/stability', stability);
999router.use('/blockentropy', blockentropy);1039router.use('/blockentropy', blockentropy);
1040router.use('/huggingface', huggingface);
10001041
1001module.exports = { router };1042module.exports = { router };
src/endpoints/tokenizers.js+29 -48
@@ -144,6 +144,7 @@ const spp_nerd_v2 = new SentencePieceTokenizer('src/tokenizers/nerdstash_v2.mode
144const spp_mistral = new SentencePieceTokenizer('src/tokenizers/mistral.model');144const spp_mistral = new SentencePieceTokenizer('src/tokenizers/mistral.model');
145const spp_yi = new SentencePieceTokenizer('src/tokenizers/yi.model');145const spp_yi = new SentencePieceTokenizer('src/tokenizers/yi.model');
146const spp_gemma = new SentencePieceTokenizer('src/tokenizers/gemma.model');146const spp_gemma = new SentencePieceTokenizer('src/tokenizers/gemma.model');
147const spp_jamba = new SentencePieceTokenizer('src/tokenizers/jamba.model');
147const claude_tokenizer = new WebTokenizer('src/tokenizers/claude.json');148const claude_tokenizer = new WebTokenizer('src/tokenizers/claude.json');
148const llama3_tokenizer = new WebTokenizer('src/tokenizers/llama3.json');149const llama3_tokenizer = new WebTokenizer('src/tokenizers/llama3.json');
149150
@@ -154,6 +155,7 @@ const sentencepieceTokenizers = [
154 'mistral',155 'mistral',
155 'yi',156 'yi',
156 'gemma',157 'gemma',
158 'jamba',
157];159];
158160
159/**161/**
@@ -186,6 +188,10 @@ function getSentencepiceTokenizer(model) {
186 return spp_gemma;188 return spp_gemma;
187 }189 }
188190
191 if (model.includes('jamba')) {
192 return spp_jamba;
193 }
194
189 return null;195 return null;
190}196}
191197
@@ -322,6 +328,10 @@ function getTokenizerModel(requestModel) {
322 return 'gemma';328 return 'gemma';
323 }329 }
324330
331 if (requestModel.includes('jamba')) {
332 return 'jamba';
333 }
334
325 // default335 // default
326 return 'gpt-3.5-turbo';336 return 'gpt-3.5-turbo';
327}337}
@@ -537,59 +547,13 @@ function createWebTokenizerDecodingHandler(tokenizer) {
537547
538const router = express.Router();548const router = express.Router();
539549
540router.post('/ai21/count', jsonParser, async function (req, res) {
541 if (!req.body) return res.sendStatus(400);
542 const key = readSecret(req.user.directories, SECRET_KEYS.AI21);
543 const options = {
544 method: 'POST',
545 headers: {
546 accept: 'application/json',
547 'content-type': 'application/json',
548 Authorization: `Bearer ${key}`,
549 },
550 body: JSON.stringify({ text: req.body[0].content }),
551 };
552
553 try {
554 const response = await fetch('https://api.ai21.com/studio/v1/tokenize', options);
555 const data = await response.json();
556 return res.send({ 'token_count': data?.tokens?.length || 0 });
557 } catch (err) {
558 console.error(err);
559 return res.send({ 'token_count': 0 });
560 }
561});
562
563router.post('/google/count', jsonParser, async function (req, res) {
564 if (!req.body) return res.sendStatus(400);
565 const options = {
566 method: 'POST',
567 headers: {
568 accept: 'application/json',
569 'content-type': 'application/json',
570 },
571 body: JSON.stringify({ contents: convertGooglePrompt(req.body, String(req.query.model)).contents }),
572 };
573 try {
574 const reverseProxy = req.query.reverse_proxy?.toString() || '';
575 const proxyPassword = req.query.proxy_password?.toString() || '';
576 const apiKey = reverseProxy ? proxyPassword : readSecret(req.user.directories, SECRET_KEYS.MAKERSUITE);
577 const apiUrl = new URL(reverseProxy || API_MAKERSUITE);
578 const response = await fetch(`${apiUrl.origin}/v1beta/models/${req.query.model}:countTokens?key=${apiKey}`, options);
579 const data = await response.json();
580 return res.send({ 'token_count': data?.totalTokens || 0 });
581 } catch (err) {
582 console.error(err);
583 return res.send({ 'token_count': 0 });
584 }
585});
586
587router.post('/llama/encode', jsonParser, createSentencepieceEncodingHandler(spp_llama));550router.post('/llama/encode', jsonParser, createSentencepieceEncodingHandler(spp_llama));
588router.post('/nerdstash/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd));551router.post('/nerdstash/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd));
589router.post('/nerdstash_v2/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd_v2));552router.post('/nerdstash_v2/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd_v2));
590router.post('/mistral/encode', jsonParser, createSentencepieceEncodingHandler(spp_mistral));553router.post('/mistral/encode', jsonParser, createSentencepieceEncodingHandler(spp_mistral));
591router.post('/yi/encode', jsonParser, createSentencepieceEncodingHandler(spp_yi));554router.post('/yi/encode', jsonParser, createSentencepieceEncodingHandler(spp_yi));
592router.post('/gemma/encode', jsonParser, createSentencepieceEncodingHandler(spp_gemma));555router.post('/gemma/encode', jsonParser, createSentencepieceEncodingHandler(spp_gemma));
556router.post('/jamba/encode', jsonParser, createSentencepieceEncodingHandler(spp_jamba));
593router.post('/gpt2/encode', jsonParser, createTiktokenEncodingHandler('gpt2'));557router.post('/gpt2/encode', jsonParser, createTiktokenEncodingHandler('gpt2'));
594router.post('/claude/encode', jsonParser, createWebTokenizerEncodingHandler(claude_tokenizer));558router.post('/claude/encode', jsonParser, createWebTokenizerEncodingHandler(claude_tokenizer));
595router.post('/llama3/encode', jsonParser, createWebTokenizerEncodingHandler(llama3_tokenizer));559router.post('/llama3/encode', jsonParser, createWebTokenizerEncodingHandler(llama3_tokenizer));
@@ -599,6 +563,7 @@ router.post('/nerdstash_v2/decode', jsonParser, createSentencepieceDecodingHandl
599router.post('/mistral/decode', jsonParser, createSentencepieceDecodingHandler(spp_mistral));563router.post('/mistral/decode', jsonParser, createSentencepieceDecodingHandler(spp_mistral));
600router.post('/yi/decode', jsonParser, createSentencepieceDecodingHandler(spp_yi));564router.post('/yi/decode', jsonParser, createSentencepieceDecodingHandler(spp_yi));
601router.post('/gemma/decode', jsonParser, createSentencepieceDecodingHandler(spp_gemma));565router.post('/gemma/decode', jsonParser, createSentencepieceDecodingHandler(spp_gemma));
566router.post('/jamba/decode', jsonParser, createSentencepieceDecodingHandler(spp_jamba));
602router.post('/gpt2/decode', jsonParser, createTiktokenDecodingHandler('gpt2'));567router.post('/gpt2/decode', jsonParser, createTiktokenDecodingHandler('gpt2'));
603router.post('/claude/decode', jsonParser, createWebTokenizerDecodingHandler(claude_tokenizer));568router.post('/claude/decode', jsonParser, createWebTokenizerDecodingHandler(claude_tokenizer));
604router.post('/llama3/decode', jsonParser, createWebTokenizerDecodingHandler(llama3_tokenizer));569router.post('/llama3/decode', jsonParser, createWebTokenizerDecodingHandler(llama3_tokenizer));
@@ -637,6 +602,11 @@ router.post('/openai/encode', jsonParser, async function (req, res) {
637 return handler(req, res);602 return handler(req, res);
638 }603 }
639604
605 if (queryModel.includes('jamba')) {
606 const handler = createSentencepieceEncodingHandler(spp_jamba);
607 return handler(req, res);
608 }
609
640 const model = getTokenizerModel(queryModel);610 const model = getTokenizerModel(queryModel);
641 const handler = createTiktokenEncodingHandler(model);611 const handler = createTiktokenEncodingHandler(model);
642 return handler(req, res);612 return handler(req, res);
@@ -680,6 +650,11 @@ router.post('/openai/decode', jsonParser, async function (req, res) {
680 return handler(req, res);650 return handler(req, res);
681 }651 }
682652
653 if (queryModel.includes('jamba')) {
654 const handler = createSentencepieceDecodingHandler(spp_jamba);
655 return handler(req, res);
656 }
657
683 const model = getTokenizerModel(queryModel);658 const model = getTokenizerModel(queryModel);
684 const handler = createTiktokenDecodingHandler(model);659 const handler = createTiktokenDecodingHandler(model);
685 return handler(req, res);660 return handler(req, res);
@@ -731,6 +706,11 @@ router.post('/openai/count', jsonParser, async function (req, res) {
731 return res.send({ 'token_count': num_tokens });706 return res.send({ 'token_count': num_tokens });
732 }707 }
733708
709 if (model === 'jamba') {
710 num_tokens = await countSentencepieceArrayTokens(spp_jamba, req.body);
711 return res.send({ 'token_count': num_tokens });
712 }
713
734 const tokensPerName = queryModel.includes('gpt-3.5-turbo-0301') ? -1 : 1;714 const tokensPerName = queryModel.includes('gpt-3.5-turbo-0301') ? -1 : 1;
735 const tokensPerMessage = queryModel.includes('gpt-3.5-turbo-0301') ? 4 : 3;715 const tokensPerMessage = queryModel.includes('gpt-3.5-turbo-0301') ? 4 : 3;
736 const tokensPadding = 3;716 const tokensPadding = 3;
@@ -812,6 +792,7 @@ router.post('/remote/textgenerationwebui/encode', jsonParser, async function (re
812 const baseUrl = String(request.body.url);792 const baseUrl = String(request.body.url);
813 const legacyApi = Boolean(request.body.legacy_api);793 const legacyApi = Boolean(request.body.legacy_api);
814 const vllmModel = String(request.body.vllm_model) || '';794 const vllmModel = String(request.body.vllm_model) || '';
795 const aphroditeModel = String(request.body.aphrodite_model) || '';
815796
816 try {797 try {
817 const args = {798 const args = {
@@ -847,7 +828,7 @@ router.post('/remote/textgenerationwebui/encode', jsonParser, async function (re
847 break;828 break;
848 case TEXTGEN_TYPES.APHRODITE:829 case TEXTGEN_TYPES.APHRODITE:
849 url += '/v1/tokenize';830 url += '/v1/tokenize';
850 args.body = JSON.stringify({ 'prompt': text });831 args.body = JSON.stringify({ 'model': aphroditeModel, 'prompt': text });
851 break;832 break;
852 default:833 default:
853 url += '/v1/internal/encode';834 url += '/v1/internal/encode';
src/endpoints/translate.js+1 -1
@@ -110,6 +110,7 @@ router.post('/google', jsonParser, async (request, response) => {
110});110});
111111
112router.post('/yandex', jsonParser, async (request, response) => {112router.post('/yandex', jsonParser, async (request, response) => {
113 try {
113 const chunks = request.body.chunks;114 const chunks = request.body.chunks;
114 const lang = request.body.lang;115 const lang = request.body.lang;
115116
@@ -130,7 +131,6 @@ router.post('/yandex', jsonParser, async (request, response) => {
130131
131 console.log('Input text: ' + inputText);132 console.log('Input text: ' + inputText);
132133
133 try {
134 const result = await fetch(`https://translate.yandex.net/api/v1/tr.json/translate?ucid=${ucid}&srv=android&format=text`, {134 const result = await fetch(`https://translate.yandex.net/api/v1/tr.json/translate?ucid=${ucid}&srv=android&format=text`, {
135 method: 'POST',135 method: 'POST',
136 body: params,136 body: params,
src/prompt-converters.js+76 -0
@@ -267,10 +267,13 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '
267 'gemini-1.5-flash',267 'gemini-1.5-flash',
268 'gemini-1.5-flash-latest',268 'gemini-1.5-flash-latest',
269 'gemini-1.5-flash-001',269 'gemini-1.5-flash-001',
270 'gemini-1.5-flash-exp-0827',
271 'gemini-1.5-flash-8b-exp-0827',
270 'gemini-1.5-pro',272 'gemini-1.5-pro',
271 'gemini-1.5-pro-latest',273 'gemini-1.5-pro-latest',
272 'gemini-1.5-pro-001',274 'gemini-1.5-pro-001',
273 'gemini-1.5-pro-exp-0801',275 'gemini-1.5-pro-exp-0801',
276 'gemini-1.5-pro-exp-0827',
274 'gemini-1.0-pro-vision-latest',277 'gemini-1.0-pro-vision-latest',
275 'gemini-pro-vision',278 'gemini-pro-vision',
276 ];279 ];
@@ -368,6 +371,78 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '
368}371}
369372
370/**373/**
374 * Convert AI21 prompt. Classic: system message squash, user/assistant message merge.
375 * @param {object[]} messages Array of messages
376 * @param {string} charName Character name
377 * @param {string} userName User name
378 */
379function convertAI21Messages(messages, charName = '', userName = '') {
380 if (!Array.isArray(messages)) {
381 return [];
382 }
383
384 // Collect all the system messages up until the first instance of a non-system message, and then remove them from the messages array.
385 let i = 0, systemPrompt = '';
386
387 for (i = 0; i < messages.length; i++) {
388 if (messages[i].role !== 'system') {
389 break;
390 }
391 // Append example names if not already done by the frontend (e.g. for group chats).
392 if (userName && messages[i].name === 'example_user') {
393 if (!messages[i].content.startsWith(`${userName}: `)) {
394 messages[i].content = `${userName}: ${messages[i].content}`;
395 }
396 }
397 if (charName && messages[i].name === 'example_assistant') {
398 if (!messages[i].content.startsWith(`${charName}: `)) {
399 messages[i].content = `${charName}: ${messages[i].content}`;
400 }
401 }
402 systemPrompt += `${messages[i].content}\n\n`;
403 }
404
405 messages.splice(0, i);
406
407 // Prevent erroring out if the messages array is empty.
408 if (messages.length === 0) {
409 messages.unshift({
410 role: 'user',
411 content: '[Start a new chat]',
412 });
413 }
414
415 if (systemPrompt) {
416 messages.unshift({
417 role: 'system',
418 content: systemPrompt.trim(),
419 });
420 }
421
422 // Doesn't support completion names, so prepend if not already done by the frontend (e.g. for group chats).
423 messages.forEach(msg => {
424 if ('name' in msg) {
425 if (msg.role !== 'system' && !msg.content.startsWith(`${msg.name}: `)) {
426 msg.content = `${msg.name}: ${msg.content}`;
427 }
428 delete msg.name;
429 }
430 });
431
432 // Since the messaging endpoint only supports alternating turns, we have to merge messages with the same role if they follow each other
433 let mergedMessages = [];
434 messages.forEach((message) => {
435 if (mergedMessages.length > 0 && mergedMessages[mergedMessages.length - 1].role === message.role) {
436 mergedMessages[mergedMessages.length - 1].content += '\n\n' + message.content;
437 } else {
438 mergedMessages.push(message);
439 }
440 });
441
442 return mergedMessages;
443}
444
445/**
371 * Convert a prompt from the ChatML objects to the format used by MistralAI.446 * Convert a prompt from the ChatML objects to the format used by MistralAI.
372 * @param {object[]} messages Array of messages447 * @param {object[]} messages Array of messages
373 * @param {string} charName Character name448 * @param {string} charName Character name
@@ -520,4 +595,5 @@ module.exports = {
520 convertCohereMessages,595 convertCohereMessages,
521 convertMistralMessages,596 convertMistralMessages,
522 convertCohereTools,597 convertCohereTools,
598 convertAI21Messages,
523};599};
src/tokenizers/jamba.model+0 -0

Binary file

src/transformers.mjs+1 -1
@@ -35,7 +35,7 @@ const tasks = {
35 defaultModel: 'Cohee/fooocus_expansion-onnx',35 defaultModel: 'Cohee/fooocus_expansion-onnx',
36 pipeline: null,36 pipeline: null,
37 configField: 'extras.promptExpansionModel',37 configField: 'extras.promptExpansionModel',
38 quantized: true,38 quantized: false,
39 },39 },
40 'automatic-speech-recognition': {40 'automatic-speech-recognition': {
41 defaultModel: 'Xenova/whisper-small',41 defaultModel: 'Xenova/whisper-small',
src/util.js+5 -4
@@ -34,8 +34,9 @@ function getConfig() {
34 CACHED_CONFIG = config;34 CACHED_CONFIG = config;
35 return config;35 return config;
36 } catch (error) {36 } catch (error) {
37 console.warn('Failed to read config.yaml');37 console.error(color.red('FATAL: Failed to read config.yaml. Please check the file for syntax errors.'));
38 return {};38 console.error(error.message);
39 process.exit(1);
39 }40 }
40}41}
4142
@@ -298,8 +299,8 @@ const color = {
298 * @returns {string} A UUIDv4 string299 * @returns {string} A UUIDv4 string
299 */300 */
300function uuidv4() {301function uuidv4() {
301 if ('randomUUID' in crypto) {302 if ('crypto' in global && 'randomUUID' in global.crypto) {
302 return crypto.randomUUID();303 return global.crypto.randomUUID();
303 }304 }
304 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {305 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
305 const r = Math.random() * 16 | 0;306 const r = Math.random() * 16 | 0;
src/vectors/makersuite-vectors.js+4 -4
@@ -23,8 +23,8 @@ async function getMakerSuiteVector(text, directories) {
23 const key = readSecret(directories, SECRET_KEYS.MAKERSUITE);23 const key = readSecret(directories, SECRET_KEYS.MAKERSUITE);
2424
25 if (!key) {25 if (!key) {
26 console.log('No MakerSuite key found');26 console.log('No Google AI Studio key found');
27 throw new Error('No MakerSuite key found');27 throw new Error('No Google AI Studio key found');
28 }28 }
2929
30 const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/embedding-gecko-001:embedText?key=${key}`, {30 const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/embedding-gecko-001:embedText?key=${key}`, {
@@ -39,8 +39,8 @@ async function getMakerSuiteVector(text, directories) {
3939
40 if (!response.ok) {40 if (!response.ok) {
41 const text = await response.text();41 const text = await response.text();
42 console.log('MakerSuite request failed', response.statusText, text);42 console.log('Google AI Studio request failed', response.statusText, text);
43 throw new Error('MakerSuite request failed');43 throw new Error('Google AI Studio request failed');
44 }44 }
4545
46 const data = await response.json();46 const data = await response.json();
tests/package-lock.json+3 -4
@@ -4513,10 +4513,9 @@
4513 }4513 }
4514 },4514 },
4515 "node_modules/micromatch": {4515 "node_modules/micromatch": {
4516 "version": "4.0.7",4516 "version": "4.0.8",
4517 "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",4517 "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
4518 "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",4518 "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
4519 "license": "MIT",
4520 "dependencies": {4519 "dependencies": {
4521 "braces": "^3.0.3",4520 "braces": "^3.0.3",
4522 "picomatch": "^2.3.1"4521 "picomatch": "^2.3.1"