Merge branch 'staging' into pr/Cohee1207/2711

127a46e42092bcecae67618650f9b239aed1950a

RossAscends <124905043+RossAscends@users.noreply.github.com>

27 files changed, +612 -408Showing whitespace changes
default/content/presets/openai/Default.json+1 -4
@@ -8,7 +8,7 @@
8 "openrouter_force_instruct": false,8 "openrouter_force_instruct": false,
9 "openrouter_group_models": false,9 "openrouter_group_models": false,
10 "openrouter_sort_models": "alphabetically",10 "openrouter_sort_models": "alphabetically",
11 "ai21_model": "j2-ultra",11 "ai21_model": "jamba-1.5-large",
12 "mistralai_model": "mistral-large-latest",12 "mistralai_model": "mistral-large-latest",
13 "custom_model": "",13 "custom_model": "",
14 "custom_url": "",14 "custom_url": "",
@@ -19,7 +19,6 @@
19 "temperature": 1,19 "temperature": 1,
20 "frequency_penalty": 0,20 "frequency_penalty": 0,
21 "presence_penalty": 0,21 "presence_penalty": 0,
22 "count_penalty": 0,
23 "top_p": 1,22 "top_p": 1,
24 "top_k": 0,23 "top_k": 0,
25 "top_a": 0,24 "top_a": 0,
@@ -233,8 +232,6 @@
233 "assistant_prefill": "",232 "assistant_prefill": "",
234 "assistant_impersonation": "",233 "assistant_impersonation": "",
235 "human_sysprompt_message": "Let's get started. Please generate your response based on the information and instructions provided above.",234 "human_sysprompt_message": "Let's get started. Please generate your response based on the information and instructions provided above.",
236 "use_ai21_tokenizer": false,
237 "use_google_tokenizer": false,
238 "claude_use_sysprompt": false,235 "claude_use_sysprompt": false,
239 "use_alt_scale": false,236 "use_alt_scale": false,
240 "squash_system_messages": false,237 "squash_system_messages": false,
default/content/settings.json+11 -5
@@ -196,7 +196,15 @@
196 "enableLabMode": false,196 "enableLabMode": false,
197 "enableZenSliders": false,197 "enableZenSliders": false,
198 "ui_mode": 1,198 "ui_mode": 1,
199 "forbid_external_media": true199 "forbid_external_media": true,
200 "stscript": {
201 "parser": {
202 "flags": {
203 "1": true,
204 "2": true
205 }
206 }
207 }
200 },208 },
201 "extension_settings": {209 "extension_settings": {
202 "apiUrl": "http://localhost:5100",210 "apiUrl": "http://localhost:5100",
@@ -452,7 +460,6 @@
452 "temp_openai": 1.0,460 "temp_openai": 1.0,
453 "freq_pen_openai": 0,461 "freq_pen_openai": 0,
454 "pres_pen_openai": 0,462 "pres_pen_openai": 0,
455 "count_pen": 0,
456 "top_p_openai": 1,463 "top_p_openai": 1,
457 "top_k_openai": 0,464 "top_k_openai": 0,
458 "stream_openai": true,465 "stream_openai": true,
@@ -614,7 +621,7 @@
614 "wi_format": "{0}",621 "wi_format": "{0}",
615 "openai_model": "gpt-4-turbo",622 "openai_model": "gpt-4-turbo",
616 "claude_model": "claude-3-5-sonnet-20240620",623 "claude_model": "claude-3-5-sonnet-20240620",
617 "ai21_model": "j2-ultra",624 "ai21_model": "jamba-1.5-large",
618 "windowai_model": "",625 "windowai_model": "",
619 "openrouter_model": "OR_Website",626 "openrouter_model": "OR_Website",
620 "jailbreak_system": true,627 "jailbreak_system": true,
@@ -625,7 +632,6 @@
625 "show_external_models": false,632 "show_external_models": false,
626 "proxy_password": "",633 "proxy_password": "",
627 "assistant_prefill": "",634 "assistant_prefill": "",
628 "assistant_impersonation": "",635 "assistant_impersonation": ""
629 "use_ai21_tokenizer": false
630 }636 }
631}637}
public/index.html+44 -42
@@ -5,7 +5,7 @@
5 <title>SillyTavern</title>5 <title>SillyTavern</title>
6 <base href="/">6 <base href="/">
7 <meta charset="utf-8">7 <meta charset="utf-8">
8 <meta name="viewport" content="width=device-width, viewport-fit=cover, initial-scale=1, maximum-scale=1.0, user-scalable=no">8 <meta name="viewport" content="width=device-width, viewport-fit=cover, initial-scale=1, maximum-scale=1.0, user-scalable=no, interactive-widget=resizes-content">
9 <meta name="apple-mobile-web-app-capable" content="yes">9 <meta name="apple-mobile-web-app-capable" content="yes">
10 <meta name="darkreader-lock">10 <meta name="darkreader-lock">
11 <meta name="robots" content="noindex, nofollow" />11 <meta name="robots" content="noindex, nofollow" />
@@ -436,7 +436,7 @@
436 </div>436 </div>
437 </div>437 </div>
438 </div>438 </div>
439 <div data-newbie-hidden class="range-block" data-source="openai,openrouter,ai21,custom,cohere,perplexity,groq">439 <div data-newbie-hidden class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq">
440 <div class="range-block-title" data-i18n="Frequency Penalty">440 <div class="range-block-title" data-i18n="Frequency Penalty">
441 Frequency Penalty441 Frequency Penalty
442 </div>442 </div>
@@ -449,7 +449,7 @@
449 </div>449 </div>
450 </div>450 </div>
451 </div>451 </div>
452 <div data-newbie-hidden class="range-block" data-source="openai,openrouter,ai21,custom,cohere,perplexity,groq">452 <div data-newbie-hidden class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq">
453 <div class="range-block-title" data-i18n="Presence Penalty">453 <div class="range-block-title" data-i18n="Presence Penalty">
454 Presence Penalty454 Presence Penalty
455 </div>455 </div>
@@ -462,20 +462,7 @@
462 </div>462 </div>
463 </div>463 </div>
464 </div>464 </div>
465 <div data-newbie-hidden class="range-block" data-source="ai21">465 <div data-newbie-hidden class="range-block" data-source="claude,openrouter,makersuite,cohere,perplexity">
466 <div class="range-block-title" data-i18n="Count Penalty">
467 Count Penalty
468 </div>
469 <div class="range-block-range-and-counter">
470 <div class="range-block-range">
471 <input type="range" id="count_pen" name="volume" min="0" max="1" step="0.01">
472 </div>
473 <div class="range-block-counter">
474 <input type="number" min="0" max="1" step="0.01" data-for="count_pen" id="count_pen_counter">
475 </div>
476 </div>
477 </div>
478 <div data-newbie-hidden class="range-block" data-source="claude,openrouter,ai21,makersuite,cohere,perplexity">
479 <div class="range-block-title" data-i18n="Top K">466 <div class="range-block-title" data-i18n="Top K">
480 Top K467 Top K
481 </div>468 </div>
@@ -1291,6 +1278,28 @@
1291 </div>1278 </div>
1292 </div>1279 </div>
1293 </div>1280 </div>
1281
1282 <div data-newbie-hidden data-tg-type="koboldcpp" id="xtc_block" class="wide100p">
1283 <h4 class="wide100p textAlignCenter">
1284 <label data-i18n="Exclude Top Choices (XTC)">Exclude Top Choices (XTC)</label>
1285 <a href="https://github.com/oobabooga/text-generation-webui/pull/6335" target="_blank">
1286 <div class=" fa-solid fa-circle-info opacity50p"></div>
1287 </a>
1288 </h4>
1289 <div class="flex-container flexFlowRow gap10px flexShrink">
1290 <div class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
1291 <small data-i18n="Threshold">Threshold</small>
1292 <input class="neo-range-slider" type="range" id="xtc_threshold_textgenerationwebui" name="volume" min="0" max="0.5" step="0.01" />
1293 <input class="neo-range-input" type="number" min="0" max="0.5" step="0.01" data-for="xtc_threshold_textgenerationwebui" id="xtc_threshold_counter_textgenerationwebui">
1294 </div>
1295 <div class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
1296 <small data-i18n="Probability">Probability</small>
1297 <input class="neo-range-slider" type="range" id="xtc_probability_textgenerationwebui" name="volume" min="0" max="1" step="0.01" />
1298 <input class="neo-range-input" type="number" min="0" max="1" step="0.01" data-for="xtc_probability_textgenerationwebui" id="xtc_probability_counter_textgenerationwebui">
1299 </div>
1300 </div>
1301 </div>
1302
1294 <!-- Enable for llama.cpp when the PR is merged: https://github.com/ggerganov/llama.cpp/pull/6839 -->1303 <!-- Enable for llama.cpp when the PR is merged: https://github.com/ggerganov/llama.cpp/pull/6839 -->
1295 <div data-newbie-hidden data-tg-type="ooba, koboldcpp" id="dryBlock" class="wide100p">1304 <div data-newbie-hidden data-tg-type="ooba, koboldcpp" id="dryBlock" class="wide100p">
1296 <h4 class="wide100p textAlignCenter" title="DRY penalizes tokens that would extend the end of the input into a sequence that has previously occurred in the input. Set multiplier to 0 to disable." data-i18n="[title]DRY_Repetition_Penalty_desc">1305 <h4 class="wide100p textAlignCenter" title="DRY penalizes tokens that would extend the end of the input into a sequence that has previously occurred in the input. Set multiplier to 0 to disable." data-i18n="[title]DRY_Repetition_Penalty_desc">
@@ -1484,11 +1493,11 @@
1484 <div id="banned_tokens_block_ooba" data-newbie-hidden class="wide100p">1493 <div id="banned_tokens_block_ooba" data-newbie-hidden class="wide100p">
1485 <hr data-newbie-hidden class="width100p">1494 <hr data-newbie-hidden class="width100p">
1486 <h4 class="range-block-title justifyCenter">1495 <h4 class="range-block-title justifyCenter">
1487 <span data-i18n="Banned Tokens">Banned Tokens</span>1496 <span data-i18n="Banned Tokens">Banned Tokens/Strings</span>
1488 <div class="margin5 fa-solid fa-circle-info opacity50p " data-i18n="[title]LLaMA / Mistral / Yi models only" title="LLaMA / Mistral / Yi models only. Make sure to select an appropriate tokenizer first.&#13;Sequences you don't want to appear in the output.&#13;One per line. Text or [token ids].&#13;Most tokens have a leading space. Use token counter if unsure."></div>1497 <div class="margin5 fa-solid fa-circle-info opacity50p " data-i18n="[title]LLaMA / Mistral / Yi models only" title="Enter sequences you don't want to appear in the output.&#13;Unquoted text will be tokenized in the back end and banned as tokens.&#13;[token ids] will be banned as-is.&#13;Most tokens have a leading space. Use token counter (with the correct tokenizer selected first!) if you are unsure.&#13;Enclose text in double quotes to ban the entire string as a set.&#13;Quoted Strings and [Token ids] must be on their own line."></div>
1489 </h4>1498 </h4>
1490 <div class="wide100p">1499 <div class="wide100p">
1491 <textarea id="banned_tokens_textgenerationwebui" class="text_pole textarea_compact" name="banned_tokens_textgenerationwebui" rows="3" data-i18n="[placeholder]Example: some text [42, 69, 1337]" placeholder="Example:&#10;some text&#10;[42, 69, 1337]"></textarea>1500 <textarea id="banned_tokens_textgenerationwebui" class="text_pole textarea_compact" name="banned_tokens_textgenerationwebui" rows="3" data-i18n="[placeholder]Example: some text [42, 69, 1337]" placeholder='some text as tokens&#10;[420, 69, 1337]&#10;"Some verbatim string"'></textarea>
1492 </div>1501 </div>
1493 </div>1502 </div>
1494 <div class="range-block wide100p">1503 <div class="range-block wide100p">
@@ -1791,22 +1800,6 @@
1791 </select>1800 </select>
1792 </div>1801 </div>
1793 </div>1802 </div>
1794 <div class="range-block" data-source="ai21">
1795 <label for="use_ai21_tokenizer" title="Use AI21 Tokenizer" data-i18n="[title]Use AI21 Tokenizer" class="checkbox_label widthFreeExpand">
1796 <input id="use_ai21_tokenizer" type="checkbox" /><span data-i18n="Use AI21 Tokenizer">Use AI21 Tokenizer</span>
1797 </label>
1798 <div class="toggle-description justifyLeft">
1799 <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>
1800 </div>
1801 </div>
1802 <div class="range-block" data-source="makersuite">
1803 <label for="use_google_tokenizer" title="Use Google Tokenizer" data-i18n="[title]Use Google Tokenizer" class="checkbox_label widthFreeExpand">
1804 <input id="use_google_tokenizer" type="checkbox" /><span data-i18n="Use Google Tokenizer">Use Google Tokenizer</span>
1805 </label>
1806 <div class="toggle-description justifyLeft">
1807 <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>
1808 </div>
1809 </div>
1810 <div class="range-block" data-source="makersuite">1803 <div class="range-block" data-source="makersuite">
1811 <label for="use_makersuite_sysprompt" class="checkbox_label widthFreeExpand">1804 <label for="use_makersuite_sysprompt" class="checkbox_label widthFreeExpand">
1812 <input id="use_makersuite_sysprompt" type="checkbox" />1805 <input id="use_makersuite_sysprompt" type="checkbox" />
@@ -1852,7 +1845,7 @@
1852 <div class="fa-solid fa-clock-rotate-left"></div>1845 <div class="fa-solid fa-clock-rotate-left"></div>
1853 </div>1846 </div>
1854 </div>1847 </div>
1855 <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>1848 <textarea id="claude_human_sysprompt_textarea" class="text_pole textarea_compact" rows="4" data-i18n="[placeholder]Human message" placeholder="Human message, instruction, etc.&#10;Adds nothing when empty, i.e. requires a new prompt with the role 'user'."></textarea>
1856 </div>1849 </div>
1857 </div>1850 </div>
1858 </div>1851 </div>
@@ -2791,10 +2784,12 @@
2791 <div>2784 <div>
2792 <h4 data-i18n="AI21 Model">AI21 Model</h4>2785 <h4 data-i18n="AI21 Model">AI21 Model</h4>
2793 <select id="model_ai21_select">2786 <select id="model_ai21_select">
2794 <optgroup label="Latest">2787 <optgroup label="Jamba 1.5">
2795 <option value="j2-ultra">j2-ultra</option>2788 <option value="jamba-1.5-mini">jamba-1.5-mini</option>
2796 <option value="j2-mid">j2-mid</option>2789 <option value="jamba-1.5-large">jamba-1.5-large</option>
2797 <option value="j2-light">j2-light</option>2790 </optgroup>
2791 <optgroup label="Jamba-Instruct (Deprecated)">
2792 <option value="jamba-instruct-preview">jamba-instruct-preview</option>
2798 </optgroup>2793 </optgroup>
2799 </select>2794 </select>
2800 </div>2795 </div>
@@ -2824,8 +2819,11 @@
2824 </optgroup>2819 </optgroup>
2825 <optgroup label="Subversions">2820 <optgroup label="Subversions">
2826 <option value="gemini-1.5-pro-exp-0801">Gemini 1.5 Pro Experiment 2024-08-01</option>2821 <option value="gemini-1.5-pro-exp-0801">Gemini 1.5 Pro Experiment 2024-08-01</option>
2822 <option value="gemini-1.5-pro-exp-0827">Gemini 1.5 Pro Experiment 2024-08-27</option>
2827 <option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option>2823 <option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option>
2828 <option value="gemini-1.5-pro-001">Gemini 1.5 Pro [001]</option>2824 <option value="gemini-1.5-pro-001">Gemini 1.5 Pro [001]</option>
2825 <option value="gemini-1.5-flash-exp-0827">Gemini 1.5 Flash Experiment 2024-08-27</option>
2826 <option value="gemini-1.5-flash-8b-exp-0827">Gemini 1.5 Flash 8B Experiment 2024-08-27</option>
2829 <option value="gemini-1.5-flash-latest">Gemini 1.5 Flash [latest]</option>2827 <option value="gemini-1.5-flash-latest">Gemini 1.5 Flash [latest]</option>
2830 <option value="gemini-1.5-flash-001">Gemini 1.5 Flash [001]</option>2828 <option value="gemini-1.5-flash-001">Gemini 1.5 Flash [001]</option>
2831 <option value="gemini-1.0-pro-latest">Gemini 1.0 Pro [latest]</option>2829 <option value="gemini-1.0-pro-latest">Gemini 1.0 Pro [latest]</option>
@@ -2955,6 +2953,8 @@
2955 <option value="command">command</option>2953 <option value="command">command</option>
2956 <option value="command-r">command-r</option>2954 <option value="command-r">command-r</option>
2957 <option value="command-r-plus">command-r-plus</option>2955 <option value="command-r-plus">command-r-plus</option>
2956 <option value="command-r-08-2024">command-r-08-2024</option>
2957 <option value="command-r-plus-08-2024">command-r-plus-08-2024</option>
2958 </optgroup>2958 </optgroup>
2959 <optgroup label="Nightly">2959 <optgroup label="Nightly">
2960 <option value="command-light-nightly">command-light-nightly</option>2960 <option value="command-light-nightly">command-light-nightly</option>
@@ -4012,7 +4012,7 @@
4012 <input id="movingUImode" type="checkbox" />4012 <input id="movingUImode" type="checkbox" />
4013 <small data-i18n="Movable UI Panels">MovingUI&nbsp;<i class="fa-solid fa-desktop"></i></small>4013 <small data-i18n="Movable UI Panels">MovingUI&nbsp;<i class="fa-solid fa-desktop"></i></small>
4014 </label>4014 </label>
4015 <div data-newbie-hidden id="movingUIreset" title="Reset MovingUI panel sizes/locations." class="menu_button margin0" data-i18n="[title]Reset MovingUI panel sizes/locations."><i class=" fa-solid fa-recycle margin-r5"></i> Reset</div>4015 <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>
4016 </div>4016 </div>
4017 <div data-newbie-hidden id="MovingUI-presets-block" class="flex-container alignitemscenter">4017 <div data-newbie-hidden id="MovingUI-presets-block" class="flex-container alignitemscenter">
4018 <div class="flex-container alignItemsFlexEnd">4018 <div class="flex-container alignItemsFlexEnd">
@@ -4503,6 +4503,7 @@
4503 <div data-newbie-hidden>4503 <div data-newbie-hidden>
4504 <label for="persona_description_position" data-i18n="Position:">Position:</label>4504 <label for="persona_description_position" data-i18n="Position:">Position:</label>
4505 <select id="persona_description_position">4505 <select id="persona_description_position">
4506 <option value="9" data-i18n="None (disabled)">None (disabled)</option>
4506 <option value="0" data-i18n="In Story String / Prompt Manager">In Story String / Prompt Manager</option>4507 <option value="0" data-i18n="In Story String / Prompt Manager">In Story String / Prompt Manager</option>
4507 <option value="2" data-i18n="Top of Author's Note">Top of Author's Note</option>4508 <option value="2" data-i18n="Top of Author's Note">Top of Author's Note</option>
4508 <option value="3" data-i18n="Bottom of Author's Note">Bottom of Author's Note</option>4509 <option value="3" data-i18n="Bottom of Author's Note">Bottom of Author's Note</option>
@@ -6488,6 +6489,7 @@
6488 <script src="lib/jquery-cookie-1.4.1.min.js"></script>6489 <script src="lib/jquery-cookie-1.4.1.min.js"></script>
6489 <script src="lib/jquery.ui.touch-punch.min.js"></script>6490 <script src="lib/jquery.ui.touch-punch.min.js"></script>
6490 <script src="lib/showdown.min.js"></script>6491 <script src="lib/showdown.min.js"></script>
6492 <script src="lib/showdown-patch.js"></script>
6491 <script src="lib/showdown-katex.min.js"></script>6493 <script src="lib/showdown-katex.min.js"></script>
6492 <script src="lib/popper.js"></script>6494 <script src="lib/popper.js"></script>
6493 <script src="lib/purify.min.js"></script>6495 <script src="lib/purify.min.js"></script>
public/lib/showdown-patch.js+24 -0
@@ -0,0 +1,24 @@
1showdown.subParser('unhashHTMLSpans', function (text, options, globals) {
2 'use strict';
3 text = globals.converter._dispatch('unhashHTMLSpans.before', text, options, globals);
4
5 for (var i = 0; i < globals.gHtmlSpans.length; ++i) {
6 var repText = globals.gHtmlSpans[i],
7 // limiter to prevent infinite loop (assume 10 as limit for recurse)
8 limit = 0;
9
10 while (/¨C(\d+)C/.test(repText)) {
11 var num = RegExp.$1;
12 repText = repText.replace('¨C' + num + 'C', globals.gHtmlSpans[num]);
13 if (limit === 10000) {
14 console.error('maximum nesting of 10000 spans reached!!!');
15 break;
16 }
17 ++limit;
18 }
19 text = text.replace('¨C' + i + 'C', repText);
20 }
21
22 text = globals.converter._dispatch('unhashHTMLSpans.after', text, options, globals);
23 return text;
24});
public/locales/ru-ru.json+54 -7
@@ -43,7 +43,7 @@
43 "Smoothing Factor": "Коэффициент сглаживания",43 "Smoothing Factor": "Коэффициент сглаживания",
44 "No Repeat Ngram Size": "Размер no_repeat_ngram",44 "No Repeat Ngram Size": "Размер no_repeat_ngram",
45 "Min Length": "Мин. длина",45 "Min Length": "Мин. длина",
46 "Alternative server URL (leave empty to use the default value).": "Альтернативный URL сервера (оставьте пустым для стандартного значения)",46 "Alternative server URL (leave empty to use the default value).": "URL альтернативного сервера (оставьте пустым для стандартного значения)",
47 "Remove your real OAI API Key from the API panel BEFORE typing anything into this box": "Удалите свой личный OAI API Key из панели API, и ТОЛЬКО ПОСЛЕ ЭТОГО вводите что-то сюда",47 "Remove your real OAI API Key from the API panel BEFORE typing anything into this box": "Удалите свой личный OAI API Key из панели API, и ТОЛЬКО ПОСЛЕ ЭТОГО вводите что-то сюда",
48 "We cannot provide support for problems encountered while using an unofficial OpenAI proxy": "Мы не сможем предоставить помощь с проблемами, с которыми вы столкнетесь при использовании неофициальных прокси для OpenAI",48 "We cannot provide support for problems encountered while using an unofficial OpenAI proxy": "Мы не сможем предоставить помощь с проблемами, с которыми вы столкнетесь при использовании неофициальных прокси для OpenAI",
49 "Context Size (tokens)": "Размер контекста (в токенах)",49 "Context Size (tokens)": "Размер контекста (в токенах)",
@@ -186,7 +186,7 @@
186 "Generate only one line per request": "Генерировать одну строку на запрос",186 "Generate only one line per request": "Генерировать одну строку на запрос",
187 "Misc. Settings": "Доп. настройки",187 "Misc. Settings": "Доп. настройки",
188 "Auto-Continue": "Авто-продолжение",188 "Auto-Continue": "Авто-продолжение",
189 "Collapse Consecutive Newlines": "Сворачивать последовательные новые строки",189 "Collapse Consecutive Newlines": "Сворачивать неск. новых строк в одну",
190 "Allow for Chat Completion APIs": "Разрешить для Chat Completion API",190 "Allow for Chat Completion APIs": "Разрешить для Chat Completion API",
191 "Target length (tokens)": "Целевая длина (в токенах)",191 "Target length (tokens)": "Целевая длина (в токенах)",
192 "World Info": "Информация о мире",192 "World Info": "Информация о мире",
@@ -373,7 +373,7 @@
373 "Prefer Character Card Prompt": "Приоритет промпту из карточки персонажа",373 "Prefer Character Card Prompt": "Приоритет промпту из карточки персонажа",
374 "Prefer Character Card Jailbreak": "Приоритет джейлбрейку из карточки персонажа",374 "Prefer Character Card Jailbreak": "Приоритет джейлбрейку из карточки персонажа",
375 "Press Send to continue": "Кнопка отправки продолжает сообщение",375 "Press Send to continue": "Кнопка отправки продолжает сообщение",
376 "Quick 'Continue' button": "Кнопка быстрого продолжения",376 "Quick 'Continue' button": "Быстрое продолжение",
377 "Log prompts to console": "Выводить промпты в консоль",377 "Log prompts to console": "Выводить промпты в консоль",
378 "Never resize avatars": "Не менять размер аватарок",378 "Never resize avatars": "Не менять размер аватарок",
379 "Show avatar filenames": "Показывать названия файлов аватарок",379 "Show avatar filenames": "Показывать названия файлов аватарок",
@@ -495,7 +495,7 @@
495 "(Write a comma-separated list of tags)": "(Список тегов через запятую)",495 "(Write a comma-separated list of tags)": "(Список тегов через запятую)",
496 "(A brief description of the personality)": "(Краткое описание личности)",496 "(A brief description of the personality)": "(Краткое описание личности)",
497 "(Circumstances and context of the interaction)": "(Обстоятельства и контекст этого диалога)",497 "(Circumstances and context of the interaction)": "(Обстоятельства и контекст этого диалога)",
498 "(Examples of chat dialog. Begin each example with START on a new line.)": "(Примеры диалога. Начинайте каждый пример с START или новой линией.)",498 "(Examples of chat dialog. Begin each example with START on a new line.)": "(Примеры диалога. Начинайте каждый пример с START или новой строкой.)",
499 "Type here...": "Пишите здесь...",499 "Type here...": "Пишите здесь...",
500 "Comma separated (required)": "Через запятую (обязательное поле)",500 "Comma separated (required)": "Через запятую (обязательное поле)",
501 "What this keyword should mean to the AI, sent verbatim": "Что это ключевое слово должно означать для ИИ, отправляется дословно",501 "What this keyword should mean to the AI, sent verbatim": "Что это ключевое слово должно означать для ИИ, отправляется дословно",
@@ -552,7 +552,7 @@
552 "Click to set a new User Name": "Нажмите, чтобы задать новое имя пользователя.",552 "Click to set a new User Name": "Нажмите, чтобы задать новое имя пользователя.",
553 "Click to lock your selected persona to the current chat. Click again to remove the lock.": "Нажмите, чтобы закрепить выбранную персону для текущего чата. Нажмите еще раз, чтобы открепить.",553 "Click to lock your selected persona to the current chat. Click again to remove the lock.": "Нажмите, чтобы закрепить выбранную персону для текущего чата. Нажмите еще раз, чтобы открепить.",
554 "Click to set user name for all messages": "Нажмите, чтобы задать имя пользователя для всех сообщений.",554 "Click to set user name for all messages": "Нажмите, чтобы задать имя пользователя для всех сообщений.",
555 "Create a dummy persona": "Создать персону-болванку",555 "Create a dummy persona": "Создать пустую персону",
556 "Character Management": "Управление персонажами",556 "Character Management": "Управление персонажами",
557 "Locked = Character Management panel will stay open": "Закреплено = Панель управление персонажами останется открытой",557 "Locked = Character Management panel will stay open": "Закреплено = Панель управление персонажами останется открытой",
558 "Select/Create Characters": "Выбрать/Создать персонажа",558 "Select/Create Characters": "Выбрать/Создать персонажа",
@@ -625,7 +625,7 @@
625 "Automatically select a background based on the chat context": "Автоматический выбор фона в зависимости от контекста чата",625 "Automatically select a background based on the chat context": "Автоматический выбор фона в зависимости от контекста чата",
626 "Filter": "Фильтр",626 "Filter": "Фильтр",
627 "Exclude message from prompts": "Исключить сообщение из промпта",627 "Exclude message from prompts": "Исключить сообщение из промпта",
628 "Include message in prompts": "Включить сообщение в подсказки",628 "Include message in prompts": "Включить сообщение в промпт",
629 "Create checkpoint": "Создать чекпоинт",629 "Create checkpoint": "Создать чекпоинт",
630 "Create Branch": "Создать ветку",630 "Create Branch": "Создать ветку",
631 "Embed file or image": "Вставить файл или изображение",631 "Embed file or image": "Вставить файл или изображение",
@@ -1661,5 +1661,52 @@
1661 "ext_regex_ai_output_desc": "Сообщения, полученные от API",1661 "ext_regex_ai_output_desc": "Сообщения, полученные от API",
1662 "ext_regex_sts_desc": "Сообщения, отправленные с помощью команд STscript",1662 "ext_regex_sts_desc": "Сообщения, отправленные с помощью команд STscript",
1663 "ext_regex_wi_desc": "Содержимое лорбуков и миров. Для работы требует включения флажка \"Только промпт\"!",1663 "ext_regex_wi_desc": "Содержимое лорбуков и миров. Для работы требует включения флажка \"Только промпт\"!",
1664 "ext_regex_only_format_display_desc": "История чата не изменится, замена будет осуществляться только в отображаемом сообщении (в UI)"1664 "ext_regex_only_format_display_desc": "История чата не изменится, замена будет осуществляться только в отображаемом сообщении (в UI)",
1665 "Prefer Character Card Instructions": "Приоритет инструкциям из карточек",
1666 "If checked and the character card contains a Post-History Instructions override, use that instead": "Если в карточке персонажа имеется собственная инструкция после истории, в промпт попадёт именно она",
1667 "Auto-select Input Text": "Автовыделение вводимого текста",
1668 "Enable auto-select of input text in some text fields when clicking/selecting them. Applies to popup input textboxes, and possible other custom input fields.": "Автоматически выделять вводимый текст в некоторых текстовых полях при клике/выборе. Работает для вспл. окон и различных кастомных полей ввода.",
1669 "Reset MovingUI panel sizes/locations.": "Сбросить расположение и размеры панелей MovingUI.",
1670 "mui_reset": "Сброс",
1671 "Quick 'Impersonate' button": "Быстрое перевоплощение",
1672 "Show a button in the input area to ask the AI to impersonate your character for a single message": "Показать в поле ввода кнопку, по нажатии на которую ИИ сгенерирует одно сообщение от лица вашего персонажа.",
1673 "Separators as Stop Strings": "Разделители как стоп-строки",
1674 "Names as Stop Strings": "Имена как стоп-строки",
1675 "Add Character and User names to a list of stopping strings.": "Добавлять имена персонажа и пользователя в список стоп-строк.",
1676 "Allow Post-History Instructions": "Разрешить инструкции после истории",
1677 "context_allow_post_history_instructions": "Добавлять в конец промпта инструкции после истории. Работает только при наличии таких инструкций в карточке И при включенной опции ''Приоритет инструкциям из карточек''.\nНЕ РЕКОМЕНДУЕТСЯ ДЛЯ МОДЕЛЕЙ TEXT COMPLETION, МОЖЕТ ПОРТИТЬ ВЫХОДНОЙ ТЕКСТ.",
1678 "First User Prefix": "Первый префикс пользователя",
1679 "Inserted before the first User's message.": "Вставляется перед первым сообщением пользователя.",
1680 "Last User Prefix": "Последний префикс пользователя",
1681 "instruct_last_input_sequence": "Вставляется перед последним сообщением пользователя.",
1682 "Inserted before a User message and as a last prompt line when impersonating.": "Вставляется перед сообщением пользователя в качестве последней строки промпта при перевоплощении.",
1683 "Inserted before a System (added by slash commands or extensions) message.": "Вставляется перед сообщением системы (может быть добавлено слэш-командой или расширением).",
1684 "Load Asset List": "Загрузить список ресурсов",
1685 "Never add character names.": "Не вставлять имя персонажа.",
1686 "Don't add character names unless necessary.": "Вставлять имя персонажа только когда это необходимо.",
1687 "character_names_none": "Не добавлять имена персонажей в качестве префикса. Может повредить качеству ответов в групповых чатах, используйте с осторожностью.",
1688 "Auxiliary": "Вспомогательный",
1689 "Post-History Instructions": "Инструкции после истории",
1690 "Current persona updated": "Текущая персона изменена",
1691 "Your messages will now be sent as ${0}": "Ваши сообщения будут отправляться от лица ${0}",
1692 "Copied!": "Скопировано!",
1693 "Are you sure you want to delete this message?": "Вы точно хотите удалить это сообщение?",
1694 "Delete Message": "Удалить сообщение",
1695 "Delete Swipe": "Удалить свайп",
1696 "Could not get a reply from API. Check your connection settings / API key and try again.": "Не удалось получить ответ от API. Проверьте настройки соединения и API-ключ и повторите попытку.",
1697 "Connecting To Proxy": "Подключение к прокси",
1698 "Are you sure you want to connect to the following proxy URL?": "Вы точно хотите соединиться с прокси по этому адресу?",
1699 "API connection successful!": "Соединение с API установлено!",
1700 "Proxy Saved": "Прокси сохранена",
1701 "Proxy Deleted": "Прокси удалена",
1702 "Could not find proxy with name '${0}'": "Не удалось найти прокси с названием '${0}'",
1703 "Proxy preset '${0}' not found in proxies array.": "Пресет с названием '${0}' не найден в списке прокси.",
1704 "Please wait for the previous request to complete.": "Пожалуйста, дождитесь окончания обработки предыдущего запроса.",
1705 "Start new chat?": "Начать новый чат?",
1706 "If necessary, you can later restore this chat file from the /backups folder": "При необходимости этот чат можно будет восстановить из папки /backups",
1707 "Also delete the current chat file": "Также удалить текущий чат",
1708 "chat_rename_1": "Введите новое имя чата:",
1709 "chat_rename_2": "!!Не используйте имя уже существующего файла, это приведёт к ошибке!!",
1710 "chat_rename_3": "Будут разрушены связи между чатами-чекпоинтами.",
1711 "chat_rename_4": "Расширение '.jsonl' дописывать не нужно."
1665}1712}
public/script.js+53 -56
@@ -488,14 +488,6 @@ let default_user_name = 'User';
488export let name1 = default_user_name;488export let name1 = default_user_name;
489export let name2 = 'SillyTavern System';489export let name2 = 'SillyTavern System';
490export let chat = [];490export let chat = [];
491let safetychat = [
492 {
493 name: systemUserName,
494 is_user: false,
495 create_date: 0,
496 mes: 'You deleted a character/chat and arrived back here for safety reasons! Pick another character!',
497 },
498];
499let chatSaveTimeout;491let chatSaveTimeout;
500let importFlashTimeout;492let importFlashTimeout;
501export let isChatSaving = false;493export let isChatSaving = false;
@@ -594,6 +586,17 @@ export const extension_prompt_roles = {
594586
595export const MAX_INJECTION_DEPTH = 1000;587export const MAX_INJECTION_DEPTH = 1000;
596588
589const SAFETY_CHAT = [
590 {
591 name: systemUserName,
592 force_avatar: system_avatar,
593 is_system: true,
594 is_user: false,
595 create_date: 0,
596 mes: 'You deleted a character/chat and arrived back here for safety reasons! Pick another character!',
597 },
598];
599
597export let system_messages = {};600export let system_messages = {};
598601
599async function getSystemMessages() {602async function getSystemMessages() {
@@ -2192,7 +2195,7 @@ export function addCopyToCodeBlocks(messageElement) {
2192 codeBlocks.get(i).appendChild(copyButton);2195 codeBlocks.get(i).appendChild(copyButton);
2193 copyButton.addEventListener('pointerup', function (event) {2196 copyButton.addEventListener('pointerup', function (event) {
2194 navigator.clipboard.writeText(codeBlocks.get(i).innerText);2197 navigator.clipboard.writeText(codeBlocks.get(i).innerText);
2195 toastr.info('Copied!', '', { timeOut: 2000 });2198 toastr.info(t`Copied!`, '', { timeOut: 2000 });
2196 });2199 });
2197 }2200 }
2198 }2201 }
@@ -2705,7 +2708,7 @@ function addPersonaDescriptionExtensionPrompt() {
2705 const INJECT_TAG = 'PERSONA_DESCRIPTION';2708 const INJECT_TAG = 'PERSONA_DESCRIPTION';
2706 setExtensionPrompt(INJECT_TAG, '', extension_prompt_types.IN_PROMPT, 0);2709 setExtensionPrompt(INJECT_TAG, '', extension_prompt_types.IN_PROMPT, 0);
27072710
2708 if (!power_user.persona_description) {2711 if (!power_user.persona_description || power_user.persona_description_position === persona_description_positions.NONE) {
2709 return;2712 return;
2710 }2713 }
27112714
@@ -2826,7 +2829,7 @@ export function getCharacterCardFields() {
2826}2829}
28272830
2828export function isStreamingEnabled() {2831export function isStreamingEnabled() {
2829 const noStreamSources = [chat_completion_sources.SCALE, chat_completion_sources.AI21];2832 const noStreamSources = [chat_completion_sources.SCALE];
2830 return ((main_api == 'openai' && oai_settings.stream_openai && !noStreamSources.includes(oai_settings.chat_completion_source) && !(oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE && oai_settings.google_model.includes('bison')))2833 return ((main_api == 'openai' && oai_settings.stream_openai && !noStreamSources.includes(oai_settings.chat_completion_source) && !(oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE && oai_settings.google_model.includes('bison')))
2831 || (main_api == 'kobold' && kai_settings.streaming_kobold && kai_flags.can_use_streaming)2834 || (main_api == 'kobold' && kai_settings.streaming_kobold && kai_flags.can_use_streaming)
2832 || (main_api == 'novel' && nai_settings.streaming_novel)2835 || (main_api == 'novel' && nai_settings.streaming_novel)
@@ -3726,7 +3729,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3726 const storyStringParams = {3729 const storyStringParams = {
3727 description: description,3730 description: description,
3728 personality: personality,3731 personality: personality,
3729 persona: persona,3732 persona: power_user.persona_description_position == persona_description_positions.IN_PROMPT ? persona : '',
3730 scenario: scenario,3733 scenario: scenario,
3731 system: isInstruct ? system : '',3734 system: isInstruct ? system : '',
3732 char: name2,3735 char: name2,
@@ -3760,7 +3763,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3760 }3763 }
37613764
3762 let examplesString = '';3765 let examplesString = '';
3763 let chatString = '';3766 let chatString = addChatsPreamble(addChatsSeparator(''));
3764 let cyclePrompt = '';3767 let cyclePrompt = '';
37653768
3766 async function getMessagesTokenCount() {3769 async function getMessagesTokenCount() {
@@ -3769,10 +3772,10 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3769 storyString,3772 storyString,
3770 afterScenarioAnchor,3773 afterScenarioAnchor,
3771 examplesString,3774 examplesString,
3775 userAlignmentMessage,
3772 chatString,3776 chatString,
3773 quiet_prompt,3777 modifyLastPromptLine(''),
3774 cyclePrompt,3778 cyclePrompt,
3775 userAlignmentMessage,
3776 ].join('').replace(/\r/gm, '');3779 ].join('').replace(/\r/gm, '');
3777 return getTokenCountAsync(encodeString, power_user.token_padding);3780 return getTokenCountAsync(encodeString, power_user.token_padding);
3778 }3781 }
@@ -3803,8 +3806,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3803 }3806 }
38043807
3805 tokenCount += await getTokenCountAsync(item.replace(/\r/gm, ''));3808 tokenCount += await getTokenCountAsync(item.replace(/\r/gm, ''));
3806 chatString = item + chatString;
3807 if (tokenCount < this_max_context) {3809 if (tokenCount < this_max_context) {
3810 chatString = chatString + item;
3808 arrMes[index] = item;3811 arrMes[index] = item;
3809 lastAddedIndex = Math.max(lastAddedIndex, index);3812 lastAddedIndex = Math.max(lastAddedIndex, index);
3810 } else {3813 } else {
@@ -3830,8 +3833,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3830 }3833 }
38313834
3832 tokenCount += await getTokenCountAsync(item.replace(/\r/gm, ''));3835 tokenCount += await getTokenCountAsync(item.replace(/\r/gm, ''));
3833 chatString = item + chatString;
3834 if (tokenCount < this_max_context) {3836 if (tokenCount < this_max_context) {
3837 chatString = chatString + item;
3835 arrMes[i] = item;3838 arrMes[i] = item;
3836 lastAddedIndex = Math.max(lastAddedIndex, i);3839 lastAddedIndex = Math.max(lastAddedIndex, i);
3837 } else {3840 } else {
@@ -4028,15 +4031,16 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4028 async function checkPromptSize() {4031 async function checkPromptSize() {
4029 console.debug('---checking Prompt size');4032 console.debug('---checking Prompt size');
4030 setPromptString();4033 setPromptString();
4034 const jointMessages = mesSend.map((e) => `${e.extensionPrompts.join('')}${e.message}`).join('');
4031 const prompt = [4035 const prompt = [
4032 beforeScenarioAnchor,4036 beforeScenarioAnchor,
4033 storyString,4037 storyString,
4034 afterScenarioAnchor,4038 afterScenarioAnchor,
4035 mesExmString,4039 mesExmString,
4036 mesSend.map((e) => `${e.extensionPrompts.join('')}${e.message}`).join(''),4040 addChatsPreamble(addChatsSeparator(jointMessages)),
4037 '\n',4041 '\n',
4042 modifyLastPromptLine(''),
4038 generatedPromptCache,4043 generatedPromptCache,
4039 quiet_prompt,
4040 ].join('').replace(/\r/gm, '');4044 ].join('').replace(/\r/gm, '');
4041 let thisPromptContextSize = await getTokenCountAsync(prompt, power_user.token_padding);4045 let thisPromptContextSize = await getTokenCountAsync(prompt, power_user.token_padding);
40424046
@@ -4302,7 +4306,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4302 padding: power_user.token_padding,4306 padding: power_user.token_padding,
4303 main_api: main_api,4307 main_api: main_api,
4304 instruction: isInstruct ? substituteParams(power_user.prefer_character_prompt && system ? system : power_user.instruct.system_prompt) : '',4308 instruction: isInstruct ? substituteParams(power_user.prefer_character_prompt && system ? system : power_user.instruct.system_prompt) : '',
4305 userPersona: (power_user.persona_description || ''),4309 userPersona: (power_user.persona_description_position == persona_description_positions.IN_PROMPT ? (persona || '') : ''),
4306 };4310 };
43074311
4308 //console.log(additionalPromptStuff);4312 //console.log(additionalPromptStuff);
@@ -5080,7 +5084,7 @@ async function promptItemize(itemizedPrompts, requestedMesId) {
5080 }5084 }
50815085
5082 navigator.clipboard.writeText(rawPromptValues);5086 navigator.clipboard.writeText(rawPromptValues);
5083 toastr.info('Copied!');5087 toastr.info(t`Copied!`);
5084 });5088 });
50855089
5086 popup.dlg.querySelector('#showRawPrompt').addEventListener('click', function () {5090 popup.dlg.querySelector('#showRawPrompt').addEventListener('click', function () {
@@ -5679,7 +5683,7 @@ export function resetChatState() {
5679 // replaces deleted charcter name with system user since it will be displayed next.5683 // replaces deleted charcter name with system user since it will be displayed next.
5680 name2 = systemUserName;5684 name2 = systemUserName;
5681 // sets up system user to tell user about having deleted a character5685 // sets up system user to tell user about having deleted a character
5682 chat = [...safetychat];5686 chat.splice(0, chat.length, ...SAFETY_CHAT);
5683 // resets chat metadata5687 // resets chat metadata
5684 chat_metadata = {};5688 chat_metadata = {};
5685 // resets the characters array, forcing getcharacters to reset5689 // resets the characters array, forcing getcharacters to reset
@@ -6310,7 +6314,7 @@ export function setUserName(value) {
6310 console.log(`User name changed to ${name1}`);6314 console.log(`User name changed to ${name1}`);
6311 $('#your_name').val(name1);6315 $('#your_name').val(name1);
6312 if (power_user.persona_show_notifications) {6316 if (power_user.persona_show_notifications) {
6313 toastr.success(`Your messages will now be sent as ${name1}`, 'Current persona updated');6317 toastr.success(t`Your messages will now be sent as ${name1}`, t`Current persona updated`);
6314 }6318 }
6315 saveSettingsDebounced();6319 saveSettingsDebounced();
6316}6320}
@@ -8840,16 +8844,21 @@ export async function handleDeleteCharacter(this_chid, delete_chats) {
8840/**8844/**
8841 * Deletes a character completely, including associated chats if specified8845 * Deletes a character completely, including associated chats if specified
8842 *8846 *
8843 * @param {string} characterKey - The key (avatar) of the character to be deleted8847 * @param {string|string[]} characterKey - The key (avatar) of the character to be deleted
8844 * @param {Object} [options] - Optional parameters for the deletion8848 * @param {Object} [options] - Optional parameters for the deletion
8845 * @param {boolean} [options.deleteChats=true] - Whether to delete associated chats or not8849 * @param {boolean} [options.deleteChats=true] - Whether to delete associated chats or not
8846 * @return {Promise<void>} - A promise that resolves when the character is successfully deleted8850 * @return {Promise<void>} - A promise that resolves when the character is successfully deleted
8847 */8851 */
8848export async function deleteCharacter(characterKey, { deleteChats = true } = {}) {8852export async function deleteCharacter(characterKey, { deleteChats = true } = {}) {
8849 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);
8850 if (!character) {8859 if (!character) {
8851 toastr.warning(`Character ${characterKey} not found. Cannot be deleted.`);8860 toastr.warning(`Character ${key} not found. Skipping deletion.`);
8852 return;8861 continue;
8853 }8862 }
88548863
8855 const chid = characters.indexOf(character);8864 const chid = characters.indexOf(character);
@@ -8865,10 +8874,12 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
8865 });8874 });
88668875
8867 if (!response.ok) {8876 if (!response.ok) {
8868 throw new Error(`Failed to delete character: ${response.status} ${response.statusText}`);8877 toastr.error(`${response.status} ${response.statusText}`, 'Failed to delete character');
8878 continue;
8869 }8879 }
88708880
8871 await removeCharacterFromUI(character.name, character.avatar);8881 delete tag_map[character.avatar];
8882 select_rm_info('char_delete', character.name);
88728883
8873 if (deleteChats) {8884 if (deleteChats) {
8874 for (const chat of pastChats) {8885 for (const chat of pastChats) {
@@ -8877,35 +8888,30 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
8877 }8888 }
8878 }8889 }
88798890
8880 eventSource.emit(event_types.CHARACTER_DELETED, { id: this_chid, character: characters[this_chid] });8891 await eventSource.emit(event_types.CHARACTER_DELETED, { id: chid, character: character });
8892 }
8893
8894 await removeCharacterFromUI();
8881}8895}
88828896
8883/**8897/**
8884 * Function to delete a character from UI after character deletion API success.8898 * Function to delete a character from UI after character deletion API success.
8885 * It manages necessary UI changes such as closing advanced editing popup, unsetting8899 * It manages necessary UI changes such as closing advanced editing popup, unsetting
8886 * character ID, resetting characters array and chat metadata, deselecting character's tab8900 * character ID, resetting characters array and chat metadata, deselecting character's tab
8887 * panel, removing character name from navigation tabs, clearing chat, removing character's8901 * panel, removing character name from navigation tabs, clearing chat, fetching updated list of characters.
8888 * avatar from tag_map, fetching updated list of characters and updating the 'deleted
8889 * character' message.
8890 * It also ensures to save the settings after all the operations.8902 * It also ensures to save the settings after all the operations.
8891 *
8892 * @param {string} name - The name of the character to be deleted.
8893 * @param {string} avatar - The avatar URL of the character to be deleted.
8894 * @param {boolean} reloadCharacters - Whether the character list should be refreshed after deletion.
8895 */8903 */
8896async function removeCharacterFromUI(name, avatar, reloadCharacters = true) {8904async function removeCharacterFromUI() {
8897 await clearChat();8905 await clearChat();
8898 $('#character_cross').click();8906 $('#character_cross').click();
8899 this_chid = undefined;8907 this_chid = undefined;
8900 characters.length = 0;8908 characters.length = 0;
8901 name2 = systemUserName;8909 name2 = systemUserName;
8902 chat = [...safetychat];8910 chat.splice(0, chat.length, ...SAFETY_CHAT);
8903 chat_metadata = {};8911 chat_metadata = {};
8904 $(document.getElementById('rm_button_selected_ch')).children('h2').text('');8912 $(document.getElementById('rm_button_selected_ch')).children('h2').text('');
8905 this_chid = undefined;8913 this_chid = undefined;
8906 delete tag_map[avatar];8914 await getCharacters();
8907 if (reloadCharacters) await getCharacters();
8908 select_rm_info('char_delete', name);
8909 await printMessages();8915 await printMessages();
8910 saveSettingsDebounced();8916 saveSettingsDebounced();
8911}8917}
@@ -9532,11 +9538,7 @@ jQuery(async function () {
9532 const oldFileNameFull = $(this).closest('.select_chat_block_wrapper').find('.select_chat_block_filename').text();9538 const oldFileNameFull = $(this).closest('.select_chat_block_wrapper').find('.select_chat_block_filename').text();
9533 const oldFileName = oldFileNameFull.replace('.jsonl', '');9539 const oldFileName = oldFileNameFull.replace('.jsonl', '');
95349540
9535 const popupText = `<h3>Enter the new name for the chat:<h3>9541 const popupText = await renderTemplateAsync('chatRename');
9536 <small>!!Using an existing filename will produce an error!!<br>
9537 This will break the link between checkpoint chats.<br>
9538 No need to add '.jsonl' at the end.<br>
9539 </small>`;
9540 const newName = await callPopup(popupText, 'input', oldFileName);9542 const newName = await callPopup(popupText, 'input', oldFileName);
95419543
9542 if (!newName || newName == oldFileName) {9544 if (!newName || newName == oldFileName) {
@@ -9744,12 +9746,7 @@ jQuery(async function () {
9744 else if (id == 'option_start_new_chat') {9746 else if (id == 'option_start_new_chat') {
9745 if ((selected_group || this_chid !== undefined) && !is_send_press) {9747 if ((selected_group || this_chid !== undefined) && !is_send_press) {
9746 let deleteCurrentChat = false;9748 let deleteCurrentChat = false;
9747 const result = await Popup.show.confirm('Start new chat?', `9749 const result = await Popup.show.confirm(t`Start new chat?`, await renderTemplateAsync('newChatConfirm'), {
9748 <label for="del_chat_checkbox" class="checkbox_label justifyCenter"
9749 title="If necessary, you can later restore this chat file from the /backups folder">
9750 <input type="checkbox" id="del_chat_checkbox" />
9751 <small>Also delete the current chat file</small>
9752 </label>`, {
9753 onClose: () => deleteCurrentChat = !!$('#del_chat_checkbox').prop('checked'),9750 onClose: () => deleteCurrentChat = !!$('#del_chat_checkbox').prop('checked'),
9754 });9751 });
9755 if (!result) {9752 if (!result) {
@@ -10261,10 +10258,10 @@ jQuery(async function () {
1026110258
10262 let deleteOnlySwipe = false;10259 let deleteOnlySwipe = false;
10263 if (power_user.confirm_message_delete && fromSlashCommand !== true) {10260 if (power_user.confirm_message_delete && fromSlashCommand !== true) {
10264 const result = await callGenericPopup('Are you sure you want to delete this message?', POPUP_TYPE.CONFIRM, null, {10261 const result = await callGenericPopup(t`Are you sure you want to delete this message?`, POPUP_TYPE.CONFIRM, null, {
10265 okButton: canDeleteSwipe ? 'Delete Swipe' : 'Delete Message',10262 okButton: canDeleteSwipe ? t`Delete Swipe` : t`Delete Message`,
10266 cancelButton: 'Cancel',10263 cancelButton: 'Cancel',
10267 customButtons: canDeleteSwipe ? ['Delete Message'] : null,10264 customButtons: canDeleteSwipe ? [t`Delete Message`] : null,
10268 });10265 });
10269 if (!result) {10266 if (!result) {
10270 return;10267 return;
public/scripts/BulkEditOverlay.js+11 -11
@@ -108,14 +108,12 @@ class CharacterContextMenu {
108 * Delete one or more characters,108 * Delete one or more characters,
109 * opens a popup.109 * opens a popup.
110 *110 *
111 * @param {number} characterId111 * @param {string|string[]} characterKey
112 * @param {boolean} [deleteChats]112 * @param {boolean} [deleteChats]
113 * @returns {Promise<void>}113 * @returns {Promise<void>}
114 */114 */
115 static delete = async (characterId, deleteChats = false) => {115 static delete = async (characterKey, deleteChats = false) => {
116 const character = CharacterContextMenu.#getCharacter(characterId);116 await deleteCharacter(characterKey, { deleteChats: deleteChats });
117
118 await deleteCharacter(character.avatar, { deleteChats: deleteChats });
119 };117 };
120118
121 static #getCharacter = (characterId) => characters[characterId] ?? null;119 static #getCharacter = (characterId) => characters[characterId] ?? null;
@@ -599,8 +597,7 @@ class BulkEditOverlay {
599597
600 this.container.removeEventListener('mouseup', cancelHold);598 this.container.removeEventListener('mouseup', cancelHold);
601 this.container.removeEventListener('touchend', cancelHold);599 this.container.removeEventListener('touchend', cancelHold);
602 },600 }, BulkEditOverlay.longPressDelay);
603 BulkEditOverlay.longPressDelay);
604 };601 };
605602
606 handleLongPressEnd = (event) => {603 handleLongPressEnd = (event) => {
@@ -847,11 +844,14 @@ class BulkEditOverlay {
847 const deleteChats = document.getElementById('del_char_checkbox').checked ?? false;844 const deleteChats = document.getElementById('del_char_checkbox').checked ?? false;
848845
849 showLoader();846 showLoader();
850 toastr.info('We\'re deleting your characters, please wait...', 'Working on it');847 const toast = toastr.info('We\'re deleting your characters, please wait...', 'Working on it');
851 return Promise.allSettled(characterIds.map(async characterId => CharacterContextMenu.delete(characterId, deleteChats)))848 const avatarList = characterIds.map(id => characters[id]?.avatar).filter(a => a);
852 .then(() => getCharacters())849 return CharacterContextMenu.delete(avatarList, deleteChats)
853 .then(() => this.browseState())850 .then(() => this.browseState())
854 .finally(() => hideLoader());851 .finally(() => {
852 toastr.clear(toast);
853 hideLoader();
854 });
855 });855 });
856856
857 // At this moment the popup is already changed in the dom, but not yet closed/resolved. We build the avatar list here857 // At this moment the popup is already changed in the dom, but not yet closed/resolved. We build the avatar list here
public/scripts/extensions/caption/settings.html+3 -0
@@ -45,8 +45,11 @@
45 <option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>45 <option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>
46 <option data-type="google" value="gemini-pro-vision">gemini-pro-vision</option>46 <option data-type="google" value="gemini-pro-vision">gemini-pro-vision</option>
47 <option data-type="google" value="gemini-1.5-flash-latest">gemini-1.5-flash-latest</option>47 <option data-type="google" value="gemini-1.5-flash-latest">gemini-1.5-flash-latest</option>
48 <option data-type="google" value="gemini-1.5-flash-exp-0827">gemini-1.5-flash-exp-0827</option>
49 <option data-type="google" value="gemini-1.5-flash-8b-exp-0827">gemini-1.5-flash-8b-exp-0827</option>
48 <option data-type="google" value="gemini-1.5-pro-latest">gemini-1.5-pro-latest</option>50 <option data-type="google" value="gemini-1.5-pro-latest">gemini-1.5-pro-latest</option>
49 <option data-type="google" value="gemini-1.5-pro-exp-0801">gemini-1.5-pro-exp-0801</option>51 <option data-type="google" value="gemini-1.5-pro-exp-0801">gemini-1.5-pro-exp-0801</option>
52 <option data-type="google" value="gemini-1.5-pro-exp-0827">gemini-1.5-pro-exp-0827</option>
50 <option data-type="openrouter" value="openai/gpt-4-vision-preview">openai/gpt-4-vision-preview</option>53 <option data-type="openrouter" value="openai/gpt-4-vision-preview">openai/gpt-4-vision-preview</option>
51 <option data-type="openrouter" value="openai/gpt-4o">openai/gpt-4o</option>54 <option data-type="openrouter" value="openai/gpt-4o">openai/gpt-4o</option>
52 <option data-type="openrouter" value="openai/gpt-4-turbo">openai/gpt-4-turbo</option>55 <option data-type="openrouter" value="openai/gpt-4-turbo">openai/gpt-4-turbo</option>
public/scripts/extensions/tts/index.js+1 -1
@@ -421,7 +421,7 @@ function completeTtsJob() {
421async function tts(text, voiceId, char) {421async function tts(text, voiceId, char) {
422 async function processResponse(response) {422 async function processResponse(response) {
423 // RVC injection423 // RVC injection
424 if (extension_settings.rvc.enabled && typeof window['rvcVoiceConversion'] === 'function')424 if (typeof window['rvcVoiceConversion'] === 'function' && extension_settings.rvc.enabled)
425 response = await window['rvcVoiceConversion'](response, char, text);425 response = await window['rvcVoiceConversion'](response, char, text);
426426
427 await addAudioJob(response, char);427 await addAudioJob(response, char);
public/scripts/nai-settings.js+16 -1
@@ -78,6 +78,19 @@ export function getKayraMaxContextTokens() {
78 return null;78 return null;
79}79}
8080
81export function getKayraMaxResponseTokens() {
82 switch (novel_data?.tier) {
83 case 1:
84 return 100;
85 case 2:
86 return 100;
87 case 3:
88 return 150;
89 }
90
91 return maximum_output_length;
92}
93
81export function getNovelTier() {94export function getNovelTier() {
82 return nai_tiers[novel_data?.tier] ?? 'no_connection';95 return nai_tiers[novel_data?.tier] ?? 'no_connection';
83}96}
@@ -438,12 +451,14 @@ export function getNovelGenerationData(finalPrompt, settings, maxLength, isImper
438 console.log(finalPrompt);451 console.log(finalPrompt);
439 }452 }
440453
454 const adjustedMaxLength = nai_settings.model_novel.includes('kayra') ? getKayraMaxResponseTokens() : maximum_output_length;
455
441 return {456 return {
442 'input': finalPrompt,457 'input': finalPrompt,
443 'model': nai_settings.model_novel,458 'model': nai_settings.model_novel,
444 'use_string': true,459 'use_string': true,
445 'temperature': Number(nai_settings.temperature),460 'temperature': Number(nai_settings.temperature),
446 'max_length': maxLength < maximum_output_length ? maxLength : maximum_output_length,461 'max_length': maxLength < adjustedMaxLength ? maxLength : adjustedMaxLength,
447 'min_length': Number(nai_settings.min_length),462 'min_length': Number(nai_settings.min_length),
448 'tail_free_sampling': Number(nai_settings.tail_free_sampling),463 'tail_free_sampling': Number(nai_settings.tail_free_sampling),
449 'repetition_penalty': Number(nai_settings.repetition_penalty),464 'repetition_penalty': Number(nai_settings.repetition_penalty),
public/scripts/openai.js+46 -112
@@ -74,6 +74,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashComma
74import { renderTemplateAsync } from './templates.js';74import { renderTemplateAsync } from './templates.js';
75import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';75import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
76import { Popup, POPUP_RESULT } from './popup.js';76import { Popup, POPUP_RESULT } from './popup.js';
77import { t } from './i18n.js';
7778
78export {79export {
79 openai_messages_count,80 openai_messages_count,
@@ -132,13 +133,9 @@ const max_2mil = 2000 * 1000;
132const scale_max = 8191;133const scale_max = 8191;
133const claude_max = 9000; // We have a proper tokenizer, so theoretically could be larger (up to 9k)134const claude_max = 9000; // We have a proper tokenizer, so theoretically could be larger (up to 9k)
134const claude_100k_max = 99000;135const claude_100k_max = 99000;
135let ai21_max = 9200; //can easily fit 9k gpt tokens because j2's tokenizer is efficient af136const unlocked_max = max_2mil;
136const unlocked_max = max_200k;
137const oai_max_temp = 2.0;137const oai_max_temp = 2.0;
138const claude_max_temp = 1.0; //same as j2138const claude_max_temp = 1.0;
139const j2_max_topk = 10.0;
140const j2_max_freq = 5.0;
141const j2_max_pres = 5.0;
142const openrouter_website_model = 'OR_Website';139const openrouter_website_model = 'OR_Website';
143const openai_max_stop_strings = 4;140const openai_max_stop_strings = 4;
144141
@@ -218,25 +215,11 @@ const sensitiveFields = [
218 'custom_include_headers',215 'custom_include_headers',
219];216];
220217
221function getPrefixMap() {
222 return selected_group ? {
223 assistant: '',
224 user: '',
225 system: 'OOC: ',
226 }
227 : {
228 assistant: '{{char}}:',
229 user: '{{user}}:',
230 system: '',
231 };
232}
233
234const default_settings = {218const default_settings = {
235 preset_settings_openai: 'Default',219 preset_settings_openai: 'Default',
236 temp_openai: 1.0,220 temp_openai: 1.0,
237 freq_pen_openai: 0,221 freq_pen_openai: 0,
238 pres_pen_openai: 0,222 pres_pen_openai: 0,
239 count_pen: 0.0,
240 top_p_openai: 1.0,223 top_p_openai: 1.0,
241 top_k_openai: 0,224 top_k_openai: 0,
242 min_p_openai: 0,225 min_p_openai: 0,
@@ -264,7 +247,7 @@ const default_settings = {
264 openai_model: 'gpt-4-turbo',247 openai_model: 'gpt-4-turbo',
265 claude_model: 'claude-3-5-sonnet-20240620',248 claude_model: 'claude-3-5-sonnet-20240620',
266 google_model: 'gemini-1.5-pro',249 google_model: 'gemini-1.5-pro',
267 ai21_model: 'j2-ultra',250 ai21_model: 'jamba-1.5-large',
268 mistralai_model: 'mistral-large-latest',251 mistralai_model: 'mistral-large-latest',
269 cohere_model: 'command-r-plus',252 cohere_model: 'command-r-plus',
270 perplexity_model: 'llama-3.1-70b-instruct',253 perplexity_model: 'llama-3.1-70b-instruct',
@@ -294,8 +277,6 @@ const default_settings = {
294 assistant_prefill: '',277 assistant_prefill: '',
295 assistant_impersonation: '',278 assistant_impersonation: '',
296 human_sysprompt_message: default_claude_human_sysprompt_message,279 human_sysprompt_message: default_claude_human_sysprompt_message,
297 use_ai21_tokenizer: false,
298 use_google_tokenizer: false,
299 claude_use_sysprompt: false,280 claude_use_sysprompt: false,
300 use_makersuite_sysprompt: true,281 use_makersuite_sysprompt: true,
301 use_alt_scale: false,282 use_alt_scale: false,
@@ -317,7 +298,6 @@ const oai_settings = {
317 temp_openai: 1.0,298 temp_openai: 1.0,
318 freq_pen_openai: 0,299 freq_pen_openai: 0,
319 pres_pen_openai: 0,300 pres_pen_openai: 0,
320 count_pen: 0.0,
321 top_p_openai: 1.0,301 top_p_openai: 1.0,
322 top_k_openai: 0,302 top_k_openai: 0,
323 min_p_openai: 0,303 min_p_openai: 0,
@@ -345,7 +325,7 @@ const oai_settings = {
345 openai_model: 'gpt-4-turbo',325 openai_model: 'gpt-4-turbo',
346 claude_model: 'claude-3-5-sonnet-20240620',326 claude_model: 'claude-3-5-sonnet-20240620',
347 google_model: 'gemini-1.5-pro',327 google_model: 'gemini-1.5-pro',
348 ai21_model: 'j2-ultra',328 ai21_model: 'jamba-1.5-large',
349 mistralai_model: 'mistral-large-latest',329 mistralai_model: 'mistral-large-latest',
350 cohere_model: 'command-r-plus',330 cohere_model: 'command-r-plus',
351 perplexity_model: 'llama-3.1-70b-instruct',331 perplexity_model: 'llama-3.1-70b-instruct',
@@ -375,8 +355,6 @@ const oai_settings = {
375 assistant_prefill: '',355 assistant_prefill: '',
376 assistant_impersonation: '',356 assistant_impersonation: '',
377 human_sysprompt_message: default_claude_human_sysprompt_message,357 human_sysprompt_message: default_claude_human_sysprompt_message,
378 use_ai21_tokenizer: false,
379 use_google_tokenizer: false,
380 claude_use_sysprompt: false,358 claude_use_sysprompt: false,
381 use_makersuite_sysprompt: true,359 use_makersuite_sysprompt: true,
382 use_alt_scale: false,360 use_alt_scale: false,
@@ -425,7 +403,7 @@ async function validateReverseProxy() {
425 const rememberKey = `Proxy_SkipConfirm_${getStringHash(oai_settings.reverse_proxy)}`;403 const rememberKey = `Proxy_SkipConfirm_${getStringHash(oai_settings.reverse_proxy)}`;
426 const skipConfirm = localStorage.getItem(rememberKey) === 'true';404 const skipConfirm = localStorage.getItem(rememberKey) === 'true';
427405
428 const confirmation = skipConfirm || await Popup.show.confirm('Connecting To Proxy', `<span>Are you sure you want to connect to the following proxy URL?</span><var>${DOMPurify.sanitize(oai_settings.reverse_proxy)}</var>`);406 const confirmation = skipConfirm || await Popup.show.confirm(t`Connecting To Proxy`, await renderTemplateAsync('proxyConnectionWarning', { proxyURL: DOMPurify.sanitize(oai_settings.reverse_proxy) }));
429407
430 if (!confirmation) {408 if (!confirmation) {
431 toastr.error('Update or remove your reverse proxy settings.');409 toastr.error('Update or remove your reverse proxy settings.');
@@ -1148,7 +1126,6 @@ function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, wor
1148 { role: 'system', content: charDescription, identifier: 'charDescription' },1126 { role: 'system', content: charDescription, identifier: 'charDescription' },
1149 { role: 'system', content: charPersonalityText, identifier: 'charPersonality' },1127 { role: 'system', content: charPersonalityText, identifier: 'charPersonality' },
1150 { role: 'system', content: scenarioText, identifier: 'scenario' },1128 { role: 'system', content: scenarioText, identifier: 'scenario' },
1151 { role: 'system', content: personaDescription, identifier: 'personaDescription' },
1152 // Unordered prompts without marker1129 // Unordered prompts without marker
1153 { role: 'system', content: impersonationPrompt, identifier: 'impersonate' },1130 { role: 'system', content: impersonationPrompt, identifier: 'impersonate' },
1154 { role: 'system', content: quietPrompt, identifier: 'quietPrompt' },1131 { role: 'system', content: quietPrompt, identifier: 'quietPrompt' },
@@ -1395,6 +1372,11 @@ function tryParseStreamingError(response, decoded) {
1395 toastr.error(data.error.message || response.statusText, 'Chat Completion API');1372 toastr.error(data.error.message || response.statusText, 'Chat Completion API');
1396 throw new Error(data);1373 throw new Error(data);
1397 }1374 }
1375
1376 if (data.message) {
1377 toastr.error(data.message, 'Chat Completion API');
1378 throw new Error(data);
1379 }
1398 }1380 }
1399 catch {1381 catch {
1400 // No JSON. Do nothing.1382 // No JSON. Do nothing.
@@ -1802,7 +1784,6 @@ async function sendOpenAIRequest(type, messages, signal) {
1802 const isClaude = oai_settings.chat_completion_source == chat_completion_sources.CLAUDE;1784 const isClaude = oai_settings.chat_completion_source == chat_completion_sources.CLAUDE;
1803 const isOpenRouter = oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER;1785 const isOpenRouter = oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER;
1804 const isScale = oai_settings.chat_completion_source == chat_completion_sources.SCALE;1786 const isScale = oai_settings.chat_completion_source == chat_completion_sources.SCALE;
1805 const isAI21 = oai_settings.chat_completion_source == chat_completion_sources.AI21;
1806 const isGoogle = oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE;1787 const isGoogle = oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE;
1807 const isOAI = oai_settings.chat_completion_source == chat_completion_sources.OPENAI;1788 const isOAI = oai_settings.chat_completion_source == chat_completion_sources.OPENAI;
1808 const isMistral = oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI;1789 const isMistral = oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI;
@@ -1815,7 +1796,7 @@ async function sendOpenAIRequest(type, messages, signal) {
1815 const isQuiet = type === 'quiet';1796 const isQuiet = type === 'quiet';
1816 const isImpersonate = type === 'impersonate';1797 const isImpersonate = type === 'impersonate';
1817 const isContinue = type === 'continue';1798 const isContinue = type === 'continue';
1818 const stream = oai_settings.stream_openai && !isQuiet && !isScale && !isAI21 && !(isGoogle && oai_settings.google_model.includes('bison'));1799 const stream = oai_settings.stream_openai && !isQuiet && !isScale && !(isGoogle && oai_settings.google_model.includes('bison'));
1819 const useLogprobs = !!power_user.request_token_probabilities;1800 const useLogprobs = !!power_user.request_token_probabilities;
1820 const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom);1801 const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom);
18211802
@@ -1824,15 +1805,6 @@ async function sendOpenAIRequest(type, messages, signal) {
1824 replaceItemizedPromptText(messageId, messages);1805 replaceItemizedPromptText(messageId, messages);
1825 }1806 }
18261807
1827 if (isAI21) {
1828 const joinedMsgs = messages.reduce((acc, obj) => {
1829 const prefix = getPrefixMap()[obj.role];
1830 return acc + (prefix ? (selected_group ? '\n' : prefix + ' ') : '') + obj.content + '\n';
1831 }, '');
1832 messages = substituteParams(joinedMsgs) + (isImpersonate ? `${name1}:` : `${name2}:`);
1833 replaceItemizedPromptText(messageId, messages);
1834 }
1835
1836 // If we're using the window.ai extension, use that instead1808 // If we're using the window.ai extension, use that instead
1837 // Doesn't support logit bias yet1809 // Doesn't support logit bias yet
1838 if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI) {1810 if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI) {
@@ -1931,12 +1903,6 @@ async function sendOpenAIRequest(type, messages, signal) {
1931 generate_data['use_makersuite_sysprompt'] = oai_settings.use_makersuite_sysprompt;1903 generate_data['use_makersuite_sysprompt'] = oai_settings.use_makersuite_sysprompt;
1932 }1904 }
19331905
1934 if (isAI21) {
1935 generate_data['top_k'] = Number(oai_settings.top_k_openai);
1936 generate_data['count_pen'] = Number(oai_settings.count_pen);
1937 generate_data['stop_tokens'] = [name1 + ':', substituteParams(oai_settings.new_chat_prompt), substituteParams(oai_settings.new_group_chat_prompt)];
1938 }
1939
1940 if (isMistral) {1906 if (isMistral) {
1941 generate_data['safe_prompt'] = false; // already defaults to false, but just incase they change that in the future.1907 generate_data['safe_prompt'] = false; // already defaults to false, but just incase they change that in the future.
1942 }1908 }
@@ -3008,7 +2974,6 @@ function loadOpenAISettings(data, settings) {
3008 oai_settings.temp_openai = settings.temp_openai ?? default_settings.temp_openai;2974 oai_settings.temp_openai = settings.temp_openai ?? default_settings.temp_openai;
3009 oai_settings.freq_pen_openai = settings.freq_pen_openai ?? default_settings.freq_pen_openai;2975 oai_settings.freq_pen_openai = settings.freq_pen_openai ?? default_settings.freq_pen_openai;
3010 oai_settings.pres_pen_openai = settings.pres_pen_openai ?? default_settings.pres_pen_openai;2976 oai_settings.pres_pen_openai = settings.pres_pen_openai ?? default_settings.pres_pen_openai;
3011 oai_settings.count_pen = settings.count_pen ?? default_settings.count_pen;
3012 oai_settings.top_p_openai = settings.top_p_openai ?? default_settings.top_p_openai;2977 oai_settings.top_p_openai = settings.top_p_openai ?? default_settings.top_p_openai;
3013 oai_settings.top_k_openai = settings.top_k_openai ?? default_settings.top_k_openai;2978 oai_settings.top_k_openai = settings.top_k_openai ?? default_settings.top_k_openai;
3014 oai_settings.top_a_openai = settings.top_a_openai ?? default_settings.top_a_openai;2979 oai_settings.top_a_openai = settings.top_a_openai ?? default_settings.top_a_openai;
@@ -3080,10 +3045,12 @@ function loadOpenAISettings(data, settings) {
3080 oai_settings.names_behavior = character_names_behavior.COMPLETION;3045 oai_settings.names_behavior = character_names_behavior.COMPLETION;
3081 }3046 }
30823047
3048 if (oai_settings.ai21_model.startsWith('j2-')) {
3049 oai_settings.ai21_model = 'jamba-1.5-large';
3050 }
3051
3083 if (settings.wrap_in_quotes !== undefined) oai_settings.wrap_in_quotes = !!settings.wrap_in_quotes;3052 if (settings.wrap_in_quotes !== undefined) oai_settings.wrap_in_quotes = !!settings.wrap_in_quotes;
3084 if (settings.openai_model !== undefined) oai_settings.openai_model = settings.openai_model;3053 if (settings.openai_model !== undefined) oai_settings.openai_model = settings.openai_model;
3085 if (settings.use_ai21_tokenizer !== undefined) { oai_settings.use_ai21_tokenizer = !!settings.use_ai21_tokenizer; oai_settings.use_ai21_tokenizer ? ai21_max = 8191 : ai21_max = 9200; }
3086 if (settings.use_google_tokenizer !== undefined) oai_settings.use_google_tokenizer = !!settings.use_google_tokenizer;
3087 if (settings.claude_use_sysprompt !== undefined) oai_settings.claude_use_sysprompt = !!settings.claude_use_sysprompt;3054 if (settings.claude_use_sysprompt !== undefined) oai_settings.claude_use_sysprompt = !!settings.claude_use_sysprompt;
3088 if (settings.use_makersuite_sysprompt !== undefined) oai_settings.use_makersuite_sysprompt = !!settings.use_makersuite_sysprompt;3055 if (settings.use_makersuite_sysprompt !== undefined) oai_settings.use_makersuite_sysprompt = !!settings.use_makersuite_sysprompt;
3089 if (settings.use_alt_scale !== undefined) { oai_settings.use_alt_scale = !!settings.use_alt_scale; updateScaleForm(); }3056 if (settings.use_alt_scale !== undefined) { oai_settings.use_alt_scale = !!settings.use_alt_scale; updateScaleForm(); }
@@ -3133,8 +3100,6 @@ function loadOpenAISettings(data, settings) {
3133 $('#jailbreak_system').prop('checked', oai_settings.jailbreak_system);3100 $('#jailbreak_system').prop('checked', oai_settings.jailbreak_system);
3134 $('#openai_show_external_models').prop('checked', oai_settings.show_external_models);3101 $('#openai_show_external_models').prop('checked', oai_settings.show_external_models);
3135 $('#openai_external_category').toggle(oai_settings.show_external_models);3102 $('#openai_external_category').toggle(oai_settings.show_external_models);
3136 $('#use_ai21_tokenizer').prop('checked', oai_settings.use_ai21_tokenizer);
3137 $('#use_google_tokenizer').prop('checked', oai_settings.use_google_tokenizer);
3138 $('#claude_use_sysprompt').prop('checked', oai_settings.claude_use_sysprompt);3103 $('#claude_use_sysprompt').prop('checked', oai_settings.claude_use_sysprompt);
3139 $('#use_makersuite_sysprompt').prop('checked', oai_settings.use_makersuite_sysprompt);3104 $('#use_makersuite_sysprompt').prop('checked', oai_settings.use_makersuite_sysprompt);
3140 $('#scale-alt').prop('checked', oai_settings.use_alt_scale);3105 $('#scale-alt').prop('checked', oai_settings.use_alt_scale);
@@ -3170,9 +3135,6 @@ function loadOpenAISettings(data, settings) {
3170 $('#pres_pen_openai').val(oai_settings.pres_pen_openai);3135 $('#pres_pen_openai').val(oai_settings.pres_pen_openai);
3171 $('#pres_pen_counter_openai').val(Number(oai_settings.pres_pen_openai).toFixed(2));3136 $('#pres_pen_counter_openai').val(Number(oai_settings.pres_pen_openai).toFixed(2));
31723137
3173 $('#count_pen').val(oai_settings.count_pen);
3174 $('#count_pen_counter').val(Number(oai_settings.count_pen).toFixed(2));
3175
3176 $('#top_p_openai').val(oai_settings.top_p_openai);3138 $('#top_p_openai').val(oai_settings.top_p_openai);
3177 $('#top_p_counter_openai').val(Number(oai_settings.top_p_openai).toFixed(2));3139 $('#top_p_counter_openai').val(Number(oai_settings.top_p_openai).toFixed(2));
31783140
@@ -3392,7 +3354,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3392 temperature: settings.temp_openai,3354 temperature: settings.temp_openai,
3393 frequency_penalty: settings.freq_pen_openai,3355 frequency_penalty: settings.freq_pen_openai,
3394 presence_penalty: settings.pres_pen_openai,3356 presence_penalty: settings.pres_pen_openai,
3395 count_penalty: settings.count_pen,
3396 top_p: settings.top_p_openai,3357 top_p: settings.top_p_openai,
3397 top_k: settings.top_k_openai,3358 top_k: settings.top_k_openai,
3398 top_a: settings.top_a_openai,3359 top_a: settings.top_a_openai,
@@ -3427,8 +3388,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3427 assistant_prefill: settings.assistant_prefill,3388 assistant_prefill: settings.assistant_prefill,
3428 assistant_impersonation: settings.assistant_impersonation,3389 assistant_impersonation: settings.assistant_impersonation,
3429 human_sysprompt_message: settings.human_sysprompt_message,3390 human_sysprompt_message: settings.human_sysprompt_message,
3430 use_ai21_tokenizer: settings.use_ai21_tokenizer,
3431 use_google_tokenizer: settings.use_google_tokenizer,
3432 claude_use_sysprompt: settings.claude_use_sysprompt,3391 claude_use_sysprompt: settings.claude_use_sysprompt,
3433 use_makersuite_sysprompt: settings.use_makersuite_sysprompt,3392 use_makersuite_sysprompt: settings.use_makersuite_sysprompt,
3434 use_alt_scale: settings.use_alt_scale,3393 use_alt_scale: settings.use_alt_scale,
@@ -3799,7 +3758,6 @@ function onSettingsPresetChange() {
3799 temperature: ['#temp_openai', 'temp_openai', false],3758 temperature: ['#temp_openai', 'temp_openai', false],
3800 frequency_penalty: ['#freq_pen_openai', 'freq_pen_openai', false],3759 frequency_penalty: ['#freq_pen_openai', 'freq_pen_openai', false],
3801 presence_penalty: ['#pres_pen_openai', 'pres_pen_openai', false],3760 presence_penalty: ['#pres_pen_openai', 'pres_pen_openai', false],
3802 count_penalty: ['#count_pen', 'count_pen', false],
3803 top_p: ['#top_p_openai', 'top_p_openai', false],3761 top_p: ['#top_p_openai', 'top_p_openai', false],
3804 top_k: ['#top_k_openai', 'top_k_openai', false],3762 top_k: ['#top_k_openai', 'top_k_openai', false],
3805 top_a: ['#top_a_openai', 'top_a_openai', false],3763 top_a: ['#top_a_openai', 'top_a_openai', false],
@@ -3856,8 +3814,6 @@ function onSettingsPresetChange() {
3856 assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],3814 assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],
3857 assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false],3815 assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false],
3858 human_sysprompt_message: ['#claude_human_sysprompt_textarea', 'human_sysprompt_message', false],3816 human_sysprompt_message: ['#claude_human_sysprompt_textarea', 'human_sysprompt_message', false],
3859 use_ai21_tokenizer: ['#use_ai21_tokenizer', 'use_ai21_tokenizer', true],
3860 use_google_tokenizer: ['#use_google_tokenizer', 'use_google_tokenizer', true],
3861 claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true],3817 claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true],
3862 use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true],3818 use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true],
3863 use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],3819 use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],
@@ -4028,6 +3984,11 @@ async function onModelChange() {
4028 }3984 }
40293985
4030 if ($(this).is('#model_ai21_select')) {3986 if ($(this).is('#model_ai21_select')) {
3987 if (value === '' || value.startsWith('j2-')) {
3988 value = 'jamba-1.5-large';
3989 $('#model_ai21_select').val(value);
3990 }
3991
4031 console.log('AI21 model changed to', value);3992 console.log('AI21 model changed to', value);
4032 oai_settings.ai21_model = value;3993 oai_settings.ai21_model = value;
4033 }3994 }
@@ -4230,7 +4191,7 @@ async function onModelChange() {
4230 else if (['command-light-nightly', 'command-nightly'].includes(oai_settings.cohere_model)) {4191 else if (['command-light-nightly', 'command-nightly'].includes(oai_settings.cohere_model)) {
4231 $('#openai_max_context').attr('max', max_8k);4192 $('#openai_max_context').attr('max', max_8k);
4232 }4193 }
4233 else if (['command-r', 'command-r-plus'].includes(oai_settings.cohere_model)) {4194 else if (oai_settings.cohere_model.includes('command-r')) {
4234 $('#openai_max_context').attr('max', max_128k);4195 $('#openai_max_context').attr('max', max_128k);
4235 }4196 }
4236 else if (['c4ai-aya-23'].includes(oai_settings.cohere_model)) {4197 else if (['c4ai-aya-23'].includes(oai_settings.cohere_model)) {
@@ -4305,33 +4266,13 @@ async function onModelChange() {
4305 if (oai_settings.chat_completion_source == chat_completion_sources.AI21) {4266 if (oai_settings.chat_completion_source == chat_completion_sources.AI21) {
4306 if (oai_settings.max_context_unlocked) {4267 if (oai_settings.max_context_unlocked) {
4307 $('#openai_max_context').attr('max', unlocked_max);4268 $('#openai_max_context').attr('max', unlocked_max);
4308 } else {4269 } else if (oai_settings.ai21_model.includes('jamba-1.5') || oai_settings.ai21_model.includes('jamba-instruct')) {
4309 $('#openai_max_context').attr('max', ai21_max);4270 $('#openai_max_context').attr('max', max_256k);
4310 }4271 }
43114272
4312 oai_settings.openai_max_context = Math.min(oai_settings.openai_max_context, Number($('#openai_max_context').attr('max')));4273 oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
4313 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');4274 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
43144275 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
4315 oai_settings.temp_openai = Math.min(claude_max_temp, oai_settings.temp_openai);
4316 $('#temp_openai').attr('max', claude_max_temp).val(oai_settings.temp_openai).trigger('input');
4317
4318 oai_settings.freq_pen_openai = Math.min(j2_max_freq, oai_settings.freq_pen_openai < 0 ? 0 : oai_settings.freq_pen_openai);
4319 $('#freq_pen_openai').attr('min', 0).attr('max', j2_max_freq).val(oai_settings.freq_pen_openai).trigger('input');
4320
4321 oai_settings.pres_pen_openai = Math.min(j2_max_pres, oai_settings.pres_pen_openai < 0 ? 0 : oai_settings.pres_pen_openai);
4322 $('#pres_pen_openai').attr('min', 0).attr('max', j2_max_pres).val(oai_settings.pres_pen_openai).trigger('input');
4323
4324 oai_settings.top_k_openai = Math.min(j2_max_topk, oai_settings.top_k_openai);
4325 $('#top_k_openai').attr('max', j2_max_topk).val(oai_settings.top_k_openai).trigger('input');
4326 } else if (oai_settings.chat_completion_source != chat_completion_sources.AI21) {
4327 oai_settings.freq_pen_openai = Math.min(2.0, oai_settings.freq_pen_openai);
4328 $('#freq_pen_openai').attr('min', -2.0).attr('max', 2.0).val(oai_settings.freq_pen_openai).trigger('input');
4329
4330 oai_settings.pres_pen_openai = Math.min(2.0, oai_settings.pres_pen_openai);
4331 $('#pres_pen_openai').attr('min', -2.0).attr('max', 2.0).val(oai_settings.pres_pen_openai).trigger('input');
4332
4333 oai_settings.top_k_openai = Math.min(200, oai_settings.top_k_openai);
4334 $('#top_k_openai').attr('max', 200).val(oai_settings.top_k_openai).trigger('input');
4335 }4276 }
43364277
4337 if (oai_settings.chat_completion_source == chat_completion_sources.CUSTOM) {4278 if (oai_settings.chat_completion_source == chat_completion_sources.CUSTOM) {
@@ -4388,6 +4329,16 @@ async function onModelChange() {
4388 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');4329 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
4389 }4330 }
43904331
4332 if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) {
4333 oai_settings.pres_pen_openai = Math.min(Math.max(0, oai_settings.pres_pen_openai), 1);
4334 $('#pres_pen_openai').attr('max', 1).attr('min', 0).val(oai_settings.pres_pen_openai).trigger('input');
4335 oai_settings.freq_pen_openai = Math.min(Math.max(0, oai_settings.freq_pen_openai), 1);
4336 $('#freq_pen_openai').attr('max', 1).attr('min', 0).val(oai_settings.freq_pen_openai).trigger('input');
4337 } else {
4338 $('#pres_pen_openai').attr('max', 2).attr('min', -2).val(oai_settings.pres_pen_openai).trigger('input');
4339 $('#freq_pen_openai').attr('max', 2).attr('min', -2).val(oai_settings.freq_pen_openai).trigger('input');
4340 }
4341
4391 $('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max')));4342 $('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max')));
43924343
4393 saveSettingsDebounced();4344 saveSettingsDebounced();
@@ -4673,17 +4624,17 @@ function toggleChatCompletionForms() {
4673async function testApiConnection() {4624async function testApiConnection() {
4674 // Check if the previous request is still in progress4625 // Check if the previous request is still in progress
4675 if (is_send_press) {4626 if (is_send_press) {
4676 toastr.info('Please wait for the previous request to complete.');4627 toastr.info(t`Please wait for the previous request to complete.`);
4677 return;4628 return;
4678 }4629 }
46794630
4680 try {4631 try {
4681 const reply = await sendOpenAIRequest('quiet', [{ 'role': 'user', 'content': 'Hi' }]);4632 const reply = await sendOpenAIRequest('quiet', [{ 'role': 'user', 'content': 'Hi' }]);
4682 console.log(reply);4633 console.log(reply);
4683 toastr.success('API connection successful!');4634 toastr.success(t`API connection successful!`);
4684 }4635 }
4685 catch (err) {4636 catch (err) {
4686 toastr.error('Could not get a reply from API. Check your connection settings / API key and try again.');4637 toastr.error(t`Could not get a reply from API. Check your connection settings / API key and try again.`);
4687 }4638 }
4688}4639}
46894640
@@ -4752,11 +4703,14 @@ export function isImageInliningSupported() {
4752 'gemini-1.5-flash',4703 'gemini-1.5-flash',
4753 'gemini-1.5-flash-latest',4704 'gemini-1.5-flash-latest',
4754 'gemini-1.5-flash-001',4705 'gemini-1.5-flash-001',
4706 'gemini-1.5-flash-exp-0827',
4707 'gemini-1.5-flash-8b-exp-0827',
4755 'gemini-1.0-pro-vision-latest',4708 'gemini-1.0-pro-vision-latest',
4756 'gemini-1.5-pro',4709 'gemini-1.5-pro',
4757 'gemini-1.5-pro-latest',4710 'gemini-1.5-pro-latest',
4758 'gemini-1.5-pro-001',4711 'gemini-1.5-pro-001',
4759 'gemini-1.5-pro-exp-0801',4712 'gemini-1.5-pro-exp-0801',
4713 'gemini-1.5-pro-exp-0827',
4760 'gemini-pro-vision',4714 'gemini-pro-vision',
4761 'claude-3',4715 'claude-3',
4762 'claude-3-5',4716 'claude-3-5',
@@ -4837,7 +4791,7 @@ function onProxyPresetChange() {
4837 if (selectedPreset) {4791 if (selectedPreset) {
4838 setProxyPreset(selectedPreset.name, selectedPreset.url, selectedPreset.password);4792 setProxyPreset(selectedPreset.name, selectedPreset.url, selectedPreset.password);
4839 } else {4793 } else {
4840 console.error(`Proxy preset "${value}" not found in proxies array.`);4794 console.error(t`Proxy preset '${value}' not found in proxies array.`);
4841 }4795 }
4842 saveSettingsDebounced();4796 saveSettingsDebounced();
4843}4797}
@@ -4849,7 +4803,7 @@ $('#save_proxy').on('click', async function () {
48494803
4850 setProxyPreset(presetName, reverseProxy, proxyPassword);4804 setProxyPreset(presetName, reverseProxy, proxyPassword);
4851 saveSettingsDebounced();4805 saveSettingsDebounced();
4852 toastr.success('Proxy Saved');4806 toastr.success(t`Proxy Saved`);
4853 if ($('#openai_proxy_preset').val() !== presetName) {4807 if ($('#openai_proxy_preset').val() !== presetName) {
4854 const option = document.createElement('option');4808 const option = document.createElement('option');
4855 option.text = presetName;4809 option.text = presetName;
@@ -4883,9 +4837,9 @@ $('#delete_proxy').on('click', async function () {
48834837
4884 saveSettingsDebounced();4838 saveSettingsDebounced();
4885 $('#openai_proxy_preset').val(selected_proxy.name);4839 $('#openai_proxy_preset').val(selected_proxy.name);
4886 toastr.success('Proxy Deleted');4840 toastr.success(t`Proxy Deleted`);
4887 } else {4841 } else {
4888 toastr.error(`Could not find proxy with name "${presetName}"`);4842 toastr.error(t`Could not find proxy with name '${presetName}'`);
4889 }4843 }
4890});4844});
48914845
@@ -4953,12 +4907,6 @@ $(document).ready(async function () {
4953 saveSettingsDebounced();4907 saveSettingsDebounced();
4954 });4908 });
49554909
4956 $('#count_pen').on('input', function () {
4957 oai_settings.count_pen = Number($(this).val());
4958 $('#count_pen_counter').val(Number($(this).val()).toFixed(2));
4959 saveSettingsDebounced();
4960 });
4961
4962 $('#top_p_openai').on('input', function () {4910 $('#top_p_openai').on('input', function () {
4963 oai_settings.top_p_openai = Number($(this).val());4911 oai_settings.top_p_openai = Number($(this).val());
4964 $('#top_p_counter_openai').val(Number($(this).val()).toFixed(2));4912 $('#top_p_counter_openai').val(Number($(this).val()).toFixed(2));
@@ -5017,20 +4965,6 @@ $(document).ready(async function () {
5017 saveSettingsDebounced();4965 saveSettingsDebounced();
5018 });4966 });
50194967
5020 $('#use_ai21_tokenizer').on('change', function () {
5021 oai_settings.use_ai21_tokenizer = !!$('#use_ai21_tokenizer').prop('checked');
5022 oai_settings.use_ai21_tokenizer ? ai21_max = 8191 : ai21_max = 9200;
5023 oai_settings.openai_max_context = Math.min(ai21_max, oai_settings.openai_max_context);
5024 $('#openai_max_context').attr('max', ai21_max).val(oai_settings.openai_max_context).trigger('input');
5025 $('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max')));
5026 saveSettingsDebounced();
5027 });
5028
5029 $('#use_google_tokenizer').on('change', function () {
5030 oai_settings.use_google_tokenizer = !!$('#use_google_tokenizer').prop('checked');
5031 saveSettingsDebounced();
5032 });
5033
5034 $('#claude_use_sysprompt').on('change', function () {4968 $('#claude_use_sysprompt').on('change', function () {
5035 oai_settings.claude_use_sysprompt = !!$('#claude_use_sysprompt').prop('checked');4969 oai_settings.claude_use_sysprompt = !!$('#claude_use_sysprompt').prop('checked');
5036 $('#claude_human_sysprompt_message_block').toggle(oai_settings.claude_use_sysprompt);4970 $('#claude_human_sysprompt_message_block').toggle(oai_settings.claude_use_sysprompt);
public/scripts/power-user.js+1 -0
@@ -107,6 +107,7 @@ export const persona_description_positions = {
107 TOP_AN: 2,107 TOP_AN: 2,
108 BOTTOM_AN: 3,108 BOTTOM_AN: 3,
109 AT_DEPTH: 4,109 AT_DEPTH: 4,
110 NONE: 9,
110};111};
111112
112let power_user = {113let power_user = {
public/scripts/templates/chatRename.html+6 -0
@@ -0,0 +1,6 @@
1<h3 data-i18n="chat_rename_1">Enter the new name for the chat:</h3>
2<small>
3 <span data-i18n="chat_rename_2">!!Using an existing filename will produce an error!!</span><br>
4 <span data-i18n="chat_rename_3">This will break the link between checkpoint chats.</span><br>
5 <span data-i18n="chat_rename_4">No need to add '.jsonl' at the end.</span><br>
6</small>
\ No newline at end of file6 \ No newline at end of file
public/scripts/templates/newChatConfirm.html+4 -0
@@ -0,0 +1,4 @@
1<label for="del_chat_checkbox" class="checkbox_label justifyCenter" data-i18n="[title]If necessary, you can later restore this chat file from the /backups folder" title="If necessary, you can later restore this chat file from the /backups folder">
2 <input type="checkbox" id="del_chat_checkbox" />
3 <small data-i18n="Also delete the current chat file">Also delete the current chat file</small>
4</label>
\ No newline at end of file4 \ No newline at end of file
public/scripts/templates/proxyConnectionWarning.html+1 -0
@@ -0,0 +1 @@
1<span data-i18n="Are you sure you want to connect to the following proxy URL?">Are you sure you want to connect to the following proxy URL?</span><var>{{proxyURL}}</var>
\ No newline at end of file1 \ No newline at end of file
public/scripts/textgen-settings.js+8 -0
@@ -188,6 +188,8 @@ const settings = {
188 custom_model: '',188 custom_model: '',
189 bypass_status_check: false,189 bypass_status_check: false,
190 openrouter_allow_fallbacks: true,190 openrouter_allow_fallbacks: true,
191 xtc_threshold: 0.1,
192 xtc_probability: 0,
191};193};
192194
193export let textgenerationwebui_banned_in_macros = [];195export let textgenerationwebui_banned_in_macros = [];
@@ -263,6 +265,8 @@ export const setting_names = [
263 'custom_model',265 'custom_model',
264 'bypass_status_check',266 'bypass_status_check',
265 'openrouter_allow_fallbacks',267 'openrouter_allow_fallbacks',
268 'xtc_threshold',
269 'xtc_probability',
266];270];
267271
268const DYNATEMP_BLOCK = document.getElementById('dynatemp_block_ooba');272const DYNATEMP_BLOCK = document.getElementById('dynatemp_block_ooba');
@@ -718,6 +722,8 @@ jQuery(function () {
718 'dry_multiplier_textgenerationwebui': 0,722 'dry_multiplier_textgenerationwebui': 0,
719 'dry_base_textgenerationwebui': 1.75,723 'dry_base_textgenerationwebui': 1.75,
720 'dry_penalty_last_n_textgenerationwebui': 0,724 'dry_penalty_last_n_textgenerationwebui': 0,
725 'xtc_threshold_textgenerationwebui': 0.1,
726 'xtc_probability_textgenerationwebui': 0,
721 };727 };
722728
723 for (const [id, value] of Object.entries(inputs)) {729 for (const [id, value] of Object.entries(inputs)) {
@@ -1156,6 +1162,8 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
1156 'api_server': getTextGenServer(),1162 'api_server': getTextGenServer(),
1157 'legacy_api': settings.legacy_api && (settings.type === OOBA || settings.type === APHRODITE),1163 'legacy_api': settings.legacy_api && (settings.type === OOBA || settings.type === APHRODITE),
1158 'sampler_order': settings.type === textgen_types.KOBOLDCPP ? settings.sampler_order : undefined,1164 'sampler_order': settings.type === textgen_types.KOBOLDCPP ? settings.sampler_order : undefined,
1165 'xtc_threshold': settings.xtc_threshold,
1166 'xtc_probability': settings.xtc_probability,
1159 };1167 };
1160 const nonAphroditeParams = {1168 const nonAphroditeParams = {
1161 'rep_pen': settings.rep_pen,1169 'rep_pen': settings.rep_pen,
public/scripts/tokenizers.js+26 -23
@@ -27,6 +27,7 @@ export const tokenizers = {
27 CLAUDE: 11,27 CLAUDE: 11,
28 LLAMA3: 12,28 LLAMA3: 12,
29 GEMMA: 13,29 GEMMA: 13,
30 JAMBA: 14,
30 BEST_MATCH: 99,31 BEST_MATCH: 99,
31};32};
3233
@@ -36,6 +37,7 @@ export const SENTENCEPIECE_TOKENIZERS = [
36 tokenizers.YI,37 tokenizers.YI,
37 tokenizers.LLAMA3,38 tokenizers.LLAMA3,
38 tokenizers.GEMMA,39 tokenizers.GEMMA,
40 tokenizers.JAMBA,
39 // uncomment when NovelAI releases Kayra and Clio weights, lol41 // uncomment when NovelAI releases Kayra and Clio weights, lol
40 //tokenizers.NERD,42 //tokenizers.NERD,
41 //tokenizers.NERD2,43 //tokenizers.NERD2,
@@ -98,6 +100,11 @@ const TOKENIZER_URLS = {
98 decode: '/api/tokenizers/gemma/decode',100 decode: '/api/tokenizers/gemma/decode',
99 count: '/api/tokenizers/gemma/encode',101 count: '/api/tokenizers/gemma/encode',
100 },102 },
103 [tokenizers.JAMBA]: {
104 encode: '/api/tokenizers/jamba/encode',
105 decode: '/api/tokenizers/jamba/decode',
106 count: '/api/tokenizers/jamba/encode',
107 },
101 [tokenizers.API_TEXTGENERATIONWEBUI]: {108 [tokenizers.API_TEXTGENERATIONWEBUI]: {
102 encode: '/api/tokenizers/remote/textgenerationwebui/encode',109 encode: '/api/tokenizers/remote/textgenerationwebui/encode',
103 count: '/api/tokenizers/remote/textgenerationwebui/encode',110 count: '/api/tokenizers/remote/textgenerationwebui/encode',
@@ -164,7 +171,7 @@ export function getAvailableTokenizers() {
164 tokenizerId: Number(tokenizerOption.value),171 tokenizerId: Number(tokenizerOption.value),
165 tokenizerKey: Object.entries(tokenizers).find(([_, value]) => value === Number(tokenizerOption.value))[0].toLocaleLowerCase(),172 tokenizerKey: Object.entries(tokenizers).find(([_, value]) => value === Number(tokenizerOption.value))[0].toLocaleLowerCase(),
166 tokenizerName: tokenizerOption.text,173 tokenizerName: tokenizerOption.text,
167 }))174 }));
168}175}
169176
170/**177/**
@@ -280,6 +287,12 @@ export function getTokenizerBestMatch(forApi) {
280 if (model.includes('gemma')) {287 if (model.includes('gemma')) {
281 return tokenizers.GEMMA;288 return tokenizers.GEMMA;
282 }289 }
290 if (model.includes('yi')) {
291 return tokenizers.YI;
292 }
293 if (model.includes('jamba')) {
294 return tokenizers.JAMBA;
295 }
283 }296 }
284297
285 return tokenizers.LLAMA;298 return tokenizers.LLAMA;
@@ -497,6 +510,7 @@ export function getTokenizerModel() {
497 const mistralTokenizer = 'mistral';510 const mistralTokenizer = 'mistral';
498 const yiTokenizer = 'yi';511 const yiTokenizer = 'yi';
499 const gemmaTokenizer = 'gemma';512 const gemmaTokenizer = 'gemma';
513 const jambaTokenizer = 'jamba';
500514
501 // Assuming no one would use it for different models.. right?515 // Assuming no one would use it for different models.. right?
502 if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) {516 if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) {
@@ -562,12 +576,19 @@ export function getTokenizerModel() {
562 else if (oai_settings.openrouter_model.includes('GPT-NeoXT')) {576 else if (oai_settings.openrouter_model.includes('GPT-NeoXT')) {
563 return gpt2Tokenizer;577 return gpt2Tokenizer;
564 }578 }
579 else if (oai_settings.openrouter_model.includes('jamba')) {
580 return jambaTokenizer;
581 }
565 }582 }
566583
567 if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) {584 if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) {
568 return gemmaTokenizer;585 return gemmaTokenizer;
569 }586 }
570587
588 if (oai_settings.chat_completion_source == chat_completion_sources.AI21) {
589 return jambaTokenizer;
590 }
591
571 if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {592 if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
572 return claudeTokenizer;593 return claudeTokenizer;
573 }594 }
@@ -626,16 +647,7 @@ export function getTokenizerModel() {
626 * @deprecated Use countTokensOpenAIAsync instead.647 * @deprecated Use countTokensOpenAIAsync instead.
627 */648 */
628export function countTokensOpenAI(messages, full = false) {649export function countTokensOpenAI(messages, full = false) {
629 const shouldTokenizeAI21 = oai_settings.chat_completion_source === chat_completion_sources.AI21 && oai_settings.use_ai21_tokenizer;650 const tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
630 const shouldTokenizeGoogle = oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE && oai_settings.use_google_tokenizer;
631 let tokenizerEndpoint = '';
632 if (shouldTokenizeAI21) {
633 tokenizerEndpoint = '/api/tokenizers/ai21/count';
634 } else if (shouldTokenizeGoogle) {
635 tokenizerEndpoint = `/api/tokenizers/google/count?model=${getTokenizerModel()}&reverse_proxy=${oai_settings.reverse_proxy}&proxy_password=${oai_settings.proxy_password}`;
636 } else {
637 tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
638 }
639 const cacheObject = getTokenCacheObject();651 const cacheObject = getTokenCacheObject();
640652
641 if (!Array.isArray(messages)) {653 if (!Array.isArray(messages)) {
@@ -647,7 +659,7 @@ export function countTokensOpenAI(messages, full = false) {
647 for (const message of messages) {659 for (const message of messages) {
648 const model = getTokenizerModel();660 const model = getTokenizerModel();
649661
650 if (model === 'claude' || shouldTokenizeAI21 || shouldTokenizeGoogle) {662 if (model === 'claude') {
651 full = true;663 full = true;
652 }664 }
653665
@@ -687,16 +699,7 @@ export function countTokensOpenAI(messages, full = false) {
687 * @returns {Promise<number>} Token count.699 * @returns {Promise<number>} Token count.
688 */700 */
689export async function countTokensOpenAIAsync(messages, full = false) {701export async function countTokensOpenAIAsync(messages, full = false) {
690 const shouldTokenizeAI21 = oai_settings.chat_completion_source === chat_completion_sources.AI21 && oai_settings.use_ai21_tokenizer;702 const tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
691 const shouldTokenizeGoogle = oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE && oai_settings.use_google_tokenizer;
692 let tokenizerEndpoint = '';
693 if (shouldTokenizeAI21) {
694 tokenizerEndpoint = '/api/tokenizers/ai21/count';
695 } else if (shouldTokenizeGoogle) {
696 tokenizerEndpoint = `/api/tokenizers/google/count?model=${getTokenizerModel()}`;
697 } else {
698 tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
699 }
700 const cacheObject = getTokenCacheObject();703 const cacheObject = getTokenCacheObject();
701704
702 if (!Array.isArray(messages)) {705 if (!Array.isArray(messages)) {
@@ -708,7 +711,7 @@ export async function countTokensOpenAIAsync(messages, full = false) {
708 for (const message of messages) {711 for (const message of messages) {
709 const model = getTokenizerModel();712 const model = getTokenizerModel();
710713
711 if (model === 'claude' || shouldTokenizeAI21 || shouldTokenizeGoogle) {714 if (model === 'claude') {
712 full = true;715 full = true;
713 }716 }
714717
public/scripts/world-info.js+6 -2
@@ -4777,8 +4777,10 @@ jQuery(() => {
4777 world_info_min_activations = Number($(this).val());4777 world_info_min_activations = Number($(this).val());
4778 $('#world_info_min_activations_counter').val(world_info_min_activations);4778 $('#world_info_min_activations_counter').val(world_info_min_activations);
47794779
4780 if (world_info_min_activations !== 0) {4780 if (world_info_min_activations !== 0 && world_info_max_recursion_steps !== 0) {
4781 $('#world_info_max_recursion_steps').val(0).trigger('input');4781 $('#world_info_max_recursion_steps').val(0).trigger('input');
4782 flashHighlight($('#world_info_max_recursion_steps').parent()); // flash the other control to show it has changed
4783 console.info('[WI] Max recursion steps set to 0, as min activations is set to', world_info_min_activations);
4782 } else {4784 } else {
4783 saveSettings();4785 saveSettings();
4784 }4786 }
@@ -4840,8 +4842,10 @@ jQuery(() => {
4840 $('#world_info_max_recursion_steps').on('input', function () {4842 $('#world_info_max_recursion_steps').on('input', function () {
4841 world_info_max_recursion_steps = Number($(this).val());4843 world_info_max_recursion_steps = Number($(this).val());
4842 $('#world_info_max_recursion_steps_counter').val(world_info_max_recursion_steps);4844 $('#world_info_max_recursion_steps_counter').val(world_info_max_recursion_steps);
4843 if (world_info_max_recursion_steps !== 0) {4845 if (world_info_max_recursion_steps !== 0 && world_info_min_activations !== 0) {
4844 $('#world_info_min_activations').val(0).trigger('input');4846 $('#world_info_min_activations').val(0).trigger('input');
4847 flashHighlight($('#world_info_min_activations').parent()); // flash the other control to show it has changed
4848 console.info('[WI] Min activations set to 0, as max recursion steps is set to', world_info_max_recursion_steps);
4845 } else {4849 } else {
4846 saveSettings();4850 saveSettings();
4847 }4851 }
public/style.css+0 -1
@@ -2421,7 +2421,6 @@ input[type="file"] {
2421}2421}
24222422
2423#rm_print_characters_block .text_block {2423#rm_print_characters_block .text_block {
2424 height: 100%;
2425 width: 100%;2424 width: 100%;
2426 opacity: 0.5;2425 opacity: 0.5;
2427 margin: 0 auto 1px auto;2426 margin: 0 auto 1px auto;
src/cohere-stream.js+126 -0
@@ -0,0 +1,126 @@
1const DATA_PREFIX = 'data:';
2
3/**
4 * Borrowed from Cohere SDK (MIT License)
5 * https://github.com/cohere-ai/cohere-typescript/blob/main/src/core/streaming-fetcher/Stream.ts
6 * Copyright (c) 2021 Cohere
7 */
8class CohereStream {
9 /** @type {ReadableStream} */
10 stream;
11 /** @type {string} */
12 prefix;
13 /** @type {string} */
14 messageTerminator;
15 /** @type {string|undefined} */
16 streamTerminator;
17 /** @type {AbortController} */
18 controller = new AbortController();
19
20 constructor({ stream, eventShape }) {
21 this.stream = stream;
22 if (eventShape.type === 'sse') {
23 this.prefix = DATA_PREFIX;
24 this.messageTerminator = '\n';
25 this.streamTerminator = eventShape.streamTerminator;
26 } else {
27 this.messageTerminator = eventShape.messageTerminator;
28 }
29 }
30
31 async *iterMessages() {
32 const stream = readableStreamAsyncIterable(this.stream);
33 let buf = '';
34 let prefixSeen = false;
35 let parsedAnyMessages = false;
36 for await (const chunk of stream) {
37 buf += this.decodeChunk(chunk);
38
39 let terminatorIndex;
40 // Parse the chunk into as many messages as possible
41 while ((terminatorIndex = buf.indexOf(this.messageTerminator)) >= 0) {
42 // Extract the line from the buffer
43 let line = buf.slice(0, terminatorIndex + 1);
44 buf = buf.slice(terminatorIndex + 1);
45
46 // Skip empty lines
47 if (line.length === 0) {
48 continue;
49 }
50
51 // Skip the chunk until the prefix is found
52 if (!prefixSeen && this.prefix != null) {
53 const prefixIndex = line.indexOf(this.prefix);
54 if (prefixIndex === -1) {
55 continue;
56 }
57 prefixSeen = true;
58 line = line.slice(prefixIndex + this.prefix.length);
59 }
60
61 // If the stream terminator is present, return
62 if (this.streamTerminator != null && line.includes(this.streamTerminator)) {
63 return;
64 }
65
66 // Otherwise, yield message from the prefix to the terminator
67 const message = JSON.parse(line);
68 yield message;
69 prefixSeen = false;
70 parsedAnyMessages = true;
71 }
72 }
73
74 if (!parsedAnyMessages && buf.length > 0) {
75 try {
76 yield JSON.parse(buf);
77 } catch (e) {
78 console.error('Error parsing message:', e);
79 }
80 }
81 }
82
83 async *[Symbol.asyncIterator]() {
84 for await (const message of this.iterMessages()) {
85 yield message;
86 }
87 }
88
89 decodeChunk(chunk) {
90 const decoder = new TextDecoder('utf8');
91 return decoder.decode(chunk);
92 }
93}
94
95function readableStreamAsyncIterable(stream) {
96 if (stream[Symbol.asyncIterator]) {
97 return stream;
98 }
99
100 const reader = stream.getReader();
101 return {
102 async next() {
103 try {
104 const result = await reader.read();
105 if (result?.done) {
106 reader.releaseLock();
107 } // release lock when stream becomes closed
108 return result;
109 } catch (e) {
110 reader.releaseLock(); // release lock when stream becomes errored
111 throw e;
112 }
113 },
114 async return() {
115 const cancelPromise = reader.cancel();
116 reader.releaseLock();
117 await cancelPromise;
118 return { done: true, value: undefined };
119 },
120 [Symbol.asyncIterator]() {
121 return this;
122 },
123 };
124}
125
126module.exports = CohereStream;
src/endpoints/backends/chat-completions.js+56 -86
@@ -1,11 +1,11 @@
1const express = require('express');1const express = require('express');
2const fetch = require('node-fetch').default;2const fetch = require('node-fetch').default;
3const Readable = require('stream').Readable;
43
5const { jsonParser } = require('../../express-common');4const { jsonParser } = require('../../express-common');
6const { CHAT_COMPLETION_SOURCES, GEMINI_SAFETY, BISON_SAFETY, OPENROUTER_HEADERS } = require('../../constants');5const { CHAT_COMPLETION_SOURCES, GEMINI_SAFETY, BISON_SAFETY, OPENROUTER_HEADERS } = require('../../constants');
7const { forwardFetchResponse, getConfigValue, tryParse, uuidv4, mergeObjectWithYaml, excludeKeysByYaml, color } = require('../../util');6const { forwardFetchResponse, getConfigValue, tryParse, uuidv4, mergeObjectWithYaml, excludeKeysByYaml, color } = require('../../util');
8const { convertClaudeMessages, convertGooglePrompt, convertTextCompletionPrompt, convertCohereMessages, convertMistralMessages, convertCohereTools } = require('../../prompt-converters');7const { convertClaudeMessages, convertGooglePrompt, convertTextCompletionPrompt, convertCohereMessages, convertMistralMessages, convertCohereTools, convertAI21Messages } = require('../../prompt-converters');
8const CohereStream = require('../../cohere-stream');
99
10const { readSecret, SECRET_KEYS } = require('../secrets');10const { readSecret, SECRET_KEYS } = require('../secrets');
11const { getTokenizerModel, getSentencepiceTokenizer, getTiktokenTokenizer, sentencepieceTokenizers, TEXT_COMPLETION_MODELS } = require('../tokenizers');11const { getTokenizerModel, getSentencepiceTokenizer, getTiktokenTokenizer, sentencepieceTokenizers, TEXT_COMPLETION_MODELS } = require('../tokenizers');
@@ -19,6 +19,7 @@ const API_GROQ = 'https://api.groq.com/openai/v1';
19const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';19const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';
20const API_01AI = 'https://api.01.ai/v1';20const API_01AI = 'https://api.01.ai/v1';
21const API_BLOCKENTROPY = 'https://api.blockentropy.ai/v1';21const API_BLOCKENTROPY = 'https://api.blockentropy.ai/v1';
22const API_AI21 = 'https://api.ai21.com/studio/v1';
2223
23/**24/**
24 * Applies a post-processing step to the generated messages.25 * Applies a post-processing step to the generated messages.
@@ -40,52 +41,30 @@ function postProcessPrompt(messages, type, charName, userName) {
40/**41/**
41 * Ollama strikes back. Special boy #2's steaming routine.42 * Ollama strikes back. Special boy #2's steaming routine.
42 * Wrap this abomination into proper SSE stream, again.43 * Wrap this abomination into proper SSE stream, again.
43 * @param {import('node-fetch').Response} jsonStream JSON stream44 * @param {Response} jsonStream JSON stream
44 * @param {import('express').Request} request Express request45 * @param {import('express').Request} request Express request
45 * @param {import('express').Response} response Express response46 * @param {import('express').Response} response Express response
46 * @returns {Promise<any>} Nothing valuable47 * @returns {Promise<any>} Nothing valuable
47 */48 */
48async function parseCohereStream(jsonStream, request, response) {49async function parseCohereStream(jsonStream, request, response) {
49 try {50 try {
50 let partialData = '';51 const stream = new CohereStream({ stream: jsonStream.body, eventShape: { type: 'json', messageTerminator: '\n' } });
51 jsonStream.body.on('data', (data) => {52
52 const chunk = data.toString();53 for await (const json of stream.iterMessages()) {
53 partialData += chunk;
54 while (true) {
55 let json;
56 try {
57 json = JSON.parse(partialData);
58 } catch (e) {
59 break;
60 }
61 if (json.message) {54 if (json.message) {
62 const message = json.message || 'Unknown error';55 const message = json.message || 'Unknown error';
63 const chunk = { error: { message: message } };56 const chunk = { error: { message: message } };
64 response.write(`data: ${JSON.stringify(chunk)}\n\n`);57 response.write(`data: ${JSON.stringify(chunk)}\n\n`);
65 partialData = '';
66 break;
67 } else if (json.event_type === 'text-generation') {58 } else if (json.event_type === 'text-generation') {
68 const text = json.text || '';59 const text = json.text || '';
69 const chunk = { choices: [{ text }] };60 const chunk = { choices: [{ text }] };
70 response.write(`data: ${JSON.stringify(chunk)}\n\n`);61 response.write(`data: ${JSON.stringify(chunk)}\n\n`);
71 partialData = '';
72 } else {
73 partialData = '';
74 break;
75 }62 }
76 }63 }
77 });
7864
79 request.socket.on('close', function () {
80 if (jsonStream.body instanceof Readable) jsonStream.body.destroy();
81 response.end();
82 });
83
84 jsonStream.body.on('end', () => {
85 console.log('Streaming request finished');65 console.log('Streaming request finished');
86 response.write('data: [DONE]\n\n');66 response.write('data: [DONE]\n\n');
87 response.end();67 response.end();
88 });
89 } catch (error) {68 } catch (error) {
90 console.log('Error forwarding streaming response:', error);69 console.log('Error forwarding streaming response:', error);
91 if (!response.headersSent) {70 if (!response.headersSent) {
@@ -233,7 +212,7 @@ async function sendScaleRequest(request, response) {
233212
234 if (!generateResponse.ok) {213 if (!generateResponse.ok) {
235 console.log(`Scale API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);214 console.log(`Scale API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
236 return response.status(generateResponse.status).send({ error: true });215 return response.status(500).send({ error: true });
237 }216 }
238217
239 const generateResponseJson = await generateResponse.json();218 const generateResponseJson = await generateResponse.json();
@@ -413,6 +392,16 @@ async function sendAI21Request(request, response) {
413 request.socket.on('close', function () {392 request.socket.on('close', function () {
414 controller.abort();393 controller.abort();
415 });394 });
395 const convertedPrompt = convertAI21Messages(request.body.messages, request.body.char_name, request.body.user_name);
396 const body = {
397 messages: convertedPrompt,
398 model: request.body.model,
399 max_tokens: request.body.max_tokens,
400 temperature: request.body.temperature,
401 top_p: request.body.top_p,
402 stop: request.body.stop,
403 stream: request.body.stream,
404 };
416 const options = {405 const options = {
417 method: 'POST',406 method: 'POST',
418 headers: {407 headers: {
@@ -420,59 +409,35 @@ async function sendAI21Request(request, response) {
420 'content-type': 'application/json',409 'content-type': 'application/json',
421 Authorization: `Bearer ${readSecret(request.user.directories, SECRET_KEYS.AI21)}`,410 Authorization: `Bearer ${readSecret(request.user.directories, SECRET_KEYS.AI21)}`,
422 },411 },
423 body: JSON.stringify({412 body: JSON.stringify(body),
424 numResults: 1,
425 maxTokens: request.body.max_tokens,
426 minTokens: 0,
427 temperature: request.body.temperature,
428 topP: request.body.top_p,
429 stopSequences: request.body.stop_tokens,
430 topKReturn: request.body.top_k,
431 frequencyPenalty: {
432 scale: request.body.frequency_penalty * 100,
433 applyToWhitespaces: false,
434 applyToPunctuations: false,
435 applyToNumbers: false,
436 applyToStopwords: false,
437 applyToEmojis: false,
438 },
439 presencePenalty: {
440 scale: request.body.presence_penalty,
441 applyToWhitespaces: false,
442 applyToPunctuations: false,
443 applyToNumbers: false,
444 applyToStopwords: false,
445 applyToEmojis: false,
446 },
447 countPenalty: {
448 scale: request.body.count_pen,
449 applyToWhitespaces: false,
450 applyToPunctuations: false,
451 applyToNumbers: false,
452 applyToStopwords: false,
453 applyToEmojis: false,
454 },
455 prompt: request.body.messages,
456 }),
457 signal: controller.signal,413 signal: controller.signal,
458 };414 };
459415
460 fetch(`https://api.ai21.com/studio/v1/${request.body.model}/complete`, options)416 console.log('AI21 request:', body);
461 .then(r => r.json())417
462 .then(r => {418 try {
463 if (r.completions === undefined) {419 const generateResponse = await fetch(API_AI21 + '/chat/completions', options);
464 console.log(r);420 if (request.body.stream) {
421 forwardFetchResponse(generateResponse, response);
465 } else {422 } else {
466 console.log(r.completions[0].data.text);423 if (!generateResponse.ok) {
424 const errorText = await generateResponse.text();
425 console.log(`AI21 API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
426 const errorJson = tryParse(errorText) ?? { error: true };
427 return response.status(500).send(errorJson);
428 }
429 const generateResponseJson = await generateResponse.json();
430 console.log('AI21 response:', generateResponseJson);
431 return response.send(generateResponseJson);
432 }
433 } catch (error) {
434 console.log('Error communicating with AI21 API: ', error);
435 if (!response.headersSent) {
436 response.send({ error: true });
437 } else {
438 response.end();
439 }
467 }440 }
468 const reply = { choices: [{ 'message': { 'content': r.completions?.[0]?.data?.text } }] };
469 return response.send(reply);
470 })
471 .catch(err => {
472 console.error(err);
473 return response.send({ error: true });
474 });
475
476}441}
477442
478/**443/**
@@ -531,10 +496,10 @@ async function sendMistralAIRequest(request, response) {
531 forwardFetchResponse(generateResponse, response);496 forwardFetchResponse(generateResponse, response);
532 } else {497 } else {
533 if (!generateResponse.ok) {498 if (!generateResponse.ok) {
534 console.log(`MistralAI API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);499 const errorText = await generateResponse.text();
535 // a 401 unauthorized response breaks the frontend auth, so return a 500 instead. prob a better way of dealing with this.500 console.log(`MistralAI API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
536 // 401s are already handled by the streaming processor and dont pop up an error toast, that should probably be fixed too.501 const errorJson = tryParse(errorText) ?? { error: true };
537 return response.status(generateResponse.status === 401 ? 500 : generateResponse.status).send({ error: true });502 return response.status(500).send(errorJson);
538 }503 }
539 const generateResponseJson = await generateResponse.json();504 const generateResponseJson = await generateResponse.json();
540 console.log('MistralAI response:', generateResponseJson);505 console.log('MistralAI response:', generateResponseJson);
@@ -607,6 +572,11 @@ async function sendCohereRequest(request, response) {
607 search_queries_only: false,572 search_queries_only: false,
608 };573 };
609574
575 const canDoSafetyMode = String(request.body.model).endsWith('08-2024');
576 if (canDoSafetyMode) {
577 requestBody.safety_mode = 'NONE';
578 }
579
610 console.log('Cohere request:', requestBody);580 console.log('Cohere request:', requestBody);
611581
612 const config = {582 const config = {
@@ -623,15 +593,15 @@ async function sendCohereRequest(request, response) {
623 const apiUrl = API_COHERE + '/chat';593 const apiUrl = API_COHERE + '/chat';
624594
625 if (request.body.stream) {595 if (request.body.stream) {
626 const stream = await fetch(apiUrl, config);596 const stream = await global.fetch(apiUrl, config);
627 parseCohereStream(stream, request, response);597 parseCohereStream(stream, request, response);
628 } else {598 } else {
629 const generateResponse = await fetch(apiUrl, config);599 const generateResponse = await fetch(apiUrl, config);
630 if (!generateResponse.ok) {600 if (!generateResponse.ok) {
631 console.log(`Cohere API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);601 const errorText = await generateResponse.text();
632 // a 401 unauthorized response breaks the frontend auth, so return a 500 instead. prob a better way of dealing with this.602 console.log(`Cohere API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
633 // 401s are already handled by the streaming processor and dont pop up an error toast, that should probably be fixed too.603 const errorJson = tryParse(errorText) ?? { error: true };
634 return response.status(generateResponse.status === 401 ? 500 : generateResponse.status).send({ error: true });604 return response.status(500).send(errorJson);
635 }605 }
636 const generateResponseJson = await generateResponse.json();606 const generateResponseJson = await generateResponse.json();
637 console.log('Cohere response:', generateResponseJson);607 console.log('Cohere response:', generateResponseJson);
src/endpoints/novelai.js+5 -5
@@ -6,6 +6,7 @@ const { readAllChunks, extractFileFromZipBuffer, forwardFetchResponse } = requir
6const { jsonParser } = require('../express-common');6const { jsonParser } = require('../express-common');
77
8const API_NOVELAI = 'https://api.novelai.net';8const API_NOVELAI = 'https://api.novelai.net';
9const TEXT_NOVELAI = 'https://text.novelai.net';
9const IMAGE_NOVELAI = 'https://image.novelai.net';10const IMAGE_NOVELAI = 'https://image.novelai.net';
1011
11// Ban bracket generation, plus defaults12// Ban bracket generation, plus defaults
@@ -155,7 +156,7 @@ router.post('/generate', jsonParser, async function (req, res) {
155 'repetition_penalty_slope': req.body.repetition_penalty_slope,156 'repetition_penalty_slope': req.body.repetition_penalty_slope,
156 'repetition_penalty_frequency': req.body.repetition_penalty_frequency,157 'repetition_penalty_frequency': req.body.repetition_penalty_frequency,
157 'repetition_penalty_presence': req.body.repetition_penalty_presence,158 'repetition_penalty_presence': req.body.repetition_penalty_presence,
158 'repetition_penalty_whitelist': isNewModel ? repPenaltyAllowList : null,159 'repetition_penalty_whitelist': isNewModel ? repPenaltyAllowList.flat() : null,
159 'top_a': req.body.top_a,160 'top_a': req.body.top_a,
160 'top_p': req.body.top_p,161 'top_p': req.body.top_p,
161 'top_k': req.body.top_k,162 'top_k': req.body.top_k,
@@ -178,9 +179,7 @@ router.post('/generate', jsonParser, async function (req, res) {
178 };179 };
179180
180 // Tells the model to stop generation at '>'181 // Tells the model to stop generation at '>'
181 if ('theme_textadventure' === req.body.prefix &&182 if ('theme_textadventure' === req.body.prefix && isNewModel) {
182 (true === req.body.model.includes('clio') ||
183 true === req.body.model.includes('kayra'))) {
184 data.parameters.eos_token_id = 49405;183 data.parameters.eos_token_id = 49405;
185 }184 }
186185
@@ -193,7 +192,8 @@ router.post('/generate', jsonParser, async function (req, res) {
193 };192 };
194193
195 try {194 try {
196 const url = req.body.streaming ? `${API_NOVELAI}/ai/generate-stream` : `${API_NOVELAI}/ai/generate`;195 const baseURL = req.body.model.includes('kayra') ? TEXT_NOVELAI : API_NOVELAI;
196 const url = req.body.streaming ? `${baseURL}/ai/generate-stream` : `${baseURL}/ai/generate`;
197 const response = await fetch(url, { method: 'POST', timeout: 0, ...args });197 const response = await fetch(url, { method: 'POST', timeout: 0, ...args });
198198
199 if (req.body.streaming) {199 if (req.body.streaming) {
src/endpoints/tokenizers.js+27 -47
@@ -144,6 +144,7 @@ const spp_nerd_v2 = new SentencePieceTokenizer('src/tokenizers/nerdstash_v2.mode
144const spp_mistral = new SentencePieceTokenizer('src/tokenizers/mistral.model');144const spp_mistral = new SentencePieceTokenizer('src/tokenizers/mistral.model');
145const spp_yi = new SentencePieceTokenizer('src/tokenizers/yi.model');145const spp_yi = new SentencePieceTokenizer('src/tokenizers/yi.model');
146const spp_gemma = new SentencePieceTokenizer('src/tokenizers/gemma.model');146const spp_gemma = new SentencePieceTokenizer('src/tokenizers/gemma.model');
147const spp_jamba = new SentencePieceTokenizer('src/tokenizers/jamba.model');
147const claude_tokenizer = new WebTokenizer('src/tokenizers/claude.json');148const claude_tokenizer = new WebTokenizer('src/tokenizers/claude.json');
148const llama3_tokenizer = new WebTokenizer('src/tokenizers/llama3.json');149const llama3_tokenizer = new WebTokenizer('src/tokenizers/llama3.json');
149150
@@ -154,6 +155,7 @@ const sentencepieceTokenizers = [
154 'mistral',155 'mistral',
155 'yi',156 'yi',
156 'gemma',157 'gemma',
158 'jamba',
157];159];
158160
159/**161/**
@@ -186,6 +188,10 @@ function getSentencepiceTokenizer(model) {
186 return spp_gemma;188 return spp_gemma;
187 }189 }
188190
191 if (model.includes('jamba')) {
192 return spp_jamba;
193 }
194
189 return null;195 return null;
190}196}
191197
@@ -322,6 +328,10 @@ function getTokenizerModel(requestModel) {
322 return 'gemma';328 return 'gemma';
323 }329 }
324330
331 if (requestModel.includes('jamba')) {
332 return 'jamba';
333 }
334
325 // default335 // default
326 return 'gpt-3.5-turbo';336 return 'gpt-3.5-turbo';
327}337}
@@ -537,59 +547,13 @@ function createWebTokenizerDecodingHandler(tokenizer) {
537547
538const router = express.Router();548const router = express.Router();
539549
540router.post('/ai21/count', jsonParser, async function (req, res) {
541 if (!req.body) return res.sendStatus(400);
542 const key = readSecret(req.user.directories, SECRET_KEYS.AI21);
543 const options = {
544 method: 'POST',
545 headers: {
546 accept: 'application/json',
547 'content-type': 'application/json',
548 Authorization: `Bearer ${key}`,
549 },
550 body: JSON.stringify({ text: req.body[0].content }),
551 };
552
553 try {
554 const response = await fetch('https://api.ai21.com/studio/v1/tokenize', options);
555 const data = await response.json();
556 return res.send({ 'token_count': data?.tokens?.length || 0 });
557 } catch (err) {
558 console.error(err);
559 return res.send({ 'token_count': 0 });
560 }
561});
562
563router.post('/google/count', jsonParser, async function (req, res) {
564 if (!req.body) return res.sendStatus(400);
565 const options = {
566 method: 'POST',
567 headers: {
568 accept: 'application/json',
569 'content-type': 'application/json',
570 },
571 body: JSON.stringify({ contents: convertGooglePrompt(req.body, String(req.query.model)).contents }),
572 };
573 try {
574 const reverseProxy = req.query.reverse_proxy?.toString() || '';
575 const proxyPassword = req.query.proxy_password?.toString() || '';
576 const apiKey = reverseProxy ? proxyPassword : readSecret(req.user.directories, SECRET_KEYS.MAKERSUITE);
577 const apiUrl = new URL(reverseProxy || API_MAKERSUITE);
578 const response = await fetch(`${apiUrl.origin}/v1beta/models/${req.query.model}:countTokens?key=${apiKey}`, options);
579 const data = await response.json();
580 return res.send({ 'token_count': data?.totalTokens || 0 });
581 } catch (err) {
582 console.error(err);
583 return res.send({ 'token_count': 0 });
584 }
585});
586
587router.post('/llama/encode', jsonParser, createSentencepieceEncodingHandler(spp_llama));550router.post('/llama/encode', jsonParser, createSentencepieceEncodingHandler(spp_llama));
588router.post('/nerdstash/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd));551router.post('/nerdstash/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd));
589router.post('/nerdstash_v2/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd_v2));552router.post('/nerdstash_v2/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd_v2));
590router.post('/mistral/encode', jsonParser, createSentencepieceEncodingHandler(spp_mistral));553router.post('/mistral/encode', jsonParser, createSentencepieceEncodingHandler(spp_mistral));
591router.post('/yi/encode', jsonParser, createSentencepieceEncodingHandler(spp_yi));554router.post('/yi/encode', jsonParser, createSentencepieceEncodingHandler(spp_yi));
592router.post('/gemma/encode', jsonParser, createSentencepieceEncodingHandler(spp_gemma));555router.post('/gemma/encode', jsonParser, createSentencepieceEncodingHandler(spp_gemma));
556router.post('/jamba/encode', jsonParser, createSentencepieceEncodingHandler(spp_jamba));
593router.post('/gpt2/encode', jsonParser, createTiktokenEncodingHandler('gpt2'));557router.post('/gpt2/encode', jsonParser, createTiktokenEncodingHandler('gpt2'));
594router.post('/claude/encode', jsonParser, createWebTokenizerEncodingHandler(claude_tokenizer));558router.post('/claude/encode', jsonParser, createWebTokenizerEncodingHandler(claude_tokenizer));
595router.post('/llama3/encode', jsonParser, createWebTokenizerEncodingHandler(llama3_tokenizer));559router.post('/llama3/encode', jsonParser, createWebTokenizerEncodingHandler(llama3_tokenizer));
@@ -599,6 +563,7 @@ router.post('/nerdstash_v2/decode', jsonParser, createSentencepieceDecodingHandl
599router.post('/mistral/decode', jsonParser, createSentencepieceDecodingHandler(spp_mistral));563router.post('/mistral/decode', jsonParser, createSentencepieceDecodingHandler(spp_mistral));
600router.post('/yi/decode', jsonParser, createSentencepieceDecodingHandler(spp_yi));564router.post('/yi/decode', jsonParser, createSentencepieceDecodingHandler(spp_yi));
601router.post('/gemma/decode', jsonParser, createSentencepieceDecodingHandler(spp_gemma));565router.post('/gemma/decode', jsonParser, createSentencepieceDecodingHandler(spp_gemma));
566router.post('/jamba/decode', jsonParser, createSentencepieceDecodingHandler(spp_jamba));
602router.post('/gpt2/decode', jsonParser, createTiktokenDecodingHandler('gpt2'));567router.post('/gpt2/decode', jsonParser, createTiktokenDecodingHandler('gpt2'));
603router.post('/claude/decode', jsonParser, createWebTokenizerDecodingHandler(claude_tokenizer));568router.post('/claude/decode', jsonParser, createWebTokenizerDecodingHandler(claude_tokenizer));
604router.post('/llama3/decode', jsonParser, createWebTokenizerDecodingHandler(llama3_tokenizer));569router.post('/llama3/decode', jsonParser, createWebTokenizerDecodingHandler(llama3_tokenizer));
@@ -637,6 +602,11 @@ router.post('/openai/encode', jsonParser, async function (req, res) {
637 return handler(req, res);602 return handler(req, res);
638 }603 }
639604
605 if (queryModel.includes('jamba')) {
606 const handler = createSentencepieceEncodingHandler(spp_jamba);
607 return handler(req, res);
608 }
609
640 const model = getTokenizerModel(queryModel);610 const model = getTokenizerModel(queryModel);
641 const handler = createTiktokenEncodingHandler(model);611 const handler = createTiktokenEncodingHandler(model);
642 return handler(req, res);612 return handler(req, res);
@@ -680,6 +650,11 @@ router.post('/openai/decode', jsonParser, async function (req, res) {
680 return handler(req, res);650 return handler(req, res);
681 }651 }
682652
653 if (queryModel.includes('jamba')) {
654 const handler = createSentencepieceDecodingHandler(spp_jamba);
655 return handler(req, res);
656 }
657
683 const model = getTokenizerModel(queryModel);658 const model = getTokenizerModel(queryModel);
684 const handler = createTiktokenDecodingHandler(model);659 const handler = createTiktokenDecodingHandler(model);
685 return handler(req, res);660 return handler(req, res);
@@ -731,6 +706,11 @@ router.post('/openai/count', jsonParser, async function (req, res) {
731 return res.send({ 'token_count': num_tokens });706 return res.send({ 'token_count': num_tokens });
732 }707 }
733708
709 if (model === 'jamba') {
710 num_tokens = await countSentencepieceArrayTokens(spp_jamba, req.body);
711 return res.send({ 'token_count': num_tokens });
712 }
713
734 const tokensPerName = queryModel.includes('gpt-3.5-turbo-0301') ? -1 : 1;714 const tokensPerName = queryModel.includes('gpt-3.5-turbo-0301') ? -1 : 1;
735 const tokensPerMessage = queryModel.includes('gpt-3.5-turbo-0301') ? 4 : 3;715 const tokensPerMessage = queryModel.includes('gpt-3.5-turbo-0301') ? 4 : 3;
736 const tokensPadding = 3;716 const tokensPadding = 3;
src/endpoints/translate.js+1 -1
@@ -110,6 +110,7 @@ router.post('/google', jsonParser, async (request, response) => {
110});110});
111111
112router.post('/yandex', jsonParser, async (request, response) => {112router.post('/yandex', jsonParser, async (request, response) => {
113 try {
113 const chunks = request.body.chunks;114 const chunks = request.body.chunks;
114 const lang = request.body.lang;115 const lang = request.body.lang;
115116
@@ -130,7 +131,6 @@ router.post('/yandex', jsonParser, async (request, response) => {
130131
131 console.log('Input text: ' + inputText);132 console.log('Input text: ' + inputText);
132133
133 try {
134 const result = await fetch(`https://translate.yandex.net/api/v1/tr.json/translate?ucid=${ucid}&srv=android&format=text`, {134 const result = await fetch(`https://translate.yandex.net/api/v1/tr.json/translate?ucid=${ucid}&srv=android&format=text`, {
135 method: 'POST',135 method: 'POST',
136 body: params,136 body: params,
src/prompt-converters.js+76 -0
@@ -267,10 +267,13 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '
267 'gemini-1.5-flash',267 'gemini-1.5-flash',
268 'gemini-1.5-flash-latest',268 'gemini-1.5-flash-latest',
269 'gemini-1.5-flash-001',269 'gemini-1.5-flash-001',
270 'gemini-1.5-flash-exp-0827',
271 'gemini-1.5-flash-8b-exp-0827',
270 'gemini-1.5-pro',272 'gemini-1.5-pro',
271 'gemini-1.5-pro-latest',273 'gemini-1.5-pro-latest',
272 'gemini-1.5-pro-001',274 'gemini-1.5-pro-001',
273 'gemini-1.5-pro-exp-0801',275 'gemini-1.5-pro-exp-0801',
276 'gemini-1.5-pro-exp-0827',
274 'gemini-1.0-pro-vision-latest',277 'gemini-1.0-pro-vision-latest',
275 'gemini-pro-vision',278 'gemini-pro-vision',
276 ];279 ];
@@ -368,6 +371,78 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '
368}371}
369372
370/**373/**
374 * Convert AI21 prompt. Classic: system message squash, user/assistant message merge.
375 * @param {object[]} messages Array of messages
376 * @param {string} charName Character name
377 * @param {string} userName User name
378 */
379function convertAI21Messages(messages, charName = '', userName = '') {
380 if (!Array.isArray(messages)) {
381 return [];
382 }
383
384 // Collect all the system messages up until the first instance of a non-system message, and then remove them from the messages array.
385 let i = 0, systemPrompt = '';
386
387 for (i = 0; i < messages.length; i++) {
388 if (messages[i].role !== 'system') {
389 break;
390 }
391 // Append example names if not already done by the frontend (e.g. for group chats).
392 if (userName && messages[i].name === 'example_user') {
393 if (!messages[i].content.startsWith(`${userName}: `)) {
394 messages[i].content = `${userName}: ${messages[i].content}`;
395 }
396 }
397 if (charName && messages[i].name === 'example_assistant') {
398 if (!messages[i].content.startsWith(`${charName}: `)) {
399 messages[i].content = `${charName}: ${messages[i].content}`;
400 }
401 }
402 systemPrompt += `${messages[i].content}\n\n`;
403 }
404
405 messages.splice(0, i);
406
407 // Prevent erroring out if the messages array is empty.
408 if (messages.length === 0) {
409 messages.unshift({
410 role: 'user',
411 content: '[Start a new chat]',
412 });
413 }
414
415 if (systemPrompt) {
416 messages.unshift({
417 role: 'system',
418 content: systemPrompt.trim(),
419 });
420 }
421
422 // Doesn't support completion names, so prepend if not already done by the frontend (e.g. for group chats).
423 messages.forEach(msg => {
424 if ('name' in msg) {
425 if (msg.role !== 'system' && !msg.content.startsWith(`${msg.name}: `)) {
426 msg.content = `${msg.name}: ${msg.content}`;
427 }
428 delete msg.name;
429 }
430 });
431
432 // Since the messaging endpoint only supports alternating turns, we have to merge messages with the same role if they follow each other
433 let mergedMessages = [];
434 messages.forEach((message) => {
435 if (mergedMessages.length > 0 && mergedMessages[mergedMessages.length - 1].role === message.role) {
436 mergedMessages[mergedMessages.length - 1].content += '\n\n' + message.content;
437 } else {
438 mergedMessages.push(message);
439 }
440 });
441
442 return mergedMessages;
443}
444
445/**
371 * Convert a prompt from the ChatML objects to the format used by MistralAI.446 * Convert a prompt from the ChatML objects to the format used by MistralAI.
372 * @param {object[]} messages Array of messages447 * @param {object[]} messages Array of messages
373 * @param {string} charName Character name448 * @param {string} charName Character name
@@ -520,4 +595,5 @@ module.exports = {
520 convertCohereMessages,595 convertCohereMessages,
521 convertMistralMessages,596 convertMistralMessages,
522 convertCohereTools,597 convertCohereTools,
598 convertAI21Messages,
523};599};
src/tokenizers/jamba.model+0 -0

Binary file

src/util.js+5 -4
@@ -34,8 +34,9 @@ function getConfig() {
34 CACHED_CONFIG = config;34 CACHED_CONFIG = config;
35 return config;35 return config;
36 } catch (error) {36 } catch (error) {
37 console.warn('Failed to read config.yaml');37 console.error(color.red('FATAL: Failed to read config.yaml. Please check the file for syntax errors.'));
38 return {};38 console.error(error.message);
39 process.exit(1);
39 }40 }
40}41}
4142
@@ -298,8 +299,8 @@ const color = {
298 * @returns {string} A UUIDv4 string299 * @returns {string} A UUIDv4 string
299 */300 */
300function uuidv4() {301function uuidv4() {
301 if ('randomUUID' in crypto) {302 if ('crypto' in global && 'randomUUID' in global.crypto) {
302 return crypto.randomUUID();303 return global.crypto.randomUUID();
303 }304 }
304 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {305 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
305 const r = Math.random() * 16 | 0;306 const r = Math.random() * 16 | 0;