Merge branch 'staging' of https://github.com/joenunezb/SillyTavern into optimize/improve-search

78c55558af3b6a57a484131916a8ba8c33e50452

Joe <joenunezb@gmail.com>

15 files changed, +177 -79Showing whitespace changes
.github/readme-ru_ru.md+40 -0
@@ -229,6 +229,46 @@ SillyTavern сохраняет ключи от ваших API в файле `sec
2292291. Зайдите в файл `config.yaml` и установите `allowKeysExposure` в положение `true`.
2302302. Перезапустите сервер SillyTavern.
231231
232+## Аргументы командной строки
233+
234+Вы можете передавать аргументы командной строки при запуске сервера SillyTavern, чтобы переопределять настройки из `config.yaml`.
235+
236+### Примеры
237+
238+```shell
239+node server.js --port 8000 --listen false
240+# или
241+npm run start -- --port 8000 --listen false
242+# или (только на Windows)
243+Start.bat --port 8000 --listen false
244+```
245+
246+### Поддерживаемые аргументы
247+
248+| Аргумент | Описание | Тип |
249+|-------------------------|----------------------------------------------------------------------------------------------------------------|----------|
250+| `--version` | Показывает номер версии. | boolean |
251+| `--enableIPv6` | Включает IPv6. | boolean |
252+| `--enableIPv4` | Включает IPv4. | boolean |
253+| `--port` | Устанавливает порт, котрый будет использовать SillyTavern. Если не указан, то используется yaml-конфиг 'port'. | number |
254+| `--dnsPreferIPv6` | Отдает предпочтение IPv6 для dns. Если не указан, то используется yaml-конфиг 'preferIPv6'. | boolean |
255+| `--autorun` | Автоматический запуск SillyTavern в браузере. Если не указан, то используется yaml-конфиг 'autorun'. | boolean |
256+| `--autorunHostname` | Имя хоста автозапуска, лучше оставить на 'auto'. | string |
257+| `--autorunPortOverride` | Переопределяет порт для автозапуска. | string |
258+| `--listen` | SillyTavern будет прослушивать все сетевые интерфейсы. Если не указан, то используется yaml-конфиг 'listen'. | boolean |
259+| `--corsProxy` | Включает CORS-прокси. Если не указан, то используется yaml-конфиг 'enableCorsProxy'. | boolean |
260+| `--disableCsrf` | Отключает защиту от CSRF. | boolean |
261+| `--ssl` | Включает SSL. | boolean |
262+| `--certPath` | Путь к файлу c сертификатом. | string |
263+| `--keyPath` | Путь к файлу с закрытым ключом. | string |
264+| `--whitelist` | Включает режим белого списка. | boolean |
265+| `--dataRoot` | Корневой каталог для хранения данных. | string |
266+| `--avoidLocalhost` | Избегает использования 'localhost' для автозапуска в режиме 'auto'. | boolean |
267+| `--basicAuthMode` | Включает простую аутентификацию. | boolean |
268+| `--requestProxyEnabled` | Разрешает использование прокси для исходящих запросов. | boolean |
269+| `--requestProxyUrl` | URL-адрес прокси (протоколы HTTP или SOCKS). | string |
270+| `--requestProxyBypass` | Bypass список прокси (список хостов, разделенных пробелами). | array |
271+
232272## Удалённое подключение
233273
234274В основном этим пользуются тогда, когда хотят использовать SillyTavern с телефона, запустив сервер SillyTavern на стационарном ПК в той же Wi-Fi-сети.
default/content/presets/openai/Default.json+0 -1
@@ -230,7 +230,6 @@
230230 "show_external_models": false,
231231 "assistant_prefill": "",
232232 "assistant_impersonation": "",
233- "human_sysprompt_message": "Let's get started. Please generate your response based on the information and instructions provided above.",
234233 "claude_use_sysprompt": false,
235234 "use_alt_scale": false,
236235 "squash_system_messages": false,
public/index.html+11 -15
@@ -1291,6 +1291,14 @@
12911291 <input class="neo-range-slider" type="range" id="epsilon_cutoff_textgenerationwebui" name="volume" min="0" max="9" step="0.01">
12921292 <input class="neo-range-input" type="number" min="0" max="9" step="0.01" data-for="epsilon_cutoff_textgenerationwebui" id="epsilon_cutoff_counter_textgenerationwebui">
12931293 </div>
1294+ <div data-tg-type="aphrodite" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
1295+ <small>
1296+ <span data-i18n="Top nsigma">Top nsigma</span>
1297+ <div class="fa-solid fa-circle-info opacity50p" title="A sampling method that filters logits based on their statistical properties. It keeps tokens within n standard deviations of the maximum logit value, providing a simpler alternative to top-p/top-k sampling while maintaining sampling stability across different temperatures."></div>
1298+ </small>
1299+ <input class="neo-range-slider" type="range" id="nsigma_textgenerationwebui" name="volume" min="0" max="5" step="0.01">
1300+ <input class="neo-range-input" type="number" min="0" max="5" step="0.01" data-for="nsigma_textgenerationwebui" id="nsigma_counter_textgenerationwebui">
1301+ </div>
12941302 <div data-tg-type="ooba,mancer,aphrodite" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
12951303 <small>
12961304 <span data-i18n="Eta Cutoff">Eta Cutoff</span>
@@ -1334,12 +1342,12 @@
13341342 <input class="neo-range-slider" type="range" id="presence_pen_textgenerationwebui" name="volume" min="-2" max="2" step="0.01" />
13351343 <input class="neo-range-input" type="number" min="-2" max="2" step="0.01" data-for="presence_pen_textgenerationwebui" id="presence_pen_counter_textgenerationwebui">
13361344 </div>
13371345 <div data-tg-type="aphrodite, ooba" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
13381346 <small data-i18n="No Repeat Ngram Size">No Repeat Ngram Size</small>
13391347 <input class="neo-range-slider" type="range" id="no_repeat_ngram_size_textgenerationwebui" name="volume" min="0" max="20" step="1">
13401348 <input class="neo-range-input" type="number" min="0" max="20" step="1" data-for="no_repeat_ngram_size_textgenerationwebui" id="no_repeat_ngram_size_counter_textgenerationwebui">
13411349 </div>
13421350 <div data-tg-type="tabby, aphrodite" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
13431351 <small data-i18n="Skew">Skew</small>
13441352 <input class="neo-range-slider" type="range" id="skew_textgenerationwebui" name="volume" min="-5" max="5" step="0.01" />
13451353 <input class="neo-range-input" type="number" min="-5" max="5" step="0.01" data-for="skew_textgenerationwebui" id="skew_counter_textgenerationwebui">
@@ -1394,7 +1402,7 @@
13941402 </div>
13951403 </div>
13961404
13971405 <div data-tg-type="aphrodite, ooba, koboldcpp, tabby, llamacpp" id="dryBlock" class="wide100p">
13981406 <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">
13991407 <label data-i18n="DRY Repetition Penalty">DRY Repetition Penalty</label>
14001408 <a href="https://github.com/oobabooga/text-generation-webui/pull/5677" target="_blank">
@@ -1951,15 +1959,6 @@
19511959 Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.
19521960 </span>
19531961 </div>
1954- <div id="claude_human_sysprompt_message_block" class="wide100p">
1955- <div class="range-block-title openai_restorable">
1956- <span data-i18n="User first message">User first message</span>
1957- <div id="claude_human_sysprompt_message_restore" title="Restore User first message" data-i18n="[title]Restore User first message" class="right_menu_button">
1958- <div class="fa-solid fa-clock-rotate-left"></div>
1959- </div>
1960- </div>
1961- <textarea id="claude_human_sysprompt_textarea" class="text_pole textarea_compact autoSetHeight" rows="2" 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>
1962- </div>
19631962 </div>
19641963 </div>
19651964 <div class="range-block m-t-1" data-source="openai,openrouter,scale,custom">
@@ -2805,9 +2804,6 @@
28052804 <option value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>
28062805 <option value="claude-2.1">claude-2.1</option>
28072806 <option value="claude-2.0">claude-2.0</option>
2808- <option value="claude-1.3">claude-1.3</option>
2809- <option value="claude-instant-1.2">claude-instant-1.2</option>
2810- <option value="claude-instant-1.1">claude-instant-1.1</option>
28112807 </optgroup>
28122808 </select>
28132809 </div>
public/script.js+30 -11
@@ -2707,8 +2707,7 @@ export async function generateQuietPrompt(quiet_prompt, quietToLoud, skipWIAN, q
27072707 quietName: quietName,
27082708 };
27092709 originalResponseLength = responseLengthCustomized ? saveResponseLength(main_api, responseLength) : -1;
27102710 const generateFinished =return await Generate('quiet', options);
2711- return generateFinished;
27122711 } finally {
27132712 if (responseLengthCustomized) {
27142713 restoreResponseLength(main_api, originalResponseLength);
@@ -3363,9 +3362,9 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy
33633362
33643363 let data = {};
33653364
33663365 if (api === 'koboldhorde') {
33673366 data = await generateHorde(prompt, generateData, abortController.signal, false);
33683367 } else if (api === 'openai') {
33693368 data = await sendOpenAIRequest('quiet', generateData, abortController.signal);
33703369 } else {
33713370 const generateUrl = getGenerateUrl(api);
@@ -3378,13 +3377,15 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy
33783377 });
33793378
33803379 if (!response.ok) {
33813380 const error =throw await response.json();
3382- throw error;
33833381 }
33843382
33853383 data = await response.json();
33863384 }
33873385
3386+ // should only happen for text completions
3387+ // other frontend paths do not return data if calling the backend fails,
3388+ // they throw things instead
33883389 if (data.error) {
33893390 throw new Error(data.response);
33903391 }
@@ -4436,6 +4437,11 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
44364437 return Promise.resolve();
44374438 }
44384439
4440+ /**
4441+ * Saves itemized prompt bits and calls streaming or non-streaming generation API.
4442+ * @returns {Promise<void|*|Awaited<*>|String|{fromStream}|string|undefined|Object>}
4443+ * @throws {Error|object} Error with message text, or Error with response JSON (OAI/Horde), or the actual response JSON (novel|textgenerationwebui|kobold)
4444+ */
44394445 async function finishGenerating() {
44404446 if (power_user.console_log_prompts) {
44414447 console.log(generate_data.prompt);
@@ -4547,6 +4553,12 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
45474553
45484554 return finishGenerating().then(onSuccess, onError);
45494555
4556+ /**
4557+ * Handles the successful response from the generation API.
4558+ * @param data
4559+ * @returns {Promise<String|{fromStream}|*|string|string|void|Awaited<*>|undefined>}
4560+ * @throws {Error} Throws an error if the response data contains an error message
4561+ */
45504562 async function onSuccess(data) {
45514563 if (!data) return;
45524564
@@ -4556,6 +4568,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
45564568
45574569 let messageChunk = '';
45584570
4571+ // if an error was returned in data (textgenwebui), show it and throw it
45594572 if (data.error) {
45604573 unblockGeneration(type);
45614574 generatedPromptCache = '';
@@ -4670,9 +4683,15 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
46704683 return Object.defineProperty(new String(getMessage), 'messageChunk', { value: messageChunk });
46714684 }
46724685
4686+ /**
4687+ * Exception handler for finishGenerating
4688+ * @param {Error|object} exception Error or response JSON
4689+ * @throws {Error|object} Re-throws the exception
4690+ */
46734691 function onError(exception) {
4692+ // if the response JSON was thrown (novel|textgenerationwebui|kobold), show the error message
46744693 if (typeof exception?.error?.message === 'string') {
46754694 toastr.error(exception.error.message, t`ErrorText generation error`, { timeOut: 10000, extendedTimeOut: 20000 });
46764695 }
46774696
46784697 generatedPromptCache = '';
@@ -5340,6 +5359,7 @@ function setInContextMessages(lastmsg, type) {
53405359 * @param {string} type Generation type
53415360 * @param {object} data Generation data
53425361 * @returns {Promise<object>} Response data from the API
5362+ * @throws {Error|object}
53435363 */
53445364export async function sendGenerationRequest(type, data) {
53455365 if (main_api === 'openai') {
@@ -5359,12 +5379,10 @@ export async function sendGenerationRequest(type, data) {
53595379 });
53605380
53615381 if (!response.ok) {
53625382 const error =throw await response.json();
5363- throw error;
53645383 }
53655384
53665385 const responseData =return await response.json();
5367- return responseData;
53685386}
53695387
53705388/**
@@ -5396,6 +5414,7 @@ export async function sendStreamingRequest(type, data) {
53965414 * Gets the generation endpoint URL for the specified API.
53975415 * @param {string} api API name
53985416 * @returns {string} Generation URL
5417+ * @throws {Error} If the API is unknown
53995418 */
54005419function getGenerateUrl(api) {
54015420 switch (api) {
public/scripts/chat-templates.js+5 -0
@@ -65,6 +65,11 @@ const parse_derivation = derivation => (typeof derivation === 'string') ? {
6565} : derivation;
6666
6767export async function deriveTemplatesFromChatTemplate(chat_template, hash) {
68+ if (chat_template.trim() === '') {
69+ console.log('Missing chat template.');
70+ return null;
71+ }
72+
6873 if (hash in hash_derivations) {
6974 return parse_derivation(hash_derivations[hash]);
7075 }
public/scripts/extensions/stable-diffusion/index.js+13 -5
@@ -2373,6 +2373,7 @@ function ensureSelectionExists(setting, selector) {
23732373 * @param {string} [message] Chat message
23742374 * @param {function} [callback] Callback function
23752375 * @returns {Promise<string|undefined>} Image path
2376+ * @throws {Error} If the prompt or image generation fails
23762377 */
23772378async function generatePicture(initiator, args, trigger, message, callback) {
23782379 if (!trigger || trigger.trim().length === 0) {
@@ -2391,7 +2392,7 @@ async function generatePicture(initiator, args, trigger, message, callback) {
23912392 trigger = trigger.trim();
23922393 const generationType = getGenerationType(trigger);
23932394 const generationTypeKey = Object.keys(generationMode).find(key => generationMode[key] === generationType);
23942395 console.log(`GenerationImage generation mode ${generationTypeKey} triggered with "${trigger}"`);
23952396
23962397 const quietPrompt = getQuietPrompt(generationType, trigger);
23972398 const context = getContext();
@@ -2428,6 +2429,8 @@ async function generatePicture(initiator, args, trigger, message, callback) {
24282429
24292430 try {
24302431 const combineNegatives = (prefix) => { negativePromptPrefix = combinePrefixes(negativePromptPrefix, prefix); };
2432+
2433+ // generate the text prompt for the image
24312434 const prompt = await getPrompt(generationType, message, trigger, quietPrompt, combineNegatives);
24322435 console.log('Processed image prompt:', prompt);
24332436
@@ -2438,11 +2441,16 @@ async function generatePicture(initiator, args, trigger, message, callback) {
24382441 args._abortController.addEventListener('abort', stopListener);
24392442 }
24402443
2444+ // generate the image
24412445 imagePath = await sendGenerationRequest(generationType, prompt, negativePromptPrefix, characterName, callback, initiator, abortController.signal);
24422446 } catch (err) {
24432447 console.trace(err);
2444- toastr.error('SD prompt text generation failed. Reason: ' + err, 'Image Generation');
2448+ // errors here are most likely due to text generation failure
2445- throw new Error('SD prompt text generation failed. Reason: ' + err);
2449+ // sendGenerationRequest mostly deals with its own errors
2450+ const reason = err.error?.message || err.message || 'Unknown error';
2451+ const errorText = 'SD prompt text generation failed. ' + reason;
2452+ toastr.error(errorText, 'Image Generation');
2453+ throw new Error(errorText);
24462454 }
24472455 finally {
24482456 $(stopButton).hide();
@@ -2513,7 +2521,7 @@ function restoreOriginalDimensions(savedParams) {
25132521 */
25142522async function getPrompt(generationType, message, trigger, quietPrompt, combineNegatives) {
25152523 let prompt;
2516-
2524+ console.log('getPrompt: Generation mode', generationType, 'triggered with', trigger);
25172525 switch (generationType) {
25182526 case generationMode.RAW_LAST:
25192527 prompt = message || getRawLastMessage();
@@ -2729,7 +2737,7 @@ async function sendGenerationRequest(generationType, prompt, additionalNegativeP
27292737 throw new Error('Endpoint did not return image data.');
27302738 }
27312739 } catch (err) {
27322740 console.error('Image generation request error: ', err);
27332741 toastr.error('Image generation failed. Please try again.' + '\n\n' + String(err), 'Image Generation');
27342742 return;
27352743 }
public/scripts/horde.js+8 -0
@@ -181,6 +181,14 @@ function setContextSizePreview() {
181181 }
182182}
183183
184+/** Generates text using the Horde API.
185+ * @param {string} prompt
186+ * @param params
187+ * @param signal
188+ * @param reportProgress
189+ * @returns {Promise<{text: *, workerName: string}>}
190+ * @throws {Error}
191+ */
184192async function generateHorde(prompt, params, signal, reportProgress) {
185193 validateHordeModel();
186194 delete params.prompt;
public/scripts/openai.js+32 -31
@@ -99,7 +99,6 @@ const default_wi_format = '{0}';
9999const default_new_chat_prompt = '[Start a new Chat]';
100100const default_new_group_chat_prompt = '[Start a new group chat. Group members: {{group}}]';
101101const default_new_example_chat_prompt = '[Example Chat]';
102-const default_claude_human_sysprompt_message = 'Let\'s get started. Please generate your response based on the information and instructions provided above.';
103102const default_continue_nudge_prompt = '[Continue the following message. Do not include ANY parts of the original message. Use capitalization and punctuation as if your reply is a part of the original message: {{lastChatMessage}}]';
104103const default_bias = 'Default (none)';
105104const default_personality_format = '[{{char}}\'s personality: {{personality}}]';
@@ -276,7 +275,6 @@ const default_settings = {
276275 proxy_password: '',
277276 assistant_prefill: '',
278277 assistant_impersonation: '',
279- human_sysprompt_message: default_claude_human_sysprompt_message,
280278 claude_use_sysprompt: false,
281279 use_makersuite_sysprompt: true,
282280 use_alt_scale: false,
@@ -353,7 +351,6 @@ const oai_settings = {
353351 proxy_password: '',
354352 assistant_prefill: '',
355353 assistant_impersonation: '',
356- human_sysprompt_message: default_claude_human_sysprompt_message,
357354 claude_use_sysprompt: false,
358355 use_makersuite_sysprompt: true,
359356 use_alt_scale: false,
@@ -1313,6 +1310,11 @@ export async function prepareOpenAIMessages({
13131310 return [chat, promptManager.tokenHandler.counts];
13141311}
13151312
1313+/**
1314+ * Handles errors during streaming requests.
1315+ * @param {Response} response
1316+ * @param {string} decoded - response text or decoded stream data
1317+ */
13161318function tryParseStreamingError(response, decoded) {
13171319 try {
13181320 const data = JSON.parse(decoded);
@@ -1324,6 +1326,9 @@ function tryParseStreamingError(response, decoded) {
13241326 checkQuotaError(data);
13251327 checkModerationError(data);
13261328
1329+ // these do not throw correctly (equiv to Error("[object Object]"))
1330+ // if trying to fix "[object Object]" displayed to users, start here
1331+
13271332 if (data.error) {
13281333 toastr.error(data.error.message || response.statusText, 'Chat Completion API');
13291334 throw new Error(data);
@@ -1339,15 +1344,22 @@ function tryParseStreamingError(response, decoded) {
13391344 }
13401345}
13411346
1342-async function checkQuotaError(data) {
1347+/**
1343- const errorText = await renderTemplateAsync('quotaError');
1348+ * Checks if the response contains a quota error and displays a popup if it does.
1344-
1349+ * @param data
1350+ * @returns {void}
1351+ * @throws {object} - response JSON
1352+ */
1353+function checkQuotaError(data) {
13451354 if (!data) {
13461355 return;
13471356 }
13481357
13491358 if (data.quota_error) {
1350- callPopup(errorText, 'text');
1359+ renderTemplateAsync('quotaError').then((html) => Popup.show.text('Quota Error', html));
1360+
1361+ // this does not throw correctly (equiv to Error("[object Object]"))
1362+ // if trying to fix "[object Object]" displayed to users, start here
13511363 throw new Error(data);
13521364 }
13531365}
@@ -1766,6 +1778,15 @@ async function sendAltScaleRequest(messages, logit_bias, signal, type) {
17661778 return data.output;
17671779}
17681780
1781+/**
1782+ * Send a chat completion request to backend
1783+ * @param {string} type (impersonate, quiet, continue, etc)
1784+ * @param {Array} messages
1785+ * @param {AbortSignal?} signal
1786+ * @returns {Promise<unknown>}
1787+ * @throws {Error}
1788+ */
1789+
17691790async function sendOpenAIRequest(type, messages, signal) {
17701791 // Provide default abort signal
17711792 if (!signal) {
@@ -1868,7 +1889,6 @@ async function sendOpenAIRequest(type, messages, signal) {
18681889 generate_data['top_k'] = Number(oai_settings.top_k_openai);
18691890 generate_data['claude_use_sysprompt'] = oai_settings.claude_use_sysprompt;
18701891 generate_data['stop'] = getCustomStoppingStrings(); // Claude shouldn't have limits on stop strings.
1871- generate_data['human_sysprompt_message'] = substituteParams(oai_settings.human_sysprompt_message);
18721892 // Don't add a prefill on quiet gens (summarization) and when using continue prefill.
18731893 if (!isQuiet && !(isContinue && oai_settings.continue_prefill)) {
18741894 generate_data['assistant_prefill'] = isImpersonate ? substituteParams(oai_settings.assistant_impersonation) : substituteParams(oai_settings.assistant_prefill);
@@ -2028,12 +2048,13 @@ async function sendOpenAIRequest(type, messages, signal) {
20282048 else {
20292049 const data = await response.json();
20302050
20312051 await checkQuotaError(data);
20322052 checkModerationError(data);
20332053
20342054 if (data.error) {
20352055 toastr.error(const message = data.error.message || response.statusText, || t`API returned anUnknown error`);
2036- throw new Error(data);
2056+ toastr.error(message, t`API returned an error`);
2057+ throw new Error(message);
20372058 }
20382059
20392060 if (type !== 'quiet') {
@@ -3005,7 +3026,6 @@ function loadOpenAISettings(data, settings) {
30053026 oai_settings.proxy_password = settings.proxy_password ?? default_settings.proxy_password;
30063027 oai_settings.assistant_prefill = settings.assistant_prefill ?? default_settings.assistant_prefill;
30073028 oai_settings.assistant_impersonation = settings.assistant_impersonation ?? default_settings.assistant_impersonation;
3008- oai_settings.human_sysprompt_message = settings.human_sysprompt_message ?? default_settings.human_sysprompt_message;
30093029 oai_settings.image_inlining = settings.image_inlining ?? default_settings.image_inlining;
30103030 oai_settings.inline_image_quality = settings.inline_image_quality ?? default_settings.inline_image_quality;
30113031 oai_settings.bypass_status_check = settings.bypass_status_check ?? default_settings.bypass_status_check;
@@ -3045,7 +3065,6 @@ function loadOpenAISettings(data, settings) {
30453065 $('#openai_proxy_password').val(oai_settings.proxy_password);
30463066 $('#claude_assistant_prefill').val(oai_settings.assistant_prefill);
30473067 $('#claude_assistant_impersonation').val(oai_settings.assistant_impersonation);
3048- $('#claude_human_sysprompt_textarea').val(oai_settings.human_sysprompt_message);
30493068 $('#openai_image_inlining').prop('checked', oai_settings.image_inlining);
30503069 $('#openai_bypass_status_check').prop('checked', oai_settings.bypass_status_check);
30513070
@@ -3375,7 +3394,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
33753394 show_external_models: settings.show_external_models,
33763395 assistant_prefill: settings.assistant_prefill,
33773396 assistant_impersonation: settings.assistant_impersonation,
3378- human_sysprompt_message: settings.human_sysprompt_message,
33793397 claude_use_sysprompt: settings.claude_use_sysprompt,
33803398 use_makersuite_sysprompt: settings.use_makersuite_sysprompt,
33813399 use_alt_scale: settings.use_alt_scale,
@@ -3800,7 +3818,6 @@ function onSettingsPresetChange() {
38003818 proxy_password: ['#openai_proxy_password', 'proxy_password', false],
38013819 assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],
38023820 assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false],
3803- human_sysprompt_message: ['#claude_human_sysprompt_textarea', 'human_sysprompt_message', false],
38043821 claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true],
38053822 use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true],
38063823 use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],
@@ -4652,10 +4669,6 @@ function toggleChatCompletionForms() {
46524669 const validSources = $(this).data('source').split(',');
46534670 $(this).toggle(validSources.includes(oai_settings.chat_completion_source));
46544671 });
4655-
4656- if (chat_completion_sources.CLAUDE == oai_settings.chat_completion_source) {
4657- $('#claude_human_sysprompt_message_block').toggle(oai_settings.claude_use_sysprompt);
4658- }
46594672}
46604673
46614674async function testApiConnection() {
@@ -5011,7 +5024,6 @@ export function initOpenAI() {
50115024
50125025 $('#claude_use_sysprompt').on('change', function () {
50135026 oai_settings.claude_use_sysprompt = !!$('#claude_use_sysprompt').prop('checked');
5014- $('#claude_human_sysprompt_message_block').toggle(oai_settings.claude_use_sysprompt);
50155027 saveSettingsDebounced();
50165028 });
50175029
@@ -5088,12 +5100,6 @@ export function initOpenAI() {
50885100 saveSettingsDebounced();
50895101 });
50905102
5091- $('#claude_human_sysprompt_message_restore').on('click', function () {
5092- oai_settings.human_sysprompt_message = default_claude_human_sysprompt_message;
5093- $('#claude_human_sysprompt_textarea').val(oai_settings.human_sysprompt_message);
5094- saveSettingsDebounced();
5095- });
5096-
50975103 $('#newgroupchat_prompt_restore').on('click', function () {
50985104 oai_settings.new_group_chat_prompt = default_new_group_chat_prompt;
50995105 $('#newgroupchat_prompt_textarea').val(oai_settings.new_group_chat_prompt);
@@ -5185,11 +5191,6 @@ export function initOpenAI() {
51855191 saveSettingsDebounced();
51865192 });
51875193
5188- $('#claude_human_sysprompt_textarea').on('input', function () {
5189- oai_settings.human_sysprompt_message = String($('#claude_human_sysprompt_textarea').val());
5190- saveSettingsDebounced();
5191- });
5192-
51935194 $('#openrouter_use_fallback').on('input', function () {
51945195 oai_settings.openrouter_use_fallback = !!$(this).prop('checked');
51955196 saveSettingsDebounced();
public/scripts/power-user.js+5 -0
@@ -658,6 +658,10 @@ async function CreateZenSliders(elmnt) {
658658 numSteps = 50;
659659 decimals = 1;
660660 }
661+ if (sliderID == 'nsigma') {
662+ numSteps = 50;
663+ decimals = 1;
664+ }
661665 //customize steps
662666 if (sliderID == 'mirostat_mode_textgenerationwebui' ||
663667 sliderID == 'mirostat_mode_kobold') {
@@ -702,6 +706,7 @@ async function CreateZenSliders(elmnt) {
702706 sliderID == 'penalty_alpha_textgenerationwebui' ||
703707 sliderID == 'length_penalty_textgenerationwebui' ||
704708 sliderID == 'epsilon_cutoff_textgenerationwebui' ||
709+ sliderID == 'nsigma' ||
705710 sliderID == 'rep_pen_range' ||
706711 sliderID == 'eta_cutoff_textgenerationwebui' ||
707712 sliderID == 'top_a_textgenerationwebui' ||
public/scripts/slash-commands/SlashCommandBrowser.js+6 -7
@@ -1,5 +1,4 @@
11import { escapeRegex } from '../utils.js';
2-import { SlashCommand } from './SlashCommand.js';
32import { SlashCommandParser } from './SlashCommandParser.js';
43
54export class SlashCommandBrowser {
@@ -30,7 +29,7 @@ export class SlashCommandBrowser {
3029 this.details?.remove();
3130 this.details = null;
3231 let query = inp.value.trim();
3332 if (query.slice(-1) === '"' && !/(?:^|\s+)"/.test(query)) {
3433 query = `"${query}`;
3534 }
3635 let fuzzyList = [];
@@ -59,7 +58,7 @@ export class SlashCommandBrowser {
5958 cmd.helpString,
6059 ];
6160 const find = ()=>targets.find(t=>(fuzzyList.find(f=>f.test(t)) ?? quotedList.find(q=>t.includes(q))) !== undefined) !== undefined;
6261 if (fuzzyList.length + quotedList.length === 0 || find()) {
6362 this.itemMap[cmd.name].classList.remove('isFiltered');
6463 } else {
6564 this.itemMap[cmd.name].classList.add('isFiltered');
@@ -78,7 +77,7 @@ export class SlashCommandBrowser {
7877 list.classList.add('autoComplete');
7978 this.cmdList = Object
8079 .keys(SlashCommandParser.commands)
8180 .filter(key => SlashCommandParser.commands[key].name === key) // exclude aliases
8281 .sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
8382 .map(key => SlashCommandParser.commands[key])
8483 ;
@@ -97,7 +96,7 @@ export class SlashCommandBrowser {
9796 }
9897 }
9998 }
10099 if (this.details !== details) {
101100 Array.from(list.querySelectorAll('.selected')).forEach(it=>it.classList.remove('selected'));
102101 item.classList.add('selected');
103102 this.details?.remove();
@@ -124,7 +123,7 @@ export class SlashCommandBrowser {
124123 parent.append(this.dom);
125124
126125 this.mo = new MutationObserver(muts=>{
127126 if (muts.find(mut=>Array.from(mut.removedNodes).find(it=>it === this.dom || it.contains(this.dom)))) {
128127 this.mo.disconnect();
129128 window.removeEventListener('keydown', boundHandler);
130129 }
@@ -136,7 +135,7 @@ export class SlashCommandBrowser {
136135 }
137136
138137 handleKeyDown(evt) {
139138 if (!evt.shiftKey && !evt.altKey && evt.ctrlKey && evt.key.toLowerCase() === 'f') {
140139 if (!this.dom.closest('body')) return;
141140 if (this.dom.closest('.mes') && !this.dom.closest('.last_mes')) return;
142141 evt.preventDefault();
public/scripts/textgen-settings.js+13 -0
@@ -193,6 +193,7 @@ const settings = {
193193 openrouter_allow_fallbacks: true,
194194 xtc_threshold: 0.1,
195195 xtc_probability: 0,
196+ nsigma: 0.0,
196197 featherless_model: '',
197198};
198199
@@ -265,6 +266,7 @@ export const setting_names = [
265266 'openrouter_allow_fallbacks',
266267 'xtc_threshold',
267268 'xtc_probability',
269+ 'nsigma',
268270];
269271
270272const DYNATEMP_BLOCK = document.getElementById('dynatemp_block_ooba');
@@ -880,6 +882,13 @@ function setSettingByName(setting, value, trigger) {
880882 }
881883}
882884
885+/**
886+ * Sends a streaming request for textgenerationwebui.
887+ * @param generate_data
888+ * @param signal
889+ * @returns {Promise<(function(): AsyncGenerator<{swipes: [], text: string, toolCalls: [], logprobs: {token: string, topLogprobs: Candidate[]}|null}, void, *>)|*>}
890+ * @throws {Error} - If the response status is not OK, or from within the generator
891+ */
883892async function generateTextGenWithStreaming(generate_data, signal) {
884893 generate_data.stream = true;
885894
@@ -995,6 +1004,7 @@ export function parseTabbyLogprobs(data) {
9951004 * @param {Response} response - Response from the server.
9961005 * @param {string} decoded - Decoded response body.
9971006 * @returns {void} Nothing.
1007+ * @throws {Error} If the response contains an error message, throws Error with the message.
9981008 */
9991009function tryParseStreamingError(response, decoded) {
10001010 let data = {};
@@ -1178,6 +1188,7 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
11781188 'sampler_order': settings.type === textgen_types.KOBOLDCPP ? settings.sampler_order : undefined,
11791189 'xtc_threshold': settings.xtc_threshold,
11801190 'xtc_probability': settings.xtc_probability,
1191+ 'nsigma': settings.nsigma,
11811192 };
11821193 const nonAphroditeParams = {
11831194 'rep_pen': settings.rep_pen,
@@ -1245,7 +1256,9 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
12451256 'dynatemp_exponent': dynatemp ? settings.dynatemp_exponent : undefined,
12461257 'xtc_threshold': settings.xtc_threshold,
12471258 'xtc_probability': settings.xtc_probability,
1259+ 'nsigma': settings.nsigma,
12481260 'custom_token_bans': toIntArray(banned_tokens),
1261+ 'no_repeat_ngram_size': settings.no_repeat_ngram_size,
12491262 };
12501263
12511264 if (settings.type === OPENROUTER) {
src/endpoints/backends/chat-completions.js+7 -3
@@ -102,7 +102,7 @@ async function sendClaudeRequest(request, response) {
102102 const additionalHeaders = {};
103103 const useTools = request.body.model.startsWith('claude-3') && Array.isArray(request.body.tools) && request.body.tools.length > 0;
104104 const useSystemPrompt = (request.body.model.startsWith('claude-2') || request.body.model.startsWith('claude-3')) && request.body.claude_use_sysprompt;
105105 const convertedPrompt = convertClaudeMessages(request.body.messages, request.body.assistant_prefill, useSystemPrompt, useTools, request.body.human_sysprompt_message, request.body.char_name, request.body.user_name);
106106 // Add custom stop sequences
107107 const stopSequences = [];
108108 if (Array.isArray(request.body.stop)) {
@@ -1051,8 +1051,12 @@ router.post('/generate', jsonParser, function (request, response) {
10511051 }
10521052 } catch (error) {
10531053 console.log('Generation failed', error);
1054+ const message = error.code === 'ECONNREFUSED'
1055+ ? `Connection refused: ${error.message}`
1056+ : error.message || 'Unknown error occurred';
1057+
10541058 if (!response.headersSent) {
10551059 response.status(502).send({ error: true{ message, ...error } });
10561060 } else {
10571061 response.end();
10581062 }
@@ -1068,7 +1072,7 @@ router.post('/generate', jsonParser, function (request, response) {
10681072
10691073 const message = errorResponse.statusText || 'Unknown error occurred';
10701074 const quota_error = errorResponse.status === 429 && errorData?.error?.type === 'insufficient_quota';
10711075 console.log('Chat completion request error: ', message, responseText);
10721076
10731077 if (!response.headersSent) {
10741078 response.send({ error: { message }, quota_error: quota_error });
src/endpoints/characters.js+3 -1
@@ -24,6 +24,8 @@ const defaultAvatarPath = './public/img/ai4.png';
2424
2525// KV-store for parsed character data
2626const characterDataCache = new Map();
27+// Some Android devices require tighter memory management
28+const isAndroid = process.platform === 'android';
2729
2830/**
2931 * Reads the character card from the specified image file.
@@ -39,7 +41,7 @@ async function readCharacterData(inputFile, inputFormat = 'png') {
3941 }
4042
4143 const result = parse(inputFile, inputFormat);
4244 !isAndroid && characterDataCache.set(cacheKey, result);
4345 return result;
4446}
4547
src/middleware/webpack-serve.js+1 -1
@@ -6,7 +6,7 @@ import { publicLibConfig } from '../../webpack.config.js';
66export default function getWebpackServeMiddleware() {
77 const compiler = webpack(publicLibConfig);
88
99 if (process.env.NODE_ENV === 'production' || process.platform === 'android') {
1010 compiler.hooks.done.tap('serve', () => {
1111 if (compiler.watching) {
1212 compiler.watching.close(() => { });
src/prompt-converters.js+3 -4
@@ -91,11 +91,10 @@ export function convertClaudePrompt(messages, addAssistantPostfix, addAssistantP
9191 * @param {string} prefillString User determined prefill string
9292 * @param {boolean} useSysPrompt See if we want to use a system prompt
9393 * @param {boolean} useTools See if we want to use tools
94- * @param {string} humanMsgFix Add Human message between system prompt and assistant.
9594 * @param {string} charName Character name
9695 * @param {string} userName User name
9796 */
9897export function convertClaudeMessages(messages, prefillString, useSysPrompt, useTools, humanMsgFix, charName = '', userName = '') {
9998 let systemPrompt = [];
10099 if (useSysPrompt) {
101100 // Collect all the system messages up until the first instance of a non-system message, and then remove them from the messages array.
@@ -122,10 +121,10 @@ export function convertClaudeMessages(messages, prefillString, useSysPrompt, use
122121
123122 // Check if the first message in the array is of type user, if not, interject with humanMsgFix or a blank message.
124123 // Also prevents erroring out if the messages array is empty.
125124 if (messages.length === 0 || (messages.length > 0 && messages[0].role !== 'user')) {
126125 messages.unshift({
127126 role: 'user',
128127 content: humanMsgFix || PROMPT_PLACEHOLDER,
129128 });
130129 }
131130 }