mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2024-12-11 17:07:07 +01:00
Add sticky and cooldown for timed WI entries
This commit is contained in:
parent
e9f93ba748
commit
ab7b07ba28
@ -5390,24 +5390,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-container wide100p flexGap10">
|
<div class="flex-container wide100p flexGap10">
|
||||||
<div class="flex4 flex-container flexFlowColumn flexNoGap">
|
<div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Sticky entries will stay active for N messages after being triggered." title="Sticky entries will stay active for N messages after being triggered.">
|
||||||
<div class="flex-container justifySpaceBetween">
|
<div class="flex-container justifySpaceBetween marginBot5">
|
||||||
<small for="characterFilter" data-i18n="Filter to Character(s)">
|
<small for="sticky" data-i18n="Sticky">
|
||||||
Filter to Character(s)
|
Sticky
|
||||||
</small>
|
</small>
|
||||||
<label class="checkbox_label flexNoGap margin-r5" for="character_exclusion">
|
|
||||||
<input type="checkbox" name="character_exclusion" />
|
|
||||||
<span>
|
|
||||||
<small data-i18n="Character Exclusion">Character Exclusion</small>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="range-block-range">
|
<div class="range-block-range">
|
||||||
<select name="characterFilter" class="select2_multi_sameline" multiple>
|
<input class="text_pole margin0" name="sticky" type="number" placeholder="Non-sticky" min="1" max="999999">
|
||||||
<option value="">
|
</div>
|
||||||
<span data-i18n="-- Characters not found --">-- Characters not found --</span>
|
</div>
|
||||||
</option>
|
<div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a cooldown can't be activated N messages after being triggered." title="Entries with a cooldown can't be activated N messages after being triggered.">
|
||||||
</select>
|
<div class="flex-container justifySpaceBetween marginBot5">
|
||||||
|
<small for="cooldown" data-i18n="Cooldown">
|
||||||
|
Cooldown
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="range-block-range">
|
||||||
|
<input class="text_pole margin0" name="cooldown" type="number" placeholder="No cooldown" min="1" max="999999">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex3 flex-container flexFlowColumn flexNoGap">
|
<div class="flex3 flex-container flexFlowColumn flexNoGap">
|
||||||
@ -5443,6 +5443,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex-container wide100p flexGap10">
|
||||||
|
<div class="flex4 flex-container flexFlowColumn flexNoGap">
|
||||||
|
<div class="flex-container justifySpaceBetween">
|
||||||
|
<small for="characterFilter" data-i18n="Filter to Character(s)">
|
||||||
|
Filter to Character(s)
|
||||||
|
</small>
|
||||||
|
<label class="checkbox_label flexNoGap margin-r5" for="character_exclusion">
|
||||||
|
<input type="checkbox" name="character_exclusion" />
|
||||||
|
<span>
|
||||||
|
<small data-i18n="Character Exclusion">Character Exclusion</small>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="range-block-range">
|
||||||
|
<select name="characterFilter" class="select2_multi_sameline" multiple>
|
||||||
|
<option value="">
|
||||||
|
<span data-i18n="-- Characters not found --">-- Characters not found --</span>
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div name="WIEntryBottomControls" class="flex-container flex1 justifySpaceBetween world_entry_form_horizontal">
|
<div name="WIEntryBottomControls" class="flex-container flex1 justifySpaceBetween world_entry_form_horizontal">
|
||||||
<div class="flex-container flexFlowColumn flexNoGap wi-enter-footer-text ">
|
<div class="flex-container flexFlowColumn flexNoGap wi-enter-footer-text ">
|
||||||
<label class="checkbox flex-container">
|
<label class="checkbox flex-container">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { saveSettings, callPopup, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types, getExtensionPromptByName, saveMetadata, getCurrentChatId, extension_prompt_roles } from '../script.js';
|
import { saveSettings, callPopup, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types, getExtensionPromptByName, saveMetadata, getCurrentChatId, extension_prompt_roles } from '../script.js';
|
||||||
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition, isTrueBoolean, setValueByPath, flashHighlight, select2ModifyOptions, getSelect2OptionId, dynamicSelect2DataViaAjax, highlightRegex, select2ChoiceClickSubscribe, isFalseBoolean, equalsIgnoreCaseAndAccents, getSanitizedFilename, checkOverwriteExistingData } from './utils.js';
|
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition, isTrueBoolean, setValueByPath, flashHighlight, select2ModifyOptions, getSelect2OptionId, dynamicSelect2DataViaAjax, highlightRegex, select2ChoiceClickSubscribe, isFalseBoolean, getSanitizedFilename, checkOverwriteExistingData, getStringHash } from './utils.js';
|
||||||
import { extension_settings, getContext } from './extensions.js';
|
import { extension_settings, getContext } from './extensions.js';
|
||||||
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from './authors-note.js';
|
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from './authors-note.js';
|
||||||
import { isMobile } from './RossAscends-mods.js';
|
import { isMobile } from './RossAscends-mods.js';
|
||||||
@ -96,6 +96,8 @@ class WorldInfoBuffer {
|
|||||||
* @property {string[]} [key] The primary keys to scan for
|
* @property {string[]} [key] The primary keys to scan for
|
||||||
* @property {string[]} [keysecondary] The secondary keys to scan for
|
* @property {string[]} [keysecondary] The secondary keys to scan for
|
||||||
* @property {number} [selectiveLogic] The logic to use for selective activation
|
* @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
|
||||||
*/
|
*/
|
||||||
// End typedef area
|
// End typedef area
|
||||||
|
|
||||||
@ -104,6 +106,11 @@ class WorldInfoBuffer {
|
|||||||
*/
|
*/
|
||||||
static externalActivations = [];
|
static externalActivations = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {object[]} Array of entries that need to be suppressed no matter what
|
||||||
|
*/
|
||||||
|
static externalSuppressions = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {string[]} Array of messages sorted by ascending depth
|
* @type {string[]} Array of messages sorted by ascending depth
|
||||||
*/
|
*/
|
||||||
@ -263,10 +270,20 @@ class WorldInfoBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the force activations buffer.
|
* Check if the current entry is externally suppressed.
|
||||||
|
* @param {object} entry WI entry to check
|
||||||
|
* @returns {boolean} True if the entry is forcefully suppressed
|
||||||
*/
|
*/
|
||||||
cleanExternalActivations() {
|
isExternallySuppressed(entry) {
|
||||||
|
return WorldInfoBuffer.externalSuppressions.some(x => JSON.stringify(x) === JSON.stringify(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean-up the external effects for entries (activations and suppressions).
|
||||||
|
*/
|
||||||
|
resetExternalEffects() {
|
||||||
WorldInfoBuffer.externalActivations.splice(0, WorldInfoBuffer.externalActivations.length);
|
WorldInfoBuffer.externalActivations.splice(0, WorldInfoBuffer.externalActivations.length);
|
||||||
|
WorldInfoBuffer.externalSuppressions.splice(0, WorldInfoBuffer.externalSuppressions.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -354,7 +371,8 @@ export const wi_anchor_position = {
|
|||||||
after: 1,
|
after: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const worldInfoCache = {};
|
const worldInfoCache = new Map();
|
||||||
|
const entryHashCache = new Map();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the world info based on chat messages.
|
* Gets the world info based on chat messages.
|
||||||
@ -367,7 +385,7 @@ const worldInfoCache = {};
|
|||||||
async function getWorldInfoPrompt(chat, maxContext, isDryRun) {
|
async function getWorldInfoPrompt(chat, maxContext, isDryRun) {
|
||||||
let worldInfoString = '', worldInfoBefore = '', worldInfoAfter = '';
|
let worldInfoString = '', worldInfoBefore = '', worldInfoAfter = '';
|
||||||
|
|
||||||
const activatedWorldInfo = await checkWorldInfo(chat, maxContext);
|
const activatedWorldInfo = await checkWorldInfo(chat, maxContext, isDryRun);
|
||||||
worldInfoBefore = activatedWorldInfo.worldInfoBefore;
|
worldInfoBefore = activatedWorldInfo.worldInfoBefore;
|
||||||
worldInfoAfter = activatedWorldInfo.worldInfoAfter;
|
worldInfoAfter = activatedWorldInfo.worldInfoAfter;
|
||||||
worldInfoString = worldInfoBefore + worldInfoAfter;
|
worldInfoString = worldInfoBefore + worldInfoAfter;
|
||||||
@ -698,7 +716,8 @@ function registerWorldInfoSlashCommands() {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'world',
|
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||||
|
name: 'world',
|
||||||
callback: onWorldInfoChange,
|
callback: onWorldInfoChange,
|
||||||
namedArgumentList: [
|
namedArgumentList: [
|
||||||
new SlashCommandNamedArgument(
|
new SlashCommandNamedArgument(
|
||||||
@ -720,13 +739,15 @@ function registerWorldInfoSlashCommands() {
|
|||||||
`,
|
`,
|
||||||
aliases: [],
|
aliases: [],
|
||||||
}));
|
}));
|
||||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'getchatbook',
|
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||||
|
name: 'getchatbook',
|
||||||
callback: getChatBookCallback,
|
callback: getChatBookCallback,
|
||||||
returns: 'lorebook name',
|
returns: 'lorebook name',
|
||||||
helpString: 'Get a name of the chat-bound lorebook or create a new one if was unbound, and pass it down the pipe.',
|
helpString: 'Get a name of the chat-bound lorebook or create a new one if was unbound, and pass it down the pipe.',
|
||||||
aliases: ['getchatlore', 'getchatwi'],
|
aliases: ['getchatlore', 'getchatwi'],
|
||||||
}));
|
}));
|
||||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'findentry',
|
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||||
|
name: 'findentry',
|
||||||
aliases: ['findlore', 'findwi'],
|
aliases: ['findlore', 'findwi'],
|
||||||
returns: 'UID',
|
returns: 'UID',
|
||||||
callback: findBookEntryCallback,
|
callback: findBookEntryCallback,
|
||||||
@ -757,7 +778,8 @@ function registerWorldInfoSlashCommands() {
|
|||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
}));
|
}));
|
||||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'getentryfield',
|
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||||
|
name: 'getentryfield',
|
||||||
aliases: ['getlorefield', 'getwifield'],
|
aliases: ['getlorefield', 'getwifield'],
|
||||||
callback: getEntryFieldCallback,
|
callback: getEntryFieldCallback,
|
||||||
returns: 'field value',
|
returns: 'field value',
|
||||||
@ -788,7 +810,8 @@ function registerWorldInfoSlashCommands() {
|
|||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
}));
|
}));
|
||||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'createentry',
|
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||||
|
name: 'createentry',
|
||||||
callback: createEntryCallback,
|
callback: createEntryCallback,
|
||||||
aliases: ['createlore', 'createwi'],
|
aliases: ['createlore', 'createwi'],
|
||||||
returns: 'UID of the new record',
|
returns: 'UID of the new record',
|
||||||
@ -819,7 +842,8 @@ function registerWorldInfoSlashCommands() {
|
|||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
}));
|
}));
|
||||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'setentryfield',
|
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||||
|
name: 'setentryfield',
|
||||||
callback: setEntryFieldCallback,
|
callback: setEntryFieldCallback,
|
||||||
aliases: ['setlorefield', 'setwifield'],
|
aliases: ['setlorefield', 'setwifield'],
|
||||||
namedArgumentList: [
|
namedArgumentList: [
|
||||||
@ -871,8 +895,8 @@ async function loadWorldInfoData(name) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (worldInfoCache[name]) {
|
if (worldInfoCache.has(name)) {
|
||||||
return worldInfoCache[name];
|
return worldInfoCache.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch('/api/worldinfo/get', {
|
const response = await fetch('/api/worldinfo/get', {
|
||||||
@ -884,7 +908,7 @@ async function loadWorldInfoData(name) {
|
|||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
worldInfoCache[name] = data;
|
worldInfoCache.set(name, data);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1316,6 +1340,8 @@ const originalDataKeyMap = {
|
|||||||
'vectorized': 'extensions.vectorized',
|
'vectorized': 'extensions.vectorized',
|
||||||
'groupOverride': 'extensions.group_override',
|
'groupOverride': 'extensions.group_override',
|
||||||
'groupWeight': 'extensions.group_weight',
|
'groupWeight': 'extensions.group_weight',
|
||||||
|
'sticky': 'extensions.sticky',
|
||||||
|
'cooldown': 'extensions.cooldown',
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Checks the state of the current search, and adds/removes the search sorting option accordingly */
|
/** Checks the state of the current search, and adds/removes the search sorting option accordingly */
|
||||||
@ -1937,6 +1963,32 @@ function getWorldEntry(name, data, entry) {
|
|||||||
});
|
});
|
||||||
groupWeightInput.val(entry.groupWeight ?? DEFAULT_WEIGHT).trigger('input');
|
groupWeightInput.val(entry.groupWeight ?? DEFAULT_WEIGHT).trigger('input');
|
||||||
|
|
||||||
|
// sticky
|
||||||
|
const sticky = template.find('input[name="sticky"]');
|
||||||
|
sticky.data('uid', entry.uid);
|
||||||
|
sticky.on('input', function () {
|
||||||
|
const uid = $(this).data('uid');
|
||||||
|
const value = Number($(this).val());
|
||||||
|
data.entries[uid].sticky = !isNaN(value) ? value : null;
|
||||||
|
|
||||||
|
setOriginalDataValue(data, uid, 'extensions.sticky', data.entries[uid].sticky);
|
||||||
|
saveWorldInfo(name, data);
|
||||||
|
});
|
||||||
|
sticky.val(entry.sticky > 0 ? entry.sticky : '').trigger('input');
|
||||||
|
|
||||||
|
// cooldown
|
||||||
|
const cooldown = template.find('input[name="cooldown"]');
|
||||||
|
cooldown.data('uid', entry.uid);
|
||||||
|
cooldown.on('input', function () {
|
||||||
|
const uid = $(this).data('uid');
|
||||||
|
const value = Number($(this).val());
|
||||||
|
data.entries[uid].cooldown = !isNaN(value) ? value : null;
|
||||||
|
|
||||||
|
setOriginalDataValue(data, uid, 'extensions.cooldown', data.entries[uid].cooldown);
|
||||||
|
saveWorldInfo(name, data);
|
||||||
|
});
|
||||||
|
cooldown.val(entry.cooldown > 0 ? entry.cooldown : '').trigger('input');
|
||||||
|
|
||||||
// probability
|
// probability
|
||||||
if (entry.probability === undefined) {
|
if (entry.probability === undefined) {
|
||||||
entry.probability = null;
|
entry.probability = null;
|
||||||
@ -2478,6 +2530,8 @@ const newEntryTemplate = {
|
|||||||
useGroupScoring: null,
|
useGroupScoring: null,
|
||||||
automationId: '',
|
automationId: '',
|
||||||
role: 0,
|
role: 0,
|
||||||
|
sticky: null,
|
||||||
|
cooldown: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
function createWorldInfoEntry(_name, data) {
|
function createWorldInfoEntry(_name, data) {
|
||||||
@ -2508,7 +2562,7 @@ async function saveWorldInfo(name, data, immediately) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete worldInfoCache[name];
|
worldInfoCache.delete(name);
|
||||||
|
|
||||||
if (immediately) {
|
if (immediately) {
|
||||||
return await _save(name, data);
|
return await _save(name, data);
|
||||||
@ -2778,10 +2832,11 @@ export async function getSortedEntries() {
|
|||||||
* Performs a scan on the chat and returns the world info activated.
|
* Performs a scan on the chat and returns the world info activated.
|
||||||
* @param {string[]} chat The chat messages to scan.
|
* @param {string[]} chat The chat messages to scan.
|
||||||
* @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.
|
||||||
* @typedef {{ worldInfoBefore: string, worldInfoAfter: string, EMEntries: any[], WIDepthEntries: any[], allActivatedEntries: Set<any> }} WIActivated
|
* @typedef {{ worldInfoBefore: string, worldInfoAfter: string, EMEntries: any[], WIDepthEntries: any[], allActivatedEntries: Set<any> }} WIActivated
|
||||||
* @returns {Promise<WIActivated>} The world info activated.
|
* @returns {Promise<WIActivated>} The world info activated.
|
||||||
*/
|
*/
|
||||||
async function checkWorldInfo(chat, maxContext) {
|
async function checkWorldInfo(chat, maxContext, isDryRun) {
|
||||||
const context = getContext();
|
const context = getContext();
|
||||||
const buffer = new WorldInfoBuffer(chat);
|
const buffer = new WorldInfoBuffer(chat);
|
||||||
|
|
||||||
@ -2815,6 +2870,8 @@ async function checkWorldInfo(chat, maxContext) {
|
|||||||
console.debug(`Context size: ${maxContext}; WI budget: ${budget} (max% = ${world_info_budget}%, cap = ${world_info_budget_cap})`);
|
console.debug(`Context size: ${maxContext}; WI budget: ${budget} (max% = ${world_info_budget}%, cap = ${world_info_budget_cap})`);
|
||||||
const sortedEntries = await getSortedEntries();
|
const sortedEntries = await getSortedEntries();
|
||||||
|
|
||||||
|
!isDryRun && checkTimedEvents(chat, sortedEntries);
|
||||||
|
|
||||||
if (sortedEntries.length === 0) {
|
if (sortedEntries.length === 0) {
|
||||||
return { worldInfoBefore: '', worldInfoAfter: '', WIDepthEntries: [], EMEntries: [], allActivatedEntries: new Set() };
|
return { worldInfoBefore: '', worldInfoAfter: '', WIDepthEntries: [], EMEntries: [], allActivatedEntries: new Set() };
|
||||||
}
|
}
|
||||||
@ -2856,6 +2913,11 @@ async function checkWorldInfo(chat, maxContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (buffer.isExternallySuppressed(entry)) {
|
||||||
|
console.debug(`WI entry ${entry.uid} suppressed by external suppression`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (failedProbabilityChecks.has(entry)) {
|
if (failedProbabilityChecks.has(entry)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -3082,11 +3144,117 @@ async function checkWorldInfo(chat, maxContext) {
|
|||||||
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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.cleanExternalActivations();
|
!isDryRun && setTimedEvents(chat, Array.from(allActivatedEntries));
|
||||||
|
buffer.resetExternalEffects();
|
||||||
|
|
||||||
return { worldInfoBefore, worldInfoAfter, EMEntries, WIDepthEntries, allActivatedEntries };
|
return { worldInfoBefore, worldInfoAfter, EMEntries, WIDepthEntries, allActivatedEntries };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a hash for a WI entry.
|
||||||
|
* @param {object} entry WI entry
|
||||||
|
* @returns {number} String hash
|
||||||
|
*/
|
||||||
|
function getEntryHash(entry) {
|
||||||
|
if (entryHashCache.has(entry)) {
|
||||||
|
return entryHashCache.get(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hash = getStringHash(JSON.stringify(entry));
|
||||||
|
entryHashCache.set(entry, hash);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets timed effects on chat messages.
|
||||||
|
* @param {any[]} chat Array of chat messages
|
||||||
|
* @param {WIScanEntry[]} entries Array of entries to check
|
||||||
|
*/
|
||||||
|
function setTimedEvents(chat, entries) {
|
||||||
|
if (!chat_metadata.timedWorldInfo) {
|
||||||
|
chat_metadata.timedWorldInfo = {
|
||||||
|
sticky: {},
|
||||||
|
cooldown: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (!entry.sticky && !entry.cooldown) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hash = getEntryHash(entry);
|
||||||
|
|
||||||
|
if (entry.sticky && !chat_metadata.timedWorldInfo.sticky[hash]) {
|
||||||
|
const targetRelease = chat.length + entry.sticky;
|
||||||
|
chat_metadata.timedWorldInfo.sticky[hash] = chat.length;
|
||||||
|
console.log(`Adding sticky entry ${hash}: target release @ message ID ${targetRelease}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.cooldown && !chat_metadata.timedWorldInfo.cooldown[hash]) {
|
||||||
|
const targetRelease = chat.length + entry.cooldown;
|
||||||
|
chat_metadata.timedWorldInfo.cooldown[hash] = chat.length;
|
||||||
|
console.log(`Adding cooldown entry ${hash}: target release @ message ID ${targetRelease}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for timed effects on chat messages.
|
||||||
|
* @param {any[]} chat Array of chat messages
|
||||||
|
* @param {WIScanEntry[]} entries Array of entries to check
|
||||||
|
*/
|
||||||
|
function checkTimedEvents(chat, entries) {
|
||||||
|
if (!chat_metadata.timedWorldInfo) {
|
||||||
|
chat_metadata.timedWorldInfo = {
|
||||||
|
sticky: {},
|
||||||
|
cooldown: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes entries for a given type of timed event.
|
||||||
|
* @param {string} type Identifier for the type of timed event
|
||||||
|
* @param {any[]} buffer Buffer to store the entries
|
||||||
|
*/
|
||||||
|
function processEntries(type, buffer) {
|
||||||
|
for (const [hash, value] of Object.entries(chat_metadata.timedWorldInfo[type])) {
|
||||||
|
if (chat.length <= Number(value)) {
|
||||||
|
console.log(`Removing ${type} entry ${hash} from timedWorldInfo: chat not advanced`);
|
||||||
|
delete chat_metadata.timedWorldInfo[type][hash];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entry = entries.find(x => String(getEntryHash(x)) === String(hash));
|
||||||
|
|
||||||
|
// Ignore missing entries (they could be from another character's lorebook)
|
||||||
|
if (!entry) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entry[type]) {
|
||||||
|
console.log(`Removing ${type} entry ${hash} from timedWorldInfo: entry not ${type}`);
|
||||||
|
delete chat_metadata.timedWorldInfo[type][hash];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetRelease = Number(value) + Number(entry[type]);
|
||||||
|
|
||||||
|
if (chat.length > targetRelease) {
|
||||||
|
console.log(`Removing ${type} entry ${hash} from timedWorldInfo: ${type} interval passed`);
|
||||||
|
delete chat_metadata.timedWorldInfo[type][hash];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.push(entry);
|
||||||
|
console.log(`Timed effect "${type}" applied to entry`, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processEntries('sticky', WorldInfoBuffer.externalActivations);
|
||||||
|
processEntries('cooldown', WorldInfoBuffer.externalSuppressions);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only leaves entries with the highest key matching score in each group.
|
* Only leaves entries with the highest key matching score in each group.
|
||||||
* @param {Record<string, WIScanEntry[]>} groups The groups to filter
|
* @param {Record<string, WIScanEntry[]>} groups The groups to filter
|
||||||
@ -3243,6 +3411,8 @@ function convertAgnaiMemoryBook(inputObj) {
|
|||||||
useGroupScoring: null,
|
useGroupScoring: null,
|
||||||
automationId: '',
|
automationId: '',
|
||||||
role: extension_prompt_roles.SYSTEM,
|
role: extension_prompt_roles.SYSTEM,
|
||||||
|
sticky: null,
|
||||||
|
cooldown: null,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -3282,6 +3452,8 @@ function convertRisuLorebook(inputObj) {
|
|||||||
useGroupScoring: null,
|
useGroupScoring: null,
|
||||||
automationId: '',
|
automationId: '',
|
||||||
role: extension_prompt_roles.SYSTEM,
|
role: extension_prompt_roles.SYSTEM,
|
||||||
|
sticky: null,
|
||||||
|
cooldown: null,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -3326,6 +3498,8 @@ function convertNovelLorebook(inputObj) {
|
|||||||
useGroupScoring: null,
|
useGroupScoring: null,
|
||||||
automationId: '',
|
automationId: '',
|
||||||
role: extension_prompt_roles.SYSTEM,
|
role: extension_prompt_roles.SYSTEM,
|
||||||
|
sticky: null,
|
||||||
|
cooldown: null,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -3372,6 +3546,8 @@ function convertCharacterBook(characterBook) {
|
|||||||
automationId: entry.extensions?.automation_id ?? '',
|
automationId: entry.extensions?.automation_id ?? '',
|
||||||
role: entry.extensions?.role ?? extension_prompt_roles.SYSTEM,
|
role: entry.extensions?.role ?? extension_prompt_roles.SYSTEM,
|
||||||
vectorized: entry.extensions?.vectorized ?? false,
|
vectorized: entry.extensions?.vectorized ?? false,
|
||||||
|
sticky: entry.extensions?.sticky ?? null,
|
||||||
|
cooldown: entry.extensions?.cooldown ?? null,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -483,6 +483,8 @@ function convertWorldInfoToCharacterBook(name, entries) {
|
|||||||
automation_id: entry.automationId ?? '',
|
automation_id: entry.automationId ?? '',
|
||||||
role: entry.role ?? 0,
|
role: entry.role ?? 0,
|
||||||
vectorized: entry.vectorized ?? false,
|
vectorized: entry.vectorized ?? false,
|
||||||
|
sticky: entry.sticky ?? null,
|
||||||
|
cooldown: entry.cooldown ?? null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user