Merge branch 'staging' into reasoning-parsing-streaming

1156b648eb2713ed904fed45fff79b50cbb6a62d

Cohee <18619528+Cohee1207@users.noreply.github.com>

11 files changed, +101 -8Showing whitespace changes
.github/ISSUE_TEMPLATE/bug-report.yml+2 -0
@@ -80,6 +80,8 @@ body:
8080 required: true
8181 - label: I have checked the [docs](https://docs.sillytavern.app/) ![important](https://img.shields.io/badge/Important!-F6094E)
8282 required: true
83+ - 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
84+ required: true
8385
8486 - type: markdown
8587 attributes:
default/config.yaml+2 -0
@@ -210,3 +210,5 @@ claude:
210210 cachingAtDepth: -1
211211# -- SERVER PLUGIN CONFIGURATION --
212212enableServerPlugins: false
213+# Attempt to automatically update server plugins on startup
214+enableServerPluginsAutoUpdate: true
package-lock.json+2 -2
@@ -1,12 +1,12 @@
11{
22 "name": "sillytavern",
33 "version": "1.12.1112",
44 "lockfileVersion": 3,
55 "requires": true,
66 "packages": {
77 "": {
88 "name": "sillytavern",
99 "version": "1.12.1112",
1010 "hasInstallScript": true,
1111 "license": "AGPL-3.0",
1212 "dependencies": {
package.json+1 -1
@@ -87,7 +87,7 @@
8787 "type": "git",
8888 "url": "https://github.com/SillyTavern/SillyTavern.git"
8989 },
9090 "version": "1.12.1112",
9191 "scripts": {
9292 "start": "node server.js",
9393 "debug": "node server.js --inspect",
plugins.js+7 -0
@@ -48,6 +48,13 @@ async function updatePlugins() {
4848 console.log(`Updating plugin ${color.green(directory)}...`);
4949 const pluginPath = path.join(pluginsPath, directory);
5050 const pluginRepo = git(pluginPath);
51+
52+ const isRepo = await pluginRepo.checkIsRepo();
53+ if (!isRepo) {
54+ console.log(`Directory ${color.yellow(directory)} is not a Git repository`);
55+ continue;
56+ }
57+
5158 await pluginRepo.fetch();
5259 const commitHash = await pluginRepo.revparse(['HEAD']);
5360 const trackingBranch = await pluginRepo.revparse(['--abbrev-ref', '@{u}']);
public/script.js+4 -0
@@ -5931,6 +5931,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes,
59315931 chat[chat.length - 1]['extra']['api'] = getGeneratingApi();
59325932 chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
59335933 chat[chat.length - 1]['extra']['reasoning'] = reasoning;
5934+ chat[chat.length - 1]['extra']['reasoning_duration'] = null;
59345935 if (power_user.message_token_count_enabled) {
59355936 const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];
59365937 chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);
@@ -5953,6 +5954,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes,
59535954 chat[chat.length - 1]['extra']['api'] = getGeneratingApi();
59545955 chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
59555956 chat[chat.length - 1]['extra']['reasoning'] = reasoning;
5957+ chat[chat.length - 1]['extra']['reasoning_duration'] = null;
59565958 if (power_user.message_token_count_enabled) {
59575959 const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];
59585960 chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);
@@ -5972,6 +5974,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes,
59725974 chat[chat.length - 1]['extra']['api'] = getGeneratingApi();
59735975 chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
59745976 chat[chat.length - 1]['extra']['reasoning'] += reasoning;
5977+ // We don't know if the reasoning duration extended, so we don't update it here on purpose.
59755978 if (power_user.message_token_count_enabled) {
59765979 const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];
59775980 chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);
@@ -5991,6 +5994,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes,
59915994 chat[chat.length - 1]['extra']['api'] = getGeneratingApi();
59925995 chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
59935996 chat[chat.length - 1]['extra']['reasoning'] = reasoning;
5997+ chat[chat.length - 1]['extra']['reasoning_duration'] = null;
59945998 if (power_user.trim_spaces) {
59955999 getMessage = getMessage.trim();
59966000 }
public/scripts/extensions/quick-reply/src/ui/SettingsUi.js+1 -1
@@ -346,7 +346,7 @@ export class SettingsUi {
346346 }
347347
348348 async addQrSet() {
349349 const name = await Popup.show.input('Create a new WorldQuick InfoReply Set', 'Enter a name for the new Quick Reply Set:');
350350 if (name && name.length > 0) {
351351 const oldQrs = QuickReplySet.get(name);
352352 if (oldQrs) {
public/scripts/reasoning.js+1 -1
@@ -106,7 +106,7 @@ export function isHiddenReasoningModel() {
106106 { name: 'gemini-2.0-pro-exp', func: FUNCS.startsWith },
107107 ];
108108
109109 const model = getChatCompletionModel() || '';
110110
111111 const isHidden = hiddenReasoningModels.some(({ name, func }) => func(model, name));
112112 return isHidden;
public/scripts/textgen-models.js+5 -0
@@ -58,9 +58,14 @@ const OPENROUTER_PROVIDERS = [
5858 'Minimax',
5959 'Nineteen',
6060 'Liquid',
61+ 'InferenceNet',
62+ 'Friendli',
63+ 'AionLabs',
64+ 'Alibaba',
6165 'Nebius',
6266 'Chutes',
6367 'Kluster',
68+ 'Targon',
6469 '01.AI',
6570 'HuggingFace',
6671 'Mancer',
src/endpoints/stable-diffusion.js+2 -1
@@ -1275,7 +1275,8 @@ falai.post('/generate', jsonParser, async (request, response) => {
12751275 num_inference_steps: request.body.steps,
12761276 seed: request.body.seed ?? null,
12771277 guidance_scale: request.body.guidance,
12781278 enable_safety_checker: false, // Disable general safety checks
1279+ safety_tolerance: 6 // Make Flux the least strict
12791280 };
12801281
12811282 console.debug('FAL.AI request:', requestBody);
src/plugin-loader.js+74 -2
@@ -3,8 +3,12 @@ import path from 'node:path';
33import url from 'node:url';
44
55import express from 'express';
66import { getConfigValuedefault as git } from './util.jssimple-git';
7-const enableServerPlugins = getConfigValue('enableServerPlugins', false);
7+import { sync as commandExistsSync } from 'command-exists';
8+import { getConfigValue, color } from './util.js';
9+
10+const enableServerPlugins = !!getConfigValue('enableServerPlugins', false);
11+const enableServerPluginsAutoUpdate = !!getConfigValue('enableServerPluginsAutoUpdate', true);
812
913/**
1014 * Map of loaded plugins.
@@ -54,6 +58,8 @@ export async function loadPlugins(app, pluginsPath) {
5458 return emptyFn;
5559 }
5660
61+ await updatePlugins(pluginsPath);
62+
5763 for (const file of files) {
5864 const pluginFilePath = path.join(pluginsPath, file);
5965
@@ -70,6 +76,10 @@ export async function loadPlugins(app, pluginsPath) {
7076 await loadFromFile(app, pluginFilePath, exitHooks);
7177 }
7278
79+ if (loadedPlugins.size > 0) {
80+ 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!`);
81+ }
82+
7383 // Call all plugin "exit" functions at once and wait for them to finish
7484 return () => Promise.all(exitHooks.map(exitFn => exitFn()));
7585}
@@ -214,3 +224,65 @@ async function initPlugin(app, plugin, exitHooks) {
214224
215225 return true;
216226}
227+
228+/**
229+ * Automatically update all git plugins in the ./plugins directory
230+ * @param {string} pluginsPath Path to plugins directory
231+ */
232+async function updatePlugins(pluginsPath) {
233+ if (!enableServerPluginsAutoUpdate) {
234+ return;
235+ }
236+
237+ const directories = fs.readdirSync(pluginsPath)
238+ .filter(file => !file.startsWith('.'))
239+ .filter(file => fs.statSync(path.join(pluginsPath, file)).isDirectory());
240+
241+ if (directories.length === 0) {
242+ return;
243+ }
244+
245+ console.log(color.blue('Auto-updating server plugins... Set'), color.yellow('enableServerPluginsAutoUpdate: false'), color.blue('in config.yaml to disable this feature.'));
246+
247+ if (!commandExistsSync('git')) {
248+ console.error(color.red('Git is not installed. Please install Git to enable auto-updating of server plugins.'));
249+ return;
250+ }
251+
252+ let pluginsToUpdate = 0;
253+
254+ for (const directory of directories) {
255+ try {
256+ const pluginPath = path.join(pluginsPath, directory);
257+ const pluginRepo = git(pluginPath);
258+
259+ const isRepo = await pluginRepo.checkIsRepo();
260+ if (!isRepo) {
261+ continue;
262+ }
263+
264+ await pluginRepo.fetch();
265+ const commitHash = await pluginRepo.revparse(['HEAD']);
266+ const trackingBranch = await pluginRepo.revparse(['--abbrev-ref', '@{u}']);
267+ const log = await pluginRepo.log({
268+ from: commitHash,
269+ to: trackingBranch,
270+ });
271+
272+ if (log.total === 0) {
273+ continue;
274+ }
275+
276+ pluginsToUpdate++;
277+ await pluginRepo.pull();
278+ const latestCommit = await pluginRepo.revparse(['HEAD']);
279+ console.log(`Plugin ${color.green(directory)} updated to commit ${color.cyan(latestCommit)}`);
280+ } catch (error) {
281+ console.error(color.red(`Failed to update plugin ${directory}: ${error.message}`));
282+ }
283+ }
284+
285+ if (pluginsToUpdate === 0) {
286+ console.log('All plugins are up to date.');
287+ }
288+}