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
247247##### 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)
250249- [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/).
251250- [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
2552541. Open your Command Line
2562552. Run the following command
257256
258257`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]'`
259258
260259> 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 @@
11FROM node:lts-alpine3.1819
22
33# Arguments
44ARG APP_HOME=/home/node/app
default/config.yaml+4 -2
@@ -90,9 +90,11 @@ whitelistImportDomains:
9090## headers:
9191## User-Agent: "Googlebot/2.1 (+http://www.google.com/bot.html)"
9292requestOverrides: []
9393# -- PLUGINEXTENSIONS CONFIGURATION --
9494# Enable UI extensions
9595enableExtensions: true
96+# Automatically update extensions when a release version changes
97+enableExtensionsAutoUpdate: true
9698# Extension settings
9799extras:
98100 # Disables automatic model download from HuggingFace
@@ -126,7 +128,7 @@ ollama:
126128 # Controls how long the model will stay loaded into memory following the request
127129 # * -1: Keep the model loaded indefinitely
128130 # * 0: Unload the model immediately after the request
129131 # * 5mN (any positive number): Keep the model loaded for 5N minutesseconds after the request. Accepts duration strings (e.g. 5h30m40s)
130132 keepAlive: -1
131133# -- ANTHROPIC CLAUDE API CONFIGURATION --
132134claude:
default/content/presets/openai/Default.json+1 -4
@@ -8,7 +8,7 @@
88 "openrouter_force_instruct": false,
99 "openrouter_group_models": false,
1010 "openrouter_sort_models": "alphabetically",
1111 "ai21_model": "j2jamba-ultra1.5-large",
1212 "mistralai_model": "mistral-large-latest",
1313 "custom_model": "",
1414 "custom_url": "",
@@ -19,7 +19,6 @@
1919 "temperature": 1,
2020 "frequency_penalty": 0,
2121 "presence_penalty": 0,
22- "count_penalty": 0,
2322 "top_p": 1,
2423 "top_k": 0,
2524 "top_a": 0,
@@ -233,8 +232,6 @@
233232 "assistant_prefill": "",
234233 "assistant_impersonation": "",
235234 "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,
238235 "claude_use_sysprompt": false,
239236 "use_alt_scale": false,
240237 "squash_system_messages": false,
default/content/settings.json+12 -5
@@ -142,6 +142,7 @@
142142 "timestamps_enabled": true,
143143 "timestamp_model_icon": true,
144144 "mesIDDisplay_enabled": false,
145+ "hideChatAvatars_enabled": false,
145146 "max_context_unlocked": false,
146147 "prefer_character_prompt": true,
147148 "prefer_character_jailbreak": true,
@@ -195,7 +196,15 @@
195196 "enableLabMode": false,
196197 "enableZenSliders": false,
197198 "ui_mode": 1,
198199 "forbid_external_media": true,
200+ "stscript": {
201+ "parser": {
202+ "flags": {
203+ "1": true,
204+ "2": true
205+ }
206+ }
207+ }
199208 },
200209 "extension_settings": {
201210 "apiUrl": "http://localhost:5100",
@@ -451,7 +460,6 @@
451460 "temp_openai": 1.0,
452461 "freq_pen_openai": 0,
453462 "pres_pen_openai": 0,
454- "count_pen": 0,
455463 "top_p_openai": 1,
456464 "top_k_openai": 0,
457465 "stream_openai": true,
@@ -613,7 +621,7 @@
613621 "wi_format": "{0}",
614622 "openai_model": "gpt-4-turbo",
615623 "claude_model": "claude-3-5-sonnet-20240620",
616624 "ai21_model": "j2jamba-ultra1.5-large",
617625 "windowai_model": "",
618626 "openrouter_model": "OR_Website",
619627 "jailbreak_system": true,
@@ -624,7 +632,6 @@
624632 "show_external_models": false,
625633 "proxy_password": "",
626634 "assistant_prefill": "",
627635 "assistant_impersonation": "",
628- "use_ai21_tokenizer": false
629636 }
630637}
default/content/themes/Azure.json+1 -0
@@ -23,6 +23,7 @@
2323 "timestamps_enabled": true,
2424 "timestamp_model_icon": false,
2525 "mesIDDisplay_enabled": true,
26+ "hideChatAvatars_enabled": false,
2627 "message_token_count_enabled": false,
2728 "expand_message_actions": false,
2829 "enableZenSliders": false,
default/content/themes/Cappuccino.json+1 -0
@@ -23,6 +23,7 @@
2323 "timestamps_enabled": true,
2424 "timestamp_model_icon": true,
2525 "mesIDDisplay_enabled": true,
26+ "hideChatAvatars_enabled": false,
2627 "message_token_count_enabled": false,
2728 "expand_message_actions": false,
2829 "enableZenSliders": false,
default/content/themes/Dark Lite.json+1 -0
@@ -23,6 +23,7 @@
2323 "timestamps_enabled": true,
2424 "timestamp_model_icon": true,
2525 "mesIDDisplay_enabled": false,
26+ "hideChatAvatars_enabled": false,
2627 "message_token_count_enabled": false,
2728 "expand_message_actions": false,
2829 "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 @@
11{
22 "name": "sillytavern",
33 "version": "1.12.45",
44 "lockfileVersion": 3,
55 "requires": true,
66 "packages": {
77 "": {
88 "name": "sillytavern",
99 "version": "1.12.45",
1010 "hasInstallScript": true,
1111 "license": "AGPL-3.0",
1212 "dependencies": {
package.json+1 -1
@@ -71,7 +71,7 @@
7171 "type": "git",
7272 "url": "https://github.com/SillyTavern/SillyTavern.git"
7373 },
7474 "version": "1.12.45",
7575 "scripts": {
7676 "start": "node server.js",
7777 "start:no-csrf": "node server.js --disableCsrf",
public/img/ai4.png+0 -0

Binary file

public/index.html+39 -46
@@ -5,7 +5,7 @@
55 <title>SillyTavern</title>
66 <base href="/">
77 <meta charset="utf-8">
88 <meta name="viewport" content="width=device-width, viewport-fit=cover, initial-scale=1, maximum-scale=1.0, user-scalable=no, interactive-widget=resizes-content">
99 <meta name="apple-mobile-web-app-capable" content="yes">
1010 <meta name="darkreader-lock">
1111 <meta name="robots" content="noindex, nofollow" />
@@ -436,7 +436,7 @@
436436 </div>
437437 </div>
438438 </div>
439439 <div data-newbie-hidden class="range-block" data-source="openai,openrouter,ai21,custom,cohere,perplexity,groq">
440440 <div class="range-block-title" data-i18n="Frequency Penalty">
441441 Frequency Penalty
442442 </div>
@@ -449,7 +449,7 @@
449449 </div>
450450 </div>
451451 </div>
452452 <div data-newbie-hidden class="range-block" data-source="openai,openrouter,ai21,custom,cohere,perplexity,groq">
453453 <div class="range-block-title" data-i18n="Presence Penalty">
454454 Presence Penalty
455455 </div>
@@ -462,20 +462,7 @@
462462 </div>
463463 </div>
464464 </div>
465465 <div data-newbie-hidden class="range-block" data-source="ai21claude,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">
479466 <div class="range-block-title" data-i18n="Top K">
480467 Top K
481468 </div>
@@ -1814,22 +1801,6 @@
18141801 </select>
18151802 </div>
18161803 </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>
18331804 <div class="range-block" data-source="makersuite">
18341805 <label for="use_makersuite_sysprompt" class="checkbox_label widthFreeExpand">
18351806 <input id="use_makersuite_sysprompt" type="checkbox" />
@@ -1875,7 +1846,7 @@
18751846 <div class="fa-solid fa-clock-rotate-left"></div>
18761847 </div>
18771848 </div>
18781849 <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>
18791850 </div>
18801851 </div>
18811852 </div>
@@ -2460,7 +2431,7 @@
24602431 <option value="claude">Claude</option>
24612432 <option value="cohere">Cohere</option>
24622433 <option value="groq">Groq</option>
24632434 <option value="makersuite">Google MakerSuiteAI Studio</option>
24642435 <option value="mistralai">MistralAI</option>
24652436 <option value="openrouter">OpenRouter</option>
24662437 <option value="perplexity">Perplexity</option>
@@ -2814,16 +2785,18 @@
28142785 <div>
28152786 <h4 data-i18n="AI21 Model">AI21 Model</h4>
28162787 <select id="model_ai21_select">
28172788 <optgroup label="LatestJamba 1.5">
28182789 <option value="j2jamba-ultra1.5-mini">j2jamba-ultra1.5-mini</option>
28192790 <option value="j2jamba-mid1.5-large">j2jamba-mid1.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>
28212794 </optgroup>
28222795 </select>
28232796 </div>
28242797 </form>
28252798 <form id="makersuite_form" data-source="makersuite" action="javascript:void(null);" method="post" enctype="multipart/form-data">
28262799 <h4 data-i18n="MakerSuiteGoogle AI Studio API Key">MakerSuiteGoogle AI Studio API Key</h4>
28272800 <div class="flex-container">
28282801 <input id="api_key_makersuite" name="api_key_makersuite" class="text_pole flex1" maxlength="500" value="" type="text" autocomplete="off">
28292802 <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 @@
28472820 </optgroup>
28482821 <optgroup label="Subversions">
28492822 <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>
28502824 <option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option>
28512825 <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>
28522828 <option value="gemini-1.5-flash-latest">Gemini 1.5 Flash [latest]</option>
28532829 <option value="gemini-1.5-flash-001">Gemini 1.5 Flash [001]</option>
28542830 <option value="gemini-1.0-pro-latest">Gemini 1.0 Pro [latest]</option>
@@ -2978,6 +2954,8 @@
29782954 <option value="command">command</option>
29792955 <option value="command-r">command-r</option>
29802956 <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>
29812959 </optgroup>
29822960 <optgroup label="Nightly">
29832961 <option value="command-light-nightly">command-light-nightly</option>
@@ -3473,6 +3451,7 @@
34733451 <option value="3">Llama 1/2</option>
34743452 <option value="12">Llama 3</option>
34753453 <option value="13">Gemma / Gemini</option>
3454+ <option value="14">Jamba</option>
34763455 <option value="4">NerdStash (NovelAI Clio)</option>
34773456 <option value="5">NerdStash v2 (NovelAI Kayra)</option>
34783457 <option value="7">Mistral</option>
@@ -3645,6 +3624,7 @@
36453624 <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.">
36463625 <small>
36473626 <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>
36483628 </small>
36493629 <input class="neo-range-slider" type="range" id="world_info_min_activations" name="world_info_min_activations" min="0" max="100" step="1">
36503630 <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 @@
36583638 <input class="neo-range-slider" type="range" id="world_info_min_activations_depth_max" name="volume" min="0" max="100" step="1">
36593639 <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">
36603640 </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
36623650 <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink flexBasis48p">
36633651 <small data-i18n="Insertion Strategy">
@@ -3921,8 +3909,8 @@
39213909 <span data-i18n="Font Scale">Font Scale</span>
39223910 <div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Font size" title="Font size"></div>
39233911 </small>
39243912 <input class="neo-range-slider" type="range" id="font_scale" name="font_scale" min="0.85" max="1.25" step="0.01">
39253913 <input class="neo-range-input" type="number" min="0.85" max="1.25" step="0.01" data-for="font_scale" id="font_scale_counter">
39263914 </div>
39273915
39283916 <div class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
@@ -4134,7 +4122,7 @@
41344122 <input id="movingUImode" type="checkbox" />
41354123 <small data-i18n="Movable UI Panels">MovingUI&nbsp;<i class="fa-solid fa-desktop"></i></small>
41364124 </label>
41374125 <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>
41384126 </div>
41394127 <div data-newbie-hidden id="MovingUI-presets-block" class="flex-container alignitemscenter">
41404128 <div class="flex-container alignItemsFlexEnd">
@@ -4590,6 +4578,7 @@
45904578 <div data-newbie-hidden>
45914579 <label for="persona_description_position" data-i18n="Position:">Position:</label>
45924580 <select id="persona_description_position">
4581+ <option value="9" data-i18n="None (disabled)">None (disabled)</option>
45934582 <option value="0" data-i18n="In Story String / Prompt Manager">In Story String / Prompt Manager</option>
45944583 <option value="2" data-i18n="Top of Author's Note">Top of Author's Note</option>
45954584 <option value="3" data-i18n="Bottom of Author's Note">Bottom of Author's Note</option>
@@ -5615,13 +5604,16 @@
56155604 <div class="flex-container wide100p flexGap10">
56165605 <div class="flex4 flex-container flexFlowColumn flexNoGap">
56175606 <div class="flex-container justifySpaceBetween">
56185607 <small for="characterFilter" data-i18n="Filter to Character(s)Characters or Tags">
56195608 Filter to Character(s)Characters or Tags
56205609 </small>
56215610 <label class="checkbox_label flexNoGap margin-r5" for="character_exclusion">
56225611 <input type="checkbox" name="character_exclusion" />
56235612 <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>
56255617 </span>
56265618 </label>
56275619 </div>
@@ -6577,6 +6569,7 @@
65776569 <script src="lib/jquery-cookie-1.4.1.min.js"></script>
65786570 <script src="lib/jquery.ui.touch-punch.min.js"></script>
65796571 <script src="lib/showdown.min.js"></script>
6572+ <script src="lib/showdown-patch.js"></script>
65806573 <script src="lib/showdown-katex.min.js"></script>
65816574 <script src="lib/popper.js"></script>
65826575 <script src="lib/purify.min.js"></script>
public/lib/showdown-patch.js+24 -0
@@ -0,0 +1,24 @@
1+showdown.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 @@
390390 "Alt Method": "طريقة بديلة",
391391 "AI21 API Key": "مفتاح API لـ AI21",
392392 "AI21 Model": "نموذج AI21",
393393 "MakerSuiteGoogle AI Studio API Key": "مفتاح واجهة برمجة تطبيقات MakerSuiteGoogle AI Studio",
394394 "Google Model": "نموذج جوجل",
395395 "MistralAI API Key": "مفتاح واجهة برمجة التطبيقات MistralAI",
396396 "MistralAI Model": "نموذج ميسترال آي آي",
public/locales/de-de.json+1 -1
@@ -390,7 +390,7 @@
390390 "Alt Method": "Alternative Methode",
391391 "AI21 API Key": "AI21 API-Schlüssel",
392392 "AI21 Model": "AI21-Modell",
393393 "MakerSuiteGoogle AI Studio API Key": "MakerSuiteGoogle AI Studio API-Schlüssel",
394394 "Google Model": "Google-Modell",
395395 "MistralAI API Key": "MistralAI API-Schlüssel",
396396 "MistralAI Model": "MistralAI-Modell",
public/locales/es-es.json+1 -1
@@ -390,7 +390,7 @@
390390 "Alt Method": "Método alternativo",
391391 "AI21 API Key": "Clave API de AI21",
392392 "AI21 Model": "Modelo de AI21",
393393 "MakerSuiteGoogle AI Studio API Key": "Clave API de MakerSuiteGoogle AI Studio",
394394 "Google Model": "Modelo de Google",
395395 "MistralAI API Key": "Clave API de MistralAI",
396396 "MistralAI Model": "Modelo MistralAI",
public/locales/fr-fr.json+1 -1
@@ -390,7 +390,7 @@
390390 "Alt Method": "Méthode alternative",
391391 "AI21 API Key": "Clé API AI21",
392392 "AI21 Model": "Modèle AI21",
393393 "MakerSuiteGoogle AI Studio API Key": "Clé API MakerSuiteGoogle AI Studio",
394394 "Google Model": "Modèle Google",
395395 "MistralAI API Key": "Clé API MistralAI",
396396 "MistralAI Model": "Modèle MistralAI",
public/locales/is-is.json+1 -1
@@ -390,7 +390,7 @@
390390 "Alt Method": "Aðferð Bakmenn",
391391 "AI21 API Key": "Lykill API fyrir AI21",
392392 "AI21 Model": "AI21 Módel",
393393 "MakerSuiteGoogle AI Studio API Key": "MakerSuiteGoogle AI Studio API lykill",
394394 "Google Model": "Google líkan",
395395 "MistralAI API Key": "MistralAI API lykill",
396396 "MistralAI Model": "MistralAI líkan",
public/locales/it-it.json+1 -1
@@ -390,7 +390,7 @@
390390 "Alt Method": "Metodo alternativo",
391391 "AI21 API Key": "Chiave API di AI21",
392392 "AI21 Model": "Modello AI21",
393393 "MakerSuiteGoogle AI Studio API Key": "Chiave API MakerSuiteGoogle AI Studio",
394394 "Google Model": "Modello Google",
395395 "MistralAI API Key": "Chiave API MistralAI",
396396 "MistralAI Model": "Modello MistralAI",
public/locales/ja-jp.json+1 -1
@@ -390,7 +390,7 @@
390390 "Alt Method": "代替手法",
391391 "AI21 API Key": "AI21のAPIキー",
392392 "AI21 Model": "AI21モデル",
393393 "MakerSuiteGoogle AI Studio API Key": "MakerSuiteGoogle AI Studio APIキー",
394394 "Google Model": "Google モデル",
395395 "MistralAI API Key": "MistralAI API キー",
396396 "MistralAI Model": "MistralAI モデル",
public/locales/ko-kr.json+1 -1
@@ -390,7 +390,7 @@
390390 "Alt Method": "대체 방법",
391391 "AI21 API Key": "AI21 API 키",
392392 "AI21 Model": "AI21 모델",
393393 "MakerSuiteGoogle AI Studio API Key": "MakerSuiteGoogle AI Studio API 키",
394394 "Google Model": "구글 모델",
395395 "MistralAI API Key": "MistralAI API 키",
396396 "MistralAI Model": "MistralAI 모델",
public/locales/pt-pt.json+1 -1
@@ -390,7 +390,7 @@
390390 "Alt Method": "Método Alternativo",
391391 "AI21 API Key": "Chave da API AI21",
392392 "AI21 Model": "Modelo AI21",
393393 "MakerSuiteGoogle AI Studio API Key": "Chave API MakerSuiteGoogle AI Studio",
394394 "Google Model": "Modelo Google",
395395 "MistralAI API Key": "Chave de API MistralAI",
396396 "MistralAI Model": "Modelo MistralAI",
public/locales/ru-ru.json+55 -8
@@ -43,7 +43,7 @@
4343 "Smoothing Factor": "Коэффициент сглаживания",
4444 "No Repeat Ngram Size": "Размер no_repeat_ngram",
4545 "Min Length": "Мин. длина",
4646 "Alternative server URL (leave empty to use the default value).": "Альтернативный URL альтернативного сервера (оставьте пустым для стандартного значения)",
4747 "Remove your real OAI API Key from the API panel BEFORE typing anything into this box": "Удалите свой личный OAI API Key из панели API, и ТОЛЬКО ПОСЛЕ ЭТОГО вводите что-то сюда",
4848 "We cannot provide support for problems encountered while using an unofficial OpenAI proxy": "Мы не сможем предоставить помощь с проблемами, с которыми вы столкнетесь при использовании неофициальных прокси для OpenAI",
4949 "Context Size (tokens)": "Размер контекста (в токенах)",
@@ -186,7 +186,7 @@
186186 "Generate only one line per request": "Генерировать одну строку на запрос",
187187 "Misc. Settings": "Доп. настройки",
188188 "Auto-Continue": "Авто-продолжение",
189189 "Collapse Consecutive Newlines": "Сворачивать последовательныенеск. новыеновых строкистрок в одну",
190190 "Allow for Chat Completion APIs": "Разрешить для Chat Completion API",
191191 "Target length (tokens)": "Целевая длина (в токенах)",
192192 "World Info": "Информация о мире",
@@ -373,7 +373,7 @@
373373 "Prefer Character Card Prompt": "Приоритет промпту из карточки персонажа",
374374 "Prefer Character Card Jailbreak": "Приоритет джейлбрейку из карточки персонажа",
375375 "Press Send to continue": "Кнопка отправки продолжает сообщение",
376376 "Quick 'Continue' button": "Кнопка быстрогоБыстрое продолженияпродолжение",
377377 "Log prompts to console": "Выводить промпты в консоль",
378378 "Never resize avatars": "Не менять размер аватарок",
379379 "Show avatar filenames": "Показывать названия файлов аватарок",
@@ -495,7 +495,7 @@
495495 "(Write a comma-separated list of tags)": "(Список тегов через запятую)",
496496 "(A brief description of the personality)": "(Краткое описание личности)",
497497 "(Circumstances and context of the interaction)": "(Обстоятельства и контекст этого диалога)",
498498 "(Examples of chat dialog. Begin each example with START on a new line.)": "(Примеры диалога. Начинайте каждый пример с START или новой линиейстрокой.)",
499499 "Type here...": "Пишите здесь...",
500500 "Comma separated (required)": "Через запятую (обязательное поле)",
501501 "What this keyword should mean to the AI, sent verbatim": "Что это ключевое слово должно означать для ИИ, отправляется дословно",
@@ -552,7 +552,7 @@
552552 "Click to set a new User Name": "Нажмите, чтобы задать новое имя пользователя.",
553553 "Click to lock your selected persona to the current chat. Click again to remove the lock.": "Нажмите, чтобы закрепить выбранную персону для текущего чата. Нажмите еще раз, чтобы открепить.",
554554 "Click to set user name for all messages": "Нажмите, чтобы задать имя пользователя для всех сообщений.",
555555 "Create a dummy persona": "Создать пустую персону-болванку",
556556 "Character Management": "Управление персонажами",
557557 "Locked = Character Management panel will stay open": "Закреплено = Панель управление персонажами останется открытой",
558558 "Select/Create Characters": "Выбрать/Создать персонажа",
@@ -625,7 +625,7 @@
625625 "Automatically select a background based on the chat context": "Автоматический выбор фона в зависимости от контекста чата",
626626 "Filter": "Фильтр",
627627 "Exclude message from prompts": "Исключить сообщение из промпта",
628628 "Include message in prompts": "Включить сообщение в подсказкипромпт",
629629 "Create checkpoint": "Создать чекпоинт",
630630 "Create Branch": "Создать ветку",
631631 "Embed file or image": "Вставить файл или изображение",
@@ -722,7 +722,7 @@
722722 "Proxy Server URL": "Адрес прокси-сервера",
723723 "MistralAI Model": "Модель MistralAI",
724724 "MistralAI API Key": "Ключ от API MistralAI",
725725 "MakerSuiteGoogle AI Studio API Key": "Ключ от API MakerSuiteGoogle AI Studio",
726726 "Google Model": "Модель Google",
727727 "Cohere API Key": "Ключ от API Cohere",
728728 "Cohere Model": "Модель Cohere",
@@ -1661,5 +1661,52 @@
16611661 "ext_regex_ai_output_desc": "Сообщения, полученные от API",
16621662 "ext_regex_sts_desc": "Сообщения, отправленные с помощью команд STscript",
16631663 "ext_regex_wi_desc": "Содержимое лорбуков и миров. Для работы требует включения флажка \"Только промпт\"!",
16641664 "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' дописывать не нужно."
16651712}
public/locales/uk-ua.json+1 -1
@@ -390,7 +390,7 @@
390390 "Alt Method": "Альтернативний метод",
391391 "AI21 API Key": "Ключ API для AI21",
392392 "AI21 Model": "Модель AI21",
393393 "MakerSuiteGoogle AI Studio API Key": "Ключ API MakerSuiteGoogle AI Studio",
394394 "Google Model": "Модель Google",
395395 "MistralAI API Key": "Ключ API MistralAI",
396396 "MistralAI Model": "Модель MistralAI",
public/locales/vi-vn.json+1 -1
@@ -390,7 +390,7 @@
390390 "Alt Method": "Phương pháp thay thế",
391391 "AI21 API Key": "Khóa API của AI21",
392392 "AI21 Model": "Mô hình AI21",
393393 "MakerSuiteGoogle AI Studio API Key": "Khóa API MakerSuiteGoogle AI Studio",
394394 "Google Model": "Mô hình Google",
395395 "MistralAI API Key": "Khóa API MistralAI",
396396 "MistralAI Model": "Mô hình MistralAI",
public/locales/zh-cn.json+1 -1
@@ -406,7 +406,7 @@
406406 "Alt Method": "备用方法",
407407 "AI21 API Key": "AI21 API 密钥",
408408 "AI21 Model": "AI21 模型",
409409 "MakerSuiteGoogle AI Studio API Key": "MakerSuiteGoogle AI Studio API 密钥",
410410 "Google Model": "Google 模型",
411411 "MistralAI API Key": "MistralAI API 密钥",
412412 "MistralAI Model": "MistralAI 模型",
public/locales/zh-tw.json+1 -1
@@ -391,7 +391,7 @@
391391 "Alt Method": "替代方法",
392392 "AI21 API Key": "AI21 API 金鑰",
393393 "AI21 Model": "AI21 模型",
394394 "MakerSuiteGoogle AI Studio API Key": "MakerSuiteGoogle AI Studio API 金鑰",
395395 "Google Model": "Google 模型",
396396 "MistralAI API Key": "MistralAI API 金鑰",
397397 "MistralAI Model": "MistralAI 模型",
public/script.js+113 -81
@@ -84,6 +84,7 @@ import {
8484 context_presets,
8585 resetMovableStyles,
8686 forceCharacterEditorTokenize,
87+ applyPowerUserSettings,
8788} from './scripts/power-user.js';
8889
8990import {
@@ -242,7 +243,7 @@ import { DragAndDropHandler } from './scripts/dragdrop.js';
242243import { INTERACTABLE_CONTROL_CLASS, initKeyboard } from './scripts/keyboard.js';
243244import { initDynamicStyles } from './scripts/dynamic-styles.js';
244245import { SlashCommandEnumValue, enumTypes } from './scripts/slash-commands/SlashCommandEnumValue.js';
245246import { commonEnumProviders, enumIcons } from './scripts/slash-commands/SlashCommandCommonEnumsProvider.js';
246247
247248//exporting functions and vars for mods
248249export {
@@ -415,6 +416,7 @@ export const event_types = {
415416 GENERATION_STOPPED: 'generation_stopped',
416417 GENERATION_ENDED: 'generation_ended',
417418 EXTENSIONS_FIRST_LOAD: 'extensions_first_load',
419+ EXTENSION_SETTINGS_LOADED: 'extension_settings_loaded',
418420 SETTINGS_LOADED: 'settings_loaded',
419421 SETTINGS_UPDATED: 'settings_updated',
420422 GROUP_UPDATED: 'group_updated',
@@ -486,14 +488,6 @@ let default_user_name = 'User';
486488export let name1 = default_user_name;
487489export let name2 = 'SillyTavern System';
488490export let chat = [];
489-let 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-];
497491let chatSaveTimeout;
498492let importFlashTimeout;
499493export let isChatSaving = false;
@@ -592,6 +586,17 @@ export const extension_prompt_roles = {
592586
593587export const MAX_INJECTION_DEPTH = 1000;
594588
589+const SAFETY_CHAT = [
590+ {
591+ name: systemUserName,
592+ force_avatar: system_avatar,
593+ is_system: true,
594+ is_user: false,
595+ create_date: 0,
596+ mes: 'You deleted a character/chat and arrived back here for safety reasons! Pick another character!',
597+ },
598+];
599+
595600export let system_messages = {};
596601
597602async function getSystemMessages() {
@@ -1879,7 +1884,12 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId) {
18791884 }
18801885
18811886 if (Number(messageId) === 0 && !isSystem && !isUser) {
1887+ const mesBeforeReplace = mes;
1888+ const chatMessage = chat[messageId];
18821889 mes = substituteParams(mes, undefined, ch_name);
1890+ if (chatMessage && chatMessage.mes === mesBeforeReplace && chatMessage.extra?.display_text !== mesBeforeReplace) {
1891+ chatMessage.mes = mes;
1892+ }
18831893 }
18841894
18851895 mesForShowdownParse = mes;
@@ -2185,7 +2195,7 @@ export function addCopyToCodeBlocks(messageElement) {
21852195 codeBlocks.get(i).appendChild(copyButton);
21862196 copyButton.addEventListener('pointerup', function (event) {
21872197 navigator.clipboard.writeText(codeBlocks.get(i).innerText);
21882198 toastr.info('t`Copied!'`, '', { timeOut: 2000 });
21892199 });
21902200 }
21912201 }
@@ -2698,7 +2708,7 @@ function addPersonaDescriptionExtensionPrompt() {
26982708 const INJECT_TAG = 'PERSONA_DESCRIPTION';
26992709 setExtensionPrompt(INJECT_TAG, '', extension_prompt_types.IN_PROMPT, 0);
27002710
27012711 if (!power_user.persona_description || power_user.persona_description_position === persona_description_positions.NONE) {
27022712 return;
27032713 }
27042714
@@ -2819,7 +2829,7 @@ export function getCharacterCardFields() {
28192829}
28202830
28212831export function isStreamingEnabled() {
28222832 const noStreamSources = [chat_completion_sources.SCALE, chat_completion_sources.AI21];
28232833 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')))
28242834 || (main_api == 'kobold' && kai_settings.streaming_kobold && kai_flags.can_use_streaming)
28252835 || (main_api == 'novel' && nai_settings.streaming_novel)
@@ -2839,7 +2849,14 @@ function hideStopButton() {
28392849}
28402850
28412851class 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) {
28432860 this.result = '';
28442861 this.messageId = -1;
28452862 this.messageDom = null;
@@ -2849,14 +2866,14 @@ class StreamingProcessor {
28492866 /** @type {HTMLTextAreaElement} */
28502867 this.sendTextarea = document.querySelector('#send_textarea');
28512868 this.type = type;
28522869 this.force_name2 = force_name2forceName2;
28532870 this.isStopped = false;
28542871 this.isFinished = false;
28552872 this.generator = this.nullStreamingGeneration;
28562873 this.abortController = new AbortController();
28572874 this.firstMessageText = '...';
28582875 this.timeStarted = timeStarted;
2859- this.messageAlreadyGenerated = messageAlreadyGenerated;
2876+ this.continueMessage = type === 'continue' ? continueMessage : '';
28602877 this.swipes = [];
28612878 /** @type {import('./scripts/logprobs.js').TokenLogprobs[]} */
28622879 this.messageLogprobs = [];
@@ -3009,8 +3026,7 @@ class StreamingProcessor {
30093026 await eventSource.emit(event_types.IMPERSONATE_READY, text);
30103027 }
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);
30143030 await saveChatConditional();
30153031 unblockGeneration();
30163032 generatedPromptCache = '';
@@ -3106,7 +3122,7 @@ class StreamingProcessor {
31063122 if (logprobs) {
31073123 this.messageLogprobs.push(...(Array.isArray(logprobs) ? logprobs : [logprobs]));
31083124 }
31093125 await sw.tick(() => this.onProgressStreaming(this.messageId, this.messageAlreadyGeneratedcontinueMessage + text));
31103126 }
31113127 const seconds = (timestamps[timestamps.length - 1] - timestamps[0]) / 1000;
31123128 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
32993315 const isInstruct = power_user.instruct.enabled && main_api !== 'openai';
33003316 const isImpersonate = type == 'impersonate';
33013317
3302- let message_already_generated = isImpersonate ? `${name1}: ` : `${name2}: `;
3303-
33043318 if (!(dryRun || type == 'regenerate' || type == 'swipe' || type == 'quiet')) {
33053319 const interruptedByCommand = await processCommands(String($('#send_textarea').val()));
33063320
@@ -3715,7 +3729,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
37153729 const storyStringParams = {
37163730 description: description,
37173731 personality: personality,
3718- persona: persona,
3732+ persona: power_user.persona_description_position == persona_description_positions.IN_PROMPT ? persona : '',
37193733 scenario: scenario,
37203734 system: isInstruct ? system : '',
37213735 char: name2,
@@ -3739,7 +3753,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
37393753 let oaiMessageExamples = [];
37403754
37413755 if (main_api === 'openai') {
3742- message_already_generated = '';
37433756 oaiMessages = setOpenAIMessages(coreChat);
37443757 oaiMessageExamples = setOpenAIMessageExamples(mesExamplesArray);
37453758 }
@@ -3750,7 +3763,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
37503763 }
37513764
37523765 let examplesString = '';
37533766 let chatString = addChatsPreamble(addChatsSeparator(''));
37543767 let cyclePrompt = '';
37553768
37563769 async function getMessagesTokenCount() {
@@ -3759,10 +3772,10 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
37593772 storyString,
37603773 afterScenarioAnchor,
37613774 examplesString,
3775+ userAlignmentMessage,
37623776 chatString,
3763- quiet_prompt,
3777+ modifyLastPromptLine(''),
37643778 cyclePrompt,
3765- userAlignmentMessage,
37663779 ].join('').replace(/\r/gm, '');
37673780 return getTokenCountAsync(encodeString, power_user.token_padding);
37683781 }
@@ -3793,8 +3806,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
37933806 }
37943807
37953808 tokenCount += await getTokenCountAsync(item.replace(/\r/gm, ''));
3796- chatString = item + chatString;
37973809 if (tokenCount < this_max_context) {
3810+ chatString = chatString + item;
37983811 arrMes[index] = item;
37993812 lastAddedIndex = Math.max(lastAddedIndex, index);
38003813 } else {
@@ -3820,8 +3833,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
38203833 }
38213834
38223835 tokenCount += await getTokenCountAsync(item.replace(/\r/gm, ''));
3823- chatString = item + chatString;
38243836 if (tokenCount < this_max_context) {
3837+ chatString = chatString + item;
38253838 arrMes[i] = item;
38263839 lastAddedIndex = Math.max(lastAddedIndex, i);
38273840 } else {
@@ -3882,7 +3895,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
38823895 cyclePrompt += oai_settings.continue_postfix;
38833896 continue_mag += oai_settings.continue_postfix;
38843897 }
3885- message_already_generated = continue_mag;
38863898 }
38873899
38883900 const originalType = type;
@@ -4019,15 +4031,16 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
40194031 async function checkPromptSize() {
40204032 console.debug('---checking Prompt size');
40214033 setPromptString();
4034+ const jointMessages = mesSend.map((e) => `${e.extensionPrompts.join('')}${e.message}`).join('');
40224035 const prompt = [
40234036 beforeScenarioAnchor,
40244037 storyString,
40254038 afterScenarioAnchor,
40264039 mesExmString,
4027- mesSend.map((e) => `${e.extensionPrompts.join('')}${e.message}`).join(''),
4040+ addChatsPreamble(addChatsSeparator(jointMessages)),
40284041 '\n',
4042+ modifyLastPromptLine(''),
40294043 generatedPromptCache,
4030- quiet_prompt,
40314044 ].join('').replace(/\r/gm, '');
40324045 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
42934306 padding: power_user.token_padding,
42944307 main_api: main_api,
42954308 instruction: isInstruct ? substituteParams(power_user.prefer_character_prompt && system ? system : power_user.instruct.system_prompt) : '',
42964309 userPersona: (power_user.persona_descriptionpersona_description_position == persona_description_positions.IN_PROMPT ? (persona || '') : ''),
42974310 };
42984311
42994312 //console.log(additionalPromptStuff);
@@ -4309,7 +4322,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
43094322 console.debug(`pushed prompt bits to itemizedPrompts array. Length is now: ${itemizedPrompts.length}`);
43104323
43114324 if (isStreamingEnabled() && type !== 'quiet') {
43124325 streamingProcessor = new StreamingProcessor(type, force_name2, generation_started, message_already_generatedcontinue_mag);
43134326 if (isContinue) {
43144327 // Save reply does add cycle text to the prompt, so it's not needed here
43154328 streamingProcessor.firstMessageText = '';
@@ -5071,7 +5084,7 @@ async function promptItemize(itemizedPrompts, requestedMesId) {
50715084 }
50725085
50735086 navigator.clipboard.writeText(rawPromptValues);
50745087 toastr.info('t`Copied!'`);
50755088 });
50765089
50775090 popup.dlg.querySelector('#showRawPrompt').addEventListener('click', function () {
@@ -5337,17 +5350,10 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc
53375350 // Regex uses vars, so add before formatting
53385351 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-
53445353 if (power_user.collapse_newlines) {
53455354 getMessage = collapseNewlines(getMessage);
53465355 }
53475356
5348- if (power_user.trim_spaces) {
5349- getMessage = getMessage.trim();
5350- }
53515357 // trailing invisible whitespace before every newlines, on a multiline string
53525358 // "trailing whitespace on newlines \nevery line of the string \n?sample text" ->
53535359 // "trailing whitespace on newlines\nevery line of the string\nsample text"
@@ -5439,6 +5445,14 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc
54395445 getMessage = getMessage.trim();
54405446 }
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+
54425456 return getMessage;
54435457}
54445458
@@ -5669,7 +5683,7 @@ export function resetChatState() {
56695683 // replaces deleted charcter name with system user since it will be displayed next.
56705684 name2 = systemUserName;
56715685 // sets up system user to tell user about having deleted a character
56725686 chat.splice(0, =chat.length, [...safetychat]SAFETY_CHAT);
56735687 // resets chat metadata
56745688 chat_metadata = {};
56755689 // resets the characters array, forcing getcharacters to reset
@@ -6300,7 +6314,7 @@ export function setUserName(value) {
63006314 console.log(`User name changed to ${name1}`);
63016315 $('#your_name').val(name1);
63026316 if (power_user.persona_show_notifications) {
63036317 toastr.success(t`Your messages will now be sent as ${name1}`, 't`Current persona updated'`);
63046318 }
63056319 saveSettingsDebounced();
63066320}
@@ -6452,6 +6466,8 @@ export async function getSettings() {
64526466 // Load power user settings
64536467 await loadPowerUserSettings(settings, data);
64546468
6469+ applyPowerUserSettings();
6470+
64556471 // Load character tags
64566472 loadTagsSettings(settings);
64576473
@@ -6506,9 +6522,10 @@ export async function getSettings() {
65066522 selected_button = settings.selected_button;
65076523
65086524 if (data.enable_extensions) {
6525+ const enableAutoUpdate = Boolean(data.enable_extensions_auto_update);
65096526 const isVersionChanged = settings.currentVersion !== currentVersion;
65106527 await loadExtensionSettings(settings, isVersionChanged, enableAutoUpdate);
65116528 await eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED);
65126529 }
65136530
65146531 firstRun = !!settings.firstRun;
@@ -8379,6 +8396,12 @@ const CONNECT_API_MAP = {
83798396 button: '#api_button_openai',
83808397 source: chat_completion_sources.OPENAI,
83818398 },
8399+ // Google alias
8400+ 'google': {
8401+ selected: 'openai',
8402+ button: '#api_button_openai',
8403+ source: chat_completion_sources.MAKERSUITE,
8404+ },
83828405 // OpenRouter special naming, to differentiate between chat comp and text comp
83838406 'openrouter': {
83848407 selected: 'openai',
@@ -8464,7 +8487,7 @@ async function disableInstructCallback() {
84648487/**
84658488 * @param {string} text API name
84668489 */
84678490async function connectAPISlash(_args, text) {
84688491 if (!text.trim()) {
84698492 for (const [key, config] of Object.entries(CONNECT_API_MAP)) {
84708493 if (config.selected !== main_api) continue;
@@ -8487,12 +8510,15 @@ async function connectAPISlash(_, text) {
84878510
84888511 return key;
84898512 }
8513+
8514+ console.error('FIXME: The current API is not in the API map');
8515+ return '';
84908516 }
84918517
84928518 const apiConfig = CONNECT_API_MAP[text.toLowerCase()];
84938519 if (!apiConfig) {
84948520 toastr.error(`Error: ${text} is not a valid API`);
84958521 return '';
84968522 }
84978523
84988524 $(`#main_api option[value='${apiConfig.selected || text}']`).prop('selected', true);
@@ -8512,14 +8538,18 @@ async function connectAPISlash(_, text) {
85128538 $(apiConfig.button).trigger('click');
85138539 }
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
85178544 try {
85188545 await waitUntilCondition(() => online_status !== 'no_connection', 10000, 100);
85198546 console.log('Connection successful');
85208547 } catch {
85218548 console.log('Could not connect after 510 seconds, skipping.');
85228549 }
8550+
8551+ toastr.clear(toast);
8552+ return text;
85238553}
85248554
85258555/**
@@ -8814,16 +8844,21 @@ export async function handleDeleteCharacter(this_chid, delete_chats) {
88148844/**
88158845 * Deletes a character completely, including associated chats if specified
88168846 *
88178847 * @param {string|string[]} characterKey - The key (avatar) of the character to be deleted
88188848 * @param {Object} [options] - Optional parameters for the deletion
88198849 * @param {boolean} [options.deleteChats=true] - Whether to delete associated chats or not
88208850 * @return {Promise<void>} - A promise that resolves when the character is successfully deleted
88218851 */
88228852export 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);
88248859 if (!character) {
88258860 toastr.warning(`Character ${characterKeykey} not found. Cannot beSkipping deleteddeletion.`);
8826- return;
8861+ continue;
88278862 }
88288863
88298864 const chid = characters.indexOf(character);
@@ -8839,10 +8874,12 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
88398874 });
88408875
88418876 if (!response.ok) {
88428877 throw new Error toastr.error(`Failed to delete character: ${response.status} ${response.statusText}`, 'Failed to delete character');
8878+ continue;
88438879 }
88448880
8845- await removeCharacterFromUI(character.name, character.avatar);
8881+ delete tag_map[character.avatar];
8882+ select_rm_info('char_delete', character.name);
88468883
88478884 if (deleteChats) {
88488885 for (const chat of pastChats) {
@@ -8851,35 +8888,30 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
88518888 }
88528889 }
88538890
88548891 await eventSource.emit(event_types.CHARACTER_DELETED, { id: this_chidchid, character: characters[this_chid]character });
8892+ }
8893+
8894+ await removeCharacterFromUI();
88558895}
88568896
88578897/**
88588898 * Function to delete a character from UI after character deletion API success.
88598899 * It manages necessary UI changes such as closing advanced editing popup, unsetting
88608900 * character ID, resetting characters array and chat metadata, deselecting character's tab
88618901 * panel, removing character name from navigation tabs, clearing chat, removingfetching character'supdated list of characters.
8862- * avatar from tag_map, fetching updated list of characters and updating the 'deleted
8863- * character' message.
88648902 * 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.
88698903 */
88708904async function removeCharacterFromUI(name, avatar, reloadCharacters = true) {
88718905 await clearChat();
88728906 $('#character_cross').click();
88738907 this_chid = undefined;
88748908 characters.length = 0;
88758909 name2 = systemUserName;
88768910 chat.splice(0, =chat.length, [...safetychat]SAFETY_CHAT);
88778911 chat_metadata = {};
88788912 $(document.getElementById('rm_button_selected_ch')).children('h2').text('');
88798913 this_chid = undefined;
8880- delete tag_map[avatar];
8914+ await getCharacters();
8881- if (reloadCharacters) await getCharacters();
8882- select_rm_info('char_delete', name);
88838915 await printMessages();
88848916 saveSettingsDebounced();
88858917}
@@ -8978,6 +9010,15 @@ jQuery(async function () {
89789010 name: 'api',
89799011 callback: connectAPISlash,
89809012 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+ ],
89819022 unnamedArgumentList: [
89829023 SlashCommandArgument.fromProps({
89839024 description: 'API to connect to',
@@ -9497,11 +9538,7 @@ jQuery(async function () {
94979538 const oldFileNameFull = $(this).closest('.select_chat_block_wrapper').find('.select_chat_block_filename').text();
94989539 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>`;
95059542 const newName = await callPopup(popupText, 'input', oldFileName);
95069543
95079544 if (!newName || newName == oldFileName) {
@@ -9709,12 +9746,7 @@ jQuery(async function () {
97099746 else if (id == 'option_start_new_chat') {
97109747 if ((selected_group || this_chid !== undefined) && !is_send_press) {
97119748 let deleteCurrentChat = false;
97129749 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>`, {
97189750 onClose: () => deleteCurrentChat = !!$('#del_chat_checkbox').prop('checked'),
97199751 });
97209752 if (!result) {
@@ -10226,10 +10258,10 @@ jQuery(async function () {
1022610258
1022710259 let deleteOnlySwipe = false;
1022810260 if (power_user.confirm_message_delete && fromSlashCommand !== true) {
1022910261 const result = await callGenericPopup('t`Are you sure you want to delete this message?'`, POPUP_TYPE.CONFIRM, null, {
1023010262 okButton: canDeleteSwipe ? 't`Delete Swipe'` : 't`Delete Message'`,
1023110263 cancelButton: 'Cancel',
1023210264 customButtons: canDeleteSwipe ? ['t`Delete Message'`] : null,
1023310265 });
1023410266 if (!result) {
1023510267 return;
public/scripts/BulkEditOverlay.js+11 -11
@@ -108,14 +108,12 @@ class CharacterContextMenu {
108108 * Delete one or more characters,
109109 * opens a popup.
110110 *
111111 * @param {numberstring|string[]} characterIdcharacterKey
112112 * @param {boolean} [deleteChats]
113113 * @returns {Promise<void>}
114114 */
115115 static delete = async (characterIdcharacterKey, deleteChats = false) => {
116- const character = CharacterContextMenu.#getCharacter(characterId);
116+ await deleteCharacter(characterKey, { deleteChats: deleteChats });
117-
118- await deleteCharacter(character.avatar, { deleteChats: deleteChats });
119117 };
120118
121119 static #getCharacter = (characterId) => characters[characterId] ?? null;
@@ -599,8 +597,7 @@ class BulkEditOverlay {
599597
600598 this.container.removeEventListener('mouseup', cancelHold);
601599 this.container.removeEventListener('touchend', cancelHold);
602600 }, BulkEditOverlay.longPressDelay);
603- BulkEditOverlay.longPressDelay);
604601 };
605602
606603 handleLongPressEnd = (event) => {
@@ -847,11 +844,14 @@ class BulkEditOverlay {
847844 const deleteChats = document.getElementById('del_char_checkbox').checked ?? false;
848845
849846 showLoader();
850847 const toast = toastr.info('We\'re deleting your characters, please wait...', 'Working on it');
851- return Promise.allSettled(characterIds.map(async characterId => CharacterContextMenu.delete(characterId, deleteChats)))
848+ const avatarList = characterIds.map(id => characters[id]?.avatar).filter(a => a);
852- .then(() => getCharacters())
849+ return CharacterContextMenu.delete(avatarList, deleteChats)
853850 .then(() => this.browseState())
854851 .finally(() => hideLoader());{
852+ toastr.clear(toast);
853+ hideLoader();
854+ });
855855 });
856856
857857 // At this moment the popup is already changed in the dom, but not yet closed/resolved. We build the avatar list here
public/scripts/extensions.js+33 -7
@@ -21,6 +21,7 @@ const defaultUrl = 'http://localhost:5100';
2121let saveMetadataTimeout = null;
2222
2323let requiresReload = false;
24+let stateChanged = false;
2425
2526export function saveMetadataDebounced() {
2627 const context = getContext();
@@ -238,6 +239,7 @@ function onEnableExtensionClick() {
238239
239240async function enableExtension(name, reload = true) {
240241 extension_settings.disabledExtensions = extension_settings.disabledExtensions.filter(x => x !== name);
242+ stateChanged = true;
241243 await saveSettings();
242244 if (reload) {
243245 location.reload();
@@ -248,6 +250,7 @@ async function enableExtension(name, reload = true) {
248250
249251async function disableExtension(name, reload = true) {
250252 extension_settings.disabledExtensions.push(name);
253+ stateChanged = true;
251254 await saveSettings();
252255 if (reload) {
253256 location.reload();
@@ -304,7 +307,7 @@ async function activateExtensions() {
304307
305308 if (!isDisabled) {
306309 const promise = Promise.all([addExtensionScript(name, manifest), addExtensionStyle(name, manifest)]);
307310 await promise
308311 .then(() => activeExtensions.add(name))
309312 .catch(err => console.log('Could not activate extension: ' + name, err));
310313 promises.push(promise);
@@ -657,7 +660,29 @@ async function showExtensionsDetails() {
657660 await oldPopup.complete(POPUP_RESULT.CANCELLED);
658661 }
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+ });
661686 popupPromise = popup.show();
662687 } catch (error) {
663688 toastr.error('Error loading extensions. See browser console for details.');
@@ -797,16 +822,17 @@ export async function installExtension(url) {
797822 const response = await request.json();
798823 toastr.success(`Extension "${response.display_name}" by ${response.author} (version ${response.version}) has been installed successfully!`, 'Extension installation successful');
799824 console.debug(`Extension "${response.display_name}" has been installed successfully at ${response.extensionPath}`);
800825 await loadExtensionSettings({}, false, false);
801826 await eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED);
802827}
803828
804829/**
805830 * Loads extension settings from the app settings.
806831 * @param {object} settings App Settings
807832 * @param {boolean} versionChanged Is this a version change?
833+ * @param {boolean} enableAutoUpdate Enable auto-update
808834 */
809835async function loadExtensionSettings(settings, versionChanged, enableAutoUpdate) {
810836 if (settings.extension_settings) {
811837 Object.assign(extension_settings, settings.extension_settings);
812838 }
@@ -817,11 +843,11 @@ async function loadExtensionSettings(settings, versionChanged) {
817843 $('#extensions_notify_updates').prop('checked', extension_settings.notifyUpdates);
818844
819845 // Activate offline extensions
820846 await eventSource.emit(event_types.EXTENSIONS_FIRST_LOAD);
821847 extensionNames = await discoverExtensions();
822848 manifests = await getManifests(extensionNames);
823849
824850 if (versionChanged && enableAutoUpdate) {
825851 await autoUpdateExtensions(false);
826852 }
827853
public/scripts/extensions/caption/settings.html+4 -1
@@ -20,7 +20,7 @@
2020 <option value="zerooneai">01.AI (Yi)</option>
2121 <option value="anthropic">Anthropic</option>
2222 <option value="custom" data-i18n="Custom (OpenAI-compatible)">Custom (OpenAI-compatible)</option>
2323 <option value="google">Google MakerSuiteAI Studio</option>
2424 <option value="koboldcpp">KoboldCpp</option>
2525 <option value="llamacpp">llama.cpp</option>
2626 <option value="ollama">Ollama</option>
@@ -45,8 +45,11 @@
4545 <option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>
4646 <option data-type="google" value="gemini-pro-vision">gemini-pro-vision</option>
4747 <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>
4850 <option data-type="google" value="gemini-1.5-pro-latest">gemini-1.5-pro-latest</option>
4951 <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>
5053 <option data-type="openrouter" value="openai/gpt-4-vision-preview">openai/gpt-4-vision-preview</option>
5154 <option data-type="openrouter" value="openai/gpt-4o">openai/gpt-4o</option>
5255 <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 @@
11import { getStringHash, debounce, waitUntilCondition, extractAllWords, isTrueBoolean } from '../../utils.js';
22import { getContext, getApiUrl, extension_settings, doExtrasFetch, modules, renderExtensionTemplateAsync } from '../../extensions.js';
33import {
44 activateSendButtons,
@@ -26,6 +26,7 @@ import { SlashCommand } from '../../slash-commands/SlashCommand.js';
2626import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';
2727import { MacrosParser } from '../../macros.js';
2828import { countWebLlmTokens, generateWebLlmChatPrompt, getWebLlmContextSize, isWebLlmSupported } from '../shared.js';
29+import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
2930export { MODULE_NAME };
3031
3132const MODULE_NAME = '1_memory';
@@ -456,7 +457,12 @@ async function onChatEvent() {
456457 }
457458}
458459
459-async 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+ */
465+async function forceSummarizeChat(quiet) {
460466 if (extension_settings.memory.source === summary_sources.extras) {
461467 toastr.warning('Force summarization is not supported for Extras API');
462468 return;
@@ -471,7 +477,7 @@ async function forceSummarizeChat() {
471477 return '';
472478 }
473479
474480 const toast = quiet ? jQuery() : toastr.info('Summarizing chat...', 'Please wait', { timeOut: 0, extendedTimeOut: 0 });
475481 const value = extension_settings.memory.source === summary_sources.main
476482 ? await summarizeChatMain(context, true, skipWIAN)
477483 : await summarizeChatWebLLM(context, true);
@@ -494,9 +500,10 @@ async function forceSummarizeChat() {
494500async function summarizeCallback(args, text) {
495501 text = text.trim();
496502
497503 // Using forceSummarizeChat to summarizeSummarize the current chat if no text provided
498504 if (!text) {
499505 returnconst awaitquiet forceSummarizeChat= isTrueBoolean(args.quiet);
506+ return await forceSummarizeChat(quiet);
500507 }
501508
502509 const source = args.source || extension_settings.memory.source;
@@ -1005,7 +1012,7 @@ function setupListeners() {
10051012 $('#memory_prompt_words').off('click').on('input', onMemoryPromptWordsInput);
10061013 $('#memory_prompt_interval').off('click').on('input', onMemoryPromptIntervalInput);
10071014 $('#memory_prompt').off('click').on('input', onMemoryPromptInput);
10081015 $('#memory_force_summarize').off('click').on('click', () => forceSummarizeChat(false));
10091016 $('#memory_template').off('click').on('input', onMemoryTemplateInput);
10101017 $('#memory_depth').off('click').on('input', onMemoryDepthInput);
10111018 $('#memory_role').off('click').on('input', onMemoryRoleInput);
@@ -1055,6 +1062,13 @@ jQuery(async function () {
10551062 typeList: [ARGUMENT_TYPE.STRING],
10561063 defaultValue: '',
10571064 }),
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+ }),
10581072 ],
10591073 unnamedArgumentList: [
10601074 new SlashCommandArgument('text to summarize', [ARGUMENT_TYPE.STRING], false, false, ''),
public/scripts/extensions/quick-reply/html/settings.html+3 -0
@@ -11,6 +11,9 @@
1111 <label class="flex-container">
1212 <input type="checkbox" id="qr--isCombined"><span data-i18n="Combine Quick Replies">Combine Quick Replies</span>
1313 </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
1518 <hr>
1619
public/scripts/extensions/quick-reply/src/QuickReplySettings.js+2 -0
@@ -16,6 +16,7 @@ export class QuickReplySettings {
1616 /**@type {Boolean}*/ isEnabled = false;
1717 /**@type {Boolean}*/ isCombined = false;
1818 /**@type {Boolean}*/ isPopout = false;
19+ /**@type {Boolean}*/ showPopoutButton = true;
1920 /**@type {QuickReplyConfig}*/ config;
2021 /**@type {QuickReplyConfig}*/ _chatConfig;
2122 get chatConfig() {
@@ -79,6 +80,7 @@ export class QuickReplySettings {
7980 isEnabled: this.isEnabled,
8081 isCombined: this.isCombined,
8182 isPopout: this.isPopout,
83+ showPopoutButton: this.showPopoutButton,
8284 config: this.config,
8385 };
8486 }
public/scripts/extensions/quick-reply/src/ui/ButtonUi.js+3 -0
@@ -69,6 +69,8 @@ export class ButtonUi {
6969 root.id = 'qr--bar';
7070 root.classList.add('flex-container');
7171 root.classList.add('flexGap5');
72+ if (this.settings.showPopoutButton) {
73+ root.classList.add('popoutVisible');
7274 const popout = document.createElement('div'); {
7375 popout.id = 'qr--popoutTrigger';
7476 popout.classList.add('menu_button');
@@ -81,6 +83,7 @@ export class ButtonUi {
8183 });
8284 root.append(popout);
8385 }
86+ }
8487 if (this.settings.isCombined) {
8588 const buttons = document.createElement('div'); {
8689 buttonHolder = buttons;
public/scripts/extensions/quick-reply/src/ui/SettingsUi.js+10 -0
@@ -14,6 +14,7 @@ export class SettingsUi {
1414
1515 /**@type {HTMLInputElement}*/ isEnabled;
1616 /**@type {HTMLInputElement}*/ isCombined;
17+ /**@type {HTMLInputElement}*/ showPopoutButton;
1718
1819 /**@type {HTMLElement}*/ globalSetList;
1920
@@ -79,6 +80,10 @@ export class SettingsUi {
7980 this.isCombined = this.dom.querySelector('#qr--isCombined');
8081 this.isCombined.checked = this.settings.isCombined;
8182 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());
8287 }
8388
8489 prepareGlobalSetList() {
@@ -235,6 +240,11 @@ export class SettingsUi {
235240 this.settings.save();
236241 }
237242
243+ async onShowPopoutButton() {
244+ this.settings.showPopoutButton = this.showPopoutButton.checked;
245+ this.settings.save();
246+ }
247+
238248 async onGlobalSetListSort() {
239249 this.settings.config.setList = Array.from(this.globalSetList.children).map((it,idx)=>{
240250 const set = this.settings.config.setList[Number(it.getAttribute('data-order'))];
public/scripts/extensions/quick-reply/style.css+3 -1
@@ -27,7 +27,6 @@
2727 max-width: 100%;
2828 overflow-x: auto;
2929 order: 1;
30- padding-right: 2.5em;
3130 position: relative;
3231}
3332#qr--bar > #qr--popoutTrigger {
@@ -35,6 +34,9 @@
3534 right: 0.25em;
3635 top: 0;
3736}
37+#qr--bar.popoutVisible {
38+ padding-right: 2.5em;
39+}
3840#qr--popout {
3941 display: flex;
4042 flex-direction: column;
public/scripts/extensions/quick-reply/style.less+3 -1
@@ -25,7 +25,6 @@
2525 max-width: 100%;
2626 overflow-x: auto;
2727 order: 1;
28- padding-right: 2.5em;
2928 position: relative;
3029
3130 >#qr--popoutTrigger {
@@ -34,6 +33,9 @@
3433 top: 0;
3534 }
3635}
36+#qr--bar.popoutVisible {
37+ padding-right: 2.5em;
38+}
3739
3840#qr--popout {
3941 display: flex;
public/scripts/extensions/shared.js+1 -1
@@ -141,7 +141,7 @@ function throwIfInvalidModel(useReverseProxy) {
141141 }
142142
143143 if (extension_settings.caption.multimodal_api === 'google' && !secret_state[SECRET_KEYS.MAKERSUITE] && !useReverseProxy) {
144144 throw new Error('MakerSuiteGoogle AI Studio API key is not set.');
145145 }
146146
147147 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 = {
5252 pollinations: 'pollinations',
5353 stability: 'stability',
5454 blockentropy: 'blockentropy',
55+ huggingface: 'huggingface',
5556};
5657
5758const initiators = {
@@ -454,6 +455,7 @@ async function loadSettings() {
454455 $('#sd_command_visible').prop('checked', extension_settings.sd.command_visible);
455456 $('#sd_interactive_visible').prop('checked', extension_settings.sd.interactive_visible);
456457 $('#sd_stability_style_preset').val(extension_settings.sd.stability_style_preset);
458+ $('#sd_huggingface_model_id').val(extension_settings.sd.huggingface_model_id);
457459
458460 for (const style of extension_settings.sd.styles) {
459461 const option = document.createElement('option');
@@ -1091,6 +1093,11 @@ function onComfyUrlInput() {
10911093 saveSettingsDebounced();
10921094}
10931095
1096+function onHFModelInput() {
1097+ extension_settings.sd.huggingface_model_id = $('#sd_huggingface_model_id').val();
1098+ saveSettingsDebounced();
1099+}
1100+
10941101function onComfyWorkflowChange() {
10951102 extension_settings.sd.comfy_workflow = $('#sd_comfy_workflow').find(':selected').val();
10961103 saveSettingsDebounced();
@@ -1235,7 +1242,16 @@ async function onModelChange() {
12351242 extension_settings.sd.model = $('#sd_model').find(':selected').val();
12361243 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
12401256 if (cloudSources.includes(extension_settings.sd.source)) {
12411257 return;
@@ -1450,6 +1466,9 @@ async function loadSamplers() {
14501466 case sources.blockentropy:
14511467 samplers = ['N/A'];
14521468 break;
1469+ case sources.huggingface:
1470+ samplers = ['N/A'];
1471+ break;
14531472 }
14541473
14551474 for (const sampler of samplers) {
@@ -1639,6 +1658,9 @@ async function loadModels() {
16391658 case sources.blockentropy:
16401659 models = await loadBlockEntropyModels();
16411660 break;
1661+ case sources.huggingface:
1662+ models = [{ value: '', text: '<Enter Model ID above>' }];
1663+ break;
16421664 }
16431665
16441666 for (const model of models) {
@@ -1986,6 +2008,9 @@ async function loadSchedulers() {
19862008 case sources.blockentropy:
19872009 schedulers = ['N/A'];
19882010 break;
2011+ case sources.huggingface:
2012+ schedulers = ['N/A'];
2013+ break;
19892014 }
19902015
19912016 for (const scheduler of schedulers) {
@@ -2065,6 +2090,9 @@ async function loadVaes() {
20652090 case sources.blockentropy:
20662091 vaes = ['N/A'];
20672092 break;
2093+ case sources.huggingface:
2094+ vaes = ['N/A'];
2095+ break;
20682096 }
20692097
20702098 for (const vae of vaes) {
@@ -2596,6 +2624,9 @@ async function sendGenerationRequest(generationType, prompt, additionalNegativeP
25962624 case sources.blockentropy:
25972625 result = await generateBlockEntropyImage(prefixedPrompt, negativePrompt, signal);
25982626 break;
2627+ case sources.huggingface:
2628+ result = await generateHuggingFaceImage(prefixedPrompt, signal);
2629+ break;
25992630 }
26002631
26012632 if (!result.data) {
@@ -3229,6 +3260,34 @@ async function generateComfyImage(prompt, negativePrompt, signal) {
32293260 return { format: 'png', data: await promptResult.text() };
32303261}
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+ */
3270+async 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+
32323291async function onComfyOpenWorkflowEditorClick() {
32333292 let workflow = await (await fetch('/api/sd/comfy/workflow', {
32343293 method: 'POST',
@@ -3508,6 +3567,8 @@ function isValidState() {
35083567 return secret_state[SECRET_KEYS.STABILITY];
35093568 case sources.blockentropy:
35103569 return secret_state[SECRET_KEYS.BLOCKENTROPY];
3570+ case sources.huggingface:
3571+ return secret_state[SECRET_KEYS.HUGGINGFACE];
35113572 }
35123573}
35133574
@@ -3848,6 +3909,7 @@ jQuery(async () => {
38483909 $('#sd_swap_dimensions').on('click', onSwapDimensionsClick);
38493910 $('#sd_stability_key').on('click', onStabilityKeyClick);
38503911 $('#sd_stability_style_preset').on('change', onStabilityStylePresetChange);
3912+ $('#sd_huggingface_model_id').on('input', onHFModelInput);
38513913
38523914 $('.sd_settings .inline-drawer-toggle').on('click', function () {
38533915 initScrollHeight($('#sd_prompt_prefix'));
public/scripts/extensions/stable-diffusion/settings.html+8 -1
@@ -29,7 +29,8 @@
2929 </label>
3030 <label for="sd_expand" class="checkbox_label" data-i18n="[title]sd_expand" title="Automatically extend prompts using text generation model">
3131 <input id="sd_expand" type="checkbox" />
3232 <span data-i18n="sd_expand_txt">Auto-enhanceextend 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>
3334 </label>
3435 <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).">
3536 <input id="sd_snap" type="checkbox" />
@@ -41,6 +42,7 @@
4142 <option value="comfy">ComfyUI</option>
4243 <option value="drawthings">DrawThings HTTP API</option>
4344 <option value="extras">Extras API (local / remote)</option>
45+ <option value="huggingface">HuggingFace Inference API (serverless)</option>
4446 <option value="novel">NovelAI Diffusion</option>
4547 <option value="openai">OpenAI (DALL-E)</option>
4648 <option value="pollinations">Pollinations</option>
@@ -82,6 +84,11 @@
8284 <!-- (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. -->
8385 <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>
8486 </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>
8592 <div data-sd-source="vlad">
8693 <label for="sd_vlad_url">SD.Next API URL</label>
8794 <div class="flex-container flexnowrap">
public/scripts/extensions/tts/index.js+6 -1
@@ -421,7 +421,7 @@ function completeTtsJob() {
421421async function tts(text, voiceId, char) {
422422 async function processResponse(response) {
423423 // RVC injection
424424 if (extension_settings.rvc.enabled && typeof window['rvcVoiceConversion'] === 'function' && extension_settings.rvc.enabled)
425425 response = await window['rvcVoiceConversion'](response, char, text);
426426
427427 await addAudioJob(response, char);
@@ -755,6 +755,11 @@ async function onMessageEvent(messageId, lastCharIndex) {
755755 const message = structuredClone(context.chat[messageId]);
756756 const hashNew = getStringHash(message?.mes ?? '');
757757
758+ // Ignore prompt-hidden messages
759+ if (message.is_system) {
760+ return;
761+ }
762+
758763 // if no new messages, or same message, or same message hash, do nothing
759764 if (hashNew === lastMessageHash) {
760765 return;
public/scripts/extensions/vectors/settings.html+1 -1
@@ -12,7 +12,7 @@
1212 <select id="vectors_source" class="text_pole">
1313 <option value="cohere">Cohere</option>
1414 <option value="extras">Extras</option>
1515 <option value="palm">Google MakerSuiteAI Studio</option>
1616 <option value="llamacpp">llama.cpp</option>
1717 <option value="transformers" data-i18n="Local (Transformers)">Local (Transformers)</option>
1818 <option value="mistral">MistralAI</option>
public/scripts/nai-settings.js+16 -1
@@ -78,6 +78,19 @@ export function getKayraMaxContextTokens() {
7878 return null;
7979}
8080
81+export function getKayraMaxResponseTokens() {
82+ switch (novel_data?.tier) {
83+ case 1:
84+ return 100;
85+ case 2:
86+ return 100;
87+ case 3:
88+ return 150;
89+ }
90+
91+ return maximum_output_length;
92+}
93+
8194export function getNovelTier() {
8295 return nai_tiers[novel_data?.tier] ?? 'no_connection';
8396}
@@ -438,12 +451,14 @@ export function getNovelGenerationData(finalPrompt, settings, maxLength, isImper
438451 console.log(finalPrompt);
439452 }
440453
454+ const adjustedMaxLength = nai_settings.model_novel.includes('kayra') ? getKayraMaxResponseTokens() : maximum_output_length;
455+
441456 return {
442457 'input': finalPrompt,
443458 'model': nai_settings.model_novel,
444459 'use_string': true,
445460 'temperature': Number(nai_settings.temperature),
446461 'max_length': maxLength < maximum_output_lengthadjustedMaxLength ? maxLength : maximum_output_lengthadjustedMaxLength,
447462 'min_length': Number(nai_settings.min_length),
448463 'tail_free_sampling': Number(nai_settings.tail_free_sampling),
449464 'repetition_penalty': Number(nai_settings.repetition_penalty),
public/scripts/openai.js+47 -113
@@ -74,6 +74,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashComma
7474import { renderTemplateAsync } from './templates.js';
7575import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
7676import { Popup, POPUP_RESULT } from './popup.js';
77+import { t } from './i18n.js';
7778
7879export {
7980 openai_messages_count,
@@ -132,13 +133,9 @@ const max_2mil = 2000 * 1000;
132133const scale_max = 8191;
133134const claude_max = 9000; // We have a proper tokenizer, so theoretically could be larger (up to 9k)
134135const claude_100k_max = 99000;
135-let ai21_max = 9200; //can easily fit 9k gpt tokens because j2's tokenizer is efficient af
136+const unlocked_max = max_2mil;
136-const unlocked_max = max_200k;
137137const oai_max_temp = 2.0;
138138const claude_max_temp = 1.0; //same as j2
139-const j2_max_topk = 10.0;
140-const j2_max_freq = 5.0;
141-const j2_max_pres = 5.0;
142139const openrouter_website_model = 'OR_Website';
143140const openai_max_stop_strings = 4;
144141
@@ -218,25 +215,11 @@ const sensitiveFields = [
218215 'custom_include_headers',
219216];
220217
221-function 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-
234218const default_settings = {
235219 preset_settings_openai: 'Default',
236220 temp_openai: 1.0,
237221 freq_pen_openai: 0,
238222 pres_pen_openai: 0,
239- count_pen: 0.0,
240223 top_p_openai: 1.0,
241224 top_k_openai: 0,
242225 min_p_openai: 0,
@@ -264,7 +247,7 @@ const default_settings = {
264247 openai_model: 'gpt-4-turbo',
265248 claude_model: 'claude-3-5-sonnet-20240620',
266249 google_model: 'gemini-1.5-pro',
267250 ai21_model: 'j2jamba-ultra1.5-large',
268251 mistralai_model: 'mistral-large-latest',
269252 cohere_model: 'command-r-plus',
270253 perplexity_model: 'llama-3.1-70b-instruct',
@@ -294,8 +277,6 @@ const default_settings = {
294277 assistant_prefill: '',
295278 assistant_impersonation: '',
296279 human_sysprompt_message: default_claude_human_sysprompt_message,
297- use_ai21_tokenizer: false,
298- use_google_tokenizer: false,
299280 claude_use_sysprompt: false,
300281 use_makersuite_sysprompt: true,
301282 use_alt_scale: false,
@@ -317,7 +298,6 @@ const oai_settings = {
317298 temp_openai: 1.0,
318299 freq_pen_openai: 0,
319300 pres_pen_openai: 0,
320- count_pen: 0.0,
321301 top_p_openai: 1.0,
322302 top_k_openai: 0,
323303 min_p_openai: 0,
@@ -345,7 +325,7 @@ const oai_settings = {
345325 openai_model: 'gpt-4-turbo',
346326 claude_model: 'claude-3-5-sonnet-20240620',
347327 google_model: 'gemini-1.5-pro',
348328 ai21_model: 'j2jamba-ultra1.5-large',
349329 mistralai_model: 'mistral-large-latest',
350330 cohere_model: 'command-r-plus',
351331 perplexity_model: 'llama-3.1-70b-instruct',
@@ -375,8 +355,6 @@ const oai_settings = {
375355 assistant_prefill: '',
376356 assistant_impersonation: '',
377357 human_sysprompt_message: default_claude_human_sysprompt_message,
378- use_ai21_tokenizer: false,
379- use_google_tokenizer: false,
380358 claude_use_sysprompt: false,
381359 use_makersuite_sysprompt: true,
382360 use_alt_scale: false,
@@ -425,7 +403,7 @@ async function validateReverseProxy() {
425403 const rememberKey = `Proxy_SkipConfirm_${getStringHash(oai_settings.reverse_proxy)}`;
426404 const skipConfirm = localStorage.getItem(rememberKey) === 'true';
427405
428406 const confirmation = skipConfirm || await Popup.show.confirm('t`Connecting To Proxy', `<span>Are you sure you want to connect, toawait therenderTemplateAsync('proxyConnectionWarning', following{ proxyproxyURL: URL?</span><var>${DOMPurify.sanitize(oai_settings.reverse_proxy) }</var>`));
429407
430408 if (!confirmation) {
431409 toastr.error('Update or remove your reverse proxy settings.');
@@ -1148,7 +1126,6 @@ function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, wor
11481126 { role: 'system', content: charDescription, identifier: 'charDescription' },
11491127 { role: 'system', content: charPersonalityText, identifier: 'charPersonality' },
11501128 { role: 'system', content: scenarioText, identifier: 'scenario' },
1151- { role: 'system', content: personaDescription, identifier: 'personaDescription' },
11521129 // Unordered prompts without marker
11531130 { role: 'system', content: impersonationPrompt, identifier: 'impersonate' },
11541131 { role: 'system', content: quietPrompt, identifier: 'quietPrompt' },
@@ -1395,6 +1372,11 @@ function tryParseStreamingError(response, decoded) {
13951372 toastr.error(data.error.message || response.statusText, 'Chat Completion API');
13961373 throw new Error(data);
13971374 }
1375+
1376+ if (data.message) {
1377+ toastr.error(data.message, 'Chat Completion API');
1378+ throw new Error(data);
1379+ }
13981380 }
13991381 catch {
14001382 // No JSON. Do nothing.
@@ -1802,7 +1784,6 @@ async function sendOpenAIRequest(type, messages, signal) {
18021784 const isClaude = oai_settings.chat_completion_source == chat_completion_sources.CLAUDE;
18031785 const isOpenRouter = oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER;
18041786 const isScale = oai_settings.chat_completion_source == chat_completion_sources.SCALE;
1805- const isAI21 = oai_settings.chat_completion_source == chat_completion_sources.AI21;
18061787 const isGoogle = oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE;
18071788 const isOAI = oai_settings.chat_completion_source == chat_completion_sources.OPENAI;
18081789 const isMistral = oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI;
@@ -1815,7 +1796,7 @@ async function sendOpenAIRequest(type, messages, signal) {
18151796 const isQuiet = type === 'quiet';
18161797 const isImpersonate = type === 'impersonate';
18171798 const isContinue = type === 'continue';
18181799 const stream = oai_settings.stream_openai && !isQuiet && !isScale && !isAI21 && !(isGoogle && oai_settings.google_model.includes('bison'));
18191800 const useLogprobs = !!power_user.request_token_probabilities;
18201801 const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom);
18211802
@@ -1824,15 +1805,6 @@ async function sendOpenAIRequest(type, messages, signal) {
18241805 replaceItemizedPromptText(messageId, messages);
18251806 }
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-
18361808 // If we're using the window.ai extension, use that instead
18371809 // Doesn't support logit bias yet
18381810 if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI) {
@@ -1931,12 +1903,6 @@ async function sendOpenAIRequest(type, messages, signal) {
19311903 generate_data['use_makersuite_sysprompt'] = oai_settings.use_makersuite_sysprompt;
19321904 }
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-
19401906 if (isMistral) {
19411907 generate_data['safe_prompt'] = false; // already defaults to false, but just incase they change that in the future.
19421908 }
@@ -3008,7 +2974,6 @@ function loadOpenAISettings(data, settings) {
30082974 oai_settings.temp_openai = settings.temp_openai ?? default_settings.temp_openai;
30092975 oai_settings.freq_pen_openai = settings.freq_pen_openai ?? default_settings.freq_pen_openai;
30102976 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;
30122977 oai_settings.top_p_openai = settings.top_p_openai ?? default_settings.top_p_openai;
30132978 oai_settings.top_k_openai = settings.top_k_openai ?? default_settings.top_k_openai;
30142979 oai_settings.top_a_openai = settings.top_a_openai ?? default_settings.top_a_openai;
@@ -3080,10 +3045,12 @@ function loadOpenAISettings(data, settings) {
30803045 oai_settings.names_behavior = character_names_behavior.COMPLETION;
30813046 }
30823047
3048+ if (oai_settings.ai21_model.startsWith('j2-')) {
3049+ oai_settings.ai21_model = 'jamba-1.5-large';
3050+ }
3051+
30833052 if (settings.wrap_in_quotes !== undefined) oai_settings.wrap_in_quotes = !!settings.wrap_in_quotes;
30843053 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;
30873054 if (settings.claude_use_sysprompt !== undefined) oai_settings.claude_use_sysprompt = !!settings.claude_use_sysprompt;
30883055 if (settings.use_makersuite_sysprompt !== undefined) oai_settings.use_makersuite_sysprompt = !!settings.use_makersuite_sysprompt;
30893056 if (settings.use_alt_scale !== undefined) { oai_settings.use_alt_scale = !!settings.use_alt_scale; updateScaleForm(); }
@@ -3133,8 +3100,6 @@ function loadOpenAISettings(data, settings) {
31333100 $('#jailbreak_system').prop('checked', oai_settings.jailbreak_system);
31343101 $('#openai_show_external_models').prop('checked', oai_settings.show_external_models);
31353102 $('#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);
31383103 $('#claude_use_sysprompt').prop('checked', oai_settings.claude_use_sysprompt);
31393104 $('#use_makersuite_sysprompt').prop('checked', oai_settings.use_makersuite_sysprompt);
31403105 $('#scale-alt').prop('checked', oai_settings.use_alt_scale);
@@ -3170,9 +3135,6 @@ function loadOpenAISettings(data, settings) {
31703135 $('#pres_pen_openai').val(oai_settings.pres_pen_openai);
31713136 $('#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-
31763138 $('#top_p_openai').val(oai_settings.top_p_openai);
31773139 $('#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) {
33923354 temperature: settings.temp_openai,
33933355 frequency_penalty: settings.freq_pen_openai,
33943356 presence_penalty: settings.pres_pen_openai,
3395- count_penalty: settings.count_pen,
33963357 top_p: settings.top_p_openai,
33973358 top_k: settings.top_k_openai,
33983359 top_a: settings.top_a_openai,
@@ -3427,8 +3388,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
34273388 assistant_prefill: settings.assistant_prefill,
34283389 assistant_impersonation: settings.assistant_impersonation,
34293390 human_sysprompt_message: settings.human_sysprompt_message,
3430- use_ai21_tokenizer: settings.use_ai21_tokenizer,
3431- use_google_tokenizer: settings.use_google_tokenizer,
34323391 claude_use_sysprompt: settings.claude_use_sysprompt,
34333392 use_makersuite_sysprompt: settings.use_makersuite_sysprompt,
34343393 use_alt_scale: settings.use_alt_scale,
@@ -3799,7 +3758,6 @@ function onSettingsPresetChange() {
37993758 temperature: ['#temp_openai', 'temp_openai', false],
38003759 frequency_penalty: ['#freq_pen_openai', 'freq_pen_openai', false],
38013760 presence_penalty: ['#pres_pen_openai', 'pres_pen_openai', false],
3802- count_penalty: ['#count_pen', 'count_pen', false],
38033761 top_p: ['#top_p_openai', 'top_p_openai', false],
38043762 top_k: ['#top_k_openai', 'top_k_openai', false],
38053763 top_a: ['#top_a_openai', 'top_a_openai', false],
@@ -3856,8 +3814,6 @@ function onSettingsPresetChange() {
38563814 assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],
38573815 assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false],
38583816 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],
38613817 claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true],
38623818 use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true],
38633819 use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],
@@ -4028,6 +3984,11 @@ async function onModelChange() {
40283984 }
40293985
40303986 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+
40313992 console.log('AI21 model changed to', value);
40323993 oai_settings.ai21_model = value;
40333994 }
@@ -4230,7 +4191,7 @@ async function onModelChange() {
42304191 else if (['command-light-nightly', 'command-nightly'].includes(oai_settings.cohere_model)) {
42314192 $('#openai_max_context').attr('max', max_8k);
42324193 }
42334194 else if (['command-r', oai_settings.cohere_model.includes('command-r-plus'].includes(oai_settings.cohere_model)) {
42344195 $('#openai_max_context').attr('max', max_128k);
42354196 }
42364197 else if (['c4ai-aya-23'].includes(oai_settings.cohere_model)) {
@@ -4305,33 +4266,13 @@ async function onModelChange() {
43054266 if (oai_settings.chat_completion_source == chat_completion_sources.AI21) {
43064267 if (oai_settings.max_context_unlocked) {
43074268 $('#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')) {
43094270 $('#openai_max_context').attr('max', ai21_maxmax_256k);
43104271 }
43114272
43124273 oai_settings.openai_max_context = Math.min(oai_settings.openai_max_context, Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
43134274 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
4314-
4275+ $('#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');
43354276 }
43364277
43374278 if (oai_settings.chat_completion_source == chat_completion_sources.CUSTOM) {
@@ -4388,6 +4329,16 @@ async function onModelChange() {
43884329 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
43894330 }
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+
43914342 $('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max')));
43924343
43934344 saveSettingsDebounced();
@@ -4473,7 +4424,7 @@ async function onConnectButtonClick(e) {
44734424 }
44744425
44754426 if (!secret_state[SECRET_KEYS.MAKERSUITE] && !oai_settings.reverse_proxy) {
44764427 console.log('No secret key saved for MakerSuiteGoogle AI Studio');
44774428 return;
44784429 }
44794430 }
@@ -4673,17 +4624,17 @@ function toggleChatCompletionForms() {
46734624async function testApiConnection() {
46744625 // Check if the previous request is still in progress
46754626 if (is_send_press) {
46764627 toastr.info('t`Please wait for the previous request to complete.'`);
46774628 return;
46784629 }
46794630
46804631 try {
46814632 const reply = await sendOpenAIRequest('quiet', [{ 'role': 'user', 'content': 'Hi' }]);
46824633 console.log(reply);
46834634 toastr.success('t`API connection successful!'`);
46844635 }
46854636 catch (err) {
46864637 toastr.error('t`Could not get a reply from API. Check your connection settings / API key and try again.'`);
46874638 }
46884639}
46894640
@@ -4752,11 +4703,14 @@ export function isImageInliningSupported() {
47524703 'gemini-1.5-flash',
47534704 'gemini-1.5-flash-latest',
47544705 'gemini-1.5-flash-001',
4706+ 'gemini-1.5-flash-exp-0827',
4707+ 'gemini-1.5-flash-8b-exp-0827',
47554708 'gemini-1.0-pro-vision-latest',
47564709 'gemini-1.5-pro',
47574710 'gemini-1.5-pro-latest',
47584711 'gemini-1.5-pro-001',
47594712 'gemini-1.5-pro-exp-0801',
4713+ 'gemini-1.5-pro-exp-0827',
47604714 'gemini-pro-vision',
47614715 'claude-3',
47624716 'claude-3-5',
@@ -4837,7 +4791,7 @@ function onProxyPresetChange() {
48374791 if (selectedPreset) {
48384792 setProxyPreset(selectedPreset.name, selectedPreset.url, selectedPreset.password);
48394793 } else {
48404794 console.error(t`Proxy preset "'${value}"' not found in proxies array.`);
48414795 }
48424796 saveSettingsDebounced();
48434797}
@@ -4849,7 +4803,7 @@ $('#save_proxy').on('click', async function () {
48494803
48504804 setProxyPreset(presetName, reverseProxy, proxyPassword);
48514805 saveSettingsDebounced();
48524806 toastr.success('t`Proxy Saved'`);
48534807 if ($('#openai_proxy_preset').val() !== presetName) {
48544808 const option = document.createElement('option');
48554809 option.text = presetName;
@@ -4883,9 +4837,9 @@ $('#delete_proxy').on('click', async function () {
48834837
48844838 saveSettingsDebounced();
48854839 $('#openai_proxy_preset').val(selected_proxy.name);
48864840 toastr.success('t`Proxy Deleted'`);
48874841 } else {
48884842 toastr.error(t`Could not find proxy with name "'${presetName}"'`);
48894843 }
48904844});
48914845
@@ -4953,12 +4907,6 @@ $(document).ready(async function () {
49534907 saveSettingsDebounced();
49544908 });
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-
49624910 $('#top_p_openai').on('input', function () {
49634911 oai_settings.top_p_openai = Number($(this).val());
49644912 $('#top_p_counter_openai').val(Number($(this).val()).toFixed(2));
@@ -5017,20 +4965,6 @@ $(document).ready(async function () {
50174965 saveSettingsDebounced();
50184966 });
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-
50344968 $('#claude_use_sysprompt').on('change', function () {
50354969 oai_settings.claude_use_sysprompt = !!$('#claude_use_sysprompt').prop('checked');
50364970 $('#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 = {
4040 * @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`.
4141 * @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.
4242 * @property {CustomPopupInput[]?} [customInputs=null] - Custom inputs to add to the popup. The display below the content and the input box, one by one.
4343 * @property {(popup: Popup) => Promise<boolean?>|boolean?} [onClosing=null] - Handler called before the popup closes, return `false` to cancel the close
4444 * @property {(popup: Popup) => Promise<void?>|void?} [onClose=null] - Handler called after the popup closes, but before the DOM is cleaned up
4545 * @property {number?} [cropAspect=null] - Aspect ratio for the crop popup
4646 * @property {string?} [cropImage=null] - Image URL to display in the crop popup
4747 */
@@ -138,8 +138,8 @@ export class Popup {
138138 /** @readonly @type {CustomPopupButton[]|string[]?} */ customButtons;
139139 /** @readonly @type {CustomPopupInput[]} */ customInputs;
140140
141141 /** @type {(popup: Popup) => Promise<boolean?>|boolean?} */ onClosing;
142142 /** @type {(popup: Popup) => Promise<void?>|void?} */ onClose;
143143
144144 /** @type {POPUP_RESULT|number} */ result;
145145 /** @type {any} */ value;
@@ -509,7 +509,7 @@ export class Popup {
509509 this.result = result;
510510
511511 if (this.onClosing) {
512512 const shouldClose = await this.onClosing(this);
513513 if (!shouldClose) {
514514 this.#isClosingPrevented = true;
515515 // Set values back if we cancel out of closing the popup
@@ -547,13 +547,13 @@ export class Popup {
547547 fixToastrForDialogs();
548548
549549 // After the dialog is actually completely closed, remove it from the DOM
550550 runAfterAnimation(this.dlg, async () => {
551551 // Call the close on the dialog
552552 this.dlg.close();
553553
554554 // Run a possible custom handler right before DOM removal
555555 if (this.onClose) {
556556 await this.onClose(this);
557557 }
558558
559559 // Remove it from the dom
public/scripts/power-user.js+32 -175
@@ -60,6 +60,7 @@ export {
6060 power_user,
6161 send_on_enter_options,
6262 getContextSettings,
63+ applyPowerUserSettings,
6364};
6465
6566export const MAX_CONTEXT_DEFAULT = 8192;
@@ -106,6 +107,7 @@ export const persona_description_positions = {
106107 TOP_AN: 2,
107108 BOTTOM_AN: 3,
108109 AT_DEPTH: 4,
110+ NONE: 9,
109111};
110112
111113let power_user = {
@@ -301,45 +303,9 @@ let movingUIPresets = [];
301303export let context_presets = [];
302304
303305const 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',
341306 auto_connect_legacy: 'AutoConnectEnabled',
342307 auto_load_chat_legacy: 'AutoLoadChatEnabled',
308+ hideChatAvatars_legacy: 'hideChatAvatarsEnabled',
343309
344310 storyStringValidationCache: 'StoryStringValidationCache',
345311};
@@ -459,73 +425,47 @@ function fixMarkdown(text, forDisplay) {
459425}
460426
461427function switchHotswap() {
462- const value = localStorage.getItem(storage_keys.hotswap_enabled);
463- power_user.hotswap_enabled = value === null ? true : value == 'true';
464428 $('body').toggleClass('no-hotswap', !power_user.hotswap_enabled);
465429 $('#hotswapEnabled').prop('checked', power_user.hotswap_enabled);
466430}
467431
468432function switchTimer() {
469- const value = localStorage.getItem(storage_keys.timer_enabled);
470- power_user.timer_enabled = value === null ? true : value == 'true';
471433 $('body').toggleClass('no-timer', !power_user.timer_enabled);
472434 $('#messageTimerEnabled').prop('checked', power_user.timer_enabled);
473435}
474436
475437function switchTimestamps() {
476- const value = localStorage.getItem(storage_keys.timestamps_enabled);
477- power_user.timestamps_enabled = value === null ? true : value == 'true';
478438 $('body').toggleClass('no-timestamps', !power_user.timestamps_enabled);
479439 $('#messageTimestampsEnabled').prop('checked', power_user.timestamps_enabled);
480440}
481441
482442function switchIcons() {
483- const value = localStorage.getItem(storage_keys.timestamp_model_icon);
484- power_user.timestamp_model_icon = value === null ? true : value == 'true';
485443 $('body').toggleClass('no-modelIcons', !power_user.timestamp_model_icon);
486444 $('#messageModelIconEnabled').prop('checked', power_user.timestamp_model_icon);
487445}
488446
489447function switchTokenCount() {
490- const value = localStorage.getItem(storage_keys.message_token_count_enabled);
491- power_user.message_token_count_enabled = value === null ? false : value == 'true';
492448 $('body').toggleClass('no-tokenCount', !power_user.message_token_count_enabled);
493449 $('#messageTokensEnabled').prop('checked', power_user.message_token_count_enabled);
494450}
495451
496452function 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}`) */
503453 $('body').toggleClass('no-mesIDDisplay', !power_user.mesIDDisplay_enabled);
504454 $('#mesIDDisplayEnabled').prop('checked', power_user.mesIDDisplay_enabled);
505455}
506456
507457function 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- */
514458 $('body').toggleClass('hideChatAvatars', power_user.hideChatAvatars_enabled);
515459 $('#hideChatAvatarsEnabled').prop('checked', power_user.hideChatAvatars_enabled);
516460}
517461
518462function switchMessageActions() {
519- const value = localStorage.getItem(storage_keys.expand_message_actions);
520- power_user.expand_message_actions = value === null ? false : value == 'true';
521463 $('body').toggleClass('expandMessageActions', power_user.expand_message_actions);
522464 $('#expandMessageActions').prop('checked', power_user.expand_message_actions);
523465 $('.extraMesButtons, .extraMesButtonsHint').removeAttr('style');
524466}
525467
526468function switchReducedMotion() {
527- const value = localStorage.getItem(storage_keys.reduced_motion);
528- power_user.reduced_motion = value === null ? false : value == 'true';
529469 jQuery.fx.off = power_user.reduced_motion;
530470 const overrideDuration = power_user.reduced_motion ? 0 : ANIMATION_DURATION_DEFAULT;
531471 setAnimationDuration(overrideDuration);
@@ -534,8 +474,6 @@ function switchReducedMotion() {
534474}
535475
536476function switchCompactInputArea() {
537- const value = localStorage.getItem(storage_keys.compact_input_area);
538- power_user.compact_input_area = value === null ? true : value == 'true';
539477 $('#send_form').toggleClass('compact', power_user.compact_input_area);
540478 $('#compact_input_area').prop('checked', power_user.compact_input_area);
541479}
@@ -551,8 +489,6 @@ async function switchLabMode() {
551489 }
552490 */
553491 await delay(100);
554- const value = localStorage.getItem(storage_keys.enableLabMode);
555- power_user.enableLabMode = value === null ? false : value == 'true';
556492 $('body').toggleClass('enableLabMode', power_user.enableLabMode);
557493 $('#enableLabMode').prop('checked', power_user.enableLabMode);
558494
@@ -599,8 +535,6 @@ async function switchLabMode() {
599535
600536async function switchZenSliders() {
601537 await delay(100);
602- const value = localStorage.getItem(storage_keys.enableZenSliders);
603- power_user.enableZenSliders = value === null ? false : value == 'true';
604538 $('body').toggleClass('enableZenSliders', power_user.enableZenSliders);
605539 $('#enableZenSliders').prop('checked', power_user.enableZenSliders);
606540
@@ -987,8 +921,6 @@ async function CreateZenSliders(elmnt) {
987921 }
988922}
989923function switchUiMode() {
990- const fastUi = localStorage.getItem(storage_keys.fast_ui_mode);
991- power_user.fast_ui_mode = fastUi === null ? true : fastUi == 'true';
992924 $('body').toggleClass('no-blur', power_user.fast_ui_mode);
993925 $('#fast_ui_mode').prop('checked', power_user.fast_ui_mode);
994926 if (power_user.fast_ui_mode) {
@@ -1038,8 +970,6 @@ function switchMovingUI() {
1038970 $('.drawer-content.maximized').each(function () {
1039971 $(this).find('.inline-drawer-maximize').trigger('click');
1040972 });
1041- const movingUI = localStorage.getItem(storage_keys.movingUI);
1042- power_user.movingUI = movingUI === null ? false : movingUI == 'true';
1043973 $('body').toggleClass('movingUI', power_user.movingUI);
1044974 if (power_user.movingUI === true) {
1045975 initMovingUI();
@@ -1055,9 +985,7 @@ function switchMovingUI() {
1055985 }
1056986}
1057987
1058988function noShadowsapplyNoShadows() {
1059- const noShadows = localStorage.getItem(storage_keys.noShadows);
1060- power_user.noShadows = noShadows === null ? false : noShadows == 'true';
1061989 $('body').toggleClass('noShadows', power_user.noShadows);
1062990 $('#noShadowsmode').prop('checked', power_user.noShadows);
1063991 if (power_user.noShadows) {
@@ -1071,12 +999,9 @@ function noShadows() {
1071999}
10721000
10731001function applyAvatarStyle() {
1074- power_user.avatar_style = Number(localStorage.getItem(storage_keys.avatar_style) ?? avatar_styles.ROUND);
10751002 $('body').toggleClass('big-avatars', power_user.avatar_style === avatar_styles.RECTANGULAR);
10761003 $('body').toggleClass('square-avatars', power_user.avatar_style === avatar_styles.SQUARE);
10771004 $('#avatar_style').val(power_user.avatar_style).prop('selected', true);
1078- //$(`input[name="avatar_style"][value="${power_user.avatar_style}"]`).prop("checked", true);
1079-
10801005}
10811006
10821007function applyChatDisplay() {
@@ -1111,8 +1036,6 @@ function applyChatDisplay() {
11111036}
11121037
11131038function applyChatWidth(type) {
1114- power_user.chat_width = Number(localStorage.getItem(storage_keys.chat_width) ?? 50);
1115-
11161039 if (type === 'forced') {
11171040 let r = document.documentElement;
11181041 r.style.setProperty('--sheldWidth', `${power_user.chat_width}vw`);
@@ -1174,8 +1097,6 @@ async function applyThemeColor(type) {
11741097}
11751098
11761099async function applyCustomCSS() {
1177- power_user.custom_css = String(localStorage.getItem(storage_keys.custom_css) ?? '');
1178-
11791100 $('#customCSS').val(power_user.custom_css);
11801101 var styleId = 'custom-style';
11811102 var style = document.getElementById(styleId);
@@ -1189,32 +1110,26 @@ async function applyCustomCSS() {
11891110}
11901111
11911112async 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);
11941114 $('#blur_strength_counter').val(power_user.blur_strength);
11951115 $('#blur_strength').val(power_user.blur_strength);
1196-
1197-
11981116}
11991117
12001118async 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);
12031120 $('#shadow_width_counter').val(power_user.shadow_width);
12041121 $('#shadow_width').val(power_user.shadow_width);
12051122
12061123}
12071124
12081125async function applyFontScale(type) {
1209-
1210- power_user.font_scale = Number(localStorage.getItem(storage_keys.font_scale) ?? 1);
12111126 //this is to allow forced setting on page load, theme swap, etc
12121127 if (type === 'forced') {
12131128 document.documentElement.style.setProperty('--fontScale', String(power_user.font_scale));
12141129 } else {
12151130 //this is to prevent the slider from updating page in real time
12161131 $('#font_scale').off('mouseup touchend').on('mouseup touchend', () => {
12171132 document.documentElement.style.setProperty('--fontScale', String(power_user.font_scale));
12181133 });
12191134 }
12201135
@@ -1243,64 +1158,55 @@ async function applyTheme(name) {
12431158 {
12441159 key: 'blur_strength',
12451160 action: async () => {
1246- localStorage.setItem(storage_keys.blur_strength, power_user.blur_strength);
12471161 await applyBlurStrength();
12481162 },
12491163 },
12501164 {
12511165 key: 'custom_css',
12521166 action: async () => {
1253- localStorage.setItem(storage_keys.custom_css, power_user.custom_css);
12541167 await applyCustomCSS();
12551168 },
12561169 },
12571170 {
12581171 key: 'shadow_width',
12591172 action: async () => {
1260- localStorage.setItem(storage_keys.shadow_width, power_user.shadow_width);
12611173 await applyShadowWidth();
12621174 },
12631175 },
12641176 {
12651177 key: 'font_scale',
12661178 action: async () => {
1267- localStorage.setItem(storage_keys.font_scale, power_user.font_scale);
12681179 await applyFontScale('forced');
12691180 },
12701181 },
12711182 {
12721183 key: 'fast_ui_mode',
12731184 action: async () => {
1274- localStorage.setItem(storage_keys.fast_ui_mode, power_user.fast_ui_mode);
12751185 switchUiMode();
12761186 },
12771187 },
12781188 {
12791189 key: 'waifuMode',
12801190 action: async () => {
1281- localStorage.setItem(storage_keys.waifuMode, power_user.waifuMode);
12821191 switchWaifuMode();
12831192 },
12841193 },
12851194 {
12861195 key: 'chat_display',
12871196 action: async () => {
1288- localStorage.setItem(storage_keys.chat_display, power_user.chat_display);
12891197 applyChatDisplay();
12901198 },
12911199 },
12921200 {
12931201 key: 'avatar_style',
12941202 action: async () => {
1295- localStorage.setItem(storage_keys.avatar_style, power_user.avatar_style);
12961203 applyAvatarStyle();
12971204 },
12981205 },
12991206 {
13001207 key: 'noShadows',
13011208 action: async () => {
1302- localStorage.setItem(storage_keys.noShadows, power_user.noShadows);
1209+ applyNoShadows();
1303- noShadows();
13041210 },
13051211 },
13061212 {
@@ -1310,78 +1216,66 @@ async function applyTheme(name) {
13101216 if (!power_user.chat_width) {
13111217 power_user.chat_width = 50;
13121218 }
1313-
1314- localStorage.setItem(storage_keys.chat_width, String(power_user.chat_width));
13151219 applyChatWidth('forced');
13161220 },
13171221 },
13181222 {
13191223 key: 'timer_enabled',
13201224 action: async () => {
1321- localStorage.setItem(storage_keys.timer_enabled, Boolean(power_user.timer_enabled));
13221225 switchTimer();
13231226 },
13241227 },
13251228 {
13261229 key: 'timestamps_enabled',
13271230 action: async () => {
1328- localStorage.setItem(storage_keys.timestamps_enabled, Boolean(power_user.timestamps_enabled));
13291231 switchTimestamps();
13301232 },
13311233 },
13321234 {
13331235 key: 'timestamp_model_icon',
13341236 action: async () => {
1335- localStorage.setItem(storage_keys.timestamp_model_icon, Boolean(power_user.timestamp_model_icon));
13361237 switchIcons();
13371238 },
13381239 },
13391240 {
13401241 key: 'message_token_count_enabled',
13411242 action: async () => {
1342- localStorage.setItem(storage_keys.message_token_count_enabled, Boolean(power_user.message_token_count_enabled));
13431243 switchTokenCount();
13441244 },
13451245 },
13461246 {
13471247 key: 'mesIDDisplay_enabled',
13481248 action: async () => {
1349- localStorage.setItem(storage_keys.mesIDDisplay_enabled, Boolean(power_user.mesIDDisplay_enabled));
13501249 switchMesIDDisplay();
13511250 },
13521251 },
13531252 {
13541253 key: 'hideChatAvatars_enabled',
13551254 action: async () => {
1356- localStorage.setItem(storage_keys.hideChatAvatars_enabled, Boolean(power_user.hideChatAvatars_enabled));
13571255 switchHideChatAvatars();
13581256 },
13591257 },
13601258 {
13611259 key: 'expand_message_actions',
13621260 action: async () => {
1363- localStorage.setItem(storage_keys.expand_message_actions, Boolean(power_user.expand_message_actions));
13641261 switchMessageActions();
13651262 },
13661263 },
13671264 {
13681265 key: 'enableZenSliders',
13691266 action: async () => {
1370- localStorage.setItem(storage_keys.enableZenSliders, Boolean(power_user.enableZenSliders));
13711267 switchMessageActions();
13721268 },
13731269 },
13741270 {
13751271 key: 'enableLabMode',
13761272 action: async () => {
1377- localStorage.setItem(storage_keys.enableLabMode, Boolean(power_user.enableLabMode));
13781273 switchMessageActions();
13791274 },
13801275 },
13811276 {
13821277 key: 'hotswap_enabled',
13831278 action: async () => {
1384- localStorage.setItem(storage_keys.hotswap_enabled, Boolean(power_user.hotswap_enabled));
13851279 switchHotswap();
13861280 },
13871281 },
@@ -1402,7 +1296,6 @@ async function applyTheme(name) {
14021296 {
14031297 key: 'reduced_motion',
14041298 action: async () => {
1405- localStorage.setItem(storage_keys.reduced_motion, String(power_user.reduced_motion));
14061299 $('#reduced_motion').prop('checked', power_user.reduced_motion);
14071300 switchReducedMotion();
14081301 },
@@ -1410,7 +1303,6 @@ async function applyTheme(name) {
14101303 {
14111304 key: 'compact_input_area',
14121305 action: async () => {
1413- localStorage.setItem(storage_keys.compact_input_area, String(power_user.compact_input_area));
14141306 $('#compact_input_area').prop('checked', power_user.compact_input_area);
14151307 switchCompactInputArea();
14161308 },
@@ -1465,6 +1357,7 @@ async function showDebugMenu() {
14651357 callGenericPopup(template, POPUP_TYPE.TEXT, '', { wide: true, large: true, allowVerticalScrolling: true });
14661358}
14671359
1360+function applyPowerUserSettings() {
14681361 switchUiMode();
14691362 applyFontScale('forced');
14701363 applyThemeColor();
@@ -1474,7 +1367,7 @@ applyBlurStrength();
14741367 applyShadowWidth();
14751368 applyCustomCSS();
14761369 switchMovingUI();
14771370noShadows applyNoShadows();
14781371 switchHotswap();
14791372 switchTimer();
14801373 switchTimestamps();
@@ -1483,6 +1376,7 @@ switchMesIDDisplay();
14831376 switchHideChatAvatars();
14841377 switchTokenCount();
14851378 switchMessageActions();
1379+}
14861380
14871381function getExampleMessagesBehavior() {
14881382 if (power_user.strip_examples) {
@@ -1545,20 +1439,10 @@ async function loadPowerUserSettings(settings, data) {
15451439 context_presets = data.context;
15461440 }
15471441
15481442 // 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);
15601443 const autoLoadChat = localStorage.getItem(storage_keys.auto_load_chat_legacy);
15611444 const autoConnect = localStorage.getItem(storage_keys.auto_connect_legacy);
1445+ const hideChatAvatars = localStorage.getItem(storage_keys.hideChatAvatars_legacy);
15621446
15631447 if (autoLoadChat) {
15641448 power_user.auto_load_chat = autoLoadChat === 'true';
@@ -1570,22 +1454,10 @@ async function loadPowerUserSettings(settings, data) {
15701454 localStorage.removeItem(storage_keys.auto_connect_legacy);
15711455 }
15721456
1573- power_user.fast_ui_mode = fastUi === null ? true : fastUi == 'true';
1457+ if (hideChatAvatars) {
15741458 power_user.movingUIhideChatAvatars_enabled = movingUIhideChatAvatars === null ? false : movingUI == '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
15901462 if (power_user.chat_display === '') {
15911463 power_user.chat_display = chat_styles.DEFAULT;
@@ -1672,7 +1544,7 @@ async function loadPowerUserSettings(settings, data) {
16721544 $('#messageTimestampsEnabled').prop('checked', power_user.timestamps_enabled);
16731545 $('#messageModelIconEnabled').prop('checked', power_user.timestamp_model_icon);
16741546 $('#mesIDDisplayEnabled').prop('checked', power_user.mesIDDisplay_enabled);
16751547 $('#hideChatAvatarsEndabledhideChatAvatarsEnabled').prop('checked', power_user.hideChatAvatars_enabled);
16761548 $('#prefer_character_prompt').prop('checked', power_user.prefer_character_prompt);
16771549 $('#prefer_character_jailbreak').prop('checked', power_user.prefer_character_jailbreak);
16781550 $('#enableZenSliders').prop('checked', power_user.enableZenSliders).trigger('input');
@@ -3313,10 +3185,8 @@ $(document).ready(() => {
33133185 saveSettingsDebounced();
33143186 });
33153187
3316- // Settings that go to local storage
33173188 $('#fast_ui_mode').change(function () {
33183189 power_user.fast_ui_mode = $(this).prop('checked');
3319- localStorage.setItem(storage_keys.fast_ui_mode, power_user.fast_ui_mode);
33203190 switchUiMode();
33213191 saveSettingsDebounced();
33223192 });
@@ -3329,22 +3199,19 @@ $(document).ready(() => {
33293199
33303200 $('#customCSS').on('input', () => {
33313201 power_user.custom_css = String($('#customCSS').val());
3332- localStorage.setItem(storage_keys.custom_css, power_user.custom_css);
33333202 saveSettingsDebounced();
33343203 applyCustomCSS();
33353204 });
33363205
33373206 $('#movingUImode').change(function () {
33383207 power_user.movingUI = $(this).prop('checked');
3339- localStorage.setItem(storage_keys.movingUI, power_user.movingUI);
33403208 switchMovingUI();
33413209 saveSettingsDebounced();
33423210 });
33433211
33443212 $('#noShadowsmode').change(function () {
33453213 power_user.noShadows = $(this).prop('checked');
3346- localStorage.setItem(storage_keys.noShadows, power_user.noShadows);
3214+ applyNoShadows();
3347- noShadows();
33483215 saveSettingsDebounced();
33493216 });
33503217
@@ -3353,7 +3220,6 @@ $(document).ready(() => {
33533220 $('#avatar_style').on('change', function () {
33543221 const value = $(this).find(':selected').val();
33553222 power_user.avatar_style = Number(value);
3356- localStorage.setItem(storage_keys.avatar_style, power_user.avatar_style);
33573223 applyAvatarStyle();
33583224 saveSettingsDebounced();
33593225 });
@@ -3361,17 +3227,15 @@ $(document).ready(() => {
33613227 $('#chat_display').on('change', function () {
33623228 const value = $(this).find(':selected').val();
33633229 power_user.chat_display = Number(value);
3364- localStorage.setItem(storage_keys.chat_display, power_user.chat_display);
33653230 applyChatDisplay();
33663231 saveSettingsDebounced();
3367-
33683232 });
33693233
33703234 $('#chat_width_slider').on('input', function (e, data) {
33713235 const applyMode = data?.forced ? 'forced' : 'normal';
33723236 power_user.chat_width = Number(e.target.value);
3373- localStorage.setItem(storage_keys.chat_width, power_user.chat_width);
33743237 applyChatWidth(applyMode);
3238+ saveSettingsDebounced();
33753239 setHotswapsDebounced();
33763240 });
33773241
@@ -3401,7 +3265,6 @@ $(document).ready(() => {
34013265 const applyMode = data?.forced ? 'forced' : 'normal';
34023266 power_user.font_scale = Number(e.target.value);
34033267 $('#font_scale_counter').val(power_user.font_scale);
3404- localStorage.setItem(storage_keys.font_scale, power_user.font_scale);
34053268 await applyFontScale(applyMode);
34063269 saveSettingsDebounced();
34073270 });
@@ -3409,7 +3272,6 @@ $(document).ready(() => {
34093272 $('input[name="blur_strength"]').on('input', async function (e) {
34103273 power_user.blur_strength = Number(e.target.value);
34113274 $('#blur_strength_counter').val(power_user.blur_strength);
3412- localStorage.setItem(storage_keys.blur_strength, power_user.blur_strength);
34133275 await applyBlurStrength();
34143276 saveSettingsDebounced();
34153277 });
@@ -3417,7 +3279,6 @@ $(document).ready(() => {
34173279 $('input[name="shadow_width"]').on('input', async function (e) {
34183280 power_user.shadow_width = Number(e.target.value);
34193281 $('#shadow_width_counter').val(power_user.shadow_width);
3420- localStorage.setItem(storage_keys.shadow_width, power_user.shadow_width);
34213282 await applyShadowWidth();
34223283 saveSettingsDebounced();
34233284 });
@@ -3658,36 +3519,36 @@ $(document).ready(() => {
36583519 $('#messageTimerEnabled').on('input', function () {
36593520 const value = !!$(this).prop('checked');
36603521 power_user.timer_enabled = value;
3661- localStorage.setItem(storage_keys.timer_enabled, Boolean(power_user.timer_enabled));
36623522 switchTimer();
3523+ saveSettingsDebounced();
36633524 });
36643525
36653526 $('#messageTimestampsEnabled').on('input', function () {
36663527 const value = !!$(this).prop('checked');
36673528 power_user.timestamps_enabled = value;
3668- localStorage.setItem(storage_keys.timestamps_enabled, Boolean(power_user.timestamps_enabled));
36693529 switchTimestamps();
3530+ saveSettingsDebounced();
36703531 });
36713532
36723533 $('#messageModelIconEnabled').on('input', function () {
36733534 const value = !!$(this).prop('checked');
36743535 power_user.timestamp_model_icon = value;
3675- localStorage.setItem(storage_keys.timestamp_model_icon, Boolean(power_user.timestamp_model_icon));
36763536 switchIcons();
3537+ saveSettingsDebounced();
36773538 });
36783539
36793540 $('#messageTokensEnabled').on('input', function () {
36803541 const value = !!$(this).prop('checked');
36813542 power_user.message_token_count_enabled = value;
3682- localStorage.setItem(storage_keys.message_token_count_enabled, Boolean(power_user.message_token_count_enabled));
36833543 switchTokenCount();
3544+ saveSettingsDebounced();
36843545 });
36853546
36863547 $('#expandMessageActions').on('input', function () {
36873548 const value = !!$(this).prop('checked');
36883549 power_user.expand_message_actions = value;
3689- localStorage.setItem(storage_keys.expand_message_actions, Boolean(power_user.expand_message_actions));
36903550 switchMessageActions();
3551+ saveSettingsDebounced();
36913552 });
36923553
36933554 $('#enableZenSliders').on('input', function () {
@@ -3699,9 +3560,8 @@ $(document).ready(() => {
36993560 return;
37003561 }
37013562 power_user.enableZenSliders = value;
3702- localStorage.setItem(storage_keys.enableZenSliders, Boolean(power_user.enableZenSliders));
3703- saveSettingsDebounced();
37043563 switchZenSliders();
3564+ saveSettingsDebounced();
37053565 });
37063566
37073567 $('#enableLabMode').on('input', function () {
@@ -3714,30 +3574,29 @@ $(document).ready(() => {
37143574 }
37153575
37163576 power_user.enableLabMode = value;
3717- localStorage.setItem(storage_keys.enableLabMode, Boolean(power_user.enableLabMode));
3718- saveSettingsDebounced();
37193577 switchLabMode();
3578+ saveSettingsDebounced();
37203579 });
37213580
37223581 $('#mesIDDisplayEnabled').on('input', function () {
37233582 const value = !!$(this).prop('checked');
37243583 power_user.mesIDDisplay_enabled = value;
3725- localStorage.setItem(storage_keys.mesIDDisplay_enabled, Boolean(power_user.mesIDDisplay_enabled));
37263584 switchMesIDDisplay();
3585+ saveSettingsDebounced();
37273586 });
37283587
37293588 $('#hideChatAvatarsEnabled').on('input', function () {
37303589 const value = !!$(this).prop('checked');
37313590 power_user.hideChatAvatars_enabled = value;
3732- localStorage.setItem(storage_keys.hideChatAvatars_enabled, Boolean(power_user.hideChatAvatars_enabled));
37333591 switchHideChatAvatars();
3592+ saveSettingsDebounced();
37343593 });
37353594
37363595 $('#hotswapEnabled').on('input', function () {
37373596 const value = !!$(this).prop('checked');
37383597 power_user.hotswap_enabled = value;
3739- localStorage.setItem(storage_keys.hotswap_enabled, Boolean(power_user.hotswap_enabled));
37403598 switchHotswap();
3599+ saveSettingsDebounced();
37413600 });
37423601
37433602 $('#prefer_character_prompt').on('input', function () {
@@ -3845,8 +3704,8 @@ $(document).ready(() => {
38453704 $('#ui_mode_select').on('change', function () {
38463705 const value = $(this).find(':selected').val();
38473706 power_user.ui_mode = Number(value);
3848- saveSettingsDebounced();
38493707 switchSimpleMode();
3708+ saveSettingsDebounced();
38503709 });
38513710
38523711 $('#bogus_folders').on('input', function () {
@@ -3950,14 +3809,12 @@ $(document).ready(() => {
39503809
39513810 $('#reduced_motion').on('input', function () {
39523811 power_user.reduced_motion = !!$(this).prop('checked');
3953- localStorage.setItem(storage_keys.reduced_motion, String(power_user.reduced_motion));
39543812 switchReducedMotion();
39553813 saveSettingsDebounced();
39563814 });
39573815
39583816 $('#compact_input_area').on('input', function () {
39593817 power_user.compact_input_area = !!$(this).prop('checked');
3960- localStorage.setItem(storage_keys.compact_input_area, String(power_user.compact_input_area));
39613818 switchCompactInputArea();
39623819 saveSettingsDebounced();
39633820 });
public/scripts/showdown-underscore.js+1 -1
@@ -8,7 +8,7 @@ export const markdownUnderscoreExt = () => {
88
99 return [{
1010 type: 'output',
1111 regex: new RegExp('(<code(?:\\s+[^>]*)?>[\\s\\S]*?<\\/code>)|\\b(?<!\\S_)_(?!_)([^_\\n]+.*?)(?<!_)_(?!_)\\w)b', 'g'),
1212 replace: function(match, codeContent, italicContent) {
1313 if (codeContent) {
1414 // If it's inside <code> tags, return unchanged
public/scripts/slash-commands.js+6 -5
@@ -2147,12 +2147,13 @@ async function generateRawCallback(args, value) {
21472147 }
21482148}
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+ */
21502156async function generateCallback(args, value) {
2151- if (!value) {
2152- console.warn('WARN: No argument provided for /gen command');
2153- return;
2154- }
2155-
21562157 // Prevent generate recursion
21572158 $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true }));
21582159 const lock = isTrueBoolean(args?.lock);
public/scripts/tags.js+9 -1
@@ -445,7 +445,11 @@ export function getTagKeyForEntity(entityOrKey) {
445445 }
446446
447447 // 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+
449453 if (character) {
450454 x = character.avatar;
451455 }
@@ -722,7 +726,11 @@ async function importTags(character, { importSetting = null } = {}) {
722726 const tagsToImport = tagNamesToImport.map(tag => getTag(tag, { createNew: true }));
723727 const added = addTagsToEntity(tagsToImport, character.avatar);
724728
729+ if (added) {
725730 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
727735 return added;
728736}
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>
6 \ 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>
4 \ 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>
1 \ No newline at end of file
public/scripts/tokenizers.js+27 -23
@@ -27,6 +27,7 @@ export const tokenizers = {
2727 CLAUDE: 11,
2828 LLAMA3: 12,
2929 GEMMA: 13,
30+ JAMBA: 14,
3031 BEST_MATCH: 99,
3132};
3233
@@ -36,6 +37,7 @@ export const SENTENCEPIECE_TOKENIZERS = [
3637 tokenizers.YI,
3738 tokenizers.LLAMA3,
3839 tokenizers.GEMMA,
40+ tokenizers.JAMBA,
3941 // uncomment when NovelAI releases Kayra and Clio weights, lol
4042 //tokenizers.NERD,
4143 //tokenizers.NERD2,
@@ -98,6 +100,11 @@ const TOKENIZER_URLS = {
98100 decode: '/api/tokenizers/gemma/decode',
99101 count: '/api/tokenizers/gemma/encode',
100102 },
103+ [tokenizers.JAMBA]: {
104+ encode: '/api/tokenizers/jamba/encode',
105+ decode: '/api/tokenizers/jamba/decode',
106+ count: '/api/tokenizers/jamba/encode',
107+ },
101108 [tokenizers.API_TEXTGENERATIONWEBUI]: {
102109 encode: '/api/tokenizers/remote/textgenerationwebui/encode',
103110 count: '/api/tokenizers/remote/textgenerationwebui/encode',
@@ -164,7 +171,7 @@ export function getAvailableTokenizers() {
164171 tokenizerId: Number(tokenizerOption.value),
165172 tokenizerKey: Object.entries(tokenizers).find(([_, value]) => value === Number(tokenizerOption.value))[0].toLocaleLowerCase(),
166173 tokenizerName: tokenizerOption.text,
167174 }));
168175}
169176
170177/**
@@ -280,6 +287,12 @@ export function getTokenizerBestMatch(forApi) {
280287 if (model.includes('gemma')) {
281288 return tokenizers.GEMMA;
282289 }
290+ if (model.includes('yi')) {
291+ return tokenizers.YI;
292+ }
293+ if (model.includes('jamba')) {
294+ return tokenizers.JAMBA;
295+ }
283296 }
284297
285298 return tokenizers.LLAMA;
@@ -497,6 +510,7 @@ export function getTokenizerModel() {
497510 const mistralTokenizer = 'mistral';
498511 const yiTokenizer = 'yi';
499512 const gemmaTokenizer = 'gemma';
513+ const jambaTokenizer = 'jamba';
500514
501515 // Assuming no one would use it for different models.. right?
502516 if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) {
@@ -562,12 +576,19 @@ export function getTokenizerModel() {
562576 else if (oai_settings.openrouter_model.includes('GPT-NeoXT')) {
563577 return gpt2Tokenizer;
564578 }
579+ else if (oai_settings.openrouter_model.includes('jamba')) {
580+ return jambaTokenizer;
581+ }
565582 }
566583
567584 if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) {
568585 return gemmaTokenizer;
569586 }
570587
588+ if (oai_settings.chat_completion_source == chat_completion_sources.AI21) {
589+ return jambaTokenizer;
590+ }
591+
571592 if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
572593 return claudeTokenizer;
573594 }
@@ -626,16 +647,7 @@ export function getTokenizerModel() {
626647 * @deprecated Use countTokensOpenAIAsync instead.
627648 */
628649export 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- }
639651 const cacheObject = getTokenCacheObject();
640652
641653 if (!Array.isArray(messages)) {
@@ -647,7 +659,7 @@ export function countTokensOpenAI(messages, full = false) {
647659 for (const message of messages) {
648660 const model = getTokenizerModel();
649661
650662 if (model === 'claude' || shouldTokenizeAI21 || shouldTokenizeGoogle) {
651663 full = true;
652664 }
653665
@@ -687,16 +699,7 @@ export function countTokensOpenAI(messages, full = false) {
687699 * @returns {Promise<number>} Token count.
688700 */
689701export 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- }
700703 const cacheObject = getTokenCacheObject();
701704
702705 if (!Array.isArray(messages)) {
@@ -708,7 +711,7 @@ export async function countTokensOpenAIAsync(messages, full = false) {
708711 for (const message of messages) {
709712 const model = getTokenizerModel();
710713
711714 if (model === 'claude' || shouldTokenizeAI21 || shouldTokenizeGoogle) {
712715 full = true;
713716 }
714717
@@ -838,6 +841,7 @@ function getTextgenAPITokenizationParams(str) {
838841 url: getTextGenServer(),
839842 legacy_api: textgen_settings.legacy_api && (textgen_settings.type === OOBA || textgen_settings.type === APHRODITE),
840843 vllm_model: textgen_settings.vllm_model,
844+ aphrodite_model: textgen_settings.aphrodite_model,
841845 };
842846}
843847
public/scripts/world-info.js+30 -2
@@ -73,6 +73,7 @@ export let world_info_match_whole_words = false;
7373export let world_info_use_group_scoring = false;
7474export let world_info_character_strategy = world_info_insertion_strategy.character_first;
7575export let world_info_budget_cap = 0;
76+export let world_info_max_recursion_steps = 0;
7677const saveWorldDebounced = debounce(async (name, data) => await _save(name, data), debounce_timeout.relaxed);
7778const saveSettingsDebounced = debounce(() => {
7879 Object.assign(world_info, { globalSelect: selected_world_info });
@@ -710,6 +711,7 @@ export function getWorldInfoSettings() {
710711 world_info_character_strategy,
711712 world_info_budget_cap,
712713 world_info_use_group_scoring,
714+ world_info_max_recursion_steps,
713715 };
714716}
715717
@@ -796,6 +798,8 @@ export function setWorldInfoSettings(settings, data) {
796798 world_info_budget_cap = Number(settings.world_info_budget_cap);
797799 if (settings.world_info_use_group_scoring !== undefined)
798800 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
800804 // Migrate old settings
801805 if (world_info_budget > 100) {
@@ -844,6 +848,9 @@ export function setWorldInfoSettings(settings, data) {
844848 $('#world_info_budget_cap').val(world_info_budget_cap);
845849 $('#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+
847854 world_names = data.world_names?.length ? data.world_names : [];
848855
849856 // Add to existing selected WI if it exists
@@ -2459,7 +2466,7 @@ async function getWorldEntry(name, data, entry) {
24592466 if (!isMobile()) {
24602467 $(characterFilter).select2({
24612468 width: '100%',
24622469 placeholder: 'AllTie this entry to specific characters willor pullcharacters fromwith thisspecific entry.tags',
24632470 allowClear: true,
24642471 closeOnSelect: false,
24652472 });
@@ -3723,6 +3730,12 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
37233730 console.debug(`[WI] --- SEARCHING ENTRIES (on ${sortedEntries.length} entries) ---`);
37243731
37253732 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+
37263739 // Track how many times the loop has run. May be useful for debugging.
37273740 count++;
37283741
@@ -4762,8 +4775,13 @@ jQuery(() => {
47624775
47634776 $('#world_info_min_activations').on('input', function () {
47644777 world_info_min_activations = Number($(this).val());
47654778 $('#world_info_min_activations_counter').val($(this).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 {
47664783 saveSettings();
4784+ }
47674785 });
47684786
47694787 $('#world_info_min_activations_depth_max').on('input', function () {
@@ -4819,6 +4837,16 @@ jQuery(() => {
48194837 saveSettings();
48204838 });
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+
48224850 $('#world_button').on('click', async function (event) {
48234851 const chid = $('#set_character_world').data('chid');
48244852
server.js+2 -2
@@ -817,7 +817,7 @@ function createHttpsServer(url) {
817817 }, app);
818818 server.on('error', reject);
819819 server.on('listening', resolve);
820820 server.listen(url.port || 443, url.hostname);
821821 });
822822}
823823
@@ -832,7 +832,7 @@ function createHttpServer(url) {
832832 const server = http.createServer(app);
833833 server.on('error', reject);
834834 server.on('listening', resolve);
835835 server.listen(url.port || 80, url.hostname);
836836 });
837837}
838838
src/cohere-stream.js+126 -0
@@ -0,0 +1,126 @@
1+const 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+ */
8+class 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+
95+function 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+
126+module.exports = CohereStream;
src/endpoints/backends/chat-completions.js+63 -93
@@ -1,11 +1,11 @@
11const express = require('express');
22const fetch = require('node-fetch').default;
3-const Readable = require('stream').Readable;
43
54const { jsonParser } = require('../../express-common');
65const { CHAT_COMPLETION_SOURCES, GEMINI_SAFETY, BISON_SAFETY, OPENROUTER_HEADERS } = require('../../constants');
76const { forwardFetchResponse, getConfigValue, tryParse, uuidv4, mergeObjectWithYaml, excludeKeysByYaml, color } = require('../../util');
87const { convertClaudeMessages, convertGooglePrompt, convertTextCompletionPrompt, convertCohereMessages, convertMistralMessages, convertCohereTools, convertAI21Messages } = require('../../prompt-converters');
8+const CohereStream = require('../../cohere-stream');
99
1010const { readSecret, SECRET_KEYS } = require('../secrets');
1111const { getTokenizerModel, getSentencepiceTokenizer, getTiktokenTokenizer, sentencepieceTokenizers, TEXT_COMPLETION_MODELS } = require('../tokenizers');
@@ -19,6 +19,7 @@ const API_GROQ = 'https://api.groq.com/openai/v1';
1919const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';
2020const API_01AI = 'https://api.01.ai/v1';
2121const API_BLOCKENTROPY = 'https://api.blockentropy.ai/v1';
22+const API_AI21 = 'https://api.ai21.com/studio/v1';
2223
2324/**
2425 * Applies a post-processing step to the generated messages.
@@ -40,52 +41,30 @@ function postProcessPrompt(messages, type, charName, userName) {
4041/**
4142 * Ollama strikes back. Special boy #2's steaming routine.
4243 * Wrap this abomination into proper SSE stream, again.
4344 * @param {import('node-fetch').Response} jsonStream JSON stream
4445 * @param {import('express').Request} request Express request
4546 * @param {import('express').Response} response Express response
4647 * @returns {Promise<any>} Nothing valuable
4748 */
4849async function parseCohereStream(jsonStream, request, response) {
4950 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- }
6154 if (json.message) {
6255 const message = json.message || 'Unknown error';
6356 const chunk = { error: { message: message } };
6457 response.write(`data: ${JSON.stringify(chunk)}\n\n`);
65- partialData = '';
66- break;
6758 } else if (json.event_type === 'text-generation') {
6859 const text = json.text || '';
6960 const chunk = { choices: [{ text }] };
7061 response.write(`data: ${JSON.stringify(chunk)}\n\n`);
71- partialData = '';
72- } else {
73- partialData = '';
74- break;
7562 }
7663 }
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', () => {
8565 console.log('Streaming request finished');
8666 response.write('data: [DONE]\n\n');
8767 response.end();
88- });
8968 } catch (error) {
9069 console.log('Error forwarding streaming response:', error);
9170 if (!response.headersSent) {
@@ -233,7 +212,7 @@ async function sendScaleRequest(request, response) {
233212
234213 if (!generateResponse.ok) {
235214 console.log(`Scale API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
236215 return response.status(generateResponse.status500).send({ error: true });
237216 }
238217
239218 const generateResponseJson = await generateResponse.json();
@@ -259,7 +238,7 @@ async function sendMakerSuiteRequest(request, response) {
259238 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);
260239
261240 if (!request.body.reverse_proxy && !apiKey) {
262241 console.log('MakerSuiteGoogle AI Studio API key is missing.');
263242 return response.status(400).send({ error: true });
264243 }
265244
@@ -326,7 +305,7 @@ async function sendMakerSuiteRequest(request, response) {
326305 }
327306
328307 const body = isGemini ? getGeminiBody() : getBisonBody();
329308 console.log('MakerSuiteGoogle AI Studio request:', body);
330309
331310 try {
332311 const controller = new AbortController();
@@ -362,7 +341,7 @@ async function sendMakerSuiteRequest(request, response) {
362341 }
363342 } else {
364343 if (!generateResponse.ok) {
365344 console.log(`MakerSuiteGoogle AI Studio API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
366345 return response.status(generateResponse.status).send({ error: true });
367346 }
368347
@@ -370,7 +349,7 @@ async function sendMakerSuiteRequest(request, response) {
370349
371350 const candidates = generateResponseJson?.candidates;
372351 if (!candidates || candidates.length === 0) {
373352 let message = 'MakerSuiteGoogle AI Studio API returned no candidate';
374353 console.log(message, generateResponseJson);
375354 if (generateResponseJson?.promptFeedback?.blockReason) {
376355 message += `\nPrompt was blocked due to : ${generateResponseJson.promptFeedback.blockReason}`;
@@ -381,19 +360,19 @@ async function sendMakerSuiteRequest(request, response) {
381360 const responseContent = candidates[0].content ?? candidates[0].output;
382361 const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.[0]?.text;
383362 if (!responseText) {
384363 let message = 'MakerSuiteGoogle AI Studio Candidate text empty';
385364 console.log(message, generateResponseJson);
386365 return response.send({ error: { message } });
387366 }
388367
389368 console.log('MakerSuiteGoogle AI Studio response:', responseText);
390369
391370 // Wrap it back to OAI format
392371 const reply = { choices: [{ 'message': { 'content': responseText } }] };
393372 return response.send(reply);
394373 }
395374 } catch (error) {
396375 console.log('Error communicating with MakerSuiteGoogle AI Studio API: ', error);
397376 if (!response.headersSent) {
398377 return response.status(500).send({ error: true });
399378 }
@@ -413,6 +392,16 @@ async function sendAI21Request(request, response) {
413392 request.socket.on('close', function () {
414393 controller.abort();
415394 });
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+ };
416405 const options = {
417406 method: 'POST',
418407 headers: {
@@ -420,59 +409,35 @@ async function sendAI21Request(request, response) {
420409 'content-type': 'application/json',
421410 Authorization: `Bearer ${readSecret(request.user.directories, SECRET_KEYS.AI21)}`,
422411 },
423412 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- }),
457413 signal: controller.signal,
458414 };
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);
465422 } 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+ }
467440 }
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-
476441}
477442
478443/**
@@ -531,10 +496,10 @@ async function sendMistralAIRequest(request, response) {
531496 forwardFetchResponse(generateResponse, response);
532497 } else {
533498 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 };
537502 return response.status(generateResponse.status === 401 ? 500 : generateResponse.status).send({ error: true }errorJson);
538503 }
539504 const generateResponseJson = await generateResponse.json();
540505 console.log('MistralAI response:', generateResponseJson);
@@ -607,6 +572,11 @@ async function sendCohereRequest(request, response) {
607572 search_queries_only: false,
608573 };
609574
575+ const canDoSafetyMode = String(request.body.model).endsWith('08-2024');
576+ if (canDoSafetyMode) {
577+ requestBody.safety_mode = 'NONE';
578+ }
579+
610580 console.log('Cohere request:', requestBody);
611581
612582 const config = {
@@ -623,15 +593,15 @@ async function sendCohereRequest(request, response) {
623593 const apiUrl = API_COHERE + '/chat';
624594
625595 if (request.body.stream) {
626596 const stream = await global.fetch(apiUrl, config);
627597 parseCohereStream(stream, request, response);
628598 } else {
629599 const generateResponse = await fetch(apiUrl, config);
630600 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 };
634604 return response.status(generateResponse.status === 401 ? 500 : generateResponse.status).send({ error: true }errorJson);
635605 }
636606 const generateResponseJson = await generateResponse.json();
637607 console.log('Cohere response:', generateResponseJson);
src/endpoints/google.js+1 -1
@@ -44,7 +44,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
4444
4545 if (!result.ok) {
4646 const error = await result.json();
4747 console.log(`MakerSuiteGoogle AI Studio API returned error: ${result.status} ${result.statusText}`, error);
4848 return response.status(result.status).send({ error: true });
4949 }
5050
src/endpoints/novelai.js+5 -5
@@ -6,6 +6,7 @@ const { readAllChunks, extractFileFromZipBuffer, forwardFetchResponse } = requir
66const { jsonParser } = require('../express-common');
77
88const API_NOVELAI = 'https://api.novelai.net';
9+const TEXT_NOVELAI = 'https://text.novelai.net';
910const IMAGE_NOVELAI = 'https://image.novelai.net';
1011
1112// Ban bracket generation, plus defaults
@@ -155,7 +156,7 @@ router.post('/generate', jsonParser, async function (req, res) {
155156 'repetition_penalty_slope': req.body.repetition_penalty_slope,
156157 'repetition_penalty_frequency': req.body.repetition_penalty_frequency,
157158 'repetition_penalty_presence': req.body.repetition_penalty_presence,
158159 'repetition_penalty_whitelist': isNewModel ? repPenaltyAllowList.flat() : null,
159160 'top_a': req.body.top_a,
160161 'top_p': req.body.top_p,
161162 'top_k': req.body.top_k,
@@ -178,9 +179,7 @@ router.post('/generate', jsonParser, async function (req, res) {
178179 };
179180
180181 // Tells the model to stop generation at '>'
181182 if ('theme_textadventure' === req.body.prefix && isNewModel) {
182- (true === req.body.model.includes('clio') ||
183- true === req.body.model.includes('kayra'))) {
184183 data.parameters.eos_token_id = 49405;
185184 }
186185
@@ -193,7 +192,8 @@ router.post('/generate', jsonParser, async function (req, res) {
193192 };
194193
195194 try {
196195 const urlbaseURL = req.body.streamingmodel.includes('kayra') ? `${API_NOVELAI}/ai/generate-stream`TEXT_NOVELAI : `${API_NOVELAI}/ai/generate`;
196+ const url = req.body.streaming ? `${baseURL}/ai/generate-stream` : `${baseURL}/ai/generate`;
197197 const response = await fetch(url, { method: 'POST', timeout: 0, ...args });
198198
199199 if (req.body.streaming) {
src/endpoints/settings.js+2 -0
@@ -9,6 +9,7 @@ const { jsonParser } = require('../express-common');
99const { getAllUserHandles, getUserDirectories } = require('../users');
1010
1111const ENABLE_EXTENSIONS = getConfigValue('enableExtensions', true);
12+const ENABLE_EXTENSIONS_AUTO_UPDATE = getConfigValue('enableExtensionsAutoUpdate', true);
1213const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false);
1314
1415// 10 minutes
@@ -268,6 +269,7 @@ router.post('/get', jsonParser, (request, response) => {
268269 instruct,
269270 context,
270271 enable_extensions: ENABLE_EXTENSIONS,
272+ enable_extensions_auto_update: ENABLE_EXTENSIONS_AUTO_UPDATE,
271273 enable_accounts: ENABLE_ACCOUNTS,
272274 });
273275});
src/endpoints/stable-diffusion.js+41 -0
@@ -991,11 +991,52 @@ blockentropy.post('/generate', jsonParser, async (request, response) => {
991991});
992992
993993
994+const huggingface = express.Router();
995+
996+huggingface.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+
9941034router.use('/comfy', comfy);
9951035router.use('/together', together);
9961036router.use('/drawthings', drawthings);
9971037router.use('/pollinations', pollinations);
9981038router.use('/stability', stability);
9991039router.use('/blockentropy', blockentropy);
1040+router.use('/huggingface', huggingface);
10001041
10011042module.exports = { router };
src/endpoints/tokenizers.js+29 -48
@@ -144,6 +144,7 @@ const spp_nerd_v2 = new SentencePieceTokenizer('src/tokenizers/nerdstash_v2.mode
144144const spp_mistral = new SentencePieceTokenizer('src/tokenizers/mistral.model');
145145const spp_yi = new SentencePieceTokenizer('src/tokenizers/yi.model');
146146const spp_gemma = new SentencePieceTokenizer('src/tokenizers/gemma.model');
147+const spp_jamba = new SentencePieceTokenizer('src/tokenizers/jamba.model');
147148const claude_tokenizer = new WebTokenizer('src/tokenizers/claude.json');
148149const llama3_tokenizer = new WebTokenizer('src/tokenizers/llama3.json');
149150
@@ -154,6 +155,7 @@ const sentencepieceTokenizers = [
154155 'mistral',
155156 'yi',
156157 'gemma',
158+ 'jamba',
157159];
158160
159161/**
@@ -186,6 +188,10 @@ function getSentencepiceTokenizer(model) {
186188 return spp_gemma;
187189 }
188190
191+ if (model.includes('jamba')) {
192+ return spp_jamba;
193+ }
194+
189195 return null;
190196}
191197
@@ -322,6 +328,10 @@ function getTokenizerModel(requestModel) {
322328 return 'gemma';
323329 }
324330
331+ if (requestModel.includes('jamba')) {
332+ return 'jamba';
333+ }
334+
325335 // default
326336 return 'gpt-3.5-turbo';
327337}
@@ -537,59 +547,13 @@ function createWebTokenizerDecodingHandler(tokenizer) {
537547
538548const router = express.Router();
539549
540-router.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-
563-router.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-
587550router.post('/llama/encode', jsonParser, createSentencepieceEncodingHandler(spp_llama));
588551router.post('/nerdstash/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd));
589552router.post('/nerdstash_v2/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd_v2));
590553router.post('/mistral/encode', jsonParser, createSentencepieceEncodingHandler(spp_mistral));
591554router.post('/yi/encode', jsonParser, createSentencepieceEncodingHandler(spp_yi));
592555router.post('/gemma/encode', jsonParser, createSentencepieceEncodingHandler(spp_gemma));
556+router.post('/jamba/encode', jsonParser, createSentencepieceEncodingHandler(spp_jamba));
593557router.post('/gpt2/encode', jsonParser, createTiktokenEncodingHandler('gpt2'));
594558router.post('/claude/encode', jsonParser, createWebTokenizerEncodingHandler(claude_tokenizer));
595559router.post('/llama3/encode', jsonParser, createWebTokenizerEncodingHandler(llama3_tokenizer));
@@ -599,6 +563,7 @@ router.post('/nerdstash_v2/decode', jsonParser, createSentencepieceDecodingHandl
599563router.post('/mistral/decode', jsonParser, createSentencepieceDecodingHandler(spp_mistral));
600564router.post('/yi/decode', jsonParser, createSentencepieceDecodingHandler(spp_yi));
601565router.post('/gemma/decode', jsonParser, createSentencepieceDecodingHandler(spp_gemma));
566+router.post('/jamba/decode', jsonParser, createSentencepieceDecodingHandler(spp_jamba));
602567router.post('/gpt2/decode', jsonParser, createTiktokenDecodingHandler('gpt2'));
603568router.post('/claude/decode', jsonParser, createWebTokenizerDecodingHandler(claude_tokenizer));
604569router.post('/llama3/decode', jsonParser, createWebTokenizerDecodingHandler(llama3_tokenizer));
@@ -637,6 +602,11 @@ router.post('/openai/encode', jsonParser, async function (req, res) {
637602 return handler(req, res);
638603 }
639604
605+ if (queryModel.includes('jamba')) {
606+ const handler = createSentencepieceEncodingHandler(spp_jamba);
607+ return handler(req, res);
608+ }
609+
640610 const model = getTokenizerModel(queryModel);
641611 const handler = createTiktokenEncodingHandler(model);
642612 return handler(req, res);
@@ -680,6 +650,11 @@ router.post('/openai/decode', jsonParser, async function (req, res) {
680650 return handler(req, res);
681651 }
682652
653+ if (queryModel.includes('jamba')) {
654+ const handler = createSentencepieceDecodingHandler(spp_jamba);
655+ return handler(req, res);
656+ }
657+
683658 const model = getTokenizerModel(queryModel);
684659 const handler = createTiktokenDecodingHandler(model);
685660 return handler(req, res);
@@ -731,6 +706,11 @@ router.post('/openai/count', jsonParser, async function (req, res) {
731706 return res.send({ 'token_count': num_tokens });
732707 }
733708
709+ if (model === 'jamba') {
710+ num_tokens = await countSentencepieceArrayTokens(spp_jamba, req.body);
711+ return res.send({ 'token_count': num_tokens });
712+ }
713+
734714 const tokensPerName = queryModel.includes('gpt-3.5-turbo-0301') ? -1 : 1;
735715 const tokensPerMessage = queryModel.includes('gpt-3.5-turbo-0301') ? 4 : 3;
736716 const tokensPadding = 3;
@@ -812,6 +792,7 @@ router.post('/remote/textgenerationwebui/encode', jsonParser, async function (re
812792 const baseUrl = String(request.body.url);
813793 const legacyApi = Boolean(request.body.legacy_api);
814794 const vllmModel = String(request.body.vllm_model) || '';
795+ const aphroditeModel = String(request.body.aphrodite_model) || '';
815796
816797 try {
817798 const args = {
@@ -847,7 +828,7 @@ router.post('/remote/textgenerationwebui/encode', jsonParser, async function (re
847828 break;
848829 case TEXTGEN_TYPES.APHRODITE:
849830 url += '/v1/tokenize';
850831 args.body = JSON.stringify({ 'model': aphroditeModel, 'prompt': text });
851832 break;
852833 default:
853834 url += '/v1/internal/encode';
src/endpoints/translate.js+1 -1
@@ -110,6 +110,7 @@ router.post('/google', jsonParser, async (request, response) => {
110110});
111111
112112router.post('/yandex', jsonParser, async (request, response) => {
113+ try {
113114 const chunks = request.body.chunks;
114115 const lang = request.body.lang;
115116
@@ -130,7 +131,6 @@ router.post('/yandex', jsonParser, async (request, response) => {
130131
131132 console.log('Input text: ' + inputText);
132133
133- try {
134134 const result = await fetch(`https://translate.yandex.net/api/v1/tr.json/translate?ucid=${ucid}&srv=android&format=text`, {
135135 method: 'POST',
136136 body: params,
src/prompt-converters.js+76 -0
@@ -267,10 +267,13 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '
267267 'gemini-1.5-flash',
268268 'gemini-1.5-flash-latest',
269269 'gemini-1.5-flash-001',
270+ 'gemini-1.5-flash-exp-0827',
271+ 'gemini-1.5-flash-8b-exp-0827',
270272 'gemini-1.5-pro',
271273 'gemini-1.5-pro-latest',
272274 'gemini-1.5-pro-001',
273275 'gemini-1.5-pro-exp-0801',
276+ 'gemini-1.5-pro-exp-0827',
274277 'gemini-1.0-pro-vision-latest',
275278 'gemini-pro-vision',
276279 ];
@@ -368,6 +371,78 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '
368371}
369372
370373/**
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+ */
379+function 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+/**
371446 * Convert a prompt from the ChatML objects to the format used by MistralAI.
372447 * @param {object[]} messages Array of messages
373448 * @param {string} charName Character name
@@ -520,4 +595,5 @@ module.exports = {
520595 convertCohereMessages,
521596 convertMistralMessages,
522597 convertCohereTools,
598+ convertAI21Messages,
523599};
src/tokenizers/jamba.model+0 -0

Binary file

src/transformers.mjs+1 -1
@@ -35,7 +35,7 @@ const tasks = {
3535 defaultModel: 'Cohee/fooocus_expansion-onnx',
3636 pipeline: null,
3737 configField: 'extras.promptExpansionModel',
3838 quantized: truefalse,
3939 },
4040 'automatic-speech-recognition': {
4141 defaultModel: 'Xenova/whisper-small',
src/util.js+5 -4
@@ -34,8 +34,9 @@ function getConfig() {
3434 CACHED_CONFIG = config;
3535 return config;
3636 } catch (error) {
3737 console.warnerror(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);
3940 }
4041}
4142
@@ -298,8 +299,8 @@ const color = {
298299 * @returns {string} A UUIDv4 string
299300 */
300301function uuidv4() {
301302 if ('crypto' in global && 'randomUUID' in global.crypto) {
302303 return global.crypto.randomUUID();
303304 }
304305 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
305306 const r = Math.random() * 16 | 0;
src/vectors/makersuite-vectors.js+4 -4
@@ -23,8 +23,8 @@ async function getMakerSuiteVector(text, directories) {
2323 const key = readSecret(directories, SECRET_KEYS.MAKERSUITE);
2424
2525 if (!key) {
2626 console.log('No MakerSuiteGoogle AI Studio key found');
2727 throw new Error('No MakerSuiteGoogle AI Studio key found');
2828 }
2929
3030 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
4040 if (!response.ok) {
4141 const text = await response.text();
4242 console.log('MakerSuiteGoogle AI Studio request failed', response.statusText, text);
4343 throw new Error('MakerSuiteGoogle AI Studio request failed');
4444 }
4545
4646 const data = await response.json();
tests/package-lock.json+3 -4
@@ -4513,10 +4513,9 @@
45134513 }
45144514 },
45154515 "node_modules/micromatch": {
45164516 "version": "4.0.78",
45174517 "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.78.tgz",
45184518 "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqOPXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/QPzEJQxsYsEiFCKo2BA==",
4519- "license": "MIT",
45204519 "dependencies": {
45214520 "braces": "^3.0.3",
45224521 "picomatch": "^2.3.1"