mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge branch 'staging' into redesign-extension-manager
This commit is contained in:
@@ -54,6 +54,7 @@ module.exports = {
|
||||
},
|
||||
// These scripts are loaded in HTML; tell ESLint not to complain about them being undefined
|
||||
globals: {
|
||||
globalThis: 'readonly',
|
||||
ePub: 'readonly',
|
||||
pdfjsLib: 'readonly',
|
||||
toastr: 'readonly',
|
||||
|
38
.github/readme-ru_ru.md
vendored
38
.github/readme-ru_ru.md
vendored
@@ -209,6 +209,44 @@ SillyTavern поддерживает расширения.
|
||||
5. Запустите лаунчер установки: `chmod +x install.sh && ./install.sh` and choose what you wanna install
|
||||
6. После завершения установки, запустите лаунчер следующей командой: `chmod +x launcher.sh && ./launcher.sh`
|
||||
|
||||
## 🐋 Установка с помощью Docker
|
||||
|
||||
Предполагается, что вы уже установили Docker, имеете доступ к командной строке для установки контейнеров и знакомы с их базовым управлением.
|
||||
|
||||
### Сборка образа самостоятельно
|
||||
|
||||
У нас есть подробное руководство по использованию SillyTavern в Docker [здесь](http://docs.sillytavern.app/installation/docker/), которое охватывает установку на Windows, macOS и Linux! Ознакомьтесь с ним, если хотите создать образ самостоятельно.
|
||||
|
||||
### Использование реестра контейнеров GitHub (самый простой способ)
|
||||
|
||||
Для работы SillyTavern вам понадобятся две обязательные настройки каталогов и одна настройка порта. В команде замените указанные значения на свои:
|
||||
|
||||
#### Переменные контейнера
|
||||
|
||||
##### Маппинг томов
|
||||
|
||||
* [config] - директория, где на вашем хосте будут храниться файлы конфигурации SillyTavern.
|
||||
* [data] - директория, где на вашем хосте будут храниться пользовательские данные SillyTavern (включая персонажей).
|
||||
* [plugins] - (необязательно) директория, где на вашем хосте будут храниться плагины сервера SillyTavern.
|
||||
|
||||
##### Маппинг портов
|
||||
|
||||
* [PublicPort] - Порт, через который будет передаваться трафик. Это обязательно, так как вы будете обращаться к контейнеру извне его виртуальной машины. НЕ ОТКРЫВАЙТЕ этот порт в интернет без реализации дополнительного уровня безопасности.
|
||||
|
||||
##### Дополнительные настройки
|
||||
|
||||
* [DockerNet] - Docker сеть, к которой контейнер должен быть подключен. Если вы не знаете, что это, обратитесь к [официальной документации Docker](https://docs.docker.com/reference/cli/docker/network/).
|
||||
* [version] - на правой части этой страницы GitHub вы найдете раздел "Packages". Выберите пакет "sillytavern", чтобы увидеть версии образов. Тег "latest" позволит вам обновляться до текущего релиза. Также доступны теги "staging" и "release", которые соответствуют ночным сборкам соответствующих веток. Однако это может быть нецелесообразно, если вы используете расширения, которые могут ломаться и требуют времени для обновления.
|
||||
|
||||
#### Команда установки
|
||||
|
||||
1. Откройте командную строку
|
||||
2. Выполните следующую команду
|
||||
|
||||
`docker create --name='sillytavern' --net='[DockerNet]' -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' 'ghcr.io/sillytavern/sillytavern:[version]'`
|
||||
|
||||
> Заметьте, что 8000 является портом по умолчанию. Не забудьте использовать соответствующий порт, если вы измените его в конфиге.
|
||||
|
||||
## 📱 Мобильные устройства - Установка при помощи termux
|
||||
|
||||
> **ОБРАТИТЕ ВНИМАНИЕ!**
|
||||
|
@@ -1,6 +1,8 @@
|
||||
# -- DATA CONFIGURATION --
|
||||
# Root directory for user data storage
|
||||
dataRoot: ./data
|
||||
# The maximum amount of memory that parsed character cards can use in MB
|
||||
cardsCacheCapacity: 100
|
||||
# -- SERVER CONFIGURATION --
|
||||
# Listen for incoming connections
|
||||
listen: false
|
||||
|
22
package-lock.json
generated
22
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "sillytavern",
|
||||
"version": "1.12.8",
|
||||
"version": "1.12.9",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sillytavern",
|
||||
"version": "1.12.8",
|
||||
"version": "1.12.9",
|
||||
"hasInstallScript": true,
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
@@ -3591,9 +3591,9 @@
|
||||
"integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw=="
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.21.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
||||
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
|
||||
"version": "4.21.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
|
||||
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
@@ -3615,7 +3615,7 @@
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.10",
|
||||
"path-to-regexp": "0.1.12",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.13.0",
|
||||
"range-parser": "~1.2.1",
|
||||
@@ -3630,6 +3630,10 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/cookie": {
|
||||
@@ -5725,9 +5729,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.10",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
|
||||
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/peek-readable": {
|
||||
|
@@ -84,7 +84,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/SillyTavern/SillyTavern.git"
|
||||
},
|
||||
"version": "1.12.8",
|
||||
"version": "1.12.9",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"start:deno": "deno run --allow-run --allow-net --allow-read --allow-write --allow-sys --allow-env server.js",
|
||||
|
3
public/global.d.ts
vendored
3
public/global.d.ts
vendored
@@ -1,4 +1,5 @@
|
||||
import libs from './lib';
|
||||
import getContext from './scripts/st-context';
|
||||
|
||||
// Global namespace modules
|
||||
declare var ai;
|
||||
@@ -6,7 +7,7 @@ declare var pdfjsLib;
|
||||
declare var ePub;
|
||||
|
||||
declare var SillyTavern: {
|
||||
getContext(): any;
|
||||
getContext(): typeof getContext;
|
||||
llm: any;
|
||||
libs: typeof libs;
|
||||
};
|
||||
|
@@ -669,7 +669,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai">
|
||||
<div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt">
|
||||
<div class="range-block-title" data-i18n="Temperature">
|
||||
Temperature
|
||||
</div>
|
||||
@@ -682,7 +682,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai">
|
||||
<div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt">
|
||||
<div class="range-block-title" data-i18n="Frequency Penalty">
|
||||
Frequency Penalty
|
||||
</div>
|
||||
@@ -695,7 +695,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai">
|
||||
<div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt">
|
||||
<div class="range-block-title" data-i18n="Presence Penalty">
|
||||
Presence Penalty
|
||||
</div>
|
||||
@@ -721,7 +721,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block" data-source="openai,claude,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai">
|
||||
<div class="range-block" data-source="openai,claude,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt">
|
||||
<div class="range-block-title" data-i18n="Top P">
|
||||
Top P
|
||||
</div>
|
||||
@@ -958,7 +958,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block" data-source="openai,openrouter,mistralai,custom,cohere,groq">
|
||||
<div class="range-block" data-source="openai,openrouter,mistralai,custom,cohere,groq,nanogpt">
|
||||
<div class="range-block-title justifyLeft" data-i18n="Seed">
|
||||
Seed
|
||||
</div>
|
||||
@@ -2987,6 +2987,7 @@
|
||||
<optgroup label="Subversions">
|
||||
<option value="gemini-exp-1121">Gemini Experimental 2024-11-21</option>
|
||||
<option value="gemini-exp-1114">Gemini Experimental 2024-11-14</option>
|
||||
<option value="gemini-exp-1206">Gemini Experimental 2024-12-06</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>
|
||||
@@ -4183,7 +4184,7 @@
|
||||
</label>
|
||||
<label for="show_swipe_num_all_messages" class="checkbox_label" title="Display swipe numbers for all messages, not just the last." data-i18n="[title]Display swipe numbers for all messages, not just the last.">
|
||||
<input id="show_swipe_num_all_messages" type="checkbox" />
|
||||
<small data-i18n="Swipe # for All Messages">Swipe # for All Messages</small><i class="fa-solid fa-mobile-screen-button"></i>
|
||||
<small data-i18n="Swipe # for All Messages">Swipe # for All Messages</small>
|
||||
</label>
|
||||
<label for="hotswapEnabled" class="checkbox_label" title="In the Character Management panel, show quick selection buttons for favorited characters." data-i18n="[title]In the Character Management panel, show quick selection buttons for favorited characters">
|
||||
<input id="hotswapEnabled" type="checkbox" />
|
||||
|
@@ -5,7 +5,8 @@
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"allowUmdGlobalAccess": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strictBindCallApply": true
|
||||
},
|
||||
"exclude": [
|
||||
"**/node_modules/**",
|
||||
|
234
public/script.js
234
public/script.js
@@ -13,7 +13,7 @@ import {
|
||||
default as libs,
|
||||
} from './lib.js';
|
||||
|
||||
import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, initRossMods, shouldSendOnEnter } from './scripts/RossAscends-mods.js';
|
||||
import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, initRossMods } from './scripts/RossAscends-mods.js';
|
||||
import { userStatsHandler, statMesProcess, initStats } from './scripts/stats.js';
|
||||
import {
|
||||
generateKoboldWithStreaming,
|
||||
@@ -37,8 +37,6 @@ import {
|
||||
parseTabbyLogprobs,
|
||||
} from './scripts/textgen-settings.js';
|
||||
|
||||
const { MANCER, TOGETHERAI, OOBA, VLLM, APHRODITE, TABBY, OLLAMA, INFERMATICAI, DREAMGEN, OPENROUTER, FEATHERLESS } = textgen_types;
|
||||
|
||||
import {
|
||||
world_info,
|
||||
getWorldInfoPrompt,
|
||||
@@ -67,9 +65,7 @@ import {
|
||||
getGroupChat,
|
||||
renameGroupMember,
|
||||
createNewGroupChat,
|
||||
getGroupPastChats,
|
||||
getGroupAvatar,
|
||||
openGroupChat,
|
||||
editGroup,
|
||||
deleteGroupChat,
|
||||
renameGroupChat,
|
||||
@@ -98,7 +94,6 @@ import {
|
||||
resetMovableStyles,
|
||||
forceCharacterEditorTokenize,
|
||||
applyPowerUserSettings,
|
||||
switchSwipeNumAllMessages,
|
||||
} from './scripts/power-user.js';
|
||||
|
||||
import {
|
||||
@@ -174,8 +169,8 @@ import {
|
||||
} from './scripts/utils.js';
|
||||
import { debounce_timeout } from './scripts/constants.js';
|
||||
|
||||
import { ModuleWorkerWrapper, doDailyExtensionUpdatesCheck, extension_settings, getContext, initExtensions, loadExtensionSettings, renderExtensionTemplate, renderExtensionTemplateAsync, runGenerationInterceptors, saveMetadataDebounced, writeExtensionField } from './scripts/extensions.js';
|
||||
import { COMMENT_NAME_DEFAULT, executeSlashCommands, executeSlashCommandsOnChatInput, executeSlashCommandsWithOptions, getSlashCommandsHelp, initDefaultSlashCommands, isExecutingCommandsFromChatInput, pauseScriptExecution, processChatSlashCommands, registerSlashCommand, stopScriptExecution } from './scripts/slash-commands.js';
|
||||
import { doDailyExtensionUpdatesCheck, extension_settings, initExtensions, loadExtensionSettings, runGenerationInterceptors, saveMetadataDebounced } from './scripts/extensions.js';
|
||||
import { COMMENT_NAME_DEFAULT, executeSlashCommandsOnChatInput, getSlashCommandsHelp, initDefaultSlashCommands, isExecutingCommandsFromChatInput, pauseScriptExecution, processChatSlashCommands, stopScriptExecution } from './scripts/slash-commands.js';
|
||||
import {
|
||||
tag_map,
|
||||
tags,
|
||||
@@ -225,8 +220,8 @@ import {
|
||||
instruct_presets,
|
||||
selectContextPreset,
|
||||
} from './scripts/instruct-mode.js';
|
||||
import { initLocales, t, translate } from './scripts/i18n.js';
|
||||
import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, getTokenizerModel, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js';
|
||||
import { initLocales, t } from './scripts/i18n.js';
|
||||
import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js';
|
||||
import {
|
||||
user_avatar,
|
||||
getUserAvatars,
|
||||
@@ -241,12 +236,12 @@ import { hideLoader, showLoader } from './scripts/loader.js';
|
||||
import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay.js';
|
||||
import { loadFeatherlessModels, loadMancerModels, loadOllamaModels, loadTogetherAIModels, loadInfermaticAIModels, loadOpenRouterModels, loadVllmModels, loadAphroditeModels, loadDreamGenModels, initTextGenModels, loadTabbyModels } from './scripts/textgen-models.js';
|
||||
import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat } from './scripts/chats.js';
|
||||
import { initPresetManager } from './scripts/preset-manager.js';
|
||||
import { MacrosParser, evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js';
|
||||
import { getPresetManager, initPresetManager } from './scripts/preset-manager.js';
|
||||
import { evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js';
|
||||
import { currentUser, setUserControls } from './scripts/user.js';
|
||||
import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup, fixToastrForDialogs } from './scripts/popup.js';
|
||||
import { renderTemplate, renderTemplateAsync } from './scripts/templates.js';
|
||||
import { initScrapers, ScraperManager } from './scripts/scrapers.js';
|
||||
import { initScrapers } from './scripts/scrapers.js';
|
||||
import { SlashCommandParser } from './scripts/slash-commands/SlashCommandParser.js';
|
||||
import { SlashCommand } from './scripts/slash-commands/SlashCommand.js';
|
||||
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './scripts/slash-commands/SlashCommandArgument.js';
|
||||
@@ -268,6 +263,13 @@ import { initServerHistory } from './scripts/server-history.js';
|
||||
import { initSettingsSearch } from './scripts/setting-search.js';
|
||||
import { initBulkEdit } from './scripts/bulk-edit.js';
|
||||
import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js';
|
||||
import { getContext } from './scripts/st-context.js';
|
||||
|
||||
// API OBJECT FOR EXTERNAL WIRING
|
||||
globalThis.SillyTavern = {
|
||||
libs,
|
||||
getContext,
|
||||
};
|
||||
|
||||
//exporting functions and vars for mods
|
||||
export {
|
||||
@@ -427,10 +429,6 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => {
|
||||
}
|
||||
});
|
||||
|
||||
// API OBJECT FOR EXTERNAL WIRING
|
||||
window['SillyTavern'] = {};
|
||||
window['SillyTavern'].libs = libs;
|
||||
|
||||
// Event source init
|
||||
export const event_types = {
|
||||
APP_READY: 'app_ready',
|
||||
@@ -536,7 +534,7 @@ let displayVersion = 'SillyTavern';
|
||||
|
||||
let generatedPromptCache = '';
|
||||
let generation_started = new Date();
|
||||
/** @type {import('scripts/char-data.js').v1CharData[]} */
|
||||
/** @type {import('./scripts/char-data.js').v1CharData[]} */
|
||||
export let characters = [];
|
||||
export let this_chid;
|
||||
let saveCharactersPage = 0;
|
||||
@@ -812,7 +810,7 @@ export let menu_type = '';
|
||||
export let selected_button = ''; //which button pressed
|
||||
|
||||
//create pole save
|
||||
let create_save = {
|
||||
export let create_save = {
|
||||
name: '',
|
||||
description: '',
|
||||
creator_notes: '',
|
||||
@@ -864,7 +862,7 @@ export let amount_gen = 80; //default max length of AI generated responses
|
||||
export let max_context = 2048;
|
||||
|
||||
var swipes = true;
|
||||
let extension_prompts = {};
|
||||
export let extension_prompts = {};
|
||||
|
||||
export let main_api;// = "kobold";
|
||||
//novel settings
|
||||
@@ -1175,7 +1173,7 @@ async function getStatusTextgen() {
|
||||
return resultCheckStatus();
|
||||
}
|
||||
|
||||
if (textgen_settings.type == OOBA && textgen_settings.bypass_status_check) {
|
||||
if (textgen_settings.type == textgen_types.OOBA && textgen_settings.bypass_status_check) {
|
||||
setOnlineStatus('Status check bypassed');
|
||||
return resultCheckStatus();
|
||||
}
|
||||
@@ -1193,34 +1191,34 @@ async function getStatusTextgen() {
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (textgen_settings.type === MANCER) {
|
||||
if (textgen_settings.type === textgen_types.MANCER) {
|
||||
loadMancerModels(data?.data);
|
||||
setOnlineStatus(textgen_settings.mancer_model);
|
||||
} else if (textgen_settings.type === TOGETHERAI) {
|
||||
} else if (textgen_settings.type === textgen_types.TOGETHERAI) {
|
||||
loadTogetherAIModels(data?.data);
|
||||
setOnlineStatus(textgen_settings.togetherai_model);
|
||||
} else if (textgen_settings.type === OLLAMA) {
|
||||
} else if (textgen_settings.type === textgen_types.OLLAMA) {
|
||||
loadOllamaModels(data?.data);
|
||||
setOnlineStatus(textgen_settings.ollama_model || 'Connected');
|
||||
} else if (textgen_settings.type === INFERMATICAI) {
|
||||
} else if (textgen_settings.type === textgen_types.INFERMATICAI) {
|
||||
loadInfermaticAIModels(data?.data);
|
||||
setOnlineStatus(textgen_settings.infermaticai_model);
|
||||
} else if (textgen_settings.type === DREAMGEN) {
|
||||
} else if (textgen_settings.type === textgen_types.DREAMGEN) {
|
||||
loadDreamGenModels(data?.data);
|
||||
setOnlineStatus(textgen_settings.dreamgen_model);
|
||||
} else if (textgen_settings.type === OPENROUTER) {
|
||||
} else if (textgen_settings.type === textgen_types.OPENROUTER) {
|
||||
loadOpenRouterModels(data?.data);
|
||||
setOnlineStatus(textgen_settings.openrouter_model);
|
||||
} else if (textgen_settings.type === VLLM) {
|
||||
} else if (textgen_settings.type === textgen_types.VLLM) {
|
||||
loadVllmModels(data?.data);
|
||||
setOnlineStatus(textgen_settings.vllm_model);
|
||||
} else if (textgen_settings.type === APHRODITE) {
|
||||
} else if (textgen_settings.type === textgen_types.APHRODITE) {
|
||||
loadAphroditeModels(data?.data);
|
||||
setOnlineStatus(textgen_settings.aphrodite_model);
|
||||
} else if (textgen_settings.type === FEATHERLESS) {
|
||||
} else if (textgen_settings.type === textgen_types.FEATHERLESS) {
|
||||
loadFeatherlessModels(data?.data);
|
||||
setOnlineStatus(textgen_settings.featherless_model);
|
||||
} else if (textgen_settings.type === TABBY) {
|
||||
} else if (textgen_settings.type === textgen_types.TABBY) {
|
||||
loadTabbyModels(data?.data);
|
||||
setOnlineStatus(textgen_settings.tabby_model || data?.result);
|
||||
} else {
|
||||
@@ -1637,7 +1635,7 @@ export function getEntitiesList({ doFilter = false, doSort = true } = {}) {
|
||||
subEntities = entitiesFilter.applyFilters(subEntities, { clearScoreCache: false, tempOverrides: { [FILTER_TYPES.FOLDER]: FILTER_STATES.UNDEFINED }, clearFuzzySearchCaches: false });
|
||||
}
|
||||
if (doSort) {
|
||||
sortEntitiesList(subEntities);
|
||||
sortEntitiesList(subEntities, false);
|
||||
}
|
||||
entity.entities = subEntities;
|
||||
entity.hidden = subCount - subEntities.length;
|
||||
@@ -1665,7 +1663,7 @@ export function getEntitiesList({ doFilter = false, doSort = true } = {}) {
|
||||
|
||||
// Sort before returning if requested
|
||||
if (doSort) {
|
||||
sortEntitiesList(entities);
|
||||
sortEntitiesList(entities, false);
|
||||
}
|
||||
entitiesFilter.clearFuzzySearchCaches();
|
||||
return entities;
|
||||
@@ -2607,15 +2605,18 @@ export function substituteParams(content, _name1, _name2, _original, _group, _re
|
||||
};
|
||||
}
|
||||
|
||||
const getGroupValue = () => {
|
||||
const getGroupValue = (includeMuted) => {
|
||||
if (typeof _group === 'string') {
|
||||
return _group;
|
||||
}
|
||||
|
||||
if (selected_group) {
|
||||
const members = groups.find(x => x.id === selected_group)?.members;
|
||||
/** @type {string[]} */
|
||||
const disabledMembers = groups.find(x => x.id === selected_group)?.disabled_members ?? [];
|
||||
const isMuted = x => includeMuted ? true : !disabledMembers.includes(x);
|
||||
const names = Array.isArray(members)
|
||||
? members.map(m => characters.find(c => c.avatar === m)?.name).filter(Boolean).join(', ')
|
||||
? members.filter(isMuted).map(m => characters.find(c => c.avatar === m)?.name).filter(Boolean).join(', ')
|
||||
: '';
|
||||
return names;
|
||||
} else {
|
||||
@@ -2639,7 +2640,8 @@ export function substituteParams(content, _name1, _name2, _original, _group, _re
|
||||
// Must be substituted last so that they're replaced inside {{description}}
|
||||
environment.user = _name1 ?? name1;
|
||||
environment.char = _name2 ?? name2;
|
||||
environment.group = environment.charIfNotGroup = getGroupValue();
|
||||
environment.group = environment.charIfNotGroup = getGroupValue(true);
|
||||
environment.groupNotMuted = getGroupValue(false);
|
||||
environment.model = getGeneratingModel();
|
||||
|
||||
if (additionalMacro && typeof additionalMacro === 'object') {
|
||||
@@ -4494,6 +4496,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
|
||||
instruction: main_api !== 'openai' && power_user.sysprompt.enabled ? substituteParams(power_user.prefer_character_prompt && system ? system : power_user.sysprompt.content) : '',
|
||||
userPersona: (power_user.persona_description_position == persona_description_positions.IN_PROMPT ? (persona || '') : ''),
|
||||
tokenizer: getFriendlyTokenizerName(main_api).tokenizerName || '',
|
||||
presetName: getPresetManager()?.getSelectedPresetName() || '',
|
||||
};
|
||||
|
||||
//console.log(additionalPromptStuff);
|
||||
@@ -5160,6 +5163,7 @@ export async function itemizedParams(itemizedPrompts, thisPromptSet, incomingMes
|
||||
dataBankVectorsStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].dataBankVectorsString),
|
||||
modelUsed: chat[incomingMesId]?.extra?.model,
|
||||
apiUsed: chat[incomingMesId]?.extra?.api,
|
||||
presetName: itemizedPrompts[thisPromptSet].presetName || t`(Unknown)`,
|
||||
};
|
||||
|
||||
const getFriendlyName = (value) => $(`#rm_api_block select option[value="${value}"]`).first().text() || value;
|
||||
@@ -5536,7 +5540,7 @@ function extractMultiSwipes(data, type) {
|
||||
return swipes;
|
||||
}
|
||||
|
||||
if (main_api === 'openai' || (main_api === 'textgenerationwebui' && [MANCER, VLLM, APHRODITE, TABBY, INFERMATICAI].includes(textgen_settings.type))) {
|
||||
if (main_api === 'openai' || (main_api === 'textgenerationwebui' && [textgen_types.MANCER, textgen_types.VLLM, textgen_types.APHRODITE, textgen_types.TABBY, textgen_types.INFERMATICAI].includes(textgen_settings.type))) {
|
||||
if (!Array.isArray(data.choices)) {
|
||||
return swipes;
|
||||
}
|
||||
@@ -5699,7 +5703,7 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc
|
||||
return getMessage;
|
||||
}
|
||||
|
||||
async function saveReply(type, getMessage, fromStreaming, title, swipes) {
|
||||
export async function saveReply(type, getMessage, fromStreaming, title, swipes) {
|
||||
if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (chat[chat.length - 1]['swipe_id'] === undefined ||
|
||||
chat[chat.length - 1]['is_user'])) {
|
||||
type = 'normal';
|
||||
@@ -7658,14 +7662,12 @@ export function showSwipeButtons() {
|
||||
//allows for writing individual swipe counters for past messages
|
||||
const lastSwipeCounter = $('.last_mes .swipes-counter');
|
||||
lastSwipeCounter.text(swipeCounterText).show();
|
||||
|
||||
switchSwipeNumAllMessages();
|
||||
}
|
||||
|
||||
export function hideSwipeButtons() {
|
||||
$('#chat').find('.swipe_right').hide();
|
||||
$('#chat').find('.last_mes .swipes-counter').hide();
|
||||
$('#chat').find('.swipe_left').hide();
|
||||
chatElement.find('.swipe_right').hide();
|
||||
chatElement.find('.last_mes .swipes-counter').hide();
|
||||
chatElement.find('.swipe_left').hide();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -7752,25 +7754,31 @@ export async function saveChatConditional() {
|
||||
}
|
||||
}
|
||||
|
||||
async function importCharacterChat(formData) {
|
||||
await jQuery.ajax({
|
||||
type: 'POST',
|
||||
url: '/api/chats/import',
|
||||
data: formData,
|
||||
beforeSend: function () {
|
||||
},
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: async function (data) {
|
||||
/**
|
||||
* Saves the chat to the server.
|
||||
* @param {FormData} formData Form data to send to the server.
|
||||
* @param {EventTarget} eventTarget Event target to trigger the event on.
|
||||
*/
|
||||
async function importCharacterChat(formData, eventTarget) {
|
||||
const headers = getRequestHeaders();
|
||||
delete headers['Content-Type'];
|
||||
const fetchResult = await fetch('/api/chats/import', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: headers,
|
||||
cache: 'no-cache',
|
||||
});
|
||||
|
||||
if (fetchResult.ok) {
|
||||
const data = await fetchResult.json();
|
||||
if (data.res) {
|
||||
await displayPastChats();
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
$('#create_button').removeAttr('disabled');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (eventTarget instanceof HTMLInputElement) {
|
||||
eventTarget.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
function updateViewMessageIds(startFromZero = false) {
|
||||
@@ -8225,102 +8233,6 @@ async function createOrEditCharacter(e) {
|
||||
}
|
||||
}
|
||||
|
||||
window['SillyTavern'].getContext = function () {
|
||||
return {
|
||||
chat: chat,
|
||||
characters: characters,
|
||||
groups: groups,
|
||||
name1: name1,
|
||||
name2: name2,
|
||||
characterId: this_chid,
|
||||
groupId: selected_group,
|
||||
chatId: selected_group
|
||||
? groups.find(x => x.id == selected_group)?.chat_id
|
||||
: (this_chid && characters[this_chid] && characters[this_chid].chat),
|
||||
getCurrentChatId: getCurrentChatId,
|
||||
getRequestHeaders: getRequestHeaders,
|
||||
reloadCurrentChat: reloadCurrentChat,
|
||||
renameChat: renameChat,
|
||||
saveSettingsDebounced: saveSettingsDebounced,
|
||||
onlineStatus: online_status,
|
||||
maxContext: Number(max_context),
|
||||
chatMetadata: chat_metadata,
|
||||
streamingProcessor,
|
||||
eventSource: eventSource,
|
||||
eventTypes: event_types,
|
||||
addOneMessage: addOneMessage,
|
||||
generate: Generate,
|
||||
sendStreamingRequest: sendStreamingRequest,
|
||||
sendGenerationRequest: sendGenerationRequest,
|
||||
stopGeneration: stopGeneration,
|
||||
getTokenCount: getTokenCount,
|
||||
extensionPrompts: extension_prompts,
|
||||
setExtensionPrompt: setExtensionPrompt,
|
||||
updateChatMetadata: updateChatMetadata,
|
||||
saveChat: saveChatConditional,
|
||||
openCharacterChat: openCharacterChat,
|
||||
openGroupChat: openGroupChat,
|
||||
saveMetadata: saveMetadata,
|
||||
sendSystemMessage: sendSystemMessage,
|
||||
activateSendButtons,
|
||||
deactivateSendButtons,
|
||||
saveReply,
|
||||
substituteParams,
|
||||
substituteParamsExtended,
|
||||
SlashCommandParser,
|
||||
SlashCommand,
|
||||
SlashCommandArgument,
|
||||
SlashCommandNamedArgument,
|
||||
ARGUMENT_TYPE,
|
||||
executeSlashCommandsWithOptions,
|
||||
/** @deprecated Use SlashCommandParser.addCommandObject() instead */
|
||||
registerSlashCommand: registerSlashCommand,
|
||||
/** @deprecated Use executeSlashCommandWithOptions instead */
|
||||
executeSlashCommands: executeSlashCommands,
|
||||
timestampToMoment: timestampToMoment,
|
||||
/** @deprecated Handlebars for extensions are no longer supported. */
|
||||
registerHelper: () => { },
|
||||
registerMacro: MacrosParser.registerMacro.bind(MacrosParser),
|
||||
unregisterMacro: MacrosParser.unregisterMacro.bind(MacrosParser),
|
||||
registerFunctionTool: ToolManager.registerFunctionTool.bind(ToolManager),
|
||||
unregisterFunctionTool: ToolManager.unregisterFunctionTool.bind(ToolManager),
|
||||
isToolCallingSupported: ToolManager.isToolCallingSupported.bind(ToolManager),
|
||||
canPerformToolCalls: ToolManager.canPerformToolCalls.bind(ToolManager),
|
||||
registerDebugFunction: registerDebugFunction,
|
||||
/** @deprecated Use renderExtensionTemplateAsync instead. */
|
||||
renderExtensionTemplate: renderExtensionTemplate,
|
||||
renderExtensionTemplateAsync: renderExtensionTemplateAsync,
|
||||
registerDataBankScraper: ScraperManager.registerDataBankScraper,
|
||||
/** @deprecated Use callGenericPopup or Popup instead. */
|
||||
callPopup: callPopup,
|
||||
callGenericPopup: callGenericPopup,
|
||||
showLoader: showLoader,
|
||||
hideLoader: hideLoader,
|
||||
mainApi: main_api,
|
||||
extensionSettings: extension_settings,
|
||||
ModuleWorkerWrapper: ModuleWorkerWrapper,
|
||||
getTokenizerModel: getTokenizerModel,
|
||||
generateQuietPrompt: generateQuietPrompt,
|
||||
writeExtensionField: writeExtensionField,
|
||||
getThumbnailUrl: getThumbnailUrl,
|
||||
selectCharacterById: selectCharacterById,
|
||||
messageFormatting: messageFormatting,
|
||||
shouldSendOnEnter: shouldSendOnEnter,
|
||||
isMobile: isMobile,
|
||||
t: t,
|
||||
translate: translate,
|
||||
tags: tags,
|
||||
tagMap: tag_map,
|
||||
menuType: menu_type,
|
||||
createCharacterData: create_save,
|
||||
/** @deprecated Legacy snake-case naming, compatibility with old extensions */
|
||||
event_types: event_types,
|
||||
Popup: Popup,
|
||||
POPUP_TYPE: POPUP_TYPE,
|
||||
POPUP_RESULT: POPUP_RESULT,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Formats a counter for a swipe view.
|
||||
* @param {number} current The current number of items.
|
||||
@@ -10827,13 +10739,13 @@ jQuery(async function () {
|
||||
});
|
||||
|
||||
$('#chat_import_file').on('change', async function (e) {
|
||||
var file = e.target.files[0];
|
||||
const file = e.target.files[0];
|
||||
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
var ext = file.name.match(/\.(\w+)$/);
|
||||
const ext = file.name.match(/\.(\w+)$/);
|
||||
if (
|
||||
!ext ||
|
||||
(ext[1].toLowerCase() != 'json' && ext[1].toLowerCase() != 'jsonl')
|
||||
@@ -10846,17 +10758,17 @@ jQuery(async function () {
|
||||
return;
|
||||
}
|
||||
|
||||
var format = ext[1].toLowerCase();
|
||||
const format = ext[1].toLowerCase();
|
||||
$('#chat_import_file_type').val(format);
|
||||
|
||||
var formData = new FormData($('#form_import_chat').get(0));
|
||||
const formData = new FormData($('#form_import_chat').get(0));
|
||||
formData.append('user_name', name1);
|
||||
$('#select_chat_div').html('');
|
||||
|
||||
if (selected_group) {
|
||||
await importGroupChat(formData);
|
||||
await importGroupChat(formData, e.originalEvent.target);
|
||||
} else {
|
||||
await importCharacterChat(formData);
|
||||
await importCharacterChat(formData, e.originalEvent.target);
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -75,6 +75,7 @@
|
||||
* @property {string} [source_url] - The source URL associated with the character.
|
||||
* @property {{full_path: string}} [chub] - The Chub-specific data associated with the character.
|
||||
* @property {{source: string[]}} [risuai] - The RisuAI-specific data associated with the character.
|
||||
* @property {{positive: string, negative: string}} [sd_character_prompt] - SD-specific data associated with the character.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@@ -5,6 +5,7 @@ import { showLoader } from './loader.js';
|
||||
import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
|
||||
import { renderTemplate, renderTemplateAsync } from './templates.js';
|
||||
import { isSubsetOf, setValueByPath } from './utils.js';
|
||||
import { getContext } from './st-context.js';
|
||||
export {
|
||||
getContext,
|
||||
getApiUrl,
|
||||
@@ -174,7 +175,6 @@ const extension_settings = {
|
||||
let modules = [];
|
||||
let activeExtensions = new Set();
|
||||
|
||||
const getContext = () => window['SillyTavern'].getContext();
|
||||
const getApiUrl = () => extension_settings.apiUrl;
|
||||
let connectedToApi = false;
|
||||
|
||||
|
@@ -62,6 +62,7 @@
|
||||
<option data-type="google" value="gemini-1.5-flash-8b-exp-0924">gemini-1.5-flash-8b-exp-0924</option>
|
||||
<option data-type="google" value="gemini-exp-1114">gemini-exp-1114</option>
|
||||
<option data-type="google" value="gemini-exp-1121">gemini-exp-1121</option>
|
||||
<option data-type="google" value="gemini-exp-1206">gemini-exp-1206</option>
|
||||
<option data-type="google" value="gemini-1.5-pro">gemini-1.5-pro</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-001">gemini-1.5-pro-001</option>
|
||||
|
@@ -60,7 +60,6 @@ import { ToolManager } from '../../tool-calling.js';
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = 'sd';
|
||||
const UPDATE_INTERVAL = 1000;
|
||||
// This is a 1x1 transparent PNG
|
||||
const PNG_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
|
||||
const CUSTOM_STOP_EVENT = 'sd_stop_generation';
|
||||
@@ -3687,8 +3686,6 @@ async function addSDGenButtons() {
|
||||
const button = $('#sd_gen');
|
||||
const dropdown = $('#sd_dropdown');
|
||||
dropdown.hide();
|
||||
button.hide();
|
||||
messageButton.hide();
|
||||
|
||||
let popper = Popper.createPopper(button.get(0), dropdown.get(0), {
|
||||
placement: 'top',
|
||||
@@ -3770,18 +3767,6 @@ function isValidState() {
|
||||
}
|
||||
}
|
||||
|
||||
async function moduleWorker() {
|
||||
if (isValidState()) {
|
||||
$('#sd_gen').show();
|
||||
$('.sd_message_gen').show();
|
||||
}
|
||||
else {
|
||||
$('#sd_gen').hide();
|
||||
$('.sd_message_gen').hide();
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(moduleWorker, UPDATE_INTERVAL);
|
||||
let buttonAbortController = null;
|
||||
|
||||
async function sdMessageButton(e) {
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
#sd_dropdown {
|
||||
z-index: 30000;
|
||||
backdrop-filter: blur(--SmartThemeBlurStrength);
|
||||
backdrop-filter: blur(var(--SmartThemeBlurStrength));
|
||||
}
|
||||
|
||||
#sd_comfy_open_workflow_editor {
|
||||
|
@@ -93,7 +93,7 @@
|
||||
.at-settings-separator {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 18x;
|
||||
padding: 18px;
|
||||
font-weight: bold;
|
||||
border-top: 1px solid #e1e1e1; /* Grey line */
|
||||
border-bottom: 1px solid #e1e1e1; /* Grey line */
|
||||
|
@@ -1265,18 +1265,20 @@ function getGroupCharacters({ doFilter, onlyMembers } = {}) {
|
||||
const thisGroup = openGroupId && groups.find((x) => x.id == openGroupId);
|
||||
let candidates = characters
|
||||
.filter((x) => isGroupMember(thisGroup, x.avatar) == onlyMembers)
|
||||
.map((x, index) => ({ item: x, id: index, type: 'character' }));
|
||||
|
||||
if (onlyMembers) {
|
||||
candidates.sort(sortMembersFn);
|
||||
} else {
|
||||
sortEntitiesList(candidates);
|
||||
}
|
||||
.map((x) => ({ item: x, id: characters.indexOf(x), type: 'character' }));
|
||||
|
||||
if (doFilter) {
|
||||
candidates = groupCandidatesFilter.applyFilters(candidates);
|
||||
}
|
||||
|
||||
if (onlyMembers) {
|
||||
candidates.sort(sortMembersFn);
|
||||
} else {
|
||||
const useFilterOrder = doFilter && !!$('#rm_group_filter').val();
|
||||
sortEntitiesList(candidates, useFilterOrder, groupCandidatesFilter);
|
||||
}
|
||||
|
||||
groupCandidatesFilter.clearFuzzySearchCaches();
|
||||
return candidates;
|
||||
}
|
||||
|
||||
@@ -1863,17 +1865,23 @@ export async function deleteGroupChat(groupId, chatId) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function importGroupChat(formData) {
|
||||
await jQuery.ajax({
|
||||
type: 'POST',
|
||||
url: '/api/chats/group/import',
|
||||
data: formData,
|
||||
beforeSend: function () {
|
||||
},
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: async function (data) {
|
||||
/**
|
||||
* Imports a group chat from a file and adds it to the group.
|
||||
* @param {FormData} formData Form data to send to the server
|
||||
* @param {EventTarget} eventTarget Element that triggered the import
|
||||
*/
|
||||
export async function importGroupChat(formData, eventTarget) {
|
||||
const headers = getRequestHeaders();
|
||||
delete headers['Content-Type'];
|
||||
const fetchResult = await fetch('/api/chats/group/import', {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
body: formData,
|
||||
cache: 'no-cache',
|
||||
});
|
||||
|
||||
if (fetchResult.ok) {
|
||||
const data = await fetchResult.json();
|
||||
if (data.res) {
|
||||
const chatId = data.res;
|
||||
const group = groups.find(x => x.id == selected_group);
|
||||
@@ -1884,11 +1892,11 @@ export async function importGroupChat(formData) {
|
||||
await displayPastChats();
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
$('#create_button').removeAttr('disabled');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (eventTarget instanceof HTMLInputElement) {
|
||||
eventTarget.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveGroupBookmarkChat(groupId, name, metadata, mesId) {
|
||||
|
@@ -379,8 +379,8 @@ export let selected_proxy = proxies[0];
|
||||
let openai_setting_names;
|
||||
let openai_settings;
|
||||
|
||||
|
||||
let promptManager = null;
|
||||
/** @type {import('./PromptManager.js').PromptManager} */
|
||||
export let promptManager = null;
|
||||
|
||||
async function validateReverseProxy() {
|
||||
if (!oai_settings.reverse_proxy) {
|
||||
@@ -1812,6 +1812,7 @@ async function sendOpenAIRequest(type, messages, signal) {
|
||||
const isPerplexity = oai_settings.chat_completion_source == chat_completion_sources.PERPLEXITY;
|
||||
const isGroq = oai_settings.chat_completion_source == chat_completion_sources.GROQ;
|
||||
const is01AI = oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI;
|
||||
const isNano = oai_settings.chat_completion_source == chat_completion_sources.NANOGPT;
|
||||
const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model);
|
||||
const isQuiet = type === 'quiet';
|
||||
const isImpersonate = type === 'impersonate';
|
||||
@@ -1971,7 +1972,7 @@ async function sendOpenAIRequest(type, messages, signal) {
|
||||
delete generate_data.stop;
|
||||
}
|
||||
|
||||
if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere) && oai_settings.seed >= 0) {
|
||||
if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere || isNano) && oai_settings.seed >= 0) {
|
||||
generate_data['seed'] = oai_settings.seed;
|
||||
}
|
||||
|
||||
@@ -4076,7 +4077,7 @@ async function onModelChange() {
|
||||
$('#openai_max_context').attr('max', max_2mil);
|
||||
} else if (value.includes('gemini-exp-1114') || value.includes('gemini-exp-1121')) {
|
||||
$('#openai_max_context').attr('max', max_32k);
|
||||
} else if (value.includes('gemini-1.5-pro')) {
|
||||
} else if (value.includes('gemini-1.5-pro') || value.includes('gemini-exp-1206')) {
|
||||
$('#openai_max_context').attr('max', max_2mil);
|
||||
} else if (value.includes('gemini-1.5-flash')) {
|
||||
$('#openai_max_context').attr('max', max_1mil);
|
||||
@@ -4760,6 +4761,7 @@ export function isImageInliningSupported() {
|
||||
'gemini-1.5-flash-8b-exp-0924',
|
||||
'gemini-exp-1114',
|
||||
'gemini-exp-1121',
|
||||
'gemini-exp-1206',
|
||||
'gemini-1.0-pro-vision-latest',
|
||||
'gemini-1.5-pro',
|
||||
'gemini-1.5-pro-latest',
|
||||
|
@@ -60,7 +60,6 @@ export {
|
||||
loadMovingUIState,
|
||||
collapseNewlines,
|
||||
playMessageSound,
|
||||
sortEntitiesList,
|
||||
fixMarkdown,
|
||||
power_user,
|
||||
send_on_enter_options,
|
||||
@@ -473,9 +472,9 @@ function switchCompactInputArea() {
|
||||
$('#compact_input_area').prop('checked', power_user.compact_input_area);
|
||||
}
|
||||
|
||||
export function switchSwipeNumAllMessages() {
|
||||
function switchSwipeNumAllMessages() {
|
||||
$('#show_swipe_num_all_messages').prop('checked', power_user.show_swipe_num_all_messages);
|
||||
$('.mes:not(.last_mes) .swipes-counter').css('opacity', '').toggle(power_user.show_swipe_num_all_messages);
|
||||
$('body').toggleClass('swipeAllMessages', !!power_user.show_swipe_num_all_messages);
|
||||
}
|
||||
|
||||
var originalSliderValues = [];
|
||||
@@ -1837,7 +1836,7 @@ async function loadContextSettings() {
|
||||
* Common function to perform fuzzy search with optional caching
|
||||
* @param {string} type - Type of search from fuzzySearchCategories
|
||||
* @param {any[]} data - Data array to search in
|
||||
* @param {Array<{name: string, weight: number, getFn?: Function}>} keys - Fuse.js keys configuration
|
||||
* @param {Array<{name: string, weight: number, getFn?: (obj: any) => string}>} keys - Fuse.js keys configuration
|
||||
* @param {string} searchValue - The search term
|
||||
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
|
||||
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
|
||||
@@ -1851,7 +1850,6 @@ function performFuzzySearch(type, data, keys, searchValue, fuzzySearchCaches = n
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const fuse = new Fuse(data, {
|
||||
keys: keys,
|
||||
includeScore: true,
|
||||
@@ -1925,7 +1923,7 @@ export function fuzzySearchPersonas(data, searchValue, fuzzySearchCaches = null)
|
||||
const mappedData = data.map(x => ({
|
||||
key: x,
|
||||
name: power_user.personas[x] ?? '',
|
||||
description: power_user.persona_descriptions[x]?.description ?? ''
|
||||
description: power_user.persona_descriptions[x]?.description ?? '',
|
||||
}));
|
||||
|
||||
const keys = [
|
||||
@@ -2080,19 +2078,22 @@ const compareFunc = (first, second) => {
|
||||
/**
|
||||
* Sorts an array of entities based on the current sort settings
|
||||
* @param {any[]} entities An array of objects with an `item` property
|
||||
* @param {boolean} forceSearch Whether to force search sorting
|
||||
* @param {import('./filters.js').FilterHelper} [filterHelper=null] Filter helper to use
|
||||
*/
|
||||
function sortEntitiesList(entities) {
|
||||
export function sortEntitiesList(entities, forceSearch, filterHelper = null) {
|
||||
filterHelper = filterHelper ?? entitiesFilter;
|
||||
if (power_user.sort_field == undefined || entities.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (power_user.sort_order === 'random') {
|
||||
const isSearch = forceSearch || $('#character_sort_order option[data-field="search"]').is(':selected');
|
||||
|
||||
if (!isSearch && power_user.sort_order === 'random') {
|
||||
shuffle(entities);
|
||||
return;
|
||||
}
|
||||
|
||||
const isSearch = $('#character_sort_order option[data-field="search"]').is(':selected');
|
||||
|
||||
entities.sort((a, b) => {
|
||||
// Sort tags/folders will always be at the top
|
||||
if (a.type === 'tag' && b.type !== 'tag') {
|
||||
@@ -2104,8 +2105,8 @@ function sortEntitiesList(entities) {
|
||||
|
||||
// If we have search sorting, we take scores and use those
|
||||
if (isSearch) {
|
||||
const aScore = entitiesFilter.getScore(FILTER_TYPES.SEARCH, `${a.type}.${a.id}`);
|
||||
const bScore = entitiesFilter.getScore(FILTER_TYPES.SEARCH, `${b.type}.${b.id}`);
|
||||
const aScore = filterHelper.getScore(FILTER_TYPES.SEARCH, `${a.type}.${a.id}`);
|
||||
const bScore = filterHelper.getScore(FILTER_TYPES.SEARCH, `${b.type}.${b.id}`);
|
||||
return (aScore - bScore);
|
||||
}
|
||||
|
||||
|
@@ -52,7 +52,7 @@ import { hideChatMessageRange } from './chats.js';
|
||||
import { getContext, saveMetadataDebounced } from './extensions.js';
|
||||
import { getRegexedString, regex_placement } from './extensions/regex/engine.js';
|
||||
import { findGroupMemberId, groups, is_group_generating, openGroupById, resetSelectedGroup, saveGroupChat, selected_group } from './group-chats.js';
|
||||
import { chat_completion_sources, oai_settings, setupChatCompletionPromptManager } from './openai.js';
|
||||
import { chat_completion_sources, oai_settings, promptManager } from './openai.js';
|
||||
import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockState, togglePersonaLock, user_avatar } from './personas.js';
|
||||
import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js';
|
||||
import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js';
|
||||
@@ -269,7 +269,7 @@ export function initDefaultSlashCommands() {
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'at',
|
||||
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
|
||||
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
|
||||
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
|
||||
}),
|
||||
@@ -325,7 +325,7 @@ export function initDefaultSlashCommands() {
|
||||
),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'at',
|
||||
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
|
||||
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
|
||||
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
|
||||
}),
|
||||
@@ -388,7 +388,7 @@ export function initDefaultSlashCommands() {
|
||||
),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'at',
|
||||
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
|
||||
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
|
||||
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
|
||||
}),
|
||||
@@ -606,7 +606,7 @@ export function initDefaultSlashCommands() {
|
||||
),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'at',
|
||||
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
|
||||
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
|
||||
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
|
||||
}),
|
||||
@@ -1698,6 +1698,49 @@ export function initDefaultSlashCommands() {
|
||||
],
|
||||
helpString: 'Sets the model for the current API. Gets the current model name if no argument is provided.',
|
||||
}));
|
||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: 'getpromptentry',
|
||||
aliases: ['getpromptentries'],
|
||||
callback: getPromptEntryCallback,
|
||||
returns: 'true/false state of prompt(s)',
|
||||
namedArgumentList: [
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'identifier',
|
||||
description: 'Prompt entry identifier(s) to retrieve',
|
||||
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
|
||||
acceptsMultiple: true,
|
||||
enumProvider: () =>
|
||||
promptManager.serviceSettings.prompts
|
||||
.map(prompt => prompt.identifier)
|
||||
.map(identifier => new SlashCommandEnumValue(identifier)),
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'name',
|
||||
description: 'Prompt entry name(s) to retrieve',
|
||||
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
|
||||
acceptsMultiple: true,
|
||||
enumProvider: () =>
|
||||
promptManager.serviceSettings.prompts
|
||||
.map(prompt => prompt.name)
|
||||
.map(name => new SlashCommandEnumValue(name)),
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'return',
|
||||
description: 'Whether the return will be simple, a list, or a dict.',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
defaultValue: 'simple',
|
||||
enumList: ['simple', 'list', 'dict'],
|
||||
}),
|
||||
],
|
||||
helpString: `
|
||||
<div>
|
||||
Gets the state of the specified prompt entries.
|
||||
</div>
|
||||
<div>
|
||||
If <code>return</code> is <code>simple</code> (default) then the return will be a single value if only one value was retrieved; otherwise uses a dict (if the identifier parameter was used) or a list.
|
||||
</div>
|
||||
`,
|
||||
}));
|
||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: 'setpromptentry',
|
||||
aliases: ['setpromptentries'],
|
||||
@@ -1709,7 +1752,6 @@ export function initDefaultSlashCommands() {
|
||||
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
|
||||
acceptsMultiple: true,
|
||||
enumProvider: () => {
|
||||
const promptManager = setupChatCompletionPromptManager(oai_settings);
|
||||
const prompts = promptManager.serviceSettings.prompts;
|
||||
return prompts.map(prompt => new SlashCommandEnumValue(prompt.identifier, prompt.name, enumTypes.enum));
|
||||
},
|
||||
@@ -1720,7 +1762,6 @@ export function initDefaultSlashCommands() {
|
||||
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
|
||||
acceptsMultiple: true,
|
||||
enumProvider: () => {
|
||||
const promptManager = setupChatCompletionPromptManager(oai_settings);
|
||||
const prompts = promptManager.serviceSettings.prompts;
|
||||
return prompts.map(prompt => new SlashCommandEnumValue(prompt.name, prompt.identifier, enumTypes.enum));
|
||||
},
|
||||
@@ -3025,7 +3066,7 @@ async function sendUserMessageCallback(args, text) {
|
||||
let insertAt = Number(args?.at);
|
||||
|
||||
// Convert possible depth parameter to index
|
||||
if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) {
|
||||
if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt, -0))) {
|
||||
// Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
|
||||
insertAt = chat.length + insertAt;
|
||||
}
|
||||
@@ -3399,7 +3440,7 @@ export async function sendMessageAs(args, text) {
|
||||
let insertAt = Number(args.at);
|
||||
|
||||
// Convert possible depth parameter to index
|
||||
if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) {
|
||||
if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt, -0))) {
|
||||
// Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
|
||||
insertAt = chat.length + insertAt;
|
||||
}
|
||||
@@ -3453,7 +3494,7 @@ export async function sendNarratorMessage(args, text) {
|
||||
let insertAt = Number(args.at);
|
||||
|
||||
// Convert possible depth parameter to index
|
||||
if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) {
|
||||
if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt, -0))) {
|
||||
// Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
|
||||
insertAt = chat.length + insertAt;
|
||||
}
|
||||
@@ -3542,7 +3583,7 @@ async function sendCommentMessage(args, text) {
|
||||
let insertAt = Number(args.at);
|
||||
|
||||
// Convert possible depth parameter to index
|
||||
if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) {
|
||||
if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt, -0))) {
|
||||
// Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
|
||||
insertAt = chat.length + insertAt;
|
||||
}
|
||||
@@ -3786,6 +3827,75 @@ function modelCallback(args, model) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state of prompt entries (toggles) either via identifier/uuid or name.
|
||||
* @param {object} args Object containing arguments
|
||||
* @param {string} args.identifier Select prompt entry using an identifier (uuid)
|
||||
* @param {string} args.name Select prompt entry using name
|
||||
* @param {string} args.return The type of return value to use (simple, list, dict)
|
||||
* @returns {Object} An object containing the states of the requested prompt entries
|
||||
*/
|
||||
function getPromptEntryCallback(args) {
|
||||
const prompts = promptManager.serviceSettings.prompts;
|
||||
let returnType = args.return ?? 'simple';
|
||||
|
||||
function parseArgs(arg) {
|
||||
// Arg is already an array
|
||||
if (Array.isArray(arg)) {
|
||||
return arg;
|
||||
}
|
||||
const list = [];
|
||||
try {
|
||||
// Arg is a JSON-stringified array
|
||||
const parsedArg = JSON.parse(arg);
|
||||
list.push(...Array.isArray(parsedArg) ? parsedArg : [arg]);
|
||||
} catch {
|
||||
// Arg is a string
|
||||
list.push(arg);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
let identifiersList = parseArgs(args.identifier);
|
||||
let nameList = parseArgs(args.name);
|
||||
|
||||
// Check if identifiers exists in prompt, else remove from list
|
||||
if (identifiersList.length !== 0) {
|
||||
identifiersList = identifiersList.filter(identifier => prompts.some(prompt => prompt.identifier === identifier));
|
||||
}
|
||||
|
||||
if (nameList.length !== 0) {
|
||||
nameList.forEach(name => {
|
||||
let identifiers = prompts
|
||||
.filter(entry => entry.name === name)
|
||||
.map(entry => entry.identifier);
|
||||
identifiersList = identifiersList.concat(identifiers);
|
||||
});
|
||||
}
|
||||
|
||||
// Get the state for each prompt entry
|
||||
let promptStates = new Map();
|
||||
identifiersList.forEach(identifier => {
|
||||
const promptOrderEntry = promptManager.getPromptOrderEntry(promptManager.activeCharacter, identifier);
|
||||
if (promptOrderEntry) {
|
||||
promptStates.set(identifier, promptOrderEntry.enabled);
|
||||
}
|
||||
});
|
||||
|
||||
// If return is simple (default) but more than one prompt state was retrieved, then change return type
|
||||
if (returnType === 'simple' && promptStates.size > 1) {
|
||||
returnType = args.identifier ? 'dict' : 'list';
|
||||
}
|
||||
|
||||
const result = (() => {
|
||||
if (returnType === 'list') return [...promptStates.values()];
|
||||
if (returnType === 'dict') return Object.fromEntries(promptStates);
|
||||
return [...promptStates.values()][0];
|
||||
})();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets state of prompt entries (toggles) either via identifier/uuid or name.
|
||||
* @param {object} args Object containing arguments
|
||||
@@ -3796,7 +3906,6 @@ function modelCallback(args, model) {
|
||||
*/
|
||||
function setPromptEntryCallback(args, targetState) {
|
||||
// needs promptManager to manipulate prompt entries
|
||||
const promptManager = setupChatCompletionPromptManager(oai_settings);
|
||||
const prompts = promptManager.serviceSettings.prompts;
|
||||
|
||||
function parseArgs(arg) {
|
||||
|
171
public/scripts/st-context.js
Normal file
171
public/scripts/st-context.js
Normal file
@@ -0,0 +1,171 @@
|
||||
import {
|
||||
activateSendButtons,
|
||||
addOneMessage,
|
||||
callPopup,
|
||||
characters,
|
||||
chat,
|
||||
chat_metadata,
|
||||
create_save,
|
||||
deactivateSendButtons,
|
||||
event_types,
|
||||
eventSource,
|
||||
extension_prompts,
|
||||
Generate,
|
||||
generateQuietPrompt,
|
||||
getCurrentChatId,
|
||||
getRequestHeaders,
|
||||
getThumbnailUrl,
|
||||
main_api,
|
||||
max_context,
|
||||
menu_type,
|
||||
messageFormatting,
|
||||
name1,
|
||||
name2,
|
||||
online_status,
|
||||
openCharacterChat,
|
||||
reloadCurrentChat,
|
||||
renameChat,
|
||||
saveChatConditional,
|
||||
saveMetadata,
|
||||
saveReply,
|
||||
saveSettingsDebounced,
|
||||
selectCharacterById,
|
||||
sendGenerationRequest,
|
||||
sendStreamingRequest,
|
||||
sendSystemMessage,
|
||||
setExtensionPrompt,
|
||||
stopGeneration,
|
||||
streamingProcessor,
|
||||
substituteParams,
|
||||
substituteParamsExtended,
|
||||
this_chid,
|
||||
updateChatMetadata,
|
||||
} from '../script.js';
|
||||
import {
|
||||
extension_settings,
|
||||
ModuleWorkerWrapper,
|
||||
renderExtensionTemplate,
|
||||
renderExtensionTemplateAsync,
|
||||
writeExtensionField,
|
||||
} from './extensions.js';
|
||||
import { groups, openGroupChat, selected_group } from './group-chats.js';
|
||||
import { t, translate } from './i18n.js';
|
||||
import { hideLoader, showLoader } from './loader.js';
|
||||
import { MacrosParser } from './macros.js';
|
||||
import { oai_settings } from './openai.js';
|
||||
import { callGenericPopup, Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
|
||||
import { power_user, registerDebugFunction } from './power-user.js';
|
||||
import { isMobile, shouldSendOnEnter } from './RossAscends-mods.js';
|
||||
import { ScraperManager } from './scrapers.js';
|
||||
import { executeSlashCommands, executeSlashCommandsWithOptions, registerSlashCommand } from './slash-commands.js';
|
||||
import { SlashCommand } from './slash-commands/SlashCommand.js';
|
||||
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
|
||||
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
|
||||
import { tag_map, tags } from './tags.js';
|
||||
import { textgenerationwebui_settings } from './textgen-settings.js';
|
||||
import { getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js';
|
||||
import { ToolManager } from './tool-calling.js';
|
||||
import { timestampToMoment } from './utils.js';
|
||||
|
||||
export function getContext() {
|
||||
return {
|
||||
chat,
|
||||
characters,
|
||||
groups,
|
||||
name1,
|
||||
name2,
|
||||
characterId: this_chid,
|
||||
groupId: selected_group,
|
||||
chatId: selected_group
|
||||
? groups.find(x => x.id == selected_group)?.chat_id
|
||||
: (characters[this_chid]?.chat),
|
||||
getCurrentChatId,
|
||||
getRequestHeaders,
|
||||
reloadCurrentChat,
|
||||
renameChat,
|
||||
saveSettingsDebounced,
|
||||
onlineStatus: online_status,
|
||||
maxContext: Number(max_context),
|
||||
chatMetadata: chat_metadata,
|
||||
streamingProcessor,
|
||||
eventSource,
|
||||
eventTypes: event_types,
|
||||
addOneMessage,
|
||||
generate: Generate,
|
||||
sendStreamingRequest,
|
||||
sendGenerationRequest,
|
||||
stopGeneration,
|
||||
/** @deprecated Use getTokenCountAsync instead */
|
||||
getTokenCount,
|
||||
getTokenCountAsync,
|
||||
extensionPrompts: extension_prompts,
|
||||
setExtensionPrompt,
|
||||
updateChatMetadata,
|
||||
saveChat: saveChatConditional,
|
||||
openCharacterChat,
|
||||
openGroupChat,
|
||||
saveMetadata,
|
||||
sendSystemMessage,
|
||||
activateSendButtons,
|
||||
deactivateSendButtons,
|
||||
saveReply,
|
||||
substituteParams,
|
||||
substituteParamsExtended,
|
||||
SlashCommandParser,
|
||||
SlashCommand,
|
||||
SlashCommandArgument,
|
||||
SlashCommandNamedArgument,
|
||||
ARGUMENT_TYPE,
|
||||
executeSlashCommandsWithOptions,
|
||||
/** @deprecated Use SlashCommandParser.addCommandObject() instead */
|
||||
registerSlashCommand,
|
||||
/** @deprecated Use executeSlashCommandWithOptions instead */
|
||||
executeSlashCommands,
|
||||
timestampToMoment,
|
||||
/** @deprecated Handlebars for extensions are no longer supported. */
|
||||
registerHelper: () => { },
|
||||
registerMacro: MacrosParser.registerMacro.bind(MacrosParser),
|
||||
unregisterMacro: MacrosParser.unregisterMacro.bind(MacrosParser),
|
||||
registerFunctionTool: ToolManager.registerFunctionTool.bind(ToolManager),
|
||||
unregisterFunctionTool: ToolManager.unregisterFunctionTool.bind(ToolManager),
|
||||
isToolCallingSupported: ToolManager.isToolCallingSupported.bind(ToolManager),
|
||||
canPerformToolCalls: ToolManager.canPerformToolCalls.bind(ToolManager),
|
||||
registerDebugFunction,
|
||||
/** @deprecated Use renderExtensionTemplateAsync instead. */
|
||||
renderExtensionTemplate,
|
||||
renderExtensionTemplateAsync,
|
||||
registerDataBankScraper: ScraperManager.registerDataBankScraper.bind(ScraperManager),
|
||||
/** @deprecated Use callGenericPopup or Popup instead. */
|
||||
callPopup,
|
||||
callGenericPopup,
|
||||
showLoader,
|
||||
hideLoader,
|
||||
mainApi: main_api,
|
||||
extensionSettings: extension_settings,
|
||||
ModuleWorkerWrapper,
|
||||
getTokenizerModel,
|
||||
generateQuietPrompt,
|
||||
writeExtensionField,
|
||||
getThumbnailUrl,
|
||||
selectCharacterById,
|
||||
messageFormatting,
|
||||
shouldSendOnEnter,
|
||||
isMobile,
|
||||
t,
|
||||
translate,
|
||||
tags,
|
||||
tagMap: tag_map,
|
||||
menuType: menu_type,
|
||||
createCharacterData: create_save,
|
||||
/** @deprecated Legacy snake-case naming, compatibility with old extensions */
|
||||
event_types: event_types,
|
||||
Popup,
|
||||
POPUP_TYPE,
|
||||
POPUP_RESULT,
|
||||
chatCompletionSettings: oai_settings,
|
||||
textCompletionSettings: textgenerationwebui_settings,
|
||||
powerUserSettings: power_user,
|
||||
};
|
||||
}
|
||||
|
||||
export default getContext;
|
@@ -4,8 +4,17 @@
|
||||
<div id="copyPromptToClipboard" class="fa-solid fa-copy menu_button" title="Copy Prompt" data-i18n="[title]Copy Prompt"></div>
|
||||
<div id="diffPrevPrompt" class="fa-solid fa-code-compare menu_button" title="Show Prompt Differences" data-i18n="[title]Show Prompt Differences"></div>
|
||||
</h3>
|
||||
API/Model Used: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}– {{modelUsed}}{{/if}}<br>
|
||||
Tokenizer: {{selectedTokenizer}}<br>
|
||||
<div>
|
||||
<div>
|
||||
API/Model: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}– {{modelUsed}}{{/if}}
|
||||
</div>
|
||||
<div>
|
||||
<small>Preset: {{presetName}}</small>
|
||||
<span>|</span>
|
||||
<small>Tokenizer: {{selectedTokenizer}}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="tokenItemizingSubclass">
|
||||
Only the white numbers really matter. All numbers are estimates.
|
||||
Grey color items may not have been included in the context due to certain prompt format settings.
|
||||
|
@@ -4,8 +4,17 @@
|
||||
<div id="copyPromptToClipboard" class="fa-solid fa-copy menu_button" title="Copy Prompt" data-i18n="[title]Copy Prompt"></div>
|
||||
<div id="diffPrevPrompt" class="fa-solid fa-code-compare menu_button" title="Show Prompt Differences" data-i18n="[title]Show Prompt Differences"></div>
|
||||
</h3>
|
||||
API/Model Used: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}– {{modelUsed}}{{/if}}<br>
|
||||
Tokenizer: {{selectedTokenizer}}<br>
|
||||
<div>
|
||||
<div>
|
||||
API/Model: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}– {{modelUsed}}{{/if}}
|
||||
</div>
|
||||
<div>
|
||||
<small>Preset: {{presetName}}</small>
|
||||
<span>|</span>
|
||||
<small>Tokenizer: {{selectedTokenizer}}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="tokenItemizingSubclass">
|
||||
Only the white numbers really matter. All numbers are estimates.
|
||||
Grey color items may not have been included in the context due to certain prompt format settings.
|
||||
|
@@ -21,7 +21,8 @@
|
||||
<li><tt>{{user}}</tt> – <span data-i18n="help_macros_15">your current Persona username</span></li>
|
||||
<li><tt>{{char}}</tt> – <span data-i18n="help_macros_16">the Character's name</span></li>
|
||||
<li><tt>{{char_version}}</tt> – <span data-i18n="help_macros_17">the Character's version number</span></li>
|
||||
<li><tt>{{group}}</tt> – <span data-i18n="help_macros_18">a comma-separated list of group member names or the character name in solo chats. Alias: {{charIfNotGroup}}</span></li>
|
||||
<li><tt>{{group}}</tt> – <span data-i18n="help_macros_18">a comma-separated list of group member names (including muted) or the character name in solo chats. Alias: {{charIfNotGroup}}</span></li>
|
||||
<li><tt>{{groupNotMuted}}</tt> – <span data-i18n="help_groupNotMuted">the same as {{group}}, but excludes muted members</span></li>
|
||||
<li><tt>{{model}}</tt> – <span data-i18n="help_macros_19">a text generation model name for the currently selected API. </span><b data-i18n="Can be inaccurate!">Can be inaccurate!</b></li>
|
||||
<li><tt>{{lastMessage}}</tt> – <span data-i18n="help_macros_20">the text of the latest chat message.</span></li>
|
||||
<li><tt>{{lastUserMessage}}</tt> – <span data-i18n="help_macros_lastUser">the text of the latest user chat message.</span></li>
|
||||
|
@@ -18,13 +18,6 @@ import { getCurrentDreamGenModelTokenizer, getCurrentOpenRouterModelTokenizer }
|
||||
import { ENCODE_TOKENIZERS, TEXTGEN_TOKENIZERS, getTextTokens, tokenizers } from './tokenizers.js';
|
||||
import { getSortableDelay, onlyUnique } from './utils.js';
|
||||
|
||||
export {
|
||||
settings as textgenerationwebui_settings,
|
||||
loadTextGenSettings,
|
||||
generateTextGenWithStreaming,
|
||||
formatTextGenURL,
|
||||
};
|
||||
|
||||
export const textgen_types = {
|
||||
OOBA: 'ooba',
|
||||
MANCER: 'mancer',
|
||||
@@ -197,6 +190,10 @@ const settings = {
|
||||
featherless_model: '',
|
||||
};
|
||||
|
||||
export {
|
||||
settings as textgenerationwebui_settings,
|
||||
};
|
||||
|
||||
export let textgenerationwebui_banned_in_macros = [];
|
||||
|
||||
export let textgenerationwebui_presets = [];
|
||||
@@ -327,7 +324,7 @@ async function selectPreset(name) {
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function formatTextGenURL(value) {
|
||||
export function formatTextGenURL(value) {
|
||||
try {
|
||||
const noFormatTypes = [MANCER, TOGETHERAI, INFERMATICAI, DREAMGEN, OPENROUTER];
|
||||
if (noFormatTypes.includes(settings.type)) {
|
||||
@@ -465,7 +462,7 @@ function calculateLogitBias() {
|
||||
return result;
|
||||
}
|
||||
|
||||
function loadTextGenSettings(data, loadedSettings) {
|
||||
export function loadTextGenSettings(data, loadedSettings) {
|
||||
textgenerationwebui_presets = convertPresets(data.textgenerationwebui_presets);
|
||||
textgenerationwebui_preset_names = data.textgenerationwebui_preset_names ?? [];
|
||||
Object.assign(settings, loadedSettings.textgenerationwebui_settings ?? {});
|
||||
@@ -889,7 +886,7 @@ function setSettingByName(setting, value, trigger) {
|
||||
* @returns {Promise<(function(): AsyncGenerator<{swipes: [], text: string, toolCalls: [], logprobs: {token: string, topLogprobs: Candidate[]}|null}, void, *>)|*>}
|
||||
* @throws {Error} - If the response status is not OK, or from within the generator
|
||||
*/
|
||||
async function generateTextGenWithStreaming(generate_data, signal) {
|
||||
export async function generateTextGenWithStreaming(generate_data, signal) {
|
||||
generate_data.stream = true;
|
||||
|
||||
const response = await fetch('/api/backends/text-completions/generate', {
|
||||
@@ -1340,6 +1337,9 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
|
||||
'dry_sequence_breakers': sequenceBreakers,
|
||||
};
|
||||
params = Object.assign(params, llamaCppParams);
|
||||
if (!Array.isArray(sequenceBreakers) || sequenceBreakers.length === 0) {
|
||||
delete params.dry_sequence_breakers;
|
||||
}
|
||||
}
|
||||
|
||||
eventSource.emitAndWait(event_types.TEXT_COMPLETION_SETTINGS_READY, params);
|
||||
|
@@ -8,8 +8,6 @@ import { kai_flags } from './kai-settings.js';
|
||||
import { textgen_types, textgenerationwebui_settings as textgen_settings, getTextGenServer, getTextGenModel } from './textgen-settings.js';
|
||||
import { getCurrentDreamGenModelTokenizer, getCurrentOpenRouterModelTokenizer, openRouterModels } from './textgen-models.js';
|
||||
|
||||
const { OOBA, TABBY, KOBOLDCPP, VLLM, APHRODITE, LLAMACPP, OPENROUTER, DREAMGEN } = textgen_types;
|
||||
|
||||
export const CHARACTERS_PER_TOKEN_RATIO = 3.35;
|
||||
export const TOKENIZER_WARNING_KEY = 'tokenizationWarningShown';
|
||||
export const TOKENIZER_SUPPORTED_KEY = 'tokenizationSupported';
|
||||
@@ -52,8 +50,12 @@ export const ENCODE_TOKENIZERS = [
|
||||
//tokenizers.NERD2,
|
||||
];
|
||||
|
||||
// A list of Text Completion sources that support remote tokenization.
|
||||
export const TEXTGEN_TOKENIZERS = [OOBA, TABBY, KOBOLDCPP, LLAMACPP, VLLM, APHRODITE];
|
||||
/**
|
||||
* A list of Text Completion sources that support remote tokenization.
|
||||
* Populated in initTokenziers due to circular dependencies.
|
||||
* @type {string[]}
|
||||
*/
|
||||
export const TEXTGEN_TOKENIZERS = [];
|
||||
|
||||
const TOKENIZER_URLS = {
|
||||
[tokenizers.GPT2]: {
|
||||
@@ -287,7 +289,7 @@ export function getTokenizerBestMatch(forApi) {
|
||||
const hasTokenizerError = sessionStorage.getItem(TOKENIZER_WARNING_KEY);
|
||||
const hasValidEndpoint = sessionStorage.getItem(TOKENIZER_SUPPORTED_KEY);
|
||||
const isConnected = online_status !== 'no_connection';
|
||||
const isTokenizerSupported = TEXTGEN_TOKENIZERS.includes(textgen_settings.type) && (textgen_settings.type !== OOBA || hasValidEndpoint);
|
||||
const isTokenizerSupported = TEXTGEN_TOKENIZERS.includes(textgen_settings.type) && (textgen_settings.type !== textgen_types.OOBA || hasValidEndpoint);
|
||||
|
||||
if (!hasTokenizerError && isConnected) {
|
||||
if (forApi === 'kobold' && kai_flags.can_use_tokenization) {
|
||||
@@ -297,10 +299,10 @@ export function getTokenizerBestMatch(forApi) {
|
||||
if (forApi === 'textgenerationwebui' && isTokenizerSupported) {
|
||||
return tokenizers.API_TEXTGENERATIONWEBUI;
|
||||
}
|
||||
if (forApi === 'textgenerationwebui' && textgen_settings.type === OPENROUTER) {
|
||||
if (forApi === 'textgenerationwebui' && textgen_settings.type === textgen_types.OPENROUTER) {
|
||||
return getCurrentOpenRouterModelTokenizer();
|
||||
}
|
||||
if (forApi === 'textgenerationwebui' && textgen_settings.type === DREAMGEN) {
|
||||
if (forApi === 'textgenerationwebui' && textgen_settings.type === textgen_types.DREAMGEN) {
|
||||
return getCurrentDreamGenModelTokenizer();
|
||||
}
|
||||
}
|
||||
@@ -576,7 +578,7 @@ export function getTokenizerModel() {
|
||||
|
||||
// And for OpenRouter (if not a site model, then it's impossible to determine the tokenizer)
|
||||
if (main_api == 'openai' && oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER && oai_settings.openrouter_model ||
|
||||
main_api == 'textgenerationwebui' && textgen_settings.type === OPENROUTER && textgen_settings.openrouter_model) {
|
||||
main_api == 'textgenerationwebui' && textgen_settings.type === textgen_types.OPENROUTER && textgen_settings.openrouter_model) {
|
||||
const model = main_api == 'openai'
|
||||
? model_list.find(x => x.id === oai_settings.openrouter_model)
|
||||
: openRouterModels.find(x => x.id === textgen_settings.openrouter_model);
|
||||
@@ -1121,6 +1123,14 @@ export function decodeTextTokens(tokenizerType, ids) {
|
||||
}
|
||||
|
||||
export async function initTokenizers() {
|
||||
TEXTGEN_TOKENIZERS.push(
|
||||
textgen_types.OOBA,
|
||||
textgen_types.TABBY,
|
||||
textgen_types.KOBOLDCPP,
|
||||
textgen_types.LLAMACPP,
|
||||
textgen_types.VLLM,
|
||||
textgen_types.APHRODITE,
|
||||
);
|
||||
await loadTokenCache();
|
||||
registerDebugFunction('resetTokenCache', 'Reset token cache', 'Purges the calculated token counts. Use this if you want to force a full re-tokenization of all chats or suspect the token counts are wrong.', resetTokenCache);
|
||||
}
|
||||
|
@@ -947,7 +947,7 @@ export class ToolManager {
|
||||
enumProvider: toolsEnumProvider,
|
||||
}),
|
||||
],
|
||||
callback: async (name) => {
|
||||
callback: async (_, name) => {
|
||||
if (typeof name !== 'string' || !name) {
|
||||
throw new Error('The unnamed argument must be a non-empty string.');
|
||||
}
|
||||
|
@@ -1040,8 +1040,14 @@ body .panelControlBar {
|
||||
height: var(--swipeCounterHeight);
|
||||
}
|
||||
|
||||
.mes:not(.last_mes) .swipes-counter {
|
||||
opacity: 0.3;
|
||||
body:not(.swipeAllMessages) .mes:not(.last_mes) .swipes-counter {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
body.swipeAllMessages .mes:not(.last_mes) .swipes-counter {
|
||||
/* Avoid expensive DOM queries */
|
||||
opacity: 0.3 !important;
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.swipe_left {
|
||||
|
@@ -280,7 +280,7 @@ async function sendMakerSuiteRequest(request, response) {
|
||||
delete generationConfig.stopSequences;
|
||||
}
|
||||
|
||||
const should_use_system_prompt = (model.includes('gemini-1.5-flash') || model.includes('gemini-1.5-pro') || model.includes('gemini-exp-1114') || model.includes('gemini-exp-1121')) && request.body.use_makersuite_sysprompt;
|
||||
const should_use_system_prompt = (model.includes('gemini-1.5-flash') || model.includes('gemini-1.5-pro') || model.startsWith('gemini-exp')) && request.body.use_makersuite_sysprompt;
|
||||
const prompt = convertGooglePrompt(request.body.messages, model, should_use_system_prompt, request.body.char_name, request.body.user_name);
|
||||
let body = {
|
||||
contents: prompt.contents,
|
||||
|
@@ -14,7 +14,7 @@ import jimp from 'jimp';
|
||||
|
||||
import { AVATAR_WIDTH, AVATAR_HEIGHT } from '../constants.js';
|
||||
import { jsonParser, urlencodedParser } from '../express-common.js';
|
||||
import { deepMerge, humanizedISO8601DateTime, tryParse, extractFileFromZipBuffer, MemoryLimitedMap } from '../util.js';
|
||||
import { deepMerge, humanizedISO8601DateTime, tryParse, extractFileFromZipBuffer, MemoryLimitedMap, getConfigValue } from '../util.js';
|
||||
import { TavernCardValidator } from '../validator/TavernCardValidator.js';
|
||||
import { parse, write } from '../character-card-parser.js';
|
||||
import { readWorldInfoFile } from './worldinfo.js';
|
||||
@@ -23,8 +23,9 @@ import { importRisuSprites } from './sprites.js';
|
||||
const defaultAvatarPath = './public/img/ai4.png';
|
||||
|
||||
// KV-store for parsed character data
|
||||
// 100 MB limit. Would take roughly 3000 characters to reach this limit
|
||||
const characterDataCache = new MemoryLimitedMap(1024 * 1024 * 100);
|
||||
const cacheCapacity = Number(getConfigValue('cardsCacheCapacity', 100)); // MB
|
||||
// With 100 MB limit it would take roughly 3000 characters to reach this limit
|
||||
const characterDataCache = new MemoryLimitedMap(1024 * 1024 * cacheCapacity);
|
||||
// Some Android devices require tighter memory management
|
||||
const isAndroid = process.platform === 'android';
|
||||
|
||||
|
@@ -190,6 +190,44 @@ function importCAIChat(userName, characterName, jsonData) {
|
||||
return newChats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a chat from Kobold Lite format.
|
||||
* @param {string} _userName User name
|
||||
* @param {string} _characterName Character name
|
||||
* @param {object} data JSON data
|
||||
* @returns {string} Chat data
|
||||
*/
|
||||
function importKoboldLiteChat(_userName, _characterName, data) {
|
||||
const inputToken = '{{[INPUT]}}';
|
||||
const outputToken = '{{[OUTPUT]}}';
|
||||
|
||||
/** @type {function(string): object} */
|
||||
function processKoboldMessage(msg) {
|
||||
const isUser = msg.includes(inputToken);
|
||||
return {
|
||||
name: isUser ? header.user_name : header.character_name,
|
||||
is_user: isUser,
|
||||
mes: msg.replaceAll(inputToken, '').replaceAll(outputToken, '').trim(),
|
||||
send_date: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
// Create the header
|
||||
const header = {
|
||||
user_name: String(data.savedsettings.chatname),
|
||||
character_name: String(data.savedsettings.chatopponent).split('||$||')[0],
|
||||
};
|
||||
// Format messages
|
||||
const formattedMessages = data.actions.map(processKoboldMessage);
|
||||
// Add prompt if available
|
||||
if (data.prompt) {
|
||||
formattedMessages.unshift(processKoboldMessage(data.prompt));
|
||||
}
|
||||
// Combine header and messages
|
||||
const chatData = [header, ...formattedMessages];
|
||||
return chatData.map(obj => JSON.stringify(obj)).join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattens `msg` and `swipes` data from Chub Chat format.
|
||||
* Only changes enough to make it compatible with the standard chat serialization format.
|
||||
@@ -413,7 +451,7 @@ router.post('/import', urlencodedParser, function (request, response) {
|
||||
const format = request.body.file_type;
|
||||
const avatarUrl = (request.body.avatar_url).replace('.png', '');
|
||||
const characterName = request.body.character_name;
|
||||
const userName = request.body.user_name || 'You';
|
||||
const userName = request.body.user_name || 'User';
|
||||
|
||||
if (!request.file) {
|
||||
return response.sendStatus(400);
|
||||
@@ -426,33 +464,38 @@ router.post('/import', urlencodedParser, function (request, response) {
|
||||
if (format === 'json') {
|
||||
fs.unlinkSync(pathToUpload);
|
||||
const jsonData = JSON.parse(data);
|
||||
if (jsonData.histories !== undefined) {
|
||||
// CAI Tools format
|
||||
const chats = importCAIChat(userName, characterName, jsonData);
|
||||
for (const chat of chats) {
|
||||
const fileName = `${characterName} - ${humanizedISO8601DateTime()} imported.jsonl`;
|
||||
const filePath = path.join(request.user.directories.chats, avatarUrl, fileName);
|
||||
writeFileAtomicSync(filePath, chat, 'utf8');
|
||||
}
|
||||
return response.send({ res: true });
|
||||
} else if (Array.isArray(jsonData.data_visible)) {
|
||||
// oobabooga's format
|
||||
const chat = importOobaChat(userName, characterName, jsonData);
|
||||
const fileName = `${characterName} - ${humanizedISO8601DateTime()} imported.jsonl`;
|
||||
const filePath = path.join(request.user.directories.chats, avatarUrl, fileName);
|
||||
writeFileAtomicSync(filePath, chat, 'utf8');
|
||||
return response.send({ res: true });
|
||||
} else if (Array.isArray(jsonData.messages)) {
|
||||
// Agnai format
|
||||
const chat = importAgnaiChat(userName, characterName, jsonData);
|
||||
const fileName = `${characterName} - ${humanizedISO8601DateTime()} imported.jsonl`;
|
||||
const filePath = path.join(request.user.directories.chats, avatarUrl, fileName);
|
||||
writeFileAtomicSync(filePath, chat, 'utf8');
|
||||
return response.send({ res: true });
|
||||
} else {
|
||||
|
||||
/** @type {function(string, string, object): string|string[]} */
|
||||
let importFunc;
|
||||
|
||||
if (jsonData.savedsettings !== undefined) { // Kobold Lite format
|
||||
importFunc = importKoboldLiteChat;
|
||||
} else if (jsonData.histories !== undefined) { // CAI Tools format
|
||||
importFunc = importCAIChat;
|
||||
} else if (Array.isArray(jsonData.data_visible)) { // oobabooga's format
|
||||
importFunc = importOobaChat;
|
||||
} else if (Array.isArray(jsonData.messages)) { // Agnai's format
|
||||
importFunc = importAgnaiChat;
|
||||
} else { // Unknown format
|
||||
console.log('Incorrect chat format .json');
|
||||
return response.send({ error: true });
|
||||
}
|
||||
|
||||
const handleChat = (chat) => {
|
||||
const fileName = `${characterName} - ${humanizedISO8601DateTime()} imported.jsonl`;
|
||||
const filePath = path.join(request.user.directories.chats, avatarUrl, fileName);
|
||||
writeFileAtomicSync(filePath, chat, 'utf8');
|
||||
};
|
||||
|
||||
const chat = importFunc(userName, characterName, jsonData);
|
||||
|
||||
if (Array.isArray(chat)) {
|
||||
chat.forEach(handleChat);
|
||||
} else {
|
||||
handleChat(chat);
|
||||
}
|
||||
|
||||
return response.send({ res: true });
|
||||
}
|
||||
|
||||
if (format === 'jsonl') {
|
||||
@@ -561,11 +604,15 @@ router.post('/search', jsonParser, function (request, response) {
|
||||
|
||||
let targetGroup;
|
||||
for (const groupFile of groupFiles) {
|
||||
try {
|
||||
const groupData = JSON.parse(fs.readFileSync(path.join(groupDir, groupFile), 'utf8'));
|
||||
if (groupData.id === group_id) {
|
||||
targetGroup = groupData;
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(groupFile, 'group file is corrupted:', error);
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetGroup?.chats) {
|
||||
|
@@ -67,17 +67,17 @@ router.post('/login', jsonParser, async (request, response) => {
|
||||
const user = await storage.getItem(toKey(request.body.handle));
|
||||
|
||||
if (!user) {
|
||||
console.log('Login failed: User not found');
|
||||
console.log('Login failed: User', request.body.handle, 'not found');
|
||||
return response.status(403).json({ error: 'Incorrect credentials' });
|
||||
}
|
||||
|
||||
if (!user.enabled) {
|
||||
console.log('Login failed: User is disabled');
|
||||
console.log('Login failed: User', user.handle, 'is disabled');
|
||||
return response.status(403).json({ error: 'User is disabled' });
|
||||
}
|
||||
|
||||
if (user.password && user.password !== getPasswordHash(request.body.password, user.salt)) {
|
||||
console.log('Login failed: Incorrect password');
|
||||
console.log('Login failed: Incorrect password for', user.handle);
|
||||
return response.status(403).json({ error: 'Incorrect credentials' });
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ router.post('/login', jsonParser, async (request, response) => {
|
||||
|
||||
await loginLimiter.delete(ip);
|
||||
request.session.handle = user.handle;
|
||||
console.log('Login successful:', user.handle, request.session);
|
||||
console.log('Login successful:', user.handle, 'from', ip, 'at', new Date().toLocaleString());
|
||||
return response.json({ handle: user.handle });
|
||||
} catch (error) {
|
||||
if (error instanceof RateLimiterRes) {
|
||||
@@ -115,12 +115,12 @@ router.post('/recover-step1', jsonParser, async (request, response) => {
|
||||
const user = await storage.getItem(toKey(request.body.handle));
|
||||
|
||||
if (!user) {
|
||||
console.log('Recover step 1 failed: User not found');
|
||||
console.log('Recover step 1 failed: User', request.body.handle, 'not found');
|
||||
return response.status(404).json({ error: 'User not found' });
|
||||
}
|
||||
|
||||
if (!user.enabled) {
|
||||
console.log('Recover step 1 failed: User is disabled');
|
||||
console.log('Recover step 1 failed: User', user.handle, 'is disabled');
|
||||
return response.status(403).json({ error: 'User is disabled' });
|
||||
}
|
||||
|
||||
@@ -153,12 +153,12 @@ router.post('/recover-step2', jsonParser, async (request, response) => {
|
||||
const ip = getIpFromRequest(request);
|
||||
|
||||
if (!user) {
|
||||
console.log('Recover step 2 failed: User not found');
|
||||
console.log('Recover step 2 failed: User', request.body.handle, 'not found');
|
||||
return response.status(404).json({ error: 'User not found' });
|
||||
}
|
||||
|
||||
if (!user.enabled) {
|
||||
console.log('Recover step 2 failed: User is disabled');
|
||||
console.log('Recover step 2 failed: User', user.handle, 'is disabled');
|
||||
return response.status(403).json({ error: 'User is disabled' });
|
||||
}
|
||||
|
||||
|
@@ -347,6 +347,7 @@ export function convertGooglePrompt(messages, model, useSysPrompt = false, charN
|
||||
'gemini-1.5-flash-8b-exp-0924',
|
||||
'gemini-exp-1114',
|
||||
'gemini-exp-1121',
|
||||
'gemini-exp-1206',
|
||||
'gemini-1.5-pro',
|
||||
'gemini-1.5-pro-latest',
|
||||
'gemini-1.5-pro-001',
|
||||
|
@@ -680,8 +680,9 @@ export class MemoryLimitedMap {
|
||||
* @param {number} maxMemoryInBytes - The maximum allowed memory in bytes for string values.
|
||||
*/
|
||||
constructor(maxMemoryInBytes) {
|
||||
if (typeof maxMemoryInBytes !== 'number' || maxMemoryInBytes <= 0) {
|
||||
throw new Error('maxMemoryInBytes must be a positive number');
|
||||
if (typeof maxMemoryInBytes !== 'number' || maxMemoryInBytes <= 0 || isNaN(maxMemoryInBytes)) {
|
||||
console.warn('Invalid maxMemoryInBytes, using a fallback value of 1 GB.');
|
||||
maxMemoryInBytes = 1024 * 1024 * 1024; // 1 GB
|
||||
}
|
||||
this.maxMemory = maxMemoryInBytes;
|
||||
this.currentMemory = 0;
|
||||
|
Reference in New Issue
Block a user