Merge pull request #2425 from SillyTavern/wi-delay

Add WI entry delay
This commit is contained in:
Cohee 2024-06-27 00:36:23 +03:00 committed by GitHub
commit 3b7540da05
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 68 additions and 6 deletions

View File

@ -5425,7 +5425,7 @@
</div>
</div>
<div class="flex-container wide100p flexGap10">
<div class="flex3 flex-container flexFlowColumn flexNoGap">
<div class="flex4 flex-container flexFlowColumn flexNoGap">
<div class="flex-container justifySpaceBetween">
<small for="group">
<span data-i18n="Inclusion Group">Inclusion Group</span>
@ -5447,7 +5447,7 @@
<input type="text" class="text_pole margin0" name="group" rows="1" data-i18n="[placeholder]Only one entry with the same label will be activated" placeholder="Only one entry with the same label will be activated">
</div>
</div>
<div class="flex1 flex-container flexFlowColumn flexNoGap" data-i18n="[title]A relative likelihood of entry activation within the group" title="A relative likelihood of entry activation within the group">
<div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]A relative likelihood of entry activation within the group" title="A relative likelihood of entry activation within the group">
<div class="flex-container justifySpaceBetween marginBot5">
<small for="groupWeight" data-i18n="Group Weight">
Group Weight
@ -5483,6 +5483,19 @@
<input class="text_pole margin0" name="cooldown" type="number" placeholder="No cooldown" min="0" max="999999">
</div>
</div>
<div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a delay can't be activated until there are N messages present in the chat." title="Entries with a delay can't be activated until there are N messages present in the chat.">
<div class="flex-container justifySpaceBetween marginBot5">
<small class="flex-container alignItemsBaseline" for="delay" data-i18n="Delay">
<span data-i18n="Delay">
Delay
</span>
<i class="fa-solid fa-comments fa-xs"></i>
</small>
</div>
<div class="range-block-range">
<input class="text_pole margin0" name="delay" type="number" placeholder="No delay" min="0" max="999999">
</div>
</div>
</div>
<div class="flex-container wide100p flexGap10">
<div class="flex4 flex-container flexFlowColumn flexNoGap">

View File

@ -99,6 +99,7 @@ const MAX_SCAN_DEPTH = 1000;
* @property {number} [selectiveLogic] The logic to use for selective activation
* @property {number} [sticky] The sticky value of the entry
* @property {number} [cooldown] The cooldown of the entry
* @property {number} [delay] The delay of the entry
*/
/**
@ -111,7 +112,7 @@ const MAX_SCAN_DEPTH = 1000;
/**
* @typedef TimedEffectType Type of timed effect
* @type {'sticky'|'cooldown'}
* @type {'sticky'|'cooldown'|'delay'}
*/
// End typedef area
@ -371,6 +372,7 @@ class WorldInfoTimedEffects {
#buffer = {
'sticky': [],
'cooldown': [],
'delay': [],
};
/**
@ -404,6 +406,8 @@ class WorldInfoTimedEffects {
'cooldown': (entry) => {
console.debug('Cooldown ended for entry', entry.uid);
},
'delay': () => {},
};
/**
@ -529,12 +533,31 @@ class WorldInfoTimedEffects {
}
}
/**
* Processes entries for the "delay" timed effect.
* @param {WIScanEntry[]} buffer Buffer to store the entries
*/
#checkDelayEffect(buffer) {
for (const entry of this.#entries) {
if (!entry.delay) {
continue;
}
if (this.#chat.length < entry.delay) {
buffer.push(entry);
console.log('Timed effect "delay" applied to entry', entry);
}
}
}
/**
* Checks for timed effects on chat messages.
*/
checkTimedEffects() {
this.#checkTimedEffectOfType('sticky', this.#buffer.sticky, this.#onEnded.sticky.bind(this));
this.#checkTimedEffectOfType('cooldown', this.#buffer.cooldown, this.#onEnded.cooldown.bind(this));
this.#checkDelayEffect(this.#buffer.delay);
}
/**
@ -611,7 +634,7 @@ class WorldInfoTimedEffects {
* @returns {boolean} Is recognized type
*/
isValidEffectType(type) {
return typeof type === 'string' && ['sticky', 'cooldown'].includes(type.trim().toLowerCase());
return typeof type === 'string' && ['sticky', 'cooldown', 'delay'].includes(type.trim().toLowerCase());
}
/**
@ -1685,7 +1708,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl
// Regardless of whether success is displayed or not. Make sure the delete button is available.
// Do not put this code behind.
$('#world_popup_delete').off('click').on('click', async () => {
const confirmation = await Popup.show.confirm(`Delete the World/Lorebook: "${name}"?`, `This action is irreversible!`);
const confirmation = await Popup.show.confirm(`Delete the World/Lorebook: "${name}"?`, 'This action is irreversible!');
if (!confirmation) {
return;
}
@ -1941,6 +1964,7 @@ const originalDataKeyMap = {
'groupWeight': 'extensions.group_weight',
'sticky': 'extensions.sticky',
'cooldown': 'extensions.cooldown',
'delay': 'extensions.delay',
};
/** Checks the state of the current search, and adds/removes the search sorting option accordingly */
@ -2589,6 +2613,19 @@ function getWorldEntry(name, data, entry) {
});
cooldown.val(entry.cooldown > 0 ? entry.cooldown : '').trigger('input');
// delay
const delay = template.find('input[name="delay"]');
delay.data('uid', entry.uid);
delay.on('input', function () {
const uid = $(this).data('uid');
const value = Number($(this).val());
data.entries[uid].delay = !isNaN(value) ? value : null;
setOriginalDataValue(data, uid, 'extensions.delay', data.entries[uid].delay);
saveWorldInfo(name, data);
});
delay.val(entry.delay > 0 ? entry.delay : '').trigger('input');
// probability
if (entry.probability === undefined) {
entry.probability = null;
@ -3139,6 +3176,7 @@ const newEntryDefinition = {
role: { default: 0, type: 'enum' },
sticky: { default: null, type: 'number?' },
cooldown: { default: null, type: 'number?' },
delay: { default: null, type: 'number?' },
};
const newEntryTemplate = Object.fromEntries(
@ -3533,9 +3571,15 @@ async function checkWorldInfo(chat, maxContext, isDryRun) {
const isSticky = timedEffects.isEffectActive('sticky', entry);
const isCooldown = timedEffects.isEffectActive('cooldown', entry);
const isDelay = timedEffects.isEffectActive('delay', entry);
if (isDelay) {
console.debug(`WI entry ${entry.uid} suppressed by delay`, entry);
continue;
}
if (isCooldown && !isSticky) {
console.debug(`WI entry ${entry.uid} suppressed by cooldown`);
console.debug(`WI entry ${entry.uid} suppressed by cooldown`, entry);
continue;
}
@ -3933,6 +3977,7 @@ function convertAgnaiMemoryBook(inputObj) {
role: extension_prompt_roles.SYSTEM,
sticky: null,
cooldown: null,
delay: null,
};
});
@ -3974,6 +4019,7 @@ function convertRisuLorebook(inputObj) {
role: extension_prompt_roles.SYSTEM,
sticky: null,
cooldown: null,
delay: null,
};
});
@ -4020,6 +4066,7 @@ function convertNovelLorebook(inputObj) {
role: extension_prompt_roles.SYSTEM,
sticky: null,
cooldown: null,
delay: null,
};
});
@ -4068,6 +4115,7 @@ function convertCharacterBook(characterBook) {
vectorized: entry.extensions?.vectorized ?? false,
sticky: entry.extensions?.sticky ?? null,
cooldown: entry.extensions?.cooldown ?? null,
delay: entry.extensions?.delay ?? null,
};
});

View File

@ -485,6 +485,7 @@ function convertWorldInfoToCharacterBook(name, entries) {
vectorized: entry.vectorized ?? false,
sticky: entry.sticky ?? null,
cooldown: entry.cooldown ?? null,
delay: entry.delay ?? null,
},
};