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
Signed| @@ -3953,7 +3953,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) { | |||
| 3953 | let newContent = ''; | 3953 | let newContent = ''; |
| 3954 | const textToScanTokens = await getTokenCountAsync(allActivatedText); | 3954 | const textToScanTokens = await getTokenCountAsync(allActivatedText); |
| 3955 | 3955 | ||
| 3956 | filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanState); | 3956 | filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanState, timedEffects); |
| 3957 | 3957 | ||
| 3958 | console.debug('[WI] --- PROBABILITY CHECKS ---'); | 3958 | console.debug('[WI] --- PROBABILITY CHECKS ---'); |
| 3959 | for (const entry of newEntries) { | 3959 | for (const entry of newEntries) { |
| @@ -4143,8 +4143,9 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) { | |||
| 4143 | * @param {WorldInfoBuffer} buffer The buffer to use for scoring | 4143 | * @param {WorldInfoBuffer} buffer The buffer to use for scoring |
| 4144 | * @param {(entry: WIScanEntry) => void} removeEntry The function to remove an entry | 4144 | * @param {(entry: WIScanEntry) => void} removeEntry The function to remove an entry |
| 4145 | * @param {number} scanState The current scan state | 4145 | * @param {number} scanState The current scan state |
| 4146 | * @param {Map<string, boolean>} hasStickyMap The sticky entries map | ||
| 4146 | */ | 4147 | */ |
| 4147 | function filterGroupsByScoring(groups, buffer, removeEntry, scanState) { | 4148 | function filterGroupsByScoring(groups, buffer, removeEntry, scanState, hasStickyMap) { |
| 4148 | for (const [key, group] of Object.entries(groups)) { | 4149 | for (const [key, group] of Object.entries(groups)) { |
| 4149 | // Group scoring is disabled both globally and for the group entries | 4150 | // Group scoring is disabled both globally and for the group entries |
| 4150 | if (!world_info_use_group_scoring && !group.some(x => x.useGroupScoring)) { | 4151 | if (!world_info_use_group_scoring && !group.some(x => x.useGroupScoring)) { |
| @@ -4152,6 +4153,13 @@ function filterGroupsByScoring(groups, buffer, removeEntry, scanState) { | |||
| 4152 | continue; | 4153 | continue; |
| 4153 | } | 4154 | } |
| 4154 | 4155 | ||
| 4156 | // If the group has any sticky entries, the rest are already removed by the timed effects filter | ||
| 4157 | const hasAnySticky = hasStickyMap.get(key); | ||
| 4158 | if (hasAnySticky) { | ||
| 4159 | console.debug(`[WI] Skipping group scoring check, group '${key}' has sticky entries`); | ||
| 4160 | continue; | ||
| 4161 | } | ||
| 4162 | |||
| 4155 | const scores = group.map(entry => buffer.getScore(entry, scanState)); | 4163 | const scores = group.map(entry => buffer.getScore(entry, scanState)); |
| 4156 | const maxScore = Math.max(...scores); | 4164 | const maxScore = Math.max(...scores); |
| 4157 | console.debug(`[WI] Group '${key}' max score:`, maxScore); | 4165 | console.debug(`[WI] Group '${key}' max score:`, maxScore); |
| @@ -4176,13 +4184,64 @@ function filterGroupsByScoring(groups, buffer, removeEntry, scanState) { | |||
| 4176 | } | 4184 | } |
| 4177 | 4185 | ||
| 4178 | /** | 4186 | /** |
| 4187 | * Removes entries on cooldown and forces sticky entries as winners. | ||
| 4188 | * @param {Record<string, WIScanEntry[]>} groups The groups to filter | ||
| 4189 | * @param {WorldInfoTimedEffects} timedEffects The timed effects to use | ||
| 4190 | * @param {(entry: WIScanEntry) => void} removeEntry The function to remove an entry | ||
| 4191 | * @returns {Map<string, boolean>} If any sticky entries were found | ||
| 4192 | */ | ||
| 4193 | function filterGroupsByTimedEffects(groups, timedEffects, removeEntry) { | ||
| 4194 | /** @type {Map<string, boolean>} */ | ||
| 4195 | const hasStickyMap = new Map(); | ||
| 4196 | |||
| 4197 | for (const [key, group] of Object.entries(groups)) { | ||
| 4198 | hasStickyMap.set(key, false); | ||
| 4199 | |||
| 4200 | // If the group has any sticky entries, leave only the sticky entries | ||
| 4201 | const stickyEntries = group.filter(x => timedEffects.isEffectActive('sticky', x)); | ||
| 4202 | if (stickyEntries.length) { | ||
| 4203 | for (const entry of group) { | ||
| 4204 | if (stickyEntries.includes(entry)) { | ||
| 4205 | continue; | ||
| 4206 | } | ||
| 4207 | |||
| 4208 | console.debug(`[WI] Entry ${entry.uid}`, `removed as a non-sticky loser from inclusion group '${key}'`, entry); | ||
| 4209 | removeEntry(entry); | ||
| 4210 | } | ||
| 4211 | |||
| 4212 | hasStickyMap.set(key, true); | ||
| 4213 | } | ||
| 4214 | |||
| 4215 | // 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. | ||
| 4216 | const cooldownEntries = group.filter(x => timedEffects.isEffectActive('cooldown', x)); | ||
| 4217 | if (cooldownEntries.length) { | ||
| 4218 | console.debug(`[WI] Inclusion group '${key}' has entries on cooldown. They will be removed.`, cooldownEntries); | ||
| 4219 | for (const entry of cooldownEntries) { | ||
| 4220 | removeEntry(entry); | ||
| 4221 | } | ||
| 4222 | } | ||
| 4223 | |||
| 4224 | const delayEntries = group.filter(x => timedEffects.isEffectActive('delay', x)); | ||
| 4225 | if (delayEntries.length) { | ||
| 4226 | console.debug(`[WI] Inclusion group '${key}' has entries with delay. They will be removed.`, delayEntries); | ||
| 4227 | for (const entry of delayEntries) { | ||
| 4228 | removeEntry(entry); | ||
| 4229 | } | ||
| 4230 | } | ||
| 4231 | } | ||
| 4232 | |||
| 4233 | return hasStickyMap; | ||
| 4234 | } | ||
| 4235 | |||
| 4236 | /** | ||
| 4179 | * Filters entries by inclusion groups. | 4237 | * Filters entries by inclusion groups. |
| 4180 | * @param {object[]} newEntries Entries activated on current recursion level | 4238 | * @param {object[]} newEntries Entries activated on current recursion level |
| 4181 | * @param {Set<object>} allActivatedEntries Set of all activated entries | 4239 | * @param {Set<object>} allActivatedEntries Set of all activated entries |
| 4182 | * @param {WorldInfoBuffer} buffer The buffer to use for scanning | 4240 | * @param {WorldInfoBuffer} buffer The buffer to use for scanning |
| 4183 | * @param {number} scanState The current scan state | 4241 | * @param {number} scanState The current scan state |
| 4242 | * @param {WorldInfoTimedEffects} timedEffects The timed effects currently active | ||
| 4184 | */ | 4243 | */ |
| 4185 | function filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanState) { | 4244 | function filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanState, timedEffects) { |
| 4186 | console.debug('[WI] --- INCLUSION GROUP CHECKS ---'); | 4245 | console.debug('[WI] --- INCLUSION GROUP CHECKS ---'); |
| 4187 | 4246 | ||
| 4188 | const grouped = newEntries.filter(x => x.group).reduce((acc, item) => { | 4247 | const grouped = newEntries.filter(x => x.group).reduce((acc, item) => { |
| @@ -4212,11 +4271,19 @@ function filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanSt | |||
| 4212 | } | 4271 | } |
| 4213 | } | 4272 | } |
| 4214 | 4273 | ||
| 4215 | filterGroupsByScoring(grouped, buffer, removeEntry, scanState); | 4274 | const hasStickyMap = filterGroupsByTimedEffects(grouped, timedEffects, removeEntry); |
| 4275 | filterGroupsByScoring(grouped, buffer, removeEntry, scanState, hasStickyMap); | ||
| 4216 | 4276 | ||
| 4217 | for (const [key, group] of Object.entries(grouped)) { | 4277 | for (const [key, group] of Object.entries(grouped)) { |
| 4218 | console.debug(`[WI] Checking inclusion group '${key}' with ${group.length} entries`, group); | 4278 | console.debug(`[WI] Checking inclusion group '${key}' with ${group.length} entries`, group); |
| 4219 | 4279 | ||
| 4280 | // If the group has any sticky entries, the rest are already removed by the timed effects filter | ||
| 4281 | const hasAnySticky = hasStickyMap.get(key); | ||
| 4282 | if (hasAnySticky) { | ||
| 4283 | console.debug(`[WI] Skipping inclusion group check, group '${key}' has sticky entries`); | ||
| 4284 | continue; | ||
| 4285 | } | ||
| 4286 | |||
| 4220 | if (Array.from(allActivatedEntries).some(x => x.group === key)) { | 4287 | if (Array.from(allActivatedEntries).some(x => x.group === key)) { |
| 4221 | console.debug(`[WI] Skipping inclusion group check, group '${key}' was already activated`); | 4288 | console.debug(`[WI] Skipping inclusion group check, group '${key}' was already activated`); |
| 4222 | // We need to forcefully deactivate all other entries in the group | 4289 | // We need to forcefully deactivate all other entries in the group |