Merge branch 'staging' into pr/Cohee1207/2711

This commit is contained in:
RossAscends
2024-09-01 19:56:42 +09:00
27 changed files with 667 additions and 463 deletions

View File

@ -8,7 +8,7 @@
"openrouter_force_instruct": false, "openrouter_force_instruct": false,
"openrouter_group_models": false, "openrouter_group_models": false,
"openrouter_sort_models": "alphabetically", "openrouter_sort_models": "alphabetically",
"ai21_model": "j2-ultra", "ai21_model": "jamba-1.5-large",
"mistralai_model": "mistral-large-latest", "mistralai_model": "mistral-large-latest",
"custom_model": "", "custom_model": "",
"custom_url": "", "custom_url": "",
@ -19,7 +19,6 @@
"temperature": 1, "temperature": 1,
"frequency_penalty": 0, "frequency_penalty": 0,
"presence_penalty": 0, "presence_penalty": 0,
"count_penalty": 0,
"top_p": 1, "top_p": 1,
"top_k": 0, "top_k": 0,
"top_a": 0, "top_a": 0,
@ -233,8 +232,6 @@
"assistant_prefill": "", "assistant_prefill": "",
"assistant_impersonation": "", "assistant_impersonation": "",
"human_sysprompt_message": "Let's get started. Please generate your response based on the information and instructions provided above.", "human_sysprompt_message": "Let's get started. Please generate your response based on the information and instructions provided above.",
"use_ai21_tokenizer": false,
"use_google_tokenizer": false,
"claude_use_sysprompt": false, "claude_use_sysprompt": false,
"use_alt_scale": false, "use_alt_scale": false,
"squash_system_messages": false, "squash_system_messages": false,

View File

@ -196,7 +196,15 @@
"enableLabMode": false, "enableLabMode": false,
"enableZenSliders": false, "enableZenSliders": false,
"ui_mode": 1, "ui_mode": 1,
"forbid_external_media": true "forbid_external_media": true,
"stscript": {
"parser": {
"flags": {
"1": true,
"2": true
}
}
}
}, },
"extension_settings": { "extension_settings": {
"apiUrl": "http://localhost:5100", "apiUrl": "http://localhost:5100",
@ -452,7 +460,6 @@
"temp_openai": 1.0, "temp_openai": 1.0,
"freq_pen_openai": 0, "freq_pen_openai": 0,
"pres_pen_openai": 0, "pres_pen_openai": 0,
"count_pen": 0,
"top_p_openai": 1, "top_p_openai": 1,
"top_k_openai": 0, "top_k_openai": 0,
"stream_openai": true, "stream_openai": true,
@ -614,7 +621,7 @@
"wi_format": "{0}", "wi_format": "{0}",
"openai_model": "gpt-4-turbo", "openai_model": "gpt-4-turbo",
"claude_model": "claude-3-5-sonnet-20240620", "claude_model": "claude-3-5-sonnet-20240620",
"ai21_model": "j2-ultra", "ai21_model": "jamba-1.5-large",
"windowai_model": "", "windowai_model": "",
"openrouter_model": "OR_Website", "openrouter_model": "OR_Website",
"jailbreak_system": true, "jailbreak_system": true,
@ -625,7 +632,6 @@
"show_external_models": false, "show_external_models": false,
"proxy_password": "", "proxy_password": "",
"assistant_prefill": "", "assistant_prefill": "",
"assistant_impersonation": "", "assistant_impersonation": ""
"use_ai21_tokenizer": false
} }
} }

View File

@ -5,7 +5,7 @@
<title>SillyTavern</title> <title>SillyTavern</title>
<base href="/"> <base href="/">
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, viewport-fit=cover, initial-scale=1, maximum-scale=1.0, user-scalable=no"> <meta name="viewport" content="width=device-width, viewport-fit=cover, initial-scale=1, maximum-scale=1.0, user-scalable=no, interactive-widget=resizes-content">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="darkreader-lock"> <meta name="darkreader-lock">
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
@ -436,7 +436,7 @@
</div> </div>
</div> </div>
</div> </div>
<div data-newbie-hidden class="range-block" data-source="openai,openrouter,ai21,custom,cohere,perplexity,groq"> <div data-newbie-hidden class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq">
<div class="range-block-title" data-i18n="Frequency Penalty"> <div class="range-block-title" data-i18n="Frequency Penalty">
Frequency Penalty Frequency Penalty
</div> </div>
@ -449,7 +449,7 @@
</div> </div>
</div> </div>
</div> </div>
<div data-newbie-hidden class="range-block" data-source="openai,openrouter,ai21,custom,cohere,perplexity,groq"> <div data-newbie-hidden class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq">
<div class="range-block-title" data-i18n="Presence Penalty"> <div class="range-block-title" data-i18n="Presence Penalty">
Presence Penalty Presence Penalty
</div> </div>
@ -462,20 +462,7 @@
</div> </div>
</div> </div>
</div> </div>
<div data-newbie-hidden class="range-block" data-source="ai21"> <div data-newbie-hidden class="range-block" data-source="claude,openrouter,makersuite,cohere,perplexity">
<div class="range-block-title" data-i18n="Count Penalty">
Count Penalty
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input type="range" id="count_pen" name="volume" min="0" max="1" step="0.01">
</div>
<div class="range-block-counter">
<input type="number" min="0" max="1" step="0.01" data-for="count_pen" id="count_pen_counter">
</div>
</div>
</div>
<div data-newbie-hidden class="range-block" data-source="claude,openrouter,ai21,makersuite,cohere,perplexity">
<div class="range-block-title" data-i18n="Top K"> <div class="range-block-title" data-i18n="Top K">
Top K Top K
</div> </div>
@ -1291,6 +1278,28 @@
</div> </div>
</div> </div>
</div> </div>
<div data-newbie-hidden data-tg-type="koboldcpp" id="xtc_block" class="wide100p">
<h4 class="wide100p textAlignCenter">
<label data-i18n="Exclude Top Choices (XTC)">Exclude Top Choices (XTC)</label>
<a href="https://github.com/oobabooga/text-generation-webui/pull/6335" target="_blank">
<div class=" fa-solid fa-circle-info opacity50p"></div>
</a>
</h4>
<div class="flex-container flexFlowRow gap10px flexShrink">
<div class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
<small data-i18n="Threshold">Threshold</small>
<input class="neo-range-slider" type="range" id="xtc_threshold_textgenerationwebui" name="volume" min="0" max="0.5" step="0.01" />
<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">
</div>
<div class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
<small data-i18n="Probability">Probability</small>
<input class="neo-range-slider" type="range" id="xtc_probability_textgenerationwebui" name="volume" min="0" max="1" step="0.01" />
<input class="neo-range-input" type="number" min="0" max="1" step="0.01" data-for="xtc_probability_textgenerationwebui" id="xtc_probability_counter_textgenerationwebui">
</div>
</div>
</div>
<!-- Enable for llama.cpp when the PR is merged: https://github.com/ggerganov/llama.cpp/pull/6839 --> <!-- Enable for llama.cpp when the PR is merged: https://github.com/ggerganov/llama.cpp/pull/6839 -->
<div data-newbie-hidden data-tg-type="ooba, koboldcpp" id="dryBlock" class="wide100p"> <div data-newbie-hidden data-tg-type="ooba, koboldcpp" id="dryBlock" class="wide100p">
<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"> <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 @@
<div id="banned_tokens_block_ooba" data-newbie-hidden class="wide100p"> <div id="banned_tokens_block_ooba" data-newbie-hidden class="wide100p">
<hr data-newbie-hidden class="width100p"> <hr data-newbie-hidden class="width100p">
<h4 class="range-block-title justifyCenter"> <h4 class="range-block-title justifyCenter">
<span data-i18n="Banned Tokens">Banned Tokens</span> <span data-i18n="Banned Tokens">Banned Tokens/Strings</span>
<div class="margin5 fa-solid fa-circle-info opacity50p " data-i18n="[title]LLaMA / Mistral / Yi models only" title="LLaMA / Mistral / Yi models only. Make sure to select an appropriate tokenizer first.&#13;Sequences you don't want to appear in the output.&#13;One per line. Text or [token ids].&#13;Most tokens have a leading space. Use token counter if unsure."></div> <div class="margin5 fa-solid fa-circle-info opacity50p " data-i18n="[title]LLaMA / Mistral / Yi models only" title="Enter sequences you don't want to appear in the output.&#13;Unquoted text will be tokenized in the back end and banned as tokens.&#13;[token ids] will be banned as-is.&#13;Most tokens have a leading space. Use token counter (with the correct tokenizer selected first!) if you are unsure.&#13;Enclose text in double quotes to ban the entire string as a set.&#13;Quoted Strings and [Token ids] must be on their own line."></div>
</h4> </h4>
<div class="wide100p"> <div class="wide100p">
<textarea id="banned_tokens_textgenerationwebui" class="text_pole textarea_compact" name="banned_tokens_textgenerationwebui" rows="3" data-i18n="[placeholder]Example: some text [42, 69, 1337]" placeholder="Example:&#10;some text&#10;[42, 69, 1337]"></textarea> <textarea id="banned_tokens_textgenerationwebui" class="text_pole textarea_compact" name="banned_tokens_textgenerationwebui" rows="3" data-i18n="[placeholder]Example: some text [42, 69, 1337]" placeholder='some text as tokens&#10;[420, 69, 1337]&#10;"Some verbatim string"'></textarea>
</div> </div>
</div> </div>
<div class="range-block wide100p"> <div class="range-block wide100p">
@ -1791,22 +1800,6 @@
</select> </select>
</div> </div>
</div> </div>
<div class="range-block" data-source="ai21">
<label for="use_ai21_tokenizer" title="Use AI21 Tokenizer" data-i18n="[title]Use AI21 Tokenizer" class="checkbox_label widthFreeExpand">
<input id="use_ai21_tokenizer" type="checkbox" /><span data-i18n="Use AI21 Tokenizer">Use AI21 Tokenizer</span>
</label>
<div class="toggle-description justifyLeft">
<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>
</div>
</div>
<div class="range-block" data-source="makersuite">
<label for="use_google_tokenizer" title="Use Google Tokenizer" data-i18n="[title]Use Google Tokenizer" class="checkbox_label widthFreeExpand">
<input id="use_google_tokenizer" type="checkbox" /><span data-i18n="Use Google Tokenizer">Use Google Tokenizer</span>
</label>
<div class="toggle-description justifyLeft">
<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>
</div>
</div>
<div class="range-block" data-source="makersuite"> <div class="range-block" data-source="makersuite">
<label for="use_makersuite_sysprompt" class="checkbox_label widthFreeExpand"> <label for="use_makersuite_sysprompt" class="checkbox_label widthFreeExpand">
<input id="use_makersuite_sysprompt" type="checkbox" /> <input id="use_makersuite_sysprompt" type="checkbox" />
@ -1852,7 +1845,7 @@
<div class="fa-solid fa-clock-rotate-left"></div> <div class="fa-solid fa-clock-rotate-left"></div>
</div> </div>
</div> </div>
<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> <textarea id="claude_human_sysprompt_textarea" class="text_pole textarea_compact" rows="4" data-i18n="[placeholder]Human message" placeholder="Human message, instruction, etc.&#10;Adds nothing when empty, i.e. requires a new prompt with the role 'user'."></textarea>
</div> </div>
</div> </div>
</div> </div>
@ -2791,10 +2784,12 @@
<div> <div>
<h4 data-i18n="AI21 Model">AI21 Model</h4> <h4 data-i18n="AI21 Model">AI21 Model</h4>
<select id="model_ai21_select"> <select id="model_ai21_select">
<optgroup label="Latest"> <optgroup label="Jamba 1.5">
<option value="j2-ultra">j2-ultra</option> <option value="jamba-1.5-mini">jamba-1.5-mini</option>
<option value="j2-mid">j2-mid</option> <option value="jamba-1.5-large">jamba-1.5-large</option>
<option value="j2-light">j2-light</option> </optgroup>
<optgroup label="Jamba-Instruct (Deprecated)">
<option value="jamba-instruct-preview">jamba-instruct-preview</option>
</optgroup> </optgroup>
</select> </select>
</div> </div>
@ -2824,8 +2819,11 @@
</optgroup> </optgroup>
<optgroup label="Subversions"> <optgroup label="Subversions">
<option value="gemini-1.5-pro-exp-0801">Gemini 1.5 Pro Experiment 2024-08-01</option> <option value="gemini-1.5-pro-exp-0801">Gemini 1.5 Pro Experiment 2024-08-01</option>
<option value="gemini-1.5-pro-exp-0827">Gemini 1.5 Pro Experiment 2024-08-27</option>
<option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option> <option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option>
<option value="gemini-1.5-pro-001">Gemini 1.5 Pro [001]</option> <option value="gemini-1.5-pro-001">Gemini 1.5 Pro [001]</option>
<option value="gemini-1.5-flash-exp-0827">Gemini 1.5 Flash Experiment 2024-08-27</option>
<option value="gemini-1.5-flash-8b-exp-0827">Gemini 1.5 Flash 8B Experiment 2024-08-27</option>
<option value="gemini-1.5-flash-latest">Gemini 1.5 Flash [latest]</option> <option value="gemini-1.5-flash-latest">Gemini 1.5 Flash [latest]</option>
<option value="gemini-1.5-flash-001">Gemini 1.5 Flash [001]</option> <option value="gemini-1.5-flash-001">Gemini 1.5 Flash [001]</option>
<option value="gemini-1.0-pro-latest">Gemini 1.0 Pro [latest]</option> <option value="gemini-1.0-pro-latest">Gemini 1.0 Pro [latest]</option>
@ -2955,6 +2953,8 @@
<option value="command">command</option> <option value="command">command</option>
<option value="command-r">command-r</option> <option value="command-r">command-r</option>
<option value="command-r-plus">command-r-plus</option> <option value="command-r-plus">command-r-plus</option>
<option value="command-r-08-2024">command-r-08-2024</option>
<option value="command-r-plus-08-2024">command-r-plus-08-2024</option>
</optgroup> </optgroup>
<optgroup label="Nightly"> <optgroup label="Nightly">
<option value="command-light-nightly">command-light-nightly</option> <option value="command-light-nightly">command-light-nightly</option>
@ -4012,7 +4012,7 @@
<input id="movingUImode" type="checkbox" /> <input id="movingUImode" type="checkbox" />
<small data-i18n="Movable UI Panels">MovingUI&nbsp;<i class="fa-solid fa-desktop"></i></small> <small data-i18n="Movable UI Panels">MovingUI&nbsp;<i class="fa-solid fa-desktop"></i></small>
</label> </label>
<div data-newbie-hidden id="movingUIreset" title="Reset MovingUI panel sizes/locations." class="menu_button margin0" data-i18n="[title]Reset MovingUI panel sizes/locations."><i class=" fa-solid fa-recycle margin-r5"></i> Reset</div> <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>
</div> </div>
<div data-newbie-hidden id="MovingUI-presets-block" class="flex-container alignitemscenter"> <div data-newbie-hidden id="MovingUI-presets-block" class="flex-container alignitemscenter">
<div class="flex-container alignItemsFlexEnd"> <div class="flex-container alignItemsFlexEnd">
@ -4503,6 +4503,7 @@
<div data-newbie-hidden> <div data-newbie-hidden>
<label for="persona_description_position" data-i18n="Position:">Position:</label> <label for="persona_description_position" data-i18n="Position:">Position:</label>
<select id="persona_description_position"> <select id="persona_description_position">
<option value="9" data-i18n="None (disabled)">None (disabled)</option>
<option value="0" data-i18n="In Story String / Prompt Manager">In Story String / Prompt Manager</option> <option value="0" data-i18n="In Story String / Prompt Manager">In Story String / Prompt Manager</option>
<option value="2" data-i18n="Top of Author's Note">Top of Author's Note</option> <option value="2" data-i18n="Top of Author's Note">Top of Author's Note</option>
<option value="3" data-i18n="Bottom of Author's Note">Bottom of Author's Note</option> <option value="3" data-i18n="Bottom of Author's Note">Bottom of Author's Note</option>
@ -6488,6 +6489,7 @@
<script src="lib/jquery-cookie-1.4.1.min.js"></script> <script src="lib/jquery-cookie-1.4.1.min.js"></script>
<script src="lib/jquery.ui.touch-punch.min.js"></script> <script src="lib/jquery.ui.touch-punch.min.js"></script>
<script src="lib/showdown.min.js"></script> <script src="lib/showdown.min.js"></script>
<script src="lib/showdown-patch.js"></script>
<script src="lib/showdown-katex.min.js"></script> <script src="lib/showdown-katex.min.js"></script>
<script src="lib/popper.js"></script> <script src="lib/popper.js"></script>
<script src="lib/purify.min.js"></script> <script src="lib/purify.min.js"></script>

View File

@ -0,0 +1,24 @@
showdown.subParser('unhashHTMLSpans', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('unhashHTMLSpans.before', text, options, globals);
for (var i = 0; i < globals.gHtmlSpans.length; ++i) {
var repText = globals.gHtmlSpans[i],
// limiter to prevent infinite loop (assume 10 as limit for recurse)
limit = 0;
while (/¨C(\d+)C/.test(repText)) {
var num = RegExp.$1;
repText = repText.replace('¨C' + num + 'C', globals.gHtmlSpans[num]);
if (limit === 10000) {
console.error('maximum nesting of 10000 spans reached!!!');
break;
}
++limit;
}
text = text.replace('¨C' + i + 'C', repText);
}
text = globals.converter._dispatch('unhashHTMLSpans.after', text, options, globals);
return text;
});

View File

@ -43,7 +43,7 @@
"Smoothing Factor": "Коэффициент сглаживания", "Smoothing Factor": "Коэффициент сглаживания",
"No Repeat Ngram Size": "Размер no_repeat_ngram", "No Repeat Ngram Size": "Размер no_repeat_ngram",
"Min Length": "Мин. длина", "Min Length": "Мин. длина",
"Alternative server URL (leave empty to use the default value).": "Альтернативный URL сервера (оставьте пустым для стандартного значения)", "Alternative server URL (leave empty to use the default value).": "URL альтернативного сервера (оставьте пустым для стандартного значения)",
"Remove your real OAI API Key from the API panel BEFORE typing anything into this box": "Удалите свой личный OAI API Key из панели API, и ТОЛЬКО ПОСЛЕ ЭТОГО вводите что-то сюда", "Remove your real OAI API Key from the API panel BEFORE typing anything into this box": "Удалите свой личный OAI API Key из панели API, и ТОЛЬКО ПОСЛЕ ЭТОГО вводите что-то сюда",
"We cannot provide support for problems encountered while using an unofficial OpenAI proxy": "Мы не сможем предоставить помощь с проблемами, с которыми вы столкнетесь при использовании неофициальных прокси для OpenAI", "We cannot provide support for problems encountered while using an unofficial OpenAI proxy": "Мы не сможем предоставить помощь с проблемами, с которыми вы столкнетесь при использовании неофициальных прокси для OpenAI",
"Context Size (tokens)": "Размер контекста (в токенах)", "Context Size (tokens)": "Размер контекста (в токенах)",
@ -186,7 +186,7 @@
"Generate only one line per request": "Генерировать одну строку на запрос", "Generate only one line per request": "Генерировать одну строку на запрос",
"Misc. Settings": "Доп. настройки", "Misc. Settings": "Доп. настройки",
"Auto-Continue": "Авто-продолжение", "Auto-Continue": "Авто-продолжение",
"Collapse Consecutive Newlines": "Сворачивать последовательные новые строки", "Collapse Consecutive Newlines": "Сворачивать неск. новых строк в одну",
"Allow for Chat Completion APIs": "Разрешить для Chat Completion API", "Allow for Chat Completion APIs": "Разрешить для Chat Completion API",
"Target length (tokens)": "Целевая длина (в токенах)", "Target length (tokens)": "Целевая длина (в токенах)",
"World Info": "Информация о мире", "World Info": "Информация о мире",
@ -373,7 +373,7 @@
"Prefer Character Card Prompt": "Приоритет промпту из карточки персонажа", "Prefer Character Card Prompt": "Приоритет промпту из карточки персонажа",
"Prefer Character Card Jailbreak": "Приоритет джейлбрейку из карточки персонажа", "Prefer Character Card Jailbreak": "Приоритет джейлбрейку из карточки персонажа",
"Press Send to continue": "Кнопка отправки продолжает сообщение", "Press Send to continue": "Кнопка отправки продолжает сообщение",
"Quick 'Continue' button": "Кнопка быстрого продолжения", "Quick 'Continue' button": "Быстрое продолжение",
"Log prompts to console": "Выводить промпты в консоль", "Log prompts to console": "Выводить промпты в консоль",
"Never resize avatars": "Не менять размер аватарок", "Never resize avatars": "Не менять размер аватарок",
"Show avatar filenames": "Показывать названия файлов аватарок", "Show avatar filenames": "Показывать названия файлов аватарок",
@ -495,7 +495,7 @@
"(Write a comma-separated list of tags)": "(Список тегов через запятую)", "(Write a comma-separated list of tags)": "(Список тегов через запятую)",
"(A brief description of the personality)": "(Краткое описание личности)", "(A brief description of the personality)": "(Краткое описание личности)",
"(Circumstances and context of the interaction)": "(Обстоятельства и контекст этого диалога)", "(Circumstances and context of the interaction)": "(Обстоятельства и контекст этого диалога)",
"(Examples of chat dialog. Begin each example with START on a new line.)": "(Примеры диалога. Начинайте каждый пример с START или новой линией.)", "(Examples of chat dialog. Begin each example with START on a new line.)": "(Примеры диалога. Начинайте каждый пример с START или новой строкой.)",
"Type here...": "Пишите здесь...", "Type here...": "Пишите здесь...",
"Comma separated (required)": "Через запятую (обязательное поле)", "Comma separated (required)": "Через запятую (обязательное поле)",
"What this keyword should mean to the AI, sent verbatim": "Что это ключевое слово должно означать для ИИ, отправляется дословно", "What this keyword should mean to the AI, sent verbatim": "Что это ключевое слово должно означать для ИИ, отправляется дословно",
@ -552,7 +552,7 @@
"Click to set a new User Name": "Нажмите, чтобы задать новое имя пользователя.", "Click to set a new User Name": "Нажмите, чтобы задать новое имя пользователя.",
"Click to lock your selected persona to the current chat. Click again to remove the lock.": "Нажмите, чтобы закрепить выбранную персону для текущего чата. Нажмите еще раз, чтобы открепить.", "Click to lock your selected persona to the current chat. Click again to remove the lock.": "Нажмите, чтобы закрепить выбранную персону для текущего чата. Нажмите еще раз, чтобы открепить.",
"Click to set user name for all messages": "Нажмите, чтобы задать имя пользователя для всех сообщений.", "Click to set user name for all messages": "Нажмите, чтобы задать имя пользователя для всех сообщений.",
"Create a dummy persona": "Создать персону-болванку", "Create a dummy persona": "Создать пустую персону",
"Character Management": "Управление персонажами", "Character Management": "Управление персонажами",
"Locked = Character Management panel will stay open": "Закреплено = Панель управление персонажами останется открытой", "Locked = Character Management panel will stay open": "Закреплено = Панель управление персонажами останется открытой",
"Select/Create Characters": "Выбрать/Создать персонажа", "Select/Create Characters": "Выбрать/Создать персонажа",
@ -625,7 +625,7 @@
"Automatically select a background based on the chat context": "Автоматический выбор фона в зависимости от контекста чата", "Automatically select a background based on the chat context": "Автоматический выбор фона в зависимости от контекста чата",
"Filter": "Фильтр", "Filter": "Фильтр",
"Exclude message from prompts": "Исключить сообщение из промпта", "Exclude message from prompts": "Исключить сообщение из промпта",
"Include message in prompts": "Включить сообщение в подсказки", "Include message in prompts": "Включить сообщение в промпт",
"Create checkpoint": "Создать чекпоинт", "Create checkpoint": "Создать чекпоинт",
"Create Branch": "Создать ветку", "Create Branch": "Создать ветку",
"Embed file or image": "Вставить файл или изображение", "Embed file or image": "Вставить файл или изображение",
@ -1661,5 +1661,52 @@
"ext_regex_ai_output_desc": "Сообщения, полученные от API", "ext_regex_ai_output_desc": "Сообщения, полученные от API",
"ext_regex_sts_desc": "Сообщения, отправленные с помощью команд STscript", "ext_regex_sts_desc": "Сообщения, отправленные с помощью команд STscript",
"ext_regex_wi_desc": "Содержимое лорбуков и миров. Для работы требует включения флажка \"Только промпт\"!", "ext_regex_wi_desc": "Содержимое лорбуков и миров. Для работы требует включения флажка \"Только промпт\"!",
"ext_regex_only_format_display_desc": "История чата не изменится, замена будет осуществляться только в отображаемом сообщении (в UI)" "ext_regex_only_format_display_desc": "История чата не изменится, замена будет осуществляться только в отображаемом сообщении (в UI)",
"Prefer Character Card Instructions": "Приоритет инструкциям из карточек",
"If checked and the character card contains a Post-History Instructions override, use that instead": "Если в карточке персонажа имеется собственная инструкция после истории, в промпт попадёт именно она",
"Auto-select Input Text": "Автовыделение вводимого текста",
"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.": "Автоматически выделять вводимый текст в некоторых текстовых полях при клике/выборе. Работает для вспл. окон и различных кастомных полей ввода.",
"Reset MovingUI panel sizes/locations.": "Сбросить расположение и размеры панелей MovingUI.",
"mui_reset": "Сброс",
"Quick 'Impersonate' button": "Быстрое перевоплощение",
"Show a button in the input area to ask the AI to impersonate your character for a single message": "Показать в поле ввода кнопку, по нажатии на которую ИИ сгенерирует одно сообщение от лица вашего персонажа.",
"Separators as Stop Strings": "Разделители как стоп-строки",
"Names as Stop Strings": "Имена как стоп-строки",
"Add Character and User names to a list of stopping strings.": "Добавлять имена персонажа и пользователя в список стоп-строк.",
"Allow Post-History Instructions": "Разрешить инструкции после истории",
"context_allow_post_history_instructions": "Добавлять в конец промпта инструкции после истории. Работает только при наличии таких инструкций в карточке И при включенной опции ''Приоритет инструкциям из карточек''.\nНЕ РЕКОМЕНДУЕТСЯ ДЛЯ МОДЕЛЕЙ TEXT COMPLETION, МОЖЕТ ПОРТИТЬ ВЫХОДНОЙ ТЕКСТ.",
"First User Prefix": "Первый префикс пользователя",
"Inserted before the first User's message.": "Вставляется перед первым сообщением пользователя.",
"Last User Prefix": "Последний префикс пользователя",
"instruct_last_input_sequence": "Вставляется перед последним сообщением пользователя.",
"Inserted before a User message and as a last prompt line when impersonating.": "Вставляется перед сообщением пользователя в качестве последней строки промпта при перевоплощении.",
"Inserted before a System (added by slash commands or extensions) message.": "Вставляется перед сообщением системы (может быть добавлено слэш-командой или расширением).",
"Load Asset List": "Загрузить список ресурсов",
"Never add character names.": "Не вставлять имя персонажа.",
"Don't add character names unless necessary.": "Вставлять имя персонажа только когда это необходимо.",
"character_names_none": "Не добавлять имена персонажей в качестве префикса. Может повредить качеству ответов в групповых чатах, используйте с осторожностью.",
"Auxiliary": "Вспомогательный",
"Post-History Instructions": "Инструкции после истории",
"Current persona updated": "Текущая персона изменена",
"Your messages will now be sent as ${0}": "Ваши сообщения будут отправляться от лица ${0}",
"Copied!": "Скопировано!",
"Are you sure you want to delete this message?": "Вы точно хотите удалить это сообщение?",
"Delete Message": "Удалить сообщение",
"Delete Swipe": "Удалить свайп",
"Could not get a reply from API. Check your connection settings / API key and try again.": "Не удалось получить ответ от API. Проверьте настройки соединения и API-ключ и повторите попытку.",
"Connecting To Proxy": "Подключение к прокси",
"Are you sure you want to connect to the following proxy URL?": "Вы точно хотите соединиться с прокси по этому адресу?",
"API connection successful!": "Соединение с API установлено!",
"Proxy Saved": "Прокси сохранена",
"Proxy Deleted": "Прокси удалена",
"Could not find proxy with name '${0}'": "Не удалось найти прокси с названием '${0}'",
"Proxy preset '${0}' not found in proxies array.": "Пресет с названием '${0}' не найден в списке прокси.",
"Please wait for the previous request to complete.": "Пожалуйста, дождитесь окончания обработки предыдущего запроса.",
"Start new chat?": "Начать новый чат?",
"If necessary, you can later restore this chat file from the /backups folder": "При необходимости этот чат можно будет восстановить из папки /backups",
"Also delete the current chat file": "Также удалить текущий чат",
"chat_rename_1": "Введите новое имя чата:",
"chat_rename_2": "!!Не используйте имя уже существующего файла, это приведёт к ошибке!!",
"chat_rename_3": "Будут разрушены связи между чатами-чекпоинтами.",
"chat_rename_4": "Расширение '.jsonl' дописывать не нужно."
} }

View File

@ -488,14 +488,6 @@ let default_user_name = 'User';
export let name1 = default_user_name; export let name1 = default_user_name;
export let name2 = 'SillyTavern System'; export let name2 = 'SillyTavern System';
export let chat = []; export let chat = [];
let safetychat = [
{
name: systemUserName,
is_user: false,
create_date: 0,
mes: 'You deleted a character/chat and arrived back here for safety reasons! Pick another character!',
},
];
let chatSaveTimeout; let chatSaveTimeout;
let importFlashTimeout; let importFlashTimeout;
export let isChatSaving = false; export let isChatSaving = false;
@ -594,6 +586,17 @@ export const extension_prompt_roles = {
export const MAX_INJECTION_DEPTH = 1000; export const MAX_INJECTION_DEPTH = 1000;
const SAFETY_CHAT = [
{
name: systemUserName,
force_avatar: system_avatar,
is_system: true,
is_user: false,
create_date: 0,
mes: 'You deleted a character/chat and arrived back here for safety reasons! Pick another character!',
},
];
export let system_messages = {}; export let system_messages = {};
async function getSystemMessages() { async function getSystemMessages() {
@ -2192,7 +2195,7 @@ export function addCopyToCodeBlocks(messageElement) {
codeBlocks.get(i).appendChild(copyButton); codeBlocks.get(i).appendChild(copyButton);
copyButton.addEventListener('pointerup', function (event) { copyButton.addEventListener('pointerup', function (event) {
navigator.clipboard.writeText(codeBlocks.get(i).innerText); navigator.clipboard.writeText(codeBlocks.get(i).innerText);
toastr.info('Copied!', '', { timeOut: 2000 }); toastr.info(t`Copied!`, '', { timeOut: 2000 });
}); });
} }
} }
@ -2705,7 +2708,7 @@ function addPersonaDescriptionExtensionPrompt() {
const INJECT_TAG = 'PERSONA_DESCRIPTION'; const INJECT_TAG = 'PERSONA_DESCRIPTION';
setExtensionPrompt(INJECT_TAG, '', extension_prompt_types.IN_PROMPT, 0); setExtensionPrompt(INJECT_TAG, '', extension_prompt_types.IN_PROMPT, 0);
if (!power_user.persona_description) { if (!power_user.persona_description || power_user.persona_description_position === persona_description_positions.NONE) {
return; return;
} }
@ -2826,7 +2829,7 @@ export function getCharacterCardFields() {
} }
export function isStreamingEnabled() { export function isStreamingEnabled() {
const noStreamSources = [chat_completion_sources.SCALE, chat_completion_sources.AI21]; const noStreamSources = [chat_completion_sources.SCALE];
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'))) 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')))
|| (main_api == 'kobold' && kai_settings.streaming_kobold && kai_flags.can_use_streaming) || (main_api == 'kobold' && kai_settings.streaming_kobold && kai_flags.can_use_streaming)
|| (main_api == 'novel' && nai_settings.streaming_novel) || (main_api == 'novel' && nai_settings.streaming_novel)
@ -3726,7 +3729,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
const storyStringParams = { const storyStringParams = {
description: description, description: description,
personality: personality, personality: personality,
persona: persona, persona: power_user.persona_description_position == persona_description_positions.IN_PROMPT ? persona : '',
scenario: scenario, scenario: scenario,
system: isInstruct ? system : '', system: isInstruct ? system : '',
char: name2, char: name2,
@ -3760,7 +3763,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
} }
let examplesString = ''; let examplesString = '';
let chatString = ''; let chatString = addChatsPreamble(addChatsSeparator(''));
let cyclePrompt = ''; let cyclePrompt = '';
async function getMessagesTokenCount() { async function getMessagesTokenCount() {
@ -3769,10 +3772,10 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
storyString, storyString,
afterScenarioAnchor, afterScenarioAnchor,
examplesString, examplesString,
chatString,
quiet_prompt,
cyclePrompt,
userAlignmentMessage, userAlignmentMessage,
chatString,
modifyLastPromptLine(''),
cyclePrompt,
].join('').replace(/\r/gm, ''); ].join('').replace(/\r/gm, '');
return getTokenCountAsync(encodeString, power_user.token_padding); return getTokenCountAsync(encodeString, power_user.token_padding);
} }
@ -3803,8 +3806,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
} }
tokenCount += await getTokenCountAsync(item.replace(/\r/gm, '')); tokenCount += await getTokenCountAsync(item.replace(/\r/gm, ''));
chatString = item + chatString;
if (tokenCount < this_max_context) { if (tokenCount < this_max_context) {
chatString = chatString + item;
arrMes[index] = item; arrMes[index] = item;
lastAddedIndex = Math.max(lastAddedIndex, index); lastAddedIndex = Math.max(lastAddedIndex, index);
} else { } else {
@ -3830,8 +3833,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
} }
tokenCount += await getTokenCountAsync(item.replace(/\r/gm, '')); tokenCount += await getTokenCountAsync(item.replace(/\r/gm, ''));
chatString = item + chatString;
if (tokenCount < this_max_context) { if (tokenCount < this_max_context) {
chatString = chatString + item;
arrMes[i] = item; arrMes[i] = item;
lastAddedIndex = Math.max(lastAddedIndex, i); lastAddedIndex = Math.max(lastAddedIndex, i);
} else { } else {
@ -4028,15 +4031,16 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
async function checkPromptSize() { async function checkPromptSize() {
console.debug('---checking Prompt size'); console.debug('---checking Prompt size');
setPromptString(); setPromptString();
const jointMessages = mesSend.map((e) => `${e.extensionPrompts.join('')}${e.message}`).join('');
const prompt = [ const prompt = [
beforeScenarioAnchor, beforeScenarioAnchor,
storyString, storyString,
afterScenarioAnchor, afterScenarioAnchor,
mesExmString, mesExmString,
mesSend.map((e) => `${e.extensionPrompts.join('')}${e.message}`).join(''), addChatsPreamble(addChatsSeparator(jointMessages)),
'\n', '\n',
modifyLastPromptLine(''),
generatedPromptCache, generatedPromptCache,
quiet_prompt,
].join('').replace(/\r/gm, ''); ].join('').replace(/\r/gm, '');
let thisPromptContextSize = await getTokenCountAsync(prompt, power_user.token_padding); let thisPromptContextSize = await getTokenCountAsync(prompt, power_user.token_padding);
@ -4302,7 +4306,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
padding: power_user.token_padding, padding: power_user.token_padding,
main_api: main_api, main_api: main_api,
instruction: isInstruct ? substituteParams(power_user.prefer_character_prompt && system ? system : power_user.instruct.system_prompt) : '', instruction: isInstruct ? substituteParams(power_user.prefer_character_prompt && system ? system : power_user.instruct.system_prompt) : '',
userPersona: (power_user.persona_description || ''), userPersona: (power_user.persona_description_position == persona_description_positions.IN_PROMPT ? (persona || '') : ''),
}; };
//console.log(additionalPromptStuff); //console.log(additionalPromptStuff);
@ -5080,7 +5084,7 @@ async function promptItemize(itemizedPrompts, requestedMesId) {
} }
navigator.clipboard.writeText(rawPromptValues); navigator.clipboard.writeText(rawPromptValues);
toastr.info('Copied!'); toastr.info(t`Copied!`);
}); });
popup.dlg.querySelector('#showRawPrompt').addEventListener('click', function () { popup.dlg.querySelector('#showRawPrompt').addEventListener('click', function () {
@ -5679,7 +5683,7 @@ export function resetChatState() {
// replaces deleted charcter name with system user since it will be displayed next. // replaces deleted charcter name with system user since it will be displayed next.
name2 = systemUserName; name2 = systemUserName;
// sets up system user to tell user about having deleted a character // sets up system user to tell user about having deleted a character
chat = [...safetychat]; chat.splice(0, chat.length, ...SAFETY_CHAT);
// resets chat metadata // resets chat metadata
chat_metadata = {}; chat_metadata = {};
// resets the characters array, forcing getcharacters to reset // resets the characters array, forcing getcharacters to reset
@ -6310,7 +6314,7 @@ export function setUserName(value) {
console.log(`User name changed to ${name1}`); console.log(`User name changed to ${name1}`);
$('#your_name').val(name1); $('#your_name').val(name1);
if (power_user.persona_show_notifications) { if (power_user.persona_show_notifications) {
toastr.success(`Your messages will now be sent as ${name1}`, 'Current persona updated'); toastr.success(t`Your messages will now be sent as ${name1}`, t`Current persona updated`);
} }
saveSettingsDebounced(); saveSettingsDebounced();
} }
@ -8840,72 +8844,74 @@ export async function handleDeleteCharacter(this_chid, delete_chats) {
/** /**
* Deletes a character completely, including associated chats if specified * Deletes a character completely, including associated chats if specified
* *
* @param {string} characterKey - The key (avatar) of the character to be deleted * @param {string|string[]} characterKey - The key (avatar) of the character to be deleted
* @param {Object} [options] - Optional parameters for the deletion * @param {Object} [options] - Optional parameters for the deletion
* @param {boolean} [options.deleteChats=true] - Whether to delete associated chats or not * @param {boolean} [options.deleteChats=true] - Whether to delete associated chats or not
* @return {Promise<void>} - A promise that resolves when the character is successfully deleted * @return {Promise<void>} - A promise that resolves when the character is successfully deleted
*/ */
export async function deleteCharacter(characterKey, { deleteChats = true } = {}) { export async function deleteCharacter(characterKey, { deleteChats = true } = {}) {
const character = characters.find(x => x.avatar == characterKey); if (!Array.isArray(characterKey)) {
if (!character) { characterKey = [characterKey];
toastr.warning(`Character ${characterKey} not found. Cannot be deleted.`);
return;
} }
const chid = characters.indexOf(character); for (const key of characterKey) {
const pastChats = await getPastCharacterChats(chid); const character = characters.find(x => x.avatar == key);
if (!character) {
const msg = { avatar_url: character.avatar, delete_chats: deleteChats }; toastr.warning(`Character ${key} not found. Skipping deletion.`);
continue;
const response = await fetch('/api/characters/delete', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify(msg),
cache: 'no-cache',
});
if (!response.ok) {
throw new Error(`Failed to delete character: ${response.status} ${response.statusText}`);
}
await removeCharacterFromUI(character.name, character.avatar);
if (deleteChats) {
for (const chat of pastChats) {
const name = chat.file_name.replace('.jsonl', '');
await eventSource.emit(event_types.CHAT_DELETED, name);
} }
const chid = characters.indexOf(character);
const pastChats = await getPastCharacterChats(chid);
const msg = { avatar_url: character.avatar, delete_chats: deleteChats };
const response = await fetch('/api/characters/delete', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify(msg),
cache: 'no-cache',
});
if (!response.ok) {
toastr.error(`${response.status} ${response.statusText}`, 'Failed to delete character');
continue;
}
delete tag_map[character.avatar];
select_rm_info('char_delete', character.name);
if (deleteChats) {
for (const chat of pastChats) {
const name = chat.file_name.replace('.jsonl', '');
await eventSource.emit(event_types.CHAT_DELETED, name);
}
}
await eventSource.emit(event_types.CHARACTER_DELETED, { id: chid, character: character });
} }
eventSource.emit(event_types.CHARACTER_DELETED, { id: this_chid, character: characters[this_chid] }); await removeCharacterFromUI();
} }
/** /**
* Function to delete a character from UI after character deletion API success. * Function to delete a character from UI after character deletion API success.
* It manages necessary UI changes such as closing advanced editing popup, unsetting * It manages necessary UI changes such as closing advanced editing popup, unsetting
* character ID, resetting characters array and chat metadata, deselecting character's tab * character ID, resetting characters array and chat metadata, deselecting character's tab
* panel, removing character name from navigation tabs, clearing chat, removing character's * panel, removing character name from navigation tabs, clearing chat, fetching updated list of characters.
* avatar from tag_map, fetching updated list of characters and updating the 'deleted
* character' message.
* It also ensures to save the settings after all the operations. * It also ensures to save the settings after all the operations.
*
* @param {string} name - The name of the character to be deleted.
* @param {string} avatar - The avatar URL of the character to be deleted.
* @param {boolean} reloadCharacters - Whether the character list should be refreshed after deletion.
*/ */
async function removeCharacterFromUI(name, avatar, reloadCharacters = true) { async function removeCharacterFromUI() {
await clearChat(); await clearChat();
$('#character_cross').click(); $('#character_cross').click();
this_chid = undefined; this_chid = undefined;
characters.length = 0; characters.length = 0;
name2 = systemUserName; name2 = systemUserName;
chat = [...safetychat]; chat.splice(0, chat.length, ...SAFETY_CHAT);
chat_metadata = {}; chat_metadata = {};
$(document.getElementById('rm_button_selected_ch')).children('h2').text(''); $(document.getElementById('rm_button_selected_ch')).children('h2').text('');
this_chid = undefined; this_chid = undefined;
delete tag_map[avatar]; await getCharacters();
if (reloadCharacters) await getCharacters();
select_rm_info('char_delete', name);
await printMessages(); await printMessages();
saveSettingsDebounced(); saveSettingsDebounced();
} }
@ -9532,11 +9538,7 @@ jQuery(async function () {
const oldFileNameFull = $(this).closest('.select_chat_block_wrapper').find('.select_chat_block_filename').text(); const oldFileNameFull = $(this).closest('.select_chat_block_wrapper').find('.select_chat_block_filename').text();
const oldFileName = oldFileNameFull.replace('.jsonl', ''); const oldFileName = oldFileNameFull.replace('.jsonl', '');
const popupText = `<h3>Enter the new name for the chat:<h3> const popupText = await renderTemplateAsync('chatRename');
<small>!!Using an existing filename will produce an error!!<br>
This will break the link between checkpoint chats.<br>
No need to add '.jsonl' at the end.<br>
</small>`;
const newName = await callPopup(popupText, 'input', oldFileName); const newName = await callPopup(popupText, 'input', oldFileName);
if (!newName || newName == oldFileName) { if (!newName || newName == oldFileName) {
@ -9744,12 +9746,7 @@ jQuery(async function () {
else if (id == 'option_start_new_chat') { else if (id == 'option_start_new_chat') {
if ((selected_group || this_chid !== undefined) && !is_send_press) { if ((selected_group || this_chid !== undefined) && !is_send_press) {
let deleteCurrentChat = false; let deleteCurrentChat = false;
const result = await Popup.show.confirm('Start new chat?', ` const result = await Popup.show.confirm(t`Start new chat?`, await renderTemplateAsync('newChatConfirm'), {
<label for="del_chat_checkbox" class="checkbox_label justifyCenter"
title="If necessary, you can later restore this chat file from the /backups folder">
<input type="checkbox" id="del_chat_checkbox" />
<small>Also delete the current chat file</small>
</label>`, {
onClose: () => deleteCurrentChat = !!$('#del_chat_checkbox').prop('checked'), onClose: () => deleteCurrentChat = !!$('#del_chat_checkbox').prop('checked'),
}); });
if (!result) { if (!result) {
@ -10261,10 +10258,10 @@ jQuery(async function () {
let deleteOnlySwipe = false; let deleteOnlySwipe = false;
if (power_user.confirm_message_delete && fromSlashCommand !== true) { if (power_user.confirm_message_delete && fromSlashCommand !== true) {
const result = await callGenericPopup('Are you sure you want to delete this message?', POPUP_TYPE.CONFIRM, null, { const result = await callGenericPopup(t`Are you sure you want to delete this message?`, POPUP_TYPE.CONFIRM, null, {
okButton: canDeleteSwipe ? 'Delete Swipe' : 'Delete Message', okButton: canDeleteSwipe ? t`Delete Swipe` : t`Delete Message`,
cancelButton: 'Cancel', cancelButton: 'Cancel',
customButtons: canDeleteSwipe ? ['Delete Message'] : null, customButtons: canDeleteSwipe ? [t`Delete Message`] : null,
}); });
if (!result) { if (!result) {
return; return;

View File

@ -108,14 +108,12 @@ class CharacterContextMenu {
* Delete one or more characters, * Delete one or more characters,
* opens a popup. * opens a popup.
* *
* @param {number} characterId * @param {string|string[]} characterKey
* @param {boolean} [deleteChats] * @param {boolean} [deleteChats]
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
static delete = async (characterId, deleteChats = false) => { static delete = async (characterKey, deleteChats = false) => {
const character = CharacterContextMenu.#getCharacter(characterId); await deleteCharacter(characterKey, { deleteChats: deleteChats });
await deleteCharacter(character.avatar, { deleteChats: deleteChats });
}; };
static #getCharacter = (characterId) => characters[characterId] ?? null; static #getCharacter = (characterId) => characters[characterId] ?? null;
@ -344,7 +342,7 @@ class BulkTagPopupHandler {
const mutualTags = this.getMutualTags(); const mutualTags = this.getMutualTags();
for (const characterId of this.characterIds) { for (const characterId of this.characterIds) {
for(const tag of mutualTags) { for (const tag of mutualTags) {
removeTagFromMap(tag.id, characterId); removeTagFromMap(tag.id, characterId);
} }
} }
@ -599,8 +597,7 @@ class BulkEditOverlay {
this.container.removeEventListener('mouseup', cancelHold); this.container.removeEventListener('mouseup', cancelHold);
this.container.removeEventListener('touchend', cancelHold); this.container.removeEventListener('touchend', cancelHold);
}, }, BulkEditOverlay.longPressDelay);
BulkEditOverlay.longPressDelay);
}; };
handleLongPressEnd = (event) => { handleLongPressEnd = (event) => {
@ -847,11 +844,14 @@ class BulkEditOverlay {
const deleteChats = document.getElementById('del_char_checkbox').checked ?? false; const deleteChats = document.getElementById('del_char_checkbox').checked ?? false;
showLoader(); showLoader();
toastr.info('We\'re deleting your characters, please wait...', 'Working on it'); const toast = toastr.info('We\'re deleting your characters, please wait...', 'Working on it');
return Promise.allSettled(characterIds.map(async characterId => CharacterContextMenu.delete(characterId, deleteChats))) const avatarList = characterIds.map(id => characters[id]?.avatar).filter(a => a);
.then(() => getCharacters()) return CharacterContextMenu.delete(avatarList, deleteChats)
.then(() => this.browseState()) .then(() => this.browseState())
.finally(() => hideLoader()); .finally(() => {
toastr.clear(toast);
hideLoader();
});
}); });
// At this moment the popup is already changed in the dom, but not yet closed/resolved. We build the avatar list here // At this moment the popup is already changed in the dom, but not yet closed/resolved. We build the avatar list here

View File

@ -45,8 +45,11 @@
<option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option> <option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>
<option data-type="google" value="gemini-pro-vision">gemini-pro-vision</option> <option data-type="google" value="gemini-pro-vision">gemini-pro-vision</option>
<option data-type="google" value="gemini-1.5-flash-latest">gemini-1.5-flash-latest</option> <option data-type="google" value="gemini-1.5-flash-latest">gemini-1.5-flash-latest</option>
<option data-type="google" value="gemini-1.5-flash-exp-0827">gemini-1.5-flash-exp-0827</option>
<option data-type="google" value="gemini-1.5-flash-8b-exp-0827">gemini-1.5-flash-8b-exp-0827</option>
<option data-type="google" value="gemini-1.5-pro-latest">gemini-1.5-pro-latest</option> <option data-type="google" value="gemini-1.5-pro-latest">gemini-1.5-pro-latest</option>
<option data-type="google" value="gemini-1.5-pro-exp-0801">gemini-1.5-pro-exp-0801</option> <option data-type="google" value="gemini-1.5-pro-exp-0801">gemini-1.5-pro-exp-0801</option>
<option data-type="google" value="gemini-1.5-pro-exp-0827">gemini-1.5-pro-exp-0827</option>
<option data-type="openrouter" value="openai/gpt-4-vision-preview">openai/gpt-4-vision-preview</option> <option data-type="openrouter" value="openai/gpt-4-vision-preview">openai/gpt-4-vision-preview</option>
<option data-type="openrouter" value="openai/gpt-4o">openai/gpt-4o</option> <option data-type="openrouter" value="openai/gpt-4o">openai/gpt-4o</option>
<option data-type="openrouter" value="openai/gpt-4-turbo">openai/gpt-4-turbo</option> <option data-type="openrouter" value="openai/gpt-4-turbo">openai/gpt-4-turbo</option>

View File

@ -421,7 +421,7 @@ function completeTtsJob() {
async function tts(text, voiceId, char) { async function tts(text, voiceId, char) {
async function processResponse(response) { async function processResponse(response) {
// RVC injection // RVC injection
if (extension_settings.rvc.enabled && typeof window['rvcVoiceConversion'] === 'function') if (typeof window['rvcVoiceConversion'] === 'function' && extension_settings.rvc.enabled)
response = await window['rvcVoiceConversion'](response, char, text); response = await window['rvcVoiceConversion'](response, char, text);
await addAudioJob(response, char); await addAudioJob(response, char);

View File

@ -78,6 +78,19 @@ export function getKayraMaxContextTokens() {
return null; return null;
} }
export function getKayraMaxResponseTokens() {
switch (novel_data?.tier) {
case 1:
return 100;
case 2:
return 100;
case 3:
return 150;
}
return maximum_output_length;
}
export function getNovelTier() { export function getNovelTier() {
return nai_tiers[novel_data?.tier] ?? 'no_connection'; return nai_tiers[novel_data?.tier] ?? 'no_connection';
} }
@ -438,12 +451,14 @@ export function getNovelGenerationData(finalPrompt, settings, maxLength, isImper
console.log(finalPrompt); console.log(finalPrompt);
} }
const adjustedMaxLength = nai_settings.model_novel.includes('kayra') ? getKayraMaxResponseTokens() : maximum_output_length;
return { return {
'input': finalPrompt, 'input': finalPrompt,
'model': nai_settings.model_novel, 'model': nai_settings.model_novel,
'use_string': true, 'use_string': true,
'temperature': Number(nai_settings.temperature), 'temperature': Number(nai_settings.temperature),
'max_length': maxLength < maximum_output_length ? maxLength : maximum_output_length, 'max_length': maxLength < adjustedMaxLength ? maxLength : adjustedMaxLength,
'min_length': Number(nai_settings.min_length), 'min_length': Number(nai_settings.min_length),
'tail_free_sampling': Number(nai_settings.tail_free_sampling), 'tail_free_sampling': Number(nai_settings.tail_free_sampling),
'repetition_penalty': Number(nai_settings.repetition_penalty), 'repetition_penalty': Number(nai_settings.repetition_penalty),

View File

@ -74,6 +74,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashComma
import { renderTemplateAsync } from './templates.js'; import { renderTemplateAsync } from './templates.js';
import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js'; import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
import { Popup, POPUP_RESULT } from './popup.js'; import { Popup, POPUP_RESULT } from './popup.js';
import { t } from './i18n.js';
export { export {
openai_messages_count, openai_messages_count,
@ -132,13 +133,9 @@ const max_2mil = 2000 * 1000;
const scale_max = 8191; const scale_max = 8191;
const claude_max = 9000; // We have a proper tokenizer, so theoretically could be larger (up to 9k) const claude_max = 9000; // We have a proper tokenizer, so theoretically could be larger (up to 9k)
const claude_100k_max = 99000; const claude_100k_max = 99000;
let ai21_max = 9200; //can easily fit 9k gpt tokens because j2's tokenizer is efficient af const unlocked_max = max_2mil;
const unlocked_max = max_200k;
const oai_max_temp = 2.0; const oai_max_temp = 2.0;
const claude_max_temp = 1.0; //same as j2 const claude_max_temp = 1.0;
const j2_max_topk = 10.0;
const j2_max_freq = 5.0;
const j2_max_pres = 5.0;
const openrouter_website_model = 'OR_Website'; const openrouter_website_model = 'OR_Website';
const openai_max_stop_strings = 4; const openai_max_stop_strings = 4;
@ -218,25 +215,11 @@ const sensitiveFields = [
'custom_include_headers', 'custom_include_headers',
]; ];
function getPrefixMap() {
return selected_group ? {
assistant: '',
user: '',
system: 'OOC: ',
}
: {
assistant: '{{char}}:',
user: '{{user}}:',
system: '',
};
}
const default_settings = { const default_settings = {
preset_settings_openai: 'Default', preset_settings_openai: 'Default',
temp_openai: 1.0, temp_openai: 1.0,
freq_pen_openai: 0, freq_pen_openai: 0,
pres_pen_openai: 0, pres_pen_openai: 0,
count_pen: 0.0,
top_p_openai: 1.0, top_p_openai: 1.0,
top_k_openai: 0, top_k_openai: 0,
min_p_openai: 0, min_p_openai: 0,
@ -264,7 +247,7 @@ const default_settings = {
openai_model: 'gpt-4-turbo', openai_model: 'gpt-4-turbo',
claude_model: 'claude-3-5-sonnet-20240620', claude_model: 'claude-3-5-sonnet-20240620',
google_model: 'gemini-1.5-pro', google_model: 'gemini-1.5-pro',
ai21_model: 'j2-ultra', ai21_model: 'jamba-1.5-large',
mistralai_model: 'mistral-large-latest', mistralai_model: 'mistral-large-latest',
cohere_model: 'command-r-plus', cohere_model: 'command-r-plus',
perplexity_model: 'llama-3.1-70b-instruct', perplexity_model: 'llama-3.1-70b-instruct',
@ -294,8 +277,6 @@ const default_settings = {
assistant_prefill: '', assistant_prefill: '',
assistant_impersonation: '', assistant_impersonation: '',
human_sysprompt_message: default_claude_human_sysprompt_message, human_sysprompt_message: default_claude_human_sysprompt_message,
use_ai21_tokenizer: false,
use_google_tokenizer: false,
claude_use_sysprompt: false, claude_use_sysprompt: false,
use_makersuite_sysprompt: true, use_makersuite_sysprompt: true,
use_alt_scale: false, use_alt_scale: false,
@ -317,7 +298,6 @@ const oai_settings = {
temp_openai: 1.0, temp_openai: 1.0,
freq_pen_openai: 0, freq_pen_openai: 0,
pres_pen_openai: 0, pres_pen_openai: 0,
count_pen: 0.0,
top_p_openai: 1.0, top_p_openai: 1.0,
top_k_openai: 0, top_k_openai: 0,
min_p_openai: 0, min_p_openai: 0,
@ -345,7 +325,7 @@ const oai_settings = {
openai_model: 'gpt-4-turbo', openai_model: 'gpt-4-turbo',
claude_model: 'claude-3-5-sonnet-20240620', claude_model: 'claude-3-5-sonnet-20240620',
google_model: 'gemini-1.5-pro', google_model: 'gemini-1.5-pro',
ai21_model: 'j2-ultra', ai21_model: 'jamba-1.5-large',
mistralai_model: 'mistral-large-latest', mistralai_model: 'mistral-large-latest',
cohere_model: 'command-r-plus', cohere_model: 'command-r-plus',
perplexity_model: 'llama-3.1-70b-instruct', perplexity_model: 'llama-3.1-70b-instruct',
@ -375,8 +355,6 @@ const oai_settings = {
assistant_prefill: '', assistant_prefill: '',
assistant_impersonation: '', assistant_impersonation: '',
human_sysprompt_message: default_claude_human_sysprompt_message, human_sysprompt_message: default_claude_human_sysprompt_message,
use_ai21_tokenizer: false,
use_google_tokenizer: false,
claude_use_sysprompt: false, claude_use_sysprompt: false,
use_makersuite_sysprompt: true, use_makersuite_sysprompt: true,
use_alt_scale: false, use_alt_scale: false,
@ -425,7 +403,7 @@ async function validateReverseProxy() {
const rememberKey = `Proxy_SkipConfirm_${getStringHash(oai_settings.reverse_proxy)}`; const rememberKey = `Proxy_SkipConfirm_${getStringHash(oai_settings.reverse_proxy)}`;
const skipConfirm = localStorage.getItem(rememberKey) === 'true'; const skipConfirm = localStorage.getItem(rememberKey) === 'true';
const confirmation = skipConfirm || await Popup.show.confirm('Connecting To Proxy', `<span>Are you sure you want to connect to the following proxy URL?</span><var>${DOMPurify.sanitize(oai_settings.reverse_proxy)}</var>`); const confirmation = skipConfirm || await Popup.show.confirm(t`Connecting To Proxy`, await renderTemplateAsync('proxyConnectionWarning', { proxyURL: DOMPurify.sanitize(oai_settings.reverse_proxy) }));
if (!confirmation) { if (!confirmation) {
toastr.error('Update or remove your reverse proxy settings.'); toastr.error('Update or remove your reverse proxy settings.');
@ -1148,7 +1126,6 @@ function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, wor
{ role: 'system', content: charDescription, identifier: 'charDescription' }, { role: 'system', content: charDescription, identifier: 'charDescription' },
{ role: 'system', content: charPersonalityText, identifier: 'charPersonality' }, { role: 'system', content: charPersonalityText, identifier: 'charPersonality' },
{ role: 'system', content: scenarioText, identifier: 'scenario' }, { role: 'system', content: scenarioText, identifier: 'scenario' },
{ role: 'system', content: personaDescription, identifier: 'personaDescription' },
// Unordered prompts without marker // Unordered prompts without marker
{ role: 'system', content: impersonationPrompt, identifier: 'impersonate' }, { role: 'system', content: impersonationPrompt, identifier: 'impersonate' },
{ role: 'system', content: quietPrompt, identifier: 'quietPrompt' }, { role: 'system', content: quietPrompt, identifier: 'quietPrompt' },
@ -1395,6 +1372,11 @@ function tryParseStreamingError(response, decoded) {
toastr.error(data.error.message || response.statusText, 'Chat Completion API'); toastr.error(data.error.message || response.statusText, 'Chat Completion API');
throw new Error(data); throw new Error(data);
} }
if (data.message) {
toastr.error(data.message, 'Chat Completion API');
throw new Error(data);
}
} }
catch { catch {
// No JSON. Do nothing. // No JSON. Do nothing.
@ -1802,7 +1784,6 @@ async function sendOpenAIRequest(type, messages, signal) {
const isClaude = oai_settings.chat_completion_source == chat_completion_sources.CLAUDE; const isClaude = oai_settings.chat_completion_source == chat_completion_sources.CLAUDE;
const isOpenRouter = oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER; const isOpenRouter = oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER;
const isScale = oai_settings.chat_completion_source == chat_completion_sources.SCALE; const isScale = oai_settings.chat_completion_source == chat_completion_sources.SCALE;
const isAI21 = oai_settings.chat_completion_source == chat_completion_sources.AI21;
const isGoogle = oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE; const isGoogle = oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE;
const isOAI = oai_settings.chat_completion_source == chat_completion_sources.OPENAI; const isOAI = oai_settings.chat_completion_source == chat_completion_sources.OPENAI;
const isMistral = oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI; const isMistral = oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI;
@ -1815,7 +1796,7 @@ async function sendOpenAIRequest(type, messages, signal) {
const isQuiet = type === 'quiet'; const isQuiet = type === 'quiet';
const isImpersonate = type === 'impersonate'; const isImpersonate = type === 'impersonate';
const isContinue = type === 'continue'; const isContinue = type === 'continue';
const stream = oai_settings.stream_openai && !isQuiet && !isScale && !isAI21 && !(isGoogle && oai_settings.google_model.includes('bison')); const stream = oai_settings.stream_openai && !isQuiet && !isScale && !(isGoogle && oai_settings.google_model.includes('bison'));
const useLogprobs = !!power_user.request_token_probabilities; const useLogprobs = !!power_user.request_token_probabilities;
const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom); const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom);
@ -1824,15 +1805,6 @@ async function sendOpenAIRequest(type, messages, signal) {
replaceItemizedPromptText(messageId, messages); replaceItemizedPromptText(messageId, messages);
} }
if (isAI21) {
const joinedMsgs = messages.reduce((acc, obj) => {
const prefix = getPrefixMap()[obj.role];
return acc + (prefix ? (selected_group ? '\n' : prefix + ' ') : '') + obj.content + '\n';
}, '');
messages = substituteParams(joinedMsgs) + (isImpersonate ? `${name1}:` : `${name2}:`);
replaceItemizedPromptText(messageId, messages);
}
// If we're using the window.ai extension, use that instead // If we're using the window.ai extension, use that instead
// Doesn't support logit bias yet // Doesn't support logit bias yet
if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI) { if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI) {
@ -1931,12 +1903,6 @@ async function sendOpenAIRequest(type, messages, signal) {
generate_data['use_makersuite_sysprompt'] = oai_settings.use_makersuite_sysprompt; generate_data['use_makersuite_sysprompt'] = oai_settings.use_makersuite_sysprompt;
} }
if (isAI21) {
generate_data['top_k'] = Number(oai_settings.top_k_openai);
generate_data['count_pen'] = Number(oai_settings.count_pen);
generate_data['stop_tokens'] = [name1 + ':', substituteParams(oai_settings.new_chat_prompt), substituteParams(oai_settings.new_group_chat_prompt)];
}
if (isMistral) { if (isMistral) {
generate_data['safe_prompt'] = false; // already defaults to false, but just incase they change that in the future. generate_data['safe_prompt'] = false; // already defaults to false, but just incase they change that in the future.
} }
@ -3008,7 +2974,6 @@ function loadOpenAISettings(data, settings) {
oai_settings.temp_openai = settings.temp_openai ?? default_settings.temp_openai; oai_settings.temp_openai = settings.temp_openai ?? default_settings.temp_openai;
oai_settings.freq_pen_openai = settings.freq_pen_openai ?? default_settings.freq_pen_openai; oai_settings.freq_pen_openai = settings.freq_pen_openai ?? default_settings.freq_pen_openai;
oai_settings.pres_pen_openai = settings.pres_pen_openai ?? default_settings.pres_pen_openai; oai_settings.pres_pen_openai = settings.pres_pen_openai ?? default_settings.pres_pen_openai;
oai_settings.count_pen = settings.count_pen ?? default_settings.count_pen;
oai_settings.top_p_openai = settings.top_p_openai ?? default_settings.top_p_openai; oai_settings.top_p_openai = settings.top_p_openai ?? default_settings.top_p_openai;
oai_settings.top_k_openai = settings.top_k_openai ?? default_settings.top_k_openai; oai_settings.top_k_openai = settings.top_k_openai ?? default_settings.top_k_openai;
oai_settings.top_a_openai = settings.top_a_openai ?? default_settings.top_a_openai; oai_settings.top_a_openai = settings.top_a_openai ?? default_settings.top_a_openai;
@ -3080,10 +3045,12 @@ function loadOpenAISettings(data, settings) {
oai_settings.names_behavior = character_names_behavior.COMPLETION; oai_settings.names_behavior = character_names_behavior.COMPLETION;
} }
if (oai_settings.ai21_model.startsWith('j2-')) {
oai_settings.ai21_model = 'jamba-1.5-large';
}
if (settings.wrap_in_quotes !== undefined) oai_settings.wrap_in_quotes = !!settings.wrap_in_quotes; if (settings.wrap_in_quotes !== undefined) oai_settings.wrap_in_quotes = !!settings.wrap_in_quotes;
if (settings.openai_model !== undefined) oai_settings.openai_model = settings.openai_model; if (settings.openai_model !== undefined) oai_settings.openai_model = settings.openai_model;
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; }
if (settings.use_google_tokenizer !== undefined) oai_settings.use_google_tokenizer = !!settings.use_google_tokenizer;
if (settings.claude_use_sysprompt !== undefined) oai_settings.claude_use_sysprompt = !!settings.claude_use_sysprompt; if (settings.claude_use_sysprompt !== undefined) oai_settings.claude_use_sysprompt = !!settings.claude_use_sysprompt;
if (settings.use_makersuite_sysprompt !== undefined) oai_settings.use_makersuite_sysprompt = !!settings.use_makersuite_sysprompt; if (settings.use_makersuite_sysprompt !== undefined) oai_settings.use_makersuite_sysprompt = !!settings.use_makersuite_sysprompt;
if (settings.use_alt_scale !== undefined) { oai_settings.use_alt_scale = !!settings.use_alt_scale; updateScaleForm(); } if (settings.use_alt_scale !== undefined) { oai_settings.use_alt_scale = !!settings.use_alt_scale; updateScaleForm(); }
@ -3133,8 +3100,6 @@ function loadOpenAISettings(data, settings) {
$('#jailbreak_system').prop('checked', oai_settings.jailbreak_system); $('#jailbreak_system').prop('checked', oai_settings.jailbreak_system);
$('#openai_show_external_models').prop('checked', oai_settings.show_external_models); $('#openai_show_external_models').prop('checked', oai_settings.show_external_models);
$('#openai_external_category').toggle(oai_settings.show_external_models); $('#openai_external_category').toggle(oai_settings.show_external_models);
$('#use_ai21_tokenizer').prop('checked', oai_settings.use_ai21_tokenizer);
$('#use_google_tokenizer').prop('checked', oai_settings.use_google_tokenizer);
$('#claude_use_sysprompt').prop('checked', oai_settings.claude_use_sysprompt); $('#claude_use_sysprompt').prop('checked', oai_settings.claude_use_sysprompt);
$('#use_makersuite_sysprompt').prop('checked', oai_settings.use_makersuite_sysprompt); $('#use_makersuite_sysprompt').prop('checked', oai_settings.use_makersuite_sysprompt);
$('#scale-alt').prop('checked', oai_settings.use_alt_scale); $('#scale-alt').prop('checked', oai_settings.use_alt_scale);
@ -3170,9 +3135,6 @@ function loadOpenAISettings(data, settings) {
$('#pres_pen_openai').val(oai_settings.pres_pen_openai); $('#pres_pen_openai').val(oai_settings.pres_pen_openai);
$('#pres_pen_counter_openai').val(Number(oai_settings.pres_pen_openai).toFixed(2)); $('#pres_pen_counter_openai').val(Number(oai_settings.pres_pen_openai).toFixed(2));
$('#count_pen').val(oai_settings.count_pen);
$('#count_pen_counter').val(Number(oai_settings.count_pen).toFixed(2));
$('#top_p_openai').val(oai_settings.top_p_openai); $('#top_p_openai').val(oai_settings.top_p_openai);
$('#top_p_counter_openai').val(Number(oai_settings.top_p_openai).toFixed(2)); $('#top_p_counter_openai').val(Number(oai_settings.top_p_openai).toFixed(2));
@ -3392,7 +3354,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
temperature: settings.temp_openai, temperature: settings.temp_openai,
frequency_penalty: settings.freq_pen_openai, frequency_penalty: settings.freq_pen_openai,
presence_penalty: settings.pres_pen_openai, presence_penalty: settings.pres_pen_openai,
count_penalty: settings.count_pen,
top_p: settings.top_p_openai, top_p: settings.top_p_openai,
top_k: settings.top_k_openai, top_k: settings.top_k_openai,
top_a: settings.top_a_openai, top_a: settings.top_a_openai,
@ -3427,8 +3388,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
assistant_prefill: settings.assistant_prefill, assistant_prefill: settings.assistant_prefill,
assistant_impersonation: settings.assistant_impersonation, assistant_impersonation: settings.assistant_impersonation,
human_sysprompt_message: settings.human_sysprompt_message, human_sysprompt_message: settings.human_sysprompt_message,
use_ai21_tokenizer: settings.use_ai21_tokenizer,
use_google_tokenizer: settings.use_google_tokenizer,
claude_use_sysprompt: settings.claude_use_sysprompt, claude_use_sysprompt: settings.claude_use_sysprompt,
use_makersuite_sysprompt: settings.use_makersuite_sysprompt, use_makersuite_sysprompt: settings.use_makersuite_sysprompt,
use_alt_scale: settings.use_alt_scale, use_alt_scale: settings.use_alt_scale,
@ -3799,7 +3758,6 @@ function onSettingsPresetChange() {
temperature: ['#temp_openai', 'temp_openai', false], temperature: ['#temp_openai', 'temp_openai', false],
frequency_penalty: ['#freq_pen_openai', 'freq_pen_openai', false], frequency_penalty: ['#freq_pen_openai', 'freq_pen_openai', false],
presence_penalty: ['#pres_pen_openai', 'pres_pen_openai', false], presence_penalty: ['#pres_pen_openai', 'pres_pen_openai', false],
count_penalty: ['#count_pen', 'count_pen', false],
top_p: ['#top_p_openai', 'top_p_openai', false], top_p: ['#top_p_openai', 'top_p_openai', false],
top_k: ['#top_k_openai', 'top_k_openai', false], top_k: ['#top_k_openai', 'top_k_openai', false],
top_a: ['#top_a_openai', 'top_a_openai', false], top_a: ['#top_a_openai', 'top_a_openai', false],
@ -3856,8 +3814,6 @@ function onSettingsPresetChange() {
assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false], assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],
assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false], assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false],
human_sysprompt_message: ['#claude_human_sysprompt_textarea', 'human_sysprompt_message', false], human_sysprompt_message: ['#claude_human_sysprompt_textarea', 'human_sysprompt_message', false],
use_ai21_tokenizer: ['#use_ai21_tokenizer', 'use_ai21_tokenizer', true],
use_google_tokenizer: ['#use_google_tokenizer', 'use_google_tokenizer', true],
claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true], claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true],
use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true], use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true],
use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true], use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],
@ -4028,6 +3984,11 @@ async function onModelChange() {
} }
if ($(this).is('#model_ai21_select')) { if ($(this).is('#model_ai21_select')) {
if (value === '' || value.startsWith('j2-')) {
value = 'jamba-1.5-large';
$('#model_ai21_select').val(value);
}
console.log('AI21 model changed to', value); console.log('AI21 model changed to', value);
oai_settings.ai21_model = value; oai_settings.ai21_model = value;
} }
@ -4230,7 +4191,7 @@ async function onModelChange() {
else if (['command-light-nightly', 'command-nightly'].includes(oai_settings.cohere_model)) { else if (['command-light-nightly', 'command-nightly'].includes(oai_settings.cohere_model)) {
$('#openai_max_context').attr('max', max_8k); $('#openai_max_context').attr('max', max_8k);
} }
else if (['command-r', 'command-r-plus'].includes(oai_settings.cohere_model)) { else if (oai_settings.cohere_model.includes('command-r')) {
$('#openai_max_context').attr('max', max_128k); $('#openai_max_context').attr('max', max_128k);
} }
else if (['c4ai-aya-23'].includes(oai_settings.cohere_model)) { else if (['c4ai-aya-23'].includes(oai_settings.cohere_model)) {
@ -4305,33 +4266,13 @@ async function onModelChange() {
if (oai_settings.chat_completion_source == chat_completion_sources.AI21) { if (oai_settings.chat_completion_source == chat_completion_sources.AI21) {
if (oai_settings.max_context_unlocked) { if (oai_settings.max_context_unlocked) {
$('#openai_max_context').attr('max', unlocked_max); $('#openai_max_context').attr('max', unlocked_max);
} else { } else if (oai_settings.ai21_model.includes('jamba-1.5') || oai_settings.ai21_model.includes('jamba-instruct')) {
$('#openai_max_context').attr('max', ai21_max); $('#openai_max_context').attr('max', max_256k);
} }
oai_settings.openai_max_context = Math.min(oai_settings.openai_max_context, Number($('#openai_max_context').attr('max'))); oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
$('#openai_max_context').val(oai_settings.openai_max_context).trigger('input'); $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
$('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
oai_settings.temp_openai = Math.min(claude_max_temp, oai_settings.temp_openai);
$('#temp_openai').attr('max', claude_max_temp).val(oai_settings.temp_openai).trigger('input');
oai_settings.freq_pen_openai = Math.min(j2_max_freq, oai_settings.freq_pen_openai < 0 ? 0 : oai_settings.freq_pen_openai);
$('#freq_pen_openai').attr('min', 0).attr('max', j2_max_freq).val(oai_settings.freq_pen_openai).trigger('input');
oai_settings.pres_pen_openai = Math.min(j2_max_pres, oai_settings.pres_pen_openai < 0 ? 0 : oai_settings.pres_pen_openai);
$('#pres_pen_openai').attr('min', 0).attr('max', j2_max_pres).val(oai_settings.pres_pen_openai).trigger('input');
oai_settings.top_k_openai = Math.min(j2_max_topk, oai_settings.top_k_openai);
$('#top_k_openai').attr('max', j2_max_topk).val(oai_settings.top_k_openai).trigger('input');
} else if (oai_settings.chat_completion_source != chat_completion_sources.AI21) {
oai_settings.freq_pen_openai = Math.min(2.0, oai_settings.freq_pen_openai);
$('#freq_pen_openai').attr('min', -2.0).attr('max', 2.0).val(oai_settings.freq_pen_openai).trigger('input');
oai_settings.pres_pen_openai = Math.min(2.0, oai_settings.pres_pen_openai);
$('#pres_pen_openai').attr('min', -2.0).attr('max', 2.0).val(oai_settings.pres_pen_openai).trigger('input');
oai_settings.top_k_openai = Math.min(200, oai_settings.top_k_openai);
$('#top_k_openai').attr('max', 200).val(oai_settings.top_k_openai).trigger('input');
} }
if (oai_settings.chat_completion_source == chat_completion_sources.CUSTOM) { if (oai_settings.chat_completion_source == chat_completion_sources.CUSTOM) {
@ -4388,6 +4329,16 @@ async function onModelChange() {
$('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input'); $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
} }
if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) {
oai_settings.pres_pen_openai = Math.min(Math.max(0, oai_settings.pres_pen_openai), 1);
$('#pres_pen_openai').attr('max', 1).attr('min', 0).val(oai_settings.pres_pen_openai).trigger('input');
oai_settings.freq_pen_openai = Math.min(Math.max(0, oai_settings.freq_pen_openai), 1);
$('#freq_pen_openai').attr('max', 1).attr('min', 0).val(oai_settings.freq_pen_openai).trigger('input');
} else {
$('#pres_pen_openai').attr('max', 2).attr('min', -2).val(oai_settings.pres_pen_openai).trigger('input');
$('#freq_pen_openai').attr('max', 2).attr('min', -2).val(oai_settings.freq_pen_openai).trigger('input');
}
$('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max'))); $('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max')));
saveSettingsDebounced(); saveSettingsDebounced();
@ -4673,17 +4624,17 @@ function toggleChatCompletionForms() {
async function testApiConnection() { async function testApiConnection() {
// Check if the previous request is still in progress // Check if the previous request is still in progress
if (is_send_press) { if (is_send_press) {
toastr.info('Please wait for the previous request to complete.'); toastr.info(t`Please wait for the previous request to complete.`);
return; return;
} }
try { try {
const reply = await sendOpenAIRequest('quiet', [{ 'role': 'user', 'content': 'Hi' }]); const reply = await sendOpenAIRequest('quiet', [{ 'role': 'user', 'content': 'Hi' }]);
console.log(reply); console.log(reply);
toastr.success('API connection successful!'); toastr.success(t`API connection successful!`);
} }
catch (err) { catch (err) {
toastr.error('Could not get a reply from API. Check your connection settings / API key and try again.'); toastr.error(t`Could not get a reply from API. Check your connection settings / API key and try again.`);
} }
} }
@ -4752,11 +4703,14 @@ export function isImageInliningSupported() {
'gemini-1.5-flash', 'gemini-1.5-flash',
'gemini-1.5-flash-latest', 'gemini-1.5-flash-latest',
'gemini-1.5-flash-001', 'gemini-1.5-flash-001',
'gemini-1.5-flash-exp-0827',
'gemini-1.5-flash-8b-exp-0827',
'gemini-1.0-pro-vision-latest', 'gemini-1.0-pro-vision-latest',
'gemini-1.5-pro', 'gemini-1.5-pro',
'gemini-1.5-pro-latest', 'gemini-1.5-pro-latest',
'gemini-1.5-pro-001', 'gemini-1.5-pro-001',
'gemini-1.5-pro-exp-0801', 'gemini-1.5-pro-exp-0801',
'gemini-1.5-pro-exp-0827',
'gemini-pro-vision', 'gemini-pro-vision',
'claude-3', 'claude-3',
'claude-3-5', 'claude-3-5',
@ -4837,7 +4791,7 @@ function onProxyPresetChange() {
if (selectedPreset) { if (selectedPreset) {
setProxyPreset(selectedPreset.name, selectedPreset.url, selectedPreset.password); setProxyPreset(selectedPreset.name, selectedPreset.url, selectedPreset.password);
} else { } else {
console.error(`Proxy preset "${value}" not found in proxies array.`); console.error(t`Proxy preset '${value}' not found in proxies array.`);
} }
saveSettingsDebounced(); saveSettingsDebounced();
} }
@ -4849,7 +4803,7 @@ $('#save_proxy').on('click', async function () {
setProxyPreset(presetName, reverseProxy, proxyPassword); setProxyPreset(presetName, reverseProxy, proxyPassword);
saveSettingsDebounced(); saveSettingsDebounced();
toastr.success('Proxy Saved'); toastr.success(t`Proxy Saved`);
if ($('#openai_proxy_preset').val() !== presetName) { if ($('#openai_proxy_preset').val() !== presetName) {
const option = document.createElement('option'); const option = document.createElement('option');
option.text = presetName; option.text = presetName;
@ -4883,9 +4837,9 @@ $('#delete_proxy').on('click', async function () {
saveSettingsDebounced(); saveSettingsDebounced();
$('#openai_proxy_preset').val(selected_proxy.name); $('#openai_proxy_preset').val(selected_proxy.name);
toastr.success('Proxy Deleted'); toastr.success(t`Proxy Deleted`);
} else { } else {
toastr.error(`Could not find proxy with name "${presetName}"`); toastr.error(t`Could not find proxy with name '${presetName}'`);
} }
}); });
@ -4953,12 +4907,6 @@ $(document).ready(async function () {
saveSettingsDebounced(); saveSettingsDebounced();
}); });
$('#count_pen').on('input', function () {
oai_settings.count_pen = Number($(this).val());
$('#count_pen_counter').val(Number($(this).val()).toFixed(2));
saveSettingsDebounced();
});
$('#top_p_openai').on('input', function () { $('#top_p_openai').on('input', function () {
oai_settings.top_p_openai = Number($(this).val()); oai_settings.top_p_openai = Number($(this).val());
$('#top_p_counter_openai').val(Number($(this).val()).toFixed(2)); $('#top_p_counter_openai').val(Number($(this).val()).toFixed(2));
@ -5017,20 +4965,6 @@ $(document).ready(async function () {
saveSettingsDebounced(); saveSettingsDebounced();
}); });
$('#use_ai21_tokenizer').on('change', function () {
oai_settings.use_ai21_tokenizer = !!$('#use_ai21_tokenizer').prop('checked');
oai_settings.use_ai21_tokenizer ? ai21_max = 8191 : ai21_max = 9200;
oai_settings.openai_max_context = Math.min(ai21_max, oai_settings.openai_max_context);
$('#openai_max_context').attr('max', ai21_max).val(oai_settings.openai_max_context).trigger('input');
$('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max')));
saveSettingsDebounced();
});
$('#use_google_tokenizer').on('change', function () {
oai_settings.use_google_tokenizer = !!$('#use_google_tokenizer').prop('checked');
saveSettingsDebounced();
});
$('#claude_use_sysprompt').on('change', function () { $('#claude_use_sysprompt').on('change', function () {
oai_settings.claude_use_sysprompt = !!$('#claude_use_sysprompt').prop('checked'); oai_settings.claude_use_sysprompt = !!$('#claude_use_sysprompt').prop('checked');
$('#claude_human_sysprompt_message_block').toggle(oai_settings.claude_use_sysprompt); $('#claude_human_sysprompt_message_block').toggle(oai_settings.claude_use_sysprompt);

View File

@ -107,6 +107,7 @@ export const persona_description_positions = {
TOP_AN: 2, TOP_AN: 2,
BOTTOM_AN: 3, BOTTOM_AN: 3,
AT_DEPTH: 4, AT_DEPTH: 4,
NONE: 9,
}; };
let power_user = { let power_user = {

View File

@ -0,0 +1,6 @@
<h3 data-i18n="chat_rename_1">Enter the new name for the chat:</h3>
<small>
<span data-i18n="chat_rename_2">!!Using an existing filename will produce an error!!</span><br>
<span data-i18n="chat_rename_3">This will break the link between checkpoint chats.</span><br>
<span data-i18n="chat_rename_4">No need to add '.jsonl' at the end.</span><br>
</small>

View File

@ -0,0 +1,4 @@
<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">
<input type="checkbox" id="del_chat_checkbox" />
<small data-i18n="Also delete the current chat file">Also delete the current chat file</small>
</label>

View File

@ -0,0 +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>

View File

@ -188,6 +188,8 @@ const settings = {
custom_model: '', custom_model: '',
bypass_status_check: false, bypass_status_check: false,
openrouter_allow_fallbacks: true, openrouter_allow_fallbacks: true,
xtc_threshold: 0.1,
xtc_probability: 0,
}; };
export let textgenerationwebui_banned_in_macros = []; export let textgenerationwebui_banned_in_macros = [];
@ -263,6 +265,8 @@ export const setting_names = [
'custom_model', 'custom_model',
'bypass_status_check', 'bypass_status_check',
'openrouter_allow_fallbacks', 'openrouter_allow_fallbacks',
'xtc_threshold',
'xtc_probability',
]; ];
const DYNATEMP_BLOCK = document.getElementById('dynatemp_block_ooba'); const DYNATEMP_BLOCK = document.getElementById('dynatemp_block_ooba');
@ -718,6 +722,8 @@ jQuery(function () {
'dry_multiplier_textgenerationwebui': 0, 'dry_multiplier_textgenerationwebui': 0,
'dry_base_textgenerationwebui': 1.75, 'dry_base_textgenerationwebui': 1.75,
'dry_penalty_last_n_textgenerationwebui': 0, 'dry_penalty_last_n_textgenerationwebui': 0,
'xtc_threshold_textgenerationwebui': 0.1,
'xtc_probability_textgenerationwebui': 0,
}; };
for (const [id, value] of Object.entries(inputs)) { for (const [id, value] of Object.entries(inputs)) {
@ -1156,6 +1162,8 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
'api_server': getTextGenServer(), 'api_server': getTextGenServer(),
'legacy_api': settings.legacy_api && (settings.type === OOBA || settings.type === APHRODITE), 'legacy_api': settings.legacy_api && (settings.type === OOBA || settings.type === APHRODITE),
'sampler_order': settings.type === textgen_types.KOBOLDCPP ? settings.sampler_order : undefined, 'sampler_order': settings.type === textgen_types.KOBOLDCPP ? settings.sampler_order : undefined,
'xtc_threshold': settings.xtc_threshold,
'xtc_probability': settings.xtc_probability,
}; };
const nonAphroditeParams = { const nonAphroditeParams = {
'rep_pen': settings.rep_pen, 'rep_pen': settings.rep_pen,

View File

@ -27,6 +27,7 @@ export const tokenizers = {
CLAUDE: 11, CLAUDE: 11,
LLAMA3: 12, LLAMA3: 12,
GEMMA: 13, GEMMA: 13,
JAMBA: 14,
BEST_MATCH: 99, BEST_MATCH: 99,
}; };
@ -36,6 +37,7 @@ export const SENTENCEPIECE_TOKENIZERS = [
tokenizers.YI, tokenizers.YI,
tokenizers.LLAMA3, tokenizers.LLAMA3,
tokenizers.GEMMA, tokenizers.GEMMA,
tokenizers.JAMBA,
// uncomment when NovelAI releases Kayra and Clio weights, lol // uncomment when NovelAI releases Kayra and Clio weights, lol
//tokenizers.NERD, //tokenizers.NERD,
//tokenizers.NERD2, //tokenizers.NERD2,
@ -98,6 +100,11 @@ const TOKENIZER_URLS = {
decode: '/api/tokenizers/gemma/decode', decode: '/api/tokenizers/gemma/decode',
count: '/api/tokenizers/gemma/encode', count: '/api/tokenizers/gemma/encode',
}, },
[tokenizers.JAMBA]: {
encode: '/api/tokenizers/jamba/encode',
decode: '/api/tokenizers/jamba/decode',
count: '/api/tokenizers/jamba/encode',
},
[tokenizers.API_TEXTGENERATIONWEBUI]: { [tokenizers.API_TEXTGENERATIONWEBUI]: {
encode: '/api/tokenizers/remote/textgenerationwebui/encode', encode: '/api/tokenizers/remote/textgenerationwebui/encode',
count: '/api/tokenizers/remote/textgenerationwebui/encode', count: '/api/tokenizers/remote/textgenerationwebui/encode',
@ -164,7 +171,7 @@ export function getAvailableTokenizers() {
tokenizerId: Number(tokenizerOption.value), tokenizerId: Number(tokenizerOption.value),
tokenizerKey: Object.entries(tokenizers).find(([_, value]) => value === Number(tokenizerOption.value))[0].toLocaleLowerCase(), tokenizerKey: Object.entries(tokenizers).find(([_, value]) => value === Number(tokenizerOption.value))[0].toLocaleLowerCase(),
tokenizerName: tokenizerOption.text, tokenizerName: tokenizerOption.text,
})) }));
} }
/** /**
@ -280,6 +287,12 @@ export function getTokenizerBestMatch(forApi) {
if (model.includes('gemma')) { if (model.includes('gemma')) {
return tokenizers.GEMMA; return tokenizers.GEMMA;
} }
if (model.includes('yi')) {
return tokenizers.YI;
}
if (model.includes('jamba')) {
return tokenizers.JAMBA;
}
} }
return tokenizers.LLAMA; return tokenizers.LLAMA;
@ -497,6 +510,7 @@ export function getTokenizerModel() {
const mistralTokenizer = 'mistral'; const mistralTokenizer = 'mistral';
const yiTokenizer = 'yi'; const yiTokenizer = 'yi';
const gemmaTokenizer = 'gemma'; const gemmaTokenizer = 'gemma';
const jambaTokenizer = 'jamba';
// Assuming no one would use it for different models.. right? // Assuming no one would use it for different models.. right?
if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) { if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) {
@ -562,12 +576,19 @@ export function getTokenizerModel() {
else if (oai_settings.openrouter_model.includes('GPT-NeoXT')) { else if (oai_settings.openrouter_model.includes('GPT-NeoXT')) {
return gpt2Tokenizer; return gpt2Tokenizer;
} }
else if (oai_settings.openrouter_model.includes('jamba')) {
return jambaTokenizer;
}
} }
if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) { if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) {
return gemmaTokenizer; return gemmaTokenizer;
} }
if (oai_settings.chat_completion_source == chat_completion_sources.AI21) {
return jambaTokenizer;
}
if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) { if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
return claudeTokenizer; return claudeTokenizer;
} }
@ -626,16 +647,7 @@ export function getTokenizerModel() {
* @deprecated Use countTokensOpenAIAsync instead. * @deprecated Use countTokensOpenAIAsync instead.
*/ */
export function countTokensOpenAI(messages, full = false) { export function countTokensOpenAI(messages, full = false) {
const shouldTokenizeAI21 = oai_settings.chat_completion_source === chat_completion_sources.AI21 && oai_settings.use_ai21_tokenizer; const tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
const shouldTokenizeGoogle = oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE && oai_settings.use_google_tokenizer;
let tokenizerEndpoint = '';
if (shouldTokenizeAI21) {
tokenizerEndpoint = '/api/tokenizers/ai21/count';
} else if (shouldTokenizeGoogle) {
tokenizerEndpoint = `/api/tokenizers/google/count?model=${getTokenizerModel()}&reverse_proxy=${oai_settings.reverse_proxy}&proxy_password=${oai_settings.proxy_password}`;
} else {
tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
}
const cacheObject = getTokenCacheObject(); const cacheObject = getTokenCacheObject();
if (!Array.isArray(messages)) { if (!Array.isArray(messages)) {
@ -647,7 +659,7 @@ export function countTokensOpenAI(messages, full = false) {
for (const message of messages) { for (const message of messages) {
const model = getTokenizerModel(); const model = getTokenizerModel();
if (model === 'claude' || shouldTokenizeAI21 || shouldTokenizeGoogle) { if (model === 'claude') {
full = true; full = true;
} }
@ -687,16 +699,7 @@ export function countTokensOpenAI(messages, full = false) {
* @returns {Promise<number>} Token count. * @returns {Promise<number>} Token count.
*/ */
export async function countTokensOpenAIAsync(messages, full = false) { export async function countTokensOpenAIAsync(messages, full = false) {
const shouldTokenizeAI21 = oai_settings.chat_completion_source === chat_completion_sources.AI21 && oai_settings.use_ai21_tokenizer; const tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
const shouldTokenizeGoogle = oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE && oai_settings.use_google_tokenizer;
let tokenizerEndpoint = '';
if (shouldTokenizeAI21) {
tokenizerEndpoint = '/api/tokenizers/ai21/count';
} else if (shouldTokenizeGoogle) {
tokenizerEndpoint = `/api/tokenizers/google/count?model=${getTokenizerModel()}`;
} else {
tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
}
const cacheObject = getTokenCacheObject(); const cacheObject = getTokenCacheObject();
if (!Array.isArray(messages)) { if (!Array.isArray(messages)) {
@ -708,7 +711,7 @@ export async function countTokensOpenAIAsync(messages, full = false) {
for (const message of messages) { for (const message of messages) {
const model = getTokenizerModel(); const model = getTokenizerModel();
if (model === 'claude' || shouldTokenizeAI21 || shouldTokenizeGoogle) { if (model === 'claude') {
full = true; full = true;
} }

View File

@ -4777,8 +4777,10 @@ jQuery(() => {
world_info_min_activations = Number($(this).val()); world_info_min_activations = Number($(this).val());
$('#world_info_min_activations_counter').val(world_info_min_activations); $('#world_info_min_activations_counter').val(world_info_min_activations);
if (world_info_min_activations !== 0) { if (world_info_min_activations !== 0 && world_info_max_recursion_steps !== 0) {
$('#world_info_max_recursion_steps').val(0).trigger('input'); $('#world_info_max_recursion_steps').val(0).trigger('input');
flashHighlight($('#world_info_max_recursion_steps').parent()); // flash the other control to show it has changed
console.info('[WI] Max recursion steps set to 0, as min activations is set to', world_info_min_activations);
} else { } else {
saveSettings(); saveSettings();
} }
@ -4840,8 +4842,10 @@ jQuery(() => {
$('#world_info_max_recursion_steps').on('input', function () { $('#world_info_max_recursion_steps').on('input', function () {
world_info_max_recursion_steps = Number($(this).val()); world_info_max_recursion_steps = Number($(this).val());
$('#world_info_max_recursion_steps_counter').val(world_info_max_recursion_steps); $('#world_info_max_recursion_steps_counter').val(world_info_max_recursion_steps);
if (world_info_max_recursion_steps !== 0) { if (world_info_max_recursion_steps !== 0 && world_info_min_activations !== 0) {
$('#world_info_min_activations').val(0).trigger('input'); $('#world_info_min_activations').val(0).trigger('input');
flashHighlight($('#world_info_min_activations').parent()); // flash the other control to show it has changed
console.info('[WI] Min activations set to 0, as max recursion steps is set to', world_info_max_recursion_steps);
} else { } else {
saveSettings(); saveSettings();
} }

View File

@ -2421,7 +2421,6 @@ input[type="file"] {
} }
#rm_print_characters_block .text_block { #rm_print_characters_block .text_block {
height: 100%;
width: 100%; width: 100%;
opacity: 0.5; opacity: 0.5;
margin: 0 auto 1px auto; margin: 0 auto 1px auto;

126
src/cohere-stream.js Normal file
View File

@ -0,0 +1,126 @@
const DATA_PREFIX = 'data:';
/**
* Borrowed from Cohere SDK (MIT License)
* https://github.com/cohere-ai/cohere-typescript/blob/main/src/core/streaming-fetcher/Stream.ts
* Copyright (c) 2021 Cohere
*/
class CohereStream {
/** @type {ReadableStream} */
stream;
/** @type {string} */
prefix;
/** @type {string} */
messageTerminator;
/** @type {string|undefined} */
streamTerminator;
/** @type {AbortController} */
controller = new AbortController();
constructor({ stream, eventShape }) {
this.stream = stream;
if (eventShape.type === 'sse') {
this.prefix = DATA_PREFIX;
this.messageTerminator = '\n';
this.streamTerminator = eventShape.streamTerminator;
} else {
this.messageTerminator = eventShape.messageTerminator;
}
}
async *iterMessages() {
const stream = readableStreamAsyncIterable(this.stream);
let buf = '';
let prefixSeen = false;
let parsedAnyMessages = false;
for await (const chunk of stream) {
buf += this.decodeChunk(chunk);
let terminatorIndex;
// Parse the chunk into as many messages as possible
while ((terminatorIndex = buf.indexOf(this.messageTerminator)) >= 0) {
// Extract the line from the buffer
let line = buf.slice(0, terminatorIndex + 1);
buf = buf.slice(terminatorIndex + 1);
// Skip empty lines
if (line.length === 0) {
continue;
}
// Skip the chunk until the prefix is found
if (!prefixSeen && this.prefix != null) {
const prefixIndex = line.indexOf(this.prefix);
if (prefixIndex === -1) {
continue;
}
prefixSeen = true;
line = line.slice(prefixIndex + this.prefix.length);
}
// If the stream terminator is present, return
if (this.streamTerminator != null && line.includes(this.streamTerminator)) {
return;
}
// Otherwise, yield message from the prefix to the terminator
const message = JSON.parse(line);
yield message;
prefixSeen = false;
parsedAnyMessages = true;
}
}
if (!parsedAnyMessages && buf.length > 0) {
try {
yield JSON.parse(buf);
} catch (e) {
console.error('Error parsing message:', e);
}
}
}
async *[Symbol.asyncIterator]() {
for await (const message of this.iterMessages()) {
yield message;
}
}
decodeChunk(chunk) {
const decoder = new TextDecoder('utf8');
return decoder.decode(chunk);
}
}
function readableStreamAsyncIterable(stream) {
if (stream[Symbol.asyncIterator]) {
return stream;
}
const reader = stream.getReader();
return {
async next() {
try {
const result = await reader.read();
if (result?.done) {
reader.releaseLock();
} // release lock when stream becomes closed
return result;
} catch (e) {
reader.releaseLock(); // release lock when stream becomes errored
throw e;
}
},
async return() {
const cancelPromise = reader.cancel();
reader.releaseLock();
await cancelPromise;
return { done: true, value: undefined };
},
[Symbol.asyncIterator]() {
return this;
},
};
}
module.exports = CohereStream;

View File

@ -1,11 +1,11 @@
const express = require('express'); const express = require('express');
const fetch = require('node-fetch').default; const fetch = require('node-fetch').default;
const Readable = require('stream').Readable;
const { jsonParser } = require('../../express-common'); const { jsonParser } = require('../../express-common');
const { CHAT_COMPLETION_SOURCES, GEMINI_SAFETY, BISON_SAFETY, OPENROUTER_HEADERS } = require('../../constants'); const { CHAT_COMPLETION_SOURCES, GEMINI_SAFETY, BISON_SAFETY, OPENROUTER_HEADERS } = require('../../constants');
const { forwardFetchResponse, getConfigValue, tryParse, uuidv4, mergeObjectWithYaml, excludeKeysByYaml, color } = require('../../util'); const { forwardFetchResponse, getConfigValue, tryParse, uuidv4, mergeObjectWithYaml, excludeKeysByYaml, color } = require('../../util');
const { convertClaudeMessages, convertGooglePrompt, convertTextCompletionPrompt, convertCohereMessages, convertMistralMessages, convertCohereTools } = require('../../prompt-converters'); const { convertClaudeMessages, convertGooglePrompt, convertTextCompletionPrompt, convertCohereMessages, convertMistralMessages, convertCohereTools, convertAI21Messages } = require('../../prompt-converters');
const CohereStream = require('../../cohere-stream');
const { readSecret, SECRET_KEYS } = require('../secrets'); const { readSecret, SECRET_KEYS } = require('../secrets');
const { getTokenizerModel, getSentencepiceTokenizer, getTiktokenTokenizer, sentencepieceTokenizers, TEXT_COMPLETION_MODELS } = require('../tokenizers'); const { getTokenizerModel, getSentencepiceTokenizer, getTiktokenTokenizer, sentencepieceTokenizers, TEXT_COMPLETION_MODELS } = require('../tokenizers');
@ -19,6 +19,7 @@ const API_GROQ = 'https://api.groq.com/openai/v1';
const API_MAKERSUITE = 'https://generativelanguage.googleapis.com'; const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';
const API_01AI = 'https://api.01.ai/v1'; const API_01AI = 'https://api.01.ai/v1';
const API_BLOCKENTROPY = 'https://api.blockentropy.ai/v1'; const API_BLOCKENTROPY = 'https://api.blockentropy.ai/v1';
const API_AI21 = 'https://api.ai21.com/studio/v1';
/** /**
* Applies a post-processing step to the generated messages. * Applies a post-processing step to the generated messages.
@ -40,52 +41,30 @@ function postProcessPrompt(messages, type, charName, userName) {
/** /**
* Ollama strikes back. Special boy #2's steaming routine. * Ollama strikes back. Special boy #2's steaming routine.
* Wrap this abomination into proper SSE stream, again. * Wrap this abomination into proper SSE stream, again.
* @param {import('node-fetch').Response} jsonStream JSON stream * @param {Response} jsonStream JSON stream
* @param {import('express').Request} request Express request * @param {import('express').Request} request Express request
* @param {import('express').Response} response Express response * @param {import('express').Response} response Express response
* @returns {Promise<any>} Nothing valuable * @returns {Promise<any>} Nothing valuable
*/ */
async function parseCohereStream(jsonStream, request, response) { async function parseCohereStream(jsonStream, request, response) {
try { try {
let partialData = ''; const stream = new CohereStream({ stream: jsonStream.body, eventShape: { type: 'json', messageTerminator: '\n' } });
jsonStream.body.on('data', (data) => {
const chunk = data.toString(); for await (const json of stream.iterMessages()) {
partialData += chunk; if (json.message) {
while (true) { const message = json.message || 'Unknown error';
let json; const chunk = { error: { message: message } };
try { response.write(`data: ${JSON.stringify(chunk)}\n\n`);
json = JSON.parse(partialData); } else if (json.event_type === 'text-generation') {
} catch (e) { const text = json.text || '';
break; const chunk = { choices: [{ text }] };
} response.write(`data: ${JSON.stringify(chunk)}\n\n`);
if (json.message) {
const message = json.message || 'Unknown error';
const chunk = { error: { message: message } };
response.write(`data: ${JSON.stringify(chunk)}\n\n`);
partialData = '';
break;
} else if (json.event_type === 'text-generation') {
const text = json.text || '';
const chunk = { choices: [{ text }] };
response.write(`data: ${JSON.stringify(chunk)}\n\n`);
partialData = '';
} else {
partialData = '';
break;
}
} }
}); }
request.socket.on('close', function () { console.log('Streaming request finished');
if (jsonStream.body instanceof Readable) jsonStream.body.destroy(); response.write('data: [DONE]\n\n');
response.end(); response.end();
});
jsonStream.body.on('end', () => {
console.log('Streaming request finished');
response.write('data: [DONE]\n\n');
response.end();
});
} catch (error) { } catch (error) {
console.log('Error forwarding streaming response:', error); console.log('Error forwarding streaming response:', error);
if (!response.headersSent) { if (!response.headersSent) {
@ -233,7 +212,7 @@ async function sendScaleRequest(request, response) {
if (!generateResponse.ok) { if (!generateResponse.ok) {
console.log(`Scale API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`); console.log(`Scale API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
return response.status(generateResponse.status).send({ error: true }); return response.status(500).send({ error: true });
} }
const generateResponseJson = await generateResponse.json(); const generateResponseJson = await generateResponse.json();
@ -413,6 +392,16 @@ async function sendAI21Request(request, response) {
request.socket.on('close', function () { request.socket.on('close', function () {
controller.abort(); controller.abort();
}); });
const convertedPrompt = convertAI21Messages(request.body.messages, request.body.char_name, request.body.user_name);
const body = {
messages: convertedPrompt,
model: request.body.model,
max_tokens: request.body.max_tokens,
temperature: request.body.temperature,
top_p: request.body.top_p,
stop: request.body.stop,
stream: request.body.stream,
};
const options = { const options = {
method: 'POST', method: 'POST',
headers: { headers: {
@ -420,59 +409,35 @@ async function sendAI21Request(request, response) {
'content-type': 'application/json', 'content-type': 'application/json',
Authorization: `Bearer ${readSecret(request.user.directories, SECRET_KEYS.AI21)}`, Authorization: `Bearer ${readSecret(request.user.directories, SECRET_KEYS.AI21)}`,
}, },
body: JSON.stringify({ body: JSON.stringify(body),
numResults: 1,
maxTokens: request.body.max_tokens,
minTokens: 0,
temperature: request.body.temperature,
topP: request.body.top_p,
stopSequences: request.body.stop_tokens,
topKReturn: request.body.top_k,
frequencyPenalty: {
scale: request.body.frequency_penalty * 100,
applyToWhitespaces: false,
applyToPunctuations: false,
applyToNumbers: false,
applyToStopwords: false,
applyToEmojis: false,
},
presencePenalty: {
scale: request.body.presence_penalty,
applyToWhitespaces: false,
applyToPunctuations: false,
applyToNumbers: false,
applyToStopwords: false,
applyToEmojis: false,
},
countPenalty: {
scale: request.body.count_pen,
applyToWhitespaces: false,
applyToPunctuations: false,
applyToNumbers: false,
applyToStopwords: false,
applyToEmojis: false,
},
prompt: request.body.messages,
}),
signal: controller.signal, signal: controller.signal,
}; };
fetch(`https://api.ai21.com/studio/v1/${request.body.model}/complete`, options) console.log('AI21 request:', body);
.then(r => r.json())
.then(r => {
if (r.completions === undefined) {
console.log(r);
} else {
console.log(r.completions[0].data.text);
}
const reply = { choices: [{ 'message': { 'content': r.completions?.[0]?.data?.text } }] };
return response.send(reply);
})
.catch(err => {
console.error(err);
return response.send({ error: true });
});
try {
const generateResponse = await fetch(API_AI21 + '/chat/completions', options);
if (request.body.stream) {
forwardFetchResponse(generateResponse, response);
} else {
if (!generateResponse.ok) {
const errorText = await generateResponse.text();
console.log(`AI21 API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
const errorJson = tryParse(errorText) ?? { error: true };
return response.status(500).send(errorJson);
}
const generateResponseJson = await generateResponse.json();
console.log('AI21 response:', generateResponseJson);
return response.send(generateResponseJson);
}
} catch (error) {
console.log('Error communicating with AI21 API: ', error);
if (!response.headersSent) {
response.send({ error: true });
} else {
response.end();
}
}
} }
/** /**
@ -531,10 +496,10 @@ async function sendMistralAIRequest(request, response) {
forwardFetchResponse(generateResponse, response); forwardFetchResponse(generateResponse, response);
} else { } else {
if (!generateResponse.ok) { if (!generateResponse.ok) {
console.log(`MistralAI API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`); const errorText = await generateResponse.text();
// a 401 unauthorized response breaks the frontend auth, so return a 500 instead. prob a better way of dealing with this. console.log(`MistralAI API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
// 401s are already handled by the streaming processor and dont pop up an error toast, that should probably be fixed too. const errorJson = tryParse(errorText) ?? { error: true };
return response.status(generateResponse.status === 401 ? 500 : generateResponse.status).send({ error: true }); return response.status(500).send(errorJson);
} }
const generateResponseJson = await generateResponse.json(); const generateResponseJson = await generateResponse.json();
console.log('MistralAI response:', generateResponseJson); console.log('MistralAI response:', generateResponseJson);
@ -607,6 +572,11 @@ async function sendCohereRequest(request, response) {
search_queries_only: false, search_queries_only: false,
}; };
const canDoSafetyMode = String(request.body.model).endsWith('08-2024');
if (canDoSafetyMode) {
requestBody.safety_mode = 'NONE';
}
console.log('Cohere request:', requestBody); console.log('Cohere request:', requestBody);
const config = { const config = {
@ -623,15 +593,15 @@ async function sendCohereRequest(request, response) {
const apiUrl = API_COHERE + '/chat'; const apiUrl = API_COHERE + '/chat';
if (request.body.stream) { if (request.body.stream) {
const stream = await fetch(apiUrl, config); const stream = await global.fetch(apiUrl, config);
parseCohereStream(stream, request, response); parseCohereStream(stream, request, response);
} else { } else {
const generateResponse = await fetch(apiUrl, config); const generateResponse = await fetch(apiUrl, config);
if (!generateResponse.ok) { if (!generateResponse.ok) {
console.log(`Cohere API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`); const errorText = await generateResponse.text();
// a 401 unauthorized response breaks the frontend auth, so return a 500 instead. prob a better way of dealing with this. console.log(`Cohere API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
// 401s are already handled by the streaming processor and dont pop up an error toast, that should probably be fixed too. const errorJson = tryParse(errorText) ?? { error: true };
return response.status(generateResponse.status === 401 ? 500 : generateResponse.status).send({ error: true }); return response.status(500).send(errorJson);
} }
const generateResponseJson = await generateResponse.json(); const generateResponseJson = await generateResponse.json();
console.log('Cohere response:', generateResponseJson); console.log('Cohere response:', generateResponseJson);

View File

@ -6,6 +6,7 @@ const { readAllChunks, extractFileFromZipBuffer, forwardFetchResponse } = requir
const { jsonParser } = require('../express-common'); const { jsonParser } = require('../express-common');
const API_NOVELAI = 'https://api.novelai.net'; const API_NOVELAI = 'https://api.novelai.net';
const TEXT_NOVELAI = 'https://text.novelai.net';
const IMAGE_NOVELAI = 'https://image.novelai.net'; const IMAGE_NOVELAI = 'https://image.novelai.net';
// Ban bracket generation, plus defaults // Ban bracket generation, plus defaults
@ -155,7 +156,7 @@ router.post('/generate', jsonParser, async function (req, res) {
'repetition_penalty_slope': req.body.repetition_penalty_slope, 'repetition_penalty_slope': req.body.repetition_penalty_slope,
'repetition_penalty_frequency': req.body.repetition_penalty_frequency, 'repetition_penalty_frequency': req.body.repetition_penalty_frequency,
'repetition_penalty_presence': req.body.repetition_penalty_presence, 'repetition_penalty_presence': req.body.repetition_penalty_presence,
'repetition_penalty_whitelist': isNewModel ? repPenaltyAllowList : null, 'repetition_penalty_whitelist': isNewModel ? repPenaltyAllowList.flat() : null,
'top_a': req.body.top_a, 'top_a': req.body.top_a,
'top_p': req.body.top_p, 'top_p': req.body.top_p,
'top_k': req.body.top_k, 'top_k': req.body.top_k,
@ -178,9 +179,7 @@ router.post('/generate', jsonParser, async function (req, res) {
}; };
// Tells the model to stop generation at '>' // Tells the model to stop generation at '>'
if ('theme_textadventure' === req.body.prefix && if ('theme_textadventure' === req.body.prefix && isNewModel) {
(true === req.body.model.includes('clio') ||
true === req.body.model.includes('kayra'))) {
data.parameters.eos_token_id = 49405; data.parameters.eos_token_id = 49405;
} }
@ -193,7 +192,8 @@ router.post('/generate', jsonParser, async function (req, res) {
}; };
try { try {
const url = req.body.streaming ? `${API_NOVELAI}/ai/generate-stream` : `${API_NOVELAI}/ai/generate`; const baseURL = req.body.model.includes('kayra') ? TEXT_NOVELAI : API_NOVELAI;
const url = req.body.streaming ? `${baseURL}/ai/generate-stream` : `${baseURL}/ai/generate`;
const response = await fetch(url, { method: 'POST', timeout: 0, ...args }); const response = await fetch(url, { method: 'POST', timeout: 0, ...args });
if (req.body.streaming) { if (req.body.streaming) {

View File

@ -144,6 +144,7 @@ const spp_nerd_v2 = new SentencePieceTokenizer('src/tokenizers/nerdstash_v2.mode
const spp_mistral = new SentencePieceTokenizer('src/tokenizers/mistral.model'); const spp_mistral = new SentencePieceTokenizer('src/tokenizers/mistral.model');
const spp_yi = new SentencePieceTokenizer('src/tokenizers/yi.model'); const spp_yi = new SentencePieceTokenizer('src/tokenizers/yi.model');
const spp_gemma = new SentencePieceTokenizer('src/tokenizers/gemma.model'); const spp_gemma = new SentencePieceTokenizer('src/tokenizers/gemma.model');
const spp_jamba = new SentencePieceTokenizer('src/tokenizers/jamba.model');
const claude_tokenizer = new WebTokenizer('src/tokenizers/claude.json'); const claude_tokenizer = new WebTokenizer('src/tokenizers/claude.json');
const llama3_tokenizer = new WebTokenizer('src/tokenizers/llama3.json'); const llama3_tokenizer = new WebTokenizer('src/tokenizers/llama3.json');
@ -154,6 +155,7 @@ const sentencepieceTokenizers = [
'mistral', 'mistral',
'yi', 'yi',
'gemma', 'gemma',
'jamba',
]; ];
/** /**
@ -186,6 +188,10 @@ function getSentencepiceTokenizer(model) {
return spp_gemma; return spp_gemma;
} }
if (model.includes('jamba')) {
return spp_jamba;
}
return null; return null;
} }
@ -322,6 +328,10 @@ function getTokenizerModel(requestModel) {
return 'gemma'; return 'gemma';
} }
if (requestModel.includes('jamba')) {
return 'jamba';
}
// default // default
return 'gpt-3.5-turbo'; return 'gpt-3.5-turbo';
} }
@ -537,59 +547,13 @@ function createWebTokenizerDecodingHandler(tokenizer) {
const router = express.Router(); const router = express.Router();
router.post('/ai21/count', jsonParser, async function (req, res) {
if (!req.body) return res.sendStatus(400);
const key = readSecret(req.user.directories, SECRET_KEYS.AI21);
const options = {
method: 'POST',
headers: {
accept: 'application/json',
'content-type': 'application/json',
Authorization: `Bearer ${key}`,
},
body: JSON.stringify({ text: req.body[0].content }),
};
try {
const response = await fetch('https://api.ai21.com/studio/v1/tokenize', options);
const data = await response.json();
return res.send({ 'token_count': data?.tokens?.length || 0 });
} catch (err) {
console.error(err);
return res.send({ 'token_count': 0 });
}
});
router.post('/google/count', jsonParser, async function (req, res) {
if (!req.body) return res.sendStatus(400);
const options = {
method: 'POST',
headers: {
accept: 'application/json',
'content-type': 'application/json',
},
body: JSON.stringify({ contents: convertGooglePrompt(req.body, String(req.query.model)).contents }),
};
try {
const reverseProxy = req.query.reverse_proxy?.toString() || '';
const proxyPassword = req.query.proxy_password?.toString() || '';
const apiKey = reverseProxy ? proxyPassword : readSecret(req.user.directories, SECRET_KEYS.MAKERSUITE);
const apiUrl = new URL(reverseProxy || API_MAKERSUITE);
const response = await fetch(`${apiUrl.origin}/v1beta/models/${req.query.model}:countTokens?key=${apiKey}`, options);
const data = await response.json();
return res.send({ 'token_count': data?.totalTokens || 0 });
} catch (err) {
console.error(err);
return res.send({ 'token_count': 0 });
}
});
router.post('/llama/encode', jsonParser, createSentencepieceEncodingHandler(spp_llama)); router.post('/llama/encode', jsonParser, createSentencepieceEncodingHandler(spp_llama));
router.post('/nerdstash/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd)); router.post('/nerdstash/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd));
router.post('/nerdstash_v2/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd_v2)); router.post('/nerdstash_v2/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd_v2));
router.post('/mistral/encode', jsonParser, createSentencepieceEncodingHandler(spp_mistral)); router.post('/mistral/encode', jsonParser, createSentencepieceEncodingHandler(spp_mistral));
router.post('/yi/encode', jsonParser, createSentencepieceEncodingHandler(spp_yi)); router.post('/yi/encode', jsonParser, createSentencepieceEncodingHandler(spp_yi));
router.post('/gemma/encode', jsonParser, createSentencepieceEncodingHandler(spp_gemma)); router.post('/gemma/encode', jsonParser, createSentencepieceEncodingHandler(spp_gemma));
router.post('/jamba/encode', jsonParser, createSentencepieceEncodingHandler(spp_jamba));
router.post('/gpt2/encode', jsonParser, createTiktokenEncodingHandler('gpt2')); router.post('/gpt2/encode', jsonParser, createTiktokenEncodingHandler('gpt2'));
router.post('/claude/encode', jsonParser, createWebTokenizerEncodingHandler(claude_tokenizer)); router.post('/claude/encode', jsonParser, createWebTokenizerEncodingHandler(claude_tokenizer));
router.post('/llama3/encode', jsonParser, createWebTokenizerEncodingHandler(llama3_tokenizer)); router.post('/llama3/encode', jsonParser, createWebTokenizerEncodingHandler(llama3_tokenizer));
@ -599,6 +563,7 @@ router.post('/nerdstash_v2/decode', jsonParser, createSentencepieceDecodingHandl
router.post('/mistral/decode', jsonParser, createSentencepieceDecodingHandler(spp_mistral)); router.post('/mistral/decode', jsonParser, createSentencepieceDecodingHandler(spp_mistral));
router.post('/yi/decode', jsonParser, createSentencepieceDecodingHandler(spp_yi)); router.post('/yi/decode', jsonParser, createSentencepieceDecodingHandler(spp_yi));
router.post('/gemma/decode', jsonParser, createSentencepieceDecodingHandler(spp_gemma)); router.post('/gemma/decode', jsonParser, createSentencepieceDecodingHandler(spp_gemma));
router.post('/jamba/decode', jsonParser, createSentencepieceDecodingHandler(spp_jamba));
router.post('/gpt2/decode', jsonParser, createTiktokenDecodingHandler('gpt2')); router.post('/gpt2/decode', jsonParser, createTiktokenDecodingHandler('gpt2'));
router.post('/claude/decode', jsonParser, createWebTokenizerDecodingHandler(claude_tokenizer)); router.post('/claude/decode', jsonParser, createWebTokenizerDecodingHandler(claude_tokenizer));
router.post('/llama3/decode', jsonParser, createWebTokenizerDecodingHandler(llama3_tokenizer)); router.post('/llama3/decode', jsonParser, createWebTokenizerDecodingHandler(llama3_tokenizer));
@ -637,6 +602,11 @@ router.post('/openai/encode', jsonParser, async function (req, res) {
return handler(req, res); return handler(req, res);
} }
if (queryModel.includes('jamba')) {
const handler = createSentencepieceEncodingHandler(spp_jamba);
return handler(req, res);
}
const model = getTokenizerModel(queryModel); const model = getTokenizerModel(queryModel);
const handler = createTiktokenEncodingHandler(model); const handler = createTiktokenEncodingHandler(model);
return handler(req, res); return handler(req, res);
@ -680,6 +650,11 @@ router.post('/openai/decode', jsonParser, async function (req, res) {
return handler(req, res); return handler(req, res);
} }
if (queryModel.includes('jamba')) {
const handler = createSentencepieceDecodingHandler(spp_jamba);
return handler(req, res);
}
const model = getTokenizerModel(queryModel); const model = getTokenizerModel(queryModel);
const handler = createTiktokenDecodingHandler(model); const handler = createTiktokenDecodingHandler(model);
return handler(req, res); return handler(req, res);
@ -731,6 +706,11 @@ router.post('/openai/count', jsonParser, async function (req, res) {
return res.send({ 'token_count': num_tokens }); return res.send({ 'token_count': num_tokens });
} }
if (model === 'jamba') {
num_tokens = await countSentencepieceArrayTokens(spp_jamba, req.body);
return res.send({ 'token_count': num_tokens });
}
const tokensPerName = queryModel.includes('gpt-3.5-turbo-0301') ? -1 : 1; const tokensPerName = queryModel.includes('gpt-3.5-turbo-0301') ? -1 : 1;
const tokensPerMessage = queryModel.includes('gpt-3.5-turbo-0301') ? 4 : 3; const tokensPerMessage = queryModel.includes('gpt-3.5-turbo-0301') ? 4 : 3;
const tokensPadding = 3; const tokensPadding = 3;

View File

@ -110,27 +110,27 @@ router.post('/google', jsonParser, async (request, response) => {
}); });
router.post('/yandex', jsonParser, async (request, response) => { router.post('/yandex', jsonParser, async (request, response) => {
const chunks = request.body.chunks;
const lang = request.body.lang;
if (!chunks || !lang) {
return response.sendStatus(400);
}
// reconstruct original text to log
let inputText = '';
const params = new URLSearchParams();
for (const chunk of chunks) {
params.append('text', chunk);
inputText += chunk;
}
params.append('lang', lang);
const ucid = uuidv4().replaceAll('-', '');
console.log('Input text: ' + inputText);
try { try {
const chunks = request.body.chunks;
const lang = request.body.lang;
if (!chunks || !lang) {
return response.sendStatus(400);
}
// reconstruct original text to log
let inputText = '';
const params = new URLSearchParams();
for (const chunk of chunks) {
params.append('text', chunk);
inputText += chunk;
}
params.append('lang', lang);
const ucid = uuidv4().replaceAll('-', '');
console.log('Input text: ' + inputText);
const result = await fetch(`https://translate.yandex.net/api/v1/tr.json/translate?ucid=${ucid}&srv=android&format=text`, { const result = await fetch(`https://translate.yandex.net/api/v1/tr.json/translate?ucid=${ucid}&srv=android&format=text`, {
method: 'POST', method: 'POST',
body: params, body: params,

View File

@ -267,10 +267,13 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '
'gemini-1.5-flash', 'gemini-1.5-flash',
'gemini-1.5-flash-latest', 'gemini-1.5-flash-latest',
'gemini-1.5-flash-001', 'gemini-1.5-flash-001',
'gemini-1.5-flash-exp-0827',
'gemini-1.5-flash-8b-exp-0827',
'gemini-1.5-pro', 'gemini-1.5-pro',
'gemini-1.5-pro-latest', 'gemini-1.5-pro-latest',
'gemini-1.5-pro-001', 'gemini-1.5-pro-001',
'gemini-1.5-pro-exp-0801', 'gemini-1.5-pro-exp-0801',
'gemini-1.5-pro-exp-0827',
'gemini-1.0-pro-vision-latest', 'gemini-1.0-pro-vision-latest',
'gemini-pro-vision', 'gemini-pro-vision',
]; ];
@ -367,6 +370,78 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '
return { contents: contents, system_instruction: system_instruction }; return { contents: contents, system_instruction: system_instruction };
} }
/**
* Convert AI21 prompt. Classic: system message squash, user/assistant message merge.
* @param {object[]} messages Array of messages
* @param {string} charName Character name
* @param {string} userName User name
*/
function convertAI21Messages(messages, charName = '', userName = '') {
if (!Array.isArray(messages)) {
return [];
}
// Collect all the system messages up until the first instance of a non-system message, and then remove them from the messages array.
let i = 0, systemPrompt = '';
for (i = 0; i < messages.length; i++) {
if (messages[i].role !== 'system') {
break;
}
// Append example names if not already done by the frontend (e.g. for group chats).
if (userName && messages[i].name === 'example_user') {
if (!messages[i].content.startsWith(`${userName}: `)) {
messages[i].content = `${userName}: ${messages[i].content}`;
}
}
if (charName && messages[i].name === 'example_assistant') {
if (!messages[i].content.startsWith(`${charName}: `)) {
messages[i].content = `${charName}: ${messages[i].content}`;
}
}
systemPrompt += `${messages[i].content}\n\n`;
}
messages.splice(0, i);
// Prevent erroring out if the messages array is empty.
if (messages.length === 0) {
messages.unshift({
role: 'user',
content: '[Start a new chat]',
});
}
if (systemPrompt) {
messages.unshift({
role: 'system',
content: systemPrompt.trim(),
});
}
// Doesn't support completion names, so prepend if not already done by the frontend (e.g. for group chats).
messages.forEach(msg => {
if ('name' in msg) {
if (msg.role !== 'system' && !msg.content.startsWith(`${msg.name}: `)) {
msg.content = `${msg.name}: ${msg.content}`;
}
delete msg.name;
}
});
// Since the messaging endpoint only supports alternating turns, we have to merge messages with the same role if they follow each other
let mergedMessages = [];
messages.forEach((message) => {
if (mergedMessages.length > 0 && mergedMessages[mergedMessages.length - 1].role === message.role) {
mergedMessages[mergedMessages.length - 1].content += '\n\n' + message.content;
} else {
mergedMessages.push(message);
}
});
return mergedMessages;
}
/** /**
* Convert a prompt from the ChatML objects to the format used by MistralAI. * Convert a prompt from the ChatML objects to the format used by MistralAI.
* @param {object[]} messages Array of messages * @param {object[]} messages Array of messages
@ -520,4 +595,5 @@ module.exports = {
convertCohereMessages, convertCohereMessages,
convertMistralMessages, convertMistralMessages,
convertCohereTools, convertCohereTools,
convertAI21Messages,
}; };

BIN
src/tokenizers/jamba.model Normal file

Binary file not shown.

View File

@ -34,8 +34,9 @@ function getConfig() {
CACHED_CONFIG = config; CACHED_CONFIG = config;
return config; return config;
} catch (error) { } catch (error) {
console.warn('Failed to read config.yaml'); console.error(color.red('FATAL: Failed to read config.yaml. Please check the file for syntax errors.'));
return {}; console.error(error.message);
process.exit(1);
} }
} }
@ -298,8 +299,8 @@ const color = {
* @returns {string} A UUIDv4 string * @returns {string} A UUIDv4 string
*/ */
function uuidv4() { function uuidv4() {
if ('randomUUID' in crypto) { if ('crypto' in global && 'randomUUID' in global.crypto) {
return crypto.randomUUID(); return global.crypto.randomUUID();
} }
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
const r = Math.random() * 16 | 0; const r = Math.random() * 16 | 0;