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 @@
88 "openrouter_force_instruct": false,
99 "openrouter_group_models": false,
1010 "openrouter_sort_models": "alphabetically",
1111 "ai21_model": "j2jamba-ultra1.5-large",
1212 "mistralai_model": "mistral-large-latest",
1313 "custom_model": "",
1414 "custom_url": "",
@@ -19,7 +19,6 @@
1919 "temperature": 1,
2020 "frequency_penalty": 0,
2121 "presence_penalty": 0,
22- "count_penalty": 0,
2322 "top_p": 1,
2423 "top_k": 0,
2524 "top_a": 0,
@@ -233,8 +232,6 @@
233232 "assistant_prefill": "",
234233 "assistant_impersonation": "",
235234 "human_sysprompt_message": "Let's get started. Please generate your response based on the information and instructions provided above.",
236- "use_ai21_tokenizer": false,
237- "use_google_tokenizer": false,
238235 "claude_use_sysprompt": false,
239236 "use_alt_scale": false,
240237 "squash_system_messages": false,
default/content/settings.json+11 -5
@@ -196,7 +196,15 @@
196196 "enableLabMode": false,
197197 "enableZenSliders": false,
198198 "ui_mode": 1,
199199 "forbid_external_media": true,
200+ "stscript": {
201+ "parser": {
202+ "flags": {
203+ "1": true,
204+ "2": true
205+ }
206+ }
207+ }
200208 },
201209 "extension_settings": {
202210 "apiUrl": "http://localhost:5100",
@@ -452,7 +460,6 @@
452460 "temp_openai": 1.0,
453461 "freq_pen_openai": 0,
454462 "pres_pen_openai": 0,
455- "count_pen": 0,
456463 "top_p_openai": 1,
457464 "top_k_openai": 0,
458465 "stream_openai": true,
@@ -614,7 +621,7 @@
614621 "wi_format": "{0}",
615622 "openai_model": "gpt-4-turbo",
616623 "claude_model": "claude-3-5-sonnet-20240620",
617624 "ai21_model": "j2jamba-ultra1.5-large",
618625 "windowai_model": "",
619626 "openrouter_model": "OR_Website",
620627 "jailbreak_system": true,
@@ -625,7 +632,6 @@
625632 "show_external_models": false,
626633 "proxy_password": "",
627634 "assistant_prefill": "",
628635 "assistant_impersonation": "",
629- "use_ai21_tokenizer": false
630636 }
631637}
public/index.html+44 -42
@@ -5,7 +5,7 @@
55 <title>SillyTavern</title>
66 <base href="/">
77 <meta charset="utf-8">
88 <meta name="viewport" content="width=device-width, viewport-fit=cover, initial-scale=1, maximum-scale=1.0, user-scalable=no, interactive-widget=resizes-content">
99 <meta name="apple-mobile-web-app-capable" content="yes">
1010 <meta name="darkreader-lock">
1111 <meta name="robots" content="noindex, nofollow" />
@@ -436,7 +436,7 @@
436436 </div>
437437 </div>
438438 </div>
439439 <div data-newbie-hidden class="range-block" data-source="openai,openrouter,ai21,custom,cohere,perplexity,groq">
440440 <div class="range-block-title" data-i18n="Frequency Penalty">
441441 Frequency Penalty
442442 </div>
@@ -449,7 +449,7 @@
449449 </div>
450450 </div>
451451 </div>
452452 <div data-newbie-hidden class="range-block" data-source="openai,openrouter,ai21,custom,cohere,perplexity,groq">
453453 <div class="range-block-title" data-i18n="Presence Penalty">
454454 Presence Penalty
455455 </div>
@@ -462,20 +462,7 @@
462462 </div>
463463 </div>
464464 </div>
465465 <div data-newbie-hidden class="range-block" data-source="ai21claude,openrouter,makersuite,cohere,perplexity">
466- <div class="range-block-title" data-i18n="Count Penalty">
467- Count Penalty
468- </div>
469- <div class="range-block-range-and-counter">
470- <div class="range-block-range">
471- <input type="range" id="count_pen" name="volume" min="0" max="1" step="0.01">
472- </div>
473- <div class="range-block-counter">
474- <input type="number" min="0" max="1" step="0.01" data-for="count_pen" id="count_pen_counter">
475- </div>
476- </div>
477- </div>
478- <div data-newbie-hidden class="range-block" data-source="claude,openrouter,ai21,makersuite,cohere,perplexity">
479466 <div class="range-block-title" data-i18n="Top K">
480467 Top K
481468 </div>
@@ -1291,6 +1278,28 @@
12911278 </div>
12921279 </div>
12931280 </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+
12941303 <!-- Enable for llama.cpp when the PR is merged: https://github.com/ggerganov/llama.cpp/pull/6839 -->
12951304 <div data-newbie-hidden data-tg-type="ooba, koboldcpp" id="dryBlock" class="wide100p">
12961305 <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 @@
14841493 <div id="banned_tokens_block_ooba" data-newbie-hidden class="wide100p">
14851494 <hr data-newbie-hidden class="width100p">
14861495 <h4 class="range-block-title justifyCenter">
14871496 <span data-i18n="Banned Tokens">Banned Tokens/Strings</span>
14881497 <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 tokenizerEnter first.&#13;Sequencessequences you don't want to appear in the output.&#13;OneUnquoted pertext line.will Textbe ortokenized 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>
14891498 </h4>
14901499 <div class="wide100p">
14911500 <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 as tokens&#10;[42420, 69, 1337]&#10;"Some verbatim string"'></textarea>
14921501 </div>
14931502 </div>
14941503 <div class="range-block wide100p">
@@ -1791,22 +1800,6 @@
17911800 </select>
17921801 </div>
17931802 </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>
18101803 <div class="range-block" data-source="makersuite">
18111804 <label for="use_makersuite_sysprompt" class="checkbox_label widthFreeExpand">
18121805 <input id="use_makersuite_sysprompt" type="checkbox" />
@@ -1852,7 +1845,7 @@
18521845 <div class="fa-solid fa-clock-rotate-left"></div>
18531846 </div>
18541847 </div>
18551848 <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>
18561849 </div>
18571850 </div>
18581851 </div>
@@ -2791,10 +2784,12 @@
27912784 <div>
27922785 <h4 data-i18n="AI21 Model">AI21 Model</h4>
27932786 <select id="model_ai21_select">
27942787 <optgroup label="LatestJamba 1.5">
27952788 <option value="j2jamba-ultra1.5-mini">j2jamba-ultra1.5-mini</option>
27962789 <option value="j2jamba-mid1.5-large">j2jamba-mid1.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>
27982793 </optgroup>
27992794 </select>
28002795 </div>
@@ -2824,8 +2819,11 @@
28242819 </optgroup>
28252820 <optgroup label="Subversions">
28262821 <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>
28272823 <option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option>
28282824 <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>
28292827 <option value="gemini-1.5-flash-latest">Gemini 1.5 Flash [latest]</option>
28302828 <option value="gemini-1.5-flash-001">Gemini 1.5 Flash [001]</option>
28312829 <option value="gemini-1.0-pro-latest">Gemini 1.0 Pro [latest]</option>
@@ -2955,6 +2953,8 @@
29552953 <option value="command">command</option>
29562954 <option value="command-r">command-r</option>
29572955 <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>
29582958 </optgroup>
29592959 <optgroup label="Nightly">
29602960 <option value="command-light-nightly">command-light-nightly</option>
@@ -4012,7 +4012,7 @@
40124012 <input id="movingUImode" type="checkbox" />
40134013 <small data-i18n="Movable UI Panels">MovingUI&nbsp;<i class="fa-solid fa-desktop"></i></small>
40144014 </label>
40154015 <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>
40164016 </div>
40174017 <div data-newbie-hidden id="MovingUI-presets-block" class="flex-container alignitemscenter">
40184018 <div class="flex-container alignItemsFlexEnd">
@@ -4503,6 +4503,7 @@
45034503 <div data-newbie-hidden>
45044504 <label for="persona_description_position" data-i18n="Position:">Position:</label>
45054505 <select id="persona_description_position">
4506+ <option value="9" data-i18n="None (disabled)">None (disabled)</option>
45064507 <option value="0" data-i18n="In Story String / Prompt Manager">In Story String / Prompt Manager</option>
45074508 <option value="2" data-i18n="Top of Author's Note">Top of Author's Note</option>
45084509 <option value="3" data-i18n="Bottom of Author's Note">Bottom of Author's Note</option>
@@ -6488,6 +6489,7 @@
64886489 <script src="lib/jquery-cookie-1.4.1.min.js"></script>
64896490 <script src="lib/jquery.ui.touch-punch.min.js"></script>
64906491 <script src="lib/showdown.min.js"></script>
6492+ <script src="lib/showdown-patch.js"></script>
64916493 <script src="lib/showdown-katex.min.js"></script>
64926494 <script src="lib/popper.js"></script>
64936495 <script src="lib/purify.min.js"></script>
public/lib/showdown-patch.js+24 -0
@@ -0,0 +1,24 @@
1+showdown.subParser('unhashHTMLSpans', function (text, options, globals) {
2+ 'use strict';
3+ text = globals.converter._dispatch('unhashHTMLSpans.before', text, options, globals);
4+
5+ for (var i = 0; i < globals.gHtmlSpans.length; ++i) {
6+ var repText = globals.gHtmlSpans[i],
7+ // limiter to prevent infinite loop (assume 10 as limit for recurse)
8+ limit = 0;
9+
10+ while (/¨C(\d+)C/.test(repText)) {
11+ var num = RegExp.$1;
12+ repText = repText.replace('¨C' + num + 'C', globals.gHtmlSpans[num]);
13+ if (limit === 10000) {
14+ console.error('maximum nesting of 10000 spans reached!!!');
15+ break;
16+ }
17+ ++limit;
18+ }
19+ text = text.replace('¨C' + i + 'C', repText);
20+ }
21+
22+ text = globals.converter._dispatch('unhashHTMLSpans.after', text, options, globals);
23+ return text;
24+});
public/locales/ru-ru.json+54 -7
@@ -43,7 +43,7 @@
4343 "Smoothing Factor": "Коэффициент сглаживания",
4444 "No Repeat Ngram Size": "Размер no_repeat_ngram",
4545 "Min Length": "Мин. длина",
4646 "Alternative server URL (leave empty to use the default value).": "Альтернативный URL альтернативного сервера (оставьте пустым для стандартного значения)",
4747 "Remove your real OAI API Key from the API panel BEFORE typing anything into this box": "Удалите свой личный OAI API Key из панели API, и ТОЛЬКО ПОСЛЕ ЭТОГО вводите что-то сюда",
4848 "We cannot provide support for problems encountered while using an unofficial OpenAI proxy": "Мы не сможем предоставить помощь с проблемами, с которыми вы столкнетесь при использовании неофициальных прокси для OpenAI",
4949 "Context Size (tokens)": "Размер контекста (в токенах)",
@@ -186,7 +186,7 @@
186186 "Generate only one line per request": "Генерировать одну строку на запрос",
187187 "Misc. Settings": "Доп. настройки",
188188 "Auto-Continue": "Авто-продолжение",
189189 "Collapse Consecutive Newlines": "Сворачивать последовательныенеск. новыеновых строкистрок в одну",
190190 "Allow for Chat Completion APIs": "Разрешить для Chat Completion API",
191191 "Target length (tokens)": "Целевая длина (в токенах)",
192192 "World Info": "Информация о мире",
@@ -373,7 +373,7 @@
373373 "Prefer Character Card Prompt": "Приоритет промпту из карточки персонажа",
374374 "Prefer Character Card Jailbreak": "Приоритет джейлбрейку из карточки персонажа",
375375 "Press Send to continue": "Кнопка отправки продолжает сообщение",
376376 "Quick 'Continue' button": "Кнопка быстрогоБыстрое продолженияпродолжение",
377377 "Log prompts to console": "Выводить промпты в консоль",
378378 "Never resize avatars": "Не менять размер аватарок",
379379 "Show avatar filenames": "Показывать названия файлов аватарок",
@@ -495,7 +495,7 @@
495495 "(Write a comma-separated list of tags)": "(Список тегов через запятую)",
496496 "(A brief description of the personality)": "(Краткое описание личности)",
497497 "(Circumstances and context of the interaction)": "(Обстоятельства и контекст этого диалога)",
498498 "(Examples of chat dialog. Begin each example with START on a new line.)": "(Примеры диалога. Начинайте каждый пример с START или новой линиейстрокой.)",
499499 "Type here...": "Пишите здесь...",
500500 "Comma separated (required)": "Через запятую (обязательное поле)",
501501 "What this keyword should mean to the AI, sent verbatim": "Что это ключевое слово должно означать для ИИ, отправляется дословно",
@@ -552,7 +552,7 @@
552552 "Click to set a new User Name": "Нажмите, чтобы задать новое имя пользователя.",
553553 "Click to lock your selected persona to the current chat. Click again to remove the lock.": "Нажмите, чтобы закрепить выбранную персону для текущего чата. Нажмите еще раз, чтобы открепить.",
554554 "Click to set user name for all messages": "Нажмите, чтобы задать имя пользователя для всех сообщений.",
555555 "Create a dummy persona": "Создать пустую персону-болванку",
556556 "Character Management": "Управление персонажами",
557557 "Locked = Character Management panel will stay open": "Закреплено = Панель управление персонажами останется открытой",
558558 "Select/Create Characters": "Выбрать/Создать персонажа",
@@ -625,7 +625,7 @@
625625 "Automatically select a background based on the chat context": "Автоматический выбор фона в зависимости от контекста чата",
626626 "Filter": "Фильтр",
627627 "Exclude message from prompts": "Исключить сообщение из промпта",
628628 "Include message in prompts": "Включить сообщение в подсказкипромпт",
629629 "Create checkpoint": "Создать чекпоинт",
630630 "Create Branch": "Создать ветку",
631631 "Embed file or image": "Вставить файл или изображение",
@@ -1661,5 +1661,52 @@
16611661 "ext_regex_ai_output_desc": "Сообщения, полученные от API",
16621662 "ext_regex_sts_desc": "Сообщения, отправленные с помощью команд STscript",
16631663 "ext_regex_wi_desc": "Содержимое лорбуков и миров. Для работы требует включения флажка \"Только промпт\"!",
16641664 "ext_regex_only_format_display_desc": "История чата не изменится, замена будет осуществляться только в отображаемом сообщении (в UI)",
1665+ "Prefer Character Card Instructions": "Приоритет инструкциям из карточек",
1666+ "If checked and the character card contains a Post-History Instructions override, use that instead": "Если в карточке персонажа имеется собственная инструкция после истории, в промпт попадёт именно она",
1667+ "Auto-select Input Text": "Автовыделение вводимого текста",
1668+ "Enable auto-select of input text in some text fields when clicking/selecting them. Applies to popup input textboxes, and possible other custom input fields.": "Автоматически выделять вводимый текст в некоторых текстовых полях при клике/выборе. Работает для вспл. окон и различных кастомных полей ввода.",
1669+ "Reset MovingUI panel sizes/locations.": "Сбросить расположение и размеры панелей MovingUI.",
1670+ "mui_reset": "Сброс",
1671+ "Quick 'Impersonate' button": "Быстрое перевоплощение",
1672+ "Show a button in the input area to ask the AI to impersonate your character for a single message": "Показать в поле ввода кнопку, по нажатии на которую ИИ сгенерирует одно сообщение от лица вашего персонажа.",
1673+ "Separators as Stop Strings": "Разделители как стоп-строки",
1674+ "Names as Stop Strings": "Имена как стоп-строки",
1675+ "Add Character and User names to a list of stopping strings.": "Добавлять имена персонажа и пользователя в список стоп-строк.",
1676+ "Allow Post-History Instructions": "Разрешить инструкции после истории",
1677+ "context_allow_post_history_instructions": "Добавлять в конец промпта инструкции после истории. Работает только при наличии таких инструкций в карточке И при включенной опции ''Приоритет инструкциям из карточек''.\nНЕ РЕКОМЕНДУЕТСЯ ДЛЯ МОДЕЛЕЙ TEXT COMPLETION, МОЖЕТ ПОРТИТЬ ВЫХОДНОЙ ТЕКСТ.",
1678+ "First User Prefix": "Первый префикс пользователя",
1679+ "Inserted before the first User's message.": "Вставляется перед первым сообщением пользователя.",
1680+ "Last User Prefix": "Последний префикс пользователя",
1681+ "instruct_last_input_sequence": "Вставляется перед последним сообщением пользователя.",
1682+ "Inserted before a User message and as a last prompt line when impersonating.": "Вставляется перед сообщением пользователя в качестве последней строки промпта при перевоплощении.",
1683+ "Inserted before a System (added by slash commands or extensions) message.": "Вставляется перед сообщением системы (может быть добавлено слэш-командой или расширением).",
1684+ "Load Asset List": "Загрузить список ресурсов",
1685+ "Never add character names.": "Не вставлять имя персонажа.",
1686+ "Don't add character names unless necessary.": "Вставлять имя персонажа только когда это необходимо.",
1687+ "character_names_none": "Не добавлять имена персонажей в качестве префикса. Может повредить качеству ответов в групповых чатах, используйте с осторожностью.",
1688+ "Auxiliary": "Вспомогательный",
1689+ "Post-History Instructions": "Инструкции после истории",
1690+ "Current persona updated": "Текущая персона изменена",
1691+ "Your messages will now be sent as ${0}": "Ваши сообщения будут отправляться от лица ${0}",
1692+ "Copied!": "Скопировано!",
1693+ "Are you sure you want to delete this message?": "Вы точно хотите удалить это сообщение?",
1694+ "Delete Message": "Удалить сообщение",
1695+ "Delete Swipe": "Удалить свайп",
1696+ "Could not get a reply from API. Check your connection settings / API key and try again.": "Не удалось получить ответ от API. Проверьте настройки соединения и API-ключ и повторите попытку.",
1697+ "Connecting To Proxy": "Подключение к прокси",
1698+ "Are you sure you want to connect to the following proxy URL?": "Вы точно хотите соединиться с прокси по этому адресу?",
1699+ "API connection successful!": "Соединение с API установлено!",
1700+ "Proxy Saved": "Прокси сохранена",
1701+ "Proxy Deleted": "Прокси удалена",
1702+ "Could not find proxy with name '${0}'": "Не удалось найти прокси с названием '${0}'",
1703+ "Proxy preset '${0}' not found in proxies array.": "Пресет с названием '${0}' не найден в списке прокси.",
1704+ "Please wait for the previous request to complete.": "Пожалуйста, дождитесь окончания обработки предыдущего запроса.",
1705+ "Start new chat?": "Начать новый чат?",
1706+ "If necessary, you can later restore this chat file from the /backups folder": "При необходимости этот чат можно будет восстановить из папки /backups",
1707+ "Also delete the current chat file": "Также удалить текущий чат",
1708+ "chat_rename_1": "Введите новое имя чата:",
1709+ "chat_rename_2": "!!Не используйте имя уже существующего файла, это приведёт к ошибке!!",
1710+ "chat_rename_3": "Будут разрушены связи между чатами-чекпоинтами.",
1711+ "chat_rename_4": "Расширение '.jsonl' дописывать не нужно."
16651712}
public/script.js+53 -56
@@ -488,14 +488,6 @@ let default_user_name = 'User';
488488export let name1 = default_user_name;
489489export let name2 = 'SillyTavern System';
490490export let chat = [];
491-let 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-];
499491let chatSaveTimeout;
500492let importFlashTimeout;
501493export let isChatSaving = false;
@@ -594,6 +586,17 @@ export const extension_prompt_roles = {
594586
595587export const MAX_INJECTION_DEPTH = 1000;
596588
589+const SAFETY_CHAT = [
590+ {
591+ name: systemUserName,
592+ force_avatar: system_avatar,
593+ is_system: true,
594+ is_user: false,
595+ create_date: 0,
596+ mes: 'You deleted a character/chat and arrived back here for safety reasons! Pick another character!',
597+ },
598+];
599+
597600export let system_messages = {};
598601
599602async function getSystemMessages() {
@@ -2192,7 +2195,7 @@ export function addCopyToCodeBlocks(messageElement) {
21922195 codeBlocks.get(i).appendChild(copyButton);
21932196 copyButton.addEventListener('pointerup', function (event) {
21942197 navigator.clipboard.writeText(codeBlocks.get(i).innerText);
21952198 toastr.info('t`Copied!'`, '', { timeOut: 2000 });
21962199 });
21972200 }
21982201 }
@@ -2705,7 +2708,7 @@ function addPersonaDescriptionExtensionPrompt() {
27052708 const INJECT_TAG = 'PERSONA_DESCRIPTION';
27062709 setExtensionPrompt(INJECT_TAG, '', extension_prompt_types.IN_PROMPT, 0);
27072710
27082711 if (!power_user.persona_description || power_user.persona_description_position === persona_description_positions.NONE) {
27092712 return;
27102713 }
27112714
@@ -2826,7 +2829,7 @@ export function getCharacterCardFields() {
28262829}
28272830
28282831export function isStreamingEnabled() {
28292832 const noStreamSources = [chat_completion_sources.SCALE, chat_completion_sources.AI21];
28302833 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')))
28312834 || (main_api == 'kobold' && kai_settings.streaming_kobold && kai_flags.can_use_streaming)
28322835 || (main_api == 'novel' && nai_settings.streaming_novel)
@@ -3726,7 +3729,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
37263729 const storyStringParams = {
37273730 description: description,
37283731 personality: personality,
3729- persona: persona,
3732+ persona: power_user.persona_description_position == persona_description_positions.IN_PROMPT ? persona : '',
37303733 scenario: scenario,
37313734 system: isInstruct ? system : '',
37323735 char: name2,
@@ -3760,7 +3763,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
37603763 }
37613764
37623765 let examplesString = '';
37633766 let chatString = addChatsPreamble(addChatsSeparator(''));
37643767 let cyclePrompt = '';
37653768
37663769 async function getMessagesTokenCount() {
@@ -3769,10 +3772,10 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
37693772 storyString,
37703773 afterScenarioAnchor,
37713774 examplesString,
3775+ userAlignmentMessage,
37723776 chatString,
3773- quiet_prompt,
3777+ modifyLastPromptLine(''),
37743778 cyclePrompt,
3775- userAlignmentMessage,
37763779 ].join('').replace(/\r/gm, '');
37773780 return getTokenCountAsync(encodeString, power_user.token_padding);
37783781 }
@@ -3803,8 +3806,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
38033806 }
38043807
38053808 tokenCount += await getTokenCountAsync(item.replace(/\r/gm, ''));
3806- chatString = item + chatString;
38073809 if (tokenCount < this_max_context) {
3810+ chatString = chatString + item;
38083811 arrMes[index] = item;
38093812 lastAddedIndex = Math.max(lastAddedIndex, index);
38103813 } else {
@@ -3830,8 +3833,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
38303833 }
38313834
38323835 tokenCount += await getTokenCountAsync(item.replace(/\r/gm, ''));
3833- chatString = item + chatString;
38343836 if (tokenCount < this_max_context) {
3837+ chatString = chatString + item;
38353838 arrMes[i] = item;
38363839 lastAddedIndex = Math.max(lastAddedIndex, i);
38373840 } else {
@@ -4028,15 +4031,16 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
40284031 async function checkPromptSize() {
40294032 console.debug('---checking Prompt size');
40304033 setPromptString();
4034+ const jointMessages = mesSend.map((e) => `${e.extensionPrompts.join('')}${e.message}`).join('');
40314035 const prompt = [
40324036 beforeScenarioAnchor,
40334037 storyString,
40344038 afterScenarioAnchor,
40354039 mesExmString,
4036- mesSend.map((e) => `${e.extensionPrompts.join('')}${e.message}`).join(''),
4040+ addChatsPreamble(addChatsSeparator(jointMessages)),
40374041 '\n',
4042+ modifyLastPromptLine(''),
40384043 generatedPromptCache,
4039- quiet_prompt,
40404044 ].join('').replace(/\r/gm, '');
40414045 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
43024306 padding: power_user.token_padding,
43034307 main_api: main_api,
43044308 instruction: isInstruct ? substituteParams(power_user.prefer_character_prompt && system ? system : power_user.instruct.system_prompt) : '',
43054309 userPersona: (power_user.persona_descriptionpersona_description_position == persona_description_positions.IN_PROMPT ? (persona || '') : ''),
43064310 };
43074311
43084312 //console.log(additionalPromptStuff);
@@ -5080,7 +5084,7 @@ async function promptItemize(itemizedPrompts, requestedMesId) {
50805084 }
50815085
50825086 navigator.clipboard.writeText(rawPromptValues);
50835087 toastr.info('t`Copied!'`);
50845088 });
50855089
50865090 popup.dlg.querySelector('#showRawPrompt').addEventListener('click', function () {
@@ -5679,7 +5683,7 @@ export function resetChatState() {
56795683 // replaces deleted charcter name with system user since it will be displayed next.
56805684 name2 = systemUserName;
56815685 // sets up system user to tell user about having deleted a character
56825686 chat.splice(0, =chat.length, [...safetychat]SAFETY_CHAT);
56835687 // resets chat metadata
56845688 chat_metadata = {};
56855689 // resets the characters array, forcing getcharacters to reset
@@ -6310,7 +6314,7 @@ export function setUserName(value) {
63106314 console.log(`User name changed to ${name1}`);
63116315 $('#your_name').val(name1);
63126316 if (power_user.persona_show_notifications) {
63136317 toastr.success(t`Your messages will now be sent as ${name1}`, 't`Current persona updated'`);
63146318 }
63156319 saveSettingsDebounced();
63166320}
@@ -8840,16 +8844,21 @@ export async function handleDeleteCharacter(this_chid, delete_chats) {
88408844/**
88418845 * Deletes a character completely, including associated chats if specified
88428846 *
88438847 * @param {string|string[]} characterKey - The key (avatar) of the character to be deleted
88448848 * @param {Object} [options] - Optional parameters for the deletion
88458849 * @param {boolean} [options.deleteChats=true] - Whether to delete associated chats or not
88468850 * @return {Promise<void>} - A promise that resolves when the character is successfully deleted
88478851 */
88488852export 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);
88508859 if (!character) {
88518860 toastr.warning(`Character ${characterKeykey} not found. Cannot beSkipping deleteddeletion.`);
8852- return;
8861+ continue;
88538862 }
88548863
88558864 const chid = characters.indexOf(character);
@@ -8865,10 +8874,12 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
88658874 });
88668875
88678876 if (!response.ok) {
88688877 throw new Error toastr.error(`Failed to delete character: ${response.status} ${response.statusText}`, 'Failed to delete character');
8878+ continue;
88698879 }
88708880
8871- await removeCharacterFromUI(character.name, character.avatar);
8881+ delete tag_map[character.avatar];
8882+ select_rm_info('char_delete', character.name);
88728883
88738884 if (deleteChats) {
88748885 for (const chat of pastChats) {
@@ -8877,35 +8888,30 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
88778888 }
88788889 }
88798890
88808891 await eventSource.emit(event_types.CHARACTER_DELETED, { id: this_chidchid, character: characters[this_chid]character });
8892+ }
8893+
8894+ await removeCharacterFromUI();
88818895}
88828896
88838897/**
88848898 * Function to delete a character from UI after character deletion API success.
88858899 * It manages necessary UI changes such as closing advanced editing popup, unsetting
88868900 * character ID, resetting characters array and chat metadata, deselecting character's tab
88878901 * panel, removing character name from navigation tabs, clearing chat, removingfetching character'supdated list of characters.
8888- * avatar from tag_map, fetching updated list of characters and updating the 'deleted
8889- * character' message.
88908902 * 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.
88958903 */
88968904async function removeCharacterFromUI(name, avatar, reloadCharacters = true) {
88978905 await clearChat();
88988906 $('#character_cross').click();
88998907 this_chid = undefined;
89008908 characters.length = 0;
89018909 name2 = systemUserName;
89028910 chat.splice(0, =chat.length, [...safetychat]SAFETY_CHAT);
89038911 chat_metadata = {};
89048912 $(document.getElementById('rm_button_selected_ch')).children('h2').text('');
89058913 this_chid = undefined;
8906- delete tag_map[avatar];
8914+ await getCharacters();
8907- if (reloadCharacters) await getCharacters();
8908- select_rm_info('char_delete', name);
89098915 await printMessages();
89108916 saveSettingsDebounced();
89118917}
@@ -9532,11 +9538,7 @@ jQuery(async function () {
95329538 const oldFileNameFull = $(this).closest('.select_chat_block_wrapper').find('.select_chat_block_filename').text();
95339539 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>`;
95409542 const newName = await callPopup(popupText, 'input', oldFileName);
95419543
95429544 if (!newName || newName == oldFileName) {
@@ -9744,12 +9746,7 @@ jQuery(async function () {
97449746 else if (id == 'option_start_new_chat') {
97459747 if ((selected_group || this_chid !== undefined) && !is_send_press) {
97469748 let deleteCurrentChat = false;
97479749 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>`, {
97539750 onClose: () => deleteCurrentChat = !!$('#del_chat_checkbox').prop('checked'),
97549751 });
97559752 if (!result) {
@@ -10261,10 +10258,10 @@ jQuery(async function () {
1026110258
1026210259 let deleteOnlySwipe = false;
1026310260 if (power_user.confirm_message_delete && fromSlashCommand !== true) {
1026410261 const result = await callGenericPopup('t`Are you sure you want to delete this message?'`, POPUP_TYPE.CONFIRM, null, {
1026510262 okButton: canDeleteSwipe ? 't`Delete Swipe'` : 't`Delete Message'`,
1026610263 cancelButton: 'Cancel',
1026710264 customButtons: canDeleteSwipe ? ['t`Delete Message'`] : null,
1026810265 });
1026910266 if (!result) {
1027010267 return;
public/scripts/BulkEditOverlay.js+11 -11
@@ -108,14 +108,12 @@ class CharacterContextMenu {
108108 * Delete one or more characters,
109109 * opens a popup.
110110 *
111111 * @param {numberstring|string[]} characterIdcharacterKey
112112 * @param {boolean} [deleteChats]
113113 * @returns {Promise<void>}
114114 */
115115 static delete = async (characterIdcharacterKey, deleteChats = false) => {
116- const character = CharacterContextMenu.#getCharacter(characterId);
116+ await deleteCharacter(characterKey, { deleteChats: deleteChats });
117-
118- await deleteCharacter(character.avatar, { deleteChats: deleteChats });
119117 };
120118
121119 static #getCharacter = (characterId) => characters[characterId] ?? null;
@@ -599,8 +597,7 @@ class BulkEditOverlay {
599597
600598 this.container.removeEventListener('mouseup', cancelHold);
601599 this.container.removeEventListener('touchend', cancelHold);
602600 }, BulkEditOverlay.longPressDelay);
603- BulkEditOverlay.longPressDelay);
604601 };
605602
606603 handleLongPressEnd = (event) => {
@@ -847,11 +844,14 @@ class BulkEditOverlay {
847844 const deleteChats = document.getElementById('del_char_checkbox').checked ?? false;
848845
849846 showLoader();
850847 const toast = toastr.info('We\'re deleting your characters, please wait...', 'Working on it');
851- return Promise.allSettled(characterIds.map(async characterId => CharacterContextMenu.delete(characterId, deleteChats)))
848+ const avatarList = characterIds.map(id => characters[id]?.avatar).filter(a => a);
852- .then(() => getCharacters())
849+ return CharacterContextMenu.delete(avatarList, deleteChats)
853850 .then(() => this.browseState())
854851 .finally(() => hideLoader());{
852+ toastr.clear(toast);
853+ hideLoader();
854+ });
855855 });
856856
857857 // At this moment the popup is already changed in the dom, but not yet closed/resolved. We build the avatar list here
public/scripts/extensions/caption/settings.html+3 -0
@@ -45,8 +45,11 @@
4545 <option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>
4646 <option data-type="google" value="gemini-pro-vision">gemini-pro-vision</option>
4747 <option data-type="google" value="gemini-1.5-flash-latest">gemini-1.5-flash-latest</option>
48+ <option data-type="google" value="gemini-1.5-flash-exp-0827">gemini-1.5-flash-exp-0827</option>
49+ <option data-type="google" value="gemini-1.5-flash-8b-exp-0827">gemini-1.5-flash-8b-exp-0827</option>
4850 <option data-type="google" value="gemini-1.5-pro-latest">gemini-1.5-pro-latest</option>
4951 <option data-type="google" value="gemini-1.5-pro-exp-0801">gemini-1.5-pro-exp-0801</option>
52+ <option data-type="google" value="gemini-1.5-pro-exp-0827">gemini-1.5-pro-exp-0827</option>
5053 <option data-type="openrouter" value="openai/gpt-4-vision-preview">openai/gpt-4-vision-preview</option>
5154 <option data-type="openrouter" value="openai/gpt-4o">openai/gpt-4o</option>
5255 <option data-type="openrouter" value="openai/gpt-4-turbo">openai/gpt-4-turbo</option>
public/scripts/extensions/tts/index.js+1 -1
@@ -421,7 +421,7 @@ function completeTtsJob() {
421421async function tts(text, voiceId, char) {
422422 async function processResponse(response) {
423423 // RVC injection
424424 if (extension_settings.rvc.enabled && typeof window['rvcVoiceConversion'] === 'function' && extension_settings.rvc.enabled)
425425 response = await window['rvcVoiceConversion'](response, char, text);
426426
427427 await addAudioJob(response, char);
public/scripts/nai-settings.js+16 -1
@@ -78,6 +78,19 @@ export function getKayraMaxContextTokens() {
7878 return null;
7979}
8080
81+export function getKayraMaxResponseTokens() {
82+ switch (novel_data?.tier) {
83+ case 1:
84+ return 100;
85+ case 2:
86+ return 100;
87+ case 3:
88+ return 150;
89+ }
90+
91+ return maximum_output_length;
92+}
93+
8194export function getNovelTier() {
8295 return nai_tiers[novel_data?.tier] ?? 'no_connection';
8396}
@@ -438,12 +451,14 @@ export function getNovelGenerationData(finalPrompt, settings, maxLength, isImper
438451 console.log(finalPrompt);
439452 }
440453
454+ const adjustedMaxLength = nai_settings.model_novel.includes('kayra') ? getKayraMaxResponseTokens() : maximum_output_length;
455+
441456 return {
442457 'input': finalPrompt,
443458 'model': nai_settings.model_novel,
444459 'use_string': true,
445460 'temperature': Number(nai_settings.temperature),
446461 'max_length': maxLength < maximum_output_lengthadjustedMaxLength ? maxLength : maximum_output_lengthadjustedMaxLength,
447462 'min_length': Number(nai_settings.min_length),
448463 'tail_free_sampling': Number(nai_settings.tail_free_sampling),
449464 'repetition_penalty': Number(nai_settings.repetition_penalty),
public/scripts/openai.js+46 -112
@@ -74,6 +74,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashComma
7474import { renderTemplateAsync } from './templates.js';
7575import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
7676import { Popup, POPUP_RESULT } from './popup.js';
77+import { t } from './i18n.js';
7778
7879export {
7980 openai_messages_count,
@@ -132,13 +133,9 @@ const max_2mil = 2000 * 1000;
132133const scale_max = 8191;
133134const claude_max = 9000; // We have a proper tokenizer, so theoretically could be larger (up to 9k)
134135const claude_100k_max = 99000;
135-let ai21_max = 9200; //can easily fit 9k gpt tokens because j2's tokenizer is efficient af
136+const unlocked_max = max_2mil;
136-const unlocked_max = max_200k;
137137const oai_max_temp = 2.0;
138138const claude_max_temp = 1.0; //same as j2
139-const j2_max_topk = 10.0;
140-const j2_max_freq = 5.0;
141-const j2_max_pres = 5.0;
142139const openrouter_website_model = 'OR_Website';
143140const openai_max_stop_strings = 4;
144141
@@ -218,25 +215,11 @@ const sensitiveFields = [
218215 'custom_include_headers',
219216];
220217
221-function getPrefixMap() {
222- return selected_group ? {
223- assistant: '',
224- user: '',
225- system: 'OOC: ',
226- }
227- : {
228- assistant: '{{char}}:',
229- user: '{{user}}:',
230- system: '',
231- };
232-}
233-
234218const default_settings = {
235219 preset_settings_openai: 'Default',
236220 temp_openai: 1.0,
237221 freq_pen_openai: 0,
238222 pres_pen_openai: 0,
239- count_pen: 0.0,
240223 top_p_openai: 1.0,
241224 top_k_openai: 0,
242225 min_p_openai: 0,
@@ -264,7 +247,7 @@ const default_settings = {
264247 openai_model: 'gpt-4-turbo',
265248 claude_model: 'claude-3-5-sonnet-20240620',
266249 google_model: 'gemini-1.5-pro',
267250 ai21_model: 'j2jamba-ultra1.5-large',
268251 mistralai_model: 'mistral-large-latest',
269252 cohere_model: 'command-r-plus',
270253 perplexity_model: 'llama-3.1-70b-instruct',
@@ -294,8 +277,6 @@ const default_settings = {
294277 assistant_prefill: '',
295278 assistant_impersonation: '',
296279 human_sysprompt_message: default_claude_human_sysprompt_message,
297- use_ai21_tokenizer: false,
298- use_google_tokenizer: false,
299280 claude_use_sysprompt: false,
300281 use_makersuite_sysprompt: true,
301282 use_alt_scale: false,
@@ -317,7 +298,6 @@ const oai_settings = {
317298 temp_openai: 1.0,
318299 freq_pen_openai: 0,
319300 pres_pen_openai: 0,
320- count_pen: 0.0,
321301 top_p_openai: 1.0,
322302 top_k_openai: 0,
323303 min_p_openai: 0,
@@ -345,7 +325,7 @@ const oai_settings = {
345325 openai_model: 'gpt-4-turbo',
346326 claude_model: 'claude-3-5-sonnet-20240620',
347327 google_model: 'gemini-1.5-pro',
348328 ai21_model: 'j2jamba-ultra1.5-large',
349329 mistralai_model: 'mistral-large-latest',
350330 cohere_model: 'command-r-plus',
351331 perplexity_model: 'llama-3.1-70b-instruct',
@@ -375,8 +355,6 @@ const oai_settings = {
375355 assistant_prefill: '',
376356 assistant_impersonation: '',
377357 human_sysprompt_message: default_claude_human_sysprompt_message,
378- use_ai21_tokenizer: false,
379- use_google_tokenizer: false,
380358 claude_use_sysprompt: false,
381359 use_makersuite_sysprompt: true,
382360 use_alt_scale: false,
@@ -425,7 +403,7 @@ async function validateReverseProxy() {
425403 const rememberKey = `Proxy_SkipConfirm_${getStringHash(oai_settings.reverse_proxy)}`;
426404 const skipConfirm = localStorage.getItem(rememberKey) === 'true';
427405
428406 const confirmation = skipConfirm || await Popup.show.confirm('t`Connecting To Proxy', `<span>Are you sure you want to connect, toawait therenderTemplateAsync('proxyConnectionWarning', following{ proxyproxyURL: URL?</span><var>${DOMPurify.sanitize(oai_settings.reverse_proxy) }</var>`));
429407
430408 if (!confirmation) {
431409 toastr.error('Update or remove your reverse proxy settings.');
@@ -1148,7 +1126,6 @@ function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, wor
11481126 { role: 'system', content: charDescription, identifier: 'charDescription' },
11491127 { role: 'system', content: charPersonalityText, identifier: 'charPersonality' },
11501128 { role: 'system', content: scenarioText, identifier: 'scenario' },
1151- { role: 'system', content: personaDescription, identifier: 'personaDescription' },
11521129 // Unordered prompts without marker
11531130 { role: 'system', content: impersonationPrompt, identifier: 'impersonate' },
11541131 { role: 'system', content: quietPrompt, identifier: 'quietPrompt' },
@@ -1395,6 +1372,11 @@ function tryParseStreamingError(response, decoded) {
13951372 toastr.error(data.error.message || response.statusText, 'Chat Completion API');
13961373 throw new Error(data);
13971374 }
1375+
1376+ if (data.message) {
1377+ toastr.error(data.message, 'Chat Completion API');
1378+ throw new Error(data);
1379+ }
13981380 }
13991381 catch {
14001382 // No JSON. Do nothing.
@@ -1802,7 +1784,6 @@ async function sendOpenAIRequest(type, messages, signal) {
18021784 const isClaude = oai_settings.chat_completion_source == chat_completion_sources.CLAUDE;
18031785 const isOpenRouter = oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER;
18041786 const isScale = oai_settings.chat_completion_source == chat_completion_sources.SCALE;
1805- const isAI21 = oai_settings.chat_completion_source == chat_completion_sources.AI21;
18061787 const isGoogle = oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE;
18071788 const isOAI = oai_settings.chat_completion_source == chat_completion_sources.OPENAI;
18081789 const isMistral = oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI;
@@ -1815,7 +1796,7 @@ async function sendOpenAIRequest(type, messages, signal) {
18151796 const isQuiet = type === 'quiet';
18161797 const isImpersonate = type === 'impersonate';
18171798 const isContinue = type === 'continue';
18181799 const stream = oai_settings.stream_openai && !isQuiet && !isScale && !isAI21 && !(isGoogle && oai_settings.google_model.includes('bison'));
18191800 const useLogprobs = !!power_user.request_token_probabilities;
18201801 const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom);
18211802
@@ -1824,15 +1805,6 @@ async function sendOpenAIRequest(type, messages, signal) {
18241805 replaceItemizedPromptText(messageId, messages);
18251806 }
18261807
1827- if (isAI21) {
1828- const joinedMsgs = messages.reduce((acc, obj) => {
1829- const prefix = getPrefixMap()[obj.role];
1830- return acc + (prefix ? (selected_group ? '\n' : prefix + ' ') : '') + obj.content + '\n';
1831- }, '');
1832- messages = substituteParams(joinedMsgs) + (isImpersonate ? `${name1}:` : `${name2}:`);
1833- replaceItemizedPromptText(messageId, messages);
1834- }
1835-
18361808 // If we're using the window.ai extension, use that instead
18371809 // Doesn't support logit bias yet
18381810 if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI) {
@@ -1931,12 +1903,6 @@ async function sendOpenAIRequest(type, messages, signal) {
19311903 generate_data['use_makersuite_sysprompt'] = oai_settings.use_makersuite_sysprompt;
19321904 }
19331905
1934- if (isAI21) {
1935- generate_data['top_k'] = Number(oai_settings.top_k_openai);
1936- generate_data['count_pen'] = Number(oai_settings.count_pen);
1937- generate_data['stop_tokens'] = [name1 + ':', substituteParams(oai_settings.new_chat_prompt), substituteParams(oai_settings.new_group_chat_prompt)];
1938- }
1939-
19401906 if (isMistral) {
19411907 generate_data['safe_prompt'] = false; // already defaults to false, but just incase they change that in the future.
19421908 }
@@ -3008,7 +2974,6 @@ function loadOpenAISettings(data, settings) {
30082974 oai_settings.temp_openai = settings.temp_openai ?? default_settings.temp_openai;
30092975 oai_settings.freq_pen_openai = settings.freq_pen_openai ?? default_settings.freq_pen_openai;
30102976 oai_settings.pres_pen_openai = settings.pres_pen_openai ?? default_settings.pres_pen_openai;
3011- oai_settings.count_pen = settings.count_pen ?? default_settings.count_pen;
30122977 oai_settings.top_p_openai = settings.top_p_openai ?? default_settings.top_p_openai;
30132978 oai_settings.top_k_openai = settings.top_k_openai ?? default_settings.top_k_openai;
30142979 oai_settings.top_a_openai = settings.top_a_openai ?? default_settings.top_a_openai;
@@ -3080,10 +3045,12 @@ function loadOpenAISettings(data, settings) {
30803045 oai_settings.names_behavior = character_names_behavior.COMPLETION;
30813046 }
30823047
3048+ if (oai_settings.ai21_model.startsWith('j2-')) {
3049+ oai_settings.ai21_model = 'jamba-1.5-large';
3050+ }
3051+
30833052 if (settings.wrap_in_quotes !== undefined) oai_settings.wrap_in_quotes = !!settings.wrap_in_quotes;
30843053 if (settings.openai_model !== undefined) oai_settings.openai_model = settings.openai_model;
3085- if (settings.use_ai21_tokenizer !== undefined) { oai_settings.use_ai21_tokenizer = !!settings.use_ai21_tokenizer; oai_settings.use_ai21_tokenizer ? ai21_max = 8191 : ai21_max = 9200; }
3086- if (settings.use_google_tokenizer !== undefined) oai_settings.use_google_tokenizer = !!settings.use_google_tokenizer;
30873054 if (settings.claude_use_sysprompt !== undefined) oai_settings.claude_use_sysprompt = !!settings.claude_use_sysprompt;
30883055 if (settings.use_makersuite_sysprompt !== undefined) oai_settings.use_makersuite_sysprompt = !!settings.use_makersuite_sysprompt;
30893056 if (settings.use_alt_scale !== undefined) { oai_settings.use_alt_scale = !!settings.use_alt_scale; updateScaleForm(); }
@@ -3133,8 +3100,6 @@ function loadOpenAISettings(data, settings) {
31333100 $('#jailbreak_system').prop('checked', oai_settings.jailbreak_system);
31343101 $('#openai_show_external_models').prop('checked', oai_settings.show_external_models);
31353102 $('#openai_external_category').toggle(oai_settings.show_external_models);
3136- $('#use_ai21_tokenizer').prop('checked', oai_settings.use_ai21_tokenizer);
3137- $('#use_google_tokenizer').prop('checked', oai_settings.use_google_tokenizer);
31383103 $('#claude_use_sysprompt').prop('checked', oai_settings.claude_use_sysprompt);
31393104 $('#use_makersuite_sysprompt').prop('checked', oai_settings.use_makersuite_sysprompt);
31403105 $('#scale-alt').prop('checked', oai_settings.use_alt_scale);
@@ -3170,9 +3135,6 @@ function loadOpenAISettings(data, settings) {
31703135 $('#pres_pen_openai').val(oai_settings.pres_pen_openai);
31713136 $('#pres_pen_counter_openai').val(Number(oai_settings.pres_pen_openai).toFixed(2));
31723137
3173- $('#count_pen').val(oai_settings.count_pen);
3174- $('#count_pen_counter').val(Number(oai_settings.count_pen).toFixed(2));
3175-
31763138 $('#top_p_openai').val(oai_settings.top_p_openai);
31773139 $('#top_p_counter_openai').val(Number(oai_settings.top_p_openai).toFixed(2));
31783140
@@ -3392,7 +3354,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
33923354 temperature: settings.temp_openai,
33933355 frequency_penalty: settings.freq_pen_openai,
33943356 presence_penalty: settings.pres_pen_openai,
3395- count_penalty: settings.count_pen,
33963357 top_p: settings.top_p_openai,
33973358 top_k: settings.top_k_openai,
33983359 top_a: settings.top_a_openai,
@@ -3427,8 +3388,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
34273388 assistant_prefill: settings.assistant_prefill,
34283389 assistant_impersonation: settings.assistant_impersonation,
34293390 human_sysprompt_message: settings.human_sysprompt_message,
3430- use_ai21_tokenizer: settings.use_ai21_tokenizer,
3431- use_google_tokenizer: settings.use_google_tokenizer,
34323391 claude_use_sysprompt: settings.claude_use_sysprompt,
34333392 use_makersuite_sysprompt: settings.use_makersuite_sysprompt,
34343393 use_alt_scale: settings.use_alt_scale,
@@ -3799,7 +3758,6 @@ function onSettingsPresetChange() {
37993758 temperature: ['#temp_openai', 'temp_openai', false],
38003759 frequency_penalty: ['#freq_pen_openai', 'freq_pen_openai', false],
38013760 presence_penalty: ['#pres_pen_openai', 'pres_pen_openai', false],
3802- count_penalty: ['#count_pen', 'count_pen', false],
38033761 top_p: ['#top_p_openai', 'top_p_openai', false],
38043762 top_k: ['#top_k_openai', 'top_k_openai', false],
38053763 top_a: ['#top_a_openai', 'top_a_openai', false],
@@ -3856,8 +3814,6 @@ function onSettingsPresetChange() {
38563814 assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],
38573815 assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false],
38583816 human_sysprompt_message: ['#claude_human_sysprompt_textarea', 'human_sysprompt_message', false],
3859- use_ai21_tokenizer: ['#use_ai21_tokenizer', 'use_ai21_tokenizer', true],
3860- use_google_tokenizer: ['#use_google_tokenizer', 'use_google_tokenizer', true],
38613817 claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true],
38623818 use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true],
38633819 use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],
@@ -4028,6 +3984,11 @@ async function onModelChange() {
40283984 }
40293985
40303986 if ($(this).is('#model_ai21_select')) {
3987+ if (value === '' || value.startsWith('j2-')) {
3988+ value = 'jamba-1.5-large';
3989+ $('#model_ai21_select').val(value);
3990+ }
3991+
40313992 console.log('AI21 model changed to', value);
40323993 oai_settings.ai21_model = value;
40333994 }
@@ -4230,7 +4191,7 @@ async function onModelChange() {
42304191 else if (['command-light-nightly', 'command-nightly'].includes(oai_settings.cohere_model)) {
42314192 $('#openai_max_context').attr('max', max_8k);
42324193 }
42334194 else if (['command-r', oai_settings.cohere_model.includes('command-r-plus'].includes(oai_settings.cohere_model)) {
42344195 $('#openai_max_context').attr('max', max_128k);
42354196 }
42364197 else if (['c4ai-aya-23'].includes(oai_settings.cohere_model)) {
@@ -4305,33 +4266,13 @@ async function onModelChange() {
43054266 if (oai_settings.chat_completion_source == chat_completion_sources.AI21) {
43064267 if (oai_settings.max_context_unlocked) {
43074268 $('#openai_max_context').attr('max', unlocked_max);
4308- } else {
4269+ } else if (oai_settings.ai21_model.includes('jamba-1.5') || oai_settings.ai21_model.includes('jamba-instruct')) {
43094270 $('#openai_max_context').attr('max', ai21_maxmax_256k);
43104271 }
43114272
43124273 oai_settings.openai_max_context = Math.min(oai_settings.openai_max_context, Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
43134274 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
4314-
4275+ $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
4315- oai_settings.temp_openai = Math.min(claude_max_temp, oai_settings.temp_openai);
4316- $('#temp_openai').attr('max', claude_max_temp).val(oai_settings.temp_openai).trigger('input');
4317-
4318- oai_settings.freq_pen_openai = Math.min(j2_max_freq, oai_settings.freq_pen_openai < 0 ? 0 : oai_settings.freq_pen_openai);
4319- $('#freq_pen_openai').attr('min', 0).attr('max', j2_max_freq).val(oai_settings.freq_pen_openai).trigger('input');
4320-
4321- oai_settings.pres_pen_openai = Math.min(j2_max_pres, oai_settings.pres_pen_openai < 0 ? 0 : oai_settings.pres_pen_openai);
4322- $('#pres_pen_openai').attr('min', 0).attr('max', j2_max_pres).val(oai_settings.pres_pen_openai).trigger('input');
4323-
4324- oai_settings.top_k_openai = Math.min(j2_max_topk, oai_settings.top_k_openai);
4325- $('#top_k_openai').attr('max', j2_max_topk).val(oai_settings.top_k_openai).trigger('input');
4326- } else if (oai_settings.chat_completion_source != chat_completion_sources.AI21) {
4327- oai_settings.freq_pen_openai = Math.min(2.0, oai_settings.freq_pen_openai);
4328- $('#freq_pen_openai').attr('min', -2.0).attr('max', 2.0).val(oai_settings.freq_pen_openai).trigger('input');
4329-
4330- oai_settings.pres_pen_openai = Math.min(2.0, oai_settings.pres_pen_openai);
4331- $('#pres_pen_openai').attr('min', -2.0).attr('max', 2.0).val(oai_settings.pres_pen_openai).trigger('input');
4332-
4333- oai_settings.top_k_openai = Math.min(200, oai_settings.top_k_openai);
4334- $('#top_k_openai').attr('max', 200).val(oai_settings.top_k_openai).trigger('input');
43354276 }
43364277
43374278 if (oai_settings.chat_completion_source == chat_completion_sources.CUSTOM) {
@@ -4388,6 +4329,16 @@ async function onModelChange() {
43884329 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
43894330 }
43904331
4332+ if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) {
4333+ oai_settings.pres_pen_openai = Math.min(Math.max(0, oai_settings.pres_pen_openai), 1);
4334+ $('#pres_pen_openai').attr('max', 1).attr('min', 0).val(oai_settings.pres_pen_openai).trigger('input');
4335+ oai_settings.freq_pen_openai = Math.min(Math.max(0, oai_settings.freq_pen_openai), 1);
4336+ $('#freq_pen_openai').attr('max', 1).attr('min', 0).val(oai_settings.freq_pen_openai).trigger('input');
4337+ } else {
4338+ $('#pres_pen_openai').attr('max', 2).attr('min', -2).val(oai_settings.pres_pen_openai).trigger('input');
4339+ $('#freq_pen_openai').attr('max', 2).attr('min', -2).val(oai_settings.freq_pen_openai).trigger('input');
4340+ }
4341+
43914342 $('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max')));
43924343
43934344 saveSettingsDebounced();
@@ -4673,17 +4624,17 @@ function toggleChatCompletionForms() {
46734624async function testApiConnection() {
46744625 // Check if the previous request is still in progress
46754626 if (is_send_press) {
46764627 toastr.info('t`Please wait for the previous request to complete.'`);
46774628 return;
46784629 }
46794630
46804631 try {
46814632 const reply = await sendOpenAIRequest('quiet', [{ 'role': 'user', 'content': 'Hi' }]);
46824633 console.log(reply);
46834634 toastr.success('t`API connection successful!'`);
46844635 }
46854636 catch (err) {
46864637 toastr.error('t`Could not get a reply from API. Check your connection settings / API key and try again.'`);
46874638 }
46884639}
46894640
@@ -4752,11 +4703,14 @@ export function isImageInliningSupported() {
47524703 'gemini-1.5-flash',
47534704 'gemini-1.5-flash-latest',
47544705 'gemini-1.5-flash-001',
4706+ 'gemini-1.5-flash-exp-0827',
4707+ 'gemini-1.5-flash-8b-exp-0827',
47554708 'gemini-1.0-pro-vision-latest',
47564709 'gemini-1.5-pro',
47574710 'gemini-1.5-pro-latest',
47584711 'gemini-1.5-pro-001',
47594712 'gemini-1.5-pro-exp-0801',
4713+ 'gemini-1.5-pro-exp-0827',
47604714 'gemini-pro-vision',
47614715 'claude-3',
47624716 'claude-3-5',
@@ -4837,7 +4791,7 @@ function onProxyPresetChange() {
48374791 if (selectedPreset) {
48384792 setProxyPreset(selectedPreset.name, selectedPreset.url, selectedPreset.password);
48394793 } else {
48404794 console.error(t`Proxy preset "'${value}"' not found in proxies array.`);
48414795 }
48424796 saveSettingsDebounced();
48434797}
@@ -4849,7 +4803,7 @@ $('#save_proxy').on('click', async function () {
48494803
48504804 setProxyPreset(presetName, reverseProxy, proxyPassword);
48514805 saveSettingsDebounced();
48524806 toastr.success('t`Proxy Saved'`);
48534807 if ($('#openai_proxy_preset').val() !== presetName) {
48544808 const option = document.createElement('option');
48554809 option.text = presetName;
@@ -4883,9 +4837,9 @@ $('#delete_proxy').on('click', async function () {
48834837
48844838 saveSettingsDebounced();
48854839 $('#openai_proxy_preset').val(selected_proxy.name);
48864840 toastr.success('t`Proxy Deleted'`);
48874841 } else {
48884842 toastr.error(t`Could not find proxy with name "'${presetName}"'`);
48894843 }
48904844});
48914845
@@ -4953,12 +4907,6 @@ $(document).ready(async function () {
49534907 saveSettingsDebounced();
49544908 });
49554909
4956- $('#count_pen').on('input', function () {
4957- oai_settings.count_pen = Number($(this).val());
4958- $('#count_pen_counter').val(Number($(this).val()).toFixed(2));
4959- saveSettingsDebounced();
4960- });
4961-
49624910 $('#top_p_openai').on('input', function () {
49634911 oai_settings.top_p_openai = Number($(this).val());
49644912 $('#top_p_counter_openai').val(Number($(this).val()).toFixed(2));
@@ -5017,20 +4965,6 @@ $(document).ready(async function () {
50174965 saveSettingsDebounced();
50184966 });
50194967
5020- $('#use_ai21_tokenizer').on('change', function () {
5021- oai_settings.use_ai21_tokenizer = !!$('#use_ai21_tokenizer').prop('checked');
5022- oai_settings.use_ai21_tokenizer ? ai21_max = 8191 : ai21_max = 9200;
5023- oai_settings.openai_max_context = Math.min(ai21_max, oai_settings.openai_max_context);
5024- $('#openai_max_context').attr('max', ai21_max).val(oai_settings.openai_max_context).trigger('input');
5025- $('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max')));
5026- saveSettingsDebounced();
5027- });
5028-
5029- $('#use_google_tokenizer').on('change', function () {
5030- oai_settings.use_google_tokenizer = !!$('#use_google_tokenizer').prop('checked');
5031- saveSettingsDebounced();
5032- });
5033-
50344968 $('#claude_use_sysprompt').on('change', function () {
50354969 oai_settings.claude_use_sysprompt = !!$('#claude_use_sysprompt').prop('checked');
50364970 $('#claude_human_sysprompt_message_block').toggle(oai_settings.claude_use_sysprompt);
public/scripts/power-user.js+1 -0
@@ -107,6 +107,7 @@ export const persona_description_positions = {
107107 TOP_AN: 2,
108108 BOTTOM_AN: 3,
109109 AT_DEPTH: 4,
110+ NONE: 9,
110111};
111112
112113let 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>
6 \ No newline at end of file
public/scripts/templates/newChatConfirm.html+4 -0
@@ -0,0 +1,4 @@
1+<label for="del_chat_checkbox" class="checkbox_label justifyCenter" data-i18n="[title]If necessary, you can later restore this chat file from the /backups folder" title="If necessary, you can later restore this chat file from the /backups folder">
2+ <input type="checkbox" id="del_chat_checkbox" />
3+ <small data-i18n="Also delete the current chat file">Also delete the current chat file</small>
4+</label>
4 \ No newline at end of file
public/scripts/templates/proxyConnectionWarning.html+1 -0
@@ -0,0 +1 @@
1+<span data-i18n="Are you sure you want to connect to the following proxy URL?">Are you sure you want to connect to the following proxy URL?</span><var>{{proxyURL}}</var>
1 \ No newline at end of file
public/scripts/textgen-settings.js+8 -0
@@ -188,6 +188,8 @@ const settings = {
188188 custom_model: '',
189189 bypass_status_check: false,
190190 openrouter_allow_fallbacks: true,
191+ xtc_threshold: 0.1,
192+ xtc_probability: 0,
191193};
192194
193195export let textgenerationwebui_banned_in_macros = [];
@@ -263,6 +265,8 @@ export const setting_names = [
263265 'custom_model',
264266 'bypass_status_check',
265267 'openrouter_allow_fallbacks',
268+ 'xtc_threshold',
269+ 'xtc_probability',
266270];
267271
268272const DYNATEMP_BLOCK = document.getElementById('dynatemp_block_ooba');
@@ -718,6 +722,8 @@ jQuery(function () {
718722 'dry_multiplier_textgenerationwebui': 0,
719723 'dry_base_textgenerationwebui': 1.75,
720724 'dry_penalty_last_n_textgenerationwebui': 0,
725+ 'xtc_threshold_textgenerationwebui': 0.1,
726+ 'xtc_probability_textgenerationwebui': 0,
721727 };
722728
723729 for (const [id, value] of Object.entries(inputs)) {
@@ -1156,6 +1162,8 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
11561162 'api_server': getTextGenServer(),
11571163 'legacy_api': settings.legacy_api && (settings.type === OOBA || settings.type === APHRODITE),
11581164 'sampler_order': settings.type === textgen_types.KOBOLDCPP ? settings.sampler_order : undefined,
1165+ 'xtc_threshold': settings.xtc_threshold,
1166+ 'xtc_probability': settings.xtc_probability,
11591167 };
11601168 const nonAphroditeParams = {
11611169 'rep_pen': settings.rep_pen,
public/scripts/tokenizers.js+26 -23
@@ -27,6 +27,7 @@ export const tokenizers = {
2727 CLAUDE: 11,
2828 LLAMA3: 12,
2929 GEMMA: 13,
30+ JAMBA: 14,
3031 BEST_MATCH: 99,
3132};
3233
@@ -36,6 +37,7 @@ export const SENTENCEPIECE_TOKENIZERS = [
3637 tokenizers.YI,
3738 tokenizers.LLAMA3,
3839 tokenizers.GEMMA,
40+ tokenizers.JAMBA,
3941 // uncomment when NovelAI releases Kayra and Clio weights, lol
4042 //tokenizers.NERD,
4143 //tokenizers.NERD2,
@@ -98,6 +100,11 @@ const TOKENIZER_URLS = {
98100 decode: '/api/tokenizers/gemma/decode',
99101 count: '/api/tokenizers/gemma/encode',
100102 },
103+ [tokenizers.JAMBA]: {
104+ encode: '/api/tokenizers/jamba/encode',
105+ decode: '/api/tokenizers/jamba/decode',
106+ count: '/api/tokenizers/jamba/encode',
107+ },
101108 [tokenizers.API_TEXTGENERATIONWEBUI]: {
102109 encode: '/api/tokenizers/remote/textgenerationwebui/encode',
103110 count: '/api/tokenizers/remote/textgenerationwebui/encode',
@@ -164,7 +171,7 @@ export function getAvailableTokenizers() {
164171 tokenizerId: Number(tokenizerOption.value),
165172 tokenizerKey: Object.entries(tokenizers).find(([_, value]) => value === Number(tokenizerOption.value))[0].toLocaleLowerCase(),
166173 tokenizerName: tokenizerOption.text,
167174 }));
168175}
169176
170177/**
@@ -280,6 +287,12 @@ export function getTokenizerBestMatch(forApi) {
280287 if (model.includes('gemma')) {
281288 return tokenizers.GEMMA;
282289 }
290+ if (model.includes('yi')) {
291+ return tokenizers.YI;
292+ }
293+ if (model.includes('jamba')) {
294+ return tokenizers.JAMBA;
295+ }
283296 }
284297
285298 return tokenizers.LLAMA;
@@ -497,6 +510,7 @@ export function getTokenizerModel() {
497510 const mistralTokenizer = 'mistral';
498511 const yiTokenizer = 'yi';
499512 const gemmaTokenizer = 'gemma';
513+ const jambaTokenizer = 'jamba';
500514
501515 // Assuming no one would use it for different models.. right?
502516 if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) {
@@ -562,12 +576,19 @@ export function getTokenizerModel() {
562576 else if (oai_settings.openrouter_model.includes('GPT-NeoXT')) {
563577 return gpt2Tokenizer;
564578 }
579+ else if (oai_settings.openrouter_model.includes('jamba')) {
580+ return jambaTokenizer;
581+ }
565582 }
566583
567584 if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) {
568585 return gemmaTokenizer;
569586 }
570587
588+ if (oai_settings.chat_completion_source == chat_completion_sources.AI21) {
589+ return jambaTokenizer;
590+ }
591+
571592 if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
572593 return claudeTokenizer;
573594 }
@@ -626,16 +647,7 @@ export function getTokenizerModel() {
626647 * @deprecated Use countTokensOpenAIAsync instead.
627648 */
628649export function countTokensOpenAI(messages, full = false) {
629- const shouldTokenizeAI21 = oai_settings.chat_completion_source === chat_completion_sources.AI21 && oai_settings.use_ai21_tokenizer;
650+ const tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
630- const shouldTokenizeGoogle = oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE && oai_settings.use_google_tokenizer;
631- let tokenizerEndpoint = '';
632- if (shouldTokenizeAI21) {
633- tokenizerEndpoint = '/api/tokenizers/ai21/count';
634- } else if (shouldTokenizeGoogle) {
635- tokenizerEndpoint = `/api/tokenizers/google/count?model=${getTokenizerModel()}&reverse_proxy=${oai_settings.reverse_proxy}&proxy_password=${oai_settings.proxy_password}`;
636- } else {
637- tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
638- }
639651 const cacheObject = getTokenCacheObject();
640652
641653 if (!Array.isArray(messages)) {
@@ -647,7 +659,7 @@ export function countTokensOpenAI(messages, full = false) {
647659 for (const message of messages) {
648660 const model = getTokenizerModel();
649661
650662 if (model === 'claude' || shouldTokenizeAI21 || shouldTokenizeGoogle) {
651663 full = true;
652664 }
653665
@@ -687,16 +699,7 @@ export function countTokensOpenAI(messages, full = false) {
687699 * @returns {Promise<number>} Token count.
688700 */
689701export async function countTokensOpenAIAsync(messages, full = false) {
690- const shouldTokenizeAI21 = oai_settings.chat_completion_source === chat_completion_sources.AI21 && oai_settings.use_ai21_tokenizer;
702+ const tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
691- const shouldTokenizeGoogle = oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE && oai_settings.use_google_tokenizer;
692- let tokenizerEndpoint = '';
693- if (shouldTokenizeAI21) {
694- tokenizerEndpoint = '/api/tokenizers/ai21/count';
695- } else if (shouldTokenizeGoogle) {
696- tokenizerEndpoint = `/api/tokenizers/google/count?model=${getTokenizerModel()}`;
697- } else {
698- tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
699- }
700703 const cacheObject = getTokenCacheObject();
701704
702705 if (!Array.isArray(messages)) {
@@ -708,7 +711,7 @@ export async function countTokensOpenAIAsync(messages, full = false) {
708711 for (const message of messages) {
709712 const model = getTokenizerModel();
710713
711714 if (model === 'claude' || shouldTokenizeAI21 || shouldTokenizeGoogle) {
712715 full = true;
713716 }
714717
public/scripts/world-info.js+6 -2
@@ -4777,8 +4777,10 @@ jQuery(() => {
47774777 world_info_min_activations = Number($(this).val());
47784778 $('#world_info_min_activations_counter').val(world_info_min_activations);
47794779
47804780 if (world_info_min_activations !== 0 && world_info_max_recursion_steps !== 0) {
47814781 $('#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);
47824784 } else {
47834785 saveSettings();
47844786 }
@@ -4840,8 +4842,10 @@ jQuery(() => {
48404842 $('#world_info_max_recursion_steps').on('input', function () {
48414843 world_info_max_recursion_steps = Number($(this).val());
48424844 $('#world_info_max_recursion_steps_counter').val(world_info_max_recursion_steps);
48434845 if (world_info_max_recursion_steps !== 0 && world_info_min_activations !== 0) {
48444846 $('#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);
48454849 } else {
48464850 saveSettings();
48474851 }
public/style.css+0 -1
@@ -2421,7 +2421,6 @@ input[type="file"] {
24212421}
24222422
24232423#rm_print_characters_block .text_block {
2424- height: 100%;
24252424 width: 100%;
24262425 opacity: 0.5;
24272426 margin: 0 auto 1px auto;
src/cohere-stream.js+126 -0
@@ -0,0 +1,126 @@
1+const DATA_PREFIX = 'data:';
2+
3+/**
4+ * Borrowed from Cohere SDK (MIT License)
5+ * https://github.com/cohere-ai/cohere-typescript/blob/main/src/core/streaming-fetcher/Stream.ts
6+ * Copyright (c) 2021 Cohere
7+ */
8+class CohereStream {
9+ /** @type {ReadableStream} */
10+ stream;
11+ /** @type {string} */
12+ prefix;
13+ /** @type {string} */
14+ messageTerminator;
15+ /** @type {string|undefined} */
16+ streamTerminator;
17+ /** @type {AbortController} */
18+ controller = new AbortController();
19+
20+ constructor({ stream, eventShape }) {
21+ this.stream = stream;
22+ if (eventShape.type === 'sse') {
23+ this.prefix = DATA_PREFIX;
24+ this.messageTerminator = '\n';
25+ this.streamTerminator = eventShape.streamTerminator;
26+ } else {
27+ this.messageTerminator = eventShape.messageTerminator;
28+ }
29+ }
30+
31+ async *iterMessages() {
32+ const stream = readableStreamAsyncIterable(this.stream);
33+ let buf = '';
34+ let prefixSeen = false;
35+ let parsedAnyMessages = false;
36+ for await (const chunk of stream) {
37+ buf += this.decodeChunk(chunk);
38+
39+ let terminatorIndex;
40+ // Parse the chunk into as many messages as possible
41+ while ((terminatorIndex = buf.indexOf(this.messageTerminator)) >= 0) {
42+ // Extract the line from the buffer
43+ let line = buf.slice(0, terminatorIndex + 1);
44+ buf = buf.slice(terminatorIndex + 1);
45+
46+ // Skip empty lines
47+ if (line.length === 0) {
48+ continue;
49+ }
50+
51+ // Skip the chunk until the prefix is found
52+ if (!prefixSeen && this.prefix != null) {
53+ const prefixIndex = line.indexOf(this.prefix);
54+ if (prefixIndex === -1) {
55+ continue;
56+ }
57+ prefixSeen = true;
58+ line = line.slice(prefixIndex + this.prefix.length);
59+ }
60+
61+ // If the stream terminator is present, return
62+ if (this.streamTerminator != null && line.includes(this.streamTerminator)) {
63+ return;
64+ }
65+
66+ // Otherwise, yield message from the prefix to the terminator
67+ const message = JSON.parse(line);
68+ yield message;
69+ prefixSeen = false;
70+ parsedAnyMessages = true;
71+ }
72+ }
73+
74+ if (!parsedAnyMessages && buf.length > 0) {
75+ try {
76+ yield JSON.parse(buf);
77+ } catch (e) {
78+ console.error('Error parsing message:', e);
79+ }
80+ }
81+ }
82+
83+ async *[Symbol.asyncIterator]() {
84+ for await (const message of this.iterMessages()) {
85+ yield message;
86+ }
87+ }
88+
89+ decodeChunk(chunk) {
90+ const decoder = new TextDecoder('utf8');
91+ return decoder.decode(chunk);
92+ }
93+}
94+
95+function readableStreamAsyncIterable(stream) {
96+ if (stream[Symbol.asyncIterator]) {
97+ return stream;
98+ }
99+
100+ const reader = stream.getReader();
101+ return {
102+ async next() {
103+ try {
104+ const result = await reader.read();
105+ if (result?.done) {
106+ reader.releaseLock();
107+ } // release lock when stream becomes closed
108+ return result;
109+ } catch (e) {
110+ reader.releaseLock(); // release lock when stream becomes errored
111+ throw e;
112+ }
113+ },
114+ async return() {
115+ const cancelPromise = reader.cancel();
116+ reader.releaseLock();
117+ await cancelPromise;
118+ return { done: true, value: undefined };
119+ },
120+ [Symbol.asyncIterator]() {
121+ return this;
122+ },
123+ };
124+}
125+
126+module.exports = CohereStream;
src/endpoints/backends/chat-completions.js+56 -86
@@ -1,11 +1,11 @@
11const express = require('express');
22const fetch = require('node-fetch').default;
3-const Readable = require('stream').Readable;
43
54const { jsonParser } = require('../../express-common');
65const { CHAT_COMPLETION_SOURCES, GEMINI_SAFETY, BISON_SAFETY, OPENROUTER_HEADERS } = require('../../constants');
76const { forwardFetchResponse, getConfigValue, tryParse, uuidv4, mergeObjectWithYaml, excludeKeysByYaml, color } = require('../../util');
87const { convertClaudeMessages, convertGooglePrompt, convertTextCompletionPrompt, convertCohereMessages, convertMistralMessages, convertCohereTools, convertAI21Messages } = require('../../prompt-converters');
8+const CohereStream = require('../../cohere-stream');
99
1010const { readSecret, SECRET_KEYS } = require('../secrets');
1111const { getTokenizerModel, getSentencepiceTokenizer, getTiktokenTokenizer, sentencepieceTokenizers, TEXT_COMPLETION_MODELS } = require('../tokenizers');
@@ -19,6 +19,7 @@ const API_GROQ = 'https://api.groq.com/openai/v1';
1919const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';
2020const API_01AI = 'https://api.01.ai/v1';
2121const API_BLOCKENTROPY = 'https://api.blockentropy.ai/v1';
22+const API_AI21 = 'https://api.ai21.com/studio/v1';
2223
2324/**
2425 * Applies a post-processing step to the generated messages.
@@ -40,52 +41,30 @@ function postProcessPrompt(messages, type, charName, userName) {
4041/**
4142 * Ollama strikes back. Special boy #2's steaming routine.
4243 * Wrap this abomination into proper SSE stream, again.
4344 * @param {import('node-fetch').Response} jsonStream JSON stream
4445 * @param {import('express').Request} request Express request
4546 * @param {import('express').Response} response Express response
4647 * @returns {Promise<any>} Nothing valuable
4748 */
4849async function parseCohereStream(jsonStream, request, response) {
4950 try {
50- let partialData = '';
51+ const stream = new CohereStream({ stream: jsonStream.body, eventShape: { type: 'json', messageTerminator: '\n' } });
51- jsonStream.body.on('data', (data) => {
52+
52- const chunk = data.toString();
53+ for await (const json of stream.iterMessages()) {
53- partialData += chunk;
54- while (true) {
55- let json;
56- try {
57- json = JSON.parse(partialData);
58- } catch (e) {
59- break;
60- }
6154 if (json.message) {
6255 const message = json.message || 'Unknown error';
6356 const chunk = { error: { message: message } };
6457 response.write(`data: ${JSON.stringify(chunk)}\n\n`);
65- partialData = '';
66- break;
6758 } else if (json.event_type === 'text-generation') {
6859 const text = json.text || '';
6960 const chunk = { choices: [{ text }] };
7061 response.write(`data: ${JSON.stringify(chunk)}\n\n`);
71- partialData = '';
72- } else {
73- partialData = '';
74- break;
7562 }
7663 }
77- });
7864
79- request.socket.on('close', function () {
80- if (jsonStream.body instanceof Readable) jsonStream.body.destroy();
81- response.end();
82- });
83-
84- jsonStream.body.on('end', () => {
8565 console.log('Streaming request finished');
8666 response.write('data: [DONE]\n\n');
8767 response.end();
88- });
8968 } catch (error) {
9069 console.log('Error forwarding streaming response:', error);
9170 if (!response.headersSent) {
@@ -233,7 +212,7 @@ async function sendScaleRequest(request, response) {
233212
234213 if (!generateResponse.ok) {
235214 console.log(`Scale API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
236215 return response.status(generateResponse.status500).send({ error: true });
237216 }
238217
239218 const generateResponseJson = await generateResponse.json();
@@ -413,6 +392,16 @@ async function sendAI21Request(request, response) {
413392 request.socket.on('close', function () {
414393 controller.abort();
415394 });
395+ const convertedPrompt = convertAI21Messages(request.body.messages, request.body.char_name, request.body.user_name);
396+ const body = {
397+ messages: convertedPrompt,
398+ model: request.body.model,
399+ max_tokens: request.body.max_tokens,
400+ temperature: request.body.temperature,
401+ top_p: request.body.top_p,
402+ stop: request.body.stop,
403+ stream: request.body.stream,
404+ };
416405 const options = {
417406 method: 'POST',
418407 headers: {
@@ -420,59 +409,35 @@ async function sendAI21Request(request, response) {
420409 'content-type': 'application/json',
421410 Authorization: `Bearer ${readSecret(request.user.directories, SECRET_KEYS.AI21)}`,
422411 },
423412 body: JSON.stringify({body),
424- numResults: 1,
425- maxTokens: request.body.max_tokens,
426- minTokens: 0,
427- temperature: request.body.temperature,
428- topP: request.body.top_p,
429- stopSequences: request.body.stop_tokens,
430- topKReturn: request.body.top_k,
431- frequencyPenalty: {
432- scale: request.body.frequency_penalty * 100,
433- applyToWhitespaces: false,
434- applyToPunctuations: false,
435- applyToNumbers: false,
436- applyToStopwords: false,
437- applyToEmojis: false,
438- },
439- presencePenalty: {
440- scale: request.body.presence_penalty,
441- applyToWhitespaces: false,
442- applyToPunctuations: false,
443- applyToNumbers: false,
444- applyToStopwords: false,
445- applyToEmojis: false,
446- },
447- countPenalty: {
448- scale: request.body.count_pen,
449- applyToWhitespaces: false,
450- applyToPunctuations: false,
451- applyToNumbers: false,
452- applyToStopwords: false,
453- applyToEmojis: false,
454- },
455- prompt: request.body.messages,
456- }),
457413 signal: controller.signal,
458414 };
459415
460- fetch(`https://api.ai21.com/studio/v1/${request.body.model}/complete`, options)
416+ console.log('AI21 request:', body);
461- .then(r => r.json())
417+
462- .then(r => {
418+ try {
463- if (r.completions === undefined) {
419+ const generateResponse = await fetch(API_AI21 + '/chat/completions', options);
464- console.log(r);
420+ if (request.body.stream) {
421+ forwardFetchResponse(generateResponse, response);
465422 } else {
466- console.log(r.completions[0].data.text);
423+ if (!generateResponse.ok) {
424+ const errorText = await generateResponse.text();
425+ console.log(`AI21 API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
426+ const errorJson = tryParse(errorText) ?? { error: true };
427+ return response.status(500).send(errorJson);
428+ }
429+ const generateResponseJson = await generateResponse.json();
430+ console.log('AI21 response:', generateResponseJson);
431+ return response.send(generateResponseJson);
432+ }
433+ } catch (error) {
434+ console.log('Error communicating with AI21 API: ', error);
435+ if (!response.headersSent) {
436+ response.send({ error: true });
437+ } else {
438+ response.end();
439+ }
467440 }
468- const reply = { choices: [{ 'message': { 'content': r.completions?.[0]?.data?.text } }] };
469- return response.send(reply);
470- })
471- .catch(err => {
472- console.error(err);
473- return response.send({ error: true });
474- });
475-
476441}
477442
478443/**
@@ -531,10 +496,10 @@ async function sendMistralAIRequest(request, response) {
531496 forwardFetchResponse(generateResponse, response);
532497 } else {
533498 if (!generateResponse.ok) {
534- console.log(`MistralAI API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
499+ const errorText = await generateResponse.text();
535- // a 401 unauthorized response breaks the frontend auth, so return a 500 instead. prob a better way of dealing with this.
500+ console.log(`MistralAI API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
536- // 401s are already handled by the streaming processor and dont pop up an error toast, that should probably be fixed too.
501+ const errorJson = tryParse(errorText) ?? { error: true };
537502 return response.status(generateResponse.status === 401 ? 500 : generateResponse.status).send({ error: true }errorJson);
538503 }
539504 const generateResponseJson = await generateResponse.json();
540505 console.log('MistralAI response:', generateResponseJson);
@@ -607,6 +572,11 @@ async function sendCohereRequest(request, response) {
607572 search_queries_only: false,
608573 };
609574
575+ const canDoSafetyMode = String(request.body.model).endsWith('08-2024');
576+ if (canDoSafetyMode) {
577+ requestBody.safety_mode = 'NONE';
578+ }
579+
610580 console.log('Cohere request:', requestBody);
611581
612582 const config = {
@@ -623,15 +593,15 @@ async function sendCohereRequest(request, response) {
623593 const apiUrl = API_COHERE + '/chat';
624594
625595 if (request.body.stream) {
626596 const stream = await global.fetch(apiUrl, config);
627597 parseCohereStream(stream, request, response);
628598 } else {
629599 const generateResponse = await fetch(apiUrl, config);
630600 if (!generateResponse.ok) {
631- console.log(`Cohere API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
601+ const errorText = await generateResponse.text();
632- // a 401 unauthorized response breaks the frontend auth, so return a 500 instead. prob a better way of dealing with this.
602+ console.log(`Cohere API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
633- // 401s are already handled by the streaming processor and dont pop up an error toast, that should probably be fixed too.
603+ const errorJson = tryParse(errorText) ?? { error: true };
634604 return response.status(generateResponse.status === 401 ? 500 : generateResponse.status).send({ error: true }errorJson);
635605 }
636606 const generateResponseJson = await generateResponse.json();
637607 console.log('Cohere response:', generateResponseJson);
src/endpoints/novelai.js+5 -5
@@ -6,6 +6,7 @@ const { readAllChunks, extractFileFromZipBuffer, forwardFetchResponse } = requir
66const { jsonParser } = require('../express-common');
77
88const API_NOVELAI = 'https://api.novelai.net';
9+const TEXT_NOVELAI = 'https://text.novelai.net';
910const IMAGE_NOVELAI = 'https://image.novelai.net';
1011
1112// Ban bracket generation, plus defaults
@@ -155,7 +156,7 @@ router.post('/generate', jsonParser, async function (req, res) {
155156 'repetition_penalty_slope': req.body.repetition_penalty_slope,
156157 'repetition_penalty_frequency': req.body.repetition_penalty_frequency,
157158 'repetition_penalty_presence': req.body.repetition_penalty_presence,
158159 'repetition_penalty_whitelist': isNewModel ? repPenaltyAllowList.flat() : null,
159160 'top_a': req.body.top_a,
160161 'top_p': req.body.top_p,
161162 'top_k': req.body.top_k,
@@ -178,9 +179,7 @@ router.post('/generate', jsonParser, async function (req, res) {
178179 };
179180
180181 // Tells the model to stop generation at '>'
181182 if ('theme_textadventure' === req.body.prefix && isNewModel) {
182- (true === req.body.model.includes('clio') ||
183- true === req.body.model.includes('kayra'))) {
184183 data.parameters.eos_token_id = 49405;
185184 }
186185
@@ -193,7 +192,8 @@ router.post('/generate', jsonParser, async function (req, res) {
193192 };
194193
195194 try {
196195 const urlbaseURL = req.body.streamingmodel.includes('kayra') ? `${API_NOVELAI}/ai/generate-stream`TEXT_NOVELAI : `${API_NOVELAI}/ai/generate`;
196+ const url = req.body.streaming ? `${baseURL}/ai/generate-stream` : `${baseURL}/ai/generate`;
197197 const response = await fetch(url, { method: 'POST', timeout: 0, ...args });
198198
199199 if (req.body.streaming) {
src/endpoints/tokenizers.js+27 -47
@@ -144,6 +144,7 @@ const spp_nerd_v2 = new SentencePieceTokenizer('src/tokenizers/nerdstash_v2.mode
144144const spp_mistral = new SentencePieceTokenizer('src/tokenizers/mistral.model');
145145const spp_yi = new SentencePieceTokenizer('src/tokenizers/yi.model');
146146const spp_gemma = new SentencePieceTokenizer('src/tokenizers/gemma.model');
147+const spp_jamba = new SentencePieceTokenizer('src/tokenizers/jamba.model');
147148const claude_tokenizer = new WebTokenizer('src/tokenizers/claude.json');
148149const llama3_tokenizer = new WebTokenizer('src/tokenizers/llama3.json');
149150
@@ -154,6 +155,7 @@ const sentencepieceTokenizers = [
154155 'mistral',
155156 'yi',
156157 'gemma',
158+ 'jamba',
157159];
158160
159161/**
@@ -186,6 +188,10 @@ function getSentencepiceTokenizer(model) {
186188 return spp_gemma;
187189 }
188190
191+ if (model.includes('jamba')) {
192+ return spp_jamba;
193+ }
194+
189195 return null;
190196}
191197
@@ -322,6 +328,10 @@ function getTokenizerModel(requestModel) {
322328 return 'gemma';
323329 }
324330
331+ if (requestModel.includes('jamba')) {
332+ return 'jamba';
333+ }
334+
325335 // default
326336 return 'gpt-3.5-turbo';
327337}
@@ -537,59 +547,13 @@ function createWebTokenizerDecodingHandler(tokenizer) {
537547
538548const router = express.Router();
539549
540-router.post('/ai21/count', jsonParser, async function (req, res) {
541- if (!req.body) return res.sendStatus(400);
542- const key = readSecret(req.user.directories, SECRET_KEYS.AI21);
543- const options = {
544- method: 'POST',
545- headers: {
546- accept: 'application/json',
547- 'content-type': 'application/json',
548- Authorization: `Bearer ${key}`,
549- },
550- body: JSON.stringify({ text: req.body[0].content }),
551- };
552-
553- try {
554- const response = await fetch('https://api.ai21.com/studio/v1/tokenize', options);
555- const data = await response.json();
556- return res.send({ 'token_count': data?.tokens?.length || 0 });
557- } catch (err) {
558- console.error(err);
559- return res.send({ 'token_count': 0 });
560- }
561-});
562-
563-router.post('/google/count', jsonParser, async function (req, res) {
564- if (!req.body) return res.sendStatus(400);
565- const options = {
566- method: 'POST',
567- headers: {
568- accept: 'application/json',
569- 'content-type': 'application/json',
570- },
571- body: JSON.stringify({ contents: convertGooglePrompt(req.body, String(req.query.model)).contents }),
572- };
573- try {
574- const reverseProxy = req.query.reverse_proxy?.toString() || '';
575- const proxyPassword = req.query.proxy_password?.toString() || '';
576- const apiKey = reverseProxy ? proxyPassword : readSecret(req.user.directories, SECRET_KEYS.MAKERSUITE);
577- const apiUrl = new URL(reverseProxy || API_MAKERSUITE);
578- const response = await fetch(`${apiUrl.origin}/v1beta/models/${req.query.model}:countTokens?key=${apiKey}`, options);
579- const data = await response.json();
580- return res.send({ 'token_count': data?.totalTokens || 0 });
581- } catch (err) {
582- console.error(err);
583- return res.send({ 'token_count': 0 });
584- }
585-});
586-
587550router.post('/llama/encode', jsonParser, createSentencepieceEncodingHandler(spp_llama));
588551router.post('/nerdstash/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd));
589552router.post('/nerdstash_v2/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd_v2));
590553router.post('/mistral/encode', jsonParser, createSentencepieceEncodingHandler(spp_mistral));
591554router.post('/yi/encode', jsonParser, createSentencepieceEncodingHandler(spp_yi));
592555router.post('/gemma/encode', jsonParser, createSentencepieceEncodingHandler(spp_gemma));
556+router.post('/jamba/encode', jsonParser, createSentencepieceEncodingHandler(spp_jamba));
593557router.post('/gpt2/encode', jsonParser, createTiktokenEncodingHandler('gpt2'));
594558router.post('/claude/encode', jsonParser, createWebTokenizerEncodingHandler(claude_tokenizer));
595559router.post('/llama3/encode', jsonParser, createWebTokenizerEncodingHandler(llama3_tokenizer));
@@ -599,6 +563,7 @@ router.post('/nerdstash_v2/decode', jsonParser, createSentencepieceDecodingHandl
599563router.post('/mistral/decode', jsonParser, createSentencepieceDecodingHandler(spp_mistral));
600564router.post('/yi/decode', jsonParser, createSentencepieceDecodingHandler(spp_yi));
601565router.post('/gemma/decode', jsonParser, createSentencepieceDecodingHandler(spp_gemma));
566+router.post('/jamba/decode', jsonParser, createSentencepieceDecodingHandler(spp_jamba));
602567router.post('/gpt2/decode', jsonParser, createTiktokenDecodingHandler('gpt2'));
603568router.post('/claude/decode', jsonParser, createWebTokenizerDecodingHandler(claude_tokenizer));
604569router.post('/llama3/decode', jsonParser, createWebTokenizerDecodingHandler(llama3_tokenizer));
@@ -637,6 +602,11 @@ router.post('/openai/encode', jsonParser, async function (req, res) {
637602 return handler(req, res);
638603 }
639604
605+ if (queryModel.includes('jamba')) {
606+ const handler = createSentencepieceEncodingHandler(spp_jamba);
607+ return handler(req, res);
608+ }
609+
640610 const model = getTokenizerModel(queryModel);
641611 const handler = createTiktokenEncodingHandler(model);
642612 return handler(req, res);
@@ -680,6 +650,11 @@ router.post('/openai/decode', jsonParser, async function (req, res) {
680650 return handler(req, res);
681651 }
682652
653+ if (queryModel.includes('jamba')) {
654+ const handler = createSentencepieceDecodingHandler(spp_jamba);
655+ return handler(req, res);
656+ }
657+
683658 const model = getTokenizerModel(queryModel);
684659 const handler = createTiktokenDecodingHandler(model);
685660 return handler(req, res);
@@ -731,6 +706,11 @@ router.post('/openai/count', jsonParser, async function (req, res) {
731706 return res.send({ 'token_count': num_tokens });
732707 }
733708
709+ if (model === 'jamba') {
710+ num_tokens = await countSentencepieceArrayTokens(spp_jamba, req.body);
711+ return res.send({ 'token_count': num_tokens });
712+ }
713+
734714 const tokensPerName = queryModel.includes('gpt-3.5-turbo-0301') ? -1 : 1;
735715 const tokensPerMessage = queryModel.includes('gpt-3.5-turbo-0301') ? 4 : 3;
736716 const tokensPadding = 3;
src/endpoints/translate.js+1 -1
@@ -110,6 +110,7 @@ router.post('/google', jsonParser, async (request, response) => {
110110});
111111
112112router.post('/yandex', jsonParser, async (request, response) => {
113+ try {
113114 const chunks = request.body.chunks;
114115 const lang = request.body.lang;
115116
@@ -130,7 +131,6 @@ router.post('/yandex', jsonParser, async (request, response) => {
130131
131132 console.log('Input text: ' + inputText);
132133
133- try {
134134 const result = await fetch(`https://translate.yandex.net/api/v1/tr.json/translate?ucid=${ucid}&srv=android&format=text`, {
135135 method: 'POST',
136136 body: params,
src/prompt-converters.js+76 -0
@@ -267,10 +267,13 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '
267267 'gemini-1.5-flash',
268268 'gemini-1.5-flash-latest',
269269 'gemini-1.5-flash-001',
270+ 'gemini-1.5-flash-exp-0827',
271+ 'gemini-1.5-flash-8b-exp-0827',
270272 'gemini-1.5-pro',
271273 'gemini-1.5-pro-latest',
272274 'gemini-1.5-pro-001',
273275 'gemini-1.5-pro-exp-0801',
276+ 'gemini-1.5-pro-exp-0827',
274277 'gemini-1.0-pro-vision-latest',
275278 'gemini-pro-vision',
276279 ];
@@ -368,6 +371,78 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '
368371}
369372
370373/**
374+ * Convert AI21 prompt. Classic: system message squash, user/assistant message merge.
375+ * @param {object[]} messages Array of messages
376+ * @param {string} charName Character name
377+ * @param {string} userName User name
378+ */
379+function convertAI21Messages(messages, charName = '', userName = '') {
380+ if (!Array.isArray(messages)) {
381+ return [];
382+ }
383+
384+ // Collect all the system messages up until the first instance of a non-system message, and then remove them from the messages array.
385+ let i = 0, systemPrompt = '';
386+
387+ for (i = 0; i < messages.length; i++) {
388+ if (messages[i].role !== 'system') {
389+ break;
390+ }
391+ // Append example names if not already done by the frontend (e.g. for group chats).
392+ if (userName && messages[i].name === 'example_user') {
393+ if (!messages[i].content.startsWith(`${userName}: `)) {
394+ messages[i].content = `${userName}: ${messages[i].content}`;
395+ }
396+ }
397+ if (charName && messages[i].name === 'example_assistant') {
398+ if (!messages[i].content.startsWith(`${charName}: `)) {
399+ messages[i].content = `${charName}: ${messages[i].content}`;
400+ }
401+ }
402+ systemPrompt += `${messages[i].content}\n\n`;
403+ }
404+
405+ messages.splice(0, i);
406+
407+ // Prevent erroring out if the messages array is empty.
408+ if (messages.length === 0) {
409+ messages.unshift({
410+ role: 'user',
411+ content: '[Start a new chat]',
412+ });
413+ }
414+
415+ if (systemPrompt) {
416+ messages.unshift({
417+ role: 'system',
418+ content: systemPrompt.trim(),
419+ });
420+ }
421+
422+ // Doesn't support completion names, so prepend if not already done by the frontend (e.g. for group chats).
423+ messages.forEach(msg => {
424+ if ('name' in msg) {
425+ if (msg.role !== 'system' && !msg.content.startsWith(`${msg.name}: `)) {
426+ msg.content = `${msg.name}: ${msg.content}`;
427+ }
428+ delete msg.name;
429+ }
430+ });
431+
432+ // Since the messaging endpoint only supports alternating turns, we have to merge messages with the same role if they follow each other
433+ let mergedMessages = [];
434+ messages.forEach((message) => {
435+ if (mergedMessages.length > 0 && mergedMessages[mergedMessages.length - 1].role === message.role) {
436+ mergedMessages[mergedMessages.length - 1].content += '\n\n' + message.content;
437+ } else {
438+ mergedMessages.push(message);
439+ }
440+ });
441+
442+ return mergedMessages;
443+}
444+
445+/**
371446 * Convert a prompt from the ChatML objects to the format used by MistralAI.
372447 * @param {object[]} messages Array of messages
373448 * @param {string} charName Character name
@@ -520,4 +595,5 @@ module.exports = {
520595 convertCohereMessages,
521596 convertMistralMessages,
522597 convertCohereTools,
598+ convertAI21Messages,
523599};
src/tokenizers/jamba.model+0 -0

Binary file

src/util.js+5 -4
@@ -34,8 +34,9 @@ function getConfig() {
3434 CACHED_CONFIG = config;
3535 return config;
3636 } catch (error) {
3737 console.warnerror(color.red('FATAL: Failed to read config.yaml. Please check the file for syntax errors.'));
38- return {};
38+ console.error(error.message);
39+ process.exit(1);
3940 }
4041}
4142
@@ -298,8 +299,8 @@ const color = {
298299 * @returns {string} A UUIDv4 string
299300 */
300301function uuidv4() {
301302 if ('crypto' in global && 'randomUUID' in global.crypto) {
302303 return global.crypto.randomUUID();
303304 }
304305 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
305306 const r = Math.random() * 16 | 0;