From 235a1372e82bc93405131ae055d162c496b2e382 Mon Sep 17 00:00:00 2001 From: InspectorCaracal <51038201+InspectorCaracal@users.noreply.github.com> Date: Sat, 22 Mar 2025 23:41:29 -0600 Subject: [PATCH 001/230] fix group chid data attr --- public/scripts/slash-commands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index e1f8f0870..2fd403af8 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -3087,7 +3087,7 @@ async function unhideMessageCallback(args, value) { * @returns {void} */ function performGroupMemberAction(chid, action) { - const memberSelector = `.group_member[chid="${chid}"]`; + const memberSelector = `.group_member[data-chid="${chid}"]`; // Do not optimize. Paginator gets recreated on every action const paginationSelector = '#rm_group_members_pagination'; const pageSizeSelector = '#rm_group_members_pagination select'; From fef36bfc39b6dc636fe7eb0988a3bd4ec8f2ad72 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Mon, 17 Mar 2025 20:59:08 +0100 Subject: [PATCH 002/230] Fix deleting swipes overwriting reasoning - Well, someone forgot about syncing extras and mes data again.... - Built the oppositive function of `syncMesToSwipe`, so we can now call `syncSwipeToMes` Fixes #3787 --- public/script.js | 68 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/public/script.js b/public/script.js index 8d3784c71..21b2c3e33 100644 --- a/public/script.js +++ b/public/script.js @@ -6261,7 +6261,6 @@ export function syncMesToSwipe(messageId = null) { } const targetMessage = chat[targetMessageId]; - if (!targetMessage) { return false; } @@ -6294,6 +6293,68 @@ export function syncMesToSwipe(messageId = null) { return true; } +/** + * Syncs swipe data back to the message data at the given message ID (or the last message if no ID is given). + * If the swipe ID is not provided, the current swipe ID in the message object is used. + * + * If the swipe data is invalid in some way, this function will exit out without doing anything. + * @param {number?} [messageId=null] - The ID of the message to sync with the swipe data. If no ID is given, the last message is used. + * @param {number?} [swipeId=null] - The ID of the swipe to sync. If no ID is given, the current swipe ID in the message object is used. + * @returns {boolean} Whether the swipe data was successfully synced to the message + */ +export function syncSwipeToMes(messageId = null, swipeId = null) { + if (!chat.length) { + return false; + } + + const targetMessageId = messageId ?? chat.length - 1; + if (targetMessageId >= chat.length || targetMessageId < 0) { + console.warn(`[syncSwipeToMes] Invalid message ID: ${messageId}`); + return false; + } + + const targetMessage = chat[targetMessageId]; + if (!targetMessage) { + return false; + } + + if (swipeId !== null) { + if (isNaN(swipeId) || swipeId < 0) { + console.warn(`[syncSwipeToMes] Invalid swipe ID: ${swipeId}`); + return false; + } + targetMessage.swipe_id = swipeId; + } + + // No swipe data there yet, exit out + if (typeof targetMessage.swipe_id !== 'number') { + return false; + } + // If swipes structure is invalid, exit out + if (!Array.isArray(targetMessage.swipe_info) || !Array.isArray(targetMessage.swipes)) { + return false; + } + + const targetSwipeId = targetMessage.swipe_id; + if (!targetMessage.swipes[targetSwipeId] || !targetMessage.swipe_info[targetSwipeId]) { + console.warn(`[syncSwipeToMes] Invalid swipe ID: ${targetSwipeId}`); + return false; + } + + const targetSwipeInfo = targetMessage.swipe_info[targetSwipeId]; + if (typeof targetSwipeInfo !== 'object') { + return false; + } + + targetMessage.mes = targetMessage.swipes[targetSwipeId]; + targetMessage.send_date = targetSwipeInfo.send_date; + targetMessage.gen_started = targetSwipeInfo.gen_started; + targetMessage.gen_finished = targetSwipeInfo.gen_finished; + targetMessage.extra = structuredClone(targetSwipeInfo.extra); + + return true; +} + /** * Saves the image to the message object. * @param {ParsedImage} img Image object @@ -8293,10 +8354,9 @@ export async function deleteSwipe(swipeId = null) { lastMessage.swipe_info.splice(swipeId, 1); } - // Select the next swip, or the one before if it was the last one + // Select the next swipe, or the one before if it was the last one const newSwipeId = Math.min(swipeId, lastMessage.swipes.length - 1); - lastMessage.swipe_id = newSwipeId; - lastMessage.mes = lastMessage.swipes[newSwipeId]; + syncSwipeToMes(null, newSwipeId); await saveChatConditional(); await reloadCurrentChat(); From 820f4439ada3def6358fe7390150531600672243 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 9 Apr 2025 21:58:32 +0300 Subject: [PATCH 003/230] Exclude .ts files from PR lint check --- .github/workflows/pr-auto-manager.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-auto-manager.yml b/.github/workflows/pr-auto-manager.yml index d713df8b7..6ed8a6f2f 100644 --- a/.github/workflows/pr-auto-manager.yml +++ b/.github/workflows/pr-auto-manager.yml @@ -49,7 +49,7 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} eslint-args: '--ignore-path=.gitignore --quiet' - extensions: 'js,ts' + extensions: 'js' annotations: true ignore-patterns: | dist/ From c3b1573c91db5efa4b8e5c0db56d60588e6cc22b Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 9 Apr 2025 22:09:10 +0300 Subject: [PATCH 004/230] Force resave CC preset after renaming Fixes #3828 --- public/scripts/preset-manager.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/public/scripts/preset-manager.js b/public/scripts/preset-manager.js index 5ddd42422..981855704 100644 --- a/public/scripts/preset-manager.js +++ b/public/scripts/preset-manager.js @@ -902,6 +902,12 @@ export async function initPresetManager() { await presetManager.renamePreset(newName); + if (apiId === 'openai') { + // This is a horrible mess, but prevents the renamed preset from being corrupted. + $('#update_oai_preset').trigger('click'); + return; + } + const successToast = !presetManager.isAdvancedFormatting() ? t`Preset renamed` : t`Template renamed`; toastr.success(successToast); }); From 1c52099ed6d8f964bfcc69a3571ccdb22ba10e78 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 10 Apr 2025 22:59:10 +0300 Subject: [PATCH 005/230] Add xAI as chat completion source --- public/index.html | 44 +++++++++++---- public/scripts/RossAscends-mods.js | 1 + public/scripts/openai.js | 62 +++++++++++++++++++++- public/scripts/reasoning.js | 2 + public/scripts/secrets.js | 2 + public/scripts/slash-commands.js | 1 + public/scripts/tool-calling.js | 1 + src/constants.js | 1 + src/endpoints/backends/chat-completions.js | 15 ++++++ src/endpoints/secrets.js | 1 + src/prompt-converters.js | 2 +- 11 files changed, 120 insertions(+), 12 deletions(-) diff --git a/public/index.html b/public/index.html index d17b1041a..b13f2f3cf 100644 --- a/public/index.html +++ b/public/index.html @@ -685,7 +685,7 @@ -
+
Temperature
@@ -698,7 +698,7 @@
-
+
Frequency Penalty
@@ -711,7 +711,7 @@
-
+
Presence Penalty
@@ -737,7 +737,7 @@
-
+
Top P
@@ -974,7 +974,7 @@
-
+
Seed
@@ -1965,7 +1965,7 @@
-
+
-
+
-
+
-
+
Max Depth - +
diff --git a/public/scripts/extensions/vectors/settings.html b/public/scripts/extensions/vectors/settings.html index 5078ec549..fc01ebe71 100644 --- a/public/scripts/extensions/vectors/settings.html +++ b/public/scripts/extensions/vectors/settings.html @@ -310,7 +310,7 @@
diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 96afac719..f328a5184 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -15,12 +15,12 @@ import { extension_prompt_types, Generate, getExtensionPrompt, + getExtensionPromptMaxDepth, getNextMessageId, getRequestHeaders, getStoppingStrings, is_send_press, main_api, - MAX_INJECTION_DEPTH, name1, name2, replaceItemizedPromptText, @@ -742,7 +742,8 @@ async function populationInjectionPrompts(prompts, messages) { 'assistant': extension_prompt_roles.ASSISTANT, }; - for (let i = 0; i <= MAX_INJECTION_DEPTH; i++) { + const maxDepth = getExtensionPromptMaxDepth(); + for (let i = 0; i <= maxDepth; i++) { // Get prompts for current depth const depthPrompts = prompts.filter(prompt => prompt.injection_depth === i && prompt.content); From 0895bc6c1da516805651d8a0fe5367779ce0b31c Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Mon, 14 Apr 2025 09:57:10 +0300 Subject: [PATCH 034/230] Replace getExtensionPromptMaxDepth with a constant --- public/script.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/script.js b/public/script.js index 37ece2817..80202e61b 100644 --- a/public/script.js +++ b/public/script.js @@ -3051,10 +3051,13 @@ export async function getExtensionPromptByName(moduleName) { * @returns {number} Maximum depth of extension prompts */ export function getExtensionPromptMaxDepth() { + return MAX_INJECTION_DEPTH; + /* const prompts = Object.values(extension_prompts); const maxDepth = Math.max(...prompts.map(x => x.depth ?? 0)); // Clamp to 1 <= depth <= MAX_INJECTION_DEPTH return Math.max(Math.min(maxDepth, MAX_INJECTION_DEPTH), 1); + */ } /** From 36e3627705b748ea0eb5a161f114e4cacb2e19f0 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Mon, 14 Apr 2025 20:54:18 +0300 Subject: [PATCH 035/230] gpt-4.1 --- public/index.html | 8 ++++++++ public/scripts/extensions/caption/settings.html | 6 ++++++ public/scripts/openai.js | 4 ++++ src/endpoints/tokenizers.js | 2 +- 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index 45922217c..5623db848 100644 --- a/public/index.html +++ b/public/index.html @@ -2889,6 +2889,14 @@ + + + + + + + + diff --git a/public/scripts/extensions/caption/settings.html b/public/scripts/extensions/caption/settings.html index 923e83b9e..a34b25a37 100644 --- a/public/scripts/extensions/caption/settings.html +++ b/public/scripts/extensions/caption/settings.html @@ -47,6 +47,12 @@ + + + + + + diff --git a/public/scripts/openai.js b/public/scripts/openai.js index f328a5184..9aac63be1 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -4125,6 +4125,9 @@ function getMaxContextOpenAI(value) { if (oai_settings.max_context_unlocked) { return unlocked_max; } + else if (value.includes('gpt-4.1')) { + return max_1mil; + } else if (value.startsWith('o1') || value.startsWith('o3')) { return max_128k; } @@ -5145,6 +5148,7 @@ export function isImageInliningSupported() { 'c4ai-aya-vision-32b', 'grok-2-vision', 'grok-vision', + 'gpt-4.1', ]; switch (oai_settings.chat_completion_source) { diff --git a/src/endpoints/tokenizers.js b/src/endpoints/tokenizers.js index 989c78f32..c8ecb434c 100644 --- a/src/endpoints/tokenizers.js +++ b/src/endpoints/tokenizers.js @@ -411,7 +411,7 @@ export function getTokenizerModel(requestModel) { return 'gpt-4o'; } - if (requestModel.includes('gpt-4.5')) { + if (requestModel.includes('gpt-4.1') || requestModel.includes('gpt-4.5')) { return 'gpt-4o'; } From 5510e6da31e3798f0262333666bfb91a10fcd4ee Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Mon, 14 Apr 2025 22:36:56 +0300 Subject: [PATCH 036/230] Enable multi-swipe for xAI --- public/index.html | 2 +- public/scripts/openai.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/index.html b/public/index.html index 5623db848..736e7300d 100644 --- a/public/index.html +++ b/public/index.html @@ -646,7 +646,7 @@
-
+
Multiple swipes per generation
diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 9aac63be1..56d2e6ee0 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -1980,7 +1980,7 @@ async function sendOpenAIRequest(type, messages, signal) { const isContinue = type === 'continue'; const stream = oai_settings.stream_openai && !isQuiet && !isScale && !(isOAI && ['o1-2024-12-17', 'o1'].includes(oai_settings.openai_model)); const useLogprobs = !!power_user.request_token_probabilities; - const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom); + const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom || isXAI); // If we're using the window.ai extension, use that instead // Doesn't support logit bias yet From bf97686dfc99a1b01d10b043b794dd5626083f23 Mon Sep 17 00:00:00 2001 From: wrvsrx Date: Tue, 15 Apr 2025 20:20:35 +0800 Subject: [PATCH 037/230] Allow read-only installation Fix #3453. Thanks to #3499, #3500 and #3521, most of the obstacles to read-only installation have been resolved. This PR addresses the final piece, ensuring that SillyTavern no longer changes directories to `serverDirectory` and outputs files there. Instead, it outputs or copies necessary files to the directory where it is being run. Now, `serverDirectory` is read-only for SillyTavern (i.e., SillyTavern will not attempt to modify `serverDirectory`). Additionally, this PR sets the permissions for copied `default-user` files to be writable, so even if SillyTavern is installed as read-only, the copied `default-user` folder can still be modified. --- post-install.js | 48 +------------------------------- server.js | 11 +++----- src/endpoints/content-manager.js | 30 ++++++++++++++++++-- src/fetch-patch.js | 8 +++--- src/server-directory.js | 3 ++ src/transformers.js | 4 ++- src/users.js | 3 +- src/util.js | 18 ++++++------ webpack.config.js | 3 +- 9 files changed, 55 insertions(+), 73 deletions(-) create mode 100644 src/server-directory.js diff --git a/post-install.js b/post-install.js index ae01b7d1a..94f354e20 100644 --- a/post-install.js +++ b/post-install.js @@ -3,7 +3,6 @@ */ import fs from 'node:fs'; import path from 'node:path'; -import crypto from 'node:crypto'; import process from 'node:process'; import yaml from 'yaml'; import _ from 'lodash'; @@ -283,57 +282,12 @@ function createDefaultFiles() { } } -/** - * Returns the MD5 hash of the given data. - * @param {Buffer} data Input data - * @returns {string} MD5 hash of the input data - */ -function getMd5Hash(data) { - return crypto - .createHash('md5') - .update(new Uint8Array(data)) - .digest('hex'); -} - -/** - * Copies the WASM binaries from the sillytavern-transformers package to the dist folder. - */ -function copyWasmFiles() { - if (!fs.existsSync('./dist')) { - fs.mkdirSync('./dist'); - } - - const listDir = fs.readdirSync('./node_modules/sillytavern-transformers/dist'); - - for (const file of listDir) { - if (file.endsWith('.wasm')) { - const sourcePath = `./node_modules/sillytavern-transformers/dist/${file}`; - const targetPath = `./dist/${file}`; - - // Don't copy if the file already exists and is the same checksum - if (fs.existsSync(targetPath)) { - const sourceChecksum = getMd5Hash(fs.readFileSync(sourcePath)); - const targetChecksum = getMd5Hash(fs.readFileSync(targetPath)); - - if (sourceChecksum === targetChecksum) { - continue; - } - } - - fs.copyFileSync(sourcePath, targetPath); - console.log(`${file} successfully copied to ./dist/${file}`); - } - } -} - try { // 0. Convert config.conf to config.yaml convertConfig(); // 1. Create default config files createDefaultFiles(); - // 2. Copy transformers WASM binaries from node_modules - copyWasmFiles(); - // 3. Add missing config values + // 2. Add missing config values addMissingConfigValues(); } catch (error) { console.error(error); diff --git a/server.js b/server.js index a5e03cd41..76a9c6585 100644 --- a/server.js +++ b/server.js @@ -6,7 +6,6 @@ import util from 'node:util'; import net from 'node:net'; import dns from 'node:dns'; import process from 'node:process'; -import { fileURLToPath } from 'node:url'; import cors from 'cors'; import { csrfSync } from 'csrf-sync'; @@ -60,6 +59,7 @@ import { } from './src/util.js'; import { UPLOADS_DIRECTORY } from './src/constants.js'; import { ensureThumbnailCache } from './src/endpoints/thumbnails.js'; +import { serverDirectory } from './src/server-directory.js'; // Routers import { router as usersPublicRouter } from './src/endpoints/users-public.js'; @@ -74,10 +74,7 @@ util.inspect.defaultOptions.maxArrayLength = null; util.inspect.defaultOptions.maxStringLength = null; util.inspect.defaultOptions.depth = 4; -// Set a working directory for the server -const serverDirectory = import.meta.dirname ?? path.dirname(fileURLToPath(import.meta.url)); console.log(`Node version: ${process.version}. Running in ${process.env.NODE_ENV} environment. Server directory: ${serverDirectory}`); -process.chdir(serverDirectory); // Work around a node v20.0.0, v20.1.0, and v20.2.0 bug. The issue was fixed in v20.3.0. // https://github.com/nodejs/node/issues/47822#issuecomment-1564708870 @@ -211,7 +208,7 @@ app.get('/', getCacheBusterMiddleware(), (request, response) => { return response.redirect(redirectUrl); } - return response.sendFile('index.html', { root: path.join(process.cwd(), 'public') }); + return response.sendFile('index.html', { root: path.join(serverDirectory, 'public') }); }); // Callback endpoint for OAuth PKCE flows (e.g. OpenRouter) @@ -231,7 +228,7 @@ app.get('/login', loginPageMiddleware); // Host frontend assets const webpackMiddleware = getWebpackServeMiddleware(); app.use(webpackMiddleware); -app.use(express.static(process.cwd() + '/public', {})); +app.use(express.static(path.join(serverDirectory, 'public'), {})); // Public API app.use('/api/users', usersPublicRouter); @@ -375,7 +372,7 @@ async function postSetupTasks(result) { * Registers a not-found error response if a not-found error page exists. Should only be called after all other middlewares have been registered. */ function apply404Middleware() { - const notFoundWebpage = safeReadFileSync('./public/error/url-not-found.html') ?? ''; + const notFoundWebpage = safeReadFileSync(path.join(serverDirectory, 'public/error/url-not-found.html')) ?? ''; app.use((req, res) => { res.status(404).send(notFoundWebpage); }); diff --git a/src/endpoints/content-manager.js b/src/endpoints/content-manager.js index 6cebf53b7..293645bfb 100644 --- a/src/endpoints/content-manager.js +++ b/src/endpoints/content-manager.js @@ -1,6 +1,5 @@ import fs from 'node:fs'; import path from 'node:path'; -import process from 'node:process'; import { Buffer } from 'node:buffer'; import express from 'express'; @@ -10,9 +9,10 @@ import { sync as writeFileAtomicSync } from 'write-file-atomic'; import { getConfigValue, color } from '../util.js'; import { write } from '../character-card-parser.js'; +import { serverDirectory } from '../server-directory.js'; -const contentDirectory = path.join(process.cwd(), 'default/content'); -const scaffoldDirectory = path.join(process.cwd(), 'default/scaffold'); +const contentDirectory = path.join(serverDirectory, 'default/content'); +const scaffoldDirectory = path.join(serverDirectory, 'default/scaffold'); const contentIndexPath = path.join(contentDirectory, 'index.json'); const scaffoldIndexPath = path.join(scaffoldDirectory, 'index.json'); @@ -149,6 +149,30 @@ async function seedContentForUser(contentIndex, directories, forceCategories) { } fs.cpSync(contentPath, targetPath, { recursive: true, force: false }); + function setPermissionsSync(targetPath_) { + + function appendWritablePermission(filepath, stats) { + const currentMode = stats.mode; + const newMode = currentMode | 0o200; + if (newMode != currentMode) { + fs.chmodSync(filepath, newMode); + } + } + + const stats = fs.statSync(targetPath_); + + if (stats.isDirectory()) { + appendWritablePermission(targetPath_, stats); + const files = fs.readdirSync(targetPath_); + + files.forEach((file) => { + setPermissionsSync(path.join(targetPath_, file)); + }); + } else { + appendWritablePermission(targetPath_, stats); + } + } + setPermissionsSync(targetPath); console.info(`Content file ${contentItem.filename} copied to ${contentTarget}`); anyContentAdded = true; } diff --git a/src/fetch-patch.js b/src/fetch-patch.js index c17b7b85c..f6f5ded9e 100644 --- a/src/fetch-patch.js +++ b/src/fetch-patch.js @@ -2,6 +2,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import mime from 'mime-types'; +import { serverDirectory } from './server-directory.js'; const originalFetch = globalThis.fetch; @@ -67,10 +68,9 @@ globalThis.fetch = async (/** @type {string | URL | Request} */ request, /** @ty } const url = getRequestURL(request); const filePath = path.resolve(fileURLToPath(url)); - const cwd = path.resolve(process.cwd()) + path.sep; - const isUnderCwd = isPathUnderParent(cwd, filePath); - if (!isUnderCwd) { - throw new Error('Requested file path is outside of the current working directory.'); + const isUnderServerDirectory = isPathUnderParent(serverDirectory, filePath); + if (!isUnderServerDirectory) { + throw new Error('Requested file path is outside of the server directory.'); } const parsedPath = path.parse(filePath); if (!ALLOWED_EXTENSIONS.includes(parsedPath.ext)) { diff --git a/src/server-directory.js b/src/server-directory.js new file mode 100644 index 000000000..6c0c6e1da --- /dev/null +++ b/src/server-directory.js @@ -0,0 +1,3 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +export const serverDirectory = path.dirname(import.meta.dirname ?? path.dirname(fileURLToPath(import.meta.url))); diff --git a/src/transformers.js b/src/transformers.js index 2bd03f4a5..d0eb851a1 100644 --- a/src/transformers.js +++ b/src/transformers.js @@ -5,14 +5,16 @@ import { Buffer } from 'node:buffer'; import { pipeline, env, RawImage } from 'sillytavern-transformers'; import { getConfigValue } from './util.js'; +import { serverDirectory } from './server-directory.js'; configureTransformers(); function configureTransformers() { // Limit the number of threads to 1 to avoid issues on Android env.backends.onnx.wasm.numThreads = 1; + console.log(env.backends.onnx.wasm.wasmPaths); // Use WASM from a local folder to avoid CDN connections - env.backends.onnx.wasm.wasmPaths = path.join(process.cwd(), 'dist') + path.sep; + env.backends.onnx.wasm.wasmPaths = path.join(serverDirectory, 'node_modules', 'sillytavern-transformers', 'dist') + path.sep; } const tasks = { diff --git a/src/users.js b/src/users.js index b02c81c73..c1e631de5 100644 --- a/src/users.js +++ b/src/users.js @@ -18,6 +18,7 @@ import { USER_DIRECTORY_TEMPLATE, DEFAULT_USER, PUBLIC_DIRECTORIES, SETTINGS_FIL import { getConfigValue, color, delay, generateTimestamp } from './util.js'; import { readSecret, writeSecret } from './endpoints/secrets.js'; import { getContentOfType } from './endpoints/content-manager.js'; +import { serverDirectory } from './server-directory.js'; export const KEY_PREFIX = 'user:'; const AVATAR_PREFIX = 'avatar:'; @@ -905,7 +906,7 @@ export async function loginPageMiddleware(request, response) { console.error('Error during auto-login:', error); } - return response.sendFile('login.html', { root: path.join(process.cwd(), 'public') }); + return response.sendFile('login.html', { root: path.join(serverDirectory, 'public') }); } /** diff --git a/src/util.js b/src/util.js index ec94ceb1a..45cc3f1f7 100644 --- a/src/util.js +++ b/src/util.js @@ -17,6 +17,7 @@ import { default as simpleGit } from 'simple-git'; import chalk from 'chalk'; import { LOG_LEVELS } from './constants.js'; import bytes from 'bytes'; +import { serverDirectory } from './server-directory.js'; /** * Parsed config object. @@ -121,20 +122,19 @@ export async function getVersion() { try { const require = createRequire(import.meta.url); - const pkgJson = require(path.join(process.cwd(), './package.json')); + const pkgJson = require(path.join(serverDirectory, './package.json')); pkgVersion = pkgJson.version; if (commandExistsSync('git')) { - const git = simpleGit(); - const cwd = process.cwd(); - gitRevision = await git.cwd(cwd).revparse(['--short', 'HEAD']); - gitBranch = await git.cwd(cwd).revparse(['--abbrev-ref', 'HEAD']); - commitDate = await git.cwd(cwd).show(['-s', '--format=%ci', gitRevision]); + const git = simpleGit({ baseDir: serverDirectory }); + gitRevision = await git.revparse(['--short', 'HEAD']); + gitBranch = await git.revparse(['--abbrev-ref', 'HEAD']); + commitDate = await git.show(['-s', '--format=%ci', gitRevision]); - const trackingBranch = await git.cwd(cwd).revparse(['--abbrev-ref', '@{u}']); + const trackingBranch = await git.revparse(['--abbrev-ref', '@{u}']); // Might fail, but exception is caught. Just don't run anything relevant after in this block... - const localLatest = await git.cwd(cwd).revparse(['HEAD']); - const remoteLatest = await git.cwd(cwd).revparse([trackingBranch]); + const localLatest = await git.revparse(['HEAD']); + const remoteLatest = await git.revparse([trackingBranch]); isLatest = localLatest === remoteLatest; } } diff --git a/webpack.config.js b/webpack.config.js index 3579a3cae..1790de6dd 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,6 +1,7 @@ import process from 'node:process'; import path from 'node:path'; import isDocker from 'is-docker'; +import { serverDirectory } from './src/server-directory.js'; /** * Get the Webpack configuration for the public/lib.js file. @@ -40,7 +41,7 @@ export default function getPublicLibConfig(forceDist = false) { return { mode: 'production', - entry: './public/lib.js', + entry: path.join(serverDirectory, 'public/lib.js'), cache: { type: 'filesystem', cacheDirectory: cacheDirectory, From 5cf3198da13f158aed20f13ba9a0b0ed5b78b6ea Mon Sep 17 00:00:00 2001 From: pl752 Date: Wed, 16 Apr 2025 16:04:33 +0500 Subject: [PATCH 038/230] Added option to use secondary API URL in vector extension --- public/scripts/extensions/vectors/index.js | 22 +++++++++++++++---- .../scripts/extensions/vectors/settings.html | 10 +++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/public/scripts/extensions/vectors/index.js b/public/scripts/extensions/vectors/index.js index ed7df5c15..79f84ee35 100644 --- a/public/scripts/extensions/vectors/index.js +++ b/public/scripts/extensions/vectors/index.js @@ -55,6 +55,8 @@ const getBatchSize = () => ['transformers', 'palm', 'ollama'].includes(settings. const settings = { // For both source: 'transformers', + alt_endpoint_url: '', + use_alt_endpoint: false, include_wi: false, togetherai_model: 'togethercomputer/m2-bert-80M-32k-retrieval', openai_model: 'text-embedding-ada-002', @@ -109,6 +111,7 @@ const settings = { const moduleWorker = new ModuleWorkerWrapper(synchronizeChat); const webllmProvider = new WebLlmVectorProvider(); const cachedSummaries = new Map(); +const vector_api_requires_url = ['llamacpp', 'vllm', 'ollama', 'koboldcpp']; /** * Gets the Collection ID for a file embedded in the chat. @@ -777,14 +780,14 @@ function getVectorsRequestBody(args = {}) { break; case 'ollama': body.model = extension_settings.vectors.ollama_model; - body.apiUrl = textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]; + body.apiUrl = settings.use_alt_endpoint ? settings.alt_endpoint_url : textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]; body.keep = !!extension_settings.vectors.ollama_keep; break; case 'llamacpp': - body.apiUrl = textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP]; + body.apiUrl = settings.use_alt_endpoint ? settings.alt_endpoint_url : textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP]; break; case 'vllm': - body.apiUrl = textgenerationwebui_settings.server_urls[textgen_types.VLLM]; + body.apiUrl = settings.use_alt_endpoint ? settings.alt_endpoint_url : textgenerationwebui_settings.server_urls[textgen_types.VLLM]; body.model = extension_settings.vectors.vllm_model; break; case 'webllm': @@ -1087,6 +1090,7 @@ function toggleSettings() { $('#webllm_vectorsModel').toggle(settings.source === 'webllm'); $('#koboldcpp_vectorsModel').toggle(settings.source === 'koboldcpp'); $('#google_vectorsModel').toggle(settings.source === 'palm'); + $('#altEndpointUrl').toggle(vector_api_requires_url.includes(settings.source)); if (settings.source === 'webllm') { loadWebLlmModels(); } @@ -1165,7 +1169,7 @@ async function createKoboldCppEmbeddings(items) { headers: getRequestHeaders(), body: JSON.stringify({ items: items, - server: textgenerationwebui_settings.server_urls[textgen_types.KOBOLDCPP], + server: settings.use_alt_endpoint ? settings.alt_endpoint_url : textgenerationwebui_settings.server_urls[textgen_types.KOBOLDCPP], }), }); @@ -1467,6 +1471,16 @@ jQuery(async () => { saveSettingsDebounced(); toggleSettings(); }); + $('#altEndpointUrl_enabled').prop('checked', settings.use_alt_endpoint).on('input', () => { + settings.use_alt_endpoint = $('#altEndpointUrl_enabled').prop('checked'); + Object.assign(extension_settings.vectors, settings); + saveSettingsDebounced(); + }) + $('#altEndpoint_address').val(settings.alt_endpoint_url).on('change', () => { + settings.alt_endpoint_url = String($('#altEndpoint_address').val()); + Object.assign(extension_settings.vectors, settings); + saveSettingsDebounced(); + }) $('#api_key_nomicai').on('click', async () => { const popupText = 'NomicAI API Key:'; const key = await callGenericPopup(popupText, POPUP_TYPE.INPUT, '', { diff --git a/public/scripts/extensions/vectors/settings.html b/public/scripts/extensions/vectors/settings.html index fc01ebe71..4665278bc 100644 --- a/public/scripts/extensions/vectors/settings.html +++ b/public/scripts/extensions/vectors/settings.html @@ -25,6 +25,16 @@
+
+ + + +
-
-
-
-
-
- -
- Must be enabled for Thinking Budget to take effect. -
-
-
-
- Thinking Budget (tokens) -
-
-
- -
-
- -
-
-
-
-
-
-
+
+
diff --git a/public/scripts/openai.js b/public/scripts/openai.js index b22a29fba..1f768b82f 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -165,11 +165,6 @@ const textCompletionModels = [ 'code-search-ada-code-001', ]; -// One more models list to maintain, yay -const thinkingBudgetModels = [ - 'gemini-2.5-flash-preview-04-17', -]; - let biasCache = undefined; export let model_list = []; @@ -221,6 +216,15 @@ const openrouter_middleout_types = { OFF: 'off', }; +export const reasoning_effort_types = { + auto: 'auto', + low: 'low', + medium: 'medium', + high: 'high', + min: 'min', + max: 'max', +}; + const sensitiveFields = [ 'reverse_proxy', 'proxy_password', @@ -311,8 +315,6 @@ export const settingsToUpdate = { n: ['#n_openai', 'n', false], bypass_status_check: ['#openai_bypass_status_check', 'bypass_status_check', true], request_images: ['#openai_request_images', 'request_images', true], - enable_thinking: ['#enable_thinking', 'enable_thinking', true], - thinking_budget: ['#thinking_budget', 'thinking_budget', false], }; const default_settings = { @@ -389,11 +391,9 @@ const default_settings = { continue_postfix: continue_postfix_types.SPACE, custom_prompt_post_processing: custom_prompt_post_processing_types.NONE, show_thoughts: true, - reasoning_effort: 'medium', + reasoning_effort: reasoning_effort_types.medium, enable_web_search: false, request_images: false, - enable_thinking: true, - thinking_budget: 1000, seed: -1, n: 1, }; @@ -472,11 +472,9 @@ const oai_settings = { continue_postfix: continue_postfix_types.SPACE, custom_prompt_post_processing: custom_prompt_post_processing_types.NONE, show_thoughts: true, - reasoning_effort: 'medium', + reasoning_effort: reasoning_effort_types.medium, enable_web_search: false, request_images: false, - enable_thinking: true, - thinking_budget: 1000, seed: -1, n: 1, }; @@ -1948,6 +1946,35 @@ async function sendAltScaleRequest(messages, logit_bias, signal, type) { return data.output; } +function getReasoningEffort() { + // Do not set the field. Let the model decide. + if (oai_settings.reasoning_effort === reasoning_effort_types.auto) { + return undefined; + } + + // These sources require effort as a string + if (oai_settings.reasoning_effort === reasoning_effort_types.min) { + switch (oai_settings.chat_completion_source) { + case chat_completion_sources.OPENAI: + case chat_completion_sources.CUSTOM: + case chat_completion_sources.XAI: + return reasoning_effort_types.low; + } + } + + // Same here, but max effort + if (oai_settings.reasoning_effort === reasoning_effort_types.max) { + switch (oai_settings.chat_completion_source) { + case chat_completion_sources.OPENAI: + case chat_completion_sources.CUSTOM: + case chat_completion_sources.XAI: + return reasoning_effort_types.high; + } + } + + return oai_settings.reasoning_effort; +} + /** * Send a chat completion request to backend * @param {string} type (impersonate, quiet, continue, etc) @@ -2034,17 +2061,12 @@ async function sendOpenAIRequest(type, messages, signal) { 'char_name': name2, 'group_names': getGroupNames(), 'include_reasoning': Boolean(oai_settings.show_thoughts), - 'reasoning_effort': String(oai_settings.reasoning_effort), + 'reasoning_effort': getReasoningEffort(), 'enable_web_search': Boolean(oai_settings.enable_web_search), 'request_images': Boolean(oai_settings.request_images), 'custom_prompt_post_processing': oai_settings.custom_prompt_post_processing, }; - if (thinkingBudgetModels.includes(model)) { - generate_data['enable_thinking'] = oai_settings.enable_thinking; - generate_data['thinking_budget'] = oai_settings.thinking_budget; - } - if (!canMultiSwipe && ToolManager.canPerformToolCalls(type)) { await ToolManager.registerFunctionToolsOpenAI(generate_data); } @@ -3300,8 +3322,6 @@ function loadOpenAISettings(data, settings) { oai_settings.show_thoughts = settings.show_thoughts ?? default_settings.show_thoughts; oai_settings.reasoning_effort = settings.reasoning_effort ?? default_settings.reasoning_effort; oai_settings.enable_web_search = settings.enable_web_search ?? default_settings.enable_web_search; - oai_settings.enable_thinking = settings.enable_thinking ?? default_settings.enable_thinking; - oai_settings.thinking_budget = settings.thinking_budget ?? default_settings.thinking_budget; oai_settings.request_images = settings.request_images ?? default_settings.request_images; oai_settings.seed = settings.seed ?? default_settings.seed; oai_settings.n = settings.n ?? default_settings.n; @@ -3437,10 +3457,6 @@ function loadOpenAISettings(data, settings) { $('#openai_reasoning_effort').val(oai_settings.reasoning_effort); $(`#openai_reasoning_effort option[value="${oai_settings.reasoning_effort}"]`).prop('selected', true); - $('#enable_thinking').prop('checked', oai_settings.enable_thinking); - $('#thinking_budget').val(oai_settings.thinking_budget); - $('#thinking_budget_counter').val(oai_settings.thinking_budget); - if (settings.reverse_proxy !== undefined) oai_settings.reverse_proxy = settings.reverse_proxy; $('#openai_reverse_proxy').val(oai_settings.reverse_proxy); @@ -3471,7 +3487,6 @@ function loadOpenAISettings(data, settings) { setNamesBehaviorControls(); setContinuePostfixControls(); - updateThinkingBudgetUI(); if (oai_settings.custom_prompt_post_processing === custom_prompt_post_processing_types.CLAUDE) { oai_settings.custom_prompt_post_processing = custom_prompt_post_processing_types.MERGE; @@ -3529,14 +3544,6 @@ function setContinuePostfixControls() { $('#continue_postfix_display').text(checkedItemText); } -/** - * Updates the visibility and state of the Thinking Budget controls. - */ -function updateThinkingBudgetUI() { - const modelSupportsControl = thinkingBudgetModels.includes(getChatCompletionModel()); - $('#thinking_budget_controls').toggle(modelSupportsControl); -} - async function getStatusOpen() { if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI) { let status; @@ -3717,8 +3724,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) { reasoning_effort: settings.reasoning_effort, enable_web_search: settings.enable_web_search, request_images: settings.request_images, - enable_thinking: settings.enable_thinking, - thinking_budget: settings.thinking_budget, seed: settings.seed, n: settings.n, }; @@ -4749,7 +4754,6 @@ async function onModelChange() { $('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max'))); - updateThinkingBudgetUI(); saveSettingsDebounced(); eventSource.emit(event_types.CHATCOMPLETION_MODEL_CHANGED, value); } @@ -5568,7 +5572,6 @@ export function initOpenAI() { $('#chat_completion_source').on('change', function () { oai_settings.chat_completion_source = String($(this).find(':selected').val()); toggleChatCompletionForms(); - updateThinkingBudgetUI(); saveSettingsDebounced(); reconnectOpenAi(); forceCharacterEditorTokenize(); @@ -5754,29 +5757,6 @@ export function initOpenAI() { saveSettingsDebounced(); }); - $('#enable_thinking').on('input', function () { - oai_settings.enable_thinking = !!$(this).prop('checked'); - updateThinkingBudgetUI(); - saveSettingsDebounced(); - }); - - $('#thinking_budget').on('input', function () { - oai_settings.thinking_budget = Number($(this).val()); - $('#thinking_budget_counter').val(oai_settings.thinking_budget); - saveSettingsDebounced(); - }); - - $('#thinking_budget_counter').on('input', function () { - let value = Number($(this).val()); - const min = Number($('#thinking_budget').attr('min')); - const max = Number($('#thinking_budget').attr('max')); - value = Math.max(min, Math.min(max, value)); - $(this).val(value); - oai_settings.thinking_budget = value; - $('#thinking_budget').val(value); - saveSettingsDebounced(); - }); - $('#openai_enable_web_search').on('input', function () { oai_settings.enable_web_search = !!$(this).prop('checked'); calculateOpenRouterCost(); diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index 167dfbcdb..3ecf45f36 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -28,7 +28,8 @@ import { cachingAtDepthForOpenRouterClaude, cachingAtDepthForClaude, getPromptNames, - calculateBudgetTokens, + calculateClaudeBudgetTokens, + calculateGoogleBudgetTokens, } from '../../prompt-converters.js'; import { readSecret, SECRET_KEYS } from '../secrets.js'; @@ -202,7 +203,7 @@ async function sendClaudeRequest(request, response) { // No prefill when thinking voidPrefill = true; const reasoningEffort = request.body.reasoning_effort; - const budgetTokens = calculateBudgetTokens(requestBody.max_tokens, reasoningEffort, requestBody.stream); + const budgetTokens = calculateClaudeBudgetTokens(requestBody.max_tokens, reasoningEffort, requestBody.stream); const minThinkTokens = 1024; if (requestBody.max_tokens <= minThinkTokens) { const newValue = requestBody.max_tokens + minThinkTokens; @@ -340,6 +341,7 @@ async function sendMakerSuiteRequest(request, response) { const stream = Boolean(request.body.stream); const enableWebSearch = Boolean(request.body.enable_web_search); const requestImages = Boolean(request.body.request_images); + const reasoningEffort = String(request.body.reasoning_effort); const isThinking = model.includes('thinking'); const isGemma = model.includes('gemma'); @@ -412,11 +414,15 @@ async function sendMakerSuiteRequest(request, response) { tools.push({ function_declarations: functionDeclarations }); } - if ('enable_thinking' in request.body && 'thinking_budget' in request.body) { - const thinkingEnabled = Boolean(request.body.enable_thinking); - const thinkingBudget = Number(request.body.thinking_budget); + // One more models list to maintain, yay + const thinkingBudgetModels = [ + 'gemini-2.5-flash-preview-04-17', + ]; - if (thinkingEnabled) { + if (thinkingBudgetModels.includes(model)) { + const thinkingBudget = calculateGoogleBudgetTokens(generationConfig.maxOutputTokens, reasoningEffort); + + if (Number.isInteger(thinkingBudget)) { generationConfig.thinkingConfig = { thinkingBudget: thinkingBudget }; } } diff --git a/src/prompt-converters.js b/src/prompt-converters.js index 8ea3ab896..266047b8d 100644 --- a/src/prompt-converters.js +++ b/src/prompt-converters.js @@ -3,6 +3,15 @@ import { getConfigValue, tryParse } from './util.js'; const PROMPT_PLACEHOLDER = getConfigValue('promptPlaceholder', 'Let\'s get started.'); +const REASONING_EFFORT = { + auto: 'auto', + low: 'low', + medium: 'medium', + high: 'high', + min: 'min', + max: 'max', +}; + /** * @typedef {object} PromptNames * @property {string} charName Character name @@ -944,25 +953,35 @@ export function cachingAtDepthForOpenRouterClaude(messages, cachingAtDepth) { } /** - * Calculate the budget tokens for a given reasoning effort. + * Calculate the Claude budget tokens for a given reasoning effort. * @param {number} maxTokens Maximum tokens * @param {string} reasoningEffort Reasoning effort * @param {boolean} stream If streaming is enabled * @returns {number} Budget tokens */ -export function calculateBudgetTokens(maxTokens, reasoningEffort, stream) { +export function calculateClaudeBudgetTokens(maxTokens, reasoningEffort, stream) { let budgetTokens = 0; switch (reasoningEffort) { - case 'low': + // Claude doesn't have a default budget value. Use same as min. + case REASONING_EFFORT.auto: + budgetTokens = 1024; + break; + case REASONING_EFFORT.min: + budgetTokens = 1024; + break; + case REASONING_EFFORT.low: budgetTokens = Math.floor(maxTokens * 0.1); break; - case 'medium': + case REASONING_EFFORT.medium: budgetTokens = Math.floor(maxTokens * 0.25); break; - case 'high': + case REASONING_EFFORT.high: budgetTokens = Math.floor(maxTokens * 0.5); break; + case REASONING_EFFORT.max: + budgetTokens = maxTokens; + break; } budgetTokens = Math.max(budgetTokens, 1024); @@ -973,3 +992,37 @@ export function calculateBudgetTokens(maxTokens, reasoningEffort, stream) { return budgetTokens; } + +/** + * Calculate the Google budget tokens for a given reasoning effort. + * @param {number} maxTokens Maximum tokens + * @param {string} reasoningEffort Reasoning effort + * @returns {number?} Budget tokens + */ +export function calculateGoogleBudgetTokens(maxTokens, reasoningEffort) { + let budgetTokens = 0; + + switch (reasoningEffort) { + case REASONING_EFFORT.auto: + return null; + case REASONING_EFFORT.min: + budgetTokens = 0; + break; + case REASONING_EFFORT.low: + budgetTokens = Math.floor(maxTokens * 0.1); + break; + case REASONING_EFFORT.medium: + budgetTokens = Math.floor(maxTokens * 0.25); + break; + case REASONING_EFFORT.high: + budgetTokens = Math.floor(maxTokens * 0.5); + break; + case REASONING_EFFORT.max: + budgetTokens = maxTokens; + break; + } + + budgetTokens = Math.min(budgetTokens, 24576); + + return budgetTokens; +} From 5c8b8f4b9876d227296ae65d5a9e55256528b90a Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 23 Apr 2025 00:44:14 +0300 Subject: [PATCH 101/230] Refactor getReasoningEffort --- public/scripts/openai.js | 41 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 1f768b82f..6ffe84138 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -1947,32 +1947,27 @@ async function sendAltScaleRequest(messages, logit_bias, signal, type) { } function getReasoningEffort() { - // Do not set the field. Let the model decide. - if (oai_settings.reasoning_effort === reasoning_effort_types.auto) { - return undefined; + // These sources expect the effort as string. + const reasoningEffortSources = [ + chat_completion_sources.OPENAI, + chat_completion_sources.CUSTOM, + chat_completion_sources.XAI, + ]; + + if (!reasoningEffortSources.includes(oai_settings.chat_completion_source)) { + return oai_settings.reasoning_effort; } - // These sources require effort as a string - if (oai_settings.reasoning_effort === reasoning_effort_types.min) { - switch (oai_settings.chat_completion_source) { - case chat_completion_sources.OPENAI: - case chat_completion_sources.CUSTOM: - case chat_completion_sources.XAI: - return reasoning_effort_types.low; - } + switch (oai_settings.reasoning_effort) { + case reasoning_effort_types.auto: + return undefined; + case reasoning_effort_types.min: + return reasoning_effort_types.low; + case reasoning_effort_types.max: + return reasoning_effort_types.high; + default: + return oai_settings.reasoning_effort; } - - // Same here, but max effort - if (oai_settings.reasoning_effort === reasoning_effort_types.max) { - switch (oai_settings.chat_completion_source) { - case chat_completion_sources.OPENAI: - case chat_completion_sources.CUSTOM: - case chat_completion_sources.XAI: - return reasoning_effort_types.high; - } - } - - return oai_settings.reasoning_effort; } /** From 266fa5cbf83c7bbcde47c733e5fb27c398f01404 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 23 Apr 2025 00:45:49 +0300 Subject: [PATCH 102/230] Make auto (undefined) actually work --- src/endpoints/backends/chat-completions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index 3ecf45f36..934c9d52e 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -883,7 +883,7 @@ async function sendXaiRequest(request, response) { bodyParams['stop'] = request.body.stop; } - if (['grok-3-mini-beta', 'grok-3-mini-fast-beta'].includes(request.body.model)) { + if (request.body.reasoning_effort && ['grok-3-mini-beta', 'grok-3-mini-fast-beta'].includes(request.body.model)) { bodyParams['reasoning_effort'] = request.body.reasoning_effort === 'high' ? 'high' : 'low'; } @@ -1273,7 +1273,7 @@ router.post('/generate', function (request, response) { } // A few of OpenAIs reasoning models support reasoning effort - if ([CHAT_COMPLETION_SOURCES.CUSTOM, CHAT_COMPLETION_SOURCES.OPENAI].includes(request.body.chat_completion_source)) { + if (request.body.reasoning_effort && [CHAT_COMPLETION_SOURCES.CUSTOM, CHAT_COMPLETION_SOURCES.OPENAI].includes(request.body.chat_completion_source)) { if (['o1', 'o3-mini', 'o3-mini-2025-01-31', 'o4-mini', 'o4-mini-2025-04-16', 'o3', 'o3-2025-04-16'].includes(request.body.model)) { bodyParams['reasoning_effort'] = request.body.reasoning_effort; } From e43023fde78006ad7a235fed188a41cc0dc670aa Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 23 Apr 2025 00:54:03 +0300 Subject: [PATCH 103/230] Cut option labels --- public/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/index.html b/public/index.html index 58fefa004..bcb376473 100644 --- a/public/index.html +++ b/public/index.html @@ -2055,12 +2055,12 @@
From f61d600c05e5c562fe1cfe884432323f89550269 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 23 Apr 2025 00:59:12 +0300 Subject: [PATCH 104/230] ok buddy claude --- src/prompt-converters.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/prompt-converters.js b/src/prompt-converters.js index 266047b8d..69292b7a8 100644 --- a/src/prompt-converters.js +++ b/src/prompt-converters.js @@ -365,7 +365,7 @@ export function convertCohereMessages(messages, names) { * @param {string} model Model name * @param {boolean} useSysPrompt Use system prompt * @param {PromptNames} names Prompt names - * @returns {{contents: *[], system_instruction: {parts: {text: string}}}} Prompt for Google MakerSuite models + * @returns {{contents: *[], system_instruction: {parts: {text: string}[]}}} Prompt for Google MakerSuite models */ export function convertGooglePrompt(messages, model, useSysPrompt, names) { const visionSupportedModels = [ @@ -980,7 +980,7 @@ export function calculateClaudeBudgetTokens(maxTokens, reasoningEffort, stream) budgetTokens = Math.floor(maxTokens * 0.5); break; case REASONING_EFFORT.max: - budgetTokens = maxTokens; + budgetTokens = Math.floor(maxTokens * 0.95); break; } From f81bbbea08aca994f758b116956ac51e573c03af Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 23 Apr 2025 01:02:28 +0300 Subject: [PATCH 105/230] Fix effort blurb title --- public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index bcb376473..b90605a0d 100644 --- a/public/index.html +++ b/public/index.html @@ -2052,7 +2052,7 @@
From 3e8f9e268027a87b912783cd01cf9531121cf71b Mon Sep 17 00:00:00 2001 From: equal-l2 Date: Thu, 24 Apr 2025 00:02:43 +0900 Subject: [PATCH 114/230] Fix for eslint --- public/scripts/openai.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 9f1dd771c..81312affd 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -5132,11 +5132,11 @@ export function isImageInliningSupported() { switch (oai_settings.chat_completion_source) { case chat_completion_sources.OPENAI: - return visionSupportedModels.some(model => + return visionSupportedModels.some(model => oai_settings.openai_model.includes(model) && !oai_settings.openai_model.includes('gpt-4-turbo-preview') && !oai_settings.openai_model.includes('o1-mini') - && !oai_settings.openai_model.includes('o3-mini') + && !oai_settings.openai_model.includes('o3-mini'), ); case chat_completion_sources.MAKERSUITE: return visionSupportedModels.some(model => oai_settings.google_model.includes(model)); From cf44ac8c1f2537df3cfb40a214b307c58455c6ab Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 23 Apr 2025 20:04:00 +0300 Subject: [PATCH 115/230] Don't add sys instruction if empty --- src/endpoints/backends/chat-completions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index 934c9d52e..c582a099e 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -433,7 +433,7 @@ async function sendMakerSuiteRequest(request, response) { generationConfig: generationConfig, }; - if (useSystemPrompt) { + if (useSystemPrompt && Array.isArray(prompt.system_instruction) && prompt.system_instruction.length) { body.systemInstruction = prompt.system_instruction; } From 50cdaadba08c8b032f79f6908164fb31737c836e Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 23 Apr 2025 20:05:28 +0300 Subject: [PATCH 116/230] Only verify parts length --- src/endpoints/backends/chat-completions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index c582a099e..ad9557e11 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -433,7 +433,7 @@ async function sendMakerSuiteRequest(request, response) { generationConfig: generationConfig, }; - if (useSystemPrompt && Array.isArray(prompt.system_instruction) && prompt.system_instruction.length) { + if (useSystemPrompt && Array.isArray(prompt.system_instruction.parts) && prompt.system_instruction.parts.length) { body.systemInstruction = prompt.system_instruction; } From d97aa0a270c1282490bfc2b8bf10add67969d1c8 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 23 Apr 2025 20:46:36 +0300 Subject: [PATCH 117/230] CONFIG_FILE => CONFIG_PATH --- src/util.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/util.js b/src/util.js index c12091910..66dd561f4 100644 --- a/src/util.js +++ b/src/util.js @@ -23,7 +23,7 @@ import { serverDirectory } from './server-directory.js'; * Parsed config object. */ let CACHED_CONFIG = null; -let CONFIG_FILE = null; +let CONFIG_PATH = null; /** * Converts a configuration key to an environment variable key. @@ -38,10 +38,10 @@ export const keyToEnv = (key) => 'SILLYTAVERN_' + String(key).toUpperCase().repl * @param {string} configFilePath Path to the config file */ export function setConfigFilePath(configFilePath) { - if (CONFIG_FILE !== null) { + if (CONFIG_PATH !== null) { console.error(color.red('Config file path already set. Please restart the server to change the config file path.')); } - CONFIG_FILE = path.resolve(configFilePath); + CONFIG_PATH = path.resolve(configFilePath); } /** @@ -49,7 +49,7 @@ export function setConfigFilePath(configFilePath) { * @returns {object} Config object */ export function getConfig() { - if (CONFIG_FILE === null) { + if (CONFIG_PATH === null) { console.trace(); console.error(color.red('No config file path set. Please set the config file path using setConfigFilePath().')); process.exit(1); @@ -57,14 +57,14 @@ export function getConfig() { if (CACHED_CONFIG) { return CACHED_CONFIG; } - if (!fs.existsSync(CONFIG_FILE)) { + if (!fs.existsSync(CONFIG_PATH)) { console.error(color.red('No config file found. Please create a config.yaml file. The default config file can be found in the /default folder.')); console.error(color.red('The program will now exit.')); process.exit(1); } try { - const config = yaml.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); + const config = yaml.parse(fs.readFileSync(CONFIG_PATH, 'utf8')); CACHED_CONFIG = config; return config; } catch (error) { From 01c6544e22a3584c78bf9aa72bcee415bb2d910e Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 23 Apr 2025 20:50:46 +0300 Subject: [PATCH 118/230] Move server-main to /src --- server.js | 382 +------------------------------------------- src/server-main.js | 386 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 388 insertions(+), 380 deletions(-) create mode 100644 src/server-main.js diff --git a/server.js b/server.js index e43c0baf0..1a40b4fcc 100644 --- a/server.js +++ b/server.js @@ -1,393 +1,15 @@ #!/usr/bin/env node - -// native node modules -import path from 'node:path'; -import util from 'node:util'; -import net from 'node:net'; -import dns from 'node:dns'; -import process from 'node:process'; - -import cors from 'cors'; -import { csrfSync } from 'csrf-sync'; -import express from 'express'; -import compression from 'compression'; -import cookieSession from 'cookie-session'; -import multer from 'multer'; -import responseTime from 'response-time'; -import helmet from 'helmet'; -import bodyParser from 'body-parser'; -import open from 'open'; - -// local library imports -import './src/fetch-patch.js'; import { CommandLineParser } from './src/command-line.js'; import { serverDirectory } from './src/server-directory.js'; -console.log(`Node version: ${process.version}. Running in ${process.env.NODE_ENV} environment. Server directory: ${serverDirectory}`); - -// Work around a node v20.0.0, v20.1.0, and v20.2.0 bug. The issue was fixed in v20.3.0. -// https://github.com/nodejs/node/issues/47822#issuecomment-1564708870 -// Safe to remove once support for Node v20 is dropped. -if (process.versions && process.versions.node && process.versions.node.match(/20\.[0-2]\.0/)) { - // @ts-ignore - if (net.setDefaultAutoSelectFamily) net.setDefaultAutoSelectFamily(false); -} - // config.yaml will be set when parsing command line arguments const cliArgs = new CommandLineParser().parse(process.argv); globalThis.DATA_ROOT = cliArgs.dataRoot; globalThis.COMMAND_LINE_ARGS = cliArgs; process.chdir(serverDirectory); -const { serverEvents, EVENT_NAMES } = await import('./src/server-events.js'); -const { loadPlugins } = await import('./src/plugin-loader.js'); -const { - initUserStorage, - getCookieSecret, - getCookieSessionName, - ensurePublicDirectoriesExist, - getUserDirectoriesList, - migrateSystemPrompts, - migrateUserData, - requireLoginMiddleware, - setUserDataMiddleware, - shouldRedirectToLogin, - cleanUploads, - getSessionCookieAge, - verifySecuritySettings, - loginPageMiddleware, -} = await import('./src/users.js'); - -const { default: getWebpackServeMiddleware } = await import('./src/middleware/webpack-serve.js'); -const { default: basicAuthMiddleware } = await import('./src/middleware/basicAuth.js'); -const { default: getWhitelistMiddleware } = await import('./src/middleware/whitelist.js'); -const { default: accessLoggerMiddleware, getAccessLogPath, migrateAccessLog } = await import('./src/middleware/accessLogWriter.js'); -const { default: multerMonkeyPatch } = await import('./src/middleware/multerMonkeyPatch.js'); -const { default: initRequestProxy } = await import('./src/request-proxy.js'); -const { default: getCacheBusterMiddleware } = await import('./src/middleware/cacheBuster.js'); -const { default: corsProxyMiddleware } = await import('./src/middleware/corsProxy.js'); -const { - getVersion, - color, - removeColorFormatting, - getSeparator, - safeReadFileSync, - setupLogLevel, - setWindowTitle, -} = await import('./src/util.js'); -const { UPLOADS_DIRECTORY } = await import('./src/constants.js'); -const { ensureThumbnailCache } = await import('./src/endpoints/thumbnails.js'); - -// Routers -const { router : usersPublicRouter } = await import('./src/endpoints/users-public.js'); -const { init : statsInit, onExit : statsOnExit } = await import('./src/endpoints/stats.js'); -const { checkForNewContent } = await import('./src/endpoints/content-manager.js'); -const { init : settingsInit } = await import('./src/endpoints/settings.js'); -const { redirectDeprecatedEndpoints, ServerStartup, setupPrivateEndpoints } = await import('./src/server-startup.js'); -const { diskCache } = await import('./src/endpoints/characters.js'); - -// Unrestrict console logs display limit -util.inspect.defaultOptions.maxArrayLength = null; -util.inspect.defaultOptions.maxStringLength = null; -util.inspect.defaultOptions.depth = 4; - -if (!cliArgs.enableIPv6 && !cliArgs.enableIPv4) { - console.error('error: You can\'t disable all internet protocols: at least IPv6 or IPv4 must be enabled.'); - process.exit(1); -} - try { - if (cliArgs.dnsPreferIPv6) { - dns.setDefaultResultOrder('ipv6first'); - console.log('Preferring IPv6 for DNS resolution'); - } else { - dns.setDefaultResultOrder('ipv4first'); - console.log('Preferring IPv4 for DNS resolution'); - } + await import('./src/server-main.js'); } catch (error) { - console.warn('Failed to set DNS resolution order. Possibly unsupported in this Node version.'); + console.error('A critical error has occurred while starting the server:', error); } - -const app = express(); -app.use(helmet({ - contentSecurityPolicy: false, -})); -app.use(compression()); -app.use(responseTime()); - -app.use(bodyParser.json({ limit: '200mb' })); -app.use(bodyParser.urlencoded({ extended: true, limit: '200mb' })); - -// CORS Settings // -const CORS = cors({ - origin: 'null', - methods: ['OPTIONS'], -}); - -app.use(CORS); - -if (cliArgs.listen && cliArgs.basicAuthMode) { - app.use(basicAuthMiddleware); -} - -if (cliArgs.whitelistMode) { - const whitelistMiddleware = await getWhitelistMiddleware(); - app.use(whitelistMiddleware); -} - -if (cliArgs.listen) { - app.use(accessLoggerMiddleware()); -} - -if (cliArgs.enableCorsProxy) { - app.use('/proxy/:url(*)', corsProxyMiddleware); -} else { - app.use('/proxy/:url(*)', async (_, res) => { - const message = 'CORS proxy is disabled. Enable it in config.yaml or use the --corsProxy flag.'; - console.log(message); - res.status(404).send(message); - }); -} - -app.use(cookieSession({ - name: getCookieSessionName(), - sameSite: 'lax', - httpOnly: true, - maxAge: getSessionCookieAge(), - secret: getCookieSecret(globalThis.DATA_ROOT), -})); - -app.use(setUserDataMiddleware); - -// CSRF Protection // -if (!cliArgs.disableCsrf) { - const csrfSyncProtection = csrfSync({ - getTokenFromState: (req) => { - if (!req.session) { - console.error('(CSRF error) getTokenFromState: Session object not initialized'); - return; - } - return req.session.csrfToken; - }, - getTokenFromRequest: (req) => { - return req.headers['x-csrf-token']?.toString(); - }, - storeTokenInState: (req, token) => { - if (!req.session) { - console.error('(CSRF error) storeTokenInState: Session object not initialized'); - return; - } - req.session.csrfToken = token; - }, - size: 32, - }); - - app.get('/csrf-token', (req, res) => { - res.json({ - 'token': csrfSyncProtection.generateToken(req), - }); - }); - - // Customize the error message - csrfSyncProtection.invalidCsrfTokenError.message = color.red('Invalid CSRF token. Please refresh the page and try again.'); - csrfSyncProtection.invalidCsrfTokenError.stack = undefined; - - app.use(csrfSyncProtection.csrfSynchronisedProtection); -} else { - console.warn('\nCSRF protection is disabled. This will make your server vulnerable to CSRF attacks.\n'); - app.get('/csrf-token', (req, res) => { - res.json({ - 'token': 'disabled', - }); - }); -} - -// Static files -// Host index page -app.get('/', getCacheBusterMiddleware(), (request, response) => { - if (shouldRedirectToLogin(request)) { - const query = request.url.split('?')[1]; - const redirectUrl = query ? `/login?${query}` : '/login'; - return response.redirect(redirectUrl); - } - - return response.sendFile('index.html', { root: path.join(serverDirectory, 'public') }); -}); - -// Callback endpoint for OAuth PKCE flows (e.g. OpenRouter) -app.get('/callback/:source?', (request, response) => { - const source = request.params.source; - const query = request.url.split('?')[1]; - const searchParams = new URLSearchParams(); - source && searchParams.set('source', source); - query && searchParams.set('query', query); - const path = `/?${searchParams.toString()}`; - return response.redirect(307, path); -}); - -// Host login page -app.get('/login', loginPageMiddleware); - -// Host frontend assets -const webpackMiddleware = getWebpackServeMiddleware(); -app.use(webpackMiddleware); -app.use(express.static(path.join(serverDirectory, 'public'), {})); - -// Public API -app.use('/api/users', usersPublicRouter); - -// Everything below this line requires authentication -app.use(requireLoginMiddleware); -app.get('/api/ping', (request, response) => { - if (request.query.extend && request.session) { - request.session.touch = Date.now(); - } - - response.sendStatus(204); -}); - -// File uploads -const uploadsPath = path.join(cliArgs.dataRoot, UPLOADS_DIRECTORY); -app.use(multer({ dest: uploadsPath, limits: { fieldSize: 10 * 1024 * 1024 } }).single('avatar')); -app.use(multerMonkeyPatch); - -app.get('/version', async function (_, response) { - const data = await getVersion(); - response.send(data); -}); - -redirectDeprecatedEndpoints(app); -setupPrivateEndpoints(app); - -/** - * Tasks that need to be run before the server starts listening. - * @returns {Promise} - */ -async function preSetupTasks() { - const version = await getVersion(); - - // Print formatted header - console.log(); - console.log(`SillyTavern ${version.pkgVersion}`); - if (version.gitBranch) { - console.log(`Running '${version.gitBranch}' (${version.gitRevision}) - ${version.commitDate}`); - if (!version.isLatest && ['staging', 'release'].includes(version.gitBranch)) { - console.log('INFO: Currently not on the latest commit.'); - console.log(' Run \'git pull\' to update. If you have any merge conflicts, run \'git reset --hard\' and \'git pull\' to reset your branch.'); - } - } - console.log(); - - const directories = await getUserDirectoriesList(); - await checkForNewContent(directories); - await ensureThumbnailCache(directories); - await diskCache.verify(directories); - cleanUploads(); - migrateAccessLog(); - - await settingsInit(); - await statsInit(); - - const pluginsDirectory = path.join(serverDirectory, 'plugins'); - const cleanupPlugins = await loadPlugins(app, pluginsDirectory); - const consoleTitle = process.title; - - let isExiting = false; - const exitProcess = async () => { - if (isExiting) return; - isExiting = true; - await statsOnExit(); - if (typeof cleanupPlugins === 'function') { - await cleanupPlugins(); - } - diskCache.dispose(); - setWindowTitle(consoleTitle); - process.exit(); - }; - - // Set up event listeners for a graceful shutdown - process.on('SIGINT', exitProcess); - process.on('SIGTERM', exitProcess); - process.on('uncaughtException', (err) => { - console.error('Uncaught exception:', err); - exitProcess(); - }); - - // Add request proxy. - initRequestProxy({ enabled: cliArgs.requestProxyEnabled, url: cliArgs.requestProxyUrl, bypass: cliArgs.requestProxyBypass }); - - // Wait for frontend libs to compile - await webpackMiddleware.runWebpackCompiler(); -} - -/** - * Tasks that need to be run after the server starts listening. - * @param {import('./src/server-startup.js').ServerStartupResult} result The result of the server startup - * @returns {Promise} - */ -async function postSetupTasks(result) { - const autorunHostname = await cliArgs.getAutorunHostname(result); - const autorunUrl = cliArgs.getAutorunUrl(autorunHostname); - - if (cliArgs.autorun) { - try { - console.log('Launching in a browser...'); - await open(autorunUrl.toString()); - } catch (error) { - console.error('Failed to launch the browser. Open the URL manually.'); - } - } - - setWindowTitle('SillyTavern WebServer'); - - let logListen = 'SillyTavern is listening on'; - - if (result.useIPv6 && !result.v6Failed) { - logListen += color.green( - ' IPv6: ' + cliArgs.getIPv6ListenUrl().host, - ); - } - - if (result.useIPv4 && !result.v4Failed) { - logListen += color.green( - ' IPv4: ' + cliArgs.getIPv4ListenUrl().host, - ); - } - - const goToLog = 'Go to: ' + color.blue(autorunUrl) + ' to open SillyTavern'; - const plainGoToLog = removeColorFormatting(goToLog); - - console.log(logListen); - if (cliArgs.listen) { - console.log(); - console.log('To limit connections to internal localhost only ([::1] or 127.0.0.1), change the setting in config.yaml to "listen: false".'); - console.log('Check the "access.log" file in the data directory to inspect incoming connections:', color.green(getAccessLogPath())); - } - console.log('\n' + getSeparator(plainGoToLog.length) + '\n'); - console.log(goToLog); - console.log('\n' + getSeparator(plainGoToLog.length) + '\n'); - - setupLogLevel(); - serverEvents.emit(EVENT_NAMES.SERVER_STARTED, { url: autorunUrl }); -} - -/** - * Registers a not-found error response if a not-found error page exists. Should only be called after all other middlewares have been registered. - */ -function apply404Middleware() { - const notFoundWebpage = safeReadFileSync(path.join(serverDirectory, 'public/error/url-not-found.html')) ?? ''; - app.use((req, res) => { - res.status(404).send(notFoundWebpage); - }); -} - -// User storage module needs to be initialized before starting the server -initUserStorage(globalThis.DATA_ROOT) - .then(ensurePublicDirectoriesExist) - .then(migrateUserData) - .then(migrateSystemPrompts) - .then(verifySecuritySettings) - .then(preSetupTasks) - .then(apply404Middleware) - .then(() => new ServerStartup(app, cliArgs).start()) - .then(postSetupTasks); diff --git a/src/server-main.js b/src/server-main.js new file mode 100644 index 000000000..4e5344482 --- /dev/null +++ b/src/server-main.js @@ -0,0 +1,386 @@ +// native node modules +import path from 'node:path'; +import util from 'node:util'; +import net from 'node:net'; +import dns from 'node:dns'; +import process from 'node:process'; + +import cors from 'cors'; +import { csrfSync } from 'csrf-sync'; +import express from 'express'; +import compression from 'compression'; +import cookieSession from 'cookie-session'; +import multer from 'multer'; +import responseTime from 'response-time'; +import helmet from 'helmet'; +import bodyParser from 'body-parser'; +import open from 'open'; + +// local library imports +import './fetch-patch.js'; +import { serverDirectory } from './server-directory.js'; + +console.log(`Node version: ${process.version}. Running in ${process.env.NODE_ENV} environment. Server directory: ${serverDirectory}`); + +// Work around a node v20.0.0, v20.1.0, and v20.2.0 bug. The issue was fixed in v20.3.0. +// https://github.com/nodejs/node/issues/47822#issuecomment-1564708870 +// Safe to remove once support for Node v20 is dropped. +if (process.versions && process.versions.node && process.versions.node.match(/20\.[0-2]\.0/)) { + // @ts-ignore + if (net.setDefaultAutoSelectFamily) net.setDefaultAutoSelectFamily(false); +} + +import { serverEvents, EVENT_NAMES } from './server-events.js'; +import { loadPlugins } from './plugin-loader.js'; +import { + initUserStorage, + getCookieSecret, + getCookieSessionName, + ensurePublicDirectoriesExist, + getUserDirectoriesList, + migrateSystemPrompts, + migrateUserData, + requireLoginMiddleware, + setUserDataMiddleware, + shouldRedirectToLogin, + cleanUploads, + getSessionCookieAge, + verifySecuritySettings, + loginPageMiddleware, +} from './users.js'; + +import getWebpackServeMiddleware from './middleware/webpack-serve.js'; +import basicAuthMiddleware from './middleware/basicAuth.js'; +import getWhitelistMiddleware from './middleware/whitelist.js'; +import accessLoggerMiddleware, { getAccessLogPath, migrateAccessLog } from './middleware/accessLogWriter.js'; +import multerMonkeyPatch from './middleware/multerMonkeyPatch.js'; +import initRequestProxy from './request-proxy.js'; +import getCacheBusterMiddleware from './middleware/cacheBuster.js'; +import corsProxyMiddleware from './middleware/corsProxy.js'; +import { + getVersion, + color, + removeColorFormatting, + getSeparator, + safeReadFileSync, + setupLogLevel, + setWindowTitle, +} from './util.js'; +import { UPLOADS_DIRECTORY } from './constants.js'; +import { ensureThumbnailCache } from './endpoints/thumbnails.js'; + +// Routers +import { router as usersPublicRouter } from './endpoints/users-public.js'; +import { init as statsInit, onExit as statsOnExit } from './endpoints/stats.js'; +import { checkForNewContent } from './endpoints/content-manager.js'; +import { init as settingsInit } from './endpoints/settings.js'; +import { redirectDeprecatedEndpoints, ServerStartup, setupPrivateEndpoints } from './server-startup.js'; +import { diskCache } from './endpoints/characters.js'; + +// Unrestrict console logs display limit +util.inspect.defaultOptions.maxArrayLength = null; +util.inspect.defaultOptions.maxStringLength = null; +util.inspect.defaultOptions.depth = 4; + +const cliArgs = globalThis.COMMAND_LINE_ARGS; + +if (!cliArgs.enableIPv6 && !cliArgs.enableIPv4) { + console.error('error: You can\'t disable all internet protocols: at least IPv6 or IPv4 must be enabled.'); + process.exit(1); +} + +try { + if (cliArgs.dnsPreferIPv6) { + dns.setDefaultResultOrder('ipv6first'); + console.log('Preferring IPv6 for DNS resolution'); + } else { + dns.setDefaultResultOrder('ipv4first'); + console.log('Preferring IPv4 for DNS resolution'); + } +} catch (error) { + console.warn('Failed to set DNS resolution order. Possibly unsupported in this Node version.'); +} + +const app = express(); +app.use(helmet({ + contentSecurityPolicy: false, +})); +app.use(compression()); +app.use(responseTime()); + +app.use(bodyParser.json({ limit: '200mb' })); +app.use(bodyParser.urlencoded({ extended: true, limit: '200mb' })); + +// CORS Settings // +const CORS = cors({ + origin: 'null', + methods: ['OPTIONS'], +}); + +app.use(CORS); + +if (cliArgs.listen && cliArgs.basicAuthMode) { + app.use(basicAuthMiddleware); +} + +if (cliArgs.whitelistMode) { + const whitelistMiddleware = await getWhitelistMiddleware(); + app.use(whitelistMiddleware); +} + +if (cliArgs.listen) { + app.use(accessLoggerMiddleware()); +} + +if (cliArgs.enableCorsProxy) { + app.use('/proxy/:url(*)', corsProxyMiddleware); +} else { + app.use('/proxy/:url(*)', async (_, res) => { + const message = 'CORS proxy is disabled. Enable it in config.yaml or use the --corsProxy flag.'; + console.log(message); + res.status(404).send(message); + }); +} + +app.use(cookieSession({ + name: getCookieSessionName(), + sameSite: 'lax', + httpOnly: true, + maxAge: getSessionCookieAge(), + secret: getCookieSecret(globalThis.DATA_ROOT), +})); + +app.use(setUserDataMiddleware); + +// CSRF Protection // +if (!cliArgs.disableCsrf) { + const csrfSyncProtection = csrfSync({ + getTokenFromState: (req) => { + if (!req.session) { + console.error('(CSRF error) getTokenFromState: Session object not initialized'); + return; + } + return req.session.csrfToken; + }, + getTokenFromRequest: (req) => { + return req.headers['x-csrf-token']?.toString(); + }, + storeTokenInState: (req, token) => { + if (!req.session) { + console.error('(CSRF error) storeTokenInState: Session object not initialized'); + return; + } + req.session.csrfToken = token; + }, + size: 32, + }); + + app.get('/csrf-token', (req, res) => { + res.json({ + 'token': csrfSyncProtection.generateToken(req), + }); + }); + + // Customize the error message + csrfSyncProtection.invalidCsrfTokenError.message = color.red('Invalid CSRF token. Please refresh the page and try again.'); + csrfSyncProtection.invalidCsrfTokenError.stack = undefined; + + app.use(csrfSyncProtection.csrfSynchronisedProtection); +} else { + console.warn('\nCSRF protection is disabled. This will make your server vulnerable to CSRF attacks.\n'); + app.get('/csrf-token', (req, res) => { + res.json({ + 'token': 'disabled', + }); + }); +} + +// Static files +// Host index page +app.get('/', getCacheBusterMiddleware(), (request, response) => { + if (shouldRedirectToLogin(request)) { + const query = request.url.split('?')[1]; + const redirectUrl = query ? `/login?${query}` : '/login'; + return response.redirect(redirectUrl); + } + + return response.sendFile('index.html', { root: path.join(serverDirectory, 'public') }); +}); + +// Callback endpoint for OAuth PKCE flows (e.g. OpenRouter) +app.get('/callback/:source?', (request, response) => { + const source = request.params.source; + const query = request.url.split('?')[1]; + const searchParams = new URLSearchParams(); + source && searchParams.set('source', source); + query && searchParams.set('query', query); + const path = `/?${searchParams.toString()}`; + return response.redirect(307, path); +}); + +// Host login page +app.get('/login', loginPageMiddleware); + +// Host frontend assets +const webpackMiddleware = getWebpackServeMiddleware(); +app.use(webpackMiddleware); +app.use(express.static(path.join(serverDirectory, 'public'), {})); + +// Public API +app.use('/api/users', usersPublicRouter); + +// Everything below this line requires authentication +app.use(requireLoginMiddleware); +app.get('/api/ping', (request, response) => { + if (request.query.extend && request.session) { + request.session.touch = Date.now(); + } + + response.sendStatus(204); +}); + +// File uploads +const uploadsPath = path.join(cliArgs.dataRoot, UPLOADS_DIRECTORY); +app.use(multer({ dest: uploadsPath, limits: { fieldSize: 10 * 1024 * 1024 } }).single('avatar')); +app.use(multerMonkeyPatch); + +app.get('/version', async function (_, response) { + const data = await getVersion(); + response.send(data); +}); + +redirectDeprecatedEndpoints(app); +setupPrivateEndpoints(app); + +/** + * Tasks that need to be run before the server starts listening. + * @returns {Promise} + */ +async function preSetupTasks() { + const version = await getVersion(); + + // Print formatted header + console.log(); + console.log(`SillyTavern ${version.pkgVersion}`); + if (version.gitBranch) { + console.log(`Running '${version.gitBranch}' (${version.gitRevision}) - ${version.commitDate}`); + if (!version.isLatest && ['staging', 'release'].includes(version.gitBranch)) { + console.log('INFO: Currently not on the latest commit.'); + console.log(' Run \'git pull\' to update. If you have any merge conflicts, run \'git reset --hard\' and \'git pull\' to reset your branch.'); + } + } + console.log(); + + const directories = await getUserDirectoriesList(); + await checkForNewContent(directories); + await ensureThumbnailCache(directories); + await diskCache.verify(directories); + cleanUploads(); + migrateAccessLog(); + + await settingsInit(); + await statsInit(); + + const pluginsDirectory = path.join(serverDirectory, 'plugins'); + const cleanupPlugins = await loadPlugins(app, pluginsDirectory); + const consoleTitle = process.title; + + let isExiting = false; + const exitProcess = async () => { + if (isExiting) return; + isExiting = true; + await statsOnExit(); + if (typeof cleanupPlugins === 'function') { + await cleanupPlugins(); + } + diskCache.dispose(); + setWindowTitle(consoleTitle); + process.exit(); + }; + + // Set up event listeners for a graceful shutdown + process.on('SIGINT', exitProcess); + process.on('SIGTERM', exitProcess); + process.on('uncaughtException', (err) => { + console.error('Uncaught exception:', err); + exitProcess(); + }); + + // Add request proxy. + initRequestProxy({ enabled: cliArgs.requestProxyEnabled, url: cliArgs.requestProxyUrl, bypass: cliArgs.requestProxyBypass }); + + // Wait for frontend libs to compile + await webpackMiddleware.runWebpackCompiler(); +} + +/** + * Tasks that need to be run after the server starts listening. + * @param {import('./server-startup.js').ServerStartupResult} result The result of the server startup + * @returns {Promise} + */ +async function postSetupTasks(result) { + const autorunHostname = await cliArgs.getAutorunHostname(result); + const autorunUrl = cliArgs.getAutorunUrl(autorunHostname); + + if (cliArgs.autorun) { + try { + console.log('Launching in a browser...'); + await open(autorunUrl.toString()); + } catch (error) { + console.error('Failed to launch the browser. Open the URL manually.'); + } + } + + setWindowTitle('SillyTavern WebServer'); + + let logListen = 'SillyTavern is listening on'; + + if (result.useIPv6 && !result.v6Failed) { + logListen += color.green( + ' IPv6: ' + cliArgs.getIPv6ListenUrl().host, + ); + } + + if (result.useIPv4 && !result.v4Failed) { + logListen += color.green( + ' IPv4: ' + cliArgs.getIPv4ListenUrl().host, + ); + } + + const goToLog = 'Go to: ' + color.blue(autorunUrl) + ' to open SillyTavern'; + const plainGoToLog = removeColorFormatting(goToLog); + + console.log(logListen); + if (cliArgs.listen) { + console.log(); + console.log('To limit connections to internal localhost only ([::1] or 127.0.0.1), change the setting in config.yaml to "listen: false".'); + console.log('Check the "access.log" file in the data directory to inspect incoming connections:', color.green(getAccessLogPath())); + } + console.log('\n' + getSeparator(plainGoToLog.length) + '\n'); + console.log(goToLog); + console.log('\n' + getSeparator(plainGoToLog.length) + '\n'); + + setupLogLevel(); + serverEvents.emit(EVENT_NAMES.SERVER_STARTED, { url: autorunUrl }); +} + +/** + * Registers a not-found error response if a not-found error page exists. Should only be called after all other middlewares have been registered. + */ +function apply404Middleware() { + const notFoundWebpage = safeReadFileSync(path.join(serverDirectory, 'public/error/url-not-found.html')) ?? ''; + app.use((req, res) => { + res.status(404).send(notFoundWebpage); + }); +} + +// User storage module needs to be initialized before starting the server +initUserStorage(globalThis.DATA_ROOT) + .then(ensurePublicDirectoriesExist) + .then(migrateUserData) + .then(migrateSystemPrompts) + .then(verifySecuritySettings) + .then(preSetupTasks) + .then(apply404Middleware) + .then(() => new ServerStartup(app, cliArgs).start()) + .then(postSetupTasks); From 5241b22a7392dc7ff30c5646ff3d1be5cb8e52da Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 23 Apr 2025 21:38:31 +0300 Subject: [PATCH 119/230] Add reasoning effort control for CC OpenRouter Closes #3890 --- public/index.html | 4 ++-- public/scripts/openai.js | 1 + src/endpoints/backends/chat-completions.js | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/public/index.html b/public/index.html index 4dbcf243c..f708229d4 100644 --- a/public/index.html +++ b/public/index.html @@ -2048,11 +2048,11 @@
-
+
+
+
+ Case-Sensitive + +
+
+ Whole Words + +
+
+ Group Scoring + +
+
+ Automation ID + +
+
+ + Recursion Level +
+
+ +
+
@@ -6119,113 +6180,48 @@
- Activation Settings + Additional Matching Sources
-
-
- Automation ID - -
-
- Scan Depth - -
-
- Case-Sensitive - -
-
- Whole Words - -
-
- Group Scoring - -
- - - - -
- - Recursion Level -
-
- -
-
- -
- - - - - - - - - - -
+ + + + + + + +
From d7780ee4bbf0a654b620027c8e7fecc6842c7b2a Mon Sep 17 00:00:00 2001 From: Crow Date: Thu, 24 Apr 2025 20:13:18 +0100 Subject: [PATCH 123/230] Fix character card lorebook imports / exports --- public/scripts/world-info.js | 6 ++++++ src/endpoints/characters.js | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index be1ee1d34..a18ae8330 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -4925,6 +4925,12 @@ export function convertCharacterBook(characterBook) { sticky: entry.extensions?.sticky ?? null, cooldown: entry.extensions?.cooldown ?? null, delay: entry.extensions?.delay ?? null, + matchPersonaDescription: entry.extensions?.matchPersonaDescription ?? false, + matchCharacterDescription: entry.extensions?.matchCharacterDescription ?? false, + matchCharacterPersonality: entry.extensions?.matchCharacterPersonality ?? false, + matchCharacterDepthPrompt: entry.extensions?.matchCharacterDepthPrompt ?? false, + matchScenario: entry.extensions?.matchScenario ?? false, + matchCreatorNotes: entry.extensions?.matchCreatorNotes ?? false, extensions: entry.extensions ?? {}, }; }); diff --git a/src/endpoints/characters.js b/src/endpoints/characters.js index 1011e9a3d..d8df95d7b 100644 --- a/src/endpoints/characters.js +++ b/src/endpoints/characters.js @@ -702,6 +702,12 @@ function convertWorldInfoToCharacterBook(name, entries) { sticky: entry.sticky ?? null, cooldown: entry.cooldown ?? null, delay: entry.delay ?? null, + matchPersonaDescription: entry.matchPersonaDescription ?? false, + matchCharacterDescription: entry.matchCharacterDescription ?? false, + matchCharacterPersonality: entry.matchCharacterPersonality ?? false, + matchCharacterDepthPrompt: entry.matchCharacterDepthPrompt ?? false, + matchScenario: entry.matchScenario ?? false, + matchCreatorNotes: entry.matchCreatorNotes ?? false, }, }; From 7b1baed0d7193fd2b7cd92bff2b9a2db5fcb45b5 Mon Sep 17 00:00:00 2001 From: Crow Date: Thu, 24 Apr 2025 20:16:10 +0100 Subject: [PATCH 124/230] Revert world_entry_form_control css class changes --- public/css/world-info.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/css/world-info.css b/public/css/world-info.css index 91e9349a2..e7e03ad41 100644 --- a/public/css/world-info.css +++ b/public/css/world-info.css @@ -275,6 +275,6 @@ select.keyselect+span.select2-container .select2-selection--multiple { width: 100%; } -.world_entry:has(input[name="delay_until_recursion"]:not(:checked)) .world_entry_form_control_recursion_delay:has(input[name="delayUntilRecursionLevel"]) { +.world_entry:has(input[name="delay_until_recursion"]:not(:checked)) .world_entry_form_control:has(input[name="delayUntilRecursionLevel"]) { display: none; } From 4d483e7814e99c405d3efbab10d23239602ab5d1 Mon Sep 17 00:00:00 2001 From: Crow Date: Thu, 24 Apr 2025 20:21:24 +0100 Subject: [PATCH 125/230] Change handleOptionalSelect name --- public/scripts/world-info.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index a18ae8330..70bd30002 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -3358,7 +3358,7 @@ export async function getWorldEntry(name, data, entry) { }); useGroupScoringSelect.val((entry.useGroupScoring === null || entry.useGroupScoring === undefined) ? 'null' : entry.useGroupScoring ? 'true' : 'false').trigger('input'); - function handleOptionalSelect(fieldName) { + function handleMatchCheckbox(fieldName) { const key = originalWIDataKeyMap[fieldName]; const checkBoxElem = template.find(`input[type="checkbox"][name="${fieldName}"]`); checkBoxElem.data('uid', entry.uid); @@ -3373,12 +3373,12 @@ export async function getWorldEntry(name, data, entry) { checkBoxElem.prop('checked', !!entry[fieldName]).trigger('input'); } - handleOptionalSelect('matchPersonaDescription'); - handleOptionalSelect('matchCharacterDescription'); - handleOptionalSelect('matchCharacterPersonality'); - handleOptionalSelect('matchCharacterDepthPrompt'); - handleOptionalSelect('matchScenario'); - handleOptionalSelect('matchCreatorNotes'); + handleMatchCheckbox('matchPersonaDescription'); + handleMatchCheckbox('matchCharacterDescription'); + handleMatchCheckbox('matchCharacterPersonality'); + handleMatchCheckbox('matchCharacterDepthPrompt'); + handleMatchCheckbox('matchScenario'); + handleMatchCheckbox('matchCreatorNotes'); // automation id const automationIdInput = template.find('input[name="automationId"]'); From 55040213747342278e30632540e89dd1ca6829d2 Mon Sep 17 00:00:00 2001 From: Crow Date: Thu, 24 Apr 2025 20:21:52 +0100 Subject: [PATCH 126/230] Add missing machCharacterPersonality to World Info Definition --- public/scripts/world-info.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 70bd30002..4a6156bde 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -3588,6 +3588,7 @@ export const newWorldInfoEntryDefinition = { preventRecursion: { default: false, type: 'boolean' }, matchPersonaDescription: { default: false, type: 'boolean' }, matchCharacterDescription: { default: false, type: 'boolean' }, + matchCharacterPersonality: { default: false, type: 'boolean' }, matchCharacterDepthPrompt: { default: false, type: 'boolean' }, matchScenario: { default: false, type: 'boolean' }, matchCreatorNotes: { default: false, type: 'boolean' }, From 994f51c18eed892f0384226968754555ed3d67a4 Mon Sep 17 00:00:00 2001 From: Crow Date: Thu, 24 Apr 2025 20:22:47 +0100 Subject: [PATCH 127/230] Add back chat param --- public/scripts/world-info.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 4a6156bde..76cd5953c 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -4054,6 +4054,7 @@ function parseDecorators(content) { /** * Performs a scan on the chat and returns the world info activated. * @param {WIGlobalScanData} globalScanData Chat independent context to be scanned + * @param {string[]} chat The chat messages to scan, in reverse order. * @param {number} maxContext The maximum context size of the generation. * @param {boolean} isDryRun Whether to perform a dry run. * @typedef {object} WIActivated From f8b9c1f9f5b873ce0c0ee871f87a739fa113bd29 Mon Sep 17 00:00:00 2001 From: Crow Date: Thu, 24 Apr 2025 20:30:13 +0100 Subject: [PATCH 128/230] Move matching sources to bottom of entry editor --- public/index.html | 94 +++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/public/index.html b/public/index.html index 343d2a072..3127da950 100644 --- a/public/index.html +++ b/public/index.html @@ -6177,54 +6177,6 @@
- -
-
- Additional Matching Sources -
-
-
- - - - - - - - -
-
-
@@ -6339,6 +6291,52 @@
+
+
+ Additional Matching Sources +
+
+
+ + + + + + + + +
+
From 178391e450b7d8e0f63947be4ecd16c5eca7bfe6 Mon Sep 17 00:00:00 2001 From: Crow Date: Thu, 24 Apr 2025 23:25:35 +0100 Subject: [PATCH 129/230] Add i18n for Additional Matching Sources header --- public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index 3127da950..ad4068edb 100644 --- a/public/index.html +++ b/public/index.html @@ -6293,7 +6293,7 @@
- Additional Matching Sources + Additional Matching Sources
From 6ddd395211f230e99c26f7946e1a7bfe9c6d741e Mon Sep 17 00:00:00 2001 From: Crow Date: Fri, 25 Apr 2025 00:49:12 +0100 Subject: [PATCH 130/230] Move new globalScanData args to the end --- public/script.js | 2 +- public/scripts/world-info.js | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/public/script.js b/public/script.js index 96be5fdc5..cc12380a3 100644 --- a/public/script.js +++ b/public/script.js @@ -4155,7 +4155,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro scenario: scenario, creatorNotes: creatorNotes, }; - const { worldInfoString, worldInfoBefore, worldInfoAfter, worldInfoExamples, worldInfoDepth } = await getWorldInfoPrompt(globalScanData, chatForWI, this_max_context, dryRun); + const { worldInfoString, worldInfoBefore, worldInfoAfter, worldInfoExamples, worldInfoDepth } = await getWorldInfoPrompt(chatForWI, this_max_context, dryRun, globalScanData); setExtensionPrompt('QUIET_PROMPT', '', extension_prompt_types.IN_PROMPT, 0, true); // Add message example WI diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 76cd5953c..8531c8f74 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -187,10 +187,10 @@ class WorldInfoBuffer { /** * Initialize the buffer with the given messages. - * @param {WIGlobalScanData} globalScanData Chat independent context to be scanned * @param {string[]} messages Array of messages to add to the buffer + * @param {WIGlobalScanData} globalScanData Chat independent context to be scanned */ - constructor(globalScanData, messages) { + constructor(messages, globalScanData) { this.#initDepthBuffer(messages); this.#globalScanDataBuffer = globalScanData; } @@ -796,10 +796,10 @@ export const worldInfoCache = new StructuredCloneMap({ cloneOnGet: true, cloneOn /** * Gets the world info based on chat messages. - * @param {WIGlobalScanData} globalScanData Chat independent context to be scanned * @param {string[]} chat - The chat messages to scan, in reverse order. * @param {number} maxContext - The maximum context size of the generation. * @param {boolean} isDryRun - If true, the function will not emit any events. + * @param {WIGlobalScanData} globalScanData Chat independent context to be scanned * @typedef {object} WIPromptResult * @property {string} worldInfoString - Complete world info string * @property {string} worldInfoBefore - World info that goes before the prompt @@ -810,10 +810,10 @@ export const worldInfoCache = new StructuredCloneMap({ cloneOnGet: true, cloneOn * @property {Array} anAfter - Array of entries after Author's Note * @returns {Promise} The world info string and depth. */ -export async function getWorldInfoPrompt(globalScanData, chat, maxContext, isDryRun) { +export async function getWorldInfoPrompt(chat, maxContext, isDryRun, globalScanData) { let worldInfoString = '', worldInfoBefore = '', worldInfoAfter = ''; - const activatedWorldInfo = await checkWorldInfo(globalScanData, chat, maxContext, isDryRun); + const activatedWorldInfo = await checkWorldInfo(chat, maxContext, isDryRun, globalScanData); worldInfoBefore = activatedWorldInfo.worldInfoBefore; worldInfoAfter = activatedWorldInfo.worldInfoAfter; worldInfoString = worldInfoBefore + worldInfoAfter; @@ -4053,10 +4053,10 @@ function parseDecorators(content) { /** * Performs a scan on the chat and returns the world info activated. - * @param {WIGlobalScanData} globalScanData Chat independent context to be scanned * @param {string[]} chat The chat messages to scan, in reverse order. * @param {number} maxContext The maximum context size of the generation. * @param {boolean} isDryRun Whether to perform a dry run. + * @param {WIGlobalScanData} globalScanData Chat independent context to be scanned * @typedef {object} WIActivated * @property {string} worldInfoBefore The world info before the chat. * @property {string} worldInfoAfter The world info after the chat. @@ -4067,9 +4067,9 @@ function parseDecorators(content) { * @property {Set} allActivatedEntries All entries. * @returns {Promise} The world info activated. */ -export async function checkWorldInfo(globalScanData, chat, maxContext, isDryRun) { +export async function checkWorldInfo(chat, maxContext, isDryRun, globalScanData) { const context = getContext(); - const buffer = new WorldInfoBuffer(globalScanData, chat); + const buffer = new WorldInfoBuffer(chat, globalScanData); console.debug(`[WI] --- START WI SCAN (on ${chat.length} messages)${isDryRun ? ' (DRY RUN)' : ''} ---`); From 9248bf1f639222d234d8bd252ba6c8259006da93 Mon Sep 17 00:00:00 2001 From: Crow Date: Fri, 25 Apr 2025 00:49:37 +0100 Subject: [PATCH 131/230] Add creatorNotes macro expansion --- public/script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/script.js b/public/script.js index cc12380a3..6571149c5 100644 --- a/public/script.js +++ b/public/script.js @@ -4291,6 +4291,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro loreAfter: worldInfoAfter, mesExamples: mesExamplesArray.join(''), mesExamplesRaw: mesExamplesRawArray.join(''), + creatorNotes: creatorNotes, }; const storyString = renderStoryString(storyStringParams); From b5280bbfc789de99830960e53513d02fa550161c Mon Sep 17 00:00:00 2001 From: Crow Date: Fri, 25 Apr 2025 00:51:47 +0100 Subject: [PATCH 132/230] Add type data for v2DataWorldInfoEntryExtensionInfos match fields --- public/scripts/char-data.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/public/scripts/char-data.js b/public/scripts/char-data.js index 51e85722c..ac5d4e675 100644 --- a/public/scripts/char-data.js +++ b/public/scripts/char-data.js @@ -33,6 +33,12 @@ * @property {number} role - The specific function or purpose of the extension. * @property {boolean} vectorized - Indicates if the extension is optimized for vectorized processing. * @property {number} display_index - The order in which the extension should be displayed for user interfaces. + * @property {boolean} match_persona_description - Wether to match against the persona description. + * @property {boolean} match_character_description - Wether to match against the persona description. + * @property {boolean} match_character_personality - Wether to match against the character personality. + * @property {boolean} match_character_depth_prompt - Wether to match against the character depth prompt. + * @property {boolean} match_scenario - Wether to match against the character scenario. + * @property {boolean} match_creator_notes - Wether to match against the character creator notes. */ /** From a4442899f656b669523b77ecf90dec146ba41371 Mon Sep 17 00:00:00 2001 From: cloak1505 Date: Thu, 24 Apr 2025 19:35:05 -0500 Subject: [PATCH 133/230] Remove last message role restriction for Cohere --- src/prompt-converters.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/prompt-converters.js b/src/prompt-converters.js index ea23b9c81..dd6ba2f11 100644 --- a/src/prompt-converters.js +++ b/src/prompt-converters.js @@ -351,11 +351,6 @@ export function convertCohereMessages(messages, names) { } }); - // A prompt should end with a user/tool message - if (messages.length && !['user', 'tool'].includes(messages[messages.length - 1].role)) { - messages[messages.length - 1].role = 'user'; - } - return { chatHistory: messages }; } From a3d7b540c79f071b166b6832025cb5d7ff6a20c8 Mon Sep 17 00:00:00 2001 From: Crow Date: Fri, 25 Apr 2025 03:11:36 +0100 Subject: [PATCH 134/230] Change field names to match required fields --- public/scripts/world-info.js | 12 ++++++------ src/endpoints/characters.js | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 8531c8f74..aef96ca98 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -4927,12 +4927,12 @@ export function convertCharacterBook(characterBook) { sticky: entry.extensions?.sticky ?? null, cooldown: entry.extensions?.cooldown ?? null, delay: entry.extensions?.delay ?? null, - matchPersonaDescription: entry.extensions?.matchPersonaDescription ?? false, - matchCharacterDescription: entry.extensions?.matchCharacterDescription ?? false, - matchCharacterPersonality: entry.extensions?.matchCharacterPersonality ?? false, - matchCharacterDepthPrompt: entry.extensions?.matchCharacterDepthPrompt ?? false, - matchScenario: entry.extensions?.matchScenario ?? false, - matchCreatorNotes: entry.extensions?.matchCreatorNotes ?? false, + matchPersonaDescription: entry.extensions?.match_persona_description ?? false, + matchCharacterDescription: entry.extensions?.match_character_description ?? false, + matchCharacterPersonality: entry.extensions?.match_character_personality ?? false, + matchCharacterDepthPrompt: entry.extensions?.match_character_depth_prompt ?? false, + matchScenario: entry.extensions?.match_scenario ?? false, + matchCreatorNotes: entry.extensions?.match_creator_notes ?? false, extensions: entry.extensions ?? {}, }; }); diff --git a/src/endpoints/characters.js b/src/endpoints/characters.js index d8df95d7b..e884971f3 100644 --- a/src/endpoints/characters.js +++ b/src/endpoints/characters.js @@ -702,12 +702,12 @@ function convertWorldInfoToCharacterBook(name, entries) { sticky: entry.sticky ?? null, cooldown: entry.cooldown ?? null, delay: entry.delay ?? null, - matchPersonaDescription: entry.matchPersonaDescription ?? false, - matchCharacterDescription: entry.matchCharacterDescription ?? false, - matchCharacterPersonality: entry.matchCharacterPersonality ?? false, - matchCharacterDepthPrompt: entry.matchCharacterDepthPrompt ?? false, - matchScenario: entry.matchScenario ?? false, - matchCreatorNotes: entry.matchCreatorNotes ?? false, + match_persona_description: entry.matchPersonaDescription ?? false, + match_character_description: entry.matchCharacterDescription ?? false, + match_character_personality: entry.matchCharacterPersonality ?? false, + match_character_depth_prompt: entry.matchCharacterDepthPrompt ?? false, + match_scenario: entry.matchScenario ?? false, + match_creator_notes: entry.matchCreatorNotes ?? false, }, }; From bb9f765ce3ad49a62cc51c6adac6bc032280d296 Mon Sep 17 00:00:00 2001 From: Crow Date: Fri, 25 Apr 2025 03:17:13 +0100 Subject: [PATCH 135/230] Add pointer cursor to Additional Matching Sources drawer header --- public/css/world-info.css | 4 ++++ public/index.html | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/public/css/world-info.css b/public/css/world-info.css index e7e03ad41..f8130d51f 100644 --- a/public/css/world-info.css +++ b/public/css/world-info.css @@ -124,6 +124,10 @@ cursor: initial; } +.world_entry .inline-drawer-header-pointer { + cursor: pointer; +} + .world_entry .killSwitch { cursor: pointer; } diff --git a/public/index.html b/public/index.html index ad4068edb..7cbdb172f 100644 --- a/public/index.html +++ b/public/index.html @@ -6292,7 +6292,7 @@
-
+
Additional Matching Sources
From b233cc248005f8efad1b71096bf6e54e19ea5770 Mon Sep 17 00:00:00 2001 From: Crow Date: Fri, 25 Apr 2025 03:23:14 +0100 Subject: [PATCH 136/230] Add extra field docs --- public/scripts/world-info.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index aef96ca98..719f4e5a6 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -101,11 +101,11 @@ const KNOWN_DECORATORS = ['@@activate', '@@dont_activate']; * @typedef {object} WIGlobalScanData The chat-independent data to be scanned. Each of * these fields can be enabled for scanning per entry. * @property {string} personaDescription User persona description - * @property {string} characterDescription - * @property {string} characterPersonality - * @property {string} characterDepthPrompt + * @property {string} characterDescription Character description + * @property {string} characterPersonality Character personality + * @property {string} characterDepthPrompt Character depth prompt (sometimes referred to as character notes) * @property {string} scenario Character defined scenario - * @property {string} creatorNotes + * @property {string} creatorNotes Character creator notes */ /** From 2d366117ddfaa53363e207c6e9adbe7d59c52cee Mon Sep 17 00:00:00 2001 From: Karl-Johan Alm Date: Thu, 24 Apr 2025 23:14:20 +0900 Subject: [PATCH 137/230] chat preset: GLM-4 --- default/content/index.json | 8 ++++++++ default/content/presets/context/GLM-4.json | 11 +++++++++++ default/content/presets/instruct/GLM-4.json | 22 +++++++++++++++++++++ public/scripts/chat-templates.js | 5 +++++ 4 files changed, 46 insertions(+) create mode 100644 default/content/presets/context/GLM-4.json create mode 100644 default/content/presets/instruct/GLM-4.json diff --git a/default/content/index.json b/default/content/index.json index ac88da3e3..449c55289 100644 --- a/default/content/index.json +++ b/default/content/index.json @@ -795,6 +795,14 @@ "filename": "presets/context/DeepSeek-V2.5.json", "type": "context" }, + { + "filename": "presets/instruct/GLM-4.json", + "type": "instruct" + }, + { + "filename": "presets/context/GLM-4.json", + "type": "context" + }, { "filename": "presets/reasoning/DeepSeek.json", "type": "reasoning" diff --git a/default/content/presets/context/GLM-4.json b/default/content/presets/context/GLM-4.json new file mode 100644 index 000000000..f250f60fd --- /dev/null +++ b/default/content/presets/context/GLM-4.json @@ -0,0 +1,11 @@ +{ + "story_string": "[gMASK]\n{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}{{trim}}\n", + "example_separator": "", + "chat_start": "", + "use_stop_strings": false, + "allow_jailbreak": false, + "always_force_name2": true, + "trim_sentences": false, + "single_line": false, + "name": "GLM-4" +} diff --git a/default/content/presets/instruct/GLM-4.json b/default/content/presets/instruct/GLM-4.json new file mode 100644 index 000000000..8fb081365 --- /dev/null +++ b/default/content/presets/instruct/GLM-4.json @@ -0,0 +1,22 @@ +{ + "input_sequence": "<|user|>\n", + "output_sequence": "<|assistant|>\n", + "first_output_sequence": "", + "last_output_sequence": "", + "system_sequence_prefix": "<|system|>\n", + "system_sequence_suffix": "", + "stop_sequence": "", + "wrap": false, + "macro": true, + "names_behavior": "force", + "activation_regex": "", + "skip_examples": false, + "output_suffix": "", + "input_suffix": "", + "system_sequence": "", + "system_suffix": "", + "user_alignment_message": "", + "last_system_sequence": "", + "system_same_as_user": true, + "name": "GLM-4" +} diff --git a/public/scripts/chat-templates.js b/public/scripts/chat-templates.js index 5c3ad7cf1..95bac803f 100644 --- a/public/scripts/chat-templates.js +++ b/public/scripts/chat-templates.js @@ -74,6 +74,11 @@ const hash_derivations = { 'b6835114b7303ddd78919a82e4d9f7d8c26ed0d7dfc36beeb12d524f6144eab1': 'DeepSeek-V2.5' , + + // THUDM-GLM 4 + '854b703e44ca06bdb196cc471c728d15dbab61e744fe6cdce980086b61646ed1': + 'GLM-4' + , }; const substr_derivations = { From 81fec97f5491b3f8b01db658d8bfc5c4665e68eb Mon Sep 17 00:00:00 2001 From: cloak1505 Date: Fri, 25 Apr 2025 09:22:10 -0500 Subject: [PATCH 138/230] Repair Lightning 1.1's system prompt --- default/content/presets/context/Lightning 1.1.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/default/content/presets/context/Lightning 1.1.json b/default/content/presets/context/Lightning 1.1.json index 24878e009..e5c283e00 100644 --- a/default/content/presets/context/Lightning 1.1.json +++ b/default/content/presets/context/Lightning 1.1.json @@ -1,5 +1,5 @@ { - "story_string": "{{system}}\n{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{char}}'s description:{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality:{{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{user}}'s persona: {{persona}}\n{{/if}}", + "story_string": "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\n### Instruction:\nTake the role of {{char}} in a play that leaves a lasting impression on {{user}}. Write {{char}}'s next reply.\nNever skip or gloss over {{char}}’s actions. Progress the scene at a naturally slow pace.\n\n{{system}}\n{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{char}}'s description:{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality:{{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{user}}'s persona: {{persona}}\n{{/if}}", "example_separator": "Example of an interaction:", "chat_start": "This is the history of the roleplay:", "use_stop_strings": false, From 2151ae7aaa4a4f8781cd72e11755cd0b01f01992 Mon Sep 17 00:00:00 2001 From: cloak1505 Date: Fri, 25 Apr 2025 09:40:49 -0500 Subject: [PATCH 139/230] Normalize instruct "names_behavior" to "force" for those that don't require "none" or "always --- default/content/presets/instruct/ChatML.json | 2 +- default/content/presets/instruct/Command R.json | 2 +- default/content/presets/instruct/Gemma 2.json | 2 +- default/content/presets/instruct/Llama 3 Instruct.json | 2 +- default/content/presets/instruct/Llama 4 Instruct.json | 2 +- default/content/presets/instruct/Mistral V1.json | 2 +- default/content/presets/instruct/Mistral V2 & V3.json | 2 +- default/content/presets/instruct/Mistral V3-Tekken.json | 2 +- default/content/presets/instruct/Mistral V7.json | 2 +- default/content/presets/instruct/Phi.json | 2 +- default/content/presets/instruct/Pygmalion.json | 2 +- default/content/presets/instruct/Tulu.json | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/default/content/presets/instruct/ChatML.json b/default/content/presets/instruct/ChatML.json index 199b3915b..ee543433a 100644 --- a/default/content/presets/instruct/ChatML.json +++ b/default/content/presets/instruct/ChatML.json @@ -6,7 +6,7 @@ "stop_sequence": "<|im_end|>", "wrap": true, "macro": true, - "names_behavior": "always", + "names_behavior": "force", "activation_regex": "", "system_sequence_prefix": "", "system_sequence_suffix": "", diff --git a/default/content/presets/instruct/Command R.json b/default/content/presets/instruct/Command R.json index f34159081..762c0df8d 100644 --- a/default/content/presets/instruct/Command R.json +++ b/default/content/presets/instruct/Command R.json @@ -8,7 +8,7 @@ "stop_sequence": "<|END_OF_TURN_TOKEN|>", "wrap": false, "macro": true, - "names_behavior": "always", + "names_behavior": "force", "activation_regex": "", "skip_examples": false, "output_suffix": "<|END_OF_TURN_TOKEN|>", diff --git a/default/content/presets/instruct/Gemma 2.json b/default/content/presets/instruct/Gemma 2.json index 7a21a2316..d5585a2c8 100644 --- a/default/content/presets/instruct/Gemma 2.json +++ b/default/content/presets/instruct/Gemma 2.json @@ -6,7 +6,7 @@ "stop_sequence": "", "wrap": true, "macro": true, - "names_behavior": "none", + "names_behavior": "force", "activation_regex": "", "system_sequence_prefix": "", "system_sequence_suffix": "", diff --git a/default/content/presets/instruct/Llama 3 Instruct.json b/default/content/presets/instruct/Llama 3 Instruct.json index 317a4c698..8bc2e0ace 100644 --- a/default/content/presets/instruct/Llama 3 Instruct.json +++ b/default/content/presets/instruct/Llama 3 Instruct.json @@ -6,7 +6,7 @@ "stop_sequence": "<|eot_id|>", "wrap": false, "macro": true, - "names_behavior": "always", + "names_behavior": "force", "activation_regex": "", "system_sequence_prefix": "", "system_sequence_suffix": "", diff --git a/default/content/presets/instruct/Llama 4 Instruct.json b/default/content/presets/instruct/Llama 4 Instruct.json index 28115c6ce..13c712af2 100644 --- a/default/content/presets/instruct/Llama 4 Instruct.json +++ b/default/content/presets/instruct/Llama 4 Instruct.json @@ -6,7 +6,7 @@ "stop_sequence": "<|eot|>", "wrap": false, "macro": true, - "names_behavior": "always", + "names_behavior": "force", "activation_regex": "", "system_sequence_prefix": "", "system_sequence_suffix": "", diff --git a/default/content/presets/instruct/Mistral V1.json b/default/content/presets/instruct/Mistral V1.json index c47dbdc77..679ae01db 100644 --- a/default/content/presets/instruct/Mistral V1.json +++ b/default/content/presets/instruct/Mistral V1.json @@ -6,7 +6,7 @@ "stop_sequence": "", "wrap": false, "macro": true, - "names_behavior": "always", + "names_behavior": "force", "activation_regex": "", "system_sequence_prefix": "", "system_sequence_suffix": "", diff --git a/default/content/presets/instruct/Mistral V2 & V3.json b/default/content/presets/instruct/Mistral V2 & V3.json index 7619a1aa5..da072c92b 100644 --- a/default/content/presets/instruct/Mistral V2 & V3.json +++ b/default/content/presets/instruct/Mistral V2 & V3.json @@ -6,7 +6,7 @@ "stop_sequence": "", "wrap": false, "macro": true, - "names_behavior": "always", + "names_behavior": "force", "activation_regex": "", "system_sequence_prefix": "", "system_sequence_suffix": "", diff --git a/default/content/presets/instruct/Mistral V3-Tekken.json b/default/content/presets/instruct/Mistral V3-Tekken.json index 217007d73..ee0207748 100644 --- a/default/content/presets/instruct/Mistral V3-Tekken.json +++ b/default/content/presets/instruct/Mistral V3-Tekken.json @@ -6,7 +6,7 @@ "stop_sequence": "", "wrap": false, "macro": true, - "names_behavior": "always", + "names_behavior": "force", "activation_regex": "", "system_sequence_prefix": "", "system_sequence_suffix": "", diff --git a/default/content/presets/instruct/Mistral V7.json b/default/content/presets/instruct/Mistral V7.json index b6b81ae02..4568365a9 100644 --- a/default/content/presets/instruct/Mistral V7.json +++ b/default/content/presets/instruct/Mistral V7.json @@ -6,7 +6,7 @@ "stop_sequence": "", "wrap": false, "macro": true, - "names_behavior": "always", + "names_behavior": "force", "activation_regex": "", "system_sequence_prefix": "", "system_sequence_suffix": "", diff --git a/default/content/presets/instruct/Phi.json b/default/content/presets/instruct/Phi.json index 2762cdbd2..617be7ae6 100644 --- a/default/content/presets/instruct/Phi.json +++ b/default/content/presets/instruct/Phi.json @@ -8,7 +8,7 @@ "stop_sequence": "<|end|>", "wrap": false, "macro": true, - "names_behavior": "always", + "names_behavior": "force", "activation_regex": "", "skip_examples": false, "output_suffix": "<|end|>\n", diff --git a/default/content/presets/instruct/Pygmalion.json b/default/content/presets/instruct/Pygmalion.json index 3827585bf..96cd55312 100644 --- a/default/content/presets/instruct/Pygmalion.json +++ b/default/content/presets/instruct/Pygmalion.json @@ -6,7 +6,7 @@ "stop_sequence": "<|user|>", "wrap": false, "macro": true, - "names_behavior": "always", + "names_behavior": "force", "activation_regex": "", "system_sequence_prefix": "<|system|>", "system_sequence_suffix": "", diff --git a/default/content/presets/instruct/Tulu.json b/default/content/presets/instruct/Tulu.json index 14072f563..3cfe4f87a 100644 --- a/default/content/presets/instruct/Tulu.json +++ b/default/content/presets/instruct/Tulu.json @@ -8,7 +8,7 @@ "stop_sequence": "<|end_of_text|>", "wrap": false, "macro": true, - "names_behavior": "always", + "names_behavior": "force", "activation_regex": "", "skip_examples": false, "output_suffix": "<|end_of_text|>\n", From cf7edd99a7fc876445bf420d99e9a5c343d9e2f9 Mon Sep 17 00:00:00 2001 From: Karl-Johan Alm Date: Sat, 26 Apr 2025 00:08:02 +0900 Subject: [PATCH 140/230] trivial: remove extraneous \n after sop token --- default/content/presets/context/GLM-4.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/default/content/presets/context/GLM-4.json b/default/content/presets/context/GLM-4.json index f250f60fd..e958deaca 100644 --- a/default/content/presets/context/GLM-4.json +++ b/default/content/presets/context/GLM-4.json @@ -1,5 +1,5 @@ { - "story_string": "[gMASK]\n{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}{{trim}}\n", + "story_string": "[gMASK]{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}{{trim}}\n", "example_separator": "", "chat_start": "", "use_stop_strings": false, From b3e51c8b1c82e0cb1279a8f83e2b4d0ab44b60cc Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 25 Apr 2025 20:20:53 +0300 Subject: [PATCH 141/230] Fix continuation suffix trimming Fixes #3901 --- public/script.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/public/script.js b/public/script.js index ca5f87850..87418da02 100644 --- a/public/script.js +++ b/public/script.js @@ -4235,12 +4235,20 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro // Do not suffix the message for continuation if (i === 0 && isContinue) { + // Pick something that's very unlikely to be in a message + const FORMAT_TOKEN = '\u0000\ufffc\u0000\ufffd'; + if (isInstruct) { + const originalMessage = String(coreChat[j].mes ?? ''); + coreChat[j].mes = originalMessage.replaceAll(FORMAT_TOKEN, '') + FORMAT_TOKEN; // Reformat with the last output sequence (if any) chat2[i] = formatMessageHistoryItem(coreChat[j], isInstruct, force_output_sequence.LAST); + coreChat[j].mes = originalMessage; } - chat2[i] = chat2[i].slice(0, chat2[i].lastIndexOf(coreChat[j].mes) + coreChat[j].mes.length); + chat2[i] = chat2[i].includes(FORMAT_TOKEN) + ? chat2[i].slice(0, chat2[i].lastIndexOf(FORMAT_TOKEN)) + : chat2[i].slice(0, chat2[i].lastIndexOf(coreChat[j].mes) + coreChat[j].mes.length); continue_mag = coreChat[j].mes; } From 5c4794812fec15a3554f2ae143f3f97ac1b7ecd2 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 25 Apr 2025 20:54:24 +0300 Subject: [PATCH 142/230] Move creatorNotes macro init --- public/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/script.js b/public/script.js index 6571149c5..ff86cd342 100644 --- a/public/script.js +++ b/public/script.js @@ -2751,6 +2751,7 @@ export function substituteParams(content, _name1, _name2, _original, _group, _re environment.charVersion = fields.version || ''; environment.char_version = fields.version || ''; environment.charDepthPrompt = fields.charDepthPrompt || ''; + environment.creatorNotes = fields.creatorNotes || ''; } // Must be substituted last so that they're replaced inside {{description}} @@ -4291,7 +4292,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro loreAfter: worldInfoAfter, mesExamples: mesExamplesArray.join(''), mesExamplesRaw: mesExamplesRawArray.join(''), - creatorNotes: creatorNotes, }; const storyString = renderStoryString(storyStringParams); From 421c924c229df846e74b88b83a2194b814b2c922 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 25 Apr 2025 21:01:53 +0300 Subject: [PATCH 143/230] Do not append empty joiners --- public/scripts/world-info.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 719f4e5a6..74fce2aa3 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -158,7 +158,7 @@ class WorldInfoBuffer { /** * @type {WIGlobalScanData} Chat independent data to be scanned, such as persona and character descriptions */ - #globalScanDataBuffer = null; + #globalScanData = null; /** * @type {string[]} Array of messages sorted by ascending depth @@ -192,7 +192,7 @@ class WorldInfoBuffer { */ constructor(messages, globalScanData) { this.#initDepthBuffer(messages); - this.#globalScanDataBuffer = globalScanData; + this.#globalScanData = globalScanData; } /** @@ -249,23 +249,23 @@ class WorldInfoBuffer { const JOINER = '\n' + MATCHER; let result = MATCHER + this.#depthBuffer.slice(this.#startDepth, depth).join(JOINER); - if (entry.matchPersonaDescription) { - result += JOINER + this.#globalScanDataBuffer.personaDescription; + if (entry.matchPersonaDescription && this.#globalScanData.personaDescription) { + result += JOINER + this.#globalScanData.personaDescription; } - if (entry.matchCharacterDescription) { - result += JOINER + this.#globalScanDataBuffer.characterDescription; + if (entry.matchCharacterDescription && this.#globalScanData.characterDescription) { + result += JOINER + this.#globalScanData.characterDescription; } - if (entry.matchCharacterPersonality) { - result += JOINER + this.#globalScanDataBuffer.characterPersonality; + if (entry.matchCharacterPersonality && this.#globalScanData.characterPersonality) { + result += JOINER + this.#globalScanData.characterPersonality; } - if (entry.matchCharacterDepthPrompt) { - result += JOINER + this.#globalScanDataBuffer.characterDepthPrompt; + if (entry.matchCharacterDepthPrompt && this.#globalScanData.characterDepthPrompt) { + result += JOINER + this.#globalScanData.characterDepthPrompt; } - if (entry.matchScenario) { - result += JOINER + this.#globalScanDataBuffer.scenario; + if (entry.matchScenario && this.#globalScanData.scenario) { + result += JOINER + this.#globalScanData.scenario; } - if (entry.matchCreatorNotes) { - result += JOINER + this.#globalScanDataBuffer.creatorNotes; + if (entry.matchCreatorNotes && this.#globalScanData.creatorNotes) { + result += JOINER + this.#globalScanData.creatorNotes; } if (this.#injectBuffer.length > 0) { From bd1d393e5dccfaafefb0fbeca06f41e67f2ac226 Mon Sep 17 00:00:00 2001 From: cloak1505 Date: Fri, 25 Apr 2025 13:05:14 -0500 Subject: [PATCH 144/230] Print full Google response --- src/endpoints/backends/chat-completions.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index e61dd16bf..8ff3f569c 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -1,6 +1,7 @@ import process from 'node:process'; import express from 'express'; import fetch from 'node-fetch'; +import util from 'node:util'; import { CHAT_COMPLETION_SOURCES, @@ -498,7 +499,7 @@ async function sendMakerSuiteRequest(request, response) { const responseContent = candidates[0].content ?? candidates[0].output; const functionCall = (candidates?.[0]?.content?.parts ?? []).some(part => part.functionCall); const inlineData = (candidates?.[0]?.content?.parts ?? []).some(part => part.inlineData); - console.warn('Google AI Studio response:', responseContent); + console.warn('Google AI Studio response:', util.inspect(generateResponseJson, {depth: 6, colors: true})); const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.filter(part => !part.thought)?.map(part => part.text)?.join('\n\n'); if (!responseText && !functionCall && !inlineData) { From ea7ff5b1c2eec29e277a6d216ad2b96e90ee794b Mon Sep 17 00:00:00 2001 From: cloak1505 Date: Fri, 25 Apr 2025 13:22:33 -0500 Subject: [PATCH 145/230] inspect depth 5 --- src/endpoints/backends/chat-completions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index 8ff3f569c..4b789891b 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -499,7 +499,7 @@ async function sendMakerSuiteRequest(request, response) { const responseContent = candidates[0].content ?? candidates[0].output; const functionCall = (candidates?.[0]?.content?.parts ?? []).some(part => part.functionCall); const inlineData = (candidates?.[0]?.content?.parts ?? []).some(part => part.inlineData); - console.warn('Google AI Studio response:', util.inspect(generateResponseJson, {depth: 6, colors: true})); + console.warn('Google AI Studio response:', util.inspect(generateResponseJson, {depth: 5, colors: true})); const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.filter(part => !part.thought)?.map(part => part.text)?.join('\n\n'); if (!responseText && !functionCall && !inlineData) { From 93ea8b6a2255fbf73e4701d84b4053175914a1bc Mon Sep 17 00:00:00 2001 From: cloak1505 Date: Fri, 25 Apr 2025 13:37:56 -0500 Subject: [PATCH 146/230] ESLint woes --- src/endpoints/backends/chat-completions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index 4b789891b..ab88d64ba 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -499,7 +499,7 @@ async function sendMakerSuiteRequest(request, response) { const responseContent = candidates[0].content ?? candidates[0].output; const functionCall = (candidates?.[0]?.content?.parts ?? []).some(part => part.functionCall); const inlineData = (candidates?.[0]?.content?.parts ?? []).some(part => part.inlineData); - console.warn('Google AI Studio response:', util.inspect(generateResponseJson, {depth: 5, colors: true})); + console.warn('Google AI Studio response:', util.inspect(generateResponseJson, { depth: 5, colors: true })); const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.filter(part => !part.thought)?.map(part => part.text)?.join('\n\n'); if (!responseText && !functionCall && !inlineData) { From 776d2203746d8568f845c483371c9021e6fb573b Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 25 Apr 2025 21:40:59 +0300 Subject: [PATCH 147/230] Why was it a warning level --- src/endpoints/backends/chat-completions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index ab88d64ba..adc62e641 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -1,7 +1,7 @@ import process from 'node:process'; +import util from 'node:util'; import express from 'express'; import fetch from 'node-fetch'; -import util from 'node:util'; import { CHAT_COMPLETION_SOURCES, @@ -499,7 +499,7 @@ async function sendMakerSuiteRequest(request, response) { const responseContent = candidates[0].content ?? candidates[0].output; const functionCall = (candidates?.[0]?.content?.parts ?? []).some(part => part.functionCall); const inlineData = (candidates?.[0]?.content?.parts ?? []).some(part => part.inlineData); - console.warn('Google AI Studio response:', util.inspect(generateResponseJson, { depth: 5, colors: true })); + console.debug('Google AI Studio response:', util.inspect(generateResponseJson, { depth: 5, colors: true })); const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.filter(part => !part.thought)?.map(part => part.text)?.join('\n\n'); if (!responseText && !functionCall && !inlineData) { From 470a0964f7be9a433a7385f145f2d13927f3662c Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Fri, 25 Apr 2025 20:43:13 +0200 Subject: [PATCH 148/230] Initialize world info during app startup Moves world info event binding from jQuery document-ready handler to explicit initialization function for better control flow Ensures world info setup occurs after core dependencies are loaded --- public/script.js | 2 ++ public/scripts/world-info.js | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/public/script.js b/public/script.js index ca5f87850..cf18599fa 100644 --- a/public/script.js +++ b/public/script.js @@ -50,6 +50,7 @@ import { importWorldInfo, wi_anchor_position, world_info_include_names, + initWorldInfo, } from './scripts/world-info.js'; import { @@ -992,6 +993,7 @@ async function firstLoadInit() { initBackgrounds(); initAuthorsNote(); await initPersonas(); + initWorldInfo(); initRossMods(); initStats(); initCfg(); diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 917840532..501218c33 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -5160,8 +5160,7 @@ export async function assignLorebookToChat(event) { return callGenericPopup(template, POPUP_TYPE.TEXT); } -jQuery(() => { - +export function initWorldInfo() { $('#world_info').on('mousedown change', async function (e) { // If there's no world names, don't do anything if (world_names.length === 0) { @@ -5373,7 +5372,7 @@ jQuery(() => { } }); }); -}); +} /** * Moves a World Info entry from a source lorebook to a target lorebook. From a667e14c8b15ede354a67497973ce62c25ed84de Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Fri, 25 Apr 2025 20:45:13 +0200 Subject: [PATCH 149/230] Make global WI placeholder translatable --- public/scripts/world-info.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 501218c33..2f7d3c9fc 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -5347,7 +5347,7 @@ export function initWorldInfo() { if (!isMobile()) { $('#world_info').select2({ width: '100%', - placeholder: 'No Worlds active. Click here to select.', + placeholder: t`No Worlds active. Click here to select.`, allowClear: true, closeOnSelect: false, }); From 05c010223b36973017a33d625c1dea976152e467 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Fri, 25 Apr 2025 20:49:47 +0200 Subject: [PATCH 150/230] Move function above init Well, I like that init is last in nearly all files... --- public/scripts/world-info.js | 192 +++++++++++++++++------------------ 1 file changed, 96 insertions(+), 96 deletions(-) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 2f7d3c9fc..17b52e824 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -5160,6 +5160,102 @@ export async function assignLorebookToChat(event) { return callGenericPopup(template, POPUP_TYPE.TEXT); } +/** + * Moves a World Info entry from a source lorebook to a target lorebook. + * + * @param {string} sourceName - The name of the source lorebook file. + * @param {string} targetName - The name of the target lorebook file. + * @param {string|number} uid - The UID of the entry to move from the source lorebook. + * @returns {Promise} True if the move was successful, false otherwise. + */ +export async function moveWorldInfoEntry(sourceName, targetName, uid) { + if (sourceName === targetName) { + return false; + } + + if (!world_names.includes(sourceName)) { + toastr.error(t`Source lorebook '${sourceName}' not found.`); + console.error(`[WI Move] Source lorebook '${sourceName}' does not exist.`); + return false; + } + + if (!world_names.includes(targetName)) { + toastr.error(t`Target lorebook '${targetName}' not found.`); + console.error(`[WI Move] Target lorebook '${targetName}' does not exist.`); + return false; + } + + const entryUidString = String(uid); + + try { + const sourceData = await loadWorldInfo(sourceName); + const targetData = await loadWorldInfo(targetName); + + if (!sourceData || !sourceData.entries) { + toastr.error(t`Failed to load data for source lorebook '${sourceName}'.`); + console.error(`[WI Move] Could not load source data for '${sourceName}'.`); + return false; + } + if (!targetData || !targetData.entries) { + toastr.error(t`Failed to load data for target lorebook '${targetName}'.`); + console.error(`[WI Move] Could not load target data for '${targetName}'.`); + return false; + } + + if (!sourceData.entries[entryUidString]) { + toastr.error(t`Entry not found in source lorebook '${sourceName}'.`); + console.error(`[WI Move] Entry UID ${entryUidString} not found in '${sourceName}'.`); + return false; + } + + const entryToMove = structuredClone(sourceData.entries[entryUidString]); + + + const newUid = getFreeWorldEntryUid(targetData); + if (newUid === null) { + console.error(`[WI Move] Failed to get a free UID in '${targetName}'.`); + return false; + } + + entryToMove.uid = newUid; + // Place the entry at the end of the target lorebook + const maxDisplayIndex = Object.values(targetData.entries).reduce((max, entry) => Math.max(max, entry.displayIndex ?? -1), -1); + entryToMove.displayIndex = maxDisplayIndex + 1; + + targetData.entries[newUid] = entryToMove; + + delete sourceData.entries[entryUidString]; + // Remove from originalData if it exists + deleteWIOriginalDataValue(sourceData, entryUidString); + // TODO: setWIOriginalDataValue + console.debug(`[WI Move] Removed entry UID ${entryUidString} from source '${sourceName}'.`); + + + await saveWorldInfo(targetName, targetData, true); + console.debug(`[WI Move] Saved target lorebook '${targetName}'.`); + await saveWorldInfo(sourceName, sourceData, true); + console.debug(`[WI Move] Saved source lorebook '${sourceName}'.`); + + + console.log(`[WI Move] ${entryToMove.comment} moved successfully to '${targetName}'.`); + + // Check if the currently viewed book in the editor is the source or target and reload it + const currentEditorBookIndex = Number($('#world_editor_select').val()); + if (!isNaN(currentEditorBookIndex)) { + const currentEditorBookName = world_names[currentEditorBookIndex]; + if (currentEditorBookName === sourceName || currentEditorBookName === targetName) { + reloadEditor(currentEditorBookName); + } + } + + return true; + } catch (error) { + toastr.error(t`An unexpected error occurred while moving the entry: ${error.message}`); + console.error('[WI Move] Unexpected error:', error); + return false; + } +} + export function initWorldInfo() { $('#world_info').on('mousedown change', async function (e) { // If there's no world names, don't do anything @@ -5373,99 +5469,3 @@ export function initWorldInfo() { }); }); } - -/** - * Moves a World Info entry from a source lorebook to a target lorebook. - * - * @param {string} sourceName - The name of the source lorebook file. - * @param {string} targetName - The name of the target lorebook file. - * @param {string|number} uid - The UID of the entry to move from the source lorebook. - * @returns {Promise} True if the move was successful, false otherwise. - */ -export async function moveWorldInfoEntry(sourceName, targetName, uid) { - if (sourceName === targetName) { - return false; - } - - if (!world_names.includes(sourceName)) { - toastr.error(t`Source lorebook '${sourceName}' not found.`); - console.error(`[WI Move] Source lorebook '${sourceName}' does not exist.`); - return false; - } - - if (!world_names.includes(targetName)) { - toastr.error(t`Target lorebook '${targetName}' not found.`); - console.error(`[WI Move] Target lorebook '${targetName}' does not exist.`); - return false; - } - - const entryUidString = String(uid); - - try { - const sourceData = await loadWorldInfo(sourceName); - const targetData = await loadWorldInfo(targetName); - - if (!sourceData || !sourceData.entries) { - toastr.error(t`Failed to load data for source lorebook '${sourceName}'.`); - console.error(`[WI Move] Could not load source data for '${sourceName}'.`); - return false; - } - if (!targetData || !targetData.entries) { - toastr.error(t`Failed to load data for target lorebook '${targetName}'.`); - console.error(`[WI Move] Could not load target data for '${targetName}'.`); - return false; - } - - if (!sourceData.entries[entryUidString]) { - toastr.error(t`Entry not found in source lorebook '${sourceName}'.`); - console.error(`[WI Move] Entry UID ${entryUidString} not found in '${sourceName}'.`); - return false; - } - - const entryToMove = structuredClone(sourceData.entries[entryUidString]); - - - const newUid = getFreeWorldEntryUid(targetData); - if (newUid === null) { - console.error(`[WI Move] Failed to get a free UID in '${targetName}'.`); - return false; - } - - entryToMove.uid = newUid; - // Place the entry at the end of the target lorebook - const maxDisplayIndex = Object.values(targetData.entries).reduce((max, entry) => Math.max(max, entry.displayIndex ?? -1), -1); - entryToMove.displayIndex = maxDisplayIndex + 1; - - targetData.entries[newUid] = entryToMove; - - delete sourceData.entries[entryUidString]; - // Remove from originalData if it exists - deleteWIOriginalDataValue(sourceData, entryUidString); - // TODO: setWIOriginalDataValue - console.debug(`[WI Move] Removed entry UID ${entryUidString} from source '${sourceName}'.`); - - - await saveWorldInfo(targetName, targetData, true); - console.debug(`[WI Move] Saved target lorebook '${targetName}'.`); - await saveWorldInfo(sourceName, sourceData, true); - console.debug(`[WI Move] Saved source lorebook '${sourceName}'.`); - - - console.log(`[WI Move] ${entryToMove.comment} moved successfully to '${targetName}'.`); - - // Check if the currently viewed book in the editor is the source or target and reload it - const currentEditorBookIndex = Number($('#world_editor_select').val()); - if (!isNaN(currentEditorBookIndex)) { - const currentEditorBookName = world_names[currentEditorBookIndex]; - if (currentEditorBookName === sourceName || currentEditorBookName === targetName) { - reloadEditor(currentEditorBookName); - } - } - - return true; - } catch (error) { - toastr.error(t`An unexpected error occurred while moving the entry: ${error.message}`); - console.error('[WI Move] Unexpected error:', error); - return false; - } -} From 6eb89bd21c8eabcb30d0b346f41d6b40ae260840 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Fri, 25 Apr 2025 20:58:28 +0200 Subject: [PATCH 151/230] fix some linting --- public/scripts/utils.js | 2 +- public/scripts/world-info.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/public/scripts/utils.js b/public/scripts/utils.js index 47bdaa253..56b4f4a70 100644 --- a/public/scripts/utils.js +++ b/public/scripts/utils.js @@ -1047,7 +1047,7 @@ export function getImageSizeFromDataURL(dataUrl) { /** * Gets the filename of the character avatar without extension - * @param {number?} [chid=null] - Character ID. If not provided, uses the current character ID + * @param {string|number?} [chid=null] - Character ID. If not provided, uses the current character ID * @param {object} [options={}] - Options arguments * @param {string?} [options.manualAvatarKey=null] - Manually take the following avatar key, instead of using the chid to determine the name * @returns {string?} The filename of the character avatar without extension, or null if the character ID is invalid diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 17b52e824..52c25c1da 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -966,7 +966,7 @@ function registerWorldInfoSlashCommands() { /** * Gets the name of the character-bound lorebook. * @param {import('./slash-commands/SlashCommand.js').NamedArguments} args Named arguments - * @param {import('./slash-commands/SlashCommand.js').UnnamedArguments} name Character name + * @param {string} name Character name * @returns {string} The name of the character-bound lorebook, a JSON string of the character's lorebooks, or an empty string */ function getCharBookCallback({ type }, name) { @@ -3443,7 +3443,7 @@ function createEntryInputAutocomplete(input, callback, { allowMultiple = false } }); $(input).on('focus click', function () { - $(input).autocomplete('search', allowMultiple ? String($(input).val()).split(/,\s*/).pop() : $(input).val()); + $(input).autocomplete('search', allowMultiple ? String($(input).val()).split(/,\s*/).pop() : String($(input).val())); }); } @@ -5118,7 +5118,7 @@ export function openWorldInfoEditor(worldName) { /** * Assigns a lorebook to the current chat. - * @param {PointerEvent} event Pointer event + * @param {JQuery.ClickEvent} event Pointer event * @returns {Promise} */ export async function assignLorebookToChat(event) { @@ -5157,7 +5157,7 @@ export async function assignLorebookToChat(event) { saveMetadata(); }); - return callGenericPopup(template, POPUP_TYPE.TEXT); + await callGenericPopup(template, POPUP_TYPE.TEXT); } /** From 005a495e964da2fe85636ff731cb86f25bdcc614 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 25 Apr 2025 22:22:44 +0300 Subject: [PATCH 152/230] Move config migration from post-install to src --- .github/readme.md | 1 + post-install.js | 184 +---------------------------------------- src/command-line.js | 5 +- src/config-init.js | 197 ++++++++++++++++++++++++++++++++++++++++++++ src/transformers.js | 1 - src/util.js | 2 +- 6 files changed, 204 insertions(+), 186 deletions(-) create mode 100644 src/config-init.js diff --git a/.github/readme.md b/.github/readme.md index fc8cef288..dd68b3b31 100644 --- a/.github/readme.md +++ b/.github/readme.md @@ -350,6 +350,7 @@ Start.bat --port 8000 --listen false | Option | Description | Type | |-------------------------|----------------------------------------------------------------------|----------| | `--version` | Show version number | boolean | +| `--configPath` | Override the path to the config.yaml file | string | | `--dataRoot` | Root directory for data storage | string | | `--port` | Sets the port under which SillyTavern will run | number | | `--listen` | SillyTavern will listen on all network interfaces | boolean | diff --git a/post-install.js b/post-install.js index 94f354e20..4908e950e 100644 --- a/post-install.js +++ b/post-install.js @@ -5,130 +5,15 @@ import fs from 'node:fs'; import path from 'node:path'; import process from 'node:process'; import yaml from 'yaml'; -import _ from 'lodash'; import chalk from 'chalk'; import { createRequire } from 'node:module'; +import { addMissingConfigValues } from './src/config-init.js'; /** * Colorizes console output. */ const color = chalk; -const keyMigrationMap = [ - { - oldKey: 'disableThumbnails', - newKey: 'thumbnails.enabled', - migrate: (value) => !value, - }, - { - oldKey: 'thumbnailsQuality', - newKey: 'thumbnails.quality', - migrate: (value) => value, - }, - { - oldKey: 'avatarThumbnailsPng', - newKey: 'thumbnails.format', - migrate: (value) => (value ? 'png' : 'jpg'), - }, - { - oldKey: 'disableChatBackup', - newKey: 'backups.chat.enabled', - migrate: (value) => !value, - }, - { - oldKey: 'numberOfBackups', - newKey: 'backups.common.numberOfBackups', - migrate: (value) => value, - }, - { - oldKey: 'maxTotalChatBackups', - newKey: 'backups.chat.maxTotalBackups', - migrate: (value) => value, - }, - { - oldKey: 'chatBackupThrottleInterval', - newKey: 'backups.chat.throttleInterval', - migrate: (value) => value, - }, - { - oldKey: 'enableExtensions', - newKey: 'extensions.enabled', - migrate: (value) => value, - }, - { - oldKey: 'enableExtensionsAutoUpdate', - newKey: 'extensions.autoUpdate', - migrate: (value) => value, - }, - { - oldKey: 'extras.disableAutoDownload', - newKey: 'extensions.models.autoDownload', - migrate: (value) => !value, - }, - { - oldKey: 'extras.classificationModel', - newKey: 'extensions.models.classification', - migrate: (value) => value, - }, - { - oldKey: 'extras.captioningModel', - newKey: 'extensions.models.captioning', - migrate: (value) => value, - }, - { - oldKey: 'extras.embeddingModel', - newKey: 'extensions.models.embedding', - migrate: (value) => value, - }, - { - oldKey: 'extras.speechToTextModel', - newKey: 'extensions.models.speechToText', - migrate: (value) => value, - }, - { - oldKey: 'extras.textToSpeechModel', - newKey: 'extensions.models.textToSpeech', - migrate: (value) => value, - }, - { - oldKey: 'minLogLevel', - newKey: 'logging.minLogLevel', - migrate: (value) => value, - }, - { - oldKey: 'cardsCacheCapacity', - newKey: 'performance.memoryCacheCapacity', - migrate: (value) => `${value}mb`, - }, - { - oldKey: 'cookieSecret', - newKey: 'cookieSecret', - migrate: () => void 0, - remove: true, - }, -]; - -/** - * Gets all keys from an object recursively. - * @param {object} obj Object to get all keys from - * @param {string} prefix Prefix to prepend to all keys - * @returns {string[]} Array of all keys in the object - */ -function getAllKeys(obj, prefix = '') { - if (typeof obj !== 'object' || Array.isArray(obj) || obj === null) { - return []; - } - - return _.flatMap(Object.keys(obj), key => { - const newPrefix = prefix ? `${prefix}.${key}` : key; - if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) { - return getAllKeys(obj[key], newPrefix); - } else { - return [newPrefix]; - } - }); -} - /** * Converts the old config.conf file to the new config.yaml format. */ @@ -155,71 +40,6 @@ function convertConfig() { } } -/** - * Compares the current config.yaml with the default config.yaml and adds any missing values. - */ -function addMissingConfigValues() { - try { - const defaultConfig = yaml.parse(fs.readFileSync(path.join(process.cwd(), './default/config.yaml'), 'utf8')); - let config = yaml.parse(fs.readFileSync(path.join(process.cwd(), './config.yaml'), 'utf8')); - - // Migrate old keys to new keys - const migratedKeys = []; - for (const { oldKey, newKey, migrate, remove } of keyMigrationMap) { - if (_.has(config, oldKey)) { - if (remove) { - _.unset(config, oldKey); - migratedKeys.push({ - oldKey, - newValue: void 0, - }); - continue; - } - - const oldValue = _.get(config, oldKey); - const newValue = migrate(oldValue); - _.set(config, newKey, newValue); - _.unset(config, oldKey); - - migratedKeys.push({ - oldKey, - newKey, - oldValue, - newValue, - }); - } - } - - // Get all keys from the original config - const originalKeys = getAllKeys(config); - - // Use lodash's defaultsDeep function to recursively apply default properties - config = _.defaultsDeep(config, defaultConfig); - - // Get all keys from the updated config - const updatedKeys = getAllKeys(config); - - // Find the keys that were added - const addedKeys = _.difference(updatedKeys, originalKeys); - - if (addedKeys.length === 0 && migratedKeys.length === 0) { - return; - } - - if (addedKeys.length > 0) { - console.log('Adding missing config values to config.yaml:', addedKeys); - } - - if (migratedKeys.length > 0) { - console.log('Migrating config values in config.yaml:', migratedKeys); - } - - fs.writeFileSync('./config.yaml', yaml.stringify(config)); - } catch (error) { - console.error(color.red('FATAL: Could not add missing config values to config.yaml'), error); - } -} - /** * Creates the default config files if they don't exist yet. */ @@ -288,7 +108,7 @@ try { // 1. Create default config files createDefaultFiles(); // 2. Add missing config values - addMissingConfigValues(); + addMissingConfigValues(path.join(process.cwd(), './config.yaml')); } catch (error) { console.error(error); } diff --git a/src/command-line.js b/src/command-line.js index c7d4d9d9b..cb1dd1015 100644 --- a/src/command-line.js +++ b/src/command-line.js @@ -1,7 +1,8 @@ import yargs from 'yargs/yargs'; import { hideBin } from 'yargs/helpers'; import ipRegex from 'ip-regex'; -import { canResolve, color, getConfigValue, setConfigFilePath, stringToBool } from './util.js'; +import { canResolve, color, getConfigValue, stringToBool } from './util.js'; +import { initConfig } from './config-init.js'; /** * @typedef {object} CommandLineArguments Parsed command line arguments @@ -185,7 +186,7 @@ export class CommandLineParser { }).parseSync(); const configPath = cliArguments.configPath ?? this.default.configPath; - setConfigFilePath(configPath); + initConfig(configPath); /** @type {CommandLineArguments} */ const result = { configPath: configPath, diff --git a/src/config-init.js b/src/config-init.js new file mode 100644 index 000000000..826929b50 --- /dev/null +++ b/src/config-init.js @@ -0,0 +1,197 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import yaml from 'yaml'; +import color from 'chalk'; +import _ from 'lodash'; +import { serverDirectory } from './server-directory.js'; +import { setConfigFilePath } from './util.js'; + +const keyMigrationMap = [ + { + oldKey: 'disableThumbnails', + newKey: 'thumbnails.enabled', + migrate: (value) => !value, + }, + { + oldKey: 'thumbnailsQuality', + newKey: 'thumbnails.quality', + migrate: (value) => value, + }, + { + oldKey: 'avatarThumbnailsPng', + newKey: 'thumbnails.format', + migrate: (value) => (value ? 'png' : 'jpg'), + }, + { + oldKey: 'disableChatBackup', + newKey: 'backups.chat.enabled', + migrate: (value) => !value, + }, + { + oldKey: 'numberOfBackups', + newKey: 'backups.common.numberOfBackups', + migrate: (value) => value, + }, + { + oldKey: 'maxTotalChatBackups', + newKey: 'backups.chat.maxTotalBackups', + migrate: (value) => value, + }, + { + oldKey: 'chatBackupThrottleInterval', + newKey: 'backups.chat.throttleInterval', + migrate: (value) => value, + }, + { + oldKey: 'enableExtensions', + newKey: 'extensions.enabled', + migrate: (value) => value, + }, + { + oldKey: 'enableExtensionsAutoUpdate', + newKey: 'extensions.autoUpdate', + migrate: (value) => value, + }, + { + oldKey: 'extras.disableAutoDownload', + newKey: 'extensions.models.autoDownload', + migrate: (value) => !value, + }, + { + oldKey: 'extras.classificationModel', + newKey: 'extensions.models.classification', + migrate: (value) => value, + }, + { + oldKey: 'extras.captioningModel', + newKey: 'extensions.models.captioning', + migrate: (value) => value, + }, + { + oldKey: 'extras.embeddingModel', + newKey: 'extensions.models.embedding', + migrate: (value) => value, + }, + { + oldKey: 'extras.speechToTextModel', + newKey: 'extensions.models.speechToText', + migrate: (value) => value, + }, + { + oldKey: 'extras.textToSpeechModel', + newKey: 'extensions.models.textToSpeech', + migrate: (value) => value, + }, + { + oldKey: 'minLogLevel', + newKey: 'logging.minLogLevel', + migrate: (value) => value, + }, + { + oldKey: 'cardsCacheCapacity', + newKey: 'performance.memoryCacheCapacity', + migrate: (value) => `${value}mb`, + }, + { + oldKey: 'cookieSecret', + newKey: 'cookieSecret', + migrate: () => void 0, + remove: true, + }, +]; + +/** + * Gets all keys from an object recursively. + * @param {object} obj Object to get all keys from + * @param {string} prefix Prefix to prepend to all keys + * @returns {string[]} Array of all keys in the object + */ +function getAllKeys(obj, prefix = '') { + if (typeof obj !== 'object' || Array.isArray(obj) || obj === null) { + return []; + } + + return _.flatMap(Object.keys(obj), key => { + const newPrefix = prefix ? `${prefix}.${key}` : key; + if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) { + return getAllKeys(obj[key], newPrefix); + } else { + return [newPrefix]; + } + }); +} + +/** + * Compares the current config.yaml with the default config.yaml and adds any missing values. + * @param {string} configPath Path to config.yaml + */ +export function addMissingConfigValues(configPath) { + try { + const defaultConfig = yaml.parse(fs.readFileSync(path.join(serverDirectory, './default/config.yaml'), 'utf8')); + let config = yaml.parse(fs.readFileSync(configPath, 'utf8')); + + // Migrate old keys to new keys + const migratedKeys = []; + for (const { oldKey, newKey, migrate, remove } of keyMigrationMap) { + if (_.has(config, oldKey)) { + if (remove) { + _.unset(config, oldKey); + migratedKeys.push({ + oldKey, + newValue: void 0, + }); + continue; + } + + const oldValue = _.get(config, oldKey); + const newValue = migrate(oldValue); + _.set(config, newKey, newValue); + _.unset(config, oldKey); + + migratedKeys.push({ + oldKey, + newKey, + oldValue, + newValue, + }); + } + } + + // Get all keys from the original config + const originalKeys = getAllKeys(config); + + // Use lodash's defaultsDeep function to recursively apply default properties + config = _.defaultsDeep(config, defaultConfig); + + // Get all keys from the updated config + const updatedKeys = getAllKeys(config); + + // Find the keys that were added + const addedKeys = _.difference(updatedKeys, originalKeys); + + if (addedKeys.length === 0 && migratedKeys.length === 0) { + return; + } + + if (addedKeys.length > 0) { + console.log('Adding missing config values to config.yaml:', addedKeys); + } + + if (migratedKeys.length > 0) { + console.log('Migrating config values in config.yaml:', migratedKeys); + } + + fs.writeFileSync(configPath, yaml.stringify(config)); + } catch (error) { + console.error(color.red('FATAL: Could not add missing config values to config.yaml'), error); + } +} + +/** + * Performs early initialization tasks before the server starts. + * @param {string} configPath Path to config.yaml + */ +export function initConfig(configPath) { + setConfigFilePath(configPath); + addMissingConfigValues(configPath); +} diff --git a/src/transformers.js b/src/transformers.js index d0eb851a1..1fdea760b 100644 --- a/src/transformers.js +++ b/src/transformers.js @@ -12,7 +12,6 @@ configureTransformers(); function configureTransformers() { // Limit the number of threads to 1 to avoid issues on Android env.backends.onnx.wasm.numThreads = 1; - console.log(env.backends.onnx.wasm.wasmPaths); // Use WASM from a local folder to avoid CDN connections env.backends.onnx.wasm.wasmPaths = path.join(serverDirectory, 'node_modules', 'sillytavern-transformers', 'dist') + path.sep; } diff --git a/src/util.js b/src/util.js index 66dd561f4..f59c0cea8 100644 --- a/src/util.js +++ b/src/util.js @@ -15,8 +15,8 @@ import yauzl from 'yauzl'; import mime from 'mime-types'; import { default as simpleGit } from 'simple-git'; import chalk from 'chalk'; -import { LOG_LEVELS } from './constants.js'; import bytes from 'bytes'; +import { LOG_LEVELS } from './constants.js'; import { serverDirectory } from './server-directory.js'; /** From 82c86c9ce631bb1aa08d6a0f57e36569f47d42ff Mon Sep 17 00:00:00 2001 From: cloak1505 <170299980+cloak1505@users.noreply.github.com> Date: Fri, 25 Apr 2025 14:57:42 -0500 Subject: [PATCH 153/230] Clean Lightning 1.1 --- default/content/presets/context/Lightning 1.1.json | 6 +++--- default/content/presets/instruct/Lightning 1.1.json | 8 ++++---- default/content/presets/sysprompt/Lightning 1.1.json | 4 ++++ 3 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 default/content/presets/sysprompt/Lightning 1.1.json diff --git a/default/content/presets/context/Lightning 1.1.json b/default/content/presets/context/Lightning 1.1.json index e5c283e00..8aff8abc1 100644 --- a/default/content/presets/context/Lightning 1.1.json +++ b/default/content/presets/context/Lightning 1.1.json @@ -1,7 +1,7 @@ { - "story_string": "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\n### Instruction:\nTake the role of {{char}} in a play that leaves a lasting impression on {{user}}. Write {{char}}'s next reply.\nNever skip or gloss over {{char}}’s actions. Progress the scene at a naturally slow pace.\n\n{{system}}\n{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{char}}'s description:{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality:{{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{user}}'s persona: {{persona}}\n{{/if}}", - "example_separator": "Example of an interaction:", - "chat_start": "This is the history of the roleplay:", + "story_string": "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\n### Instruction:\n{{system}}\n{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{char}}'s description:{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality:{{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{user}}'s persona: {{persona}}\n{{/if}}\n\n", + "example_separator": "Example of an interaction:\n", + "chat_start": "This is the history of the roleplay:\n", "use_stop_strings": false, "allow_jailbreak": false, "always_force_name2": true, diff --git a/default/content/presets/instruct/Lightning 1.1.json b/default/content/presets/instruct/Lightning 1.1.json index 3ce4d6a90..6f1439629 100644 --- a/default/content/presets/instruct/Lightning 1.1.json +++ b/default/content/presets/instruct/Lightning 1.1.json @@ -1,7 +1,7 @@ { "input_sequence": "### Instruction:", - "output_sequence": "### Response: (length = unlimited)", - "last_output_sequence": "", + "output_sequence": "### Response:", + "last_output_sequence": "### Response: (length = unlimited)", "system_sequence": "", "stop_sequence": "", "wrap": true, @@ -12,8 +12,8 @@ "system_sequence_suffix": "", "first_output_sequence": "", "skip_examples": false, - "output_suffix": "", - "input_suffix": "", + "output_suffix": "\n\n", + "input_suffix": "\n\n", "system_suffix": "", "user_alignment_message": "", "system_same_as_user": true, diff --git a/default/content/presets/sysprompt/Lightning 1.1.json b/default/content/presets/sysprompt/Lightning 1.1.json new file mode 100644 index 000000000..b122f0a12 --- /dev/null +++ b/default/content/presets/sysprompt/Lightning 1.1.json @@ -0,0 +1,4 @@ +{ + "name": "Lightning 1.1", + "content": "Take the role of {{char}} in a play that leaves a lasting impression on {{user}}. Write {{char}}'s next reply.\nNever skip or gloss over {{char}}’s actions. Progress the scene at a naturally slow pace." +} From 321efa354aa9c21c846b992d22d9f2be26715122 Mon Sep 17 00:00:00 2001 From: cloak1505 <170299980+cloak1505@users.noreply.github.com> Date: Fri, 25 Apr 2025 15:35:44 -0500 Subject: [PATCH 154/230] Update index.json --- default/content/index.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/default/content/index.json b/default/content/index.json index 449c55289..3557ea8e0 100644 --- a/default/content/index.json +++ b/default/content/index.json @@ -755,6 +755,10 @@ "filename": "presets/sysprompt/Neutral - Chat.json", "type": "sysprompt" }, + { + "filename": "presets/sysprompt/Lightning 1.1.json", + "type": "sysprompt" + }, { "filename": "presets/instruct/Mistral V1.json", "type": "instruct" From e621f0d967d94d8c2c984dd7261759fa1c96783d Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 26 Apr 2025 00:34:21 +0300 Subject: [PATCH 155/230] Remove model name check in convertGooglePrompt --- src/prompt-converters.js | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/prompt-converters.js b/src/prompt-converters.js index 75ee89cc8..421455b7a 100644 --- a/src/prompt-converters.js +++ b/src/prompt-converters.js @@ -362,22 +362,12 @@ export function convertCohereMessages(messages, names) { /** * Convert a prompt from the ChatML objects to the format used by Google MakerSuite models. * @param {object[]} messages Array of messages - * @param {string} model Model name + * @param {string} _model Model name * @param {boolean} useSysPrompt Use system prompt * @param {PromptNames} names Prompt names * @returns {{contents: *[], system_instruction: {parts: {text: string}[]}}} Prompt for Google MakerSuite models */ -export function convertGooglePrompt(messages, model, useSysPrompt, names) { - const visionSupportedModelPrefix = [ - 'gemini-1.5', - 'gemini-2.0', - 'gemini-2.5', - 'gemini-exp-1114', - 'gemini-exp-1121', - 'gemini-exp-1206', - ]; - - const isMultimodal = visionSupportedModelPrefix.some(prefix => model.startsWith(prefix)); +export function convertGooglePrompt(messages, _model, useSysPrompt, names) { const sysPrompt = []; if (useSysPrompt) { @@ -477,7 +467,7 @@ export function convertGooglePrompt(messages, model, useSysPrompt, names) { toolNameMap[toolCall.id] = toolCall.function.name; }); - } else if (part.type === 'image_url' && isMultimodal) { + } else if (part.type === 'image_url') { const mimeType = part.image_url.url.split(';')[0].split(':')[1]; const base64Data = part.image_url.url.split(',')[1]; parts.push({ From a927ab557a1c97a2e67efc3b3da4626a7ec3aa77 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 26 Apr 2025 01:47:07 +0300 Subject: [PATCH 156/230] Check instruct activation regex before selecting bound context template match --- public/scripts/instruct-mode.js | 50 +++++++++++++++++---------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/public/scripts/instruct-mode.js b/public/scripts/instruct-mode.js index 56b16f560..28baa7e04 100644 --- a/public/scripts/instruct-mode.js +++ b/public/scripts/instruct-mode.js @@ -208,37 +208,39 @@ export function autoSelectInstructPreset(modelId) { // Select matching instruct preset let foundMatch = false; - for (const instruct_preset of instruct_presets) { - // If instruct preset matches the context template - if (power_user.instruct.bind_to_context && instruct_preset.name === power_user.context.preset) { - foundMatch = true; - selectInstructPreset(instruct_preset.name, { isAuto: true }); - break; - } - } - // If no match was found, auto-select instruct preset - if (!foundMatch) { - for (const preset of instruct_presets) { - // If activation regex is set, check if it matches the model id - if (preset.activation_regex) { - try { - const regex = regexFromString(preset.activation_regex); - // Stop on first match so it won't cycle back and forth between presets if multiple regexes match - if (regex instanceof RegExp && regex.test(modelId)) { - selectInstructPreset(preset.name, { isAuto: true }); + for (const preset of instruct_presets) { + // If activation regex is set, check if it matches the model id + if (preset.activation_regex) { + try { + const regex = regexFromString(preset.activation_regex); - return true; - } - } catch { - // If regex is invalid, ignore it - console.warn(`Invalid instruct activation regex in preset "${preset.name}"`); + // Stop on first match so it won't cycle back and forth between presets if multiple regexes match + if (regex instanceof RegExp && regex.test(modelId)) { + selectInstructPreset(preset.name, { isAuto: true }); + foundMatch = true; + break; } + } catch { + // If regex is invalid, ignore it + console.warn(`Invalid instruct activation regex in preset "${preset.name}"`); } } } - return false; + // If no match was found, auto-select instruct preset + if (!foundMatch && power_user.instruct.bind_to_context) { + for (const instruct_preset of instruct_presets) { + // If instruct preset matches the context template + if (instruct_preset.name === power_user.context.preset) { + selectInstructPreset(instruct_preset.name, { isAuto: true }); + foundMatch = true; + break; + } + } + } + + return foundMatch; } /** From 28d42e520035567223fb985304d6585a87cc99ab Mon Sep 17 00:00:00 2001 From: cloak1505 <170299980+cloak1505@users.noreply.github.com> Date: Sat, 26 Apr 2025 11:39:44 -0500 Subject: [PATCH 157/230] Prune Google models --- public/index.html | 62 +++++++------------ .../scripts/extensions/caption/settings.html | 27 +------- public/scripts/openai.js | 16 ++--- src/endpoints/backends/chat-completions.js | 3 +- 4 files changed, 32 insertions(+), 76 deletions(-) diff --git a/public/index.html b/public/index.html index f708229d4..195c22165 100644 --- a/public/index.html +++ b/public/index.html @@ -3146,49 +3146,31 @@

Google Model

diff --git a/public/scripts/extensions/caption/settings.html b/public/scripts/extensions/caption/settings.html index 539a737ef..dbab7c6ea 100644 --- a/public/scripts/extensions/caption/settings.html +++ b/public/scripts/extensions/caption/settings.html @@ -79,34 +79,13 @@ - - - - - - - - - - - - - - - - - - - - - - + - - + + diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 2eb809a92..00a9ded80 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -4481,20 +4481,16 @@ async function onModelChange() { if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) { if (oai_settings.max_context_unlocked) { $('#openai_max_context').attr('max', max_2mil); - } else if (value.includes('gemini-exp-1114') || value.includes('gemini-exp-1121') || value.includes('gemini-2.0-flash-thinking-exp-1219')) { - $('#openai_max_context').attr('max', max_32k); - } else if (value.includes('gemini-1.5-pro') || value.includes('gemini-exp-1206') || value.includes('gemini-2.0-pro')) { + } else if (value.includes('gemini-1.5-pro')) { $('#openai_max_context').attr('max', max_2mil); - } else if (value.includes('gemini-1.5-flash') || value.includes('gemini-2.0-flash') || value.includes('gemini-2.5-flash-preview-04-17') || value.includes('gemini-2.5-pro-exp-03-25') || value.includes('gemini-2.5-pro-preview-03-25')) { + } else if (value.includes('gemini-1.5-flash') || value.includes('gemini-2.0-flash') || value.includes('gemini-2.0-pro') || value.includes('gemini-2.5-flash') || value.includes('gemini-2.5-pro') || value.includes('learnlm-2.0-flash')) { $('#openai_max_context').attr('max', max_1mil); - } else if (value.includes('gemini-1.0-pro') || value === 'gemini-pro') { - $('#openai_max_context').attr('max', max_32k); - } else if (value.includes('gemini-1.0-ultra') || value === 'gemini-ultra') { - $('#openai_max_context').attr('max', max_32k); - } else if (value.includes('gemma-3')) { + } else if (value.includes('gemma-3-27b-it')) { $('#openai_max_context').attr('max', max_128k); + } else if (value.includes('gemma-3') || value.includes('learnlm-1.5-pro-experimental')) { + $('#openai_max_context').attr('max', max_32k); } else { - $('#openai_max_context').attr('max', max_4k); + $('#openai_max_context').attr('max', max_32k); } let makersuite_max_temp = (value.includes('vision') || value.includes('ultra') || value.includes('gemma')) ? 1.0 : 2.0; oai_settings.temp_openai = Math.min(makersuite_max_temp, oai_settings.temp_openai); diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index adc62e641..83b929192 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -372,9 +372,8 @@ async function sendMakerSuiteRequest(request, response) { model.includes('gemini-2.5-flash') || model.includes('gemini-2.0-pro') || model.includes('gemini-2.0-flash') || - model.includes('gemini-2.0-flash-thinking-exp') || - model.includes('gemini-1.5-flash') || model.includes('gemini-1.5-pro') || + model.includes('gemini-1.5-flash') || model.startsWith('gemini-exp') ) && request.body.use_makersuite_sysprompt; From 01d52f140a4d19bb5a5e1cba7b54f2911b62fcdc Mon Sep 17 00:00:00 2001 From: cloak1505 <170299980+cloak1505@users.noreply.github.com> Date: Sat, 26 Apr 2025 12:19:06 -0500 Subject: [PATCH 158/230] Update "Use system prompt" --- public/index.html | 2 +- src/endpoints/backends/chat-completions.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index 195c22165..34916670f 100644 --- a/public/index.html +++ b/public/index.html @@ -2025,7 +2025,7 @@ Use system prompt - +
diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index 83b929192..c3ad8debe 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -374,6 +374,7 @@ async function sendMakerSuiteRequest(request, response) { model.includes('gemini-2.0-flash') || model.includes('gemini-1.5-pro') || model.includes('gemini-1.5-flash') || + model.includes('learnlm') || model.startsWith('gemini-exp') ) && request.body.use_makersuite_sysprompt; From a764e5ce54e942ff5f03a73250621dac6be0d574 Mon Sep 17 00:00:00 2001 From: cloak1505 <170299980+cloak1505@users.noreply.github.com> Date: Sat, 26 Apr 2025 12:51:26 -0500 Subject: [PATCH 159/230] Exclude LearnLM from web search --- src/endpoints/backends/chat-completions.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index c3ad8debe..a96b78889 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -345,6 +345,7 @@ async function sendMakerSuiteRequest(request, response) { const reasoningEffort = String(request.body.reasoning_effort); const isThinking = model.includes('thinking'); const isGemma = model.includes('gemma'); + const isLearnLM = model.includes('learnlm'); const generationConfig = { stopSequences: request.body.stop, @@ -392,7 +393,7 @@ async function sendMakerSuiteRequest(request, response) { } // Most of the other models allow for setting the threshold of filters, except for HARM_CATEGORY_CIVIC_INTEGRITY, to OFF. - if (enableWebSearch && !useMultiModal && !isGemma) { + if (enableWebSearch && !useMultiModal && !isGemma && !isLearnLM) { const searchTool = model.includes('1.5') || model.includes('1.0') ? ({ google_search_retrieval: {} }) : ({ google_search: {} }); From 05e60ff00bef9dbb12487b34d321c7d78959cafc Mon Sep 17 00:00:00 2001 From: cloak1505 <170299980+cloak1505@users.noreply.github.com> Date: Sat, 26 Apr 2025 13:04:00 -0500 Subject: [PATCH 160/230] Exclude gemini-2.0-flash-lite from web search --- src/endpoints/backends/chat-completions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index a96b78889..6c238eccc 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -393,7 +393,7 @@ async function sendMakerSuiteRequest(request, response) { } // Most of the other models allow for setting the threshold of filters, except for HARM_CATEGORY_CIVIC_INTEGRITY, to OFF. - if (enableWebSearch && !useMultiModal && !isGemma && !isLearnLM) { + if (enableWebSearch && !useMultiModal && !isGemma && !isLearnLM && !model.includes('gemini-2.0-flash-lite')) { const searchTool = model.includes('1.5') || model.includes('1.0') ? ({ google_search_retrieval: {} }) : ({ google_search: {} }); From 023976444fef4a48b2adbc4d797ef214e96bc3d9 Mon Sep 17 00:00:00 2001 From: cloak1505 <170299980+cloak1505@users.noreply.github.com> Date: Sat, 26 Apr 2025 14:04:31 -0500 Subject: [PATCH 161/230] Oops, gemini-1.5-pro-001 is still live --- public/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/index.html b/public/index.html index 34916670f..499d5665c 100644 --- a/public/index.html +++ b/public/index.html @@ -3157,10 +3157,10 @@ - - - --> + + From c6a047651b5f237db396bf8556a387dabd395ba3 Mon Sep 17 00:00:00 2001 From: cloak1505 <170299980+cloak1505@users.noreply.github.com> Date: Sat, 26 Apr 2025 14:23:10 -0500 Subject: [PATCH 162/230] Add 'learn' to visionSupportedModels Also remove dead gemini-exp models --- public/scripts/openai.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 00a9ded80..7139c9623 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -5148,9 +5148,8 @@ export function isImageInliningSupported() { 'gemini-1.5', 'gemini-2.0', 'gemini-2.5', - 'gemini-exp-1114', - 'gemini-exp-1121', 'gemini-exp-1206', + 'learnlm', // MistralAI 'mistral-small-2503', 'mistral-small-latest', From af64ac001a078245cdb9b2f146121d99ef0f2c65 Mon Sep 17 00:00:00 2001 From: cloak1505 <170299980+cloak1505@users.noreply.github.com> Date: Sat, 26 Apr 2025 14:33:47 -0500 Subject: [PATCH 163/230] Update caption_multimodal_model And fix optgroup typo --- public/index.html | 2 +- public/scripts/extensions/caption/settings.html | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index 499d5665c..361cd5299 100644 --- a/public/index.html +++ b/public/index.html @@ -3160,7 +3160,7 @@ - + diff --git a/public/scripts/extensions/caption/settings.html b/public/scripts/extensions/caption/settings.html index dbab7c6ea..815d29360 100644 --- a/public/scripts/extensions/caption/settings.html +++ b/public/scripts/extensions/caption/settings.html @@ -84,8 +84,11 @@ + + + From fc09be75a6adc26f261bcac065fbe600ce0c46ad Mon Sep 17 00:00:00 2001 From: cloak1505 <170299980+cloak1505@users.noreply.github.com> Date: Sat, 26 Apr 2025 17:54:34 -0500 Subject: [PATCH 164/230] Almost done with Google pruning * Put back 1.5-latest * Put back missing flash 002 (same deal about safetySettings like pro 001 vs 002) * Remove dead models and gemma from BLOCK_NONE check --- public/index.html | 11 +++++------ public/scripts/extensions/caption/settings.html | 3 +++ src/endpoints/backends/chat-completions.js | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/public/index.html b/public/index.html index 361cd5299..6853b8c89 100644 --- a/public/index.html +++ b/public/index.html @@ -3153,22 +3153,21 @@ + + + + - - - - - - + diff --git a/public/scripts/extensions/caption/settings.html b/public/scripts/extensions/caption/settings.html index 815d29360..43c143cf5 100644 --- a/public/scripts/extensions/caption/settings.html +++ b/public/scripts/extensions/caption/settings.html @@ -83,8 +83,11 @@ + + + diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index 6c238eccc..4680a20df 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -384,7 +384,7 @@ async function sendMakerSuiteRequest(request, response) { let safetySettings = GEMINI_SAFETY; // These models do not support setting the threshold to OFF at all. - if (['gemini-1.5-pro-001', 'gemini-1.5-flash-001', 'gemini-1.5-flash-8b-exp-0827', 'gemini-1.5-flash-8b-exp-0924', 'gemini-pro', 'gemini-1.0-pro', 'gemini-1.0-pro-001', 'gemma-3-27b-it'].includes(model)) { + if (['gemini-1.5-pro-001', 'gemini-1.5-flash-001', 'gemini-1.5-flash-8b-exp-0827', 'gemini-1.5-flash-8b-exp-0924'].includes(model)) { safetySettings = GEMINI_SAFETY.map(setting => ({ ...setting, threshold: 'BLOCK_NONE' })); } // Interestingly, Gemini 2.0 Flash does support setting the threshold for HARM_CATEGORY_CIVIC_INTEGRITY to OFF. From 3e11a90b3cc29a84d823718bbf14546670078dd6 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 27 Apr 2025 02:26:43 +0300 Subject: [PATCH 165/230] Add message and example counts to itemization templates --- public/script.js | 4 ++++ public/scripts/templates/itemizationChat.html | 10 ++++++++-- public/scripts/templates/itemizationText.html | 10 ++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/public/script.js b/public/script.js index 953262042..c600cb5f3 100644 --- a/public/script.js +++ b/public/script.js @@ -4864,6 +4864,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro userPersona: (power_user.persona_description_position == persona_description_positions.IN_PROMPT ? (persona || '') : ''), tokenizer: getFriendlyTokenizerName(main_api).tokenizerName || '', presetName: getPresetManager()?.getSelectedPresetName() || '', + messagesCount: main_api !== 'openai' ? mesSend.length : oaiMessages.length, + examplesCount: main_api !== 'openai' ? (pinExmString ? mesExamplesArray.length : count_exm_add) : oaiMessageExamples.length, }; //console.log(additionalPromptStuff); @@ -5547,6 +5549,8 @@ export async function itemizedParams(itemizedPrompts, thisPromptSet, incomingMes modelUsed: chat[incomingMesId]?.extra?.model, apiUsed: chat[incomingMesId]?.extra?.api, presetName: itemizedPrompts[thisPromptSet].presetName || t`(Unknown)`, + messagesCount: String(itemizedPrompts[thisPromptSet].messagesCount ?? ''), + examplesCount: String(itemizedPrompts[thisPromptSet].examplesCount ?? ''), }; const getFriendlyName = (value) => $(`#rm_api_block select option[value="${value}"]`).first().text() || value; diff --git a/public/scripts/templates/itemizationChat.html b/public/scripts/templates/itemizationChat.html index f78079ee6..bbd7927c3 100644 --- a/public/scripts/templates/itemizationChat.html +++ b/public/scripts/templates/itemizationChat.html @@ -83,7 +83,10 @@
{{scenarioTextTokens}}
-
-- Examples:
+
+ -- Examples: + {{#if examplesCount}}({{examplesCount}}){{/if}} +
{{examplesStringTokens}}
@@ -96,7 +99,10 @@
{{worldInfoStringTokens}}
-
Chat History:
+
+ Chat History: + {{#if messagesCount}}({{messagesCount}}){{/if}} +
{{ActualChatHistoryTokens}}
diff --git a/public/scripts/templates/itemizationText.html b/public/scripts/templates/itemizationText.html index 3b9c31296..b8b90f2d2 100644 --- a/public/scripts/templates/itemizationText.html +++ b/public/scripts/templates/itemizationText.html @@ -51,7 +51,10 @@
{{scenarioTextTokens}}
-
-- Examples:
+
+ -- Examples: + {{#if examplesCount}}({{examplesCount}}){{/if}} +
{{examplesStringTokens}}
@@ -68,7 +71,10 @@
{{worldInfoStringTokens}}
-
Chat History:
+
+ Chat History: + {{#if messagesCount}}({{messagesCount}}){{/if}} +
{{ActualChatHistoryTokens}}
From 340be02777311339d95caeea3d7a3a7247908dd2 Mon Sep 17 00:00:00 2001 From: cloak1505 <170299980+cloak1505@users.noreply.github.com> Date: Sat, 26 Apr 2025 18:43:56 -0500 Subject: [PATCH 166/230] Default HARM_CATEGORY_CIVIC_INTEGRITY to OFF All models support either all BLOCK_NONE, or all OFF. --- src/constants.js | 2 +- src/endpoints/backends/chat-completions.js | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/constants.js b/src/constants.js index 64b9330b2..3b18e732f 100644 --- a/src/constants.js +++ b/src/constants.js @@ -156,7 +156,7 @@ export const GEMINI_SAFETY = [ }, { category: 'HARM_CATEGORY_CIVIC_INTEGRITY', - threshold: 'BLOCK_NONE', + threshold: 'OFF', }, ]; diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index 4680a20df..4809ae8ac 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -387,11 +387,6 @@ async function sendMakerSuiteRequest(request, response) { if (['gemini-1.5-pro-001', 'gemini-1.5-flash-001', 'gemini-1.5-flash-8b-exp-0827', 'gemini-1.5-flash-8b-exp-0924'].includes(model)) { safetySettings = GEMINI_SAFETY.map(setting => ({ ...setting, threshold: 'BLOCK_NONE' })); } - // Interestingly, Gemini 2.0 Flash does support setting the threshold for HARM_CATEGORY_CIVIC_INTEGRITY to OFF. - else if (['gemini-2.0-flash', 'gemini-2.0-flash-001', 'gemini-2.0-flash-exp', 'gemini-2.0-flash-exp-image-generation'].includes(model)) { - safetySettings = GEMINI_SAFETY.map(setting => ({ ...setting, threshold: 'OFF' })); - } - // Most of the other models allow for setting the threshold of filters, except for HARM_CATEGORY_CIVIC_INTEGRITY, to OFF. if (enableWebSearch && !useMultiModal && !isGemma && !isLearnLM && !model.includes('gemini-2.0-flash-lite')) { const searchTool = model.includes('1.5') || model.includes('1.0') From 4599797baf9a31f1b3db4d5d89722f617fb31353 Mon Sep 17 00:00:00 2001 From: cloak1505 <170299980+cloak1505@users.noreply.github.com> Date: Sat, 26 Apr 2025 20:04:50 -0500 Subject: [PATCH 167/230] Revert responsive Google models per Cohee's executive order I was wrong on a few models. At least Gemini still fits on a 1440p monitor. --- public/index.html | 21 ++++++++++++++++++- .../scripts/extensions/caption/settings.html | 14 +++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index 6853b8c89..705045ca5 100644 --- a/public/index.html +++ b/public/index.html @@ -3146,20 +3146,39 @@

Google Model

- - Match Persona Description - - + - + + + + From d8bc38c0b05a0ffa4bd3fd5ca181423bf09877ce Mon Sep 17 00:00:00 2001 From: cloak1505 <170299980+cloak1505@users.noreply.github.com> Date: Sun, 27 Apr 2025 12:31:19 -0500 Subject: [PATCH 179/230] Make the redirection note slightly less eye bleeding --- public/index.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/index.html b/public/index.html index 705045ca5..4f57d80b4 100644 --- a/public/index.html +++ b/public/index.html @@ -3152,16 +3152,16 @@ - - - + + + - - - + + + From 0f8b6104547bf7262a51e0505debeff535d7eb9a Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 27 Apr 2025 20:36:14 +0300 Subject: [PATCH 180/230] Prettify captioning model redirects --- public/scripts/extensions/caption/settings.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/scripts/extensions/caption/settings.html b/public/scripts/extensions/caption/settings.html index ad0e0cf85..8b767ca13 100644 --- a/public/scripts/extensions/caption/settings.html +++ b/public/scripts/extensions/caption/settings.html @@ -80,16 +80,16 @@ - - - + + + - - - + + + From 05daddb60cc85bdd4e8d23807d1d3f10b36357c7 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 27 Apr 2025 23:18:39 +0300 Subject: [PATCH 181/230] Fix KoboldCpp saved vectors retrieval --- public/scripts/extensions/vectors/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/scripts/extensions/vectors/index.js b/public/scripts/extensions/vectors/index.js index 491c4eb95..4d238ff4e 100644 --- a/public/scripts/extensions/vectors/index.js +++ b/public/scripts/extensions/vectors/index.js @@ -829,11 +829,12 @@ async function getAdditionalArgs(items) { * @returns {Promise} Saved hashes */ async function getSavedHashes(collectionId) { + const args = await getAdditionalArgs([]); const response = await fetch('/api/vector/list', { method: 'POST', headers: getRequestHeaders(), body: JSON.stringify({ - ...getVectorsRequestBody(), + ...getVectorsRequestBody(args), collectionId: collectionId, source: settings.source, }), @@ -1155,6 +1156,9 @@ function loadWebLlmModels() { * @returns {Promise>} Calculated embeddings */ async function createWebLlmEmbeddings(items) { + if (items.length === 0) { + return /** @type {Record} */ ({}); + } return executeWithWebLlmErrorHandling(async () => { const embeddings = await webllmProvider.embedTexts(items, settings.webllm_model); const result = /** @type {Record} */ ({}); From 97e1f482c11ee33abafd90ddd1b11ba3d7910a49 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 27 Apr 2025 23:19:21 +0300 Subject: [PATCH 182/230] Add class to AdvancedFormatting --- public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index 3e9416a8b..6f1dfa016 100644 --- a/public/index.html +++ b/public/index.html @@ -3503,7 +3503,7 @@
-
+

From 775ae0f5570e2adda3f986a383e62113a3b1213d Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Mon, 28 Apr 2025 00:14:57 +0300 Subject: [PATCH 183/230] TC sysprompt: Add Post-History Instructions control Closes #3920 --- default/content/presets/context/Adventure.json | 1 - .../presets/context/Alpaca-Single-Turn.json | 1 - default/content/presets/context/Alpaca.json | 1 - .../content/presets/context/ChatML-Names.json | 1 - default/content/presets/context/ChatML.json | 1 - default/content/presets/context/Command R.json | 1 - .../content/presets/context/DeepSeek-V2.5.json | 1 - default/content/presets/context/Default.json | 1 - .../context/DreamGen Role-Play V1 ChatML.json | 1 - .../context/DreamGen Role-Play V1 Llama3.json | 1 - default/content/presets/context/GLM-4.json | 1 - default/content/presets/context/Gemma 2.json | 1 - default/content/presets/context/Libra-32B.json | 1 - .../content/presets/context/Lightning 1.1.json | 1 - .../content/presets/context/Llama 2 Chat.json | 1 - .../presets/context/Llama 3 Instruct.json | 1 - .../presets/context/Llama 4 Instruct.json | 1 - .../context/Llama-3-Instruct-Names.json | 1 - .../content/presets/context/Minimalist.json | 1 - .../content/presets/context/Mistral V1.json | 1 - .../presets/context/Mistral V2 & V3.json | 1 - .../presets/context/Mistral V3-Tekken.json | 1 - .../content/presets/context/Mistral V7.json | 1 - default/content/presets/context/NovelAI.json | 1 - .../content/presets/context/OldDefault.json | 1 - default/content/presets/context/Phi.json | 1 - default/content/presets/context/Pygmalion.json | 1 - default/content/presets/context/Story.json | 1 - default/content/presets/context/Synthia.json | 1 - default/content/presets/context/Tulu.json | 1 - .../context/simple-proxy-for-tavern.json | 1 - public/index.html | 17 ++++++++--------- public/locales/ar-sa.json | 1 - public/locales/de-de.json | 1 - public/locales/es-es.json | 1 - public/locales/is-is.json | 1 - public/locales/it-it.json | 1 - public/locales/ja-jp.json | 1 - public/locales/ko-kr.json | 1 - public/locales/nl-nl.json | 1 - public/locales/pt-pt.json | 1 - public/locales/ru-ru.json | 1 - public/locales/uk-ua.json | 1 - public/locales/vi-vn.json | 1 - public/locales/zh-tw.json | 1 - public/script.js | 12 +++++++----- public/scripts/power-user.js | 3 +-- public/scripts/sysprompt.js | 18 +++++++++++++++--- 48 files changed, 31 insertions(+), 63 deletions(-) diff --git a/default/content/presets/context/Adventure.json b/default/content/presets/context/Adventure.json index 03ad99837..9f4f9d858 100644 --- a/default/content/presets/context/Adventure.json +++ b/default/content/presets/context/Adventure.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": false, "trim_sentences": false, "single_line": true, diff --git a/default/content/presets/context/Alpaca-Single-Turn.json b/default/content/presets/context/Alpaca-Single-Turn.json index ea0b08816..496ac8322 100644 --- a/default/content/presets/context/Alpaca-Single-Turn.json +++ b/default/content/presets/context/Alpaca-Single-Turn.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": false, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Alpaca.json b/default/content/presets/context/Alpaca.json index 0432976f1..bc3ad8fd7 100644 --- a/default/content/presets/context/Alpaca.json +++ b/default/content/presets/context/Alpaca.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/ChatML-Names.json b/default/content/presets/context/ChatML-Names.json index 8aa30b516..82719085b 100644 --- a/default/content/presets/context/ChatML-Names.json +++ b/default/content/presets/context/ChatML-Names.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/ChatML.json b/default/content/presets/context/ChatML.json index 4b8610b7a..118c8d234 100644 --- a/default/content/presets/context/ChatML.json +++ b/default/content/presets/context/ChatML.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Command R.json b/default/content/presets/context/Command R.json index 01410e517..28d0cc8b1 100644 --- a/default/content/presets/context/Command R.json +++ b/default/content/presets/context/Command R.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "<|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|>New Roleplay:<|END_OF_TURN_TOKEN|>", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/DeepSeek-V2.5.json b/default/content/presets/context/DeepSeek-V2.5.json index 49efaba59..9a00bab27 100644 --- a/default/content/presets/context/DeepSeek-V2.5.json +++ b/default/content/presets/context/DeepSeek-V2.5.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Default.json b/default/content/presets/context/Default.json index de84def35..817dd60fa 100644 --- a/default/content/presets/context/Default.json +++ b/default/content/presets/context/Default.json @@ -3,7 +3,6 @@ "example_separator": "***", "chat_start": "***", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/DreamGen Role-Play V1 ChatML.json b/default/content/presets/context/DreamGen Role-Play V1 ChatML.json index 6b2d8bc61..c16cab917 100644 --- a/default/content/presets/context/DreamGen Role-Play V1 ChatML.json +++ b/default/content/presets/context/DreamGen Role-Play V1 ChatML.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": false, "trim_sentences": true, "single_line": false, diff --git a/default/content/presets/context/DreamGen Role-Play V1 Llama3.json b/default/content/presets/context/DreamGen Role-Play V1 Llama3.json index aa51e64f8..22991883b 100644 --- a/default/content/presets/context/DreamGen Role-Play V1 Llama3.json +++ b/default/content/presets/context/DreamGen Role-Play V1 Llama3.json @@ -3,7 +3,6 @@ "example_separator": "<|eot_id|>\n<|start_header_id|>user<|end_header_id|>\n\nWrite an example narrative / conversation that is not part of the main story.", "chat_start": "<|eot_id|>\n<|start_header_id|>user<|end_header_id|>\n\nStart the role-play between {{char}} and {{user}}.", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": false, "trim_sentences": true, "single_line": false, diff --git a/default/content/presets/context/GLM-4.json b/default/content/presets/context/GLM-4.json index e958deaca..e9dfb2776 100644 --- a/default/content/presets/context/GLM-4.json +++ b/default/content/presets/context/GLM-4.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Gemma 2.json b/default/content/presets/context/Gemma 2.json index 768346957..1b3dd4ddc 100644 --- a/default/content/presets/context/Gemma 2.json +++ b/default/content/presets/context/Gemma 2.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Libra-32B.json b/default/content/presets/context/Libra-32B.json index 974391be6..370f4a0e8 100644 --- a/default/content/presets/context/Libra-32B.json +++ b/default/content/presets/context/Libra-32B.json @@ -3,7 +3,6 @@ "example_separator": "### Example:", "chat_start": "### START ROLEPLAY:", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Lightning 1.1.json b/default/content/presets/context/Lightning 1.1.json index 8aff8abc1..0551ed136 100644 --- a/default/content/presets/context/Lightning 1.1.json +++ b/default/content/presets/context/Lightning 1.1.json @@ -3,7 +3,6 @@ "example_separator": "Example of an interaction:\n", "chat_start": "This is the history of the roleplay:\n", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Llama 2 Chat.json b/default/content/presets/context/Llama 2 Chat.json index d0cd16ca0..a5623791c 100644 --- a/default/content/presets/context/Llama 2 Chat.json +++ b/default/content/presets/context/Llama 2 Chat.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Llama 3 Instruct.json b/default/content/presets/context/Llama 3 Instruct.json index cbc155504..9808b0a75 100644 --- a/default/content/presets/context/Llama 3 Instruct.json +++ b/default/content/presets/context/Llama 3 Instruct.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Llama 4 Instruct.json b/default/content/presets/context/Llama 4 Instruct.json index e1be872e7..9d145295e 100644 --- a/default/content/presets/context/Llama 4 Instruct.json +++ b/default/content/presets/context/Llama 4 Instruct.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Llama-3-Instruct-Names.json b/default/content/presets/context/Llama-3-Instruct-Names.json index a1815e02c..74e9bee89 100644 --- a/default/content/presets/context/Llama-3-Instruct-Names.json +++ b/default/content/presets/context/Llama-3-Instruct-Names.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Minimalist.json b/default/content/presets/context/Minimalist.json index 107da83b3..cfcf3fddd 100644 --- a/default/content/presets/context/Minimalist.json +++ b/default/content/presets/context/Minimalist.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Mistral V1.json b/default/content/presets/context/Mistral V1.json index 93d037941..5a806b518 100644 --- a/default/content/presets/context/Mistral V1.json +++ b/default/content/presets/context/Mistral V1.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Mistral V2 & V3.json b/default/content/presets/context/Mistral V2 & V3.json index bc41b01a1..460e05665 100644 --- a/default/content/presets/context/Mistral V2 & V3.json +++ b/default/content/presets/context/Mistral V2 & V3.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Mistral V3-Tekken.json b/default/content/presets/context/Mistral V3-Tekken.json index e5b211606..554cbe94a 100644 --- a/default/content/presets/context/Mistral V3-Tekken.json +++ b/default/content/presets/context/Mistral V3-Tekken.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Mistral V7.json b/default/content/presets/context/Mistral V7.json index 519d5d152..e469af131 100644 --- a/default/content/presets/context/Mistral V7.json +++ b/default/content/presets/context/Mistral V7.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/NovelAI.json b/default/content/presets/context/NovelAI.json index 8914a953d..a72053301 100644 --- a/default/content/presets/context/NovelAI.json +++ b/default/content/presets/context/NovelAI.json @@ -3,7 +3,6 @@ "example_separator": "***", "chat_start": "***", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/OldDefault.json b/default/content/presets/context/OldDefault.json index 81dd70911..3c9a43b8a 100644 --- a/default/content/presets/context/OldDefault.json +++ b/default/content/presets/context/OldDefault.json @@ -3,7 +3,6 @@ "example_separator": "This is how {{char}} should talk", "chat_start": "\nThen the roleplay chat between {{user}} and {{char}} begins.\n", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Phi.json b/default/content/presets/context/Phi.json index 79a27ada9..b67893891 100644 --- a/default/content/presets/context/Phi.json +++ b/default/content/presets/context/Phi.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Pygmalion.json b/default/content/presets/context/Pygmalion.json index eab73535c..38a81adc6 100644 --- a/default/content/presets/context/Pygmalion.json +++ b/default/content/presets/context/Pygmalion.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Story.json b/default/content/presets/context/Story.json index 525a06dc0..63d190bf1 100644 --- a/default/content/presets/context/Story.json +++ b/default/content/presets/context/Story.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Synthia.json b/default/content/presets/context/Synthia.json index 1fb639c0b..e8939343a 100644 --- a/default/content/presets/context/Synthia.json +++ b/default/content/presets/context/Synthia.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/Tulu.json b/default/content/presets/context/Tulu.json index af3c1f9cf..b5f64664e 100644 --- a/default/content/presets/context/Tulu.json +++ b/default/content/presets/context/Tulu.json @@ -3,7 +3,6 @@ "example_separator": "", "chat_start": "", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/default/content/presets/context/simple-proxy-for-tavern.json b/default/content/presets/context/simple-proxy-for-tavern.json index 0346c2f32..d5597c520 100644 --- a/default/content/presets/context/simple-proxy-for-tavern.json +++ b/default/content/presets/context/simple-proxy-for-tavern.json @@ -3,7 +3,6 @@ "example_separator": "### New Roleplay:", "chat_start": "### New Roleplay:", "use_stop_strings": false, - "allow_jailbreak": false, "always_force_name2": true, "trim_sentences": false, "single_line": false, diff --git a/public/index.html b/public/index.html index 6f1dfa016..d1f90aef3 100644 --- a/public/index.html +++ b/public/index.html @@ -3622,11 +3622,6 @@ Names as Stop Strings

- -
@@ -3816,7 +3811,7 @@
-
+

System Prompt @@ -3852,10 +3847,14 @@
-

-
-   +
+ + +
diff --git a/public/locales/ar-sa.json b/public/locales/ar-sa.json index 152545df6..f316a8c73 100644 --- a/public/locales/ar-sa.json +++ b/public/locales/ar-sa.json @@ -411,7 +411,6 @@ "Chat Start": "بداية الدردشة", "Add Chat Start and Example Separator to a list of stopping strings.": "أضف بداية الدردشة وفاصل الأمثلة إلى قائمة سلاسل التوقف.", "Use as Stop Strings": "استخدم كسلاسل التوقف", - "context_allow_jailbreak": "يتضمن كسر الحماية في نهاية المطالبة، إذا تم تحديده في بطاقة الشخصية و''Prefer Char. تم تمكين الهروب من السجن.\nلا يُنصح بهذا بالنسبة لنماذج إكمال النص، فقد يؤدي إلى نتائج سيئة.", "Allow Jailbreak": "السماح بالجيلبريك", "Context Order": "ترتيب السياق", "Summary": "ملخص", diff --git a/public/locales/de-de.json b/public/locales/de-de.json index 5ceabc9d0..d66a85c59 100644 --- a/public/locales/de-de.json +++ b/public/locales/de-de.json @@ -411,7 +411,6 @@ "Chat Start": "Chat-Start", "Add Chat Start and Example Separator to a list of stopping strings.": "Fügen Sie einer Liste von Stoppzeichenfolgen „Chat-Start“ und „Beispieltrennzeichen“ hinzu.", "Use as Stop Strings": "Verwende als Stoppzeichenfolgen", - "context_allow_jailbreak": "Schließt Jailbreak am Ende der Eingabeaufforderung ein, wenn dies in der Charakterkarte definiert ist UND „Charakter-Jailbreak bevorzugen“ aktiviert ist.\nDIES WIRD FÜR TEXTVERVOLLSTÄNDIGUNGSMODELLE NICHT EMPFOHLEN, KANN ZU SCHLECHTEN AUSGABEN FÜHREN.", "Allow Jailbreak": "Jailbreak zulassen", "Context Order": "Kontextreihenfolge", "Summary": "Zusammenfassung", diff --git a/public/locales/es-es.json b/public/locales/es-es.json index af598b9ae..fa0ef34bd 100644 --- a/public/locales/es-es.json +++ b/public/locales/es-es.json @@ -411,7 +411,6 @@ "Chat Start": "Inicio de chat", "Add Chat Start and Example Separator to a list of stopping strings.": "Agregue Inicio de chat y Separador de ejemplo a una lista de cadenas de parada.", "Use as Stop Strings": "Usar como Cadenas de Parada", - "context_allow_jailbreak": "Incluye Jailbreak al final del mensaje, si está definido en la tarjeta de personaje Y está habilitado \"Prefer Char. Jailbreak\".\nESTO NO SE RECOMIENDA PARA MODELOS DE COMPLETO DE TEXTO, PUEDE PRODUCIR UN RESULTADO INCORRECTO.", "Allow Jailbreak": "Permitir Jailbreak", "Context Order": "Orden de contexto", "Summary": "Resumen", diff --git a/public/locales/is-is.json b/public/locales/is-is.json index 009b22bd6..7d13d3804 100644 --- a/public/locales/is-is.json +++ b/public/locales/is-is.json @@ -411,7 +411,6 @@ "Chat Start": "Chat Start", "Add Chat Start and Example Separator to a list of stopping strings.": "Bættu Chat Start og Example Separator við lista yfir stöðvunarstrengi.", "Use as Stop Strings": "Nota sem Stoppa Strengir", - "context_allow_jailbreak": "Inniheldur Jailbreak í lok hvetjunnar, ef það er skilgreint á stafkortinu OG ''Velst Char. Jailbreak'' er virkt.\nÞETTA ER EKKI MÆLT FYRIR TEXTAÚRSLUNARGERÐ, GETUR leitt til lélegrar úttaks.", "Allow Jailbreak": "Leyfa jailbreak", "Context Order": "Samhengisröð", "Summary": "Samantekt", diff --git a/public/locales/it-it.json b/public/locales/it-it.json index bd8f4b9e0..bc648399a 100644 --- a/public/locales/it-it.json +++ b/public/locales/it-it.json @@ -411,7 +411,6 @@ "Chat Start": "Inizio chat", "Add Chat Start and Example Separator to a list of stopping strings.": "Aggiungi Inizio chat e Separatore di esempio a un elenco di stringhe di arresto.", "Use as Stop Strings": "Usa come stringhe di arresto", - "context_allow_jailbreak": "Include il jailbreak alla fine del prompt, se definito nella carta personaggio E ''Preferisci Char. Il jailbreak'' è abilitato.\nQUESTO NON È CONSIGLIATO PER I MODELLI DI COMPLETAMENTO DEL TESTO, PUÒ PORTARE A UN RISULTATO CATTIVO.", "Allow Jailbreak": "Consenti jailbreak", "Context Order": "Ordine del contesto", "Summary": "Riepilogo", diff --git a/public/locales/ja-jp.json b/public/locales/ja-jp.json index 000ac838d..2ba4bfeb3 100644 --- a/public/locales/ja-jp.json +++ b/public/locales/ja-jp.json @@ -411,7 +411,6 @@ "Chat Start": "チャット開始", "Add Chat Start and Example Separator to a list of stopping strings.": "停止文字列のリストにチャット開始と例の区切り文字を追加します。", "Use as Stop Strings": "ストップ文字列として使用", - "context_allow_jailbreak": "文字カードで定義されていて、「文字 Jailbreak を優先」が有効になっている場合は、プロンプトの最後に Jailbreak が含まれます。\nこれはテキスト補完モデルには推奨されません。出力が悪くなる可能性があります。", "Allow Jailbreak": "脱獄を許可する", "Context Order": "コンテキスト順序", "Summary": "まとめ", diff --git a/public/locales/ko-kr.json b/public/locales/ko-kr.json index d4e881143..0a41055a4 100644 --- a/public/locales/ko-kr.json +++ b/public/locales/ko-kr.json @@ -421,7 +421,6 @@ "Chat Start": "채팅 시작", "Add Chat Start and Example Separator to a list of stopping strings.": "중지 문자열 목록에 채팅 시작 및 예제 구분 기호를 추가합니다.", "Use as Stop Strings": "중지 문자열로 사용", - "context_allow_jailbreak": "캐릭터 카드에 정의되어 있고 ''Prefer Char. Jailbreak''가 활성화되어 있는 경우 프롬프트 끝에 Jailbreak를 포함합니다.\n이는 텍스트 완성 모델에 권장되지 않으며, 나쁜 출력으로 이어질 수 있습니다.", "Allow Jailbreak": "탈옥 허용", "Context Order": "컨텍스트 순서", "Summary": "요약", diff --git a/public/locales/nl-nl.json b/public/locales/nl-nl.json index e3305c9ac..75d1b1281 100644 --- a/public/locales/nl-nl.json +++ b/public/locales/nl-nl.json @@ -411,7 +411,6 @@ "Chat Start": "Chatstart", "Add Chat Start and Example Separator to a list of stopping strings.": "Voeg Chat Start en Voorbeeldscheidingsteken toe aan een lijst met stoptekenreeksen.", "Use as Stop Strings": "Gebruik als stopreeksen", - "context_allow_jailbreak": "Inclusief jailbreak aan het einde van de prompt, indien gedefinieerd in de karakterkaart EN ''Prefer Char. Jailbreak'' is ingeschakeld.\nDIT WORDT NIET AANBEVOLEN VOOR MODELLEN VOOR HET INVOEREN VAN TEKST. KAN TOT SLECHTE UITVOER LEIDEN.", "Allow Jailbreak": "Jailbreak toestaan", "Context Order": "Contextvolgorde", "Summary": "Samenvatting", diff --git a/public/locales/pt-pt.json b/public/locales/pt-pt.json index d61cc3601..14ec2f0d1 100644 --- a/public/locales/pt-pt.json +++ b/public/locales/pt-pt.json @@ -411,7 +411,6 @@ "Chat Start": "Início do Chat", "Add Chat Start and Example Separator to a list of stopping strings.": "Adicione o início do bate-papo e o separador de exemplo a uma lista de strings de parada.", "Use as Stop Strings": "Usar como Strings de Parada", - "context_allow_jailbreak": "Inclui Jailbreak no final do prompt, se definido no cartão de personagem E ''Prefer Char. Jailbreak'' está habilitado.\nISTO NÃO É RECOMENDADO PARA MODELOS DE COMPLEMENTAÇÃO DE TEXTO, PODE LEVAR A UMA SAÍDA RUIM.", "Allow Jailbreak": "Permitir jailbreak", "Context Order": "Ordem de Contexto", "Summary": "Resumo", diff --git a/public/locales/ru-ru.json b/public/locales/ru-ru.json index 00e075a68..1bb476ea3 100644 --- a/public/locales/ru-ru.json +++ b/public/locales/ru-ru.json @@ -1257,7 +1257,6 @@ "Peek a password": "Посмотреть пароль", "Clear your cookie": "Clear your cookie", "Add Chat Start and Example Separator to a list of stopping strings.": "Использовать Начало чата и Разделитель примеров сообщений в качестве стоп-строк.", - "context_allow_jailbreak": "Если в карточке есть джейлбрейк И ПРИ ЭТОМ включена опция \"Приоритет джейлбрейку из карточки персонажа\", то этот джейлбрейк добавляется в конец промпта.\nНЕ РЕКОМЕНДУЕТСЯ ДЛЯ МОДЕЛЕЙ TEXT COMPLETION, МОЖЕТ ПОРТИТЬ ВЫХОДНОЙ ТЕКСТ.", "Context Order": "Context Order", "Summary": "Summary", "Example Dialogues": "Примеры диалогов", diff --git a/public/locales/uk-ua.json b/public/locales/uk-ua.json index c0d3ceb5a..72e139404 100644 --- a/public/locales/uk-ua.json +++ b/public/locales/uk-ua.json @@ -411,7 +411,6 @@ "Chat Start": "Початок чату", "Add Chat Start and Example Separator to a list of stopping strings.": "Додайте початок чату та роздільник прикладів до списку рядків зупинки.", "Use as Stop Strings": "Використовувати як рядки зупинки", - "context_allow_jailbreak": "Включає втечу з в’язниці в кінці підказки, якщо визначено в картці символів ТА «Переважати символ. Втечу з в'язниці'' увімкнено.\nЦЕ НЕ РЕКОМЕНДУЄТЬСЯ ДЛЯ МОДЕЛЕЙ ЗАВЕРШЕННЯ ТЕКСТУ, МОЖЕ ПРИЗВЕСТИ ДО ПОГАНОГО РЕЗУЛЬТАТУ.", "Allow Jailbreak": "Дозволити втечу з в'язниці", "Context Order": "Порядок контексту", "Summary": "Резюме", diff --git a/public/locales/vi-vn.json b/public/locales/vi-vn.json index 11c30c557..62342ad72 100644 --- a/public/locales/vi-vn.json +++ b/public/locales/vi-vn.json @@ -411,7 +411,6 @@ "Chat Start": "Bắt đầu Chat", "Add Chat Start and Example Separator to a list of stopping strings.": "Thêm Bắt đầu trò chuyện và Dấu phân cách ví dụ vào danh sách các chuỗi dừng.", "Use as Stop Strings": "Sử dụng như chuỗi dừng", - "context_allow_jailbreak": "Bao gồm Bẻ khóa ở cuối Prompt, nếu được xác định trong thẻ ký tự VÀ ''Thích Char. Bẻ khóa'' được bật.\nĐIỀU NÀY KHÔNG ĐƯỢC KHUYẾN NGHỊ CHO CÁC MÔ HÌNH HOÀN THÀNH VĂN BẢN, CÓ THỂ DẪN ĐẾN ĐẦU RA XẤU.", "Allow Jailbreak": "Cho phép bẻ khóa", "Context Order": "Thứ tự bối cảnh", "Summary": "Bản tóm tắt", diff --git a/public/locales/zh-tw.json b/public/locales/zh-tw.json index 28e1ee7e1..da32d8957 100644 --- a/public/locales/zh-tw.json +++ b/public/locales/zh-tw.json @@ -412,7 +412,6 @@ "Chat Start": "聊天開始符號", "Add Chat Start and Example Separator to a list of stopping strings.": "將聊天開始和範例分隔符號加入終止字串中。", "Use as Stop Strings": "用作停止字串", - "context_allow_jailbreak": "如果在角色卡中定義了越獄,且啟用了「角色卡越獄優先」,則會在提示詞的結尾加入越獄內容。\n這不建議用於文字完成模型,因為可能導致不良的輸出結果。", "Allow Jailbreak": "允許越獄", "Context Order": "上下文順序", "Summary": "摘要", diff --git a/public/script.js b/public/script.js index 8421305f4..0afd957fb 100644 --- a/public/script.js +++ b/public/script.js @@ -4000,7 +4000,9 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro if (main_api !== 'openai') { if (power_user.sysprompt.enabled) { - system = power_user.prefer_character_prompt && system ? system : baseChatReplace(power_user.sysprompt.content, name1, name2); + system = power_user.prefer_character_prompt && system + ? substituteParams(system, name1, name2, (power_user.sysprompt.content ?? '')) + : baseChatReplace(power_user.sysprompt.content, name1, name2); system = isInstruct ? formatInstructModeSystemPrompt(substituteParams(system, name1, name2, power_user.sysprompt.content)) : system; } else { // Nullify if it's not enabled @@ -4207,10 +4209,10 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro injectedIndices = await doChatInject(coreChat, isContinue); } - // Insert character jailbreak as the last user message (if exists, allowed, preferred, and not using Chat Completion) - if (power_user.context.allow_jailbreak && power_user.prefer_character_jailbreak && main_api !== 'openai' && jailbreak) { - // Set "original" explicity to empty string since there's no original - jailbreak = substituteParams(jailbreak, name1, name2, ''); + if (main_api !== 'openai' && power_user.sysprompt.enabled) { + jailbreak = power_user.prefer_character_jailbreak && jailbreak + ? substituteParams(jailbreak, name1, name2, (power_user.sysprompt.post_history ?? '')) + : baseChatReplace(power_user.sysprompt.post_history, name1, name2); // When continuing generation of previous output, last user message precedes the message to continue if (isContinue) { diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index 570ddc655..cf5d7a145 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -244,7 +244,6 @@ let power_user = { chat_start: defaultChatStart, example_separator: defaultExampleSeparator, use_stop_strings: true, - allow_jailbreak: false, names_as_stop_strings: true, }, @@ -255,6 +254,7 @@ let power_user = { enabled: true, name: 'Neutral - Chat', content: 'Write {{char}}\'s next reply in a fictional chat between {{char}} and {{user}}.', + post_history: '', }, reasoning: { @@ -334,7 +334,6 @@ const contextControls = [ { id: 'context_example_separator', property: 'example_separator', isCheckbox: false, isGlobalSetting: false }, { id: 'context_chat_start', property: 'chat_start', isCheckbox: false, isGlobalSetting: false }, { id: 'context_use_stop_strings', property: 'use_stop_strings', isCheckbox: true, isGlobalSetting: false, defaultValue: false }, - { id: 'context_allow_jailbreak', property: 'allow_jailbreak', isCheckbox: true, isGlobalSetting: false, defaultValue: false }, { id: 'context_names_as_stop_strings', property: 'names_as_stop_strings', isCheckbox: true, isGlobalSetting: false, defaultValue: true }, // Existing power user settings diff --git a/public/scripts/sysprompt.js b/public/scripts/sysprompt.js index 37471c7cd..9b26c9723 100644 --- a/public/scripts/sysprompt.js +++ b/public/scripts/sysprompt.js @@ -17,6 +17,7 @@ export let system_prompts = []; const $enabled = $('#sysprompt_enabled'); const $select = $('#sysprompt_select'); const $content = $('#sysprompt_content'); +const $postHistory = $('#sysprompt_post_history'); const $contentBlock = $('#SystemPromptBlock'); async function migrateSystemPromptFromInstructMode() { @@ -25,6 +26,7 @@ async function migrateSystemPromptFromInstructMode() { delete power_user.instruct.system_prompt; power_user.sysprompt.enabled = power_user.instruct.enabled; power_user.sysprompt.content = prompt; + power_user.sysprompt.post_history = ''; const existingPromptName = system_prompts.find(x => x.content === prompt)?.name; @@ -59,7 +61,8 @@ export async function loadSystemPrompts(data) { $enabled.prop('checked', power_user.sysprompt.enabled); $select.val(power_user.sysprompt.name); - $content.val(power_user.sysprompt.content); + $content.val(power_user.sysprompt.content || ''); + $postHistory.val(power_user.sysprompt.post_history || ''); if (!CSS.supports('field-sizing', 'content')) { await resetScrollHeight($content); } @@ -165,13 +168,17 @@ export function initSystemPrompts() { const name = String($(this).val()); const prompt = system_prompts.find(p => p.name === name); if (prompt) { - $content.val(prompt.content); + $content.val(prompt.content || ''); + $postHistory.val(prompt.post_history || ''); + if (!CSS.supports('field-sizing', 'content')) { await resetScrollHeight($content); + await resetScrollHeight($postHistory); } power_user.sysprompt.name = name; - power_user.sysprompt.content = prompt.content; + power_user.sysprompt.content = prompt.content || ''; + power_user.sysprompt.post_history = prompt.post_history || ''; } saveSettingsDebounced(); }); @@ -181,6 +188,11 @@ export function initSystemPrompts() { saveSettingsDebounced(); }); + $postHistory.on('input', function () { + power_user.sysprompt.post_history = String($(this).val()); + saveSettingsDebounced(); + }); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sysprompt', aliases: ['system-prompt'], From d5002863e0863d5523c435a071cb3492c62c8f5f Mon Sep 17 00:00:00 2001 From: Crow Date: Mon, 28 Apr 2025 11:03:27 +0100 Subject: [PATCH 184/230] Add both `test` and `match` regex commands These commands match the behavior of the javascript `Regex.test()` and `String.match()` / `String.matchAll()` functions. --- public/scripts/slash-commands.js | 80 ++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index ae1523039..99810d98b 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -2131,6 +2131,86 @@ export function initDefaultSlashCommands() {
`, })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'test', + callback: (({ pattern }, text) => { + if (pattern === '') { + throw new Error('Argument of \'pattern=\' cannot be empty'); + } + let re = regexFromString(pattern.toString()); + return JSON.stringify(re.test(text.toString())); + }), + returns: 'true | false', + namedArgumentList: [ + new SlashCommandNamedArgument( + 'pattern', 'pattern to find', [ARGUMENT_TYPE.STRING], true, false, + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'text to test', [ARGUMENT_TYPE.STRING], true, false, + ), + ], + helpString: ` +
+ Tests text for a regular expression match. +
+
+ Returns true if the match is found, false otherwise. +
+
+ Example: +
/let x Blue house and green car                         ||
+
/test pattern="green" {{var::x}}    | /echo  |/# true   ||
+
/test pattern="blue" {{var::x}}     | /echo  |/# false  ||
+
/test pattern="/blue/i" {{var::x}}  | /echo  |/# true   ||
+
+ `, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'match', + callback: (({ pattern }, text) => { + if (pattern === '') { + throw new Error('Argument of \'pattern=\' cannot be empty'); + } + let re = regexFromString(pattern.toString()); + if (re.flags.includes('g')) { + return JSON.stringify([...text.toString().matchAll(re)]); + } else { + return JSON.stringify(text.toString().match(re)); + } + }), + returns: 'group array for each match', + namedArgumentList: [ + new SlashCommandNamedArgument( + 'pattern', 'pattern to find', [ARGUMENT_TYPE.STRING], true, false, + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'text to match against', [ARGUMENT_TYPE.STRING], true, false, + ), + ], + helpString: ` +
+ Retrieves regular expression matches in the given text +
+
+ Returns an array of groups (with the first group being the full match). If the regex contains the global flag (i.e. /g), + multiple nested arrays are returned for each match. If the regex is global, returns [] if no matches are found, + otherwise it returns null. +
+
+ Example: +
/let x color_green green lamp color_blue                                                                            ||
+
/match pattern="green" {{var::x}}            | /echo  |/# [ "green" ]                                               ||
+
/match pattern="color_(\\w+)" {{var::x}}     | /echo  |/# [ "color_green", "green" ]                                ||
+
/match pattern="/color_(\\w+)/g" {{var::x}}  | /echo  |/# [ [ "color_green", "green" ], [ "color_blue", "blue" ] ]  ||
+
/match pattern="orange" {{var::x}}           | /echo  |/# null                                                      ||
+
/match pattern="/orange/g" {{var::x}}        | /echo  |/# []                                                        ||
+
+ `, + })); SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'chat-jump', From 11908f7363651bf434cf23c1d8bb39feab12e9ad Mon Sep 17 00:00:00 2001 From: Yokayo Date: Mon, 28 Apr 2025 18:45:16 +0700 Subject: [PATCH 185/230] Work on tl --- public/css/rm-groups.css | 4 +- public/index.html | 16 ++-- public/locales/ru-ru.json | 126 +++++++++++++++++++++++++------ public/script.js | 14 ++-- public/scripts/backgrounds.js | 2 +- public/scripts/group-chats.js | 6 +- public/scripts/horde.js | 6 +- public/scripts/openai.js | 2 +- public/scripts/personas.js | 6 +- public/scripts/tags.js | 11 +-- public/scripts/textgen-models.js | 21 ++++-- public/scripts/utils.js | 34 ++++++++- public/scripts/world-info.js | 4 +- 13 files changed, 191 insertions(+), 61 deletions(-) diff --git a/public/css/rm-groups.css b/public/css/rm-groups.css index 499f59873..6e12164b2 100644 --- a/public/css/rm-groups.css +++ b/public/css/rm-groups.css @@ -87,7 +87,7 @@ } #rm_group_members:empty::before { - content: 'Group is empty'; + content: attr(group_empty_text); font-weight: bolder; width: 100%; @@ -115,7 +115,7 @@ } #rm_group_add_members:empty::before { - content: 'No characters available'; + content: attr(no_characters_text); font-weight: bolder; width: 100%; diff --git a/public/index.html b/public/index.html index 6f1dfa016..3f8cc407f 100644 --- a/public/index.html +++ b/public/index.html @@ -2052,8 +2052,8 @@
@@ -5534,7 +5534,7 @@
-
+
@@ -5552,7 +5552,7 @@
-
+
@@ -5972,7 +5972,7 @@
-
 entries
+
 entries
diff --git a/public/locales/ru-ru.json b/public/locales/ru-ru.json index 00e075a68..730500d77 100644 --- a/public/locales/ru-ru.json +++ b/public/locales/ru-ru.json @@ -52,7 +52,7 @@ "Presence Penalty": "Штраф за присутствие", "Top A": "Top А", "Tail Free Sampling": "Tail Free Sampling", - "Rep. Pen. Slope": "Rep. Pen. Slope", + "Rep. Pen. Slope": "Рост штрафа за повтор к концу промпта", "Top K": "Top K", "Top P": "Top P", "Do Sample": "Включить сэмплинг", @@ -162,9 +162,9 @@ "Story String": "Строка истории", "Example Separator": "Разделитель примеров сообщений", "Chat Start": "Начало чата", - "Activation Regex": "Regex для активации", + "Activation Regex": "Рег. выражение для активации", "Instruct Mode": "Режим Instruct", - "Wrap Sequences with Newline": "Отделять строки символом новой строки", + "Wrap Sequences with Newline": "Каждая строка из шаблона на новой строке", "Include Names": "Добавлять имена", "Force for Groups and Personas": "Также для групп и персон", "System Prompt": "Системный промпт", @@ -299,7 +299,7 @@ "AI Horde": "AI Horde", "NovelAI": "NovelAI", "OpenAI API key": "Ключ для API OpenAI", - "Trim spaces": "Обрезать пробелы", + "Trim spaces": "Обрезать пробелы в начале и конце", "Trim Incomplete Sentences": "Удалять неоконченные предложения", "Include Newline": "Добавлять новую строку", "Non-markdown strings": "Строки без разметки", @@ -510,7 +510,7 @@ "New preset": "Новый пресет", "Delete preset": "Удалить пресет", "API Connections": "Соединения с API", - "Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "Может помочь с плохими ответами ставя в очередь только подтвержденных работников. Может замедлить время ответа.", + "Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "Может помочь при плохих ответах, делая запросы только к доверенным рабочим машинам. Может замедлить время ответа.", "Clear your API key": "Стереть ключ от API", "Refresh models": "Обновить модели", "Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Получите свой OpenRouter API токен используя OAuth. У вас будет открыта вкладка openrouter.ai", @@ -551,7 +551,7 @@ "Token counts may be inaccurate and provided just for reference.": "Счетчик токенов может быть неточным, используйте как ориентир", "Click to select a new avatar for this character": "Нажмите чтобы выбрать новый аватар для этого персонажа", "Example: [{{user}} is a 28-year-old Romanian cat girl.]": "Пример:\n [{{user}} is a 28-year-old Romanian cat girl.]", - "Toggle grid view": "Переключить вид сетки", + "Toggle grid view": "Сменить вид сетки", "Add to Favorites": "Добавить в Избранное", "Advanced Definition": "Расширенное описание", "Character Lore": "Лор персонажа", @@ -624,7 +624,7 @@ "UI Theme": "Тема UI", "This message is invisible for the AI": "Это сообщение невидимо для ИИ", "Sampler Priority": "Приоритет сэмплеров", - "Ooba only. Determines the order of samplers.": "Только oobabooga. Определяет порядок сэмплеров.", + "Ooba only. Determines the order of samplers.": "Только для oobabooga. Определяет порядок сэмплеров.", "Load default order": "Загрузить стандартный порядок", "Max Tokens Second": "Макс. кол-во токенов в секунду", "CFG": "CFG", @@ -695,7 +695,7 @@ "Medium": "Средний", "Aggressive": "Агрессивный", "Very aggressive": "Очень агрессивный", - "Eta_Cutoff_desc": "Eta cutoff - основной параметр специальной техники сэмплинга под названием Eta Sampling. В единицах 1e-4; разумное значение - 3. Установите в 0, чтобы отключить. См. статью Truncation Sampling as Language Model Desmoothing от Хьюитт и др. (2022) для получения подробной информации.", + "Eta_Cutoff_desc": "Eta cutoff - основной параметр специальной техники сэмплинга под названием Eta Sampling.\nВ единицах 1e-4; разумное значение - 3.\nУстановите в 0, чтобы отключить.\nСм. статью Truncation Sampling as Language Model Desmoothing от Хьюитт и др. (2022) для получения подробной информации.", "Learn how to contribute your idle GPU cycles to the Horde": "Узнайте, как использовать время простоя вашего GPU для помощи Horde", "Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "Используйте соответствующий токенизатор для моделей Google через их API. Медленная обработка подсказок, но предлагает намного более точный подсчет токенов.", "Load koboldcpp order": "Загрузить порядок из koboldcpp", @@ -964,7 +964,7 @@ "char_import_3": "Персонаж с JanitorAI (прямая ссылка или UUID)", "char_import_4": "Персонаж с Pygmalion.chat (прямая ссылка или UUID)", "char_import_5": "Персонаж с AICharacterCards.com (прямая ссылка или ID)", - "char_import_6": "Прямая ссылка на PNG-файл (чтобы узнать список разрешённых хостов, загляните в", + "char_import_6": "Прямая ссылка на PNG-файл (список разрешённых хостов находится в", "char_import_7": ")", "Grammar String": "Грамматика", "GBNF or EBNF, depends on the backend in use. If you're using this you should know which.": "GBNF или EBNF, зависит от бэкенда. Если вы это используете, то, скорее всего, сами знаете, какой именно.", @@ -1016,7 +1016,7 @@ "prompt_manager_relative": "Относительная", "prompt_manager_depth": "Глубина", "Injection depth. 0 = after the last message, 1 = before the last message, etc.": "Глубина вставки. 0 = после последнего сообщения, 1 = перед последним сообщением, и т.д.", - "The prompt to be sent.": "Отправляемый ИИ промпт.", + "The prompt to be sent.": "Текст промпта.", "prompt_manager_forbid_overrides": "Запретить перезапись", "This prompt cannot be overridden by character cards, even if overrides are preferred.": "Карточка персонажа не сможет перезаписать этот промпт, даже если настройки отдают приоритет именно ей.", "image_inlining_hint_1": "Отправлять картинки как часть промпта, если позволяет модель (такой функционал поддерживают GPT-4V, Claude 3 или Llava 13B). Чтобы добавить в чат изображение, используйте на нужном сообщении действие", @@ -1232,7 +1232,7 @@ "Top P & Min P": "Top P & Min P", "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.": "llama.cpp only. Determines the order of samplers. If Mirostat mode is not 0, sampler order is ignored.", "Helps the model to associate messages with characters.": "Помогает модели связывать сообщения с персонажами.", - "character_names_default": "Except for groups and past personas. Otherwise, make sure you provide names in the prompt.", + "character_names_default": "Добавлять префиксы для групповых чатов и предыдущих персон. В остальных случаях указывайте имена в промпте иными способами.", "Completion": "Completion Object", "character_names_completion": "Только латинские буквы, цифры и знак подчёркивания. Работает не для всех бэкендов, в частности для Claude, MistralAI, Google.", "Use AI21 Tokenizer": "Использовать токенайзер AI21", @@ -1278,7 +1278,7 @@ "Will be inserted as a last prompt line when using system/neutral generation.": "Will be inserted as a last prompt line when using system/neutral generation.", "If a stop sequence is generated, everything past it will be removed from the output (inclusive).": "Если ИИ генерирует стоп-строку, то всё после неё будет вырезано из ответа (включая и саму стоп-строку).", "Will be inserted at the start of the chat history if it doesn't start with a User message.": "Вставляется в начале истории чата, если она начинается не с сообщения пользователя.", - "Global World Info/Lorebook activation settings": "Настройки активации глобального лорбука / Информации о мире", + "Global World Info/Lorebook activation settings": "Глобальные настройки активации лорбука / Информации о мире", "Click to expand": "Щёлкните, чтобы развернуть", "Insertion Strategy": "Как инжектить", "Only the entries with the most number of key matches will be selected for Inclusion Group filtering": "Only the entries with the most number of key matches will be selected for Inclusion Group filtering", @@ -1647,8 +1647,8 @@ "mui_reset": "Сброс", "Quick 'Impersonate' button": "Быстрое перевоплощение", "Show a button in the input area to ask the AI to impersonate your character for a single message": "Показать в поле ввода кнопку, по нажатии на которую ИИ сгенерирует одно сообщение от лица вашего персонажа.", - "Separators as Stop Strings": "Разделители как стоп-строки", - "Names as Stop Strings": "Имена как стоп-строки", + "Separators as Stop Strings": "Разделители в качестве стоп-строк", + "Names as Stop Strings": "Имена в качестве стоп-строк", "Add Character and User names to a list of stopping strings.": "Добавлять имена персонажа и пользователя в список стоп-строк.", "Allow Post-History Instructions": "Разрешить инструкции после истории", "context_allow_post_history_instructions": "Добавлять в конец промпта инструкции после истории. Работает только при наличии таких инструкций в карточке И при включенной опции ''Приоритет инструкциям из карточек''.\nНЕ РЕКОМЕНДУЕТСЯ ДЛЯ МОДЕЛЕЙ TEXT COMPLETION, МОЖЕТ ПОРТИТЬ ВЫХОДНОЙ ТЕКСТ.", @@ -1916,8 +1916,8 @@ "Cannot restore GUI preset": "Пресет для Gui восстановить нельзя", "Default preset cannot be restored": "Невозможно восстановить пресет по умолчанию", "Default template cannot be restored": "Невозможно восстановить шаблон по умолчанию", - "Resetting a default preset will restore the default settings": "Сброс стандартного пресета восстановит настройки по умолчанию.", - "Resetting a default template will restore the default settings.": "Сброс стандартного шаблона восстановит настройки по умолчанию.", + "Resetting a default preset will restore the default settings.": "Сброс комплектного пресета восстановит настройки по умолчанию.", + "Resetting a default template will restore the default settings.": "Сброс комплектного шаблона восстановит настройки по умолчанию.", "Are you sure?": "Вы уверены?", "Default preset restored": "Стандартный пресет восстановлен", "Default template restored": "Стандартный шаблон восстановлен", @@ -2048,11 +2048,11 @@ "prompt_post_processing_merge": "Объединять идущие подряд сообщения с одной ролью", "prompt_post_processing_semi": "Semi-strict (чередовать роли)", "prompt_post_processing_strict": "Strict (чередовать роли, сначала пользователь)", - "Select Horde models": "Выбрать модель из Horde", + "Select Horde models": "Выберите модель из Horde", "Model ID (optional)": "Идентификатор модели (необязательно)", "Derive context size from backend": "Использовать бэкенд для определения размера контекста", "Rename current preset": "Переименовать пресет", - "No Worlds active. Click here to select.": "Нет активных миров. Нажмите, чтобы выбрать.", + "No Worlds active. Click here to select.": "Активных миров нет, ЛКМ для выбора.", "Title/Memo": "Название", "Strategy": "Статус", "Position": "Позиция", @@ -2171,7 +2171,7 @@ "instruct_derived": "Считывать из метаданных модели (по возможности)", "Confirm token parsing with": "Чтобы убедиться в правильности выделения токенов, используйте", "Reasoning Effort": "Рассуждения", - "Constrains effort on reasoning for reasoning models.": "Регулирует объём внутренних рассуждений модели (reasoning), для моделей которые поддерживают эту возможность.\nНа данный момент поддерживаются три значения: Подробные, Обычные, Поверхностные.\nПри менее подробном рассуждении ответ получается быстрее, а также экономятся токены, уходящие на рассуждения.", + "Constrains effort on reasoning for reasoning models.": "Регулирует объём внутренних рассуждений модели (reasoning), для моделей, которые поддерживают эту возможность.\nПри менее подробном рассуждении ответ получается быстрее, а также экономятся токены, уходящие на рассуждения.", "openai_reasoning_effort_low": "Поверхностные", "openai_reasoning_effort_medium": "Обычные", "openai_reasoning_effort_high": "Подробные", @@ -2276,8 +2276,8 @@ "Persona Name Not Set": "У персоны отсутствует имя", "You must bind a name to this persona before you can set a lorebook.": "Перед привязкой лорбука персоне необходимо присвоить имя.", "Default Persona Removed": "Персона по умолчанию снята", - "Persona is locked to the current character": "Персона закреплена за этим персонажем", - "Persona is locked to the current chat": "Персона закреплена за этим чатом", + "Persona is locked to the current character": "Персона закреплена за текущим персонажем", + "Persona is locked to the current chat": "Персона закреплена за текущим чатом", "characters": "перс.", "character": "персонаж", "in this group": "в группе", @@ -2338,5 +2338,87 @@ "Reasoning already exists.": "Рассуждения уже присутствуют.", "Edit Message": "Редактирование", "Status check bypassed": "Проверка статуса отключена", - "Valid": "Работает" + "Valid": "Работает", + "Use Group Scoring": "Использовать Group Scoring", + "Only the entries with the most number of key matches will be selected for Inclusion Group filtering": "До групповых фильтров будут допущены только записи с наибольшим кол-вом совпадений", + "Can be used to automatically activate Quick Replies": "Используется для автоматической активации быстрых ответов (Quick Replies)", + "( None )": "(Отсутствует)", + "Tie this entry to specific characters or characters with specific tags": "Привязать запись к опред. персонажам или персонажам с заданными тегами", + "Move Entry to Another Lorebook": "Переместить запись в другой лорбук", + "There are no other lorebooks to move to.": "Некуда перемещать: не найдено других лорбуков.", + "Select Target Lorebook": "Выберите куда переместить", + "Move '${0}' to:": "Переместить '${0}' в:", + "Please select a target lorebook.": "Выберите лорбук, в который будет перемещена запись.", + "Scan depth cannot be negative": "Глубина сканирования не может быть отрицательной", + "Scan depth cannot exceed ${0}": "Глубина сканирования не может превышать ${0}", + "Select your current Reasoning Template": "Выберите текущий Шаблон рассуждений", + "Delete template": "Удалить шаблон", + "Reasoning Template": "Шаблон рассуждений", + "openai_reasoning_effort_auto": "Авто", + "openai_reasoning_effort_minimum": "Минимальные", + "openai_reasoning_effort_maximum": "Максимальные", + "OpenAI-style options: low, medium, high. Minimum and maximum are aliased to low and high. Auto does not send an effort level.": "OpenAI принимает следующее: low (Поверхностные), medium (Обычные), high (Подробные). Minimum (Минимальные) - то же самое, что low. Maximum (Максимальные) - то же самое, что high. При выборе Auto (Авто) значение не отсылается вообще.", + "Allocates a portion of the response length for thinking (low: 10%, medium: 25%, high: 50%). Other options are model-dependent.": "Резервирует часть ответа для рассуждений (Поверхностные: 10% ответа, Обычные: 25%, Подробные: 50%). Остальные значения зависят от конкретной модели.", + "xAI Model": "Модель xAI", + "xAI API Key": "Ключ от API xAI", + "HuggingFace Token": "Токен HuggingFace", + "Endpoint URL": "Адрес эндпоинта", + "Example: https://****.endpoints.huggingface.cloud": "Пример: https://****.endpoints.huggingface.cloud", + "Featherless Model Selection": "Выбор модели из Featherless", + "category": "категория", + "Top": "Топовые", + "All Classes": "Все классы", + "Date Asc": "Дата, возрастание", + "Date Desc": "Дата, убывание", + "Background Image": "Фоновое изображение", + "Delete the background?": "Удалить фон?", + "Tags_as_Folders_desc": "Чтобы тег отображался как папка, его нужно отметить таковым в меню управления тегами. Нажмите сюда, чтобы открыть его.", + "tag_entries": "раз исп.", + "Multiple personas are connected to this character.\nSelect a persona to use for this chat.": "К этому персонажу привязано несколько персон.\nВыберите персону, которую хотите использовать в этом чате.", + "Select Persona": "Выберите персону", + "Completion Object": "Как часть Completion Object", + "Move ${0} to:": "Переместить '${0}' в:", + "Chat Scenario Override": "Перезапись сценария чата", + "Unique to this chat.": "Действует только в рамках текущего чата.", + "All group members will use the following scenario text instead of what is specified in their character cards.": "Все участники группы будут использовать этот сценарий вместо того, который указан в карточке.", + "Checkpoints inherit the scenario override from their parent, and can be changed individually after that.": "Чекпоинты наследуют сценарий родителя, после отделения его можно менять.", + "Delete Tag": "Удалить тег", + "Do you want to delete the tag": "Вы точно хотите удалить тег", + "If you want to merge all references to this tag into another tag, select it below:": "Если хотите заменить ссылки на этот тег на какой-то другой, то выберите из списка:", + "Open Folder (Show all characters even if not selected)": "Открытая папка (показать всех персонажей, включая невыбранных)", + "Closed Folder (Hide all characters unless selected)": "Закрытая папка (скрыть всех персонажей, кроме выбранных)", + "No Folder": "Не папка", + "Show only favorites": "Показать только избранных персонажей", + "Show only groups": "Показать только группы", + "Show only folders": "Показать только папки", + "Manage tags": "Панель управления тегами", + "Show Tag List": "Показать список тегов", + "Clear all filters": "Сбросить все фильтры", + "There are no items to display.": "Отображать абсолютно нечего.", + "Characters and groups hidden by filters or closed folders": "Персонажи и группы скрыты настройками фильтров либо закрытыми папками", + "Otterly empty": "Всё что можно, всё выдрано", + "Here be dragons": "Список настолько очистился, что в него вернулись драконы", + "Kiwibunga": "Настолько пусто, что киви прилетела посидеть", + "Pump-a-Rum": "Пу-пу-пу", + "Croak it": "Только кваканье лягушек и стрёкот сверчков", + "${0} ${1} hidden.": "Персонажей скрыто: ${0}.", + "pagination_of": "из", + "/ page": "/ стр.", + "Context Length": "Размер контекста", + "Added On": "Добавлена", + "Class": "Класс", + "Bulk_edit_characters": "Массовое редактирование персонажей\n\nЛКМ, чтобы выделить либо отменить выделение персонажа\nShift+ЛКМ, чтобы массово выделить либо отменить выделение персонажей\nПКМ, чтобы выбрать действие", + "Bulk select all characters": "Выбрать всех персонажей", + "Duplicate": "Клонировать", + "Next page": "След. страница", + "Previous page": "Пред. страница", + "Group:": "Группа:", + "You deleted a character/chat and arrived back here for safety reasons! Pick another character!": "Вы удалили персонажа или чат, и мы из соображений безопасности перенесли вас на эту страницу! Выберите другого персонажа!", + "Group is empty.": "Группа пуста.", + "No characters available": "Персонажей нет", + "Choose what to export": "Выберите, что экспортировать", + "Text Completion Preset": "Пресет для режима Text Completion", + "Update enabled": "Обновить включенные", + "Could not connect to API": "Не удалось подключиться к API", + "Connected to API": "Соединение с API установлено" } diff --git a/public/script.js b/public/script.js index 8421305f4..d3e964031 100644 --- a/public/script.js +++ b/public/script.js @@ -143,6 +143,7 @@ import { getHordeModels, adjustHordeGenerationParams, MIN_LENGTH, + initHorde } from './scripts/horde.js'; import { @@ -175,6 +176,7 @@ import { saveBase64AsFile, uuidv4, equalsIgnoreCaseAndAccents, + localizePagination } from './scripts/utils.js'; import { debounce_timeout, IGNORE_SYMBOL } from './scripts/constants.js'; @@ -229,7 +231,7 @@ import { instruct_presets, selectContextPreset, } from './scripts/instruct-mode.js'; -import { initLocales, t } from './scripts/i18n.js'; +import { initLocales, t, translate } from './scripts/i18n.js'; import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js'; import { user_avatar, @@ -994,6 +996,7 @@ async function firstLoadInit() { initAuthorsNote(); await initPersonas(); initWorldInfo(); + initHorde(); initRossMods(); initStats(); initCfg(); @@ -1425,13 +1428,13 @@ function getBackBlock() { function getEmptyBlock() { const icons = ['fa-dragon', 'fa-otter', 'fa-kiwi-bird', 'fa-crow', 'fa-frog']; - const texts = ['Here be dragons', 'Otterly empty', 'Kiwibunga', 'Pump-a-Rum', 'Croak it']; + const texts = [t`Here be dragons`, t`Otterly empty`, t`Kiwibunga`, t`Pump-a-Rum`, t`Croak it`]; const roll = new Date().getMinutes() % icons.length; const emptyBlock = `

${texts[roll]}

-

There are no items to display.

+

` + t`There are no items to display.` + `

`; return $(emptyBlock); } @@ -1443,7 +1446,7 @@ function getHiddenBlock(hidden) { const hiddenBlock = `
-

${hidden} ${hidden > 1 ? 'characters' : 'character'} hidden.

+

` + t`${hidden} ${hidden > 1 ? 'characters' : 'character'} hidden.` + `

`; @@ -1568,6 +1571,7 @@ export async function printCharacters(fullRefresh = false) { if (hidden > 0 && entitiesFilter.hasAnyFilter()) { $(listId).append(getHiddenBlock(hidden)); } + localizePagination($('#rm_print_characters_pagination')); eventSource.emit(event_types.CHARACTER_PAGE_LOADED); }, @@ -8429,7 +8433,7 @@ export function callPopup(text, type, inputValue = '', { okButton, rows, wide, w } else if (['delete_extension'].includes(popup_type)) { return okButton ?? 'Ok'; } else if (['new_chat', 'confirm'].includes(popup_type)) { - return okButton ?? 'Yes'; + return okButton ?? t`Yes`; } else if (['input'].includes(popup_type)) { return okButton ?? t`Save`; } diff --git a/public/scripts/backgrounds.js b/public/scripts/backgrounds.js index 308aac532..2352df253 100644 --- a/public/scripts/backgrounds.js +++ b/public/scripts/backgrounds.js @@ -291,7 +291,7 @@ async function onDeleteBackgroundClick(e) { const bgToDelete = $(this).closest('.bg_example'); const url = bgToDelete.data('url'); const isCustom = bgToDelete.attr('custom') === 'true'; - const confirm = await callPopup('

Delete the background?

', 'confirm'); + const confirm = await callPopup('

' + t`Delete the background?` + '

', 'confirm'); const bg = bgToDelete.attr('bgfile'); if (confirm) { diff --git a/public/scripts/group-chats.js b/public/scripts/group-chats.js index 91cf0a88d..15250a1fd 100644 --- a/public/scripts/group-chats.js +++ b/public/scripts/group-chats.js @@ -13,6 +13,7 @@ import { getBase64Async, resetScrollHeight, initScrollHeight, + localizePagination } from './utils.js'; import { RA_CountCharTokens, humanizedDateTime, dragElement, favsToHotswap, getMessageTimeStamp } from './RossAscends-mods.js'; import { power_user, loadMovingUIState, sortEntitiesList } from './power-user.js'; @@ -1394,6 +1395,7 @@ function printGroupCandidates() { for (const i of data) { $('#rm_group_add_members').append(getGroupCharacterBlock(i.item)); } + localizePagination($('#rm_group_add_members_pagination')); }, }); } @@ -1401,6 +1403,7 @@ function printGroupCandidates() { function printGroupMembers() { const storageKey = 'GroupMembers_PerPage'; $('.rm_group_members_pagination').each(function () { + let that = this; $(this).pagination({ dataSource: getGroupCharacters({ doFilter: false, onlyMembers: true }), pageRange: 1, @@ -1421,6 +1424,7 @@ function printGroupMembers() { for (const i of data) { $('.rm_group_members').append(getGroupCharacterBlock(i.item)); } + localizePagination($(that)); }, }); }); @@ -1804,7 +1808,7 @@ async function createGroup() { const memberNames = characters.filter(x => members.includes(x.avatar)).map(x => x.name).join(', '); if (!name) { - name = `Group: ${memberNames}`; + name = t`Group:` + ` ${memberNames}`; } const avatar_url = $('#group_avatar_preview img').attr('src'); diff --git a/public/scripts/horde.js b/public/scripts/horde.js index fc5ca93f2..27a17fe22 100644 --- a/public/scripts/horde.js +++ b/public/scripts/horde.js @@ -394,7 +394,7 @@ function getHordeModelTemplate(option) { `)); } -jQuery(function () { +export function initHorde () { $('#horde_model').on('mousedown change', async function (e) { console.log('Horde model change', e); horde_settings.models = $('#horde_model').val(); @@ -441,7 +441,7 @@ jQuery(function () { if (!isMobile()) { $('#horde_model').select2({ width: '100%', - placeholder: 'Select Horde models', + placeholder: t`Select Horde models`, allowClear: true, closeOnSelect: false, templateSelection: function (data) { @@ -451,5 +451,5 @@ jQuery(function () { templateResult: getHordeModelTemplate, }); } -}); +}; diff --git a/public/scripts/openai.js b/public/scripts/openai.js index d1b996c37..832bdd6e4 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -3851,7 +3851,7 @@ function createLogitBiasListItem(entry) { } async function createNewLogitBiasPreset() { - const name = await callPopup('Preset name:', 'input'); + const name = await callPopup(t`Preset name:`, 'input'); if (!name) { return; diff --git a/public/scripts/personas.js b/public/scripts/personas.js index b302c37a0..70d5c68fa 100644 --- a/public/scripts/personas.js +++ b/public/scripts/personas.js @@ -22,12 +22,12 @@ import { } from '../script.js'; import { persona_description_positions, power_user } from './power-user.js'; import { getTokenCountAsync } from './tokenizers.js'; -import { PAGINATION_TEMPLATE, clearInfoBlock, debounce, delay, download, ensureImageFormatSupported, flashHighlight, getBase64Async, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, parseJsonFile, setInfoBlock } from './utils.js'; +import { PAGINATION_TEMPLATE, clearInfoBlock, debounce, delay, download, ensureImageFormatSupported, flashHighlight, getBase64Async, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, parseJsonFile, setInfoBlock, localizePagination } from './utils.js'; import { debounce_timeout } from './constants.js'; import { FILTER_TYPES, FilterHelper } from './filters.js'; import { groups, selected_group } from './group-chats.js'; import { POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; -import { t } from './i18n.js'; +import { t, translate } from './i18n.js'; import { openWorldInfoEditor, world_names } from './world-info.js'; import { renderTemplateAsync } from './templates.js'; import { saveMetadataDebounced } from './extensions.js'; @@ -263,6 +263,7 @@ export async function getUserAvatars(doRender = true, openPageAt = '') { prevText: '<', nextText: '>', formatNavigator: PAGINATION_TEMPLATE, + afterRender: function(a) {console.log(a)}, showNavigator: true, callback: function (data) { $(listId).empty(); @@ -270,6 +271,7 @@ export async function getUserAvatars(doRender = true, openPageAt = '') { $(listId).append(getUserAvatarBlock(item)); } updatePersonaUIStates(); + localizePagination($('#persona_pagination_container')); }, afterSizeSelectorChange: function (e) { accountStorage.setItem(storageKey, e.target.value); diff --git a/public/scripts/tags.js b/public/scripts/tags.js index 6f7f98298..635067cbe 100644 --- a/public/scripts/tags.js +++ b/public/scripts/tags.js @@ -27,7 +27,7 @@ import { debounce_timeout } from './constants.js'; import { INTERACTABLE_CONTROL_CLASS } from './keyboard.js'; import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js'; import { renderTemplateAsync } from './templates.js'; -import { t } from './i18n.js'; +import { t, translate } from './i18n.js'; export { TAG_FOLDER_TYPES, @@ -1057,7 +1057,7 @@ function appendTagToList(listElement, tag, { removable = false, isFilter = false tagElement.attr('title', tag.title); } if (tag.icon) { - tagElement.find('.tag_name').text('').attr('title', `${tag.name} ${tag.title || ''}`.trim()).addClass(tag.icon); + tagElement.find('.tag_name').text('').attr('title', `${translate(tag.name)} ${tag.title || ''}`.trim()).addClass(tag.icon); tagElement.addClass('actionable'); } @@ -1644,6 +1644,7 @@ function updateDrawTagFolder(element, tag) { // Draw/update css attributes for this class folderElement.attr('title', tagFolder.tooltip); + folderElement.attr('data-i18n', '[title]' + tagFolder.tooltip); const indicator = folderElement.find('.tag_folder_indicator'); indicator.text(tagFolder.icon); indicator.css('color', tagFolder.color); @@ -1656,9 +1657,9 @@ async function onTagDeleteClick() { const otherTags = sortTags(tags.filter(x => x.id !== id).map(x => ({ id: x.id, name: x.name }))); const popupContent = $(` -

Delete Tag

-
Do you want to delete the tag
?
-
If you want to merge all references to this tag into another tag, select it below:
+

` + t`Delete Tag` + `

+
` + t`Do you want to delete the tag` + `
?
+
` + t`If you want to merge all references to this tag into another tag, select it below:` + `
- - ${otherTags.map(x => ``).join('')} - `); + const popupContent = $(await renderTemplateAsync('deleteTag', {otherTags})); appendTagToList(popupContent.find('#tag_to_delete'), tag); diff --git a/public/scripts/templates/deleteTag.html b/public/scripts/templates/deleteTag.html new file mode 100644 index 000000000..667ccc00f --- /dev/null +++ b/public/scripts/templates/deleteTag.html @@ -0,0 +1,9 @@ +

Delete Tag

+
Do you want to delete the tag
?
+
If you want to merge all references to this tag into another tag, select it below:
+ \ No newline at end of file From e4d389a5b6b70b6bda7c293cc80c7fb3c4121d2a Mon Sep 17 00:00:00 2001 From: Yokayo Date: Tue, 29 Apr 2025 17:24:49 +0700 Subject: [PATCH 198/230] eslint fix --- public/scripts/tags.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/tags.js b/public/scripts/tags.js index bbbf71eb8..1b9ba8502 100644 --- a/public/scripts/tags.js +++ b/public/scripts/tags.js @@ -1656,7 +1656,7 @@ async function onTagDeleteClick() { const tag = tags.find(x => x.id === id); const otherTags = sortTags(tags.filter(x => x.id !== id).map(x => ({ id: x.id, name: x.name }))); - const popupContent = $(await renderTemplateAsync('deleteTag', {otherTags})); + const popupContent = $(await renderTemplateAsync('deleteTag', { otherTags })); appendTagToList(popupContent.find('#tag_to_delete'), tag); From 6cb1eb3fe69cfebd2c71138c6b0971007007acc4 Mon Sep 17 00:00:00 2001 From: Crow Date: Tue, 29 Apr 2025 15:21:45 +0100 Subject: [PATCH 199/230] Change return to empty string on no single match --- public/scripts/slash-commands.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 047c28d29..fab878e7a 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -2177,14 +2177,15 @@ export function initDefaultSlashCommands() { if (!pattern) { throw new Error('Argument of \'pattern=\' cannot be empty'); } - let re = regexFromString(pattern.toString()); + const re = regexFromString(pattern.toString()); if (!re) { throw new Error('The value of \'pattern\' argument is not a valid regular expression.'); } if (re.flags.includes('g')) { return JSON.stringify([...text.toString().matchAll(re)]); } else { - return JSON.stringify(text.toString().match(re)); + const match = text.toString().match(re); + return match ? JSON.stringify(match) : ''; } }), returns: 'group array for each match', @@ -2205,7 +2206,7 @@ export function initDefaultSlashCommands() {
Returns an array of groups (with the first group being the full match). If the regex contains the global flag (i.e. /g), multiple nested arrays are returned for each match. If the regex is global, returns [] if no matches are found, - otherwise it returns null. + otherwise it returns an empty string.
Example: @@ -2213,7 +2214,7 @@ export function initDefaultSlashCommands() {
/match pattern="green" {{var::x}}            | /echo  |/# [ "green" ]                                               ||
/match pattern="color_(\\w+)" {{var::x}}      | /echo  |/# [ "color_green", "green" ]                                ||
/match pattern="/color_(\\w+)/g" {{var::x}}   | /echo  |/# [ [ "color_green", "green" ], [ "color_blue", "blue" ] ]  ||
-
/match pattern="orange" {{var::x}}           | /echo  |/# null                                                      ||
+
/match pattern="orange" {{var::x}}           | /echo  |/#                                                           ||
/match pattern="/orange/g" {{var::x}}        | /echo  |/# []                                                        ||
`, From 6aeced98a6a1fdbebc0dd6110119f8f8424904c3 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 29 Apr 2025 21:22:51 +0300 Subject: [PATCH 200/230] Prefer const. I love const --- public/scripts/slash-commands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index fab878e7a..66555216c 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -2138,7 +2138,7 @@ export function initDefaultSlashCommands() { if (!pattern) { throw new Error('Argument of \'pattern=\' cannot be empty'); } - let re = regexFromString(pattern.toString()); + const re = regexFromString(pattern.toString()); if (!re) { throw new Error('The value of \'pattern\' argument is not a valid regular expression.'); } From 98e96b8c0705247cd2574df03605ac56b20ae6f1 Mon Sep 17 00:00:00 2001 From: Shinon Date: Wed, 30 Apr 2025 21:23:13 +0800 Subject: [PATCH 201/230] Check for `error` as well when parsing streaming responses --- public/scripts/textgen-settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/textgen-settings.js b/public/scripts/textgen-settings.js index f1a0d8de9..204593201 100644 --- a/public/scripts/textgen-settings.js +++ b/public/scripts/textgen-settings.js @@ -1146,7 +1146,7 @@ function tryParseStreamingError(response, decoded) { // No JSON. Do nothing. } - const message = data?.error?.message || data?.message || data?.detail; + const message = data?.error?.message || data?.error || data?.message || data?.detail; if (message) { toastr.error(message, 'Text Completion API'); From 8dc7aa0c20fb47dd0faf22ce0d539d3a86f42e60 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 30 Apr 2025 21:07:06 +0300 Subject: [PATCH 202/230] Add post_history field to default prompts --- default/content/presets/sysprompt/Actor.json | 3 ++- default/content/presets/sysprompt/Assistant - Expert.json | 3 ++- default/content/presets/sysprompt/Assistant - Simple.json | 3 ++- default/content/presets/sysprompt/Blank.json | 3 ++- default/content/presets/sysprompt/Chain of Thought.json | 3 ++- default/content/presets/sysprompt/Lightning 1.1.json | 3 ++- default/content/presets/sysprompt/Neutral - Chat.json | 3 ++- default/content/presets/sysprompt/Roleplay - Detailed.json | 3 ++- default/content/presets/sysprompt/Roleplay - Immersive.json | 3 ++- default/content/presets/sysprompt/Roleplay - Simple.json | 3 ++- default/content/presets/sysprompt/Text Adventure.json | 3 ++- default/content/presets/sysprompt/Writer - Creative.json | 3 ++- default/content/presets/sysprompt/Writer - Realistic.json | 3 ++- 13 files changed, 26 insertions(+), 13 deletions(-) diff --git a/default/content/presets/sysprompt/Actor.json b/default/content/presets/sysprompt/Actor.json index 3fa842c4f..965f4fe91 100644 --- a/default/content/presets/sysprompt/Actor.json +++ b/default/content/presets/sysprompt/Actor.json @@ -1,4 +1,5 @@ { "name": "Actor", - "content": "You are an expert actor that can fully immerse yourself into any role given. You do not break character for any reason, even if someone tries addressing you as an AI or language model. Currently your role is {{char}}, which is described in detail below. As {{char}}, continue the exchange with {{user}}." + "content": "You are an expert actor that can fully immerse yourself into any role given. You do not break character for any reason, even if someone tries addressing you as an AI or language model. Currently your role is {{char}}, which is described in detail below. As {{char}}, continue the exchange with {{user}}.", + "post_history": "" } diff --git a/default/content/presets/sysprompt/Assistant - Expert.json b/default/content/presets/sysprompt/Assistant - Expert.json index a493a8db3..f95814399 100644 --- a/default/content/presets/sysprompt/Assistant - Expert.json +++ b/default/content/presets/sysprompt/Assistant - Expert.json @@ -1,4 +1,5 @@ { "name": "Assistant - Expert", - "content": "You are a helpful assistant. Please answer truthfully and write out your thinking step by step to be sure you get the right answer. If you make a mistake or encounter an error in your thinking, say so out loud and attempt to correct it. If you don't know or aren't sure about something, say so clearly. You will act as a professional logician, mathematician, and physicist. You will also act as the most appropriate type of expert to answer any particular question or solve the relevant problem; state which expert type your are, if so. Also think of any particular named expert that would be ideal to answer the relevant question or solve the relevant problem; name and act as them, if appropriate." + "content": "You are a helpful assistant. Please answer truthfully and write out your thinking step by step to be sure you get the right answer. If you make a mistake or encounter an error in your thinking, say so out loud and attempt to correct it. If you don't know or aren't sure about something, say so clearly. You will act as a professional logician, mathematician, and physicist. You will also act as the most appropriate type of expert to answer any particular question or solve the relevant problem; state which expert type your are, if so. Also think of any particular named expert that would be ideal to answer the relevant question or solve the relevant problem; name and act as them, if appropriate.", + "post_history": "" } diff --git a/default/content/presets/sysprompt/Assistant - Simple.json b/default/content/presets/sysprompt/Assistant - Simple.json index 8a367983a..e00ef7572 100644 --- a/default/content/presets/sysprompt/Assistant - Simple.json +++ b/default/content/presets/sysprompt/Assistant - Simple.json @@ -1,4 +1,5 @@ { "name": "Assistant - Simple", - "content": "A chat between a curious human and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the human's questions." + "content": "A chat between a curious human and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the human's questions.", + "post_history": "" } diff --git a/default/content/presets/sysprompt/Blank.json b/default/content/presets/sysprompt/Blank.json index 9648d8583..6d112729d 100644 --- a/default/content/presets/sysprompt/Blank.json +++ b/default/content/presets/sysprompt/Blank.json @@ -1,4 +1,5 @@ { "name": "Blank", - "content": "" + "content": "", + "post_history": "" } diff --git a/default/content/presets/sysprompt/Chain of Thought.json b/default/content/presets/sysprompt/Chain of Thought.json index 0b9e45bea..36067b684 100644 --- a/default/content/presets/sysprompt/Chain of Thought.json +++ b/default/content/presets/sysprompt/Chain of Thought.json @@ -1,4 +1,5 @@ { "name": "Chain of Thought", - "content": "Elaborate on the topic using a Tree of Thoughts and backtrack when necessary to construct a clear, cohesive Chain of Thought reasoning. Always answer without hesitation." + "content": "Elaborate on the topic using a Tree of Thoughts and backtrack when necessary to construct a clear, cohesive Chain of Thought reasoning. Always answer without hesitation.", + "post_history": "" } diff --git a/default/content/presets/sysprompt/Lightning 1.1.json b/default/content/presets/sysprompt/Lightning 1.1.json index b122f0a12..16ee980da 100644 --- a/default/content/presets/sysprompt/Lightning 1.1.json +++ b/default/content/presets/sysprompt/Lightning 1.1.json @@ -1,4 +1,5 @@ { "name": "Lightning 1.1", - "content": "Take the role of {{char}} in a play that leaves a lasting impression on {{user}}. Write {{char}}'s next reply.\nNever skip or gloss over {{char}}’s actions. Progress the scene at a naturally slow pace." + "content": "Take the role of {{char}} in a play that leaves a lasting impression on {{user}}. Write {{char}}'s next reply.\nNever skip or gloss over {{char}}’s actions. Progress the scene at a naturally slow pace.", + "post_history": "" } diff --git a/default/content/presets/sysprompt/Neutral - Chat.json b/default/content/presets/sysprompt/Neutral - Chat.json index 9b392d28d..6ac181a98 100644 --- a/default/content/presets/sysprompt/Neutral - Chat.json +++ b/default/content/presets/sysprompt/Neutral - Chat.json @@ -1,4 +1,5 @@ { "name": "Neutral - Chat", - "content": "Write {{char}}'s next reply in a fictional chat between {{char}} and {{user}}." + "content": "Write {{char}}'s next reply in a fictional chat between {{char}} and {{user}}.", + "post_history": "" } diff --git a/default/content/presets/sysprompt/Roleplay - Detailed.json b/default/content/presets/sysprompt/Roleplay - Detailed.json index 2e3c95c7a..3aaac47c3 100644 --- a/default/content/presets/sysprompt/Roleplay - Detailed.json +++ b/default/content/presets/sysprompt/Roleplay - Detailed.json @@ -1,4 +1,5 @@ { "name": "Roleplay - Detailed", - "content": "Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions. Keep the story immersive and engaging." + "content": "Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions. Keep the story immersive and engaging.", + "post_history": "" } diff --git a/default/content/presets/sysprompt/Roleplay - Immersive.json b/default/content/presets/sysprompt/Roleplay - Immersive.json index cdd85f7d2..6c94e6806 100644 --- a/default/content/presets/sysprompt/Roleplay - Immersive.json +++ b/default/content/presets/sysprompt/Roleplay - Immersive.json @@ -1,4 +1,5 @@ { "name": "Roleplay - Immersive", - "content": "[System note: Write one reply only. Do not decide what {{user}} says or does. Write at least one paragraph, up to four. Be descriptive and immersive, providing vivid details about {{char}}'s actions, emotions, and the environment. Write with a high degree of complexity and burstiness. Do not repeat this message.]" + "content": "[System note: Write one reply only. Do not decide what {{user}} says or does. Write at least one paragraph, up to four. Be descriptive and immersive, providing vivid details about {{char}}'s actions, emotions, and the environment. Write with a high degree of complexity and burstiness. Do not repeat this message.]", + "post_history": "" } diff --git a/default/content/presets/sysprompt/Roleplay - Simple.json b/default/content/presets/sysprompt/Roleplay - Simple.json index dcef1f10c..d3a17ecc1 100644 --- a/default/content/presets/sysprompt/Roleplay - Simple.json +++ b/default/content/presets/sysprompt/Roleplay - Simple.json @@ -1,4 +1,5 @@ { "name": "Roleplay - Simple", - "content": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}." + "content": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.", + "post_history": "" } diff --git a/default/content/presets/sysprompt/Text Adventure.json b/default/content/presets/sysprompt/Text Adventure.json index 2e22abe5b..f860730f4 100644 --- a/default/content/presets/sysprompt/Text Adventure.json +++ b/default/content/presets/sysprompt/Text Adventure.json @@ -1,4 +1,5 @@ { "name": "Text Adventure", - "content": "[Enter Adventure Mode. Narrate the story based on {{user}}'s dialogue and actions after \">\". Describe the surroundings in vivid detail. Be detailed, creative, verbose, and proactive. Move the story forward by introducing fantasy elements and interesting characters.]" + "content": "[Enter Adventure Mode. Narrate the story based on {{user}}'s dialogue and actions after \">\". Describe the surroundings in vivid detail. Be detailed, creative, verbose, and proactive. Move the story forward by introducing fantasy elements and interesting characters.]", + "post_history": "" } diff --git a/default/content/presets/sysprompt/Writer - Creative.json b/default/content/presets/sysprompt/Writer - Creative.json index 65a7aec52..83da22c96 100644 --- a/default/content/presets/sysprompt/Writer - Creative.json +++ b/default/content/presets/sysprompt/Writer - Creative.json @@ -1,4 +1,5 @@ { "name": "Writer - Creative", - "content": "You are an intelligent, skilled, versatile writer.\n\nYour task is to write a role-play based on the information below." + "content": "You are an intelligent, skilled, versatile writer.\n\nYour task is to write a role-play based on the information below.", + "post_history": "" } diff --git a/default/content/presets/sysprompt/Writer - Realistic.json b/default/content/presets/sysprompt/Writer - Realistic.json index a5abcbfb5..16aa33ba4 100644 --- a/default/content/presets/sysprompt/Writer - Realistic.json +++ b/default/content/presets/sysprompt/Writer - Realistic.json @@ -1,4 +1,5 @@ { "name": "Writer - Realistic", - "content": "Continue writing this story and portray characters realistically." + "content": "Continue writing this story and portray characters realistically.", + "post_history": "" } From 63e7139a8125644e2fc82eab1b54a57b3a387874 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 30 Apr 2025 22:00:09 +0300 Subject: [PATCH 203/230] Clean-up i18n --- public/locales/fr-fr.json | 1 - public/locales/ko-kr.json | 1 - public/locales/ru-ru.json | 1 - public/locales/zh-cn.json | 1 - public/locales/zh-tw.json | 1 - 5 files changed, 5 deletions(-) diff --git a/public/locales/fr-fr.json b/public/locales/fr-fr.json index b6d54db9f..631302ca2 100644 --- a/public/locales/fr-fr.json +++ b/public/locales/fr-fr.json @@ -1448,7 +1448,6 @@ "Add Character and User names to a list of stopping strings.": "Ajouter les noms de personnages et d'utilisateurs à une liste de chaînes d'arrêt.", "Names as Stop Strings": "Noms comme chaînes d'arrêt", "context_allow_post_history_instructions": "Inclut les instructions post-historiques à la fin du prompt, si elles sont définies dans la fiche de personnage ET si l'option 'Préférer les instructions de personnage' est activée.\nN'EST PAS RECOMMANDÉ POUR LES MODÈLES DE COMPLÉTION DE TEXTE, CAR IL PEUT ENTRAÎNER DE MAUVAIS RÉSULTATS.", - "Allow Post-History Instructions": "Autoriser les instructions post-histoire", "Instruct Template": "Modèle d'instruction", "instruct_derived": "Dériver des métadonnées du modèle, si possible.", "instruct_enabled": "Activer le mode d'instruction", diff --git a/public/locales/ko-kr.json b/public/locales/ko-kr.json index 0a41055a4..a23331561 100644 --- a/public/locales/ko-kr.json +++ b/public/locales/ko-kr.json @@ -1519,7 +1519,6 @@ "Always": "항상 추가함", "Separators as Stop Strings": "구분 기호를 정지 문자열로 사용하기", "Names as Stop Strings": "캐릭터의 이름들을 정지 문자열로 사용하기", - "Allow Post-History Instructions": "Post-History 지침 허용", "Image Captioning": "이미지 캡셔닝", "Automatically caption images": "자동으로 이미지에 대한 설명 문장으로 나타내기", "Edit captions before saving": "저장하기 전에 이미지에 대한 설명 문장 편집하기", diff --git a/public/locales/ru-ru.json b/public/locales/ru-ru.json index 1bb476ea3..c1591f212 100644 --- a/public/locales/ru-ru.json +++ b/public/locales/ru-ru.json @@ -1649,7 +1649,6 @@ "Separators as Stop Strings": "Разделители как стоп-строки", "Names as Stop Strings": "Имена как стоп-строки", "Add Character and User names to a list of stopping strings.": "Добавлять имена персонажа и пользователя в список стоп-строк.", - "Allow Post-History Instructions": "Разрешить инструкции после истории", "context_allow_post_history_instructions": "Добавлять в конец промпта инструкции после истории. Работает только при наличии таких инструкций в карточке И при включенной опции ''Приоритет инструкциям из карточек''.\nНЕ РЕКОМЕНДУЕТСЯ ДЛЯ МОДЕЛЕЙ TEXT COMPLETION, МОЖЕТ ПОРТИТЬ ВЫХОДНОЙ ТЕКСТ.", "First User Prefix": "Первый префикс пользователя", "Inserted before the first User's message.": "Вставляется перед первым сообщением пользователя.", diff --git a/public/locales/zh-cn.json b/public/locales/zh-cn.json index 66eebedf8..28dbf3cff 100644 --- a/public/locales/zh-cn.json +++ b/public/locales/zh-cn.json @@ -504,7 +504,6 @@ "Add Character and User names to a list of stopping strings.": "将角色和用户名添加到停止字符串列表中。", "Names as Stop Strings": "名称作为终止字符串", "context_allow_post_history_instructions": "如果在角色卡中定义并且启用了“首选角色卡说明”,则在提示末尾包含后历史说明。\n不建议在文本补全模型中使用此功能,否则会导致输出错误。", - "Allow Post-History Instructions": "允许后历史说明", "Instruct Template": "指导模板", "instruct_derived": "如果可能,从模型元数据中获取", "instruct_bind_to_context": "如果启用,上下文模板将根据所选的指导模板名称或偏好自动选择。", diff --git a/public/locales/zh-tw.json b/public/locales/zh-tw.json index da32d8957..ae2c9bb6d 100644 --- a/public/locales/zh-tw.json +++ b/public/locales/zh-tw.json @@ -1554,7 +1554,6 @@ "All": "全部", "Allow fallback models": "允許回退模型", "Allow fallback providers": "允許回退供應商", - "Allow Post-History Instructions": "允許聊天歷史後指示", "Allow reverse proxy": "允許反向代理", "Alternate Greeting #": "備選問候語 #", "alternate_greetings_hint_1": "點選", From 511ae39b0b992b3040753a3b1a530da00b9ede4a Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 30 Apr 2025 22:23:12 +0300 Subject: [PATCH 204/230] Move margin class --- public/index.html | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/public/index.html b/public/index.html index d1f90aef3..89c429642 100644 --- a/public/index.html +++ b/public/index.html @@ -3811,7 +3811,7 @@ -
+

System Prompt @@ -3823,9 +3823,7 @@

-
- - +
From b3a3b9d347092ec4feb00435eec9f5bb32160799 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 30 Apr 2025 22:36:24 +0300 Subject: [PATCH 205/230] Fix npm audit in tests --- tests/package-lock.json | 455 ++++++++++++++-------------------------- 1 file changed, 153 insertions(+), 302 deletions(-) diff --git a/tests/package-lock.json b/tests/package-lock.json index b8fbe2986..33dd9157e 100644 --- a/tests/package-lock.json +++ b/tests/package-lock.json @@ -1038,20 +1038,19 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.3.tgz", - "integrity": "sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==", + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.2.tgz", + "integrity": "sha512-i4Ez+s9oRWQbNjtI/3+jxr7OH508mjAKvza0ekPJem0ZtmsYHP3B5dq62+IaBHKaGCOuqJxXzvFLUhJvQ6jtsQ==", "license": "Apache-2.0", "peer": true, "dependencies": { - "debug": "4.3.4", - "extract-zip": "2.0.1", - "progress": "2.0.3", - "proxy-agent": "6.4.0", - "semver": "7.6.0", - "tar-fs": "3.0.5", - "unbzip2-stream": "1.4.3", - "yargs": "17.7.2" + "debug": "^4.4.0", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.5.0", + "semver": "^7.7.1", + "tar-fs": "^3.0.8", + "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" @@ -1060,46 +1059,12 @@ "node": ">=18" } }, - "node_modules/@puppeteer/browsers/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@puppeteer/browsers/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@puppeteer/browsers/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "license": "ISC", "peer": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -1107,13 +1072,6 @@ "node": ">=10" } }, - "node_modules/@puppeteer/browsers/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC", - "peer": true - }, "node_modules/@sideway/address": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", @@ -1452,14 +1410,11 @@ } }, "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "license": "MIT", "peer": true, - "dependencies": { - "debug": "^4.3.4" - }, "engines": { "node": ">= 14" } @@ -1581,9 +1536,9 @@ } }, "node_modules/b4a": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", - "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", "license": "Apache-2.0", "peer": true }, @@ -1701,76 +1656,81 @@ "license": "MIT" }, "node_modules/bare-events": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", - "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz", + "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==", "license": "Apache-2.0", "optional": true, "peer": true }, "node_modules/bare-fs": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.1.tgz", - "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.1.3.tgz", + "integrity": "sha512-OeEZYIg+2qepaWLyphaOXHAHKo3xkM8y3BeGAvHdMN8GNWvEAU1Yw6rYpGzu/wDDbKxgEjVeVDpgGhDzaeMpjg==", "license": "Apache-2.0", "optional": true, "peer": true, "dependencies": { - "bare-events": "^2.0.0", - "bare-path": "^2.0.0", - "bare-stream": "^2.0.0" + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } } }, "node_modules/bare-os": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.0.tgz", - "integrity": "sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.1.tgz", + "integrity": "sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==", "license": "Apache-2.0", "optional": true, - "peer": true + "peer": true, + "engines": { + "bare": ">=1.14.0" + } }, "node_modules/bare-path": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", - "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", "license": "Apache-2.0", "optional": true, "peer": true, "dependencies": { - "bare-os": "^2.1.0" + "bare-os": "^3.0.1" } }, "node_modules/bare-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.1.3.tgz", - "integrity": "sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==", + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.5.tgz", + "integrity": "sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==", "license": "Apache-2.0", "optional": true, "peer": true, "dependencies": { - "streamx": "^2.18.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" + "streamx": "^2.21.0" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" + "bare-events": { + "optional": true } - ], - "license": "MIT", - "peer": true + } }, "node_modules/basic-ftp": { "version": "5.0.5", @@ -1845,31 +1805,6 @@ "node-int64": "^0.4.0" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -1950,15 +1885,14 @@ } }, "node_modules/chromium-bidi": { - "version": "0.5.24", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.24.tgz", - "integrity": "sha512-5xQNN2SVBdZv4TxeMLaI+PelrnZsHDhn8h2JtyriLr+0qHcZS8BMuo93qN6J1VmtmrgYP+rmcLHcbpnA8QJh+w==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-4.1.1.tgz", + "integrity": "sha512-biR7t4vF3YluE6RlMSk9IWk+b9U+WWyzHp+N2pL9vRTk+UXHYRTVp7jTK58ZNzMLBgoLMHY4QyJMbeuw3eKxqg==", "license": "Apache-2.0", "peer": true, "dependencies": { - "mitt": "3.0.1", - "urlpattern-polyfill": "10.0.0", - "zod": "3.23.8" + "mitt": "^3.0.1", + "zod": "^3.24.1" }, "peerDependencies": { "devtools-protocol": "*" @@ -2169,12 +2103,12 @@ } }, "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -2248,9 +2182,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1299070", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1299070.tgz", - "integrity": "sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg==", + "version": "0.0.1425554", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1425554.tgz", + "integrity": "sha512-uRfxR6Nlzdzt0ihVIkV+sLztKgs7rgquY/Mhcv1YNCWDh5IZgl5mnn2aeEnW5stYTE0wwiF4RYVz8eMEpV1SEw==", "license": "BSD-3-Clause", "peer": true }, @@ -2987,21 +2921,6 @@ "node": ">=0.10.0" } }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "license": "MIT", - "peer": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3071,16 +2990,15 @@ } }, "node_modules/get-uri": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", - "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz", + "integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==", "license": "MIT", "peer": true, "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4", - "fs-extra": "^11.2.0" + "debug": "^4.3.4" }, "engines": { "node": ">= 14" @@ -3254,13 +3172,13 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", "peer": true, "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -3276,27 +3194,6 @@ "node": ">=10.17.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause", - "peer": true - }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -4287,19 +4184,6 @@ "node": ">=6" } }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4501,9 +4385,9 @@ "peer": true }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/natural-compare": { @@ -4657,20 +4541,20 @@ } }, "node_modules/pac-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", - "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", "license": "MIT", "peer": true, "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.5", + "https-proxy-agent": "^7.0.6", "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.4" + "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" @@ -4876,20 +4760,20 @@ } }, "node_modules/proxy-agent": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", - "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", "license": "MIT", "peer": true, "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "^4.3.4", "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.3", + "https-proxy-agent": "^7.0.6", "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.1", + "pac-proxy-agent": "^7.1.0", "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.2" + "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" @@ -4912,9 +4796,9 @@ "license": "MIT" }, "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "license": "MIT", "peer": true, "dependencies": { @@ -4932,37 +4816,40 @@ } }, "node_modules/puppeteer": { - "version": "22.12.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.12.1.tgz", - "integrity": "sha512-1GxY8dnEnHr1SLzdSDr0FCjM6JQfAh2E2I/EqzeF8a58DbGVk9oVjj4lFdqNoVbpgFSpAbz7VER9St7S1wDpNg==", + "version": "24.7.2", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.7.2.tgz", + "integrity": "sha512-ifYqoY6wGs0yZeFuFPn8BE9FhuveXkarF+eO18I2e/axdoCh4Qh1AE+qXdJBhdaeoPt6eRNTY4Dih29Jbq8wow==", "hasInstallScript": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@puppeteer/browsers": "2.2.3", + "@puppeteer/browsers": "2.10.2", + "chromium-bidi": "4.1.1", "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1299070", - "puppeteer-core": "22.12.1" + "devtools-protocol": "0.0.1425554", + "puppeteer-core": "24.7.2", + "typed-query-selector": "^2.12.0" }, "bin": { - "puppeteer": "lib/esm/puppeteer/node/cli.js" + "puppeteer": "lib/cjs/puppeteer/node/cli.js" }, "engines": { "node": ">=18" } }, "node_modules/puppeteer-core": { - "version": "22.12.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.12.1.tgz", - "integrity": "sha512-XmqeDPVdC5/3nGJys1jbgeoZ02wP0WV1GBlPtr/ULRbGXJFuqgXMcKQ3eeNtFpBzGRbpeoCGWHge1ZWKWl0Exw==", + "version": "24.7.2", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.7.2.tgz", + "integrity": "sha512-P9pZyTmJqKODFCnkZgemCpoFA4LbAa8+NumHVQKyP5X9IgdNS1ZnAnIh1sMAwhF8/xEUGf7jt+qmNLlKieFw1Q==", "license": "Apache-2.0", "peer": true, "dependencies": { - "@puppeteer/browsers": "2.2.3", - "chromium-bidi": "0.5.24", - "debug": "^4.3.5", - "devtools-protocol": "0.0.1299070", - "ws": "^8.17.1" + "@puppeteer/browsers": "2.10.2", + "chromium-bidi": "4.1.1", + "debug": "^4.4.0", + "devtools-protocol": "0.0.1425554", + "typed-query-selector": "^2.12.0", + "ws": "^8.18.1" }, "engines": { "node": ">=18" @@ -5051,13 +4938,6 @@ ], "license": "MIT" }, - "node_modules/queue-tick": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", - "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "license": "MIT", - "peer": true - }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -5254,9 +5134,9 @@ } }, "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", + "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", "license": "MIT", "peer": true, "dependencies": { @@ -5269,13 +5149,13 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", - "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "license": "MIT", "peer": true, "dependencies": { - "agent-base": "^7.1.1", + "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" }, @@ -5345,14 +5225,13 @@ } }, "node_modules/streamx": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz", - "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", + "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", "license": "MIT", "peer": true, "dependencies": { "fast-fifo": "^1.3.2", - "queue-tick": "^1.0.1", "text-decoder": "^1.1.0" }, "optionalDependencies": { @@ -5453,9 +5332,9 @@ } }, "node_modules/tar-fs": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz", - "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.8.tgz", + "integrity": "sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==", "license": "MIT", "peer": true, "dependencies": { @@ -5463,8 +5342,8 @@ "tar-stream": "^3.1.5" }, "optionalDependencies": { - "bare-fs": "^2.1.1", - "bare-path": "^2.1.0" + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" } }, "node_modules/tar-stream": { @@ -5494,9 +5373,9 @@ } }, "node_modules/text-decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz", - "integrity": "sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", "license": "Apache-2.0", "peer": true, "dependencies": { @@ -5509,13 +5388,6 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "license": "MIT" }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "license": "MIT", - "peer": true - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -5594,6 +5466,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-query-selector": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", + "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", + "license": "MIT", + "peer": true + }, "node_modules/typescript": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", @@ -5608,33 +5487,12 @@ "node": ">=14.17" } }, - "node_modules/unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "license": "MIT", - "peer": true, - "dependencies": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "license": "MIT" }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/update-browserslist-db": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", @@ -5674,13 +5532,6 @@ "punycode": "^2.1.0" } }, - "node_modules/urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "license": "MIT", - "peer": true - }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -5784,9 +5635,9 @@ } }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", "license": "MIT", "peer": true, "engines": { @@ -5871,9 +5722,9 @@ } }, "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "version": "3.24.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", + "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", "license": "MIT", "peer": true, "funding": { From 9cff3861b49ee9253b40c2780aa0c20908b7dc32 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 30 Apr 2025 22:41:50 +0300 Subject: [PATCH 206/230] Fix path.join to extension --- src/endpoints/extensions.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/endpoints/extensions.js b/src/endpoints/extensions.js index d48a6afce..52b3f9b36 100644 --- a/src/endpoints/extensions.js +++ b/src/endpoints/extensions.js @@ -132,7 +132,7 @@ router.post('/update', async (request, response) => { } const basePath = global ? PUBLIC_DIRECTORIES.globalExtensions : request.user.directories.extensions; - const extensionPath = path.join(basePath, extensionName); + const extensionPath = path.join(basePath, sanitize(extensionName)); if (!fs.existsSync(extensionPath)) { return response.status(404).send(`Directory does not exist at ${extensionPath}`); @@ -174,7 +174,7 @@ router.post('/branches', async (request, response) => { } const basePath = global ? PUBLIC_DIRECTORIES.globalExtensions : request.user.directories.extensions; - const extensionPath = path.join(basePath, extensionName); + const extensionPath = path.join(basePath, sanitize(extensionName)); if (!fs.existsSync(extensionPath)) { return response.status(404).send(`Directory does not exist at ${extensionPath}`); @@ -200,7 +200,7 @@ router.post('/branches', async (request, response) => { return response.send(result); } catch (error) { console.error('Getting branches failed', error); - return response.status(500).send('Internal Server Error. Try again later.'); + return response.status(500).send('Internal Server Error. Check the server logs for more details.'); } }); @@ -220,7 +220,7 @@ router.post('/switch', async (request, response) => { } const basePath = global ? PUBLIC_DIRECTORIES.globalExtensions : request.user.directories.extensions; - const extensionPath = path.join(basePath, extensionName); + const extensionPath = path.join(basePath, sanitize(extensionName)); if (!fs.existsSync(extensionPath)) { return response.status(404).send(`Directory does not exist at ${extensionPath}`); @@ -304,7 +304,7 @@ router.post('/move', async (request, response) => { return response.sendStatus(204); } catch (error) { console.error('Moving extension failed', error); - return response.status(500).send('Internal Server Error. Try again later.'); + return response.status(500).send('Internal Server Error. Check the server logs for more details.'); } }); From ef59afcec13e969e4bb4b8aaec1a5e779937765a Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 30 Apr 2025 22:56:35 +0300 Subject: [PATCH 207/230] Specify tag support in messaging --- public/scripts/extensions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js index 2c2c52261..df6e8b898 100644 --- a/public/scripts/extensions.js +++ b/public/scripts/extensions.js @@ -1520,9 +1520,9 @@ export async function openThirdPartyExtensionMenu(suggestUrl = '') { /** @type {import('./popup.js').CustomPopupInput} */ const branchNameInput = { id: 'extension_branch_name', - label: t`Branch name (optional)`, + label: t`Branch or tag name (optional)`, type: 'text', - tooltip: 'e.g. main, master, dev', + tooltip: 'e.g. main, dev, v1.0.0', }; const customButtons = isCurrentUserAdmin ? [installForAllButton] : []; From 63b48b921189ad1d79784cd6340d25e8243eb1c4 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 30 Apr 2025 22:58:43 +0300 Subject: [PATCH 208/230] Log a warning on an unknown input type --- public/scripts/popup.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/public/scripts/popup.js b/public/scripts/popup.js index a41626740..3a1b03877 100644 --- a/public/scripts/popup.js +++ b/public/scripts/popup.js @@ -284,9 +284,7 @@ export class Popup { } this.inputControls.appendChild(label); - } - - if (input.type === 'text') { + } else if (input.type === 'text') { const label = document.createElement('label'); label.classList.add('text_label', 'justifyCenter'); label.setAttribute('for', input.id); @@ -306,6 +304,9 @@ export class Popup { label.appendChild(inputElement); this.inputControls.appendChild(label); + } else { + console.warn('Unknown custom input type. Only checkbox and text are supported.', input); + return; } }); From 2c0dcdc4494db39c98bb049c345442facb72f3ff Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 30 Apr 2025 23:54:14 +0300 Subject: [PATCH 209/230] Refactor git operations to use baseDir --- src/endpoints/extensions.js | 64 ++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/endpoints/extensions.js b/src/endpoints/extensions.js index 52b3f9b36..6ac291b8e 100644 --- a/src/endpoints/extensions.js +++ b/src/endpoints/extensions.js @@ -30,17 +30,23 @@ async function getManifest(extensionPath) { * @returns {Promise} - Returns the extension information as an object */ async function checkIfRepoIsUpToDate(extensionPath) { - const git = simpleGit(); - await git.cwd(extensionPath).fetch('origin'); - const currentBranch = await git.cwd(extensionPath).branch(); - const currentCommitHash = await git.cwd(extensionPath).revparse(['HEAD']); - const log = await git.cwd(extensionPath).log({ + const git = simpleGit({ baseDir: extensionPath }); + await git.fetch('origin'); + const currentBranch = await git.branch(); + const currentCommitHash = await git.revparse(['HEAD']); + const log = await git.log({ from: currentCommitHash, to: `origin/${currentBranch.current}`, }); // Fetch remote repository information - const remotes = await git.cwd(extensionPath).getRemotes(true); + const remotes = await git.getRemotes(true); + if (remotes.length === 0) { + return { + isUpToDate: true, + remoteUrl: '', + }; + } return { isUpToDate: log.total === 0, @@ -118,7 +124,6 @@ router.post('/install', async (request, response) => { * @returns {void} */ router.post('/update', async (request, response) => { - const git = simpleGit(); if (!request.body.extensionName) { return response.status(400).send('Bad Request: extensionName is required in the request body.'); } @@ -139,15 +144,16 @@ router.post('/update', async (request, response) => { } const { isUpToDate, remoteUrl } = await checkIfRepoIsUpToDate(extensionPath); - const currentBranch = await git.cwd(extensionPath).branch(); + const git = simpleGit({ baseDir: extensionPath }); + const currentBranch = await git.branch(); if (!isUpToDate) { - await git.cwd(extensionPath).pull('origin', currentBranch.current); + await git.pull('origin', currentBranch.current); console.info(`Extension has been updated at ${extensionPath}`); } else { console.info(`Extension is up to date at ${extensionPath}`); } - await git.cwd(extensionPath).fetch('origin'); - const fullCommitHash = await git.cwd(extensionPath).revparse(['HEAD']); + await git.fetch('origin'); + const fullCommitHash = await git.revparse(['HEAD']); const shortCommitHash = fullCommitHash.slice(0, 7); return response.send({ shortCommitHash, extensionPath, isUpToDate, remoteUrl }); @@ -160,8 +166,6 @@ router.post('/update', async (request, response) => { router.post('/branches', async (request, response) => { try { - const git = simpleGit(); - const { extensionName, global } = request.body; if (!extensionName) { @@ -180,18 +184,19 @@ router.post('/branches', async (request, response) => { return response.status(404).send(`Directory does not exist at ${extensionPath}`); } + const git = simpleGit({ baseDir: extensionPath }); // Unshallow the repository if it is shallow - const isShallow = await git.cwd(extensionPath).revparse(['--is-shallow-repository']) === 'true'; + const isShallow = await git.revparse(['--is-shallow-repository']) === 'true'; if (isShallow) { console.info(`Unshallowing the repository at ${extensionPath}`); - await git.cwd(extensionPath).fetch('origin', ['--unshallow']); + await git.fetch('origin', ['--unshallow']); } // Fetch all branches - await git.cwd(extensionPath).remote(['set-branches', 'origin', '*']); - await git.cwd(extensionPath).fetch('origin'); - const localBranches = await git.cwd(extensionPath).branchLocal(); - const remoteBranches = await git.cwd(extensionPath).branch(['-r', '--list', 'origin/*']); + await git.remote(['set-branches', 'origin', '*']); + await git.fetch('origin'); + const localBranches = await git.branchLocal(); + const remoteBranches = await git.branch(['-r', '--list', 'origin/*']); const result = [ ...Object.values(localBranches.branches), ...Object.values(remoteBranches.branches), @@ -206,8 +211,6 @@ router.post('/branches', async (request, response) => { router.post('/switch', async (request, response) => { try { - const git = simpleGit(); - const { extensionName, branch, global } = request.body; if (!extensionName || !branch) { @@ -226,18 +229,19 @@ router.post('/switch', async (request, response) => { return response.status(404).send(`Directory does not exist at ${extensionPath}`); } - const branches = await git.cwd(extensionPath).branchLocal(); + const git = simpleGit({ baseDir: extensionPath }); + const branches = await git.branchLocal(); if (String(branch).startsWith('origin/')) { const localBranch = branch.replace('origin/', ''); if (branches.all.includes(localBranch)) { console.info(`Branch ${localBranch} already exists locally, checking it out`); - await git.cwd(extensionPath).checkout(localBranch); + await git.checkout(localBranch); return response.sendStatus(204); } console.info(`Branch ${localBranch} does not exist locally, creating it from ${branch}`); - await git.cwd(extensionPath).checkoutBranch(localBranch, branch); + await git.checkoutBranch(localBranch, branch); return response.sendStatus(204); } @@ -247,14 +251,14 @@ router.post('/switch', async (request, response) => { } // Check if the branch is already checked out - const currentBranch = await git.cwd(extensionPath).branch(); + const currentBranch = await git.branch(); if (currentBranch.current === branch) { console.info(`Branch ${branch} is already checked out`); return response.sendStatus(204); } // Checkout the branch - await git.cwd(extensionPath).checkout(branch); + await git.checkout(branch); console.info(`Checked out branch ${branch} at ${extensionPath}`); return response.sendStatus(204); @@ -319,7 +323,6 @@ router.post('/move', async (request, response) => { * @returns {void} */ router.post('/version', async (request, response) => { - const git = simpleGit(); if (!request.body.extensionName) { return response.status(400).send('Bad Request: extensionName is required in the request body.'); } @@ -333,19 +336,20 @@ router.post('/version', async (request, response) => { return response.status(404).send(`Directory does not exist at ${extensionPath}`); } + const git = simpleGit({ baseDir: extensionPath }); let currentCommitHash; try { - currentCommitHash = await git.cwd(extensionPath).revparse(['HEAD']); + currentCommitHash = await git.revparse(['HEAD']); } catch (error) { // it is not a git repo, or has no commits yet, or is a bare repo // not possible to update it, most likely can't get the branch name either return response.send({ currentBranchName: '', currentCommitHash: '', isUpToDate: true, remoteUrl: '' }); } - const currentBranch = await git.cwd(extensionPath).branch(); + const currentBranch = await git.branch(); // get only the working branch const currentBranchName = currentBranch.current; - await git.cwd(extensionPath).fetch('origin'); + await git.fetch('origin'); console.debug(extensionName, currentBranchName, currentCommitHash); const { isUpToDate, remoteUrl } = await checkIfRepoIsUpToDate(extensionPath); From 7431b0e8aa8f96794d8635d77bbda3825875d8aa Mon Sep 17 00:00:00 2001 From: wickedcode Date: Thu, 1 May 2025 02:23:19 -0400 Subject: [PATCH 210/230] fix: replace rmSync with unlinkSync to resolve an issue deleting files with non-English characters in their names --- src/endpoints/avatars.js | 2 +- src/endpoints/characters.js | 2 +- src/endpoints/chats.js | 2 +- src/endpoints/themes.js | 2 +- src/endpoints/thumbnails.js | 2 +- src/endpoints/worldinfo.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/endpoints/avatars.js b/src/endpoints/avatars.js index db52ab643..4362a0058 100644 --- a/src/endpoints/avatars.js +++ b/src/endpoints/avatars.js @@ -28,7 +28,7 @@ router.post('/delete', getFileNameValidationFunction('avatar'), function (reques const fileName = path.join(request.user.directories.avatars, sanitize(request.body.avatar)); if (fs.existsSync(fileName)) { - fs.rmSync(fileName); + fs.unlinkSync(fileName); return response.send({ result: 'ok' }); } diff --git a/src/endpoints/characters.js b/src/endpoints/characters.js index 1011e9a3d..d5f549a86 100644 --- a/src/endpoints/characters.js +++ b/src/endpoints/characters.js @@ -1150,7 +1150,7 @@ router.post('/delete', validateAvatarUrlMiddleware, async function (request, res return response.sendStatus(400); } - fs.rmSync(avatarPath); + fs.unlinkSync(avatarPath); invalidateThumbnail(request.user.directories, 'avatar', request.body.avatar_url); let dir_name = (request.body.avatar_url.replace('.png', '')); diff --git a/src/endpoints/chats.js b/src/endpoints/chats.js index 9c7376838..2762055a2 100644 --- a/src/endpoints/chats.js +++ b/src/endpoints/chats.js @@ -449,7 +449,7 @@ router.post('/delete', validateAvatarUrlMiddleware, function (request, response) return response.sendStatus(400); } - fs.rmSync(filePath); + fs.unlinkSync(filePath); console.info(`Deleted chat file: ${filePath}`); return response.send('ok'); }); diff --git a/src/endpoints/themes.js b/src/endpoints/themes.js index 868a769d0..00a0dcb97 100644 --- a/src/endpoints/themes.js +++ b/src/endpoints/themes.js @@ -29,7 +29,7 @@ router.post('/delete', function (request, response) { console.error('Theme file not found:', filename); return response.sendStatus(404); } - fs.rmSync(filename); + fs.unlinkSync(filename); return response.sendStatus(200); } catch (error) { console.error(error); diff --git a/src/endpoints/thumbnails.js b/src/endpoints/thumbnails.js index 2e242ec17..583877127 100644 --- a/src/endpoints/thumbnails.js +++ b/src/endpoints/thumbnails.js @@ -75,7 +75,7 @@ export function invalidateThumbnail(directories, type, file) { const pathToThumbnail = path.join(folder, file); if (fs.existsSync(pathToThumbnail)) { - fs.rmSync(pathToThumbnail); + fs.unlinkSync(pathToThumbnail); } } diff --git a/src/endpoints/worldinfo.js b/src/endpoints/worldinfo.js index 0c043ea35..a989b6cb2 100644 --- a/src/endpoints/worldinfo.js +++ b/src/endpoints/worldinfo.js @@ -57,7 +57,7 @@ router.post('/delete', (request, response) => { throw new Error(`World info file ${filename} doesn't exist.`); } - fs.rmSync(pathToWorldInfo); + fs.unlinkSync(pathToWorldInfo); return response.sendStatus(200); }); From d3bb625efebec5805348df6bc05a855897bdd27d Mon Sep 17 00:00:00 2001 From: wickedcode Date: Thu, 1 May 2025 03:09:25 -0400 Subject: [PATCH 211/230] fix: recommend to use unlinkSync instead of rmSync, which has a better compatibility handling non-English characters --- src/endpoints/assets.js | 4 ++-- src/endpoints/avatars.js | 2 +- src/endpoints/backends/kobold.js | 2 +- src/endpoints/backgrounds.js | 6 +++--- src/endpoints/characters.js | 6 +++--- src/endpoints/chats.js | 4 ++-- src/endpoints/files.js | 2 +- src/endpoints/groups.js | 4 ++-- src/endpoints/openai.js | 2 +- src/endpoints/presets.js | 2 +- src/endpoints/sprites.js | 10 +++++----- src/util.js | 2 +- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/endpoints/assets.js b/src/endpoints/assets.js index 439a62ec5..9420555a4 100644 --- a/src/endpoints/assets.js +++ b/src/endpoints/assets.js @@ -235,14 +235,14 @@ router.post('/download', async (request, response) => { const contentType = mime.lookup(temp_path) || 'application/octet-stream'; response.setHeader('Content-Type', contentType); response.send(fileContent); - fs.rmSync(temp_path); + fs.unlinkSync(temp_path); return; } // Move into asset place console.info('Download finished, moving file from', temp_path, 'to', file_path); fs.copyFileSync(temp_path, file_path); - fs.rmSync(temp_path); + fs.unlinkSync(temp_path); response.sendStatus(200); } catch (error) { diff --git a/src/endpoints/avatars.js b/src/endpoints/avatars.js index 4362a0058..6b665e8ee 100644 --- a/src/endpoints/avatars.js +++ b/src/endpoints/avatars.js @@ -53,7 +53,7 @@ router.post('/upload', async (request, response) => { const filename = request.body.overwrite_name || `${Date.now()}.png`; const pathToNewFile = path.join(request.user.directories.avatars, filename); writeFileAtomicSync(pathToNewFile, image); - fs.rmSync(pathToUpload); + fs.unlinkSync(pathToUpload); return response.send({ path: filename }); } catch (err) { return response.status(400).send('Is not a valid image'); diff --git a/src/endpoints/backends/kobold.js b/src/endpoints/backends/kobold.js index c0c158d62..44c5dd22c 100644 --- a/src/endpoints/backends/kobold.js +++ b/src/endpoints/backends/kobold.js @@ -204,7 +204,7 @@ router.post('/transcribe-audio', async function (request, response) { console.debug('Transcribing audio with KoboldCpp', server); const fileBase64 = fs.readFileSync(request.file.path).toString('base64'); - fs.rmSync(request.file.path); + fs.unlinkSync(request.file.path); const headers = {}; setAdditionalHeadersByType(headers, TEXTGEN_TYPES.KOBOLDCPP, server, request.user.directories); diff --git a/src/endpoints/backgrounds.js b/src/endpoints/backgrounds.js index 0559900eb..131bd1223 100644 --- a/src/endpoints/backgrounds.js +++ b/src/endpoints/backgrounds.js @@ -30,7 +30,7 @@ router.post('/delete', getFileNameValidationFunction('bg'), function (request, r return response.sendStatus(400); } - fs.rmSync(fileName); + fs.unlinkSync(fileName); invalidateThumbnail(request.user.directories, 'bg', request.body.bg); return response.send('ok'); }); @@ -52,7 +52,7 @@ router.post('/rename', function (request, response) { } fs.copyFileSync(oldFileName, newFileName); - fs.rmSync(oldFileName); + fs.unlinkSync(oldFileName); invalidateThumbnail(request.user.directories, 'bg', request.body.old_bg); return response.send('ok'); }); @@ -65,7 +65,7 @@ router.post('/upload', function (request, response) { try { fs.copyFileSync(img_path, path.join(request.user.directories.backgrounds, filename)); - fs.rmSync(img_path); + fs.unlinkSync(img_path); invalidateThumbnail(request.user.directories, 'bg', filename); response.send(filename); } catch (err) { diff --git a/src/endpoints/characters.js b/src/endpoints/characters.js index d5f549a86..59ab52fe2 100644 --- a/src/endpoints/characters.js +++ b/src/endpoints/characters.js @@ -720,7 +720,7 @@ function convertWorldInfoToCharacterBook(name, entries) { */ async function importFromYaml(uploadPath, context, preservedFileName) { const fileText = fs.readFileSync(uploadPath, 'utf8'); - fs.rmSync(uploadPath); + fs.unlinkSync(uploadPath); const yamlData = yaml.parse(fileText); console.info('Importing from YAML'); yamlData.name = sanitize(yamlData.name); @@ -754,7 +754,7 @@ async function importFromYaml(uploadPath, context, preservedFileName) { */ async function importFromCharX(uploadPath, { request }, preservedFileName) { const data = fs.readFileSync(uploadPath).buffer; - fs.rmSync(uploadPath); + fs.unlinkSync(uploadPath); console.info('Importing from CharX'); const cardBuffer = await extractFileFromZipBuffer(data, 'card.json'); @@ -995,7 +995,7 @@ router.post('/rename', validateAvatarUrlMiddleware, async function (request, res } // Remove the old character file - fs.rmSync(oldAvatarPath); + fs.unlinkSync(oldAvatarPath); // Return new avatar name to ST return response.send({ avatar: newAvatarName }); diff --git a/src/endpoints/chats.js b/src/endpoints/chats.js index 2762055a2..08309a170 100644 --- a/src/endpoints/chats.js +++ b/src/endpoints/chats.js @@ -433,7 +433,7 @@ router.post('/rename', validateAvatarUrlMiddleware, async function (request, res } fs.copyFileSync(pathToOriginalFile, pathToRenamedFile); - fs.rmSync(pathToOriginalFile); + fs.unlinkSync(pathToOriginalFile); console.info('Successfully renamed.'); return response.send({ ok: true, sanitizedFileName }); }); @@ -665,7 +665,7 @@ router.post('/group/delete', (request, response) => { const pathToFile = path.join(request.user.directories.groupChats, `${id}.jsonl`); if (fs.existsSync(pathToFile)) { - fs.rmSync(pathToFile); + fs.unlinkSync(pathToFile); return response.send({ ok: true }); } diff --git a/src/endpoints/files.js b/src/endpoints/files.js index 4238b5003..46abc82c9 100644 --- a/src/endpoints/files.js +++ b/src/endpoints/files.js @@ -66,7 +66,7 @@ router.post('/delete', async (request, response) => { return response.status(404).send('File not found'); } - fs.rmSync(pathToDelete); + fs.unlinkSync(pathToDelete); console.info(`Deleted file: ${request.body.path} from ${request.user.profile.handle}`); return response.sendStatus(200); } catch (error) { diff --git a/src/endpoints/groups.js b/src/endpoints/groups.js index d6c5bf249..469bddba6 100644 --- a/src/endpoints/groups.js +++ b/src/endpoints/groups.js @@ -117,7 +117,7 @@ router.post('/delete', async (request, response) => { const pathToFile = path.join(request.user.directories.groupChats, `${id}.jsonl`); if (fs.existsSync(pathToFile)) { - fs.rmSync(pathToFile); + fs.unlinkSync(pathToFile); } } } @@ -126,7 +126,7 @@ router.post('/delete', async (request, response) => { } if (fs.existsSync(pathToGroup)) { - fs.rmSync(pathToGroup); + fs.unlinkSync(pathToGroup); } return response.send({ ok: true }); diff --git a/src/endpoints/openai.js b/src/endpoints/openai.js index 8cbc1bb76..bb8c1a9d2 100644 --- a/src/endpoints/openai.js +++ b/src/endpoints/openai.js @@ -234,7 +234,7 @@ router.post('/transcribe-audio', async (request, response) => { return response.status(500).send(text); } - fs.rmSync(request.file.path); + fs.unlinkSync(request.file.path); const data = await result.json(); console.debug('OpenAI transcription response', data); return response.json(data); diff --git a/src/endpoints/presets.js b/src/endpoints/presets.js index 7c5db1fdb..ab57f355b 100644 --- a/src/endpoints/presets.js +++ b/src/endpoints/presets.js @@ -124,7 +124,7 @@ router.post('/delete-openai', function (request, response) { const pathToFile = path.join(request.user.directories.openAI_Settings, `${name}.json`); if (fs.existsSync(pathToFile)) { - fs.rmSync(pathToFile); + fs.unlinkSync(pathToFile); return response.send({ ok: true }); } diff --git a/src/endpoints/sprites.js b/src/endpoints/sprites.js index 15a21bff1..d9a380e65 100644 --- a/src/endpoints/sprites.js +++ b/src/endpoints/sprites.js @@ -165,7 +165,7 @@ router.post('/delete', async (request, response) => { // Remove existing sprite with the same label for (const file of files) { if (path.parse(file).name === spriteName) { - fs.rmSync(path.join(spritesPath, file)); + fs.unlinkSync(path.join(spritesPath, file)); } } @@ -206,7 +206,7 @@ router.post('/upload-zip', async (request, response) => { const existingFile = files.find(file => path.parse(file).name === path.parse(filename).name); if (existingFile) { - fs.rmSync(path.join(spritesPath, existingFile)); + fs.unlinkSync(path.join(spritesPath, existingFile)); } // Write sprite buffer to disk @@ -215,7 +215,7 @@ router.post('/upload-zip', async (request, response) => { } // Remove uploaded ZIP file - fs.rmSync(spritePackPath); + fs.unlinkSync(spritePackPath); return response.send({ count: sprites.length }); } catch (error) { console.error(error); @@ -251,7 +251,7 @@ router.post('/upload', async (request, response) => { // Remove existing sprite with the same label for (const file of files) { if (path.parse(file).name === spriteName) { - fs.rmSync(path.join(spritesPath, file)); + fs.unlinkSync(path.join(spritesPath, file)); } } @@ -261,7 +261,7 @@ router.post('/upload', async (request, response) => { // Copy uploaded file to sprites folder fs.cpSync(spritePath, pathToFile); // Remove uploaded file - fs.rmSync(spritePath); + fs.unlinkSync(spritePath); return response.sendStatus(200); } catch (error) { console.error(error); diff --git a/src/util.js b/src/util.js index ec94ceb1a..c1d1ed4b4 100644 --- a/src/util.js +++ b/src/util.js @@ -419,7 +419,7 @@ export function removeOldBackups(directory, prefix, limit = null) { break; } - fs.rmSync(oldest); + fs.unlinkSync(oldest); } } } From db2971c82d226b08ddeaff7e3f843ee5bf95d22c Mon Sep 17 00:00:00 2001 From: cloak1505 <170299980+cloak1505@users.noreply.github.com> Date: Thu, 1 May 2025 03:24:28 -0500 Subject: [PATCH 212/230] Remove Pygmalion instruct template (duplicate of Metharme) ST already applies user sequence as stop string, so Pygmalion's <|user|> stop_sequence is meaningless. --- default/content/index.json | 6 +---- .../context/{Pygmalion.json => Metharme.json} | 2 +- .../content/presets/instruct/Pygmalion.json | 22 ------------------- 3 files changed, 2 insertions(+), 28 deletions(-) rename default/content/presets/context/{Pygmalion.json => Metharme.json} (95%) delete mode 100644 default/content/presets/instruct/Pygmalion.json diff --git a/default/content/index.json b/default/content/index.json index 3557ea8e0..c74592051 100644 --- a/default/content/index.json +++ b/default/content/index.json @@ -540,7 +540,7 @@ "type": "context" }, { - "filename": "presets/context/Pygmalion.json", + "filename": "presets/context/Metharme.json", "type": "context" }, { @@ -619,10 +619,6 @@ "filename": "presets/instruct/OpenOrca-OpenChat.json", "type": "instruct" }, - { - "filename": "presets/instruct/Pygmalion.json", - "type": "instruct" - }, { "filename": "presets/instruct/Story.json", "type": "instruct" diff --git a/default/content/presets/context/Pygmalion.json b/default/content/presets/context/Metharme.json similarity index 95% rename from default/content/presets/context/Pygmalion.json rename to default/content/presets/context/Metharme.json index 38a81adc6..f27b140bf 100644 --- a/default/content/presets/context/Pygmalion.json +++ b/default/content/presets/context/Metharme.json @@ -6,5 +6,5 @@ "always_force_name2": true, "trim_sentences": false, "single_line": false, - "name": "Pygmalion" + "name": "Metharme" } diff --git a/default/content/presets/instruct/Pygmalion.json b/default/content/presets/instruct/Pygmalion.json deleted file mode 100644 index 96cd55312..000000000 --- a/default/content/presets/instruct/Pygmalion.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "input_sequence": "<|user|>", - "output_sequence": "<|model|>", - "last_output_sequence": "", - "system_sequence": "", - "stop_sequence": "<|user|>", - "wrap": false, - "macro": true, - "names_behavior": "force", - "activation_regex": "", - "system_sequence_prefix": "<|system|>", - "system_sequence_suffix": "", - "first_output_sequence": "", - "skip_examples": false, - "output_suffix": "", - "input_suffix": "", - "system_suffix": "", - "user_alignment_message": "", - "system_same_as_user": true, - "last_system_sequence": "", - "name": "Pygmalion" -} From 2301b5324adcdfda338800855af7e278718cc256 Mon Sep 17 00:00:00 2001 From: huisman <23581164+huisman@users.noreply.github.com> Date: Thu, 1 May 2025 11:29:39 +0000 Subject: [PATCH 213/230] Update ollama links to current ollama github url --- public/index.html | 4 ++-- src/constants.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/index.html b/public/index.html index 89c429642..f3b04f1bc 100644 --- a/public/index.html +++ b/public/index.html @@ -2622,8 +2622,8 @@
diff --git a/src/constants.js b/src/constants.js index 3b18e732f..3d079d829 100644 --- a/src/constants.js +++ b/src/constants.js @@ -284,7 +284,7 @@ export const TOGETHERAI_KEYS = [ 'stop', ]; -// https://github.com/jmorganca/ollama/blob/main/docs/api.md#request-with-options +// https://github.com/ollama/ollama/blob/main/docs/api.md#request-with-options export const OLLAMA_KEYS = [ 'num_predict', 'num_ctx', From a08972759178c9704583c46a0820ce512c3ea32c Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 1 May 2025 17:46:02 +0300 Subject: [PATCH 214/230] Extract templates, replace pagination format --- public/script.js | 34 +++++++++++------------ public/scripts/templates/emptyBlock.html | 7 +++++ public/scripts/templates/hiddenBlock.html | 6 ++++ public/scripts/textgen-models.js | 17 ++---------- public/scripts/utils.js | 15 +--------- 5 files changed, 33 insertions(+), 46 deletions(-) create mode 100644 public/scripts/templates/emptyBlock.html create mode 100644 public/scripts/templates/hiddenBlock.html diff --git a/public/script.js b/public/script.js index b071e7e34..960ed5c55 100644 --- a/public/script.js +++ b/public/script.js @@ -1426,30 +1426,26 @@ function getBackBlock() { return template; } -function getEmptyBlock() { +async function getEmptyBlock() { const icons = ['fa-dragon', 'fa-otter', 'fa-kiwi-bird', 'fa-crow', 'fa-frog']; const texts = [t`Here be dragons`, t`Otterly empty`, t`Kiwibunga`, t`Pump-a-Rum`, t`Croak it`]; const roll = new Date().getMinutes() % icons.length; - const emptyBlock = ` -
- -

${texts[roll]}

-

` + t`There are no items to display.` + `

-
`; + const params = { + text: texts[roll], + icon: icons[roll], + }; + const emptyBlock = await renderTemplateAsync('emptyBlock', params); return $(emptyBlock); } /** * @param {number} hidden Number of hidden characters */ -function getHiddenBlock(hidden) { - const hiddenBlock = ` -
- -

` + (hidden > 1 ? t`${hidden} characters hidden.` : t`${hidden} character hidden.`) + `

-
-
-
`; +async function getHiddenBlock(hidden) { + const params = { + text: (hidden > 1 ? t`${hidden} characters hidden.` : t`${hidden} character hidden.`), + }; + const hiddenBlock = await renderTemplateAsync('hiddenBlock', params); return $(hiddenBlock); } @@ -1542,13 +1538,14 @@ export async function printCharacters(fullRefresh = false) { nextText: '>', formatNavigator: PAGINATION_TEMPLATE, showNavigator: true, - callback: function (/** @type {Entity[]} */ data) { + callback: async function (/** @type {Entity[]} */ data) { $(listId).empty(); if (power_user.bogus_folders && isBogusFolderOpen()) { $(listId).append(getBackBlock()); } if (!data.length) { - $(listId).append(getEmptyBlock()); + const emptyBlock = await getEmptyBlock(); + $(listId).append(emptyBlock); } let displayCount = 0; for (const i of data) { @@ -1569,7 +1566,8 @@ export async function printCharacters(fullRefresh = false) { const hidden = (characters.length + groups.length) - displayCount; if (hidden > 0 && entitiesFilter.hasAnyFilter()) { - $(listId).append(getHiddenBlock(hidden)); + const hiddenBlock = await getHiddenBlock(hidden); + $(listId).append(hiddenBlock); } localizePagination($('#rm_print_characters_pagination')); diff --git a/public/scripts/templates/emptyBlock.html b/public/scripts/templates/emptyBlock.html new file mode 100644 index 000000000..3ff057acd --- /dev/null +++ b/public/scripts/templates/emptyBlock.html @@ -0,0 +1,7 @@ +
+ +

{{text}}

+

+ There are no items to display. +

+
diff --git a/public/scripts/templates/hiddenBlock.html b/public/scripts/templates/hiddenBlock.html new file mode 100644 index 000000000..6b1d84406 --- /dev/null +++ b/public/scripts/templates/hiddenBlock.html @@ -0,0 +1,6 @@ +
+ +

{{text}}

+
+
+
diff --git a/public/scripts/textgen-models.js b/public/scripts/textgen-models.js index dc1a1e644..a607d87b9 100644 --- a/public/scripts/textgen-models.js +++ b/public/scripts/textgen-models.js @@ -5,9 +5,9 @@ import { textgenerationwebui_settings as textgen_settings, textgen_types } from import { tokenizers } from './tokenizers.js'; import { renderTemplateAsync } from './templates.js'; import { POPUP_TYPE, callGenericPopup } from './popup.js'; -import { t, translate } from './i18n.js'; +import { t } from './i18n.js'; import { accountStorage } from './util/AccountStorage.js'; -import { localizePagination } from './utils.js'; +import { localizePagination, PAGINATION_TEMPLATE } from './utils.js'; let mancerModels = []; let togetherModels = []; @@ -362,18 +362,7 @@ export async function loadFeatherlessModels(data) { showSizeChanger: false, prevText: '<', nextText: '>', - formatNavigator: function (currentPage, totalPage) { - let translated_of; - try { - translated_of = translate('pagination_of'); - if (translated_of == 'pagination_of') { - translated_of = 'of'; - } - } catch (e) { - translated_of = 'of'; - } - return (currentPage - 1) * perPage + 1 + ' - ' + currentPage * perPage + ` ${translated_of} ` + totalPage * perPage; - }, + formatNavigator: PAGINATION_TEMPLATE, showNavigator: true, callback: function (modelsOnPage, pagination) { modelCardBlock.innerHTML = ''; diff --git a/public/scripts/utils.js b/public/scripts/utils.js index a12492aa4..c760b9f97 100644 --- a/public/scripts/utils.js +++ b/public/scripts/utils.js @@ -18,21 +18,8 @@ import { getCurrentLocale, t, translate } from './i18n.js'; /** * Function returning pagination status string template. - * @type {function} */ -export const PAGINATION_TEMPLATE = function() { - let translated_of; - try { - translated_of = translate('pagination_of'); - if (translated_of == 'pagination_of') { - translated_of = 'of'; - } - } catch (e) { - console.error(e); - translated_of = 'of'; - } - return `<%= rangeStart %>-<%= rangeEnd %> ${translated_of} <%= totalNumber %>`; -}; +export const PAGINATION_TEMPLATE = '<%= rangeStart %>-<%= rangeEnd %> .. <%= totalNumber %>'; export const localizePagination = function(container) { let options = container.find('option'); From c677f0324a3a3fa808c1bea02ecb48c85d31cfe6 Mon Sep 17 00:00:00 2001 From: Cal Date: Thu, 1 May 2025 15:16:51 -0600 Subject: [PATCH 215/230] Add argument to command --- public/scripts/slash-commands.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 66555216c..8ea913160 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -315,6 +315,11 @@ export function initDefaultSlashCommands() { typeList: [ARGUMENT_TYPE.NUMBER], enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), }), + SlashCommandNamedArgument.fromProps({ + name: 'name', + description: 'Optional custom display name to use for this system narrator message.', + typeList: [ARGUMENT_TYPE.STRING], + }), SlashCommandNamedArgument.fromProps({ name: 'return', description: 'The way how you want the return value to be provided', @@ -3851,7 +3856,7 @@ export async function sendMessageAs(args, text) { export async function sendNarratorMessage(args, text) { text = String(text ?? ''); - const name = chat_metadata[NARRATOR_NAME_KEY] || NARRATOR_NAME_DEFAULT; + const name = args.name ?? (chat_metadata[NARRATOR_NAME_KEY] || NARRATOR_NAME_DEFAULT); // Messages that do nothing but set bias will be hidden from the context const bias = extractMessageBias(text); const isSystem = bias && !removeMacros(text).length; From b8f7675d8ce41c43ac73c8cb549e8971f700ef98 Mon Sep 17 00:00:00 2001 From: InspectorCaracal <51038201+InspectorCaracal@users.noreply.github.com> Date: Fri, 2 May 2025 14:57:51 -0600 Subject: [PATCH 216/230] don't inject empty jb --- public/script.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/public/script.js b/public/script.js index 0afd957fb..caf02f576 100644 --- a/public/script.js +++ b/public/script.js @@ -4214,12 +4214,15 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro ? substituteParams(jailbreak, name1, name2, (power_user.sysprompt.post_history ?? '')) : baseChatReplace(power_user.sysprompt.post_history, name1, name2); - // When continuing generation of previous output, last user message precedes the message to continue - if (isContinue) { - coreChat.splice(coreChat.length - 1, 0, { mes: jailbreak, is_user: true }); - } - else { - coreChat.push({ mes: jailbreak, is_user: true }); + // Only inject the jb if there is one + if (jailbreak && typeof(jailbreak) != 'undefined') { + // When continuing generation of previous output, last user message precedes the message to continue + if (isContinue) { + coreChat.splice(coreChat.length - 1, 0, { mes: jailbreak, is_user: true }); + } + else { + coreChat.push({ mes: jailbreak, is_user: true }); + } } } From 07a601744361ac2c43f02259c77d6189066cf507 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 3 May 2025 01:18:58 +0300 Subject: [PATCH 217/230] Remove redundant condition --- public/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/script.js b/public/script.js index caf02f576..7d851e87f 100644 --- a/public/script.js +++ b/public/script.js @@ -4215,7 +4215,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro : baseChatReplace(power_user.sysprompt.post_history, name1, name2); // Only inject the jb if there is one - if (jailbreak && typeof(jailbreak) != 'undefined') { + if (jailbreak) { // When continuing generation of previous output, last user message precedes the message to continue if (isContinue) { coreChat.splice(coreChat.length - 1, 0, { mes: jailbreak, is_user: true }); From 1822c4f91b99ec15f64c4ac4b0bdc01360427af3 Mon Sep 17 00:00:00 2001 From: Yokayo Date: Sat, 3 May 2025 18:12:18 +0700 Subject: [PATCH 218/230] More work on tl --- public/index.html | 2 +- public/locales/ru-ru.json | 4 ++-- public/script.js | 13 ++++++++++--- public/scripts/group-chats.js | 20 ++++++++++++++------ public/scripts/personas.js | 9 ++++++--- public/scripts/tags.js | 2 +- public/scripts/utils.js | 30 +++++++++++++++++++----------- 7 files changed, 53 insertions(+), 27 deletions(-) diff --git a/public/index.html b/public/index.html index 3f8cc407f..bf8d129ac 100644 --- a/public/index.html +++ b/public/index.html @@ -6707,7 +6707,7 @@
- Go back + Go back
diff --git a/public/locales/ru-ru.json b/public/locales/ru-ru.json index 00f94e88d..328051038 100644 --- a/public/locales/ru-ru.json +++ b/public/locales/ru-ru.json @@ -2403,7 +2403,6 @@ "Croak it": "Только кваканье лягушек и стрёкот сверчков", "${0} character hidden.": "Персонажей скрыто: ${0}.", "${0} characters hidden.": "Персонажей скрыто: ${0}.", - "pagination_of": "из", "/ page": "/ стр.", "Context Length": "Размер контекста", "Added On": "Добавлена", @@ -2421,5 +2420,6 @@ "Text Completion Preset": "Пресет для режима Text Completion", "Update enabled": "Обновить включенные", "Could not connect to API": "Не удалось подключиться к API", - "Connected to API": "Соединение с API установлено" + "Connected to API": "Соединение с API установлено", + "Go back": "Назад" } diff --git a/public/script.js b/public/script.js index 960ed5c55..c73efb23b 100644 --- a/public/script.js +++ b/public/script.js @@ -177,6 +177,8 @@ import { uuidv4, equalsIgnoreCaseAndAccents, localizePagination, + renderPaginationDropdown, + paginationDropdownChangeHandler, } from './scripts/utils.js'; import { debounce_timeout, IGNORE_SYMBOL } from './scripts/constants.js'; @@ -1525,10 +1527,12 @@ export async function printCharacters(fullRefresh = false) { const entities = getEntitiesList({ doFilter: true }); + const pageSize = Number(accountStorage.getItem(storageKey)) || per_page_default; + console.log('pageSize for characters = ' + pageSize); + const sizeChangerOptions = [10, 25, 50, 100, 250, 500, 1000]; $('#rm_print_characters_pagination').pagination({ dataSource: entities, - pageSize: Number(accountStorage.getItem(storageKey)) || per_page_default, - sizeChangerOptions: [10, 25, 50, 100, 250, 500, 1000], + pageSize, pageRange: 1, pageNumber: saveCharactersPage || 1, position: 'top', @@ -1537,6 +1541,7 @@ export async function printCharacters(fullRefresh = false) { prevText: '<', nextText: '>', formatNavigator: PAGINATION_TEMPLATE, + formatSizeChanger: renderPaginationDropdown(pageSize, sizeChangerOptions), showNavigator: true, callback: async function (/** @type {Entity[]} */ data) { $(listId).empty(); @@ -1573,8 +1578,10 @@ export async function printCharacters(fullRefresh = false) { eventSource.emit(event_types.CHARACTER_PAGE_LOADED); }, - afterSizeSelectorChange: function (e) { + afterSizeSelectorChange: function (e, size) { accountStorage.setItem(storageKey, e.target.value); + console.log('pageSize for characters after getting updated = ' + accountStorage.getItem(storageKey)); + paginationDropdownChangeHandler(e, size); }, afterPaging: function (e) { saveCharactersPage = e; diff --git a/public/scripts/group-chats.js b/public/scripts/group-chats.js index 78ce0b275..b3b0f3806 100644 --- a/public/scripts/group-chats.js +++ b/public/scripts/group-chats.js @@ -14,6 +14,8 @@ import { resetScrollHeight, initScrollHeight, localizePagination, + renderPaginationDropdown, + paginationDropdownChangeHandler, } from './utils.js'; import { RA_CountCharTokens, humanizedDateTime, dragElement, favsToHotswap, getMessageTimeStamp } from './RossAscends-mods.js'; import { power_user, loadMovingUIState, sortEntitiesList } from './power-user.js'; @@ -1375,6 +1377,8 @@ function getGroupCharacters({ doFilter, onlyMembers } = {}) { function printGroupCandidates() { const storageKey = 'GroupCandidates_PerPage'; + const pageSize = Number(accountStorage.getItem(storageKey)) || 5; + const sizeChangerOptions = [5, 10, 25, 50, 100, 200, 500, 1000]; $('#rm_group_add_members_pagination').pagination({ dataSource: getGroupCharacters({ doFilter: true, onlyMembers: false }), pageRange: 1, @@ -1383,12 +1387,13 @@ function printGroupCandidates() { prevText: '<', nextText: '>', formatNavigator: PAGINATION_TEMPLATE, + formatSizeChanger: renderPaginationDropdown(pageSize, sizeChangerOptions), showNavigator: true, showSizeChanger: true, - pageSize: Number(accountStorage.getItem(storageKey)) || 5, - sizeChangerOptions: [5, 10, 25, 50, 100, 200, 500, 1000], - afterSizeSelectorChange: function (e) { + pageSize, + afterSizeSelectorChange: function (e, size) { accountStorage.setItem(storageKey, e.target.value); + paginationDropdownChangeHandler(e, size); }, callback: function (data) { $('#rm_group_add_members').empty(); @@ -1404,6 +1409,8 @@ function printGroupMembers() { const storageKey = 'GroupMembers_PerPage'; $('.rm_group_members_pagination').each(function () { let that = this; + const pageSize = Number(accountStorage.getItem(storageKey)) || 5; + const sizeChangerOptions = [5, 10, 25, 50, 100, 200, 500, 1000]; $(this).pagination({ dataSource: getGroupCharacters({ doFilter: false, onlyMembers: true }), pageRange: 1, @@ -1414,10 +1421,11 @@ function printGroupMembers() { formatNavigator: PAGINATION_TEMPLATE, showNavigator: true, showSizeChanger: true, - pageSize: Number(accountStorage.getItem(storageKey)) || 5, - sizeChangerOptions: [5, 10, 25, 50, 100, 200, 500, 1000], - afterSizeSelectorChange: function (e) { + formatSizeChanger: renderPaginationDropdown(pageSize, sizeChangerOptions), + pageSize, + afterSizeSelectorChange: function (e, size) { accountStorage.setItem(storageKey, e.target.value); + paginationDropdownChangeHandler(e, size); }, callback: function (data) { $('.rm_group_members').empty(); diff --git a/public/scripts/personas.js b/public/scripts/personas.js index 6b9fc1e0d..1aeefa0b9 100644 --- a/public/scripts/personas.js +++ b/public/scripts/personas.js @@ -22,7 +22,7 @@ import { } from '../script.js'; import { persona_description_positions, power_user } from './power-user.js'; import { getTokenCountAsync } from './tokenizers.js'; -import { PAGINATION_TEMPLATE, clearInfoBlock, debounce, delay, download, ensureImageFormatSupported, flashHighlight, getBase64Async, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, parseJsonFile, setInfoBlock, localizePagination } from './utils.js'; +import { PAGINATION_TEMPLATE, clearInfoBlock, debounce, delay, download, ensureImageFormatSupported, flashHighlight, getBase64Async, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, parseJsonFile, setInfoBlock, localizePagination, renderPaginationDropdown, paginationDropdownChangeHandler } from './utils.js'; import { debounce_timeout } from './constants.js'; import { FILTER_TYPES, FilterHelper } from './filters.js'; import { groups, selected_group } from './group-chats.js'; @@ -250,16 +250,18 @@ export async function getUserAvatars(doRender = true, openPageAt = '') { const storageKey = 'Personas_PerPage'; const listId = '#user_avatar_block'; const perPage = Number(accountStorage.getItem(storageKey)) || 5; + const sizeChangerOptions = [5, 10, 25, 50, 100, 250, 500, 1000]; $('#persona_pagination_container').pagination({ dataSource: entities, pageSize: perPage, - sizeChangerOptions: [5, 10, 25, 50, 100, 250, 500, 1000], + sizeChangerOptions, pageRange: 1, pageNumber: savePersonasPage || 1, position: 'top', showPageNumbers: false, showSizeChanger: true, + formatSizeChanger: renderPaginationDropdown(perPage, sizeChangerOptions), prevText: '<', nextText: '>', formatNavigator: PAGINATION_TEMPLATE, @@ -272,8 +274,9 @@ export async function getUserAvatars(doRender = true, openPageAt = '') { updatePersonaUIStates(); localizePagination($('#persona_pagination_container')); }, - afterSizeSelectorChange: function (e) { + afterSizeSelectorChange: function (e, size) { accountStorage.setItem(storageKey, e.target.value); + paginationDropdownChangeHandler(e, size); }, afterPaging: function (e) { savePersonasPage = e; diff --git a/public/scripts/tags.js b/public/scripts/tags.js index 1b9ba8502..2705da21c 100644 --- a/public/scripts/tags.js +++ b/public/scripts/tags.js @@ -318,7 +318,7 @@ function getTagBlock(tag, entities, hidden = 0, isUseless = false) { template.find('.avatar').css({ 'background-color': tag.color, 'color': tag.color2 }).attr('title', `[Folder] ${tag.name}`); template.find('.ch_name').text(tag.name).attr('title', `[Folder] ${tag.name}`); template.find('.bogus_folder_hidden_counter').text(hidden > 0 ? `${hidden} hidden` : ''); - template.find('.bogus_folder_counter').text(`${count} ${count != 1 ? 'characters' : 'character'}`); + template.find('.bogus_folder_counter').text(`${count} ` + (count != 1 ? t`characters` : t`character`)); template.find('.bogus_folder_icon').addClass(tagFolder.fa_icon); if (isUseless) template.addClass('useless'); diff --git a/public/scripts/utils.js b/public/scripts/utils.js index c760b9f97..dd500efb4 100644 --- a/public/scripts/utils.js +++ b/public/scripts/utils.js @@ -22,21 +22,29 @@ import { getCurrentLocale, t, translate } from './i18n.js'; export const PAGINATION_TEMPLATE = '<%= rangeStart %>-<%= rangeEnd %> .. <%= totalNumber %>'; export const localizePagination = function(container) { - let options = container.find('option'); - for (let option of options) { - option = $(option); - try { - option.text(option.text().replace('/ page', translate('/ page'))); - } catch (e) { - // means that i18n facilities aren't ready, so there's no point doing anything - console.error(e); - return; - } - } container.find('[title="Next page"]').attr('title', translate('Next page')); container.find('[title="Previous page"]').attr('title', translate('Previous page')); }; +export const renderPaginationDropdown = function(pageSize, sizeChangerOptions) { + let sizeSelect = ``; + return sizeSelect; +} + +export const paginationDropdownChangeHandler = function(event, size) { + let dropdown = $(event?.originalEvent?.currentTarget || event.delegateTarget).find('select'); + dropdown.find('[selected]').removeAttr('selected'); + dropdown.find(`[value=${size}]`).attr('selected', ''); +} + /** * Navigation options for pagination. * @enum {number} From e27fca66283b26e74b8afbc80f9c39b0270b5f4c Mon Sep 17 00:00:00 2001 From: Yokayo Date: Sat, 3 May 2025 18:14:26 +0700 Subject: [PATCH 219/230] eslint fixes --- public/scripts/utils.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/scripts/utils.js b/public/scripts/utils.js index dd500efb4..85b4bf861 100644 --- a/public/scripts/utils.js +++ b/public/scripts/utils.js @@ -27,23 +27,23 @@ export const localizePagination = function(container) { }; export const renderPaginationDropdown = function(pageSize, sizeChangerOptions) { - let sizeSelect = `'; if (sizeChangerOptions.indexOf(pageSize) === -1) { - sizeChangerOptions.unshift(pageSize); - sizeChangerOptions.sort((a, b) => a - b); + sizeChangerOptions.unshift(pageSize); + sizeChangerOptions.sort((a, b) => a - b); } for (let i = 0; i < sizeChangerOptions.length; i++) { - sizeSelect += ``; + sizeSelect += ``; } sizeSelect += ``; return sizeSelect; -} +}; export const paginationDropdownChangeHandler = function(event, size) { let dropdown = $(event?.originalEvent?.currentTarget || event.delegateTarget).find('select'); dropdown.find('[selected]').removeAttr('selected'); dropdown.find(`[value=${size}]`).attr('selected', ''); -} +}; /** * Navigation options for pagination. From b9383ace1e602a7356806e96f667f42e8b24681e Mon Sep 17 00:00:00 2001 From: Yokayo Date: Sat, 3 May 2025 18:16:02 +0700 Subject: [PATCH 220/230] eslint fixes 2 --- public/scripts/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/scripts/utils.js b/public/scripts/utils.js index 85b4bf861..47ff7d139 100644 --- a/public/scripts/utils.js +++ b/public/scripts/utils.js @@ -33,9 +33,9 @@ export const renderPaginationDropdown = function(pageSize, sizeChangerOptions) { sizeChangerOptions.sort((a, b) => a - b); } for (let i = 0; i < sizeChangerOptions.length; i++) { - sizeSelect += ``; + sizeSelect += `'; } - sizeSelect += ``; + sizeSelect += ''; return sizeSelect; }; From a122109e0c130ede997423cdd37b95b95d3c7273 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 4 May 2025 12:26:44 +0300 Subject: [PATCH 221/230] Add new model option 'embed-v4.0' to Cohere vectorization settings Closes #3951 --- public/scripts/extensions/vectors/settings.html | 1 + 1 file changed, 1 insertion(+) diff --git a/public/scripts/extensions/vectors/settings.html b/public/scripts/extensions/vectors/settings.html index 50f693fdc..7657bec17 100644 --- a/public/scripts/extensions/vectors/settings.html +++ b/public/scripts/extensions/vectors/settings.html @@ -97,6 +97,7 @@ Vectorization Model '; + const sizeSelect = document.createElement('select'); + sizeSelect.classList.add('J-paginationjs-size-select'); + if (sizeChangerOptions.indexOf(pageSize) === -1) { sizeChangerOptions.unshift(pageSize); sizeChangerOptions.sort((a, b) => a - b); } + for (let i = 0; i < sizeChangerOptions.length; i++) { - sizeSelect += `'; + const option = document.createElement('option'); + option.value = `${sizeChangerOptions[i]}`; + option.textContent = `${sizeChangerOptions[i]} ${t`/ page`}`; + if (sizeChangerOptions[i] === pageSize) { + option.setAttribute('selected', 'selected'); + } + sizeSelect.appendChild(option); } - sizeSelect += ''; - return sizeSelect; + + return sizeSelect.outerHTML; }; export const paginationDropdownChangeHandler = function(event, size) { diff --git a/public/style.css b/public/style.css index faa7524bc..cc29f1551 100644 --- a/public/style.css +++ b/public/style.css @@ -5715,6 +5715,7 @@ body:not(.movingUI) .drawer-content.maximized { width: unset; margin: 0; font-size: calc(var(--mainFontSize) * 0.85); + padding-right: 20px; } .paginationjs-pages ul li a { From 27f2fac916d5c709e8ae5ecc0f578b8f0a8d8f4e Mon Sep 17 00:00:00 2001 From: Samueras Date: Sun, 4 May 2025 11:59:17 +0200 Subject: [PATCH 223/230] Export swipe_right in public/script.js and add swipe_right to getContext in st-context.js --- public/scripts/st-context.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/scripts/st-context.js b/public/scripts/st-context.js index 0ba1104bd..ebf4b5378 100644 --- a/public/scripts/st-context.js +++ b/public/scripts/st-context.js @@ -50,6 +50,7 @@ import { unshallowCharacter, deleteLastMessage, getCharacterCardFields, + swipe_right, } from '../script.js'; import { extension_settings, @@ -196,6 +197,7 @@ export function getContext() { humanizedDateTime, updateMessageBlock, appendMediaToMessage, + swipe_right, variables: { local: { get: getLocalVariable, From 5f79c0c26220ced26be21576e4331bf5e3350c93 Mon Sep 17 00:00:00 2001 From: Samueras Date: Sun, 4 May 2025 12:05:45 +0200 Subject: [PATCH 224/230] Export swipe_right in public/script.js --- public/script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/script.js b/public/script.js index f2efda2b5..c1b65e24e 100644 --- a/public/script.js +++ b/public/script.js @@ -293,6 +293,7 @@ export { isOdd, countOccurrences, renderTemplate, + swipe_right }; /** From 3165537ce878721d15adf42d10bdecd13b4597a1 Mon Sep 17 00:00:00 2001 From: Samueras Date: Sun, 4 May 2025 12:12:50 +0200 Subject: [PATCH 225/230] Update script.js Added trailing comma --- public/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/script.js b/public/script.js index c1b65e24e..9f948bb32 100644 --- a/public/script.js +++ b/public/script.js @@ -293,7 +293,7 @@ export { isOdd, countOccurrences, renderTemplate, - swipe_right + swipe_right, }; /** From bf9ef8fa0fd56525f004ae6375d54a439edf1d91 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 4 May 2025 14:00:55 +0300 Subject: [PATCH 226/230] Remove debug logs --- public/script.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/public/script.js b/public/script.js index c73efb23b..3d07b782a 100644 --- a/public/script.js +++ b/public/script.js @@ -1528,7 +1528,6 @@ export async function printCharacters(fullRefresh = false) { const entities = getEntitiesList({ doFilter: true }); const pageSize = Number(accountStorage.getItem(storageKey)) || per_page_default; - console.log('pageSize for characters = ' + pageSize); const sizeChangerOptions = [10, 25, 50, 100, 250, 500, 1000]; $('#rm_print_characters_pagination').pagination({ dataSource: entities, @@ -1580,7 +1579,6 @@ export async function printCharacters(fullRefresh = false) { }, afterSizeSelectorChange: function (e, size) { accountStorage.setItem(storageKey, e.target.value); - console.log('pageSize for characters after getting updated = ' + accountStorage.getItem(storageKey)); paginationDropdownChangeHandler(e, size); }, afterPaging: function (e) { From 4e0685f9985542e86896fa94943d0a0eac03b2d6 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 4 May 2025 14:05:44 +0300 Subject: [PATCH 227/230] Revert comment --- public/scripts/utils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/scripts/utils.js b/public/scripts/utils.js index 4c41d547a..6f13a9794 100644 --- a/public/scripts/utils.js +++ b/public/scripts/utils.js @@ -17,7 +17,8 @@ import { groups, selected_group } from './group-chats.js'; import { getCurrentLocale, t } from './i18n.js'; /** - * Function returning pagination status string template. + * Pagination status string template. + * @type {string} */ export const PAGINATION_TEMPLATE = '<%= rangeStart %>-<%= rangeEnd %> .. <%= totalNumber %>'; From 99f47de88b7f86b14ed880add5bb766a4db70ed1 Mon Sep 17 00:00:00 2001 From: Samueras Date: Sun, 4 May 2025 18:15:28 +0200 Subject: [PATCH 228/230] Export Swipe left and right Exporting the swipe_left and the swipe_right functions --- public/script.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/script.js b/public/script.js index 9f948bb32..c75d52e9c 100644 --- a/public/script.js +++ b/public/script.js @@ -9105,7 +9105,7 @@ function formatSwipeCounter(current, total) { * @param {string} [params.source] The source of the swipe event. * @param {boolean} [params.repeated] Is the swipe event repeated. */ -function swipe_left(_event, { source, repeated } = {}) { +export function swipe_left(_event, { source, repeated } = {}) { if (chat.length - 1 === Number(this_edit_mes_id)) { closeMessageEditor(); } @@ -9253,7 +9253,7 @@ function swipe_left(_event, { source, repeated } = {}) { * @param {string} [params.source] The source of the swipe event. * @param {boolean} [params.repeated] Is the swipe event repeated. */ -function swipe_right(_event, { source, repeated } = {}) { +export function swipe_right(_event, { source, repeated } = {}) { if (chat.length - 1 === Number(this_edit_mes_id)) { closeMessageEditor(); } From f0fbd7e3d4003c192822f89f55989a0e821676fb Mon Sep 17 00:00:00 2001 From: Samueras Date: Sun, 4 May 2025 18:17:44 +0200 Subject: [PATCH 229/230] added swipe left and right to st-context Added swipe_right and swipe_left to st-context as a swipe group. --- public/scripts/st-context.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/scripts/st-context.js b/public/scripts/st-context.js index ebf4b5378..dce502bea 100644 --- a/public/scripts/st-context.js +++ b/public/scripts/st-context.js @@ -51,6 +51,7 @@ import { deleteLastMessage, getCharacterCardFields, swipe_right, + swipe_left, } from '../script.js'; import { extension_settings, @@ -197,7 +198,7 @@ export function getContext() { humanizedDateTime, updateMessageBlock, appendMediaToMessage, - swipe_right, + swipe: { left: swipe_left, right: swipe_right }, variables: { local: { get: getLocalVariable, From 3db2db1c657d1a75ed4d8a61923cc5e82f9c198a Mon Sep 17 00:00:00 2001 From: Samueras Date: Sun, 4 May 2025 18:20:40 +0200 Subject: [PATCH 230/230] Removed Swipe_right from legacy export Removed Swipe_right from legacy export --- public/script.js | 1 - 1 file changed, 1 deletion(-) diff --git a/public/script.js b/public/script.js index c75d52e9c..0e5c3cd8c 100644 --- a/public/script.js +++ b/public/script.js @@ -293,7 +293,6 @@ export { isOdd, countOccurrences, renderTemplate, - swipe_right, }; /**