Merge branch 'staging' of https://github.com/Cohee1207/SillyTavern into staging

d772d24052674993efa53bd9c04057ec537eebff

RossAscends <124905043+RossAscends@users.noreply.github.com>

4 files changed, +44 -39Ignore whitespace
public/scripts/extensions/stable-diffusion/index.js+2 -4
@@ -2208,10 +2208,9 @@ function processReply(str) {
22082208
2209 str = str.replaceAll('"', '');2209 str = str.replaceAll('"', '');
2210 str = str.replaceAll('“', '');2210 str = str.replaceAll('“', '');
2211 str = str.replaceAll('.', ',');
2212 str = str.replaceAll('\n', ', ');2211 str = str.replaceAll('\n', ', ');
2213 str = str.normalize('NFD');2212 str = str.normalize('NFD');
2214 str = str.replace(/[^a-zA-Z0-9,:_(){}<>[\]\-']+/g, ' ');2213 str = str.replace(/[^a-zA-Z0-9\.,:_(){}<>[\]\-']+/g, ' ');
2215 str = str.replace(/\s+/g, ' '); // Collapse multiple whitespaces into one2214 str = str.replace(/\s+/g, ' '); // Collapse multiple whitespaces into one
2216 str = str.trim();2215 str = str.trim();
22172216
@@ -2675,8 +2674,7 @@ async function generateTogetherAIImage(prompt, negativePrompt, signal) {
2675 });2674 });
26762675
2677 if (result.ok) {2676 if (result.ok) {
2678 const data = await result.json();2677 return await result.json();
2679 return { format: 'jpg', data: data?.output?.choices?.[0]?.image_base64 };
2680 } else {2678 } else {
2681 const text = await result.text();2679 const text = await result.text();
2682 throw new Error(text);2680 throw new Error(text);
public/scripts/variables.js+2 -1
@@ -514,7 +514,8 @@ export function parseBooleanOperands(args) {
514 return '';514 return '';
515 }515 }
516516
517 const operandNumber = Number(operand);517 // parseFloat will return NaN for spaces.
518 const operandNumber = parseFloat(operand);
518519
519 if (!isNaN(operandNumber)) {520 if (!isNaN(operandNumber)) {
520 return operandNumber;521 return operandNumber;
public/scripts/world-info.js+31 -25
@@ -130,9 +130,9 @@ const KNOWN_DECORATORS = ['@@activate', '@@dont_activate'];
130 */130 */
131class WorldInfoBuffer {131class WorldInfoBuffer {
132 /**132 /**
133 * @type {object[]} Array of entries that need to be activated no matter what133 * @type {Map<string, object>} Map of entries that need to be activated no matter what
134 */134 */
135 static externalActivations = [];135 static externalActivations = new Map();
136136
137 /**137 /**
138 * @type {string[]} Array of messages sorted by ascending depth138 * @type {string[]} Array of messages sorted by ascending depth
@@ -311,20 +311,19 @@ class WorldInfoBuffer {
311 }311 }
312312
313 /**313 /**
314 * Check if the current entry is externally activated.314 * Get the externally activated version of the entry, if there is one.
315 * @param {object} entry WI entry to check315 * @param {object} entry WI entry to check
316 * @returns {boolean} True if the entry is forcefully activated316 * @returns {object|undefined} the external version if the entry is forcefully activated, undefined otherwise
317 */317 */
318 isExternallyActivated(entry) {318 getExternallyActivated(entry) {
319 // Entries could be copied with structuredClone, so we need to compare them by string representation319 return WorldInfoBuffer.externalActivations.get(`${entry.world}.${entry.uid}`);
320 return WorldInfoBuffer.externalActivations.some(x => JSON.stringify(x) === JSON.stringify(entry));
321 }320 }
322321
323 /**322 /**
324 * Clean-up the external effects for entries.323 * Clean-up the external effects for entries.
325 */324 */
326 resetExternalEffects() {325 resetExternalEffects() {
327 WorldInfoBuffer.externalActivations.splice(0, WorldInfoBuffer.externalActivations.length);326 WorldInfoBuffer.externalActivations = new Map();
328 }327 }
329328
330 /**329 /**
@@ -751,7 +750,7 @@ export async function getWorldInfoPrompt(chat, maxContext, isDryRun) {
751 worldInfoString = worldInfoBefore + worldInfoAfter;750 worldInfoString = worldInfoBefore + worldInfoAfter;
752751
753 if (!isDryRun && activatedWorldInfo.allActivatedEntries && activatedWorldInfo.allActivatedEntries.size > 0) {752 if (!isDryRun && activatedWorldInfo.allActivatedEntries && activatedWorldInfo.allActivatedEntries.size > 0) {
754 const arg = Array.from(activatedWorldInfo.allActivatedEntries);753 const arg = Array.from(activatedWorldInfo.allActivatedEntries.values());
755 await eventSource.emit(event_types.WORLD_INFO_ACTIVATED, arg);754 await eventSource.emit(event_types.WORLD_INFO_ACTIVATED, arg);
756 }755 }
757756
@@ -868,7 +867,14 @@ export function setWorldInfoSettings(settings, data) {
868 });867 });
869868
870 eventSource.on(event_types.WORLDINFO_FORCE_ACTIVATE, (entries) => {869 eventSource.on(event_types.WORLDINFO_FORCE_ACTIVATE, (entries) => {
871 WorldInfoBuffer.externalActivations.push(...entries);870 for (const entry of entries) {
871 if (!Object.hasOwn(entry, 'world') || !Object.hasOwn(entry, 'uid')) {
872 console.error('[WI] WORLDINFO_FORCE_ACTIVATE requires all entries to have both world and uid fields, entry IGNORED', entry);
873 } else {
874 WorldInfoBuffer.externalActivations.set(`${entry.world}.${entry.uid}`, entry);
875 console.log('[WI] WORLDINFO_FORCE_ACTIVATE added entry', entry);
876 }
877 }
872 });878 });
873879
874 // Add slash commands880 // Add slash commands
@@ -3724,7 +3730,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
3724 let scanState = scan_state.INITIAL;3730 let scanState = scan_state.INITIAL;
3725 let token_budget_overflowed = false;3731 let token_budget_overflowed = false;
3726 let count = 0;3732 let count = 0;
3727 let allActivatedEntries = new Set();3733 let allActivatedEntries = new Map();
3728 let failedProbabilityChecks = new Set();3734 let failedProbabilityChecks = new Set();
3729 let allActivatedText = '';3735 let allActivatedText = '';
37303736
@@ -3789,7 +3795,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
3789 }3795 }
37903796
3791 // Already processed, considered and then skipped entries should still be skipped3797 // Already processed, considered and then skipped entries should still be skipped
3792 if (failedProbabilityChecks.has(entry) || allActivatedEntries.has(entry)) {3798 if (failedProbabilityChecks.has(entry) || allActivatedEntries.has(`${entry.world}.${entry.uid}`)) {
3793 continue;3799 continue;
3794 }3800 }
37953801
@@ -3869,15 +3875,15 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
3869 continue;3875 continue;
3870 }3876 }
38713877
3872 // Now do checks for immediate activations3878 if (buffer.getExternallyActivated(entry)) {
3873 if (entry.constant) {3879 log('externally activated');
3874 log('activated because of constant');3880 activatedNow.add(buffer.getExternallyActivated(entry));
3875 activatedNow.add(entry);
3876 continue;3881 continue;
3877 }3882 }
38783883
3879 if (buffer.isExternallyActivated(entry)) {3884 // Now do checks for immediate activations
3880 log('externally activated');3885 if (entry.constant) {
3886 log('activated because of constant');
3881 activatedNow.add(entry);3887 activatedNow.add(entry);
3882 continue;3888 continue;
3883 }3889 }
@@ -4039,7 +4045,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
4039 break;4045 break;
4040 }4046 }
40414047
4042 allActivatedEntries.add(entry);4048 allActivatedEntries.set(`${entry.world}.${entry.uid}`, entry);
4043 console.debug(`[WI] Entry ${entry.uid} activation successful, adding to prompt`, entry);4049 console.debug(`[WI] Entry ${entry.uid} activation successful, adding to prompt`, entry);
4044 }4050 }
40454051
@@ -4123,7 +4129,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
41234129
4124 // Appends from insertion order 999 to 1. Use unshift for this purpose4130 // Appends from insertion order 999 to 1. Use unshift for this purpose
4125 // TODO (kingbri): Change to use WI Anchor positioning instead of separate top/bottom arrays4131 // TODO (kingbri): Change to use WI Anchor positioning instead of separate top/bottom arrays
4126 [...allActivatedEntries].sort(sortFn).forEach((entry) => {4132 [...allActivatedEntries.values()].sort(sortFn).forEach((entry) => {
4127 const regexDepth = entry.position === world_info_position.atDepth ? (entry.depth ?? DEFAULT_DEPTH) : null;4133 const regexDepth = entry.position === world_info_position.atDepth ? (entry.depth ?? DEFAULT_DEPTH) : null;
4128 const content = getRegexedString(entry.content, regex_placement.WORLD_INFO, { depth: regexDepth, isMarkdown: false, isPrompt: true });4134 const content = getRegexedString(entry.content, regex_placement.WORLD_INFO, { depth: regexDepth, isMarkdown: false, isPrompt: true });
41294135
@@ -4182,14 +4188,14 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
4182 context.setExtensionPrompt(NOTE_MODULE_NAME, ANWithWI, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth], extension_settings.note.allowWIScan, chat_metadata[metadata_keys.role]);4188 context.setExtensionPrompt(NOTE_MODULE_NAME, ANWithWI, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth], extension_settings.note.allowWIScan, chat_metadata[metadata_keys.role]);
4183 }4189 }
41844190
4185 !isDryRun && timedEffects.setTimedEffects(Array.from(allActivatedEntries));4191 !isDryRun && timedEffects.setTimedEffects(Array.from(allActivatedEntries.values()));
4186 buffer.resetExternalEffects();4192 buffer.resetExternalEffects();
4187 timedEffects.cleanUp();4193 timedEffects.cleanUp();
41884194
4189 console.log(`[WI] Adding ${allActivatedEntries.size} entries to prompt`, Array.from(allActivatedEntries));4195 console.log(`[WI] Adding ${allActivatedEntries.size} entries to prompt`, Array.from(allActivatedEntries.values()));
4190 console.debug('[WI] --- DONE ---');4196 console.debug('[WI] --- DONE ---');
41914197
4192 return { worldInfoBefore, worldInfoAfter, EMEntries, WIDepthEntries, allActivatedEntries };4198 return { worldInfoBefore, worldInfoAfter, EMEntries, WIDepthEntries, allActivatedEntries: new Set(allActivatedEntries.values()) };
4193}4199}
41944200
4195/**4201/**
@@ -4291,7 +4297,7 @@ function filterGroupsByTimedEffects(groups, timedEffects, removeEntry) {
4291/**4297/**
4292 * Filters entries by inclusion groups.4298 * Filters entries by inclusion groups.
4293 * @param {object[]} newEntries Entries activated on current recursion level4299 * @param {object[]} newEntries Entries activated on current recursion level
4294 * @param {Set<object>} allActivatedEntries Set of all activated entries4300 * @param {Map<string, object>} allActivatedEntries Map of all activated entries
4295 * @param {WorldInfoBuffer} buffer The buffer to use for scanning4301 * @param {WorldInfoBuffer} buffer The buffer to use for scanning
4296 * @param {number} scanState The current scan state4302 * @param {number} scanState The current scan state
4297 * @param {WorldInfoTimedEffects} timedEffects The timed effects currently active4303 * @param {WorldInfoTimedEffects} timedEffects The timed effects currently active
@@ -4339,7 +4345,7 @@ function filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanSt
4339 continue;4345 continue;
4340 }4346 }
43414347
4342 if (Array.from(allActivatedEntries).some(x => x.group === key)) {4348 if (Array.from(allActivatedEntries.values()).some(x => x.group === key)) {
4343 console.debug(`[WI] Skipping inclusion group check, group '${key}' was already activated`);4349 console.debug(`[WI] Skipping inclusion group check, group '${key}' was already activated`);
4344 // We need to forcefully deactivate all other entries in the group4350 // We need to forcefully deactivate all other entries in the group
4345 removeAllBut(group, null, false);4351 removeAllBut(group, null, false);
src/endpoints/stable-diffusion.js+9 -9
@@ -607,10 +607,9 @@ together.post('/generate', jsonParser, async (request, response) => {
607607
608 console.log('TogetherAI request:', request.body);608 console.log('TogetherAI request:', request.body);
609609
610 const result = await fetch('https://api.together.xyz/api/inference', {610 const result = await fetch('https://api.together.xyz/v1/images/generations', {
611 method: 'POST',611 method: 'POST',
612 body: JSON.stringify({612 body: JSON.stringify({
613 request_type: 'image-model-inference',
614 prompt: request.body.prompt,613 prompt: request.body.prompt,
615 negative_prompt: request.body.negative_prompt,614 negative_prompt: request.body.negative_prompt,
616 height: request.body.height,615 height: request.body.height,
@@ -620,8 +619,6 @@ together.post('/generate', jsonParser, async (request, response) => {
620 n: 1,619 n: 1,
621 // Limited to 10000 on playground, works fine with more.620 // Limited to 10000 on playground, works fine with more.
622 seed: request.body.seed >= 0 ? request.body.seed : Math.floor(Math.random() * 10_000_000),621 seed: request.body.seed >= 0 ? request.body.seed : Math.floor(Math.random() * 10_000_000),
623 // Don't know if that's supposed to be random or not. It works either way.
624 sessionKey: getHexString(40),
625 }),622 }),
626 headers: {623 headers: {
627 'Content-Type': 'application/json',624 'Content-Type': 'application/json',
@@ -630,19 +627,22 @@ together.post('/generate', jsonParser, async (request, response) => {
630 });627 });
631628
632 if (!result.ok) {629 if (!result.ok) {
633 console.log('TogetherAI returned an error.');630 console.log('TogetherAI returned an error.', { body: await result.text() });
634 return response.sendStatus(500);631 return response.sendStatus(500);
635 }632 }
636633
637 const data = await result.json();634 const data = await result.json();
638 console.log('TogetherAI response:', data);635 console.log('TogetherAI response:', data);
639636
640 if (data.status !== 'finished') {637 const choice = data?.data?.[0];
641 console.log('TogetherAI job failed.');638 let b64_json = choice.b64_json;
642 return response.sendStatus(500);639
640 if (!b64_json) {
641 const buffer = await (await fetch(choice.url)).buffer();
642 b64_json = buffer.toString('base64');
643 }643 }
644644
645 return response.send(data);645 return response.send({ format: 'jpg', data: b64_json });
646 } catch (error) {646 } catch (error) {
647 console.log(error);647 console.log(error);
648 return response.sendStatus(500);648 return response.sendStatus(500);