mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-03-03 03:17:54 +01:00
Expand WORLD_INFO_FORCE_ACTIVATE API to allow for dynamically modified Lorebook entries
This commit is contained in:
parent
357c2e0756
commit
6efa36bdbf
@ -130,9 +130,9 @@ const KNOWN_DECORATORS = ['@@activate', '@@dont_activate'];
|
|||||||
*/
|
*/
|
||||||
class WorldInfoBuffer {
|
class WorldInfoBuffer {
|
||||||
/**
|
/**
|
||||||
* @type {object[]} Array of entries that need to be activated no matter what
|
* @type {Map<string, object>} Map of entries that need to be activated no matter what
|
||||||
*/
|
*/
|
||||||
static externalActivations = [];
|
static externalActivations = new Map();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {string[]} Array of messages sorted by ascending depth
|
* @type {string[]} Array of messages sorted by ascending depth
|
||||||
@ -311,20 +311,20 @@ class WorldInfoBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the current entry is externally activated.
|
* Get the externally activated version of the entry, if there is one.
|
||||||
* @param {object} entry WI entry to check
|
* @param {object} entry WI entry to check
|
||||||
* @returns {boolean} True if the entry is forcefully activated
|
* @returns {object|null} the external version if the entry is forcefully activated, null otherwise
|
||||||
*/
|
*/
|
||||||
isExternallyActivated(entry) {
|
getExternallyActivated(entry) {
|
||||||
// Entries could be copied with structuredClone, so we need to compare them by string representation
|
// Entries could be copied with structuredClone, so we need to compare them by string representation
|
||||||
return WorldInfoBuffer.externalActivations.some(x => JSON.stringify(x) === JSON.stringify(entry));
|
return WorldInfoBuffer.externalActivations.get(`${entry.world}.${entry.uid}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean-up the external effects for entries.
|
* Clean-up the external effects for entries.
|
||||||
*/
|
*/
|
||||||
resetExternalEffects() {
|
resetExternalEffects() {
|
||||||
WorldInfoBuffer.externalActivations.splice(0, WorldInfoBuffer.externalActivations.length);
|
WorldInfoBuffer.externalActivations = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -751,7 +751,7 @@ export async function getWorldInfoPrompt(chat, maxContext, isDryRun) {
|
|||||||
worldInfoString = worldInfoBefore + worldInfoAfter;
|
worldInfoString = worldInfoBefore + worldInfoAfter;
|
||||||
|
|
||||||
if (!isDryRun && activatedWorldInfo.allActivatedEntries && activatedWorldInfo.allActivatedEntries.size > 0) {
|
if (!isDryRun && activatedWorldInfo.allActivatedEntries && activatedWorldInfo.allActivatedEntries.size > 0) {
|
||||||
const arg = Array.from(activatedWorldInfo.allActivatedEntries);
|
const arg = Array.from(activatedWorldInfo.allActivatedEntries.values());
|
||||||
await eventSource.emit(event_types.WORLD_INFO_ACTIVATED, arg);
|
await eventSource.emit(event_types.WORLD_INFO_ACTIVATED, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -868,7 +868,13 @@ export function setWorldInfoSettings(settings, data) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
eventSource.on(event_types.WORLDINFO_FORCE_ACTIVATE, (entries) => {
|
eventSource.on(event_types.WORLDINFO_FORCE_ACTIVATE, (entries) => {
|
||||||
WorldInfoBuffer.externalActivations.push(...entries);
|
for (const entry of entries) {
|
||||||
|
if (!Object.hasOwn(entry, 'world') || !Object.hasOwn(entry, 'uid')) {
|
||||||
|
console.error('WORLDINFO_FORCE_ACTIVATE requires all entries to have both world and uid fields, entry IGNORED');
|
||||||
|
} else {
|
||||||
|
WorldInfoBuffer.externalActivations.set(`${entry.world}.${entry.uid}`, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add slash commands
|
// Add slash commands
|
||||||
@ -3698,7 +3704,7 @@ function parseDecorators(content) {
|
|||||||
* @param {string[]} chat The chat messages to scan, in reverse order.
|
* @param {string[]} chat The chat messages to scan, in reverse order.
|
||||||
* @param {number} maxContext The maximum context size of the generation.
|
* @param {number} maxContext The maximum context size of the generation.
|
||||||
* @param {boolean} isDryRun Whether to perform a dry run.
|
* @param {boolean} isDryRun Whether to perform a dry run.
|
||||||
* @typedef {{ worldInfoBefore: string, worldInfoAfter: string, EMEntries: any[], WIDepthEntries: any[], allActivatedEntries: Set<any> }} WIActivated
|
* @typedef {{ worldInfoBefore: string, worldInfoAfter: string, EMEntries: any[], WIDepthEntries: any[], allActivatedEntries: Map<string, any> }} WIActivated
|
||||||
* @returns {Promise<WIActivated>} The world info activated.
|
* @returns {Promise<WIActivated>} The world info activated.
|
||||||
*/
|
*/
|
||||||
export async function checkWorldInfo(chat, maxContext, isDryRun) {
|
export async function checkWorldInfo(chat, maxContext, isDryRun) {
|
||||||
@ -3724,7 +3730,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
|
|||||||
let scanState = scan_state.INITIAL;
|
let scanState = scan_state.INITIAL;
|
||||||
let token_budget_overflowed = false;
|
let token_budget_overflowed = false;
|
||||||
let count = 0;
|
let count = 0;
|
||||||
let allActivatedEntries = new Set();
|
let allActivatedEntries = new Map();
|
||||||
let failedProbabilityChecks = new Set();
|
let failedProbabilityChecks = new Set();
|
||||||
let allActivatedText = '';
|
let allActivatedText = '';
|
||||||
|
|
||||||
@ -3742,7 +3748,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
|
|||||||
!isDryRun && timedEffects.checkTimedEffects();
|
!isDryRun && timedEffects.checkTimedEffects();
|
||||||
|
|
||||||
if (sortedEntries.length === 0) {
|
if (sortedEntries.length === 0) {
|
||||||
return { worldInfoBefore: '', worldInfoAfter: '', WIDepthEntries: [], EMEntries: [], allActivatedEntries: new Set() };
|
return { worldInfoBefore: '', worldInfoAfter: '', WIDepthEntries: [], EMEntries: [], allActivatedEntries: new Map() };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {number[]} Represents the delay levels for entries that are delayed until recursion */
|
/** @type {number[]} Represents the delay levels for entries that are delayed until recursion */
|
||||||
@ -3789,7 +3795,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Already processed, considered and then skipped entries should still be skipped
|
// Already processed, considered and then skipped entries should still be skipped
|
||||||
if (failedProbabilityChecks.has(entry) || allActivatedEntries.has(entry)) {
|
if (failedProbabilityChecks.has(entry) || allActivatedEntries.has(`${entry.world}.${entry.uid}`)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3858,6 +3864,12 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (buffer.getExternallyActivated(entry)) {
|
||||||
|
log('externally activated');
|
||||||
|
activatedNow.add(buffer.getExternallyActivated(entry));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (entry.decorators.includes('@@activate')) {
|
if (entry.decorators.includes('@@activate')) {
|
||||||
log('activated by @@activate decorator');
|
log('activated by @@activate decorator');
|
||||||
activatedNow.add(entry);
|
activatedNow.add(entry);
|
||||||
@ -3876,12 +3888,6 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer.isExternallyActivated(entry)) {
|
|
||||||
log('externally activated');
|
|
||||||
activatedNow.add(entry);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSticky) {
|
if (isSticky) {
|
||||||
log('activated because active sticky');
|
log('activated because active sticky');
|
||||||
activatedNow.add(entry);
|
activatedNow.add(entry);
|
||||||
@ -4039,7 +4045,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
allActivatedEntries.add(entry);
|
allActivatedEntries.set(`${entry.world}.${entry.uid}`, entry);
|
||||||
console.debug(`[WI] Entry ${entry.uid} activation successful, adding to prompt`, entry);
|
console.debug(`[WI] Entry ${entry.uid} activation successful, adding to prompt`, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4123,7 +4129,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
|
|||||||
|
|
||||||
// Appends from insertion order 999 to 1. Use unshift for this purpose
|
// Appends from insertion order 999 to 1. Use unshift for this purpose
|
||||||
// TODO (kingbri): Change to use WI Anchor positioning instead of separate top/bottom arrays
|
// TODO (kingbri): Change to use WI Anchor positioning instead of separate top/bottom arrays
|
||||||
[...allActivatedEntries].sort(sortFn).forEach((entry) => {
|
[...allActivatedEntries.values()].sort(sortFn).forEach((entry) => {
|
||||||
const regexDepth = entry.position === world_info_position.atDepth ? (entry.depth ?? DEFAULT_DEPTH) : null;
|
const regexDepth = entry.position === world_info_position.atDepth ? (entry.depth ?? DEFAULT_DEPTH) : null;
|
||||||
const content = getRegexedString(entry.content, regex_placement.WORLD_INFO, { depth: regexDepth, isMarkdown: false, isPrompt: true });
|
const content = getRegexedString(entry.content, regex_placement.WORLD_INFO, { depth: regexDepth, isMarkdown: false, isPrompt: true });
|
||||||
|
|
||||||
@ -4182,14 +4188,14 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
|
|||||||
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]);
|
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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
!isDryRun && timedEffects.setTimedEffects(Array.from(allActivatedEntries));
|
!isDryRun && timedEffects.setTimedEffects(Array.from(allActivatedEntries.values()));
|
||||||
buffer.resetExternalEffects();
|
buffer.resetExternalEffects();
|
||||||
timedEffects.cleanUp();
|
timedEffects.cleanUp();
|
||||||
|
|
||||||
console.log(`[WI] Adding ${allActivatedEntries.size} entries to prompt`, Array.from(allActivatedEntries));
|
console.log(`[WI] Adding ${allActivatedEntries.size} entries to prompt`, Array.from(allActivatedEntries.values()));
|
||||||
console.debug('[WI] --- DONE ---');
|
console.debug('[WI] --- DONE ---');
|
||||||
|
|
||||||
return { worldInfoBefore, worldInfoAfter, EMEntries, WIDepthEntries, allActivatedEntries };
|
return { worldInfoBefore, worldInfoAfter, EMEntries, WIDepthEntries, allActivatedEntries: allActivatedEntries.values() };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -4291,7 +4297,7 @@ function filterGroupsByTimedEffects(groups, timedEffects, removeEntry) {
|
|||||||
/**
|
/**
|
||||||
* Filters entries by inclusion groups.
|
* Filters entries by inclusion groups.
|
||||||
* @param {object[]} newEntries Entries activated on current recursion level
|
* @param {object[]} newEntries Entries activated on current recursion level
|
||||||
* @param {Set<object>} allActivatedEntries Set of all activated entries
|
* @param {Map<string, object>} allActivatedEntries Map of all activated entries
|
||||||
* @param {WorldInfoBuffer} buffer The buffer to use for scanning
|
* @param {WorldInfoBuffer} buffer The buffer to use for scanning
|
||||||
* @param {number} scanState The current scan state
|
* @param {number} scanState The current scan state
|
||||||
* @param {WorldInfoTimedEffects} timedEffects The timed effects currently active
|
* @param {WorldInfoTimedEffects} timedEffects The timed effects currently active
|
||||||
@ -4339,7 +4345,7 @@ function filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanSt
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.from(allActivatedEntries).some(x => x.group === key)) {
|
if (Array.from(allActivatedEntries.values()).some(x => x.group === key)) {
|
||||||
console.debug(`[WI] Skipping inclusion group check, group '${key}' was already activated`);
|
console.debug(`[WI] Skipping inclusion group check, group '${key}' was already activated`);
|
||||||
// We need to forcefully deactivate all other entries in the group
|
// We need to forcefully deactivate all other entries in the group
|
||||||
removeAllBut(group, null, false);
|
removeAllBut(group, null, false);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user