Merge branch 'staging' of https://github.com/Cohee1207/SillyTavern into staging
| @@ -2208,10 +2208,9 @@ function processReply(str) { | |||
| 2208 | 2208 | ||
| 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 one | 2214 | str = str.replace(/\s+/g, ' '); // Collapse multiple whitespaces into one |
| 2216 | str = str.trim(); | 2215 | str = str.trim(); |
| 2217 | 2216 | ||
| @@ -2675,8 +2674,7 @@ async function generateTogetherAIImage(prompt, negativePrompt, signal) { | |||
| 2675 | }); | 2674 | }); |
| 2676 | 2675 | ||
| 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); |
| @@ -514,7 +514,8 @@ export function parseBooleanOperands(args) { | |||
| 514 | return ''; | 514 | return ''; |
| 515 | } | 515 | } |
| 516 | 516 | ||
| 517 | const operandNumber = Number(operand); | 517 | // parseFloat will return NaN for spaces. |
| 518 | const operandNumber = parseFloat(operand); | ||
| 518 | 519 | ||
| 519 | if (!isNaN(operandNumber)) { | 520 | if (!isNaN(operandNumber)) { |
| 520 | return operandNumber; | 521 | return operandNumber; |
| @@ -130,9 +130,9 @@ const KNOWN_DECORATORS = ['@@activate', '@@dont_activate']; | |||
| 130 | */ | 130 | */ |
| 131 | class WorldInfoBuffer { | 131 | class WorldInfoBuffer { |
| 132 | /** | 132 | /** |
| 133 | * @type {object[]} Array of entries that need to be activated no matter what | 133 | * @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(); |
| 136 | 136 | ||
| 137 | /** | 137 | /** |
| 138 | * @type {string[]} Array of messages sorted by ascending depth | 138 | * @type {string[]} Array of messages sorted by ascending depth |
| @@ -311,20 +311,19 @@ class WorldInfoBuffer { | |||
| 311 | } | 311 | } |
| 312 | 312 | ||
| 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 check | 315 | * @param {object} entry WI entry to check |
| 316 | * @returns {boolean} True if the entry is forcefully activated | 316 | * @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 representation | 319 | return WorldInfoBuffer.externalActivations.get(`${entry.world}.${entry.uid}`); |
| 320 | return WorldInfoBuffer.externalActivations.some(x => JSON.stringify(x) === JSON.stringify(entry)); | ||
| 321 | } | 320 | } |
| 322 | 321 | ||
| 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 | } |
| 329 | 328 | ||
| 330 | /** | 329 | /** |
| @@ -751,7 +750,7 @@ export async function getWorldInfoPrompt(chat, maxContext, isDryRun) { | |||
| 751 | worldInfoString = worldInfoBefore + worldInfoAfter; | 750 | worldInfoString = worldInfoBefore + worldInfoAfter; |
| 752 | 751 | ||
| 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 | } |
| 757 | 756 | ||
| @@ -868,7 +867,14 @@ export function setWorldInfoSettings(settings, data) { | |||
| 868 | }); | 867 | }); |
| 869 | 868 | ||
| 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 | }); |
| 873 | 879 | ||
| 874 | // Add slash commands | 880 | // 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 = ''; |
| 3730 | 3736 | ||
| @@ -3789,7 +3795,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) { | |||
| 3789 | } | 3795 | } |
| 3790 | 3796 | ||
| 3791 | // Already processed, considered and then skipped entries should still be skipped | 3797 | // 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 | } |
| 3795 | 3801 | ||
| @@ -3869,15 +3875,15 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) { | |||
| 3869 | continue; | 3875 | continue; |
| 3870 | } | 3876 | } |
| 3871 | 3877 | ||
| 3872 | // Now do checks for immediate activations | 3878 | 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 | } |
| 3878 | 3883 | ||
| 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 | } |
| 4041 | 4047 | ||
| 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 | } |
| 4045 | 4051 | ||
| @@ -4123,7 +4129,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) { | |||
| 4123 | 4129 | ||
| 4124 | // Appends from insertion order 999 to 1. Use unshift for this purpose | 4130 | // 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 arrays | 4131 | // 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 }); |
| 4129 | 4135 | ||
| @@ -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 | } |
| 4184 | 4190 | ||
| 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(); |
| 4188 | 4194 | ||
| 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 ---'); |
| 4191 | 4197 | ||
| 4192 | return { worldInfoBefore, worldInfoAfter, EMEntries, WIDepthEntries, allActivatedEntries }; | 4198 | return { worldInfoBefore, worldInfoAfter, EMEntries, WIDepthEntries, allActivatedEntries: new Set(allActivatedEntries.values()) }; |
| 4193 | } | 4199 | } |
| 4194 | 4200 | ||
| 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 level | 4299 | * @param {object[]} newEntries Entries activated on current recursion level |
| 4294 | * @param {Set<object>} allActivatedEntries Set of all activated entries | 4300 | * @param {Map<string, object>} allActivatedEntries Map of all activated entries |
| 4295 | * @param {WorldInfoBuffer} buffer The buffer to use for scanning | 4301 | * @param {WorldInfoBuffer} buffer The buffer to use for scanning |
| 4296 | * @param {number} scanState The current scan state | 4302 | * @param {number} scanState The current scan state |
| 4297 | * @param {WorldInfoTimedEffects} timedEffects The timed effects currently active | 4303 | * @param {WorldInfoTimedEffects} timedEffects The timed effects currently active |
| @@ -4339,7 +4345,7 @@ function filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanSt | |||
| 4339 | continue; | 4345 | continue; |
| 4340 | } | 4346 | } |
| 4341 | 4347 | ||
| 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 group | 4350 | // We need to forcefully deactivate all other entries in the group |
| 4345 | removeAllBut(group, null, false); | 4351 | removeAllBut(group, null, false); |
| @@ -607,10 +607,9 @@ together.post('/generate', jsonParser, async (request, response) => { | |||
| 607 | 607 | ||
| 608 | console.log('TogetherAI request:', request.body); | 608 | console.log('TogetherAI request:', request.body); |
| 609 | 609 | ||
| 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 | }); |
| 631 | 628 | ||
| 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 | } |
| 636 | 633 | ||
| 637 | const data = await result.json(); | 634 | const data = await result.json(); |
| 638 | console.log('TogetherAI response:', data); | 635 | console.log('TogetherAI response:', data); |
| 639 | 636 | ||
| 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 | } |
| 644 | 644 | ||
| 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); |