Merge pull request #2971 from honey-tree/world_info_force_activate_expansion Expand WORLDINFO_FORCE_ACTIVATE API to allow for dynamically modified entries

ac50cec02f72ffe70757fc55d4bc9371368d8068

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

Signed
1 files changed, +31 -25Ignore whitespace
public/scripts/world-info.js+31 -25
@@ -130,9 +130,9 @@ const KNOWN_DECORATORS = ['@@activate', '@@dont_activate'];
130130 */
131131class WorldInfoBuffer {
132132 /**
133133 * @type {Map<string, object[]>} ArrayMap of entries that need to be activated no matter what
134134 */
135135 static externalActivations = []new Map();
136136
137137 /**
138138 * @type {string[]} Array of messages sorted by ascending depth
@@ -311,20 +311,19 @@ class WorldInfoBuffer {
311311 }
312312
313313 /**
314314 * Check ifGet the currentexternally activated version of the entry, isif externallythere activatedis one.
315315 * @param {object} entry WI entry to check
316316 * @returns {booleanobject|undefined} Truethe external version if the entry is forcefully activated, undefined otherwise
317317 */
318318 isExternallyActivatedgetExternallyActivated(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));
321320 }
322321
323322 /**
324323 * Clean-up the external effects for entries.
325324 */
326325 resetExternalEffects() {
327- WorldInfoBuffer.externalActivations.splice(0, WorldInfoBuffer.externalActivations.length);
326+ WorldInfoBuffer.externalActivations = new Map();
328327 }
329328
330329 /**
@@ -751,7 +750,7 @@ export async function getWorldInfoPrompt(chat, maxContext, isDryRun) {
751750 worldInfoString = worldInfoBefore + worldInfoAfter;
752751
753752 if (!isDryRun && activatedWorldInfo.allActivatedEntries && activatedWorldInfo.allActivatedEntries.size > 0) {
754753 const arg = Array.from(activatedWorldInfo.allActivatedEntries.values());
755754 await eventSource.emit(event_types.WORLD_INFO_ACTIVATED, arg);
756755 }
757756
@@ -868,7 +867,14 @@ export function setWorldInfoSettings(settings, data) {
868867 });
869868
870869 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+ }
872878 });
873879
874880 // Add slash commands
@@ -3724,7 +3730,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
37243730 let scanState = scan_state.INITIAL;
37253731 let token_budget_overflowed = false;
37263732 let count = 0;
37273733 let allActivatedEntries = new SetMap();
37283734 let failedProbabilityChecks = new Set();
37293735 let allActivatedText = '';
37303736
@@ -3789,7 +3795,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
37893795 }
37903796
37913797 // Already processed, considered and then skipped entries should still be skipped
37923798 if (failedProbabilityChecks.has(entry) || allActivatedEntries.has(`${entry.world}.${entry.uid}`)) {
37933799 continue;
37943800 }
37953801
@@ -3869,15 +3875,15 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
38693875 continue;
38703876 }
38713877
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);
38763881 continue;
38773882 }
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');
38813887 activatedNow.add(entry);
38823888 continue;
38833889 }
@@ -4039,7 +4045,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
40394045 break;
40404046 }
40414047
4042- allActivatedEntries.add(entry);
4048+ allActivatedEntries.set(`${entry.world}.${entry.uid}`, entry);
40434049 console.debug(`[WI] Entry ${entry.uid} activation successful, adding to prompt`, entry);
40444050 }
40454051
@@ -4123,7 +4129,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
41234129
41244130 // Appends from insertion order 999 to 1. Use unshift for this purpose
41254131 // TODO (kingbri): Change to use WI Anchor positioning instead of separate top/bottom arrays
41264132 [...allActivatedEntries.values()].sort(sortFn).forEach((entry) => {
41274133 const regexDepth = entry.position === world_info_position.atDepth ? (entry.depth ?? DEFAULT_DEPTH) : null;
41284134 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) {
41824188 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]);
41834189 }
41844190
41854191 !isDryRun && timedEffects.setTimedEffects(Array.from(allActivatedEntries.values()));
41864192 buffer.resetExternalEffects();
41874193 timedEffects.cleanUp();
41884194
41894195 console.log(`[WI] Adding ${allActivatedEntries.size} entries to prompt`, Array.from(allActivatedEntries.values()));
41904196 console.debug('[WI] --- DONE ---');
41914197
41924198 return { worldInfoBefore, worldInfoAfter, EMEntries, WIDepthEntries, allActivatedEntries: new Set(allActivatedEntries.values()) };
41934199}
41944200
41954201/**
@@ -4291,7 +4297,7 @@ function filterGroupsByTimedEffects(groups, timedEffects, removeEntry) {
42914297/**
42924298 * Filters entries by inclusion groups.
42934299 * @param {object[]} newEntries Entries activated on current recursion level
42944300 * @param {SetMap<string, object>} allActivatedEntries SetMap of all activated entries
42954301 * @param {WorldInfoBuffer} buffer The buffer to use for scanning
42964302 * @param {number} scanState The current scan state
42974303 * @param {WorldInfoTimedEffects} timedEffects The timed effects currently active
@@ -4339,7 +4345,7 @@ function filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanSt
43394345 continue;
43404346 }
43414347
43424348 if (Array.from(allActivatedEntries.values()).some(x => x.group === key)) {
43434349 console.debug(`[WI] Skipping inclusion group check, group '${key}' was already activated`);
43444350 // We need to forcefully deactivate all other entries in the group
43454351 removeAllBut(group, null, false);