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

78c55558af3b6a57a484131916a8ba8c33e50452

Joe <joenunezb@gmail.com>

15 files changed, +177 -79Ignore whitespace
.github/readme-ru_ru.md+40 -0
@@ -229,6 +229,46 @@ SillyTavern сохраняет ключи от ваших API в файле `sec
2291. Зайдите в файл `config.yaml` и установите `allowKeysExposure` в положение `true`.2291. Зайдите в файл `config.yaml` и установите `allowKeysExposure` в положение `true`.
2302. Перезапустите сервер SillyTavern.2302. Перезапустите сервер SillyTavern.
231231
232## Аргументы командной строки
233
234Вы можете передавать аргументы командной строки при запуске сервера SillyTavern, чтобы переопределять настройки из `config.yaml`.
235
236### Примеры
237
238```shell
239node server.js --port 8000 --listen false
240# или
241npm run start -- --port 8000 --listen false
242# или (только на Windows)
243Start.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
232## Удалённое подключение272## Удалённое подключение
233273
234В основном этим пользуются тогда, когда хотят использовать SillyTavern с телефона, запустив сервер SillyTavern на стационарном ПК в той же Wi-Fi-сети.274В основном этим пользуются тогда, когда хотят использовать SillyTavern с телефона, запустив сервер SillyTavern на стационарном ПК в той же Wi-Fi-сети.
default/content/presets/openai/Default.json+0 -1
@@ -230,7 +230,6 @@
230 "show_external_models": false,230 "show_external_models": false,
231 "assistant_prefill": "",231 "assistant_prefill": "",
232 "assistant_impersonation": "",232 "assistant_impersonation": "",
233 "human_sysprompt_message": "Let's get started. Please generate your response based on the information and instructions provided above.",
234 "claude_use_sysprompt": false,233 "claude_use_sysprompt": false,
235 "use_alt_scale": false,234 "use_alt_scale": false,
236 "squash_system_messages": false,235 "squash_system_messages": false,
public/index.html+11 -15
@@ -1291,6 +1291,14 @@
1291 <input class="neo-range-slider" type="range" id="epsilon_cutoff_textgenerationwebui" name="volume" min="0" max="9" step="0.01">1291 <input class="neo-range-slider" type="range" id="epsilon_cutoff_textgenerationwebui" name="volume" min="0" max="9" step="0.01">
1292 <input class="neo-range-input" type="number" min="0" max="9" step="0.01" data-for="epsilon_cutoff_textgenerationwebui" id="epsilon_cutoff_counter_textgenerationwebui">1292 <input class="neo-range-input" type="number" min="0" max="9" step="0.01" data-for="epsilon_cutoff_textgenerationwebui" id="epsilon_cutoff_counter_textgenerationwebui">
1293 </div>1293 </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>
1294 <div data-tg-type="ooba,mancer,aphrodite" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">1302 <div data-tg-type="ooba,mancer,aphrodite" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
1295 <small>1303 <small>
1296 <span data-i18n="Eta Cutoff">Eta Cutoff</span>1304 <span data-i18n="Eta Cutoff">Eta Cutoff</span>
@@ -1334,12 +1342,12 @@
1334 <input class="neo-range-slider" type="range" id="presence_pen_textgenerationwebui" name="volume" min="-2" max="2" step="0.01" />1342 <input class="neo-range-slider" type="range" id="presence_pen_textgenerationwebui" name="volume" min="-2" max="2" step="0.01" />
1335 <input class="neo-range-input" type="number" min="-2" max="2" step="0.01" data-for="presence_pen_textgenerationwebui" id="presence_pen_counter_textgenerationwebui">1343 <input class="neo-range-input" type="number" min="-2" max="2" step="0.01" data-for="presence_pen_textgenerationwebui" id="presence_pen_counter_textgenerationwebui">
1336 </div>1344 </div>
1337 <div data-tg-type="ooba" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">1345 <div data-tg-type="aphrodite, ooba" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
1338 <small data-i18n="No Repeat Ngram Size">No Repeat Ngram Size</small>1346 <small data-i18n="No Repeat Ngram Size">No Repeat Ngram Size</small>
1339 <input class="neo-range-slider" type="range" id="no_repeat_ngram_size_textgenerationwebui" name="volume" min="0" max="20" step="1">1347 <input class="neo-range-slider" type="range" id="no_repeat_ngram_size_textgenerationwebui" name="volume" min="0" max="20" step="1">
1340 <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">1348 <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">
1341 </div>1349 </div>
1342 <div data-tg-type="tabby" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">1350 <div data-tg-type="tabby, aphrodite" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
1343 <small data-i18n="Skew">Skew</small>1351 <small data-i18n="Skew">Skew</small>
1344 <input class="neo-range-slider" type="range" id="skew_textgenerationwebui" name="volume" min="-5" max="5" step="0.01" />1352 <input class="neo-range-slider" type="range" id="skew_textgenerationwebui" name="volume" min="-5" max="5" step="0.01" />
1345 <input class="neo-range-input" type="number" min="-5" max="5" step="0.01" data-for="skew_textgenerationwebui" id="skew_counter_textgenerationwebui">1353 <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 @@
1394 </div>1402 </div>
1395 </div>1403 </div>
13961404
1397 <div data-tg-type="ooba, koboldcpp, tabby, llamacpp" id="dryBlock" class="wide100p">1405 <div data-tg-type="aphrodite, ooba, koboldcpp, tabby, llamacpp" id="dryBlock" class="wide100p">
1398 <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">1406 <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">
1399 <label data-i18n="DRY Repetition Penalty">DRY Repetition Penalty</label>1407 <label data-i18n="DRY Repetition Penalty">DRY Repetition Penalty</label>
1400 <a href="https://github.com/oobabooga/text-generation-webui/pull/5677" target="_blank">1408 <a href="https://github.com/oobabooga/text-generation-webui/pull/5677" target="_blank">
@@ -1951,15 +1959,6 @@
1951 Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.1959 Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.
1952 </span>1960 </span>
1953 </div>1961 </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>
1963 </div>1962 </div>
1964 </div>1963 </div>
1965 <div class="range-block m-t-1" data-source="openai,openrouter,scale,custom">1964 <div class="range-block m-t-1" data-source="openai,openrouter,scale,custom">
@@ -2805,9 +2804,6 @@
2805 <option value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>2804 <option value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>
2806 <option value="claude-2.1">claude-2.1</option>2805 <option value="claude-2.1">claude-2.1</option>
2807 <option value="claude-2.0">claude-2.0</option>2806 <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>
2811 </optgroup>2807 </optgroup>
2812 </select>2808 </select>
2813 </div>2809 </div>
public/script.js+30 -11
@@ -2707,8 +2707,7 @@ export async function generateQuietPrompt(quiet_prompt, quietToLoud, skipWIAN, q
2707 quietName: quietName,2707 quietName: quietName,
2708 };2708 };
2709 originalResponseLength = responseLengthCustomized ? saveResponseLength(main_api, responseLength) : -1;2709 originalResponseLength = responseLengthCustomized ? saveResponseLength(main_api, responseLength) : -1;
2710 const generateFinished = await Generate('quiet', options);2710 return await Generate('quiet', options);
2711 return generateFinished;
2712 } finally {2711 } finally {
2713 if (responseLengthCustomized) {2712 if (responseLengthCustomized) {
2714 restoreResponseLength(main_api, originalResponseLength);2713 restoreResponseLength(main_api, originalResponseLength);
@@ -3363,9 +3362,9 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy
33633362
3364 let data = {};3363 let data = {};
33653364
3366 if (api == 'koboldhorde') {3365 if (api === 'koboldhorde') {
3367 data = await generateHorde(prompt, generateData, abortController.signal, false);3366 data = await generateHorde(prompt, generateData, abortController.signal, false);
3368 } else if (api == 'openai') {3367 } else if (api === 'openai') {
3369 data = await sendOpenAIRequest('quiet', generateData, abortController.signal);3368 data = await sendOpenAIRequest('quiet', generateData, abortController.signal);
3370 } else {3369 } else {
3371 const generateUrl = getGenerateUrl(api);3370 const generateUrl = getGenerateUrl(api);
@@ -3378,13 +3377,15 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy
3378 });3377 });
33793378
3380 if (!response.ok) {3379 if (!response.ok) {
3381 const error = await response.json();3380 throw await response.json();
3382 throw error;
3383 }3381 }
33843382
3385 data = await response.json();3383 data = await response.json();
3386 }3384 }
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
3388 if (data.error) {3389 if (data.error) {
3389 throw new Error(data.response);3390 throw new Error(data.response);
3390 }3391 }
@@ -4436,6 +4437,11 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4436 return Promise.resolve();4437 return Promise.resolve();
4437 }4438 }
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 */
4439 async function finishGenerating() {4445 async function finishGenerating() {
4440 if (power_user.console_log_prompts) {4446 if (power_user.console_log_prompts) {
4441 console.log(generate_data.prompt);4447 console.log(generate_data.prompt);
@@ -4547,6 +4553,12 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
45474553
4548 return finishGenerating().then(onSuccess, onError);4554 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 */
4550 async function onSuccess(data) {4562 async function onSuccess(data) {
4551 if (!data) return;4563 if (!data) return;
45524564
@@ -4556,6 +4568,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
45564568
4557 let messageChunk = '';4569 let messageChunk = '';
45584570
4571 // if an error was returned in data (textgenwebui), show it and throw it
4559 if (data.error) {4572 if (data.error) {
4560 unblockGeneration(type);4573 unblockGeneration(type);
4561 generatedPromptCache = '';4574 generatedPromptCache = '';
@@ -4670,9 +4683,15 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4670 return Object.defineProperty(new String(getMessage), 'messageChunk', { value: messageChunk });4683 return Object.defineProperty(new String(getMessage), 'messageChunk', { value: messageChunk });
4671 }4684 }
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 */
4673 function onError(exception) {4691 function onError(exception) {
4692 // if the response JSON was thrown (novel|textgenerationwebui|kobold), show the error message
4674 if (typeof exception?.error?.message === 'string') {4693 if (typeof exception?.error?.message === 'string') {
4675 toastr.error(exception.error.message, t`Error`, { timeOut: 10000, extendedTimeOut: 20000 });4694 toastr.error(exception.error.message, t`Text generation error`, { timeOut: 10000, extendedTimeOut: 20000 });
4676 }4695 }
46774696
4678 generatedPromptCache = '';4697 generatedPromptCache = '';
@@ -5340,6 +5359,7 @@ function setInContextMessages(lastmsg, type) {
5340 * @param {string} type Generation type5359 * @param {string} type Generation type
5341 * @param {object} data Generation data5360 * @param {object} data Generation data
5342 * @returns {Promise<object>} Response data from the API5361 * @returns {Promise<object>} Response data from the API
5362 * @throws {Error|object}
5343 */5363 */
5344export async function sendGenerationRequest(type, data) {5364export async function sendGenerationRequest(type, data) {
5345 if (main_api === 'openai') {5365 if (main_api === 'openai') {
@@ -5359,12 +5379,10 @@ export async function sendGenerationRequest(type, data) {
5359 });5379 });
53605380
5361 if (!response.ok) {5381 if (!response.ok) {
5362 const error = await response.json();5382 throw await response.json();
5363 throw error;
5364 }5383 }
53655384
5366 const responseData = await response.json();5385 return await response.json();
5367 return responseData;
5368}5386}
53695387
5370/**5388/**
@@ -5396,6 +5414,7 @@ export async function sendStreamingRequest(type, data) {
5396 * Gets the generation endpoint URL for the specified API.5414 * Gets the generation endpoint URL for the specified API.
5397 * @param {string} api API name5415 * @param {string} api API name
5398 * @returns {string} Generation URL5416 * @returns {string} Generation URL
5417 * @throws {Error} If the API is unknown
5399 */5418 */
5400function getGenerateUrl(api) {5419function getGenerateUrl(api) {
5401 switch (api) {5420 switch (api) {
public/scripts/chat-templates.js+5 -0
@@ -65,6 +65,11 @@ const parse_derivation = derivation => (typeof derivation === 'string') ? {
65} : derivation;65} : derivation;
6666
67export async function deriveTemplatesFromChatTemplate(chat_template, hash) {67export async function deriveTemplatesFromChatTemplate(chat_template, hash) {
68 if (chat_template.trim() === '') {
69 console.log('Missing chat template.');
70 return null;
71 }
72
68 if (hash in hash_derivations) {73 if (hash in hash_derivations) {
69 return parse_derivation(hash_derivations[hash]);74 return parse_derivation(hash_derivations[hash]);
70 }75 }
public/scripts/extensions/stable-diffusion/index.js+13 -5
@@ -2373,6 +2373,7 @@ function ensureSelectionExists(setting, selector) {
2373 * @param {string} [message] Chat message2373 * @param {string} [message] Chat message
2374 * @param {function} [callback] Callback function2374 * @param {function} [callback] Callback function
2375 * @returns {Promise<string|undefined>} Image path2375 * @returns {Promise<string|undefined>} Image path
2376 * @throws {Error} If the prompt or image generation fails
2376 */2377 */
2377async function generatePicture(initiator, args, trigger, message, callback) {2378async function generatePicture(initiator, args, trigger, message, callback) {
2378 if (!trigger || trigger.trim().length === 0) {2379 if (!trigger || trigger.trim().length === 0) {
@@ -2391,7 +2392,7 @@ async function generatePicture(initiator, args, trigger, message, callback) {
2391 trigger = trigger.trim();2392 trigger = trigger.trim();
2392 const generationType = getGenerationType(trigger);2393 const generationType = getGenerationType(trigger);
2393 const generationTypeKey = Object.keys(generationMode).find(key => generationMode[key] === generationType);2394 const generationTypeKey = Object.keys(generationMode).find(key => generationMode[key] === generationType);
2394 console.log(`Generation mode ${generationTypeKey} triggered with "${trigger}"`);2395 console.log(`Image generation mode ${generationTypeKey} triggered with "${trigger}"`);
23952396
2396 const quietPrompt = getQuietPrompt(generationType, trigger);2397 const quietPrompt = getQuietPrompt(generationType, trigger);
2397 const context = getContext();2398 const context = getContext();
@@ -2428,6 +2429,8 @@ async function generatePicture(initiator, args, trigger, message, callback) {
24282429
2429 try {2430 try {
2430 const combineNegatives = (prefix) => { negativePromptPrefix = combinePrefixes(negativePromptPrefix, prefix); };2431 const combineNegatives = (prefix) => { negativePromptPrefix = combinePrefixes(negativePromptPrefix, prefix); };
2432
2433 // generate the text prompt for the image
2431 const prompt = await getPrompt(generationType, message, trigger, quietPrompt, combineNegatives);2434 const prompt = await getPrompt(generationType, message, trigger, quietPrompt, combineNegatives);
2432 console.log('Processed image prompt:', prompt);2435 console.log('Processed image prompt:', prompt);
24332436
@@ -2438,11 +2441,16 @@ async function generatePicture(initiator, args, trigger, message, callback) {
2438 args._abortController.addEventListener('abort', stopListener);2441 args._abortController.addEventListener('abort', stopListener);
2439 }2442 }
24402443
2444 // generate the image
2441 imagePath = await sendGenerationRequest(generationType, prompt, negativePromptPrefix, characterName, callback, initiator, abortController.signal);2445 imagePath = await sendGenerationRequest(generationType, prompt, negativePromptPrefix, characterName, callback, initiator, abortController.signal);
2442 } catch (err) {2446 } catch (err) {
2443 console.trace(err);2447 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);
2446 }2454 }
2447 finally {2455 finally {
2448 $(stopButton).hide();2456 $(stopButton).hide();
@@ -2513,7 +2521,7 @@ function restoreOriginalDimensions(savedParams) {
2513 */2521 */
2514async function getPrompt(generationType, message, trigger, quietPrompt, combineNegatives) {2522async function getPrompt(generationType, message, trigger, quietPrompt, combineNegatives) {
2515 let prompt;2523 let prompt;
25162524 console.log('getPrompt: Generation mode', generationType, 'triggered with', trigger);
2517 switch (generationType) {2525 switch (generationType) {
2518 case generationMode.RAW_LAST:2526 case generationMode.RAW_LAST:
2519 prompt = message || getRawLastMessage();2527 prompt = message || getRawLastMessage();
@@ -2729,7 +2737,7 @@ async function sendGenerationRequest(generationType, prompt, additionalNegativeP
2729 throw new Error('Endpoint did not return image data.');2737 throw new Error('Endpoint did not return image data.');
2730 }2738 }
2731 } catch (err) {2739 } catch (err) {
2732 console.error(err);2740 console.error('Image generation request error: ', err);
2733 toastr.error('Image generation failed. Please try again.' + '\n\n' + String(err), 'Image Generation');2741 toastr.error('Image generation failed. Please try again.' + '\n\n' + String(err), 'Image Generation');
2734 return;2742 return;
2735 }2743 }
public/scripts/horde.js+8 -0
@@ -181,6 +181,14 @@ function setContextSizePreview() {
181 }181 }
182}182}
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 */
184async function generateHorde(prompt, params, signal, reportProgress) {192async function generateHorde(prompt, params, signal, reportProgress) {
185 validateHordeModel();193 validateHordeModel();
186 delete params.prompt;194 delete params.prompt;
public/scripts/openai.js+32 -31
@@ -99,7 +99,6 @@ const default_wi_format = '{0}';
99const default_new_chat_prompt = '[Start a new Chat]';99const default_new_chat_prompt = '[Start a new Chat]';
100const default_new_group_chat_prompt = '[Start a new group chat. Group members: {{group}}]';100const default_new_group_chat_prompt = '[Start a new group chat. Group members: {{group}}]';
101const default_new_example_chat_prompt = '[Example Chat]';101const default_new_example_chat_prompt = '[Example Chat]';
102const default_claude_human_sysprompt_message = 'Let\'s get started. Please generate your response based on the information and instructions provided above.';
103const 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}}]';102const 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}}]';
104const default_bias = 'Default (none)';103const default_bias = 'Default (none)';
105const default_personality_format = '[{{char}}\'s personality: {{personality}}]';104const default_personality_format = '[{{char}}\'s personality: {{personality}}]';
@@ -276,7 +275,6 @@ const default_settings = {
276 proxy_password: '',275 proxy_password: '',
277 assistant_prefill: '',276 assistant_prefill: '',
278 assistant_impersonation: '',277 assistant_impersonation: '',
279 human_sysprompt_message: default_claude_human_sysprompt_message,
280 claude_use_sysprompt: false,278 claude_use_sysprompt: false,
281 use_makersuite_sysprompt: true,279 use_makersuite_sysprompt: true,
282 use_alt_scale: false,280 use_alt_scale: false,
@@ -353,7 +351,6 @@ const oai_settings = {
353 proxy_password: '',351 proxy_password: '',
354 assistant_prefill: '',352 assistant_prefill: '',
355 assistant_impersonation: '',353 assistant_impersonation: '',
356 human_sysprompt_message: default_claude_human_sysprompt_message,
357 claude_use_sysprompt: false,354 claude_use_sysprompt: false,
358 use_makersuite_sysprompt: true,355 use_makersuite_sysprompt: true,
359 use_alt_scale: false,356 use_alt_scale: false,
@@ -1313,6 +1310,11 @@ export async function prepareOpenAIMessages({
1313 return [chat, promptManager.tokenHandler.counts];1310 return [chat, promptManager.tokenHandler.counts];
1314}1311}
13151312
1313/**
1314 * Handles errors during streaming requests.
1315 * @param {Response} response
1316 * @param {string} decoded - response text or decoded stream data
1317 */
1316function tryParseStreamingError(response, decoded) {1318function tryParseStreamingError(response, decoded) {
1317 try {1319 try {
1318 const data = JSON.parse(decoded);1320 const data = JSON.parse(decoded);
@@ -1324,6 +1326,9 @@ function tryParseStreamingError(response, decoded) {
1324 checkQuotaError(data);1326 checkQuotaError(data);
1325 checkModerationError(data);1327 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
1327 if (data.error) {1332 if (data.error) {
1328 toastr.error(data.error.message || response.statusText, 'Chat Completion API');1333 toastr.error(data.error.message || response.statusText, 'Chat Completion API');
1329 throw new Error(data);1334 throw new Error(data);
@@ -1339,15 +1344,22 @@ function tryParseStreamingError(response, decoded) {
1339 }1344 }
1340}1345}
13411346
1342async 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.
13441349 * @param data
1350 * @returns {void}
1351 * @throws {object} - response JSON
1352 */
1353function checkQuotaError(data) {
1345 if (!data) {1354 if (!data) {
1346 return;1355 return;
1347 }1356 }
13481357
1349 if (data.quota_error) {1358 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
1351 throw new Error(data);1363 throw new Error(data);
1352 }1364 }
1353}1365}
@@ -1766,6 +1778,15 @@ async function sendAltScaleRequest(messages, logit_bias, signal, type) {
1766 return data.output;1778 return data.output;
1767}1779}
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
1769async function sendOpenAIRequest(type, messages, signal) {1790async function sendOpenAIRequest(type, messages, signal) {
1770 // Provide default abort signal1791 // Provide default abort signal
1771 if (!signal) {1792 if (!signal) {
@@ -1868,7 +1889,6 @@ async function sendOpenAIRequest(type, messages, signal) {
1868 generate_data['top_k'] = Number(oai_settings.top_k_openai);1889 generate_data['top_k'] = Number(oai_settings.top_k_openai);
1869 generate_data['claude_use_sysprompt'] = oai_settings.claude_use_sysprompt;1890 generate_data['claude_use_sysprompt'] = oai_settings.claude_use_sysprompt;
1870 generate_data['stop'] = getCustomStoppingStrings(); // Claude shouldn't have limits on stop strings.1891 generate_data['stop'] = getCustomStoppingStrings(); // Claude shouldn't have limits on stop strings.
1871 generate_data['human_sysprompt_message'] = substituteParams(oai_settings.human_sysprompt_message);
1872 // Don't add a prefill on quiet gens (summarization) and when using continue prefill.1892 // Don't add a prefill on quiet gens (summarization) and when using continue prefill.
1873 if (!isQuiet && !(isContinue && oai_settings.continue_prefill)) {1893 if (!isQuiet && !(isContinue && oai_settings.continue_prefill)) {
1874 generate_data['assistant_prefill'] = isImpersonate ? substituteParams(oai_settings.assistant_impersonation) : substituteParams(oai_settings.assistant_prefill);1894 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) {
2028 else {2048 else {
2029 const data = await response.json();2049 const data = await response.json();
20302050
2031 await checkQuotaError(data);2051 checkQuotaError(data);
2032 checkModerationError(data);2052 checkModerationError(data);
20332053
2034 if (data.error) {2054 if (data.error) {
2035 toastr.error(data.error.message || response.statusText, t`API returned an error`);2055 const message = data.error.message || response.statusText || t`Unknown error`;
2036 throw new Error(data);2056 toastr.error(message, t`API returned an error`);
2057 throw new Error(message);
2037 }2058 }
20382059
2039 if (type !== 'quiet') {2060 if (type !== 'quiet') {
@@ -3005,7 +3026,6 @@ function loadOpenAISettings(data, settings) {
3005 oai_settings.proxy_password = settings.proxy_password ?? default_settings.proxy_password;3026 oai_settings.proxy_password = settings.proxy_password ?? default_settings.proxy_password;
3006 oai_settings.assistant_prefill = settings.assistant_prefill ?? default_settings.assistant_prefill;3027 oai_settings.assistant_prefill = settings.assistant_prefill ?? default_settings.assistant_prefill;
3007 oai_settings.assistant_impersonation = settings.assistant_impersonation ?? default_settings.assistant_impersonation;3028 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;
3009 oai_settings.image_inlining = settings.image_inlining ?? default_settings.image_inlining;3029 oai_settings.image_inlining = settings.image_inlining ?? default_settings.image_inlining;
3010 oai_settings.inline_image_quality = settings.inline_image_quality ?? default_settings.inline_image_quality;3030 oai_settings.inline_image_quality = settings.inline_image_quality ?? default_settings.inline_image_quality;
3011 oai_settings.bypass_status_check = settings.bypass_status_check ?? default_settings.bypass_status_check;3031 oai_settings.bypass_status_check = settings.bypass_status_check ?? default_settings.bypass_status_check;
@@ -3045,7 +3065,6 @@ function loadOpenAISettings(data, settings) {
3045 $('#openai_proxy_password').val(oai_settings.proxy_password);3065 $('#openai_proxy_password').val(oai_settings.proxy_password);
3046 $('#claude_assistant_prefill').val(oai_settings.assistant_prefill);3066 $('#claude_assistant_prefill').val(oai_settings.assistant_prefill);
3047 $('#claude_assistant_impersonation').val(oai_settings.assistant_impersonation);3067 $('#claude_assistant_impersonation').val(oai_settings.assistant_impersonation);
3048 $('#claude_human_sysprompt_textarea').val(oai_settings.human_sysprompt_message);
3049 $('#openai_image_inlining').prop('checked', oai_settings.image_inlining);3068 $('#openai_image_inlining').prop('checked', oai_settings.image_inlining);
3050 $('#openai_bypass_status_check').prop('checked', oai_settings.bypass_status_check);3069 $('#openai_bypass_status_check').prop('checked', oai_settings.bypass_status_check);
30513070
@@ -3375,7 +3394,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3375 show_external_models: settings.show_external_models,3394 show_external_models: settings.show_external_models,
3376 assistant_prefill: settings.assistant_prefill,3395 assistant_prefill: settings.assistant_prefill,
3377 assistant_impersonation: settings.assistant_impersonation,3396 assistant_impersonation: settings.assistant_impersonation,
3378 human_sysprompt_message: settings.human_sysprompt_message,
3379 claude_use_sysprompt: settings.claude_use_sysprompt,3397 claude_use_sysprompt: settings.claude_use_sysprompt,
3380 use_makersuite_sysprompt: settings.use_makersuite_sysprompt,3398 use_makersuite_sysprompt: settings.use_makersuite_sysprompt,
3381 use_alt_scale: settings.use_alt_scale,3399 use_alt_scale: settings.use_alt_scale,
@@ -3800,7 +3818,6 @@ function onSettingsPresetChange() {
3800 proxy_password: ['#openai_proxy_password', 'proxy_password', false],3818 proxy_password: ['#openai_proxy_password', 'proxy_password', false],
3801 assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],3819 assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],
3802 assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false],3820 assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false],
3803 human_sysprompt_message: ['#claude_human_sysprompt_textarea', 'human_sysprompt_message', false],
3804 claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true],3821 claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true],
3805 use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true],3822 use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true],
3806 use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],3823 use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],
@@ -4652,10 +4669,6 @@ function toggleChatCompletionForms() {
4652 const validSources = $(this).data('source').split(',');4669 const validSources = $(this).data('source').split(',');
4653 $(this).toggle(validSources.includes(oai_settings.chat_completion_source));4670 $(this).toggle(validSources.includes(oai_settings.chat_completion_source));
4654 });4671 });
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 }
4659}4672}
46604673
4661async function testApiConnection() {4674async function testApiConnection() {
@@ -5011,7 +5024,6 @@ export function initOpenAI() {
50115024
5012 $('#claude_use_sysprompt').on('change', function () {5025 $('#claude_use_sysprompt').on('change', function () {
5013 oai_settings.claude_use_sysprompt = !!$('#claude_use_sysprompt').prop('checked');5026 oai_settings.claude_use_sysprompt = !!$('#claude_use_sysprompt').prop('checked');
5014 $('#claude_human_sysprompt_message_block').toggle(oai_settings.claude_use_sysprompt);
5015 saveSettingsDebounced();5027 saveSettingsDebounced();
5016 });5028 });
50175029
@@ -5088,12 +5100,6 @@ export function initOpenAI() {
5088 saveSettingsDebounced();5100 saveSettingsDebounced();
5089 });5101 });
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
5097 $('#newgroupchat_prompt_restore').on('click', function () {5103 $('#newgroupchat_prompt_restore').on('click', function () {
5098 oai_settings.new_group_chat_prompt = default_new_group_chat_prompt;5104 oai_settings.new_group_chat_prompt = default_new_group_chat_prompt;
5099 $('#newgroupchat_prompt_textarea').val(oai_settings.new_group_chat_prompt);5105 $('#newgroupchat_prompt_textarea').val(oai_settings.new_group_chat_prompt);
@@ -5185,11 +5191,6 @@ export function initOpenAI() {
5185 saveSettingsDebounced();5191 saveSettingsDebounced();
5186 });5192 });
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
5193 $('#openrouter_use_fallback').on('input', function () {5194 $('#openrouter_use_fallback').on('input', function () {
5194 oai_settings.openrouter_use_fallback = !!$(this).prop('checked');5195 oai_settings.openrouter_use_fallback = !!$(this).prop('checked');
5195 saveSettingsDebounced();5196 saveSettingsDebounced();
public/scripts/power-user.js+5 -0
@@ -658,6 +658,10 @@ async function CreateZenSliders(elmnt) {
658 numSteps = 50;658 numSteps = 50;
659 decimals = 1;659 decimals = 1;
660 }660 }
661 if (sliderID == 'nsigma') {
662 numSteps = 50;
663 decimals = 1;
664 }
661 //customize steps665 //customize steps
662 if (sliderID == 'mirostat_mode_textgenerationwebui' ||666 if (sliderID == 'mirostat_mode_textgenerationwebui' ||
663 sliderID == 'mirostat_mode_kobold') {667 sliderID == 'mirostat_mode_kobold') {
@@ -702,6 +706,7 @@ async function CreateZenSliders(elmnt) {
702 sliderID == 'penalty_alpha_textgenerationwebui' ||706 sliderID == 'penalty_alpha_textgenerationwebui' ||
703 sliderID == 'length_penalty_textgenerationwebui' ||707 sliderID == 'length_penalty_textgenerationwebui' ||
704 sliderID == 'epsilon_cutoff_textgenerationwebui' ||708 sliderID == 'epsilon_cutoff_textgenerationwebui' ||
709 sliderID == 'nsigma' ||
705 sliderID == 'rep_pen_range' ||710 sliderID == 'rep_pen_range' ||
706 sliderID == 'eta_cutoff_textgenerationwebui' ||711 sliderID == 'eta_cutoff_textgenerationwebui' ||
707 sliderID == 'top_a_textgenerationwebui' ||712 sliderID == 'top_a_textgenerationwebui' ||
public/scripts/slash-commands/SlashCommandBrowser.js+6 -7
@@ -1,5 +1,4 @@
1import { escapeRegex } from '../utils.js';1import { escapeRegex } from '../utils.js';
2import { SlashCommand } from './SlashCommand.js';
3import { SlashCommandParser } from './SlashCommandParser.js';2import { SlashCommandParser } from './SlashCommandParser.js';
43
5export class SlashCommandBrowser {4export class SlashCommandBrowser {
@@ -30,7 +29,7 @@ export class SlashCommandBrowser {
30 this.details?.remove();29 this.details?.remove();
31 this.details = null;30 this.details = null;
32 let query = inp.value.trim();31 let query = inp.value.trim();
33 if (query.slice(-1) == '"' && !/(?:^|\s+)"/.test(query)) {32 if (query.slice(-1) === '"' && !/(?:^|\s+)"/.test(query)) {
34 query = `"${query}`;33 query = `"${query}`;
35 }34 }
36 let fuzzyList = [];35 let fuzzyList = [];
@@ -59,7 +58,7 @@ export class SlashCommandBrowser {
59 cmd.helpString,58 cmd.helpString,
60 ];59 ];
61 const find = ()=>targets.find(t=>(fuzzyList.find(f=>f.test(t)) ?? quotedList.find(q=>t.includes(q))) !== undefined) !== undefined;60 const find = ()=>targets.find(t=>(fuzzyList.find(f=>f.test(t)) ?? quotedList.find(q=>t.includes(q))) !== undefined) !== undefined;
62 if (fuzzyList.length + quotedList.length == 0 || find()) {61 if (fuzzyList.length + quotedList.length === 0 || find()) {
63 this.itemMap[cmd.name].classList.remove('isFiltered');62 this.itemMap[cmd.name].classList.remove('isFiltered');
64 } else {63 } else {
65 this.itemMap[cmd.name].classList.add('isFiltered');64 this.itemMap[cmd.name].classList.add('isFiltered');
@@ -78,7 +77,7 @@ export class SlashCommandBrowser {
78 list.classList.add('autoComplete');77 list.classList.add('autoComplete');
79 this.cmdList = Object78 this.cmdList = Object
80 .keys(SlashCommandParser.commands)79 .keys(SlashCommandParser.commands)
81 .filter(key => SlashCommandParser.commands[key].name == key) // exclude aliases80 .filter(key => SlashCommandParser.commands[key].name === key) // exclude aliases
82 .sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))81 .sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
83 .map(key => SlashCommandParser.commands[key])82 .map(key => SlashCommandParser.commands[key])
84 ;83 ;
@@ -97,7 +96,7 @@ export class SlashCommandBrowser {
97 }96 }
98 }97 }
99 }98 }
100 if (this.details != details) {99 if (this.details !== details) {
101 Array.from(list.querySelectorAll('.selected')).forEach(it=>it.classList.remove('selected'));100 Array.from(list.querySelectorAll('.selected')).forEach(it=>it.classList.remove('selected'));
102 item.classList.add('selected');101 item.classList.add('selected');
103 this.details?.remove();102 this.details?.remove();
@@ -124,7 +123,7 @@ export class SlashCommandBrowser {
124 parent.append(this.dom);123 parent.append(this.dom);
125124
126 this.mo = new MutationObserver(muts=>{125 this.mo = new MutationObserver(muts=>{
127 if (muts.find(mut=>Array.from(mut.removedNodes).find(it=>it == this.dom || it.contains(this.dom)))) {126 if (muts.find(mut=>Array.from(mut.removedNodes).find(it=>it === this.dom || it.contains(this.dom)))) {
128 this.mo.disconnect();127 this.mo.disconnect();
129 window.removeEventListener('keydown', boundHandler);128 window.removeEventListener('keydown', boundHandler);
130 }129 }
@@ -136,7 +135,7 @@ export class SlashCommandBrowser {
136 }135 }
137136
138 handleKeyDown(evt) {137 handleKeyDown(evt) {
139 if (!evt.shiftKey && !evt.altKey && evt.ctrlKey && evt.key.toLowerCase() == 'f') {138 if (!evt.shiftKey && !evt.altKey && evt.ctrlKey && evt.key.toLowerCase() === 'f') {
140 if (!this.dom.closest('body')) return;139 if (!this.dom.closest('body')) return;
141 if (this.dom.closest('.mes') && !this.dom.closest('.last_mes')) return;140 if (this.dom.closest('.mes') && !this.dom.closest('.last_mes')) return;
142 evt.preventDefault();141 evt.preventDefault();
public/scripts/textgen-settings.js+13 -0
@@ -193,6 +193,7 @@ const settings = {
193 openrouter_allow_fallbacks: true,193 openrouter_allow_fallbacks: true,
194 xtc_threshold: 0.1,194 xtc_threshold: 0.1,
195 xtc_probability: 0,195 xtc_probability: 0,
196 nsigma: 0.0,
196 featherless_model: '',197 featherless_model: '',
197};198};
198199
@@ -265,6 +266,7 @@ export const setting_names = [
265 'openrouter_allow_fallbacks',266 'openrouter_allow_fallbacks',
266 'xtc_threshold',267 'xtc_threshold',
267 'xtc_probability',268 'xtc_probability',
269 'nsigma',
268];270];
269271
270const DYNATEMP_BLOCK = document.getElementById('dynatemp_block_ooba');272const DYNATEMP_BLOCK = document.getElementById('dynatemp_block_ooba');
@@ -880,6 +882,13 @@ function setSettingByName(setting, value, trigger) {
880 }882 }
881}883}
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 */
883async function generateTextGenWithStreaming(generate_data, signal) {892async function generateTextGenWithStreaming(generate_data, signal) {
884 generate_data.stream = true;893 generate_data.stream = true;
885894
@@ -995,6 +1004,7 @@ export function parseTabbyLogprobs(data) {
995 * @param {Response} response - Response from the server.1004 * @param {Response} response - Response from the server.
996 * @param {string} decoded - Decoded response body.1005 * @param {string} decoded - Decoded response body.
997 * @returns {void} Nothing.1006 * @returns {void} Nothing.
1007 * @throws {Error} If the response contains an error message, throws Error with the message.
998 */1008 */
999function tryParseStreamingError(response, decoded) {1009function tryParseStreamingError(response, decoded) {
1000 let data = {};1010 let data = {};
@@ -1178,6 +1188,7 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
1178 'sampler_order': settings.type === textgen_types.KOBOLDCPP ? settings.sampler_order : undefined,1188 'sampler_order': settings.type === textgen_types.KOBOLDCPP ? settings.sampler_order : undefined,
1179 'xtc_threshold': settings.xtc_threshold,1189 'xtc_threshold': settings.xtc_threshold,
1180 'xtc_probability': settings.xtc_probability,1190 'xtc_probability': settings.xtc_probability,
1191 'nsigma': settings.nsigma,
1181 };1192 };
1182 const nonAphroditeParams = {1193 const nonAphroditeParams = {
1183 'rep_pen': settings.rep_pen,1194 'rep_pen': settings.rep_pen,
@@ -1245,7 +1256,9 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
1245 'dynatemp_exponent': dynatemp ? settings.dynatemp_exponent : undefined,1256 'dynatemp_exponent': dynatemp ? settings.dynatemp_exponent : undefined,
1246 'xtc_threshold': settings.xtc_threshold,1257 'xtc_threshold': settings.xtc_threshold,
1247 'xtc_probability': settings.xtc_probability,1258 'xtc_probability': settings.xtc_probability,
1259 'nsigma': settings.nsigma,
1248 'custom_token_bans': toIntArray(banned_tokens),1260 'custom_token_bans': toIntArray(banned_tokens),
1261 'no_repeat_ngram_size': settings.no_repeat_ngram_size,
1249 };1262 };
12501263
1251 if (settings.type === OPENROUTER) {1264 if (settings.type === OPENROUTER) {
src/endpoints/backends/chat-completions.js+7 -3
@@ -102,7 +102,7 @@ async function sendClaudeRequest(request, response) {
102 const additionalHeaders = {};102 const additionalHeaders = {};
103 const useTools = request.body.model.startsWith('claude-3') && Array.isArray(request.body.tools) && request.body.tools.length > 0;103 const useTools = request.body.model.startsWith('claude-3') && Array.isArray(request.body.tools) && request.body.tools.length > 0;
104 const useSystemPrompt = (request.body.model.startsWith('claude-2') || request.body.model.startsWith('claude-3')) && request.body.claude_use_sysprompt;104 const useSystemPrompt = (request.body.model.startsWith('claude-2') || request.body.model.startsWith('claude-3')) && request.body.claude_use_sysprompt;
105 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);105 const convertedPrompt = convertClaudeMessages(request.body.messages, request.body.assistant_prefill, useSystemPrompt, useTools, request.body.char_name, request.body.user_name);
106 // Add custom stop sequences106 // Add custom stop sequences
107 const stopSequences = [];107 const stopSequences = [];
108 if (Array.isArray(request.body.stop)) {108 if (Array.isArray(request.body.stop)) {
@@ -1051,8 +1051,12 @@ router.post('/generate', jsonParser, function (request, response) {
1051 }1051 }
1052 } catch (error) {1052 } catch (error) {
1053 console.log('Generation failed', error);1053 console.log('Generation failed', error);
1054 const message = error.code === 'ECONNREFUSED'
1055 ? `Connection refused: ${error.message}`
1056 : error.message || 'Unknown error occurred';
1057
1054 if (!response.headersSent) {1058 if (!response.headersSent) {
1055 response.send({ error: true });1059 response.status(502).send({ error: { message, ...error } });
1056 } else {1060 } else {
1057 response.end();1061 response.end();
1058 }1062 }
@@ -1068,7 +1072,7 @@ router.post('/generate', jsonParser, function (request, response) {
10681072
1069 const message = errorResponse.statusText || 'Unknown error occurred';1073 const message = errorResponse.statusText || 'Unknown error occurred';
1070 const quota_error = errorResponse.status === 429 && errorData?.error?.type === 'insufficient_quota';1074 const quota_error = errorResponse.status === 429 && errorData?.error?.type === 'insufficient_quota';
1071 console.log(message, responseText);1075 console.log('Chat completion request error: ', message, responseText);
10721076
1073 if (!response.headersSent) {1077 if (!response.headersSent) {
1074 response.send({ error: { message }, quota_error: quota_error });1078 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
25// KV-store for parsed character data25// KV-store for parsed character data
26const characterDataCache = new Map();26const characterDataCache = new Map();
27// Some Android devices require tighter memory management
28const isAndroid = process.platform === 'android';
2729
28/**30/**
29 * Reads the character card from the specified image file.31 * Reads the character card from the specified image file.
@@ -39,7 +41,7 @@ async function readCharacterData(inputFile, inputFormat = 'png') {
39 }41 }
4042
41 const result = parse(inputFile, inputFormat);43 const result = parse(inputFile, inputFormat);
42 characterDataCache.set(cacheKey, result);44 !isAndroid && characterDataCache.set(cacheKey, result);
43 return result;45 return result;
44}46}
4547
src/middleware/webpack-serve.js+1 -1
@@ -6,7 +6,7 @@ import { publicLibConfig } from '../../webpack.config.js';
6export default function getWebpackServeMiddleware() {6export default function getWebpackServeMiddleware() {
7 const compiler = webpack(publicLibConfig);7 const compiler = webpack(publicLibConfig);
88
9 if (process.env.NODE_ENV === 'production') {9 if (process.env.NODE_ENV === 'production' || process.platform === 'android') {
10 compiler.hooks.done.tap('serve', () => {10 compiler.hooks.done.tap('serve', () => {
11 if (compiler.watching) {11 if (compiler.watching) {
12 compiler.watching.close(() => { });12 compiler.watching.close(() => { });
src/prompt-converters.js+3 -4
@@ -91,11 +91,10 @@ export function convertClaudePrompt(messages, addAssistantPostfix, addAssistantP
91 * @param {string} prefillString User determined prefill string91 * @param {string} prefillString User determined prefill string
92 * @param {boolean} useSysPrompt See if we want to use a system prompt92 * @param {boolean} useSysPrompt See if we want to use a system prompt
93 * @param {boolean} useTools See if we want to use tools93 * @param {boolean} useTools See if we want to use tools
94 * @param {string} humanMsgFix Add Human message between system prompt and assistant.
95 * @param {string} charName Character name94 * @param {string} charName Character name
96 * @param {string} userName User name95 * @param {string} userName User name
97 */96 */
98export function convertClaudeMessages(messages, prefillString, useSysPrompt, useTools, humanMsgFix, charName = '', userName = '') {97export function convertClaudeMessages(messages, prefillString, useSysPrompt, useTools, charName, userName) {
99 let systemPrompt = [];98 let systemPrompt = [];
100 if (useSysPrompt) {99 if (useSysPrompt) {
101 // Collect all the system messages up until the first instance of a non-system message, and then remove them from the messages array.100 // 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
123 // Check if the first message in the array is of type user, if not, interject with humanMsgFix or a blank message.122 // Check if the first message in the array is of type user, if not, interject with humanMsgFix or a blank message.
124 // Also prevents erroring out if the messages array is empty.123 // Also prevents erroring out if the messages array is empty.
125 if (messages.length === 0 || (messages.length > 0 && messages[0].role !== 'user')) {124 if (messages.length === 0) {
126 messages.unshift({125 messages.unshift({
127 role: 'user',126 role: 'user',
128 content: humanMsgFix || PROMPT_PLACEHOLDER,127 content: PROMPT_PLACEHOLDER,
129 });128 });
130 }129 }
131 }130 }