Filter inclusion groups by timed effects (#2765)

* Filter inclusion groups by timed effects
Closes #2762

* Skip group scoring check if sticky entries are present

* Optimize sticky checks
This commit is contained in:
Cohee 2024-09-05 21:15:45 +03:00 committed by GitHub
parent 758c90be00
commit 75c6bee350
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -3953,7 +3953,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
let newContent = '';
const textToScanTokens = await getTokenCountAsync(allActivatedText);
filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanState);
filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanState, timedEffects);
console.debug('[WI] --- PROBABILITY CHECKS ---');
for (const entry of newEntries) {
@ -4143,8 +4143,9 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
* @param {WorldInfoBuffer} buffer The buffer to use for scoring
* @param {(entry: WIScanEntry) => void} removeEntry The function to remove an entry
* @param {number} scanState The current scan state
* @param {Map<string, boolean>} hasStickyMap The sticky entries map
*/
function filterGroupsByScoring(groups, buffer, removeEntry, scanState) {
function filterGroupsByScoring(groups, buffer, removeEntry, scanState, hasStickyMap) {
for (const [key, group] of Object.entries(groups)) {
// Group scoring is disabled both globally and for the group entries
if (!world_info_use_group_scoring && !group.some(x => x.useGroupScoring)) {
@ -4152,6 +4153,13 @@ function filterGroupsByScoring(groups, buffer, removeEntry, scanState) {
continue;
}
// If the group has any sticky entries, the rest are already removed by the timed effects filter
const hasAnySticky = hasStickyMap.get(key);
if (hasAnySticky) {
console.debug(`[WI] Skipping group scoring check, group '${key}' has sticky entries`);
continue;
}
const scores = group.map(entry => buffer.getScore(entry, scanState));
const maxScore = Math.max(...scores);
console.debug(`[WI] Group '${key}' max score:`, maxScore);
@ -4175,14 +4183,65 @@ function filterGroupsByScoring(groups, buffer, removeEntry, scanState) {
}
}
/**
* Removes entries on cooldown and forces sticky entries as winners.
* @param {Record<string, WIScanEntry[]>} groups The groups to filter
* @param {WorldInfoTimedEffects} timedEffects The timed effects to use
* @param {(entry: WIScanEntry) => void} removeEntry The function to remove an entry
* @returns {Map<string, boolean>} If any sticky entries were found
*/
function filterGroupsByTimedEffects(groups, timedEffects, removeEntry) {
/** @type {Map<string, boolean>} */
const hasStickyMap = new Map();
for (const [key, group] of Object.entries(groups)) {
hasStickyMap.set(key, false);
// If the group has any sticky entries, leave only the sticky entries
const stickyEntries = group.filter(x => timedEffects.isEffectActive('sticky', x));
if (stickyEntries.length) {
for (const entry of group) {
if (stickyEntries.includes(entry)) {
continue;
}
console.debug(`[WI] Entry ${entry.uid}`, `removed as a non-sticky loser from inclusion group '${key}'`, entry);
removeEntry(entry);
}
hasStickyMap.set(key, true);
}
// It should not be possible for an entry on cooldown/delay to event get into the grouping phase but @Wolfsblvt told me to leave it here.
const cooldownEntries = group.filter(x => timedEffects.isEffectActive('cooldown', x));
if (cooldownEntries.length) {
console.debug(`[WI] Inclusion group '${key}' has entries on cooldown. They will be removed.`, cooldownEntries);
for (const entry of cooldownEntries) {
removeEntry(entry);
}
}
const delayEntries = group.filter(x => timedEffects.isEffectActive('delay', x));
if (delayEntries.length) {
console.debug(`[WI] Inclusion group '${key}' has entries with delay. They will be removed.`, delayEntries);
for (const entry of delayEntries) {
removeEntry(entry);
}
}
}
return hasStickyMap;
}
/**
* Filters entries by inclusion groups.
* @param {object[]} newEntries Entries activated on current recursion level
* @param {Set<object>} allActivatedEntries Set of all activated entries
* @param {WorldInfoBuffer} buffer The buffer to use for scanning
* @param {number} scanState The current scan state
* @param {WorldInfoTimedEffects} timedEffects The timed effects currently active
*/
function filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanState) {
function filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanState, timedEffects) {
console.debug('[WI] --- INCLUSION GROUP CHECKS ---');
const grouped = newEntries.filter(x => x.group).reduce((acc, item) => {
@ -4212,11 +4271,19 @@ function filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanSt
}
}
filterGroupsByScoring(grouped, buffer, removeEntry, scanState);
const hasStickyMap = filterGroupsByTimedEffects(grouped, timedEffects, removeEntry);
filterGroupsByScoring(grouped, buffer, removeEntry, scanState, hasStickyMap);
for (const [key, group] of Object.entries(grouped)) {
console.debug(`[WI] Checking inclusion group '${key}' with ${group.length} entries`, group);
// If the group has any sticky entries, the rest are already removed by the timed effects filter
const hasAnySticky = hasStickyMap.get(key);
if (hasAnySticky) {
console.debug(`[WI] Skipping inclusion group check, group '${key}' has sticky entries`);
continue;
}
if (Array.from(allActivatedEntries).some(x => x.group === key)) {
console.debug(`[WI] Skipping inclusion group check, group '${key}' was already activated`);
// We need to forcefully deactivate all other entries in the group