Merge branch 'staging' into redesign-extension-manager
| @@ -54,6 +54,7 @@ module.exports = { | ||
| 54 | 54 | }, |
| 55 | 55 | // These scripts are loaded in HTML; tell ESLint not to complain about them being undefined |
| 56 | 56 | globals: { |
| 57 | + globalThis: 'readonly', | |
| 57 | 58 | ePub: 'readonly', |
| 58 | 59 | pdfjsLib: 'readonly', |
| 59 | 60 | toastr: 'readonly', |
| @@ -209,6 +209,44 @@ SillyTavern поддерживает расширения. | ||
| 209 | 209 | 5. Запустите лаунчер установки: `chmod +x install.sh && ./install.sh` and choose what you wanna install |
| 210 | 210 | 6. После завершения установки, запустите лаунчер следующей командой: `chmod +x launcher.sh && ./launcher.sh` |
| 211 | 211 | |
| 212 | +## 🐋 Установка с помощью Docker | |
| 213 | + | |
| 214 | +Предполагается, что вы уже установили Docker, имеете доступ к командной строке для установки контейнеров и знакомы с их базовым управлением. | |
| 215 | + | |
| 216 | +### Сборка образа самостоятельно | |
| 217 | + | |
| 218 | +У нас есть подробное руководство по использованию SillyTavern в Docker [здесь](http://docs.sillytavern.app/installation/docker/), которое охватывает установку на Windows, macOS и Linux! Ознакомьтесь с ним, если хотите создать образ самостоятельно. | |
| 219 | + | |
| 220 | +### Использование реестра контейнеров GitHub (самый простой способ) | |
| 221 | + | |
| 222 | +Для работы SillyTavern вам понадобятся две обязательные настройки каталогов и одна настройка порта. В команде замените указанные значения на свои: | |
| 223 | + | |
| 224 | +#### Переменные контейнера | |
| 225 | + | |
| 226 | +##### Маппинг томов | |
| 227 | + | |
| 228 | +* [config] - директория, где на вашем хосте будут храниться файлы конфигурации SillyTavern. | |
| 229 | +* [data] - директория, где на вашем хосте будут храниться пользовательские данные SillyTavern (включая персонажей). | |
| 230 | +* [plugins] - (необязательно) директория, где на вашем хосте будут храниться плагины сервера SillyTavern. | |
| 231 | + | |
| 232 | +##### Маппинг портов | |
| 233 | + | |
| 234 | +* [PublicPort] - Порт, через который будет передаваться трафик. Это обязательно, так как вы будете обращаться к контейнеру извне его виртуальной машины. НЕ ОТКРЫВАЙТЕ этот порт в интернет без реализации дополнительного уровня безопасности. | |
| 235 | + | |
| 236 | +##### Дополнительные настройки | |
| 237 | + | |
| 238 | +* [DockerNet] - Docker сеть, к которой контейнер должен быть подключен. Если вы не знаете, что это, обратитесь к [официальной документации Docker](https://docs.docker.com/reference/cli/docker/network/). | |
| 239 | +* [version] - на правой части этой страницы GitHub вы найдете раздел "Packages". Выберите пакет "sillytavern", чтобы увидеть версии образов. Тег "latest" позволит вам обновляться до текущего релиза. Также доступны теги "staging" и "release", которые соответствуют ночным сборкам соответствующих веток. Однако это может быть нецелесообразно, если вы используете расширения, которые могут ломаться и требуют времени для обновления. | |
| 240 | + | |
| 241 | +#### Команда установки | |
| 242 | + | |
| 243 | +1. Откройте командную строку | |
| 244 | +2. Выполните следующую команду | |
| 245 | + | |
| 246 | +`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]'` | |
| 247 | + | |
| 248 | +> Заметьте, что 8000 является портом по умолчанию. Не забудьте использовать соответствующий порт, если вы измените его в конфиге. | |
| 249 | + | |
| 212 | 250 | ## 📱 Мобильные устройства - Установка при помощи termux |
| 213 | 251 | |
| 214 | 252 | > **ОБРАТИТЕ ВНИМАНИЕ!** |
| @@ -1,6 +1,8 @@ | ||
| 1 | 1 | # -- DATA CONFIGURATION -- |
| 2 | 2 | # Root directory for user data storage |
| 3 | 3 | dataRoot: ./data |
| 4 | +# The maximum amount of memory that parsed character cards can use in MB | |
| 5 | +cardsCacheCapacity: 100 | |
| 4 | 6 | # -- SERVER CONFIGURATION -- |
| 5 | 7 | # Listen for incoming connections |
| 6 | 8 | listen: false |
| @@ -1,12 +1,12 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "name": "sillytavern", |
| 3 | 3 | "version": "1.12.89", |
| 4 | 4 | "lockfileVersion": 3, |
| 5 | 5 | "requires": true, |
| 6 | 6 | "packages": { |
| 7 | 7 | "": { |
| 8 | 8 | "name": "sillytavern", |
| 9 | 9 | "version": "1.12.89", |
| 10 | 10 | "hasInstallScript": true, |
| 11 | 11 | "license": "AGPL-3.0", |
| 12 | 12 | "dependencies": { |
| @@ -3591,9 +3591,9 @@ | ||
| 3591 | 3591 | "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==" |
| 3592 | 3592 | }, |
| 3593 | 3593 | "node_modules/express": { |
| 3594 | 3594 | "version": "4.21.12", |
| 3595 | 3595 | "resolved": "https://registry.npmjs.org/express/-/express-4.21.12.tgz", |
| 3596 | 3596 | "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+BwuM1IPw8fmQBiQrXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", |
| 3597 | 3597 | "license": "MIT", |
| 3598 | 3598 | "dependencies": { |
| 3599 | 3599 | "accepts": "~1.3.8", |
| @@ -3615,7 +3615,7 @@ | ||
| 3615 | 3615 | "methods": "~1.1.2", |
| 3616 | 3616 | "on-finished": "2.4.1", |
| 3617 | 3617 | "parseurl": "~1.3.3", |
| 3618 | 3618 | "path-to-regexp": "0.1.1012", |
| 3619 | 3619 | "proxy-addr": "~2.0.7", |
| 3620 | 3620 | "qs": "6.13.0", |
| 3621 | 3621 | "range-parser": "~1.2.1", |
| @@ -3630,6 +3630,10 @@ | ||
| 3630 | 3630 | }, |
| 3631 | 3631 | "engines": { |
| 3632 | 3632 | "node": ">= 0.10.0" |
| 3633 | + }, | |
| 3634 | + "funding": { | |
| 3635 | + "type": "opencollective", | |
| 3636 | + "url": "https://opencollective.com/express" | |
| 3633 | 3637 | } |
| 3634 | 3638 | }, |
| 3635 | 3639 | "node_modules/express/node_modules/cookie": { |
| @@ -5725,9 +5729,9 @@ | ||
| 5725 | 5729 | } |
| 5726 | 5730 | }, |
| 5727 | 5731 | "node_modules/path-to-regexp": { |
| 5728 | 5732 | "version": "0.1.1012", |
| 5729 | 5733 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.1012.tgz", |
| 5730 | 5734 | "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfaRA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/BSsIwjuh56VU7katFvuM8hULfkwB3FnsTt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/rsVF7PwPKVw1sl5KQS9wbSeOZwt1pmEQ==", |
| 5731 | 5735 | "license": "MIT" |
| 5732 | 5736 | }, |
| 5733 | 5737 | "node_modules/peek-readable": { |
| @@ -84,7 +84,7 @@ | ||
| 84 | 84 | "type": "git", |
| 85 | 85 | "url": "https://github.com/SillyTavern/SillyTavern.git" |
| 86 | 86 | }, |
| 87 | 87 | "version": "1.12.89", |
| 88 | 88 | "scripts": { |
| 89 | 89 | "start": "node server.js", |
| 90 | 90 | "start:deno": "deno run --allow-run --allow-net --allow-read --allow-write --allow-sys --allow-env server.js", |
| @@ -1,4 +1,5 @@ | ||
| 1 | 1 | import libs from './lib'; |
| 2 | +import getContext from './scripts/st-context'; | |
| 2 | 3 | |
| 3 | 4 | // Global namespace modules |
| 4 | 5 | declare var ai; |
| @@ -6,7 +7,7 @@ declare var pdfjsLib; | ||
| 6 | 7 | declare var ePub; |
| 7 | 8 | |
| 8 | 9 | declare var SillyTavern: { |
| 9 | 10 | getContext(): anytypeof getContext; |
| 10 | 11 | llm: any; |
| 11 | 12 | libs: typeof libs; |
| 12 | 13 | }; |
| @@ -669,7 +669,7 @@ | ||
| 669 | 669 | </span> |
| 670 | 670 | </div> |
| 671 | 671 | </div> |
| 672 | 672 | <div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt"> |
| 673 | 673 | <div class="range-block-title" data-i18n="Temperature"> |
| 674 | 674 | Temperature |
| 675 | 675 | </div> |
| @@ -682,7 +682,7 @@ | ||
| 682 | 682 | </div> |
| 683 | 683 | </div> |
| 684 | 684 | </div> |
| 685 | 685 | <div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt"> |
| 686 | 686 | <div class="range-block-title" data-i18n="Frequency Penalty"> |
| 687 | 687 | Frequency Penalty |
| 688 | 688 | </div> |
| @@ -695,7 +695,7 @@ | ||
| 695 | 695 | </div> |
| 696 | 696 | </div> |
| 697 | 697 | </div> |
| 698 | 698 | <div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt"> |
| 699 | 699 | <div class="range-block-title" data-i18n="Presence Penalty"> |
| 700 | 700 | Presence Penalty |
| 701 | 701 | </div> |
| @@ -721,7 +721,7 @@ | ||
| 721 | 721 | </div> |
| 722 | 722 | </div> |
| 723 | 723 | </div> |
| 724 | 724 | <div class="range-block" data-source="openai,claude,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt"> |
| 725 | 725 | <div class="range-block-title" data-i18n="Top P"> |
| 726 | 726 | Top P |
| 727 | 727 | </div> |
| @@ -958,7 +958,7 @@ | ||
| 958 | 958 | </div> |
| 959 | 959 | </div> |
| 960 | 960 | </div> |
| 961 | 961 | <div class="range-block" data-source="openai,openrouter,mistralai,custom,cohere,groq,nanogpt"> |
| 962 | 962 | <div class="range-block-title justifyLeft" data-i18n="Seed"> |
| 963 | 963 | Seed |
| 964 | 964 | </div> |
| @@ -2987,6 +2987,7 @@ | ||
| 2987 | 2987 | <optgroup label="Subversions"> |
| 2988 | 2988 | <option value="gemini-exp-1121">Gemini Experimental 2024-11-21</option> |
| 2989 | 2989 | <option value="gemini-exp-1114">Gemini Experimental 2024-11-14</option> |
| 2990 | + <option value="gemini-exp-1206">Gemini Experimental 2024-12-06</option> | |
| 2990 | 2991 | <option value="gemini-1.5-pro-exp-0801">Gemini 1.5 Pro Experiment 2024-08-01</option> |
| 2991 | 2992 | <option value="gemini-1.5-pro-exp-0827">Gemini 1.5 Pro Experiment 2024-08-27</option> |
| 2992 | 2993 | <option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option> |
| @@ -4183,7 +4184,7 @@ | ||
| 4183 | 4184 | </label> |
| 4184 | 4185 | <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."> |
| 4185 | 4186 | <input id="show_swipe_num_all_messages" type="checkbox" /> |
| 4186 | 4187 | <small data-i18n="Swipe # for All Messages">Swipe # for All Messages</small><i class="fa-solid fa-mobile-screen-button"></i> |
| 4187 | 4188 | </label> |
| 4188 | 4189 | <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"> |
| 4189 | 4190 | <input id="hotswapEnabled" type="checkbox" /> |
| @@ -5,7 +5,8 @@ | ||
| 5 | 5 | "module": "ESNext", |
| 6 | 6 | "moduleResolution": "node", |
| 7 | 7 | "allowUmdGlobalAccess": true, |
| 8 | 8 | "allowSyntheticDefaultImports": true, |
| 9 | + "strictBindCallApply": true | |
| 9 | 10 | }, |
| 10 | 11 | "exclude": [ |
| 11 | 12 | "**/node_modules/**", |
| @@ -13,7 +13,7 @@ import { | ||
| 13 | 13 | default as libs, |
| 14 | 14 | } from './lib.js'; |
| 15 | 15 | |
| 16 | 16 | import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, initRossMods, shouldSendOnEnter } from './scripts/RossAscends-mods.js'; |
| 17 | 17 | import { userStatsHandler, statMesProcess, initStats } from './scripts/stats.js'; |
| 18 | 18 | import { |
| 19 | 19 | generateKoboldWithStreaming, |
| @@ -37,8 +37,6 @@ import { | ||
| 37 | 37 | parseTabbyLogprobs, |
| 38 | 38 | } from './scripts/textgen-settings.js'; |
| 39 | 39 | |
| 40 | -const { MANCER, TOGETHERAI, OOBA, VLLM, APHRODITE, TABBY, OLLAMA, INFERMATICAI, DREAMGEN, OPENROUTER, FEATHERLESS } = textgen_types; | |
| 41 | - | |
| 42 | 40 | import { |
| 43 | 41 | world_info, |
| 44 | 42 | getWorldInfoPrompt, |
| @@ -67,9 +65,7 @@ import { | ||
| 67 | 65 | getGroupChat, |
| 68 | 66 | renameGroupMember, |
| 69 | 67 | createNewGroupChat, |
| 70 | - getGroupPastChats, | |
| 71 | 68 | getGroupAvatar, |
| 72 | - openGroupChat, | |
| 73 | 69 | editGroup, |
| 74 | 70 | deleteGroupChat, |
| 75 | 71 | renameGroupChat, |
| @@ -98,7 +94,6 @@ import { | ||
| 98 | 94 | resetMovableStyles, |
| 99 | 95 | forceCharacterEditorTokenize, |
| 100 | 96 | applyPowerUserSettings, |
| 101 | - switchSwipeNumAllMessages, | |
| 102 | 97 | } from './scripts/power-user.js'; |
| 103 | 98 | |
| 104 | 99 | import { |
| @@ -174,8 +169,8 @@ import { | ||
| 174 | 169 | } from './scripts/utils.js'; |
| 175 | 170 | import { debounce_timeout } from './scripts/constants.js'; |
| 176 | 171 | |
| 177 | 172 | import { ModuleWorkerWrapper, doDailyExtensionUpdatesCheck, extension_settings, getContext, initExtensions, loadExtensionSettings, renderExtensionTemplate, renderExtensionTemplateAsync, runGenerationInterceptors, saveMetadataDebounced, writeExtensionField } from './scripts/extensions.js'; |
| 178 | 173 | import { COMMENT_NAME_DEFAULT, executeSlashCommands, executeSlashCommandsOnChatInput, executeSlashCommandsWithOptions, getSlashCommandsHelp, initDefaultSlashCommands, isExecutingCommandsFromChatInput, pauseScriptExecution, processChatSlashCommands, registerSlashCommand, stopScriptExecution } from './scripts/slash-commands.js'; |
| 179 | 174 | import { |
| 180 | 175 | tag_map, |
| 181 | 176 | tags, |
| @@ -225,8 +220,8 @@ import { | ||
| 225 | 220 | instruct_presets, |
| 226 | 221 | selectContextPreset, |
| 227 | 222 | } from './scripts/instruct-mode.js'; |
| 228 | 223 | import { initLocales, t, translate } from './scripts/i18n.js'; |
| 229 | 224 | import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, getTokenizerModel, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js'; |
| 230 | 225 | import { |
| 231 | 226 | user_avatar, |
| 232 | 227 | getUserAvatars, |
| @@ -241,12 +236,12 @@ import { hideLoader, showLoader } from './scripts/loader.js'; | ||
| 241 | 236 | import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay.js'; |
| 242 | 237 | import { loadFeatherlessModels, loadMancerModels, loadOllamaModels, loadTogetherAIModels, loadInfermaticAIModels, loadOpenRouterModels, loadVllmModels, loadAphroditeModels, loadDreamGenModels, initTextGenModels, loadTabbyModels } from './scripts/textgen-models.js'; |
| 243 | 238 | import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat } from './scripts/chats.js'; |
| 244 | 239 | import { getPresetManager, initPresetManager } from './scripts/preset-manager.js'; |
| 245 | 240 | import { MacrosParser, evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js'; |
| 246 | 241 | import { currentUser, setUserControls } from './scripts/user.js'; |
| 247 | 242 | import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup, fixToastrForDialogs } from './scripts/popup.js'; |
| 248 | 243 | import { renderTemplate, renderTemplateAsync } from './scripts/templates.js'; |
| 249 | 244 | import { initScrapers, ScraperManager } from './scripts/scrapers.js'; |
| 250 | 245 | import { SlashCommandParser } from './scripts/slash-commands/SlashCommandParser.js'; |
| 251 | 246 | import { SlashCommand } from './scripts/slash-commands/SlashCommand.js'; |
| 252 | 247 | import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './scripts/slash-commands/SlashCommandArgument.js'; |
| @@ -268,6 +263,13 @@ import { initServerHistory } from './scripts/server-history.js'; | ||
| 268 | 263 | import { initSettingsSearch } from './scripts/setting-search.js'; |
| 269 | 264 | import { initBulkEdit } from './scripts/bulk-edit.js'; |
| 270 | 265 | import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js'; |
| 266 | +import { getContext } from './scripts/st-context.js'; | |
| 267 | + | |
| 268 | +// API OBJECT FOR EXTERNAL WIRING | |
| 269 | +globalThis.SillyTavern = { | |
| 270 | + libs, | |
| 271 | + getContext, | |
| 272 | +}; | |
| 271 | 273 | |
| 272 | 274 | //exporting functions and vars for mods |
| 273 | 275 | export { |
| @@ -427,10 +429,6 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => { | ||
| 427 | 429 | } |
| 428 | 430 | }); |
| 429 | 431 | |
| 430 | -// API OBJECT FOR EXTERNAL WIRING | |
| 431 | -window['SillyTavern'] = {}; | |
| 432 | -window['SillyTavern'].libs = libs; | |
| 433 | - | |
| 434 | 432 | // Event source init |
| 435 | 433 | export const event_types = { |
| 436 | 434 | APP_READY: 'app_ready', |
| @@ -536,7 +534,7 @@ let displayVersion = 'SillyTavern'; | ||
| 536 | 534 | |
| 537 | 535 | let generatedPromptCache = ''; |
| 538 | 536 | let generation_started = new Date(); |
| 539 | 537 | /** @type {import('./scripts/char-data.js').v1CharData[]} */ |
| 540 | 538 | export let characters = []; |
| 541 | 539 | export let this_chid; |
| 542 | 540 | let saveCharactersPage = 0; |
| @@ -812,7 +810,7 @@ export let menu_type = ''; | ||
| 812 | 810 | export let selected_button = ''; //which button pressed |
| 813 | 811 | |
| 814 | 812 | //create pole save |
| 815 | 813 | export let create_save = { |
| 816 | 814 | name: '', |
| 817 | 815 | description: '', |
| 818 | 816 | creator_notes: '', |
| @@ -864,7 +862,7 @@ export let amount_gen = 80; //default max length of AI generated responses | ||
| 864 | 862 | export let max_context = 2048; |
| 865 | 863 | |
| 866 | 864 | var swipes = true; |
| 867 | 865 | export let extension_prompts = {}; |
| 868 | 866 | |
| 869 | 867 | export let main_api;// = "kobold"; |
| 870 | 868 | //novel settings |
| @@ -1175,7 +1173,7 @@ async function getStatusTextgen() { | ||
| 1175 | 1173 | return resultCheckStatus(); |
| 1176 | 1174 | } |
| 1177 | 1175 | |
| 1178 | 1176 | if (textgen_settings.type == textgen_types.OOBA && textgen_settings.bypass_status_check) { |
| 1179 | 1177 | setOnlineStatus('Status check bypassed'); |
| 1180 | 1178 | return resultCheckStatus(); |
| 1181 | 1179 | } |
| @@ -1193,34 +1191,34 @@ async function getStatusTextgen() { | ||
| 1193 | 1191 | |
| 1194 | 1192 | const data = await response.json(); |
| 1195 | 1193 | |
| 1196 | 1194 | if (textgen_settings.type === textgen_types.MANCER) { |
| 1197 | 1195 | loadMancerModels(data?.data); |
| 1198 | 1196 | setOnlineStatus(textgen_settings.mancer_model); |
| 1199 | 1197 | } else if (textgen_settings.type === textgen_types.TOGETHERAI) { |
| 1200 | 1198 | loadTogetherAIModels(data?.data); |
| 1201 | 1199 | setOnlineStatus(textgen_settings.togetherai_model); |
| 1202 | 1200 | } else if (textgen_settings.type === textgen_types.OLLAMA) { |
| 1203 | 1201 | loadOllamaModels(data?.data); |
| 1204 | 1202 | setOnlineStatus(textgen_settings.ollama_model || 'Connected'); |
| 1205 | 1203 | } else if (textgen_settings.type === textgen_types.INFERMATICAI) { |
| 1206 | 1204 | loadInfermaticAIModels(data?.data); |
| 1207 | 1205 | setOnlineStatus(textgen_settings.infermaticai_model); |
| 1208 | 1206 | } else if (textgen_settings.type === textgen_types.DREAMGEN) { |
| 1209 | 1207 | loadDreamGenModels(data?.data); |
| 1210 | 1208 | setOnlineStatus(textgen_settings.dreamgen_model); |
| 1211 | 1209 | } else if (textgen_settings.type === textgen_types.OPENROUTER) { |
| 1212 | 1210 | loadOpenRouterModels(data?.data); |
| 1213 | 1211 | setOnlineStatus(textgen_settings.openrouter_model); |
| 1214 | 1212 | } else if (textgen_settings.type === textgen_types.VLLM) { |
| 1215 | 1213 | loadVllmModels(data?.data); |
| 1216 | 1214 | setOnlineStatus(textgen_settings.vllm_model); |
| 1217 | 1215 | } else if (textgen_settings.type === textgen_types.APHRODITE) { |
| 1218 | 1216 | loadAphroditeModels(data?.data); |
| 1219 | 1217 | setOnlineStatus(textgen_settings.aphrodite_model); |
| 1220 | 1218 | } else if (textgen_settings.type === textgen_types.FEATHERLESS) { |
| 1221 | 1219 | loadFeatherlessModels(data?.data); |
| 1222 | 1220 | setOnlineStatus(textgen_settings.featherless_model); |
| 1223 | 1221 | } else if (textgen_settings.type === textgen_types.TABBY) { |
| 1224 | 1222 | loadTabbyModels(data?.data); |
| 1225 | 1223 | setOnlineStatus(textgen_settings.tabby_model || data?.result); |
| 1226 | 1224 | } else { |
| @@ -1637,7 +1635,7 @@ export function getEntitiesList({ doFilter = false, doSort = true } = {}) { | ||
| 1637 | 1635 | subEntities = entitiesFilter.applyFilters(subEntities, { clearScoreCache: false, tempOverrides: { [FILTER_TYPES.FOLDER]: FILTER_STATES.UNDEFINED }, clearFuzzySearchCaches: false }); |
| 1638 | 1636 | } |
| 1639 | 1637 | if (doSort) { |
| 1640 | 1638 | sortEntitiesList(subEntities, false); |
| 1641 | 1639 | } |
| 1642 | 1640 | entity.entities = subEntities; |
| 1643 | 1641 | entity.hidden = subCount - subEntities.length; |
| @@ -1665,7 +1663,7 @@ export function getEntitiesList({ doFilter = false, doSort = true } = {}) { | ||
| 1665 | 1663 | |
| 1666 | 1664 | // Sort before returning if requested |
| 1667 | 1665 | if (doSort) { |
| 1668 | 1666 | sortEntitiesList(entities, false); |
| 1669 | 1667 | } |
| 1670 | 1668 | entitiesFilter.clearFuzzySearchCaches(); |
| 1671 | 1669 | return entities; |
| @@ -2607,15 +2605,18 @@ export function substituteParams(content, _name1, _name2, _original, _group, _re | ||
| 2607 | 2605 | }; |
| 2608 | 2606 | } |
| 2609 | 2607 | |
| 2610 | 2608 | const getGroupValue = (includeMuted) => { |
| 2611 | 2609 | if (typeof _group === 'string') { |
| 2612 | 2610 | return _group; |
| 2613 | 2611 | } |
| 2614 | 2612 | |
| 2615 | 2613 | if (selected_group) { |
| 2616 | 2614 | const members = groups.find(x => x.id === selected_group)?.members; |
| 2615 | + /** @type {string[]} */ | |
| 2616 | + const disabledMembers = groups.find(x => x.id === selected_group)?.disabled_members ?? []; | |
| 2617 | + const isMuted = x => includeMuted ? true : !disabledMembers.includes(x); | |
| 2617 | 2618 | const names = Array.isArray(members) |
| 2618 | 2619 | ? members.filter(isMuted).map(m => characters.find(c => c.avatar === m)?.name).filter(Boolean).join(', ') |
| 2619 | 2620 | : ''; |
| 2620 | 2621 | return names; |
| 2621 | 2622 | } else { |
| @@ -2639,7 +2640,8 @@ export function substituteParams(content, _name1, _name2, _original, _group, _re | ||
| 2639 | 2640 | // Must be substituted last so that they're replaced inside {{description}} |
| 2640 | 2641 | environment.user = _name1 ?? name1; |
| 2641 | 2642 | environment.char = _name2 ?? name2; |
| 2642 | 2643 | environment.group = environment.charIfNotGroup = getGroupValue(true); |
| 2644 | + environment.groupNotMuted = getGroupValue(false); | |
| 2643 | 2645 | environment.model = getGeneratingModel(); |
| 2644 | 2646 | |
| 2645 | 2647 | if (additionalMacro && typeof additionalMacro === 'object') { |
| @@ -4494,6 +4496,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4494 | 4496 | instruction: main_api !== 'openai' && power_user.sysprompt.enabled ? substituteParams(power_user.prefer_character_prompt && system ? system : power_user.sysprompt.content) : '', |
| 4495 | 4497 | userPersona: (power_user.persona_description_position == persona_description_positions.IN_PROMPT ? (persona || '') : ''), |
| 4496 | 4498 | tokenizer: getFriendlyTokenizerName(main_api).tokenizerName || '', |
| 4499 | + presetName: getPresetManager()?.getSelectedPresetName() || '', | |
| 4497 | 4500 | }; |
| 4498 | 4501 | |
| 4499 | 4502 | //console.log(additionalPromptStuff); |
| @@ -5160,6 +5163,7 @@ export async function itemizedParams(itemizedPrompts, thisPromptSet, incomingMes | ||
| 5160 | 5163 | dataBankVectorsStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].dataBankVectorsString), |
| 5161 | 5164 | modelUsed: chat[incomingMesId]?.extra?.model, |
| 5162 | 5165 | apiUsed: chat[incomingMesId]?.extra?.api, |
| 5166 | + presetName: itemizedPrompts[thisPromptSet].presetName || t`(Unknown)`, | |
| 5163 | 5167 | }; |
| 5164 | 5168 | |
| 5165 | 5169 | const getFriendlyName = (value) => $(`#rm_api_block select option[value="${value}"]`).first().text() || value; |
| @@ -5536,7 +5540,7 @@ function extractMultiSwipes(data, type) { | ||
| 5536 | 5540 | return swipes; |
| 5537 | 5541 | } |
| 5538 | 5542 | |
| 5539 | 5543 | 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))) { |
| 5540 | 5544 | if (!Array.isArray(data.choices)) { |
| 5541 | 5545 | return swipes; |
| 5542 | 5546 | } |
| @@ -5699,7 +5703,7 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc | ||
| 5699 | 5703 | return getMessage; |
| 5700 | 5704 | } |
| 5701 | 5705 | |
| 5702 | 5706 | export async function saveReply(type, getMessage, fromStreaming, title, swipes) { |
| 5703 | 5707 | if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (chat[chat.length - 1]['swipe_id'] === undefined || |
| 5704 | 5708 | chat[chat.length - 1]['is_user'])) { |
| 5705 | 5709 | type = 'normal'; |
| @@ -7658,14 +7662,12 @@ export function showSwipeButtons() { | ||
| 7658 | 7662 | //allows for writing individual swipe counters for past messages |
| 7659 | 7663 | const lastSwipeCounter = $('.last_mes .swipes-counter'); |
| 7660 | 7664 | lastSwipeCounter.text(swipeCounterText).show(); |
| 7661 | - | |
| 7662 | - switchSwipeNumAllMessages(); | |
| 7663 | 7665 | } |
| 7664 | 7666 | |
| 7665 | 7667 | export function hideSwipeButtons() { |
| 7666 | 7668 | $('#chat')chatElement.find('.swipe_right').hide(); |
| 7667 | 7669 | $('#chat')chatElement.find('.last_mes .swipes-counter').hide(); |
| 7668 | 7670 | $('#chat')chatElement.find('.swipe_left').hide(); |
| 7669 | 7671 | } |
| 7670 | 7672 | |
| 7671 | 7673 | /** |
| @@ -7752,25 +7754,31 @@ export async function saveChatConditional() { | ||
| 7752 | 7754 | } |
| 7753 | 7755 | } |
| 7754 | 7756 | |
| 7755 | -async function importCharacterChat(formData) { | |
| 7757 | +/** | |
| 7756 | - await jQuery.ajax({ | |
| 7758 | + * Saves the chat to the server. | |
| 7757 | - type: 'POST', | |
| 7759 | + * @param {FormData} formData Form data to send to the server. | |
| 7758 | - url: '/api/chats/import', | |
| 7760 | + * @param {EventTarget} eventTarget Event target to trigger the event on. | |
| 7759 | - data: formData, | |
| 7761 | + */ | |
| 7760 | 7762 | beforeSend:async function importCharacterChat(formData, eventTarget) { |
| 7761 | - }, | |
| 7763 | + const headers = getRequestHeaders(); | |
| 7762 | - cache: false, | |
| 7764 | + delete headers['Content-Type']; | |
| 7763 | - contentType: false, | |
| 7765 | + const fetchResult = await fetch('/api/chats/import', { | |
| 7764 | 7766 | processDatamethod: false'POST', |
| 7765 | - success: async function (data) { | |
| 7767 | + body: formData, | |
| 7766 | - if (data.res) { | |
| 7768 | + headers: headers, | |
| 7767 | - await displayPastChats(); | |
| 7769 | + cache: 'no-cache', | |
| 7768 | - } | |
| 7769 | - }, | |
| 7770 | - error: function () { | |
| 7771 | - $('#create_button').removeAttr('disabled'); | |
| 7772 | - }, | |
| 7773 | 7770 | }); |
| 7771 | + | |
| 7772 | + if (fetchResult.ok) { | |
| 7773 | + const data = await fetchResult.json(); | |
| 7774 | + if (data.res) { | |
| 7775 | + await displayPastChats(); | |
| 7776 | + } | |
| 7777 | + } | |
| 7778 | + | |
| 7779 | + if (eventTarget instanceof HTMLInputElement) { | |
| 7780 | + eventTarget.value = ''; | |
| 7781 | + } | |
| 7774 | 7782 | } |
| 7775 | 7783 | |
| 7776 | 7784 | function updateViewMessageIds(startFromZero = false) { |
| @@ -8225,102 +8233,6 @@ async function createOrEditCharacter(e) { | ||
| 8225 | 8233 | } |
| 8226 | 8234 | } |
| 8227 | 8235 | |
| 8228 | -window['SillyTavern'].getContext = function () { | |
| 8229 | - return { | |
| 8230 | - chat: chat, | |
| 8231 | - characters: characters, | |
| 8232 | - groups: groups, | |
| 8233 | - name1: name1, | |
| 8234 | - name2: name2, | |
| 8235 | - characterId: this_chid, | |
| 8236 | - groupId: selected_group, | |
| 8237 | - chatId: selected_group | |
| 8238 | - ? groups.find(x => x.id == selected_group)?.chat_id | |
| 8239 | - : (this_chid && characters[this_chid] && characters[this_chid].chat), | |
| 8240 | - getCurrentChatId: getCurrentChatId, | |
| 8241 | - getRequestHeaders: getRequestHeaders, | |
| 8242 | - reloadCurrentChat: reloadCurrentChat, | |
| 8243 | - renameChat: renameChat, | |
| 8244 | - saveSettingsDebounced: saveSettingsDebounced, | |
| 8245 | - onlineStatus: online_status, | |
| 8246 | - maxContext: Number(max_context), | |
| 8247 | - chatMetadata: chat_metadata, | |
| 8248 | - streamingProcessor, | |
| 8249 | - eventSource: eventSource, | |
| 8250 | - eventTypes: event_types, | |
| 8251 | - addOneMessage: addOneMessage, | |
| 8252 | - generate: Generate, | |
| 8253 | - sendStreamingRequest: sendStreamingRequest, | |
| 8254 | - sendGenerationRequest: sendGenerationRequest, | |
| 8255 | - stopGeneration: stopGeneration, | |
| 8256 | - getTokenCount: getTokenCount, | |
| 8257 | - extensionPrompts: extension_prompts, | |
| 8258 | - setExtensionPrompt: setExtensionPrompt, | |
| 8259 | - updateChatMetadata: updateChatMetadata, | |
| 8260 | - saveChat: saveChatConditional, | |
| 8261 | - openCharacterChat: openCharacterChat, | |
| 8262 | - openGroupChat: openGroupChat, | |
| 8263 | - saveMetadata: saveMetadata, | |
| 8264 | - sendSystemMessage: sendSystemMessage, | |
| 8265 | - activateSendButtons, | |
| 8266 | - deactivateSendButtons, | |
| 8267 | - saveReply, | |
| 8268 | - substituteParams, | |
| 8269 | - substituteParamsExtended, | |
| 8270 | - SlashCommandParser, | |
| 8271 | - SlashCommand, | |
| 8272 | - SlashCommandArgument, | |
| 8273 | - SlashCommandNamedArgument, | |
| 8274 | - ARGUMENT_TYPE, | |
| 8275 | - executeSlashCommandsWithOptions, | |
| 8276 | - /** @deprecated Use SlashCommandParser.addCommandObject() instead */ | |
| 8277 | - registerSlashCommand: registerSlashCommand, | |
| 8278 | - /** @deprecated Use executeSlashCommandWithOptions instead */ | |
| 8279 | - executeSlashCommands: executeSlashCommands, | |
| 8280 | - timestampToMoment: timestampToMoment, | |
| 8281 | - /** @deprecated Handlebars for extensions are no longer supported. */ | |
| 8282 | - registerHelper: () => { }, | |
| 8283 | - registerMacro: MacrosParser.registerMacro.bind(MacrosParser), | |
| 8284 | - unregisterMacro: MacrosParser.unregisterMacro.bind(MacrosParser), | |
| 8285 | - registerFunctionTool: ToolManager.registerFunctionTool.bind(ToolManager), | |
| 8286 | - unregisterFunctionTool: ToolManager.unregisterFunctionTool.bind(ToolManager), | |
| 8287 | - isToolCallingSupported: ToolManager.isToolCallingSupported.bind(ToolManager), | |
| 8288 | - canPerformToolCalls: ToolManager.canPerformToolCalls.bind(ToolManager), | |
| 8289 | - registerDebugFunction: registerDebugFunction, | |
| 8290 | - /** @deprecated Use renderExtensionTemplateAsync instead. */ | |
| 8291 | - renderExtensionTemplate: renderExtensionTemplate, | |
| 8292 | - renderExtensionTemplateAsync: renderExtensionTemplateAsync, | |
| 8293 | - registerDataBankScraper: ScraperManager.registerDataBankScraper, | |
| 8294 | - /** @deprecated Use callGenericPopup or Popup instead. */ | |
| 8295 | - callPopup: callPopup, | |
| 8296 | - callGenericPopup: callGenericPopup, | |
| 8297 | - showLoader: showLoader, | |
| 8298 | - hideLoader: hideLoader, | |
| 8299 | - mainApi: main_api, | |
| 8300 | - extensionSettings: extension_settings, | |
| 8301 | - ModuleWorkerWrapper: ModuleWorkerWrapper, | |
| 8302 | - getTokenizerModel: getTokenizerModel, | |
| 8303 | - generateQuietPrompt: generateQuietPrompt, | |
| 8304 | - writeExtensionField: writeExtensionField, | |
| 8305 | - getThumbnailUrl: getThumbnailUrl, | |
| 8306 | - selectCharacterById: selectCharacterById, | |
| 8307 | - messageFormatting: messageFormatting, | |
| 8308 | - shouldSendOnEnter: shouldSendOnEnter, | |
| 8309 | - isMobile: isMobile, | |
| 8310 | - t: t, | |
| 8311 | - translate: translate, | |
| 8312 | - tags: tags, | |
| 8313 | - tagMap: tag_map, | |
| 8314 | - menuType: menu_type, | |
| 8315 | - createCharacterData: create_save, | |
| 8316 | - /** @deprecated Legacy snake-case naming, compatibility with old extensions */ | |
| 8317 | - event_types: event_types, | |
| 8318 | - Popup: Popup, | |
| 8319 | - POPUP_TYPE: POPUP_TYPE, | |
| 8320 | - POPUP_RESULT: POPUP_RESULT, | |
| 8321 | - }; | |
| 8322 | -}; | |
| 8323 | - | |
| 8324 | 8236 | /** |
| 8325 | 8237 | * Formats a counter for a swipe view. |
| 8326 | 8238 | * @param {number} current The current number of items. |
| @@ -10827,13 +10739,13 @@ jQuery(async function () { | ||
| 10827 | 10739 | }); |
| 10828 | 10740 | |
| 10829 | 10741 | $('#chat_import_file').on('change', async function (e) { |
| 10830 | 10742 | varconst file = e.target.files[0]; |
| 10831 | 10743 | |
| 10832 | 10744 | if (!file) { |
| 10833 | 10745 | return; |
| 10834 | 10746 | } |
| 10835 | 10747 | |
| 10836 | 10748 | varconst ext = file.name.match(/\.(\w+)$/); |
| 10837 | 10749 | if ( |
| 10838 | 10750 | !ext || |
| 10839 | 10751 | (ext[1].toLowerCase() != 'json' && ext[1].toLowerCase() != 'jsonl') |
| @@ -10846,17 +10758,17 @@ jQuery(async function () { | ||
| 10846 | 10758 | return; |
| 10847 | 10759 | } |
| 10848 | 10760 | |
| 10849 | 10761 | varconst format = ext[1].toLowerCase(); |
| 10850 | 10762 | $('#chat_import_file_type').val(format); |
| 10851 | 10763 | |
| 10852 | 10764 | varconst formData = new FormData($('#form_import_chat').get(0)); |
| 10853 | 10765 | formData.append('user_name', name1); |
| 10854 | 10766 | $('#select_chat_div').html(''); |
| 10855 | 10767 | |
| 10856 | 10768 | if (selected_group) { |
| 10857 | 10769 | await importGroupChat(formData, e.originalEvent.target); |
| 10858 | 10770 | } else { |
| 10859 | 10771 | await importCharacterChat(formData, e.originalEvent.target); |
| 10860 | 10772 | } |
| 10861 | 10773 | }); |
| 10862 | 10774 | |
| @@ -75,6 +75,7 @@ | ||
| 75 | 75 | * @property {string} [source_url] - The source URL associated with the character. |
| 76 | 76 | * @property {{full_path: string}} [chub] - The Chub-specific data associated with the character. |
| 77 | 77 | * @property {{source: string[]}} [risuai] - The RisuAI-specific data associated with the character. |
| 78 | + * @property {{positive: string, negative: string}} [sd_character_prompt] - SD-specific data associated with the character. | |
| 78 | 79 | */ |
| 79 | 80 | |
| 80 | 81 | /** |
| @@ -5,6 +5,7 @@ import { showLoader } from './loader.js'; | ||
| 5 | 5 | import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; |
| 6 | 6 | import { renderTemplate, renderTemplateAsync } from './templates.js'; |
| 7 | 7 | import { isSubsetOf, setValueByPath } from './utils.js'; |
| 8 | +import { getContext } from './st-context.js'; | |
| 8 | 9 | export { |
| 9 | 10 | getContext, |
| 10 | 11 | getApiUrl, |
| @@ -174,7 +175,6 @@ const extension_settings = { | ||
| 174 | 175 | let modules = []; |
| 175 | 176 | let activeExtensions = new Set(); |
| 176 | 177 | |
| 177 | -const getContext = () => window['SillyTavern'].getContext(); | |
| 178 | 178 | const getApiUrl = () => extension_settings.apiUrl; |
| 179 | 179 | let connectedToApi = false; |
| 180 | 180 | |
| @@ -62,6 +62,7 @@ | ||
| 62 | 62 | <option data-type="google" value="gemini-1.5-flash-8b-exp-0924">gemini-1.5-flash-8b-exp-0924</option> |
| 63 | 63 | <option data-type="google" value="gemini-exp-1114">gemini-exp-1114</option> |
| 64 | 64 | <option data-type="google" value="gemini-exp-1121">gemini-exp-1121</option> |
| 65 | + <option data-type="google" value="gemini-exp-1206">gemini-exp-1206</option> | |
| 65 | 66 | <option data-type="google" value="gemini-1.5-pro">gemini-1.5-pro</option> |
| 66 | 67 | <option data-type="google" value="gemini-1.5-pro-latest">gemini-1.5-pro-latest</option> |
| 67 | 68 | <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'; | ||
| 60 | 60 | export { MODULE_NAME }; |
| 61 | 61 | |
| 62 | 62 | const MODULE_NAME = 'sd'; |
| 63 | -const UPDATE_INTERVAL = 1000; | |
| 64 | 63 | // This is a 1x1 transparent PNG |
| 65 | 64 | const PNG_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='; |
| 66 | 65 | const CUSTOM_STOP_EVENT = 'sd_stop_generation'; |
| @@ -3687,8 +3686,6 @@ async function addSDGenButtons() { | ||
| 3687 | 3686 | const button = $('#sd_gen'); |
| 3688 | 3687 | const dropdown = $('#sd_dropdown'); |
| 3689 | 3688 | dropdown.hide(); |
| 3690 | - button.hide(); | |
| 3691 | - messageButton.hide(); | |
| 3692 | 3689 | |
| 3693 | 3690 | let popper = Popper.createPopper(button.get(0), dropdown.get(0), { |
| 3694 | 3691 | placement: 'top', |
| @@ -3770,18 +3767,6 @@ function isValidState() { | ||
| 3770 | 3767 | } |
| 3771 | 3768 | } |
| 3772 | 3769 | |
| 3773 | -async function moduleWorker() { | |
| 3774 | - if (isValidState()) { | |
| 3775 | - $('#sd_gen').show(); | |
| 3776 | - $('.sd_message_gen').show(); | |
| 3777 | - } | |
| 3778 | - else { | |
| 3779 | - $('#sd_gen').hide(); | |
| 3780 | - $('.sd_message_gen').hide(); | |
| 3781 | - } | |
| 3782 | -} | |
| 3783 | - | |
| 3784 | -setInterval(moduleWorker, UPDATE_INTERVAL); | |
| 3785 | 3770 | let buttonAbortController = null; |
| 3786 | 3771 | |
| 3787 | 3772 | async function sdMessageButton(e) { |
| @@ -4,7 +4,7 @@ | ||
| 4 | 4 | |
| 5 | 5 | #sd_dropdown { |
| 6 | 6 | z-index: 30000; |
| 7 | 7 | backdrop-filter: blur(var(--SmartThemeBlurStrength)); |
| 8 | 8 | } |
| 9 | 9 | |
| 10 | 10 | #sd_comfy_open_workflow_editor { |
| @@ -93,7 +93,7 @@ | ||
| 93 | 93 | .at-settings-separator { |
| 94 | 94 | margin-top: 10px; |
| 95 | 95 | margin-bottom: 10px; |
| 96 | 96 | padding: 18x18px; |
| 97 | 97 | font-weight: bold; |
| 98 | 98 | border-top: 1px solid #e1e1e1; /* Grey line */ |
| 99 | 99 | border-bottom: 1px solid #e1e1e1; /* Grey line */ |
| @@ -1265,18 +1265,20 @@ function getGroupCharacters({ doFilter, onlyMembers } = {}) { | ||
| 1265 | 1265 | const thisGroup = openGroupId && groups.find((x) => x.id == openGroupId); |
| 1266 | 1266 | let candidates = characters |
| 1267 | 1267 | .filter((x) => isGroupMember(thisGroup, x.avatar) == onlyMembers) |
| 1268 | 1268 | .map((x, index) => ({ item: x, id: indexcharacters.indexOf(x), type: 'character' })); |
| 1269 | + | |
| 1270 | + if (doFilter) { | |
| 1271 | + candidates = groupCandidatesFilter.applyFilters(candidates); | |
| 1272 | + } | |
| 1269 | 1273 | |
| 1270 | 1274 | if (onlyMembers) { |
| 1271 | 1275 | candidates.sort(sortMembersFn); |
| 1272 | 1276 | } else { |
| 1273 | - sortEntitiesList(candidates); | |
| 1277 | + const useFilterOrder = doFilter && !!$('#rm_group_filter').val(); | |
| 1274 | - } | |
| 1278 | + sortEntitiesList(candidates, useFilterOrder, groupCandidatesFilter); | |
| 1275 | - | |
| 1276 | - if (doFilter) { | |
| 1277 | - candidates = groupCandidatesFilter.applyFilters(candidates); | |
| 1278 | 1279 | } |
| 1279 | 1280 | |
| 1281 | + groupCandidatesFilter.clearFuzzySearchCaches(); | |
| 1280 | 1282 | return candidates; |
| 1281 | 1283 | } |
| 1282 | 1284 | |
| @@ -1863,32 +1865,38 @@ export async function deleteGroupChat(groupId, chatId) { | ||
| 1863 | 1865 | } |
| 1864 | 1866 | } |
| 1865 | 1867 | |
| 1866 | -export async function importGroupChat(formData) { | |
| 1868 | +/** | |
| 1867 | - await jQuery.ajax({ | |
| 1869 | + * Imports a group chat from a file and adds it to the group. | |
| 1868 | - type: 'POST', | |
| 1870 | + * @param {FormData} formData Form data to send to the server | |
| 1869 | - url: '/api/chats/group/import', | |
| 1871 | + * @param {EventTarget} eventTarget Element that triggered the import | |
| 1870 | - data: formData, | |
| 1872 | + */ | |
| 1871 | 1873 | beforeSend:export async function importGroupChat(formData, eventTarget) { |
| 1872 | - }, | |
| 1874 | + const headers = getRequestHeaders(); | |
| 1873 | - cache: false, | |
| 1875 | + delete headers['Content-Type']; | |
| 1874 | - contentType: false, | |
| 1876 | + const fetchResult = await fetch('/api/chats/group/import', { | |
| 1875 | 1877 | processDatamethod: false'POST', |
| 1876 | - success: async function (data) { | |
| 1878 | + headers: headers, | |
| 1877 | - if (data.res) { | |
| 1879 | + body: formData, | |
| 1878 | - const chatId = data.res; | |
| 1880 | + cache: 'no-cache', | |
| 1879 | - const group = groups.find(x => x.id == selected_group); | |
| 1880 | - | |
| 1881 | - if (group) { | |
| 1882 | - group.chats.push(chatId); | |
| 1883 | - await editGroup(selected_group, true, true); | |
| 1884 | - await displayPastChats(); | |
| 1885 | - } | |
| 1886 | - } | |
| 1887 | - }, | |
| 1888 | - error: function () { | |
| 1889 | - $('#create_button').removeAttr('disabled'); | |
| 1890 | - }, | |
| 1891 | 1881 | }); |
| 1882 | + | |
| 1883 | + if (fetchResult.ok) { | |
| 1884 | + const data = await fetchResult.json(); | |
| 1885 | + if (data.res) { | |
| 1886 | + const chatId = data.res; | |
| 1887 | + const group = groups.find(x => x.id == selected_group); | |
| 1888 | + | |
| 1889 | + if (group) { | |
| 1890 | + group.chats.push(chatId); | |
| 1891 | + await editGroup(selected_group, true, true); | |
| 1892 | + await displayPastChats(); | |
| 1893 | + } | |
| 1894 | + } | |
| 1895 | + } | |
| 1896 | + | |
| 1897 | + if (eventTarget instanceof HTMLInputElement) { | |
| 1898 | + eventTarget.value = ''; | |
| 1899 | + } | |
| 1892 | 1900 | } |
| 1893 | 1901 | |
| 1894 | 1902 | export async function saveGroupBookmarkChat(groupId, name, metadata, mesId) { |
| @@ -379,8 +379,8 @@ export let selected_proxy = proxies[0]; | ||
| 379 | 379 | let openai_setting_names; |
| 380 | 380 | let openai_settings; |
| 381 | 381 | |
| 382 | - | |
| 382 | +/** @type {import('./PromptManager.js').PromptManager} */ | |
| 383 | 383 | export let promptManager = null; |
| 384 | 384 | |
| 385 | 385 | async function validateReverseProxy() { |
| 386 | 386 | if (!oai_settings.reverse_proxy) { |
| @@ -1812,6 +1812,7 @@ async function sendOpenAIRequest(type, messages, signal) { | ||
| 1812 | 1812 | const isPerplexity = oai_settings.chat_completion_source == chat_completion_sources.PERPLEXITY; |
| 1813 | 1813 | const isGroq = oai_settings.chat_completion_source == chat_completion_sources.GROQ; |
| 1814 | 1814 | const is01AI = oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI; |
| 1815 | + const isNano = oai_settings.chat_completion_source == chat_completion_sources.NANOGPT; | |
| 1815 | 1816 | const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model); |
| 1816 | 1817 | const isQuiet = type === 'quiet'; |
| 1817 | 1818 | const isImpersonate = type === 'impersonate'; |
| @@ -1971,7 +1972,7 @@ async function sendOpenAIRequest(type, messages, signal) { | ||
| 1971 | 1972 | delete generate_data.stop; |
| 1972 | 1973 | } |
| 1973 | 1974 | |
| 1974 | 1975 | if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere || isNano) && oai_settings.seed >= 0) { |
| 1975 | 1976 | generate_data['seed'] = oai_settings.seed; |
| 1976 | 1977 | } |
| 1977 | 1978 | |
| @@ -4076,7 +4077,7 @@ async function onModelChange() { | ||
| 4076 | 4077 | $('#openai_max_context').attr('max', max_2mil); |
| 4077 | 4078 | } else if (value.includes('gemini-exp-1114') || value.includes('gemini-exp-1121')) { |
| 4078 | 4079 | $('#openai_max_context').attr('max', max_32k); |
| 4079 | 4080 | } else if (value.includes('gemini-1.5-pro') || value.includes('gemini-exp-1206')) { |
| 4080 | 4081 | $('#openai_max_context').attr('max', max_2mil); |
| 4081 | 4082 | } else if (value.includes('gemini-1.5-flash')) { |
| 4082 | 4083 | $('#openai_max_context').attr('max', max_1mil); |
| @@ -4760,6 +4761,7 @@ export function isImageInliningSupported() { | ||
| 4760 | 4761 | 'gemini-1.5-flash-8b-exp-0924', |
| 4761 | 4762 | 'gemini-exp-1114', |
| 4762 | 4763 | 'gemini-exp-1121', |
| 4764 | + 'gemini-exp-1206', | |
| 4763 | 4765 | 'gemini-1.0-pro-vision-latest', |
| 4764 | 4766 | 'gemini-1.5-pro', |
| 4765 | 4767 | 'gemini-1.5-pro-latest', |
| @@ -60,7 +60,6 @@ export { | ||
| 60 | 60 | loadMovingUIState, |
| 61 | 61 | collapseNewlines, |
| 62 | 62 | playMessageSound, |
| 63 | - sortEntitiesList, | |
| 64 | 63 | fixMarkdown, |
| 65 | 64 | power_user, |
| 66 | 65 | send_on_enter_options, |
| @@ -473,9 +472,9 @@ function switchCompactInputArea() { | ||
| 473 | 472 | $('#compact_input_area').prop('checked', power_user.compact_input_area); |
| 474 | 473 | } |
| 475 | 474 | |
| 476 | 475 | export function switchSwipeNumAllMessages() { |
| 477 | 476 | $('#show_swipe_num_all_messages').prop('checked', power_user.show_swipe_num_all_messages); |
| 478 | 477 | $('.mes:not(.last_mes) .swipes-counterbody').csstoggleClass('opacityswipeAllMessages', '').toggle(!!power_user.show_swipe_num_all_messages); |
| 479 | 478 | } |
| 480 | 479 | |
| 481 | 480 | var originalSliderValues = []; |
| @@ -1837,7 +1836,7 @@ async function loadContextSettings() { | ||
| 1837 | 1836 | * Common function to perform fuzzy search with optional caching |
| 1838 | 1837 | * @param {string} type - Type of search from fuzzySearchCategories |
| 1839 | 1838 | * @param {any[]} data - Data array to search in |
| 1840 | 1839 | * @param {Array<{name: string, weight: number, getFn?: Function(obj: any) => string}>} keys - Fuse.js keys configuration |
| 1841 | 1840 | * @param {string} searchValue - The search term |
| 1842 | 1841 | * @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches |
| 1843 | 1842 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score |
| @@ -1851,7 +1850,6 @@ function performFuzzySearch(type, data, keys, searchValue, fuzzySearchCaches = n | ||
| 1851 | 1850 | } |
| 1852 | 1851 | } |
| 1853 | 1852 | |
| 1854 | - // @ts-ignore | |
| 1855 | 1853 | const fuse = new Fuse(data, { |
| 1856 | 1854 | keys: keys, |
| 1857 | 1855 | includeScore: true, |
| @@ -1925,7 +1923,7 @@ export function fuzzySearchPersonas(data, searchValue, fuzzySearchCaches = null) | ||
| 1925 | 1923 | const mappedData = data.map(x => ({ |
| 1926 | 1924 | key: x, |
| 1927 | 1925 | name: power_user.personas[x] ?? '', |
| 1928 | 1926 | description: power_user.persona_descriptions[x]?.description ?? '', |
| 1929 | 1927 | })); |
| 1930 | 1928 | |
| 1931 | 1929 | const keys = [ |
| @@ -2080,19 +2078,22 @@ const compareFunc = (first, second) => { | ||
| 2080 | 2078 | /** |
| 2081 | 2079 | * Sorts an array of entities based on the current sort settings |
| 2082 | 2080 | * @param {any[]} entities An array of objects with an `item` property |
| 2081 | + * @param {boolean} forceSearch Whether to force search sorting | |
| 2082 | + * @param {import('./filters.js').FilterHelper} [filterHelper=null] Filter helper to use | |
| 2083 | 2083 | */ |
| 2084 | 2084 | export function sortEntitiesList(entities, forceSearch, filterHelper = null) { |
| 2085 | + filterHelper = filterHelper ?? entitiesFilter; | |
| 2085 | 2086 | if (power_user.sort_field == undefined || entities.length === 0) { |
| 2086 | 2087 | return; |
| 2087 | 2088 | } |
| 2088 | 2089 | |
| 2089 | - if (power_user.sort_order === 'random') { | |
| 2090 | + const isSearch = forceSearch || $('#character_sort_order option[data-field="search"]').is(':selected'); | |
| 2091 | + | |
| 2092 | + if (!isSearch && power_user.sort_order === 'random') { | |
| 2090 | 2093 | shuffle(entities); |
| 2091 | 2094 | return; |
| 2092 | 2095 | } |
| 2093 | 2096 | |
| 2094 | - const isSearch = $('#character_sort_order option[data-field="search"]').is(':selected'); | |
| 2095 | - | |
| 2096 | 2097 | entities.sort((a, b) => { |
| 2097 | 2098 | // Sort tags/folders will always be at the top |
| 2098 | 2099 | if (a.type === 'tag' && b.type !== 'tag') { |
| @@ -2104,8 +2105,8 @@ function sortEntitiesList(entities) { | ||
| 2104 | 2105 | |
| 2105 | 2106 | // If we have search sorting, we take scores and use those |
| 2106 | 2107 | if (isSearch) { |
| 2107 | 2108 | const aScore = entitiesFilterfilterHelper.getScore(FILTER_TYPES.SEARCH, `${a.type}.${a.id}`); |
| 2108 | 2109 | const bScore = entitiesFilterfilterHelper.getScore(FILTER_TYPES.SEARCH, `${b.type}.${b.id}`); |
| 2109 | 2110 | return (aScore - bScore); |
| 2110 | 2111 | } |
| 2111 | 2112 | |
| @@ -52,7 +52,7 @@ import { hideChatMessageRange } from './chats.js'; | ||
| 52 | 52 | import { getContext, saveMetadataDebounced } from './extensions.js'; |
| 53 | 53 | import { getRegexedString, regex_placement } from './extensions/regex/engine.js'; |
| 54 | 54 | import { findGroupMemberId, groups, is_group_generating, openGroupById, resetSelectedGroup, saveGroupChat, selected_group } from './group-chats.js'; |
| 55 | 55 | import { chat_completion_sources, oai_settings, setupChatCompletionPromptManagerpromptManager } from './openai.js'; |
| 56 | 56 | import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockState, togglePersonaLock, user_avatar } from './personas.js'; |
| 57 | 57 | import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js'; |
| 58 | 58 | import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; |
| @@ -269,7 +269,7 @@ export function initDefaultSlashCommands() { | ||
| 269 | 269 | }), |
| 270 | 270 | SlashCommandNamedArgument.fromProps({ |
| 271 | 271 | name: 'at', |
| 272 | 272 | 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.', |
| 273 | 273 | typeList: [ARGUMENT_TYPE.NUMBER], |
| 274 | 274 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), |
| 275 | 275 | }), |
| @@ -325,7 +325,7 @@ export function initDefaultSlashCommands() { | ||
| 325 | 325 | ), |
| 326 | 326 | SlashCommandNamedArgument.fromProps({ |
| 327 | 327 | name: 'at', |
| 328 | 328 | 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.', |
| 329 | 329 | typeList: [ARGUMENT_TYPE.NUMBER], |
| 330 | 330 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), |
| 331 | 331 | }), |
| @@ -388,7 +388,7 @@ export function initDefaultSlashCommands() { | ||
| 388 | 388 | ), |
| 389 | 389 | SlashCommandNamedArgument.fromProps({ |
| 390 | 390 | name: 'at', |
| 391 | 391 | 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.', |
| 392 | 392 | typeList: [ARGUMENT_TYPE.NUMBER], |
| 393 | 393 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), |
| 394 | 394 | }), |
| @@ -606,7 +606,7 @@ export function initDefaultSlashCommands() { | ||
| 606 | 606 | ), |
| 607 | 607 | SlashCommandNamedArgument.fromProps({ |
| 608 | 608 | name: 'at', |
| 609 | 609 | 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.', |
| 610 | 610 | typeList: [ARGUMENT_TYPE.NUMBER], |
| 611 | 611 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), |
| 612 | 612 | }), |
| @@ -1699,6 +1699,49 @@ export function initDefaultSlashCommands() { | ||
| 1699 | 1699 | helpString: 'Sets the model for the current API. Gets the current model name if no argument is provided.', |
| 1700 | 1700 | })); |
| 1701 | 1701 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1702 | + name: 'getpromptentry', | |
| 1703 | + aliases: ['getpromptentries'], | |
| 1704 | + callback: getPromptEntryCallback, | |
| 1705 | + returns: 'true/false state of prompt(s)', | |
| 1706 | + namedArgumentList: [ | |
| 1707 | + SlashCommandNamedArgument.fromProps({ | |
| 1708 | + name: 'identifier', | |
| 1709 | + description: 'Prompt entry identifier(s) to retrieve', | |
| 1710 | + typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST], | |
| 1711 | + acceptsMultiple: true, | |
| 1712 | + enumProvider: () => | |
| 1713 | + promptManager.serviceSettings.prompts | |
| 1714 | + .map(prompt => prompt.identifier) | |
| 1715 | + .map(identifier => new SlashCommandEnumValue(identifier)), | |
| 1716 | + }), | |
| 1717 | + SlashCommandNamedArgument.fromProps({ | |
| 1718 | + name: 'name', | |
| 1719 | + description: 'Prompt entry name(s) to retrieve', | |
| 1720 | + typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST], | |
| 1721 | + acceptsMultiple: true, | |
| 1722 | + enumProvider: () => | |
| 1723 | + promptManager.serviceSettings.prompts | |
| 1724 | + .map(prompt => prompt.name) | |
| 1725 | + .map(name => new SlashCommandEnumValue(name)), | |
| 1726 | + }), | |
| 1727 | + SlashCommandNamedArgument.fromProps({ | |
| 1728 | + name: 'return', | |
| 1729 | + description: 'Whether the return will be simple, a list, or a dict.', | |
| 1730 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 1731 | + defaultValue: 'simple', | |
| 1732 | + enumList: ['simple', 'list', 'dict'], | |
| 1733 | + }), | |
| 1734 | + ], | |
| 1735 | + helpString: ` | |
| 1736 | + <div> | |
| 1737 | + Gets the state of the specified prompt entries. | |
| 1738 | + </div> | |
| 1739 | + <div> | |
| 1740 | + 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. | |
| 1741 | + </div> | |
| 1742 | + `, | |
| 1743 | + })); | |
| 1744 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 1702 | 1745 | name: 'setpromptentry', |
| 1703 | 1746 | aliases: ['setpromptentries'], |
| 1704 | 1747 | callback: setPromptEntryCallback, |
| @@ -1709,7 +1752,6 @@ export function initDefaultSlashCommands() { | ||
| 1709 | 1752 | typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST], |
| 1710 | 1753 | acceptsMultiple: true, |
| 1711 | 1754 | enumProvider: () => { |
| 1712 | - const promptManager = setupChatCompletionPromptManager(oai_settings); | |
| 1713 | 1755 | const prompts = promptManager.serviceSettings.prompts; |
| 1714 | 1756 | return prompts.map(prompt => new SlashCommandEnumValue(prompt.identifier, prompt.name, enumTypes.enum)); |
| 1715 | 1757 | }, |
| @@ -1720,7 +1762,6 @@ export function initDefaultSlashCommands() { | ||
| 1720 | 1762 | typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST], |
| 1721 | 1763 | acceptsMultiple: true, |
| 1722 | 1764 | enumProvider: () => { |
| 1723 | - const promptManager = setupChatCompletionPromptManager(oai_settings); | |
| 1724 | 1765 | const prompts = promptManager.serviceSettings.prompts; |
| 1725 | 1766 | return prompts.map(prompt => new SlashCommandEnumValue(prompt.name, prompt.identifier, enumTypes.enum)); |
| 1726 | 1767 | }, |
| @@ -3025,7 +3066,7 @@ async function sendUserMessageCallback(args, text) { | ||
| 3025 | 3066 | let insertAt = Number(args?.at); |
| 3026 | 3067 | |
| 3027 | 3068 | // Convert possible depth parameter to index |
| 3028 | 3069 | if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt ===, Number(-0))) { |
| 3029 | 3070 | // Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7) |
| 3030 | 3071 | insertAt = chat.length + insertAt; |
| 3031 | 3072 | } |
| @@ -3399,7 +3440,7 @@ export async function sendMessageAs(args, text) { | ||
| 3399 | 3440 | let insertAt = Number(args.at); |
| 3400 | 3441 | |
| 3401 | 3442 | // Convert possible depth parameter to index |
| 3402 | 3443 | if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt ===, Number(-0))) { |
| 3403 | 3444 | // Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7) |
| 3404 | 3445 | insertAt = chat.length + insertAt; |
| 3405 | 3446 | } |
| @@ -3453,7 +3494,7 @@ export async function sendNarratorMessage(args, text) { | ||
| 3453 | 3494 | let insertAt = Number(args.at); |
| 3454 | 3495 | |
| 3455 | 3496 | // Convert possible depth parameter to index |
| 3456 | 3497 | if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt ===, Number(-0))) { |
| 3457 | 3498 | // Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7) |
| 3458 | 3499 | insertAt = chat.length + insertAt; |
| 3459 | 3500 | } |
| @@ -3542,7 +3583,7 @@ async function sendCommentMessage(args, text) { | ||
| 3542 | 3583 | let insertAt = Number(args.at); |
| 3543 | 3584 | |
| 3544 | 3585 | // Convert possible depth parameter to index |
| 3545 | 3586 | if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt ===, Number(-0))) { |
| 3546 | 3587 | // Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7) |
| 3547 | 3588 | insertAt = chat.length + insertAt; |
| 3548 | 3589 | } |
| @@ -3787,6 +3828,75 @@ function modelCallback(args, model) { | ||
| 3787 | 3828 | } |
| 3788 | 3829 | |
| 3789 | 3830 | /** |
| 3831 | + * Gets the state of prompt entries (toggles) either via identifier/uuid or name. | |
| 3832 | + * @param {object} args Object containing arguments | |
| 3833 | + * @param {string} args.identifier Select prompt entry using an identifier (uuid) | |
| 3834 | + * @param {string} args.name Select prompt entry using name | |
| 3835 | + * @param {string} args.return The type of return value to use (simple, list, dict) | |
| 3836 | + * @returns {Object} An object containing the states of the requested prompt entries | |
| 3837 | + */ | |
| 3838 | +function getPromptEntryCallback(args) { | |
| 3839 | + const prompts = promptManager.serviceSettings.prompts; | |
| 3840 | + let returnType = args.return ?? 'simple'; | |
| 3841 | + | |
| 3842 | + function parseArgs(arg) { | |
| 3843 | + // Arg is already an array | |
| 3844 | + if (Array.isArray(arg)) { | |
| 3845 | + return arg; | |
| 3846 | + } | |
| 3847 | + const list = []; | |
| 3848 | + try { | |
| 3849 | + // Arg is a JSON-stringified array | |
| 3850 | + const parsedArg = JSON.parse(arg); | |
| 3851 | + list.push(...Array.isArray(parsedArg) ? parsedArg : [arg]); | |
| 3852 | + } catch { | |
| 3853 | + // Arg is a string | |
| 3854 | + list.push(arg); | |
| 3855 | + } | |
| 3856 | + return list; | |
| 3857 | + } | |
| 3858 | + | |
| 3859 | + let identifiersList = parseArgs(args.identifier); | |
| 3860 | + let nameList = parseArgs(args.name); | |
| 3861 | + | |
| 3862 | + // Check if identifiers exists in prompt, else remove from list | |
| 3863 | + if (identifiersList.length !== 0) { | |
| 3864 | + identifiersList = identifiersList.filter(identifier => prompts.some(prompt => prompt.identifier === identifier)); | |
| 3865 | + } | |
| 3866 | + | |
| 3867 | + if (nameList.length !== 0) { | |
| 3868 | + nameList.forEach(name => { | |
| 3869 | + let identifiers = prompts | |
| 3870 | + .filter(entry => entry.name === name) | |
| 3871 | + .map(entry => entry.identifier); | |
| 3872 | + identifiersList = identifiersList.concat(identifiers); | |
| 3873 | + }); | |
| 3874 | + } | |
| 3875 | + | |
| 3876 | + // Get the state for each prompt entry | |
| 3877 | + let promptStates = new Map(); | |
| 3878 | + identifiersList.forEach(identifier => { | |
| 3879 | + const promptOrderEntry = promptManager.getPromptOrderEntry(promptManager.activeCharacter, identifier); | |
| 3880 | + if (promptOrderEntry) { | |
| 3881 | + promptStates.set(identifier, promptOrderEntry.enabled); | |
| 3882 | + } | |
| 3883 | + }); | |
| 3884 | + | |
| 3885 | + // If return is simple (default) but more than one prompt state was retrieved, then change return type | |
| 3886 | + if (returnType === 'simple' && promptStates.size > 1) { | |
| 3887 | + returnType = args.identifier ? 'dict' : 'list'; | |
| 3888 | + } | |
| 3889 | + | |
| 3890 | + const result = (() => { | |
| 3891 | + if (returnType === 'list') return [...promptStates.values()]; | |
| 3892 | + if (returnType === 'dict') return Object.fromEntries(promptStates); | |
| 3893 | + return [...promptStates.values()][0]; | |
| 3894 | + })(); | |
| 3895 | + | |
| 3896 | + return result; | |
| 3897 | +} | |
| 3898 | + | |
| 3899 | +/** | |
| 3790 | 3900 | * Sets state of prompt entries (toggles) either via identifier/uuid or name. |
| 3791 | 3901 | * @param {object} args Object containing arguments |
| 3792 | 3902 | * @param {string} args.identifier Select prompt entry using an identifier (uuid) |
| @@ -3796,7 +3906,6 @@ function modelCallback(args, model) { | ||
| 3796 | 3906 | */ |
| 3797 | 3907 | function setPromptEntryCallback(args, targetState) { |
| 3798 | 3908 | // needs promptManager to manipulate prompt entries |
| 3799 | - const promptManager = setupChatCompletionPromptManager(oai_settings); | |
| 3800 | 3909 | const prompts = promptManager.serviceSettings.prompts; |
| 3801 | 3910 | |
| 3802 | 3911 | function parseArgs(arg) { |
| @@ -0,0 +1,171 @@ | ||
| 1 | +import { | |
| 2 | + activateSendButtons, | |
| 3 | + addOneMessage, | |
| 4 | + callPopup, | |
| 5 | + characters, | |
| 6 | + chat, | |
| 7 | + chat_metadata, | |
| 8 | + create_save, | |
| 9 | + deactivateSendButtons, | |
| 10 | + event_types, | |
| 11 | + eventSource, | |
| 12 | + extension_prompts, | |
| 13 | + Generate, | |
| 14 | + generateQuietPrompt, | |
| 15 | + getCurrentChatId, | |
| 16 | + getRequestHeaders, | |
| 17 | + getThumbnailUrl, | |
| 18 | + main_api, | |
| 19 | + max_context, | |
| 20 | + menu_type, | |
| 21 | + messageFormatting, | |
| 22 | + name1, | |
| 23 | + name2, | |
| 24 | + online_status, | |
| 25 | + openCharacterChat, | |
| 26 | + reloadCurrentChat, | |
| 27 | + renameChat, | |
| 28 | + saveChatConditional, | |
| 29 | + saveMetadata, | |
| 30 | + saveReply, | |
| 31 | + saveSettingsDebounced, | |
| 32 | + selectCharacterById, | |
| 33 | + sendGenerationRequest, | |
| 34 | + sendStreamingRequest, | |
| 35 | + sendSystemMessage, | |
| 36 | + setExtensionPrompt, | |
| 37 | + stopGeneration, | |
| 38 | + streamingProcessor, | |
| 39 | + substituteParams, | |
| 40 | + substituteParamsExtended, | |
| 41 | + this_chid, | |
| 42 | + updateChatMetadata, | |
| 43 | +} from '../script.js'; | |
| 44 | +import { | |
| 45 | + extension_settings, | |
| 46 | + ModuleWorkerWrapper, | |
| 47 | + renderExtensionTemplate, | |
| 48 | + renderExtensionTemplateAsync, | |
| 49 | + writeExtensionField, | |
| 50 | +} from './extensions.js'; | |
| 51 | +import { groups, openGroupChat, selected_group } from './group-chats.js'; | |
| 52 | +import { t, translate } from './i18n.js'; | |
| 53 | +import { hideLoader, showLoader } from './loader.js'; | |
| 54 | +import { MacrosParser } from './macros.js'; | |
| 55 | +import { oai_settings } from './openai.js'; | |
| 56 | +import { callGenericPopup, Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js'; | |
| 57 | +import { power_user, registerDebugFunction } from './power-user.js'; | |
| 58 | +import { isMobile, shouldSendOnEnter } from './RossAscends-mods.js'; | |
| 59 | +import { ScraperManager } from './scrapers.js'; | |
| 60 | +import { executeSlashCommands, executeSlashCommandsWithOptions, registerSlashCommand } from './slash-commands.js'; | |
| 61 | +import { SlashCommand } from './slash-commands/SlashCommand.js'; | |
| 62 | +import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; | |
| 63 | +import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; | |
| 64 | +import { tag_map, tags } from './tags.js'; | |
| 65 | +import { textgenerationwebui_settings } from './textgen-settings.js'; | |
| 66 | +import { getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js'; | |
| 67 | +import { ToolManager } from './tool-calling.js'; | |
| 68 | +import { timestampToMoment } from './utils.js'; | |
| 69 | + | |
| 70 | +export function getContext() { | |
| 71 | + return { | |
| 72 | + chat, | |
| 73 | + characters, | |
| 74 | + groups, | |
| 75 | + name1, | |
| 76 | + name2, | |
| 77 | + characterId: this_chid, | |
| 78 | + groupId: selected_group, | |
| 79 | + chatId: selected_group | |
| 80 | + ? groups.find(x => x.id == selected_group)?.chat_id | |
| 81 | + : (characters[this_chid]?.chat), | |
| 82 | + getCurrentChatId, | |
| 83 | + getRequestHeaders, | |
| 84 | + reloadCurrentChat, | |
| 85 | + renameChat, | |
| 86 | + saveSettingsDebounced, | |
| 87 | + onlineStatus: online_status, | |
| 88 | + maxContext: Number(max_context), | |
| 89 | + chatMetadata: chat_metadata, | |
| 90 | + streamingProcessor, | |
| 91 | + eventSource, | |
| 92 | + eventTypes: event_types, | |
| 93 | + addOneMessage, | |
| 94 | + generate: Generate, | |
| 95 | + sendStreamingRequest, | |
| 96 | + sendGenerationRequest, | |
| 97 | + stopGeneration, | |
| 98 | + /** @deprecated Use getTokenCountAsync instead */ | |
| 99 | + getTokenCount, | |
| 100 | + getTokenCountAsync, | |
| 101 | + extensionPrompts: extension_prompts, | |
| 102 | + setExtensionPrompt, | |
| 103 | + updateChatMetadata, | |
| 104 | + saveChat: saveChatConditional, | |
| 105 | + openCharacterChat, | |
| 106 | + openGroupChat, | |
| 107 | + saveMetadata, | |
| 108 | + sendSystemMessage, | |
| 109 | + activateSendButtons, | |
| 110 | + deactivateSendButtons, | |
| 111 | + saveReply, | |
| 112 | + substituteParams, | |
| 113 | + substituteParamsExtended, | |
| 114 | + SlashCommandParser, | |
| 115 | + SlashCommand, | |
| 116 | + SlashCommandArgument, | |
| 117 | + SlashCommandNamedArgument, | |
| 118 | + ARGUMENT_TYPE, | |
| 119 | + executeSlashCommandsWithOptions, | |
| 120 | + /** @deprecated Use SlashCommandParser.addCommandObject() instead */ | |
| 121 | + registerSlashCommand, | |
| 122 | + /** @deprecated Use executeSlashCommandWithOptions instead */ | |
| 123 | + executeSlashCommands, | |
| 124 | + timestampToMoment, | |
| 125 | + /** @deprecated Handlebars for extensions are no longer supported. */ | |
| 126 | + registerHelper: () => { }, | |
| 127 | + registerMacro: MacrosParser.registerMacro.bind(MacrosParser), | |
| 128 | + unregisterMacro: MacrosParser.unregisterMacro.bind(MacrosParser), | |
| 129 | + registerFunctionTool: ToolManager.registerFunctionTool.bind(ToolManager), | |
| 130 | + unregisterFunctionTool: ToolManager.unregisterFunctionTool.bind(ToolManager), | |
| 131 | + isToolCallingSupported: ToolManager.isToolCallingSupported.bind(ToolManager), | |
| 132 | + canPerformToolCalls: ToolManager.canPerformToolCalls.bind(ToolManager), | |
| 133 | + registerDebugFunction, | |
| 134 | + /** @deprecated Use renderExtensionTemplateAsync instead. */ | |
| 135 | + renderExtensionTemplate, | |
| 136 | + renderExtensionTemplateAsync, | |
| 137 | + registerDataBankScraper: ScraperManager.registerDataBankScraper.bind(ScraperManager), | |
| 138 | + /** @deprecated Use callGenericPopup or Popup instead. */ | |
| 139 | + callPopup, | |
| 140 | + callGenericPopup, | |
| 141 | + showLoader, | |
| 142 | + hideLoader, | |
| 143 | + mainApi: main_api, | |
| 144 | + extensionSettings: extension_settings, | |
| 145 | + ModuleWorkerWrapper, | |
| 146 | + getTokenizerModel, | |
| 147 | + generateQuietPrompt, | |
| 148 | + writeExtensionField, | |
| 149 | + getThumbnailUrl, | |
| 150 | + selectCharacterById, | |
| 151 | + messageFormatting, | |
| 152 | + shouldSendOnEnter, | |
| 153 | + isMobile, | |
| 154 | + t, | |
| 155 | + translate, | |
| 156 | + tags, | |
| 157 | + tagMap: tag_map, | |
| 158 | + menuType: menu_type, | |
| 159 | + createCharacterData: create_save, | |
| 160 | + /** @deprecated Legacy snake-case naming, compatibility with old extensions */ | |
| 161 | + event_types: event_types, | |
| 162 | + Popup, | |
| 163 | + POPUP_TYPE, | |
| 164 | + POPUP_RESULT, | |
| 165 | + chatCompletionSettings: oai_settings, | |
| 166 | + textCompletionSettings: textgenerationwebui_settings, | |
| 167 | + powerUserSettings: power_user, | |
| 168 | + }; | |
| 169 | +} | |
| 170 | + | |
| 171 | +export default getContext; | |
| @@ -4,8 +4,17 @@ | ||
| 4 | 4 | <div id="copyPromptToClipboard" class="fa-solid fa-copy menu_button" title="Copy Prompt" data-i18n="[title]Copy Prompt"></div> |
| 5 | 5 | <div id="diffPrevPrompt" class="fa-solid fa-code-compare menu_button" title="Show Prompt Differences" data-i18n="[title]Show Prompt Differences"></div> |
| 6 | 6 | </h3> |
| 7 | -API/Model Used: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}– {{modelUsed}}{{/if}}<br> | |
| 7 | +<div> | |
| 8 | -Tokenizer: {{selectedTokenizer}}<br> | |
| 8 | + <div> | |
| 9 | + API/Model: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}– {{modelUsed}}{{/if}} | |
| 10 | + </div> | |
| 11 | + <div> | |
| 12 | + <small>Preset: {{presetName}}</small> | |
| 13 | + <span>|</span> | |
| 14 | + <small>Tokenizer: {{selectedTokenizer}}</small> | |
| 15 | + </div> | |
| 16 | +</div> | |
| 17 | + | |
| 9 | 18 | <span class="tokenItemizingSubclass"> |
| 10 | 19 | Only the white numbers really matter. All numbers are estimates. |
| 11 | 20 | Grey color items may not have been included in the context due to certain prompt format settings. |
| @@ -4,8 +4,17 @@ | ||
| 4 | 4 | <div id="copyPromptToClipboard" class="fa-solid fa-copy menu_button" title="Copy Prompt" data-i18n="[title]Copy Prompt"></div> |
| 5 | 5 | <div id="diffPrevPrompt" class="fa-solid fa-code-compare menu_button" title="Show Prompt Differences" data-i18n="[title]Show Prompt Differences"></div> |
| 6 | 6 | </h3> |
| 7 | -API/Model Used: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}– {{modelUsed}}{{/if}}<br> | |
| 7 | +<div> | |
| 8 | -Tokenizer: {{selectedTokenizer}}<br> | |
| 8 | + <div> | |
| 9 | + API/Model: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}– {{modelUsed}}{{/if}} | |
| 10 | + </div> | |
| 11 | + <div> | |
| 12 | + <small>Preset: {{presetName}}</small> | |
| 13 | + <span>|</span> | |
| 14 | + <small>Tokenizer: {{selectedTokenizer}}</small> | |
| 15 | + </div> | |
| 16 | +</div> | |
| 17 | + | |
| 9 | 18 | <span class="tokenItemizingSubclass"> |
| 10 | 19 | Only the white numbers really matter. All numbers are estimates. |
| 11 | 20 | Grey color items may not have been included in the context due to certain prompt format settings. |
| @@ -21,7 +21,8 @@ | ||
| 21 | 21 | <li><tt>{{user}}</tt> – <span data-i18n="help_macros_15">your current Persona username</span></li> |
| 22 | 22 | <li><tt>{{char}}</tt> – <span data-i18n="help_macros_16">the Character's name</span></li> |
| 23 | 23 | <li><tt>{{char_version}}</tt> – <span data-i18n="help_macros_17">the Character's version number</span></li> |
| 24 | 24 | <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> |
| 25 | + <li><tt>{{groupNotMuted}}</tt> – <span data-i18n="help_groupNotMuted">the same as {{group}}, but excludes muted members</span></li> | |
| 25 | 26 | <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> |
| 26 | 27 | <li><tt>{{lastMessage}}</tt> – <span data-i18n="help_macros_20">the text of the latest chat message.</span></li> |
| 27 | 28 | <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 } | ||
| 18 | 18 | import { ENCODE_TOKENIZERS, TEXTGEN_TOKENIZERS, getTextTokens, tokenizers } from './tokenizers.js'; |
| 19 | 19 | import { getSortableDelay, onlyUnique } from './utils.js'; |
| 20 | 20 | |
| 21 | -export { | |
| 22 | - settings as textgenerationwebui_settings, | |
| 23 | - loadTextGenSettings, | |
| 24 | - generateTextGenWithStreaming, | |
| 25 | - formatTextGenURL, | |
| 26 | -}; | |
| 27 | - | |
| 28 | 21 | export const textgen_types = { |
| 29 | 22 | OOBA: 'ooba', |
| 30 | 23 | MANCER: 'mancer', |
| @@ -197,6 +190,10 @@ const settings = { | ||
| 197 | 190 | featherless_model: '', |
| 198 | 191 | }; |
| 199 | 192 | |
| 193 | +export { | |
| 194 | + settings as textgenerationwebui_settings, | |
| 195 | +}; | |
| 196 | + | |
| 200 | 197 | export let textgenerationwebui_banned_in_macros = []; |
| 201 | 198 | |
| 202 | 199 | export let textgenerationwebui_presets = []; |
| @@ -327,7 +324,7 @@ async function selectPreset(name) { | ||
| 327 | 324 | saveSettingsDebounced(); |
| 328 | 325 | } |
| 329 | 326 | |
| 330 | 327 | export function formatTextGenURL(value) { |
| 331 | 328 | try { |
| 332 | 329 | const noFormatTypes = [MANCER, TOGETHERAI, INFERMATICAI, DREAMGEN, OPENROUTER]; |
| 333 | 330 | if (noFormatTypes.includes(settings.type)) { |
| @@ -465,7 +462,7 @@ function calculateLogitBias() { | ||
| 465 | 462 | return result; |
| 466 | 463 | } |
| 467 | 464 | |
| 468 | 465 | export function loadTextGenSettings(data, loadedSettings) { |
| 469 | 466 | textgenerationwebui_presets = convertPresets(data.textgenerationwebui_presets); |
| 470 | 467 | textgenerationwebui_preset_names = data.textgenerationwebui_preset_names ?? []; |
| 471 | 468 | Object.assign(settings, loadedSettings.textgenerationwebui_settings ?? {}); |
| @@ -889,7 +886,7 @@ function setSettingByName(setting, value, trigger) { | ||
| 889 | 886 | * @returns {Promise<(function(): AsyncGenerator<{swipes: [], text: string, toolCalls: [], logprobs: {token: string, topLogprobs: Candidate[]}|null}, void, *>)|*>} |
| 890 | 887 | * @throws {Error} - If the response status is not OK, or from within the generator |
| 891 | 888 | */ |
| 892 | 889 | export async function generateTextGenWithStreaming(generate_data, signal) { |
| 893 | 890 | generate_data.stream = true; |
| 894 | 891 | |
| 895 | 892 | const response = await fetch('/api/backends/text-completions/generate', { |
| @@ -1340,6 +1337,9 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, | ||
| 1340 | 1337 | 'dry_sequence_breakers': sequenceBreakers, |
| 1341 | 1338 | }; |
| 1342 | 1339 | params = Object.assign(params, llamaCppParams); |
| 1340 | + if (!Array.isArray(sequenceBreakers) || sequenceBreakers.length === 0) { | |
| 1341 | + delete params.dry_sequence_breakers; | |
| 1342 | + } | |
| 1343 | 1343 | } |
| 1344 | 1344 | |
| 1345 | 1345 | eventSource.emitAndWait(event_types.TEXT_COMPLETION_SETTINGS_READY, params); |
| @@ -8,8 +8,6 @@ import { kai_flags } from './kai-settings.js'; | ||
| 8 | 8 | import { textgen_types, textgenerationwebui_settings as textgen_settings, getTextGenServer, getTextGenModel } from './textgen-settings.js'; |
| 9 | 9 | import { getCurrentDreamGenModelTokenizer, getCurrentOpenRouterModelTokenizer, openRouterModels } from './textgen-models.js'; |
| 10 | 10 | |
| 11 | -const { OOBA, TABBY, KOBOLDCPP, VLLM, APHRODITE, LLAMACPP, OPENROUTER, DREAMGEN } = textgen_types; | |
| 12 | - | |
| 13 | 11 | export const CHARACTERS_PER_TOKEN_RATIO = 3.35; |
| 14 | 12 | export const TOKENIZER_WARNING_KEY = 'tokenizationWarningShown'; |
| 15 | 13 | export const TOKENIZER_SUPPORTED_KEY = 'tokenizationSupported'; |
| @@ -52,8 +50,12 @@ export const ENCODE_TOKENIZERS = [ | ||
| 52 | 50 | //tokenizers.NERD2, |
| 53 | 51 | ]; |
| 54 | 52 | |
| 55 | -// A list of Text Completion sources that support remote tokenization. | |
| 53 | +/** | |
| 56 | -export const TEXTGEN_TOKENIZERS = [OOBA, TABBY, KOBOLDCPP, LLAMACPP, VLLM, APHRODITE]; | |
| 54 | + * A list of Text Completion sources that support remote tokenization. | |
| 55 | + * Populated in initTokenziers due to circular dependencies. | |
| 56 | + * @type {string[]} | |
| 57 | + */ | |
| 58 | +export const TEXTGEN_TOKENIZERS = []; | |
| 57 | 59 | |
| 58 | 60 | const TOKENIZER_URLS = { |
| 59 | 61 | [tokenizers.GPT2]: { |
| @@ -287,7 +289,7 @@ export function getTokenizerBestMatch(forApi) { | ||
| 287 | 289 | const hasTokenizerError = sessionStorage.getItem(TOKENIZER_WARNING_KEY); |
| 288 | 290 | const hasValidEndpoint = sessionStorage.getItem(TOKENIZER_SUPPORTED_KEY); |
| 289 | 291 | const isConnected = online_status !== 'no_connection'; |
| 290 | 292 | const isTokenizerSupported = TEXTGEN_TOKENIZERS.includes(textgen_settings.type) && (textgen_settings.type !== textgen_types.OOBA || hasValidEndpoint); |
| 291 | 293 | |
| 292 | 294 | if (!hasTokenizerError && isConnected) { |
| 293 | 295 | if (forApi === 'kobold' && kai_flags.can_use_tokenization) { |
| @@ -297,10 +299,10 @@ export function getTokenizerBestMatch(forApi) { | ||
| 297 | 299 | if (forApi === 'textgenerationwebui' && isTokenizerSupported) { |
| 298 | 300 | return tokenizers.API_TEXTGENERATIONWEBUI; |
| 299 | 301 | } |
| 300 | 302 | if (forApi === 'textgenerationwebui' && textgen_settings.type === textgen_types.OPENROUTER) { |
| 301 | 303 | return getCurrentOpenRouterModelTokenizer(); |
| 302 | 304 | } |
| 303 | 305 | if (forApi === 'textgenerationwebui' && textgen_settings.type === textgen_types.DREAMGEN) { |
| 304 | 306 | return getCurrentDreamGenModelTokenizer(); |
| 305 | 307 | } |
| 306 | 308 | } |
| @@ -576,7 +578,7 @@ export function getTokenizerModel() { | ||
| 576 | 578 | |
| 577 | 579 | // And for OpenRouter (if not a site model, then it's impossible to determine the tokenizer) |
| 578 | 580 | if (main_api == 'openai' && oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER && oai_settings.openrouter_model || |
| 579 | 581 | main_api == 'textgenerationwebui' && textgen_settings.type === textgen_types.OPENROUTER && textgen_settings.openrouter_model) { |
| 580 | 582 | const model = main_api == 'openai' |
| 581 | 583 | ? model_list.find(x => x.id === oai_settings.openrouter_model) |
| 582 | 584 | : openRouterModels.find(x => x.id === textgen_settings.openrouter_model); |
| @@ -652,7 +654,7 @@ export function getTokenizerModel() { | ||
| 652 | 654 | return oai_settings.custom_model; |
| 653 | 655 | } |
| 654 | 656 | |
| 655 | 657 | if (oai_settings.chat_completion_source === chat_completion_sources.PERPLEXITY) { |
| 656 | 658 | if (oai_settings.perplexity_model.includes('llama-3') || oai_settings.perplexity_model.includes('llama3')) { |
| 657 | 659 | return llama3Tokenizer; |
| 658 | 660 | } |
| @@ -680,7 +682,7 @@ export function getTokenizerModel() { | ||
| 680 | 682 | return yiTokenizer; |
| 681 | 683 | } |
| 682 | 684 | |
| 683 | 685 | if (oai_settings.chat_completion_source === chat_completion_sources.BLOCKENTROPY) { |
| 684 | 686 | if (oai_settings.blockentropy_model.includes('llama3')) { |
| 685 | 687 | return llama3Tokenizer; |
| 686 | 688 | } |
| @@ -1121,6 +1123,14 @@ export function decodeTextTokens(tokenizerType, ids) { | ||
| 1121 | 1123 | } |
| 1122 | 1124 | |
| 1123 | 1125 | export async function initTokenizers() { |
| 1126 | + TEXTGEN_TOKENIZERS.push( | |
| 1127 | + textgen_types.OOBA, | |
| 1128 | + textgen_types.TABBY, | |
| 1129 | + textgen_types.KOBOLDCPP, | |
| 1130 | + textgen_types.LLAMACPP, | |
| 1131 | + textgen_types.VLLM, | |
| 1132 | + textgen_types.APHRODITE, | |
| 1133 | + ); | |
| 1124 | 1134 | await loadTokenCache(); |
| 1125 | 1135 | 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); |
| 1126 | 1136 | } |
| @@ -947,7 +947,7 @@ export class ToolManager { | ||
| 947 | 947 | enumProvider: toolsEnumProvider, |
| 948 | 948 | }), |
| 949 | 949 | ], |
| 950 | 950 | callback: async (_, name) => { |
| 951 | 951 | if (typeof name !== 'string' || !name) { |
| 952 | 952 | throw new Error('The unnamed argument must be a non-empty string.'); |
| 953 | 953 | } |
| @@ -1040,8 +1040,14 @@ body .panelControlBar { | ||
| 1040 | 1040 | height: var(--swipeCounterHeight); |
| 1041 | 1041 | } |
| 1042 | 1042 | |
| 1043 | 1043 | body:not(.swipeAllMessages) .mes:not(.last_mes) .swipes-counter { |
| 1044 | 1044 | opacityvisibility: 0.3hidden; |
| 1045 | +} | |
| 1046 | + | |
| 1047 | +body.swipeAllMessages .mes:not(.last_mes) .swipes-counter { | |
| 1048 | + /* Avoid expensive DOM queries */ | |
| 1049 | + opacity: 0.3 !important; | |
| 1050 | + display: flex !important; | |
| 1045 | 1051 | } |
| 1046 | 1052 | |
| 1047 | 1053 | .swipe_left { |
| @@ -280,7 +280,7 @@ async function sendMakerSuiteRequest(request, response) { | ||
| 280 | 280 | delete generationConfig.stopSequences; |
| 281 | 281 | } |
| 282 | 282 | |
| 283 | 283 | const should_use_system_prompt = (model.includes('gemini-1.5-flash') || model.includes('gemini-1.5-pro') || model.includes('gemini-exp-1114') || model.includesstartsWith('gemini-exp-1121')) && request.body.use_makersuite_sysprompt; |
| 284 | 284 | const prompt = convertGooglePrompt(request.body.messages, model, should_use_system_prompt, request.body.char_name, request.body.user_name); |
| 285 | 285 | let body = { |
| 286 | 286 | contents: prompt.contents, |
| @@ -14,7 +14,7 @@ import jimp from 'jimp'; | ||
| 14 | 14 | |
| 15 | 15 | import { AVATAR_WIDTH, AVATAR_HEIGHT } from '../constants.js'; |
| 16 | 16 | import { jsonParser, urlencodedParser } from '../express-common.js'; |
| 17 | 17 | import { deepMerge, humanizedISO8601DateTime, tryParse, extractFileFromZipBuffer, MemoryLimitedMap, getConfigValue } from '../util.js'; |
| 18 | 18 | import { TavernCardValidator } from '../validator/TavernCardValidator.js'; |
| 19 | 19 | import { parse, write } from '../character-card-parser.js'; |
| 20 | 20 | import { readWorldInfoFile } from './worldinfo.js'; |
| @@ -23,8 +23,9 @@ import { importRisuSprites } from './sprites.js'; | ||
| 23 | 23 | const defaultAvatarPath = './public/img/ai4.png'; |
| 24 | 24 | |
| 25 | 25 | // KV-store for parsed character data |
| 26 | -// 100 MB limit. Would take roughly 3000 characters to reach this limit | |
| 26 | +const cacheCapacity = Number(getConfigValue('cardsCacheCapacity', 100)); // MB | |
| 27 | -const characterDataCache = new MemoryLimitedMap(1024 * 1024 * 100); | |
| 27 | +// With 100 MB limit it would take roughly 3000 characters to reach this limit | |
| 28 | +const characterDataCache = new MemoryLimitedMap(1024 * 1024 * cacheCapacity); | |
| 28 | 29 | // Some Android devices require tighter memory management |
| 29 | 30 | const isAndroid = process.platform === 'android'; |
| 30 | 31 | |
| @@ -191,6 +191,44 @@ function importCAIChat(userName, characterName, jsonData) { | ||
| 191 | 191 | } |
| 192 | 192 | |
| 193 | 193 | /** |
| 194 | + * Imports a chat from Kobold Lite format. | |
| 195 | + * @param {string} _userName User name | |
| 196 | + * @param {string} _characterName Character name | |
| 197 | + * @param {object} data JSON data | |
| 198 | + * @returns {string} Chat data | |
| 199 | + */ | |
| 200 | +function importKoboldLiteChat(_userName, _characterName, data) { | |
| 201 | + const inputToken = '{{[INPUT]}}'; | |
| 202 | + const outputToken = '{{[OUTPUT]}}'; | |
| 203 | + | |
| 204 | + /** @type {function(string): object} */ | |
| 205 | + function processKoboldMessage(msg) { | |
| 206 | + const isUser = msg.includes(inputToken); | |
| 207 | + return { | |
| 208 | + name: isUser ? header.user_name : header.character_name, | |
| 209 | + is_user: isUser, | |
| 210 | + mes: msg.replaceAll(inputToken, '').replaceAll(outputToken, '').trim(), | |
| 211 | + send_date: Date.now(), | |
| 212 | + }; | |
| 213 | + } | |
| 214 | + | |
| 215 | + // Create the header | |
| 216 | + const header = { | |
| 217 | + user_name: String(data.savedsettings.chatname), | |
| 218 | + character_name: String(data.savedsettings.chatopponent).split('||$||')[0], | |
| 219 | + }; | |
| 220 | + // Format messages | |
| 221 | + const formattedMessages = data.actions.map(processKoboldMessage); | |
| 222 | + // Add prompt if available | |
| 223 | + if (data.prompt) { | |
| 224 | + formattedMessages.unshift(processKoboldMessage(data.prompt)); | |
| 225 | + } | |
| 226 | + // Combine header and messages | |
| 227 | + const chatData = [header, ...formattedMessages]; | |
| 228 | + return chatData.map(obj => JSON.stringify(obj)).join('\n'); | |
| 229 | +} | |
| 230 | + | |
| 231 | +/** | |
| 194 | 232 | * Flattens `msg` and `swipes` data from Chub Chat format. |
| 195 | 233 | * Only changes enough to make it compatible with the standard chat serialization format. |
| 196 | 234 | * @param {string} userName User name |
| @@ -413,7 +451,7 @@ router.post('/import', urlencodedParser, function (request, response) { | ||
| 413 | 451 | const format = request.body.file_type; |
| 414 | 452 | const avatarUrl = (request.body.avatar_url).replace('.png', ''); |
| 415 | 453 | const characterName = request.body.character_name; |
| 416 | 454 | const userName = request.body.user_name || 'YouUser'; |
| 417 | 455 | |
| 418 | 456 | if (!request.file) { |
| 419 | 457 | return response.sendStatus(400); |
| @@ -426,33 +464,38 @@ router.post('/import', urlencodedParser, function (request, response) { | ||
| 426 | 464 | if (format === 'json') { |
| 427 | 465 | fs.unlinkSync(pathToUpload); |
| 428 | 466 | const jsonData = JSON.parse(data); |
| 429 | - if (jsonData.histories !== undefined) { | |
| 467 | + | |
| 430 | - // CAI Tools format | |
| 468 | + /** @type {function(string, string, object): string|string[]} */ | |
| 431 | - const chats = importCAIChat(userName, characterName, jsonData); | |
| 469 | + let importFunc; | |
| 432 | - for (const chat of chats) { | |
| 470 | + | |
| 433 | - const fileName = `${characterName} - ${humanizedISO8601DateTime()} imported.jsonl`; | |
| 471 | + if (jsonData.savedsettings !== undefined) { // Kobold Lite format | |
| 434 | - const filePath = path.join(request.user.directories.chats, avatarUrl, fileName); | |
| 472 | + importFunc = importKoboldLiteChat; | |
| 435 | - writeFileAtomicSync(filePath, chat, 'utf8'); | |
| 473 | + } else if (jsonData.histories !== undefined) { // CAI Tools format | |
| 436 | - } | |
| 474 | + importFunc = importCAIChat; | |
| 437 | - return response.send({ res: true }); | |
| 475 | + } else if (Array.isArray(jsonData.data_visible)) { // oobabooga's format | |
| 438 | - } else if (Array.isArray(jsonData.data_visible)) { | |
| 476 | + importFunc = importOobaChat; | |
| 439 | - // oobabooga's format | |
| 477 | + } else if (Array.isArray(jsonData.messages)) { // Agnai's format | |
| 440 | - const chat = importOobaChat(userName, characterName, jsonData); | |
| 478 | + importFunc = importAgnaiChat; | |
| 441 | - const fileName = `${characterName} - ${humanizedISO8601DateTime()} imported.jsonl`; | |
| 479 | + } else { // Unknown format | |
| 442 | - const filePath = path.join(request.user.directories.chats, avatarUrl, fileName); | |
| 480 | + console.log('Incorrect chat format .json'); | |
| 443 | - writeFileAtomicSync(filePath, chat, 'utf8'); | |
| 481 | + return response.send({ error: true }); | |
| 444 | - return response.send({ res: true }); | |
| 482 | + } | |
| 445 | - } else if (Array.isArray(jsonData.messages)) { | |
| 483 | + | |
| 446 | - // Agnai format | |
| 484 | + const handleChat = (chat) => { | |
| 447 | - const chat = importAgnaiChat(userName, characterName, jsonData); | |
| 448 | 485 | const fileName = `${characterName} - ${humanizedISO8601DateTime()} imported.jsonl`; |
| 449 | 486 | const filePath = path.join(request.user.directories.chats, avatarUrl, fileName); |
| 450 | 487 | writeFileAtomicSync(filePath, chat, 'utf8'); |
| 451 | - return response.send({ res: true }); | |
| 488 | + }; | |
| 489 | + | |
| 490 | + const chat = importFunc(userName, characterName, jsonData); | |
| 491 | + | |
| 492 | + if (Array.isArray(chat)) { | |
| 493 | + chat.forEach(handleChat); | |
| 452 | 494 | } else { |
| 453 | - console.log('Incorrect chat format .json'); | |
| 495 | + handleChat(chat); | |
| 454 | - return response.send({ error: true }); | |
| 455 | 496 | } |
| 497 | + | |
| 498 | + return response.send({ res: true }); | |
| 456 | 499 | } |
| 457 | 500 | |
| 458 | 501 | if (format === 'jsonl') { |
| @@ -561,10 +604,14 @@ router.post('/search', jsonParser, function (request, response) { | ||
| 561 | 604 | |
| 562 | 605 | let targetGroup; |
| 563 | 606 | for (const groupFile of groupFiles) { |
| 564 | - const groupData = JSON.parse(fs.readFileSync(path.join(groupDir, groupFile), 'utf8')); | |
| 607 | + try { | |
| 565 | - if (groupData.id === group_id) { | |
| 608 | + const groupData = JSON.parse(fs.readFileSync(path.join(groupDir, groupFile), 'utf8')); | |
| 566 | - targetGroup = groupData; | |
| 609 | + if (groupData.id === group_id) { | |
| 567 | - break; | |
| 610 | + targetGroup = groupData; | |
| 611 | + break; | |
| 612 | + } | |
| 613 | + } catch (error) { | |
| 614 | + console.error(groupFile, 'group file is corrupted:', error); | |
| 568 | 615 | } |
| 569 | 616 | } |
| 570 | 617 | |
| @@ -67,17 +67,17 @@ router.post('/login', jsonParser, async (request, response) => { | ||
| 67 | 67 | const user = await storage.getItem(toKey(request.body.handle)); |
| 68 | 68 | |
| 69 | 69 | if (!user) { |
| 70 | 70 | console.log('Login failed: User', request.body.handle, 'not found'); |
| 71 | 71 | return response.status(403).json({ error: 'Incorrect credentials' }); |
| 72 | 72 | } |
| 73 | 73 | |
| 74 | 74 | if (!user.enabled) { |
| 75 | 75 | console.log('Login failed: User', user.handle, 'is disabled'); |
| 76 | 76 | return response.status(403).json({ error: 'User is disabled' }); |
| 77 | 77 | } |
| 78 | 78 | |
| 79 | 79 | if (user.password && user.password !== getPasswordHash(request.body.password, user.salt)) { |
| 80 | 80 | console.log('Login failed: Incorrect password for', user.handle); |
| 81 | 81 | return response.status(403).json({ error: 'Incorrect credentials' }); |
| 82 | 82 | } |
| 83 | 83 | |
| @@ -88,7 +88,7 @@ router.post('/login', jsonParser, async (request, response) => { | ||
| 88 | 88 | |
| 89 | 89 | await loginLimiter.delete(ip); |
| 90 | 90 | request.session.handle = user.handle; |
| 91 | 91 | console.log('Login successful:', user.handle, request'from', ip, 'at', new Date().sessiontoLocaleString()); |
| 92 | 92 | return response.json({ handle: user.handle }); |
| 93 | 93 | } catch (error) { |
| 94 | 94 | if (error instanceof RateLimiterRes) { |
| @@ -115,12 +115,12 @@ router.post('/recover-step1', jsonParser, async (request, response) => { | ||
| 115 | 115 | const user = await storage.getItem(toKey(request.body.handle)); |
| 116 | 116 | |
| 117 | 117 | if (!user) { |
| 118 | 118 | console.log('Recover step 1 failed: User', request.body.handle, 'not found'); |
| 119 | 119 | return response.status(404).json({ error: 'User not found' }); |
| 120 | 120 | } |
| 121 | 121 | |
| 122 | 122 | if (!user.enabled) { |
| 123 | 123 | console.log('Recover step 1 failed: User', user.handle, 'is disabled'); |
| 124 | 124 | return response.status(403).json({ error: 'User is disabled' }); |
| 125 | 125 | } |
| 126 | 126 | |
| @@ -153,12 +153,12 @@ router.post('/recover-step2', jsonParser, async (request, response) => { | ||
| 153 | 153 | const ip = getIpFromRequest(request); |
| 154 | 154 | |
| 155 | 155 | if (!user) { |
| 156 | 156 | console.log('Recover step 2 failed: User', request.body.handle, 'not found'); |
| 157 | 157 | return response.status(404).json({ error: 'User not found' }); |
| 158 | 158 | } |
| 159 | 159 | |
| 160 | 160 | if (!user.enabled) { |
| 161 | 161 | console.log('Recover step 2 failed: User', user.handle, 'is disabled'); |
| 162 | 162 | return response.status(403).json({ error: 'User is disabled' }); |
| 163 | 163 | } |
| 164 | 164 | |
| @@ -347,6 +347,7 @@ export function convertGooglePrompt(messages, model, useSysPrompt = false, charN | ||
| 347 | 347 | 'gemini-1.5-flash-8b-exp-0924', |
| 348 | 348 | 'gemini-exp-1114', |
| 349 | 349 | 'gemini-exp-1121', |
| 350 | + 'gemini-exp-1206', | |
| 350 | 351 | 'gemini-1.5-pro', |
| 351 | 352 | 'gemini-1.5-pro-latest', |
| 352 | 353 | 'gemini-1.5-pro-001', |
| @@ -680,8 +680,9 @@ export class MemoryLimitedMap { | ||
| 680 | 680 | * @param {number} maxMemoryInBytes - The maximum allowed memory in bytes for string values. |
| 681 | 681 | */ |
| 682 | 682 | constructor(maxMemoryInBytes) { |
| 683 | 683 | if (typeof maxMemoryInBytes !== 'number' || maxMemoryInBytes <= 0 || isNaN(maxMemoryInBytes)) { |
| 684 | - throw new Error('maxMemoryInBytes must be a positive number'); | |
| 684 | + console.warn('Invalid maxMemoryInBytes, using a fallback value of 1 GB.'); | |
| 685 | + maxMemoryInBytes = 1024 * 1024 * 1024; // 1 GB | |
| 685 | 686 | } |
| 686 | 687 | this.maxMemory = maxMemoryInBytes; |
| 687 | 688 | this.currentMemory = 0; |