diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 92a02412a..10734f74e 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -80,6 +80,8 @@ body: required: true - label: I have checked the [docs](https://docs.sillytavern.app/) ![important](https://img.shields.io/badge/Important!-F6094E) required: true + - label: I confirm that my issue is not related to third-party content, unofficial extension or patch. If in doubt, check with a new [user account](https://docs.sillytavern.app/administration/multi-user/) and with extensions disabled + required: true - type: markdown attributes: diff --git a/default/config.yaml b/default/config.yaml index 8f3cc4d74..5d7597a6c 100644 --- a/default/config.yaml +++ b/default/config.yaml @@ -210,3 +210,5 @@ claude: cachingAtDepth: -1 # -- SERVER PLUGIN CONFIGURATION -- enableServerPlugins: false +# Attempt to automatically update server plugins on startup +enableServerPluginsAutoUpdate: true diff --git a/package-lock.json b/package-lock.json index 3c5615536..6abfa299c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sillytavern", - "version": "1.12.11", + "version": "1.12.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sillytavern", - "version": "1.12.11", + "version": "1.12.12", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index fe79108b5..eb0edbd84 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "type": "git", "url": "https://github.com/SillyTavern/SillyTavern.git" }, - "version": "1.12.11", + "version": "1.12.12", "scripts": { "start": "node server.js", "debug": "node server.js --inspect", diff --git a/plugins.js b/plugins.js index 2ca5d9957..b296ffd3c 100644 --- a/plugins.js +++ b/plugins.js @@ -48,6 +48,13 @@ async function updatePlugins() { console.log(`Updating plugin ${color.green(directory)}...`); const pluginPath = path.join(pluginsPath, directory); const pluginRepo = git(pluginPath); + + const isRepo = await pluginRepo.checkIsRepo(); + if (!isRepo) { + console.log(`Directory ${color.yellow(directory)} is not a Git repository`); + continue; + } + await pluginRepo.fetch(); const commitHash = await pluginRepo.revparse(['HEAD']); const trackingBranch = await pluginRepo.revparse(['--abbrev-ref', '@{u}']); diff --git a/public/script.js b/public/script.js index 16e99e412..9e31a9fa1 100644 --- a/public/script.js +++ b/public/script.js @@ -5931,6 +5931,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes, chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); chat[chat.length - 1]['extra']['reasoning'] = reasoning; + chat[chat.length - 1]['extra']['reasoning_duration'] = null; if (power_user.message_token_count_enabled) { const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes']; chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); @@ -5953,6 +5954,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes, chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); chat[chat.length - 1]['extra']['reasoning'] = reasoning; + chat[chat.length - 1]['extra']['reasoning_duration'] = null; if (power_user.message_token_count_enabled) { const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes']; chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); @@ -5972,6 +5974,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes, chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); chat[chat.length - 1]['extra']['reasoning'] += reasoning; + // We don't know if the reasoning duration extended, so we don't update it here on purpose. if (power_user.message_token_count_enabled) { const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes']; chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); @@ -5991,6 +5994,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes, chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); chat[chat.length - 1]['extra']['reasoning'] = reasoning; + chat[chat.length - 1]['extra']['reasoning_duration'] = null; if (power_user.trim_spaces) { getMessage = getMessage.trim(); } diff --git a/public/scripts/extensions/quick-reply/src/ui/SettingsUi.js b/public/scripts/extensions/quick-reply/src/ui/SettingsUi.js index df6bb081f..a4bff8d06 100644 --- a/public/scripts/extensions/quick-reply/src/ui/SettingsUi.js +++ b/public/scripts/extensions/quick-reply/src/ui/SettingsUi.js @@ -346,7 +346,7 @@ export class SettingsUi { } async addQrSet() { - const name = await Popup.show.input('Create a new World Info', 'Enter a name for the new Quick Reply Set:'); + const name = await Popup.show.input('Create a new Quick Reply Set', 'Enter a name for the new Quick Reply Set:'); if (name && name.length > 0) { const oldQrs = QuickReplySet.get(name); if (oldQrs) { diff --git a/public/scripts/reasoning.js b/public/scripts/reasoning.js index 425003480..c5bd219a1 100644 --- a/public/scripts/reasoning.js +++ b/public/scripts/reasoning.js @@ -106,7 +106,7 @@ export function isHiddenReasoningModel() { { name: 'gemini-2.0-pro-exp', func: FUNCS.startsWith }, ]; - const model = getChatCompletionModel(); + const model = getChatCompletionModel() || ''; const isHidden = hiddenReasoningModels.some(({ name, func }) => func(model, name)); return isHidden; diff --git a/public/scripts/textgen-models.js b/public/scripts/textgen-models.js index 962df1a5d..98bffa4ff 100644 --- a/public/scripts/textgen-models.js +++ b/public/scripts/textgen-models.js @@ -58,9 +58,14 @@ const OPENROUTER_PROVIDERS = [ 'Minimax', 'Nineteen', 'Liquid', + 'InferenceNet', + 'Friendli', + 'AionLabs', + 'Alibaba', 'Nebius', 'Chutes', 'Kluster', + 'Targon', '01.AI', 'HuggingFace', 'Mancer', diff --git a/src/endpoints/stable-diffusion.js b/src/endpoints/stable-diffusion.js index dd6dc9adf..c5d94e4b6 100644 --- a/src/endpoints/stable-diffusion.js +++ b/src/endpoints/stable-diffusion.js @@ -1275,7 +1275,8 @@ falai.post('/generate', jsonParser, async (request, response) => { num_inference_steps: request.body.steps, seed: request.body.seed ?? null, guidance_scale: request.body.guidance, - enable_safety_checker: false, + enable_safety_checker: false, // Disable general safety checks + safety_tolerance: 6 // Make Flux the least strict }; console.debug('FAL.AI request:', requestBody); diff --git a/src/plugin-loader.js b/src/plugin-loader.js index dd97a80df..744c3396d 100644 --- a/src/plugin-loader.js +++ b/src/plugin-loader.js @@ -3,8 +3,12 @@ import path from 'node:path'; import url from 'node:url'; import express from 'express'; -import { getConfigValue } from './util.js'; -const enableServerPlugins = getConfigValue('enableServerPlugins', false); +import { default as git } from 'simple-git'; +import { sync as commandExistsSync } from 'command-exists'; +import { getConfigValue, color } from './util.js'; + +const enableServerPlugins = !!getConfigValue('enableServerPlugins', false); +const enableServerPluginsAutoUpdate = !!getConfigValue('enableServerPluginsAutoUpdate', true); /** * Map of loaded plugins. @@ -54,6 +58,8 @@ export async function loadPlugins(app, pluginsPath) { return emptyFn; } + await updatePlugins(pluginsPath); + for (const file of files) { const pluginFilePath = path.join(pluginsPath, file); @@ -70,6 +76,10 @@ export async function loadPlugins(app, pluginsPath) { await loadFromFile(app, pluginFilePath, exitHooks); } + if (loadedPlugins.size > 0) { + console.log(`${loadedPlugins.size} server plugin(s) are currently loaded. Make sure you know exactly what they do, and only install plugins from trusted sources!`); + } + // Call all plugin "exit" functions at once and wait for them to finish return () => Promise.all(exitHooks.map(exitFn => exitFn())); } @@ -214,3 +224,65 @@ async function initPlugin(app, plugin, exitHooks) { return true; } + +/** + * Automatically update all git plugins in the ./plugins directory + * @param {string} pluginsPath Path to plugins directory + */ +async function updatePlugins(pluginsPath) { + if (!enableServerPluginsAutoUpdate) { + return; + } + + const directories = fs.readdirSync(pluginsPath) + .filter(file => !file.startsWith('.')) + .filter(file => fs.statSync(path.join(pluginsPath, file)).isDirectory()); + + if (directories.length === 0) { + return; + } + + console.log(color.blue('Auto-updating server plugins... Set'), color.yellow('enableServerPluginsAutoUpdate: false'), color.blue('in config.yaml to disable this feature.')); + + if (!commandExistsSync('git')) { + console.error(color.red('Git is not installed. Please install Git to enable auto-updating of server plugins.')); + return; + } + + let pluginsToUpdate = 0; + + for (const directory of directories) { + try { + const pluginPath = path.join(pluginsPath, directory); + const pluginRepo = git(pluginPath); + + const isRepo = await pluginRepo.checkIsRepo(); + if (!isRepo) { + continue; + } + + await pluginRepo.fetch(); + const commitHash = await pluginRepo.revparse(['HEAD']); + const trackingBranch = await pluginRepo.revparse(['--abbrev-ref', '@{u}']); + const log = await pluginRepo.log({ + from: commitHash, + to: trackingBranch, + }); + + if (log.total === 0) { + continue; + } + + pluginsToUpdate++; + await pluginRepo.pull(); + const latestCommit = await pluginRepo.revparse(['HEAD']); + console.log(`Plugin ${color.green(directory)} updated to commit ${color.cyan(latestCommit)}`); + } catch (error) { + console.error(color.red(`Failed to update plugin ${directory}: ${error.message}`)); + } + } + + if (pluginsToUpdate === 0) { + console.log('All plugins are up to date.'); + } +}