mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge remote-tracking branch 'upstream/staging' into staging
This commit is contained in:
@@ -109,16 +109,21 @@ function loadSettings() {
|
|||||||
$('#memory_depth').val(extension_settings.memory.depth).trigger('input');
|
$('#memory_depth').val(extension_settings.memory.depth).trigger('input');
|
||||||
$(`input[name="memory_position"][value="${extension_settings.memory.position}"]`).prop('checked', true).trigger('input');
|
$(`input[name="memory_position"][value="${extension_settings.memory.position}"]`).prop('checked', true).trigger('input');
|
||||||
$('#memory_prompt_words_force').val(extension_settings.memory.promptForceWords).trigger('input');
|
$('#memory_prompt_words_force').val(extension_settings.memory.promptForceWords).trigger('input');
|
||||||
|
switchSourceControls(extension_settings.memory.source);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSummarySourceChange(event) {
|
function onSummarySourceChange(event) {
|
||||||
const value = event.target.value;
|
const value = event.target.value;
|
||||||
extension_settings.memory.source = value;
|
extension_settings.memory.source = value;
|
||||||
|
switchSourceControls(value);
|
||||||
|
saveSettingsDebounced();
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchSourceControls(value) {
|
||||||
$('#memory_settings [data-source]').each((_, element) => {
|
$('#memory_settings [data-source]').each((_, element) => {
|
||||||
const source = $(element).data('source');
|
const source = $(element).data('source');
|
||||||
$(element).toggle(source === value);
|
$(element).toggle(source === value);
|
||||||
});
|
});
|
||||||
saveSettingsDebounced();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMemoryShortInput() {
|
function onMemoryShortInput() {
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
import { chat_metadata, getCurrentChatId, saveSettingsDebounced, sendSystemMessage, system_message_types } from '../script.js';
|
import { chat_metadata, getCurrentChatId, saveSettingsDebounced, sendSystemMessage, system_message_types } from '../script.js';
|
||||||
import { extension_settings, saveMetadataDebounced } from './extensions.js';
|
import { extension_settings, saveMetadataDebounced } from './extensions.js';
|
||||||
import { executeSlashCommands, registerSlashCommand } from './slash-commands.js';
|
import { executeSlashCommands, registerSlashCommand } from './slash-commands.js';
|
||||||
|
import { isFalseBoolean } from './utils.js';
|
||||||
|
|
||||||
|
const MAX_LOOPS = 100;
|
||||||
|
|
||||||
function getLocalVariable(name, args = {}) {
|
function getLocalVariable(name, args = {}) {
|
||||||
if (!chat_metadata.variables) {
|
if (!chat_metadata.variables) {
|
||||||
@@ -301,8 +304,7 @@ function listVariablesCallback() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function whileCallback(args, command) {
|
async function whileCallback(args, command) {
|
||||||
const MAX_LOOPS = 100;
|
const isGuardOff = isFalseBoolean(args.guard);
|
||||||
const isGuardOff = ['off', 'false', '0'].includes(args.guard?.toLowerCase());
|
|
||||||
const iterations = isGuardOff ? Number.MAX_SAFE_INTEGER : MAX_LOOPS;
|
const iterations = isGuardOff ? Number.MAX_SAFE_INTEGER : MAX_LOOPS;
|
||||||
|
|
||||||
for (let i = 0; i < iterations; i++) {
|
for (let i = 0; i < iterations; i++) {
|
||||||
@@ -319,6 +321,19 @@ async function whileCallback(args, command) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function timesCallback(args, value) {
|
||||||
|
const [repeats, ...commandParts] = value.split(' ');
|
||||||
|
const command = commandParts.join(' ');
|
||||||
|
const isGuardOff = isFalseBoolean(args.guard);
|
||||||
|
const iterations = Math.min(Number(repeats), isGuardOff ? Number.MAX_SAFE_INTEGER : MAX_LOOPS);
|
||||||
|
|
||||||
|
for (let i = 0; i < iterations; i++) {
|
||||||
|
await executeSubCommands(command.replace(/\{\{timesIndex\}\}/g, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
async function ifCallback(args, command) {
|
async function ifCallback(args, command) {
|
||||||
const { a, b, rule } = parseBooleanOperands(args);
|
const { a, b, rule } = parseBooleanOperands(args);
|
||||||
const result = evalBoolean(rule, a, b);
|
const result = evalBoolean(rule, a, b);
|
||||||
@@ -666,6 +681,7 @@ export function registerVariableCommands() {
|
|||||||
registerSlashCommand('decglobalvar', (_, value) => decrementGlobalVariable(value), [], '<span class="monospace">(key)</span> – decrement a global variable by 1 and pass the result down the pipe, e.g. <tt>/decglobalvar score</tt>', true, true);
|
registerSlashCommand('decglobalvar', (_, value) => decrementGlobalVariable(value), [], '<span class="monospace">(key)</span> – decrement a global variable by 1 and pass the result down the pipe, e.g. <tt>/decglobalvar score</tt>', true, true);
|
||||||
registerSlashCommand('if', ifCallback, [], '<span class="monospace">left=varname1 right=varname2 rule=comparison else="(alt.command)" "(command)"</span> – compare the value of the left operand "a" with the value of the right operand "b", and if the condition yields true, then execute any valid slash command enclosed in quotes and pass the result of the command execution down the pipe. Numeric values and string literals for left and right operands supported. Available rules: gt => a > b, gte => a >= b, lt => a < b, lte => a <= b, eq => a == b, neq => a != b, not => !a, in (strings) => a includes b, nin (strings) => a not includes b, e.g. <tt>/if left=score right=10 rule=gte "/speak You win"</tt> triggers a /speak command if the value of "score" is greater or equals 10.', true, true);
|
registerSlashCommand('if', ifCallback, [], '<span class="monospace">left=varname1 right=varname2 rule=comparison else="(alt.command)" "(command)"</span> – compare the value of the left operand "a" with the value of the right operand "b", and if the condition yields true, then execute any valid slash command enclosed in quotes and pass the result of the command execution down the pipe. Numeric values and string literals for left and right operands supported. Available rules: gt => a > b, gte => a >= b, lt => a < b, lte => a <= b, eq => a == b, neq => a != b, not => !a, in (strings) => a includes b, nin (strings) => a not includes b, e.g. <tt>/if left=score right=10 rule=gte "/speak You win"</tt> triggers a /speak command if the value of "score" is greater or equals 10.', true, true);
|
||||||
registerSlashCommand('while', whileCallback, [], '<span class="monospace">left=varname1 right=varname2 rule=comparison "(command)"</span> – compare the value of the left operand "a" with the value of the right operand "b", and if the condition yields true, then execute any valid slash command enclosed in quotes. Numeric values and string literals for left and right operands supported. Available rules: gt => a > b, gte => a >= b, lt => a < b, lte => a <= b, eq => a == b, neq => a != b, not => !a, in (strings) => a includes b, nin (strings) => a not includes b, e.g. <tt>/setvar key=i 0 | /while left=i right=10 rule=let "/addvar key=i 1"</tt> adds 1 to the value of "i" until it reaches 10. Loops are limited to 100 iterations by default, pass guard=off to disable.', true, true);
|
registerSlashCommand('while', whileCallback, [], '<span class="monospace">left=varname1 right=varname2 rule=comparison "(command)"</span> – compare the value of the left operand "a" with the value of the right operand "b", and if the condition yields true, then execute any valid slash command enclosed in quotes. Numeric values and string literals for left and right operands supported. Available rules: gt => a > b, gte => a >= b, lt => a < b, lte => a <= b, eq => a == b, neq => a != b, not => !a, in (strings) => a includes b, nin (strings) => a not includes b, e.g. <tt>/setvar key=i 0 | /while left=i right=10 rule=let "/addvar key=i 1"</tt> adds 1 to the value of "i" until it reaches 10. Loops are limited to 100 iterations by default, pass guard=off to disable.', true, true);
|
||||||
|
registerSlashCommand('times', (args, value) => timesCallback(args, value), [], '<span class="monospace">(repeats) "(command)"</span> – execute any valid slash command enclosed in quotes <tt>repeats</tt> number of times, e.g. <tt>/setvar key=i 1 | /times 5 "/addvar key=i 1"</tt> adds 1 to the value of "i" 5 times. <tt>{{timesIndex}}</tt> is replaced with the iteration number (zero-based), e.g. <tt>/times 4 "/echo {{timesIndex}}"</tt> echos the numbers 0 through 4. Loops are limited to 100 iterations by default, pass guard=off to disable.', true, true);
|
||||||
registerSlashCommand('flushvar', (_, value) => deleteLocalVariable(value), [], '<span class="monospace">(key)</span> – delete a local variable, e.g. <tt>/flushvar score</tt>', true, true);
|
registerSlashCommand('flushvar', (_, value) => deleteLocalVariable(value), [], '<span class="monospace">(key)</span> – delete a local variable, e.g. <tt>/flushvar score</tt>', true, true);
|
||||||
registerSlashCommand('flushglobalvar', (_, value) => deleteGlobalVariable(value), [], '<span class="monospace">(key)</span> – delete a global variable, e.g. <tt>/flushglobalvar score</tt>', true, true);
|
registerSlashCommand('flushglobalvar', (_, value) => deleteGlobalVariable(value), [], '<span class="monospace">(key)</span> – delete a global variable, e.g. <tt>/flushglobalvar score</tt>', true, true);
|
||||||
registerSlashCommand('add', (_, value) => addValuesCallback(value), [], '<span class="monospace">(a b c d)</span> – performs an addition of the set of values and passes the result down the pipe, can use variable names, e.g. <tt>/add 10 i 30 j</tt>', true, true);
|
registerSlashCommand('add', (_, value) => addValuesCallback(value), [], '<span class="monospace">(a b c d)</span> – performs an addition of the set of values and passes the result down the pipe, can use variable names, e.g. <tt>/add 10 i 30 j</tt>', true, true);
|
||||||
|
@@ -22,8 +22,25 @@ const { importRisuSprites } = require('./sprites');
|
|||||||
|
|
||||||
let characters = {};
|
let characters = {};
|
||||||
|
|
||||||
|
// KV-store for parsed character data
|
||||||
|
const characterDataCache = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the character card from the specified image file.
|
||||||
|
* @param {string} img_url - Path to the image file
|
||||||
|
* @param {string} input_format - 'png'
|
||||||
|
* @returns {Promise<string | undefined>} - Character card data
|
||||||
|
*/
|
||||||
async function charaRead(img_url, input_format) {
|
async function charaRead(img_url, input_format) {
|
||||||
return characterCardParser.parse(img_url, input_format);
|
const stat = fs.statSync(img_url);
|
||||||
|
const cacheKey = `${img_url}-${stat.mtimeMs}`;
|
||||||
|
if (characterDataCache.has(cacheKey)) {
|
||||||
|
return characterDataCache.get(cacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = characterCardParser.parse(img_url, input_format);
|
||||||
|
characterDataCache.set(cacheKey, result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,6 +49,13 @@ async function charaRead(img_url, input_format) {
|
|||||||
*/
|
*/
|
||||||
async function charaWrite(img_url, data, target_img, response = undefined, mes = 'ok', crop = undefined) {
|
async function charaWrite(img_url, data, target_img, response = undefined, mes = 'ok', crop = undefined) {
|
||||||
try {
|
try {
|
||||||
|
// Reset the cache
|
||||||
|
for (const key of characterDataCache.keys()) {
|
||||||
|
if (key.startsWith(img_url)) {
|
||||||
|
characterDataCache.delete(key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Read the image, resize, and save it as a PNG into the buffer
|
// Read the image, resize, and save it as a PNG into the buffer
|
||||||
const image = await tryReadImage(img_url, crop);
|
const image = await tryReadImage(img_url, crop);
|
||||||
|
|
||||||
|
@@ -10,9 +10,9 @@ const { DIRECTORIES, UPLOADS_PATH } = require('../constants');
|
|||||||
const { getConfigValue, humanizedISO8601DateTime, tryParse, generateTimestamp, removeOldBackups } = require('../util');
|
const { getConfigValue, humanizedISO8601DateTime, tryParse, generateTimestamp, removeOldBackups } = require('../util');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Saves a chat to the backups directory.
|
||||||
* @param {string} name
|
* @param {string} name The name of the chat.
|
||||||
* @param {string} chat
|
* @param {string} chat The serialized chat to save.
|
||||||
*/
|
*/
|
||||||
function backupChat(name, chat) {
|
function backupChat(name, chat) {
|
||||||
try {
|
try {
|
||||||
@@ -65,7 +65,6 @@ router.post('/get', jsonParser, function (request, response) {
|
|||||||
return response.send({});
|
return response.send({});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!request.body.file_name) {
|
if (!request.body.file_name) {
|
||||||
return response.send({});
|
return response.send({});
|
||||||
}
|
}
|
||||||
@@ -140,7 +139,6 @@ router.post('/delete', jsonParser, function (request, response) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return response.send('ok');
|
return response.send('ok');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -190,6 +188,10 @@ router.post('/export', jsonParser, async function (request, response) {
|
|||||||
let buffer = '';
|
let buffer = '';
|
||||||
rl.on('line', (line) => {
|
rl.on('line', (line) => {
|
||||||
const data = JSON.parse(line);
|
const data = JSON.parse(line);
|
||||||
|
// Skip non-printable/prompt-hidden messages
|
||||||
|
if (data.is_system) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (data.mes) {
|
if (data.mes) {
|
||||||
const name = data.name;
|
const name = data.name;
|
||||||
const message = (data?.extra?.display_text || data?.mes || '').replace(/\r?\n/g, '\n');
|
const message = (data?.extra?.display_text || data?.mes || '').replace(/\r?\n/g, '\n');
|
||||||
|
Reference in New Issue
Block a user