Merge pull request #3889 from BismuthGlass/feature/wi_global_matches World Info chat-independent data matching
Signed| @@ -124,6 +124,10 @@ | ||
| 124 | 124 | cursor: initial; |
| 125 | 125 | } |
| 126 | 126 | |
| 127 | +.world_entry .inline-drawer-header-pointer { | |
| 128 | + cursor: pointer; | |
| 129 | +} | |
| 130 | + | |
| 127 | 131 | .world_entry .killSwitch { |
| 128 | 132 | cursor: pointer; |
| 129 | 133 | } |
| @@ -6292,6 +6292,54 @@ | ||
| 6292 | 6292 | </label> |
| 6293 | 6293 | </div> |
| 6294 | 6294 | </div> |
| 6295 | + <div class="inline-drawer wide100p flexFlowColumn"> | |
| 6296 | + <div class="inline-drawer-toggle inline-drawer-header inline-drawer-header-pointer userSettingsInnerExpandable"> | |
| 6297 | + <strong data-i18n="Additional Matching Sources">Additional Matching Sources</strong> | |
| 6298 | + <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div> | |
| 6299 | + </div> | |
| 6300 | + <div class="inline-drawer-content flex-container flexFlowRow flexGap10 paddingBottom5px"> | |
| 6301 | + <small class="flex-container flex1 flexFlowColumn"> | |
| 6302 | + <label class="checkbox flex-container alignItemsCenter flexNoGap"> | |
| 6303 | + <input type="checkbox" name="matchCharacterDescription" /> | |
| 6304 | + <span data-i18n="Character Description"> | |
| 6305 | + Character Description | |
| 6306 | + </span> | |
| 6307 | + </label> | |
| 6308 | + <label class="checkbox flex-container alignItemsCenter flexNoGap"> | |
| 6309 | + <input type="checkbox" name="matchCharacterPersonality" /> | |
| 6310 | + <span data-i18n="Character Personality"> | |
| 6311 | + Character Personality | |
| 6312 | + </span> | |
| 6313 | + </label> | |
| 6314 | + <label class="checkbox flex-container alignItemsCenter flexNoGap"> | |
| 6315 | + <input type="checkbox" name="matchScenario" /> | |
| 6316 | + <span data-i18n="Scenario"> | |
| 6317 | + Scenario | |
| 6318 | + </span> | |
| 6319 | + </label> | |
| 6320 | + </small> | |
| 6321 | + <small class="flex-container flex1 flexFlowColumn"> | |
| 6322 | + <label class="checkbox flex-container alignItemsCenter flexNoGap"> | |
| 6323 | + <input type="checkbox" name="matchPersonaDescription" /> | |
| 6324 | + <span data-i18n="Persona Description"> | |
| 6325 | + Persona Description | |
| 6326 | + </span> | |
| 6327 | + </label> | |
| 6328 | + <label class="checkbox flex-container alignItemsCenter flexNoGap"> | |
| 6329 | + <input type="checkbox" name="matchCharacterDepthPrompt" /> | |
| 6330 | + <span data-i18n="Character's Note"> | |
| 6331 | + Character's Note | |
| 6332 | + </span> | |
| 6333 | + </label> | |
| 6334 | + <label class="checkbox flex-container alignItemsCenter flexNoGap"> | |
| 6335 | + <input type="checkbox" name="matchCreatorNotes" /> | |
| 6336 | + <span data-i18n="Creator's Notes"> | |
| 6337 | + Creator's Notes | |
| 6338 | + </span> | |
| 6339 | + </label> | |
| 6340 | + </small> | |
| 6341 | + </div> | |
| 6342 | + </div> | |
| 6295 | 6343 | </div> |
| 6296 | 6344 | </div> |
| 6297 | 6345 | </form> |
| @@ -2753,6 +2753,7 @@ export function substituteParams(content, _name1, _name2, _original, _group, _re | ||
| 2753 | 2753 | environment.charVersion = fields.version || ''; |
| 2754 | 2754 | environment.char_version = fields.version || ''; |
| 2755 | 2755 | environment.charDepthPrompt = fields.charDepthPrompt || ''; |
| 2756 | + environment.creatorNotes = fields.creatorNotes || ''; | |
| 2756 | 2757 | } |
| 2757 | 2758 | |
| 2758 | 2759 | // Must be substituted last so that they're replaced inside {{description}} |
| @@ -3131,6 +3132,7 @@ export function baseChatReplace(value, name1, name2) { | ||
| 3131 | 3132 | * @property {string} jailbreak Jailbreak instructions |
| 3132 | 3133 | * @property {string} version Character version |
| 3133 | 3134 | * @property {string} charDepthPrompt Character depth note |
| 3135 | + * @property {string} creatorNotes Character creator notes | |
| 3134 | 3136 | * @returns {CharacterCardFields} Character card fields |
| 3135 | 3137 | */ |
| 3136 | 3138 | export function getCharacterCardFields({ chid = null } = {}) { |
| @@ -3146,6 +3148,7 @@ export function getCharacterCardFields({ chid = null } = {}) { | ||
| 3146 | 3148 | jailbreak: '', |
| 3147 | 3149 | version: '', |
| 3148 | 3150 | charDepthPrompt: '', |
| 3151 | + creatorNotes: '', | |
| 3149 | 3152 | }; |
| 3150 | 3153 | result.persona = baseChatReplace(power_user.persona_description?.trim(), name1, name2); |
| 3151 | 3154 | |
| @@ -3164,6 +3167,7 @@ export function getCharacterCardFields({ chid = null } = {}) { | ||
| 3164 | 3167 | result.jailbreak = power_user.prefer_character_jailbreak ? baseChatReplace(character.data?.post_history_instructions?.trim(), name1, name2) : ''; |
| 3165 | 3168 | result.version = character.data?.character_version ?? ''; |
| 3166 | 3169 | result.charDepthPrompt = baseChatReplace(character.data?.extensions?.depth_prompt?.prompt?.trim(), name1, name2); |
| 3170 | + result.creatorNotes = baseChatReplace(character.data?.creator_notes?.trim(), name1, name2); | |
| 3167 | 3171 | |
| 3168 | 3172 | if (selected_group) { |
| 3169 | 3173 | const groupCards = getGroupCharacterCards(selected_group, Number(currentChid)); |
| @@ -3991,6 +3995,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 3991 | 3995 | system, |
| 3992 | 3996 | jailbreak, |
| 3993 | 3997 | charDepthPrompt, |
| 3998 | + creatorNotes, | |
| 3994 | 3999 | } = getCharacterCardFields(); |
| 3995 | 4000 | |
| 3996 | 4001 | if (main_api !== 'openai') { |
| @@ -4145,7 +4150,15 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4145 | 4150 | // Make quiet prompt available for WIAN |
| 4146 | 4151 | setExtensionPrompt('QUIET_PROMPT', quiet_prompt || '', extension_prompt_types.IN_PROMPT, 0, true); |
| 4147 | 4152 | const chatForWI = coreChat.map(x => world_info_include_names ? `${x.name}: ${x.mes}` : x.mes).reverse(); |
| 4148 | - const { worldInfoString, worldInfoBefore, worldInfoAfter, worldInfoExamples, worldInfoDepth } = await getWorldInfoPrompt(chatForWI, this_max_context, dryRun); | |
| 4153 | + const globalScanData = { | |
| 4154 | + personaDescription: persona, | |
| 4155 | + characterDescription: description, | |
| 4156 | + characterPersonality: personality, | |
| 4157 | + characterDepthPrompt: charDepthPrompt, | |
| 4158 | + scenario: scenario, | |
| 4159 | + creatorNotes: creatorNotes, | |
| 4160 | + }; | |
| 4161 | + const { worldInfoString, worldInfoBefore, worldInfoAfter, worldInfoExamples, worldInfoDepth } = await getWorldInfoPrompt(chatForWI, this_max_context, dryRun, globalScanData); | |
| 4149 | 4162 | setExtensionPrompt('QUIET_PROMPT', '', extension_prompt_types.IN_PROMPT, 0, true); |
| 4150 | 4163 | |
| 4151 | 4164 | // Add message example WI |
| @@ -33,6 +33,12 @@ | ||
| 33 | 33 | * @property {number} role - The specific function or purpose of the extension. |
| 34 | 34 | * @property {boolean} vectorized - Indicates if the extension is optimized for vectorized processing. |
| 35 | 35 | * @property {number} display_index - The order in which the extension should be displayed for user interfaces. |
| 36 | + * @property {boolean} match_persona_description - Wether to match against the persona description. | |
| 37 | + * @property {boolean} match_character_description - Wether to match against the persona description. | |
| 38 | + * @property {boolean} match_character_personality - Wether to match against the character personality. | |
| 39 | + * @property {boolean} match_character_depth_prompt - Wether to match against the character depth prompt. | |
| 40 | + * @property {boolean} match_scenario - Wether to match against the character scenario. | |
| 41 | + * @property {boolean} match_creator_notes - Wether to match against the character creator notes. | |
| 36 | 42 | */ |
| 37 | 43 | |
| 38 | 44 | /** |
| @@ -98,11 +98,28 @@ const KNOWN_DECORATORS = ['@@activate', '@@dont_activate']; | ||
| 98 | 98 | |
| 99 | 99 | // Typedef area |
| 100 | 100 | /** |
| 101 | + * @typedef {object} WIGlobalScanData The chat-independent data to be scanned. Each of | |
| 102 | + * these fields can be enabled for scanning per entry. | |
| 103 | + * @property {string} personaDescription User persona description | |
| 104 | + * @property {string} characterDescription Character description | |
| 105 | + * @property {string} characterPersonality Character personality | |
| 106 | + * @property {string} characterDepthPrompt Character depth prompt (sometimes referred to as character notes) | |
| 107 | + * @property {string} scenario Character defined scenario | |
| 108 | + * @property {string} creatorNotes Character creator notes | |
| 109 | + */ | |
| 110 | + | |
| 111 | +/** | |
| 101 | 112 | * @typedef {object} WIScanEntry The entry that triggered the scan |
| 102 | 113 | * @property {number} [scanDepth] The depth of the scan |
| 103 | 114 | * @property {boolean} [caseSensitive] If the scan is case sensitive |
| 104 | 115 | * @property {boolean} [matchWholeWords] If the scan should match whole words |
| 105 | 116 | * @property {boolean} [useGroupScoring] If the scan should use group scoring |
| 117 | + * @property {boolean} [matchPersonaDescription] If the scan should match against the persona description | |
| 118 | + * @property {boolean} [matchCharacterDescription] If the scan should match against the character description | |
| 119 | + * @property {boolean} [matchCharacterPersonality] If the scan should match against the character personality | |
| 120 | + * @property {boolean} [matchCharacterDepthPrompt] If the scan should match against the character depth prompt | |
| 121 | + * @property {boolean} [matchScenario] If the scan should match against the character scenario | |
| 122 | + * @property {boolean} [matchCreatorNotes] If the scan should match against the creator notes | |
| 106 | 123 | * @property {number} [uid] The UID of the entry that triggered the scan |
| 107 | 124 | * @property {string} [world] The world info book of origin of the entry |
| 108 | 125 | * @property {string[]} [key] The primary keys to scan for |
| @@ -139,6 +156,11 @@ class WorldInfoBuffer { | ||
| 139 | 156 | static externalActivations = new Map(); |
| 140 | 157 | |
| 141 | 158 | /** |
| 159 | + * @type {WIGlobalScanData} Chat independent data to be scanned, such as persona and character descriptions | |
| 160 | + */ | |
| 161 | + #globalScanData = null; | |
| 162 | + | |
| 163 | + /** | |
| 142 | 164 | * @type {string[]} Array of messages sorted by ascending depth |
| 143 | 165 | */ |
| 144 | 166 | #depthBuffer = []; |
| @@ -166,9 +188,11 @@ class WorldInfoBuffer { | ||
| 166 | 188 | /** |
| 167 | 189 | * Initialize the buffer with the given messages. |
| 168 | 190 | * @param {string[]} messages Array of messages to add to the buffer |
| 191 | + * @param {WIGlobalScanData} globalScanData Chat independent context to be scanned | |
| 169 | 192 | */ |
| 170 | 193 | constructor(messages, globalScanData) { |
| 171 | 194 | this.#initDepthBuffer(messages); |
| 195 | + this.#globalScanData = globalScanData; | |
| 172 | 196 | } |
| 173 | 197 | |
| 174 | 198 | /** |
| @@ -225,6 +249,25 @@ class WorldInfoBuffer { | ||
| 225 | 249 | const JOINER = '\n' + MATCHER; |
| 226 | 250 | let result = MATCHER + this.#depthBuffer.slice(this.#startDepth, depth).join(JOINER); |
| 227 | 251 | |
| 252 | + if (entry.matchPersonaDescription && this.#globalScanData.personaDescription) { | |
| 253 | + result += JOINER + this.#globalScanData.personaDescription; | |
| 254 | + } | |
| 255 | + if (entry.matchCharacterDescription && this.#globalScanData.characterDescription) { | |
| 256 | + result += JOINER + this.#globalScanData.characterDescription; | |
| 257 | + } | |
| 258 | + if (entry.matchCharacterPersonality && this.#globalScanData.characterPersonality) { | |
| 259 | + result += JOINER + this.#globalScanData.characterPersonality; | |
| 260 | + } | |
| 261 | + if (entry.matchCharacterDepthPrompt && this.#globalScanData.characterDepthPrompt) { | |
| 262 | + result += JOINER + this.#globalScanData.characterDepthPrompt; | |
| 263 | + } | |
| 264 | + if (entry.matchScenario && this.#globalScanData.scenario) { | |
| 265 | + result += JOINER + this.#globalScanData.scenario; | |
| 266 | + } | |
| 267 | + if (entry.matchCreatorNotes && this.#globalScanData.creatorNotes) { | |
| 268 | + result += JOINER + this.#globalScanData.creatorNotes; | |
| 269 | + } | |
| 270 | + | |
| 228 | 271 | if (this.#injectBuffer.length > 0) { |
| 229 | 272 | result += JOINER + this.#injectBuffer.join(JOINER); |
| 230 | 273 | } |
| @@ -756,6 +799,7 @@ export const worldInfoCache = new StructuredCloneMap({ cloneOnGet: true, cloneOn | ||
| 756 | 799 | * @param {string[]} chat - The chat messages to scan, in reverse order. |
| 757 | 800 | * @param {number} maxContext - The maximum context size of the generation. |
| 758 | 801 | * @param {boolean} isDryRun - If true, the function will not emit any events. |
| 802 | + * @param {WIGlobalScanData} globalScanData Chat independent context to be scanned | |
| 759 | 803 | * @typedef {object} WIPromptResult |
| 760 | 804 | * @property {string} worldInfoString - Complete world info string |
| 761 | 805 | * @property {string} worldInfoBefore - World info that goes before the prompt |
| @@ -766,10 +810,10 @@ export const worldInfoCache = new StructuredCloneMap({ cloneOnGet: true, cloneOn | ||
| 766 | 810 | * @property {Array} anAfter - Array of entries after Author's Note |
| 767 | 811 | * @returns {Promise<WIPromptResult>} The world info string and depth. |
| 768 | 812 | */ |
| 769 | 813 | export async function getWorldInfoPrompt(chat, maxContext, isDryRun, globalScanData) { |
| 770 | 814 | let worldInfoString = '', worldInfoBefore = '', worldInfoAfter = ''; |
| 771 | 815 | |
| 772 | 816 | const activatedWorldInfo = await checkWorldInfo(chat, maxContext, isDryRun, globalScanData); |
| 773 | 817 | worldInfoBefore = activatedWorldInfo.worldInfoBefore; |
| 774 | 818 | worldInfoAfter = activatedWorldInfo.worldInfoAfter; |
| 775 | 819 | worldInfoString = worldInfoBefore + worldInfoAfter; |
| @@ -2191,6 +2235,12 @@ export const originalWIDataKeyMap = { | ||
| 2191 | 2235 | 'matchWholeWords': 'extensions.match_whole_words', |
| 2192 | 2236 | 'useGroupScoring': 'extensions.use_group_scoring', |
| 2193 | 2237 | 'caseSensitive': 'extensions.case_sensitive', |
| 2238 | + 'matchPersonaDescription': 'extensions.match_persona_description', | |
| 2239 | + 'matchCharacterDescription': 'extensions.match_character_description', | |
| 2240 | + 'matchCharacterPersonality': 'extensions.match_character_personality', | |
| 2241 | + 'matchCharacterDepthPrompt': 'extensions.match_character_depth_prompt', | |
| 2242 | + 'matchScenario': 'extensions.match_scenario', | |
| 2243 | + 'matchCreatorNotes': 'extensions.match_creator_notes', | |
| 2194 | 2244 | 'scanDepth': 'extensions.scan_depth', |
| 2195 | 2245 | 'automationId': 'extensions.automation_id', |
| 2196 | 2246 | 'vectorized': 'extensions.vectorized', |
| @@ -3308,6 +3358,28 @@ export async function getWorldEntry(name, data, entry) { | ||
| 3308 | 3358 | }); |
| 3309 | 3359 | useGroupScoringSelect.val((entry.useGroupScoring === null || entry.useGroupScoring === undefined) ? 'null' : entry.useGroupScoring ? 'true' : 'false').trigger('input'); |
| 3310 | 3360 | |
| 3361 | + function handleMatchCheckbox(fieldName) { | |
| 3362 | + const key = originalWIDataKeyMap[fieldName]; | |
| 3363 | + const checkBoxElem = template.find(`input[type="checkbox"][name="${fieldName}"]`); | |
| 3364 | + checkBoxElem.data('uid', entry.uid); | |
| 3365 | + checkBoxElem.on('input', async function () { | |
| 3366 | + const uid = $(this).data('uid'); | |
| 3367 | + const value = $(this).prop('checked'); | |
| 3368 | + | |
| 3369 | + data.entries[uid][fieldName] = value; | |
| 3370 | + setWIOriginalDataValue(data, uid, key, data.entries[uid][fieldName]); | |
| 3371 | + await saveWorldInfo(name, data); | |
| 3372 | + }); | |
| 3373 | + checkBoxElem.prop('checked', !!entry[fieldName]).trigger('input'); | |
| 3374 | + } | |
| 3375 | + | |
| 3376 | + handleMatchCheckbox('matchPersonaDescription'); | |
| 3377 | + handleMatchCheckbox('matchCharacterDescription'); | |
| 3378 | + handleMatchCheckbox('matchCharacterPersonality'); | |
| 3379 | + handleMatchCheckbox('matchCharacterDepthPrompt'); | |
| 3380 | + handleMatchCheckbox('matchScenario'); | |
| 3381 | + handleMatchCheckbox('matchCreatorNotes'); | |
| 3382 | + | |
| 3311 | 3383 | // automation id |
| 3312 | 3384 | const automationIdInput = template.find('input[name="automationId"]'); |
| 3313 | 3385 | automationIdInput.data('uid', entry.uid); |
| @@ -3514,6 +3586,12 @@ export const newWorldInfoEntryDefinition = { | ||
| 3514 | 3586 | disable: { default: false, type: 'boolean' }, |
| 3515 | 3587 | excludeRecursion: { default: false, type: 'boolean' }, |
| 3516 | 3588 | preventRecursion: { default: false, type: 'boolean' }, |
| 3589 | + matchPersonaDescription: { default: false, type: 'boolean' }, | |
| 3590 | + matchCharacterDescription: { default: false, type: 'boolean' }, | |
| 3591 | + matchCharacterPersonality: { default: false, type: 'boolean' }, | |
| 3592 | + matchCharacterDepthPrompt: { default: false, type: 'boolean' }, | |
| 3593 | + matchScenario: { default: false, type: 'boolean' }, | |
| 3594 | + matchCreatorNotes: { default: false, type: 'boolean' }, | |
| 3517 | 3595 | delayUntilRecursion: { default: 0, type: 'number' }, |
| 3518 | 3596 | probability: { default: 100, type: 'number' }, |
| 3519 | 3597 | useProbability: { default: true, type: 'boolean' }, |
| @@ -3978,6 +4056,7 @@ function parseDecorators(content) { | ||
| 3978 | 4056 | * @param {string[]} chat The chat messages to scan, in reverse order. |
| 3979 | 4057 | * @param {number} maxContext The maximum context size of the generation. |
| 3980 | 4058 | * @param {boolean} isDryRun Whether to perform a dry run. |
| 4059 | + * @param {WIGlobalScanData} globalScanData Chat independent context to be scanned | |
| 3981 | 4060 | * @typedef {object} WIActivated |
| 3982 | 4061 | * @property {string} worldInfoBefore The world info before the chat. |
| 3983 | 4062 | * @property {string} worldInfoAfter The world info after the chat. |
| @@ -3988,9 +4067,9 @@ function parseDecorators(content) { | ||
| 3988 | 4067 | * @property {Set<any>} allActivatedEntries All entries. |
| 3989 | 4068 | * @returns {Promise<WIActivated>} The world info activated. |
| 3990 | 4069 | */ |
| 3991 | 4070 | export async function checkWorldInfo(chat, maxContext, isDryRun, globalScanData) { |
| 3992 | 4071 | const context = getContext(); |
| 3993 | 4072 | const buffer = new WorldInfoBuffer(chat, globalScanData); |
| 3994 | 4073 | |
| 3995 | 4074 | console.debug(`[WI] --- START WI SCAN (on ${chat.length} messages)${isDryRun ? ' (DRY RUN)' : ''} ---`); |
| 3996 | 4075 | |
| @@ -4848,6 +4927,12 @@ export function convertCharacterBook(characterBook) { | ||
| 4848 | 4927 | sticky: entry.extensions?.sticky ?? null, |
| 4849 | 4928 | cooldown: entry.extensions?.cooldown ?? null, |
| 4850 | 4929 | delay: entry.extensions?.delay ?? null, |
| 4930 | + matchPersonaDescription: entry.extensions?.match_persona_description ?? false, | |
| 4931 | + matchCharacterDescription: entry.extensions?.match_character_description ?? false, | |
| 4932 | + matchCharacterPersonality: entry.extensions?.match_character_personality ?? false, | |
| 4933 | + matchCharacterDepthPrompt: entry.extensions?.match_character_depth_prompt ?? false, | |
| 4934 | + matchScenario: entry.extensions?.match_scenario ?? false, | |
| 4935 | + matchCreatorNotes: entry.extensions?.match_creator_notes ?? false, | |
| 4851 | 4936 | extensions: entry.extensions ?? {}, |
| 4852 | 4937 | }; |
| 4853 | 4938 | }); |
| @@ -702,6 +702,12 @@ function convertWorldInfoToCharacterBook(name, entries) { | ||
| 702 | 702 | sticky: entry.sticky ?? null, |
| 703 | 703 | cooldown: entry.cooldown ?? null, |
| 704 | 704 | delay: entry.delay ?? null, |
| 705 | + match_persona_description: entry.matchPersonaDescription ?? false, | |
| 706 | + match_character_description: entry.matchCharacterDescription ?? false, | |
| 707 | + match_character_personality: entry.matchCharacterPersonality ?? false, | |
| 708 | + match_character_depth_prompt: entry.matchCharacterDepthPrompt ?? false, | |
| 709 | + match_scenario: entry.matchScenario ?? false, | |
| 710 | + match_creator_notes: entry.matchCreatorNotes ?? false, | |
| 705 | 711 | }, |
| 706 | 712 | }; |
| 707 | 713 | |