World Info: Add example messages insertion point

Allow insertion above and below mesExamples (also known as the
"examples of dialogue") box.

Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
kingbri 2024-05-07 21:55:26 -04:00
parent 4a70e68c22
commit 01aacb9280
3 changed files with 81 additions and 30 deletions

View File

@ -5064,6 +5064,12 @@
<option value="1" data-role="" data-i18n="After Char Defs">
↓Char
</option>
<option value="5" data-role="" data-i18n="After AN">
↑EM
</option>
<option value="6" data-role="" data-i18n="After AN">
↓EM
</option>
<option value="2" data-role="" data-i18n="Before AN">
↑AN
</option>

View File

@ -34,6 +34,7 @@ import {
checkEmbeddedWorld,
setWorldInfoButtonClass,
importWorldInfo,
wi_anchor_position,
} from './scripts/world-info.js';
import {
@ -3223,32 +3224,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
setExtensionPrompt('DEPTH_PROMPT', depthPromptText, extension_prompt_types.IN_CHAT, depthPromptDepth, extension_settings.note.allowWIScan, depthPromptRole);
}
// Parse example messages
if (!mesExamples.startsWith('<START>')) {
mesExamples = '<START>\n' + mesExamples.trim();
}
if (mesExamples.replace(/<START>/gi, '').trim().length === 0) {
mesExamples = '';
}
const mesExamplesRaw = mesExamples;
/**
* Adds a block heading to the examples string.
* @param {string} examplesStr
* @returns {string[]} Examples array with block heading
*/
function addBlockHeading(examplesStr) {
const exampleSeparator = power_user.context.example_separator ? `${substituteParams(power_user.context.example_separator)}\n` : '';
const blockHeading = main_api === 'openai' ? '<START>\n' : (exampleSeparator || (isInstruct ? '<START>\n' : ''));
return examplesStr.split(/<START>/gi).slice(1).map(block => `${blockHeading}${block.trim()}\n`);
}
let mesExamplesArray = addBlockHeading(mesExamples);
let mesExamplesRawArray = addBlockHeading(mesExamplesRaw);
if (mesExamplesArray && isInstruct) {
mesExamplesArray = formatInstructModeExamples(mesExamplesArray, name1, name2);
}
// First message in fresh 1-on-1 chat reacts to user/character settings changes
if (chat.length) {
chat[0].mes = substituteParams(chat[0].mes);
@ -3317,6 +3292,30 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
force_name2 = false;
}
// TODO (kingbri): Migrate to a utility function
/**
* Parses an examples string.
* @param {string} examplesStr
* @returns {string[]} Examples array with block heading
*/
function parseMesExamples(examplesStr) {
if (examplesStr.length === 0) {
return [];
}
if (!examplesStr.startsWith('<START>')) {
examplesStr = '<START>\n' + examplesStr.trim();
}
const exampleSeparator = power_user.context.example_separator ? `${substituteParams(power_user.context.example_separator)}\n` : '';
const blockHeading = main_api === 'openai' ? '<START>\n' : (exampleSeparator || (isInstruct ? '<START>\n' : ''));
const splitExamples = examplesStr.split(/<START>/gi).slice(1).map(block => `${blockHeading}${block.trim()}\n`);
return splitExamples;
}
let mesExamplesArray = parseMesExamples(mesExamples);
//////////////////////////////////
// Extension added strings
// Set non-WI AN
@ -3326,9 +3325,35 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
// Make quiet prompt available for WIAN
setExtensionPrompt('QUIET_PROMPT', quiet_prompt || '', extension_prompt_types.IN_PROMPT, 0, true);
const chatForWI = coreChat.map(x => `${x.name}: ${x.mes}`).reverse();
let { worldInfoString, worldInfoBefore, worldInfoAfter, worldInfoDepth } = await getWorldInfoPrompt(chatForWI, this_max_context, dryRun);
let { worldInfoString, worldInfoBefore, worldInfoAfter, worldInfoExamples, worldInfoDepth } = await getWorldInfoPrompt(chatForWI, this_max_context, dryRun);
setExtensionPrompt('QUIET_PROMPT', '', extension_prompt_types.IN_PROMPT, 0, true);
// Add message example WI
for (const example of worldInfoExamples) {
let exampleMessage = example.content;
if (exampleMessage.length === 0) {
continue;
}
let formattedExample = baseChatReplace(exampleMessage, name1, name2)
const cleanedExample = parseMesExamples(formattedExample);
// Insert depending on before or after position
if (example.position === wi_anchor_position.before) {
mesExamplesArray.unshift(...cleanedExample);
} else {
mesExamplesArray.push(...cleanedExample);
}
}
// At this point, the raw message examples can be created
const mesExamplesRawArray = [...mesExamplesArray]
if (mesExamplesArray && isInstruct) {
mesExamplesArray = formatInstructModeExamples(mesExamplesArray, name1, name2);
}
if (skipWIAN !== true) {
console.log('skipWIAN not active, adding WIAN');
// Add all depth WI entries to prompt

View File

@ -333,8 +333,15 @@ const world_info_position = {
ANTop: 2,
ANBottom: 3,
atDepth: 4,
EMTop: 5,
EMBottom: 6,
};
export const wi_anchor_position = {
before: 0,
after: 1,
}
const worldInfoCache = {};
/**
@ -342,7 +349,7 @@ const worldInfoCache = {};
* @param {string[]} chat The chat messages to scan.
* @param {number} maxContext The maximum context size of the generation.
* @param {boolean} isDryRun If true, the function will not emit any events.
* @typedef {{worldInfoString: string, worldInfoBefore: string, worldInfoAfter: string, worldInfoDepth: any[]}} WIPromptResult
* @typedef {{worldInfoString: string, worldInfoBefore: string, worldInfoAfter: string, worldInfoExamples: any[], worldInfoDepth: any[]}} WIPromptResult
* @returns {Promise<WIPromptResult>} The world info string and depth.
*/
async function getWorldInfoPrompt(chat, maxContext, isDryRun) {
@ -362,6 +369,7 @@ async function getWorldInfoPrompt(chat, maxContext, isDryRun) {
worldInfoString,
worldInfoBefore,
worldInfoAfter,
worldInfoExamples: activatedWorldInfo.EMEntries,
worldInfoDepth: activatedWorldInfo.WIDepthEntries,
};
}
@ -2277,7 +2285,7 @@ export async function getSortedEntries() {
* Performs a scan on the chat and returns the world info activated.
* @param {string[]} chat The chat messages to scan.
* @param {number} maxContext The maximum context size of the generation.
* @typedef {{ worldInfoBefore: string, worldInfoAfter: string, 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.
*/
async function checkWorldInfo(chat, maxContext) {
@ -2514,11 +2522,13 @@ async function checkWorldInfo(chat, maxContext) {
// Forward-sorted list of entries for joining
const WIBeforeEntries = [];
const WIAfterEntries = [];
const EMEntries = [];
const ANTopEntries = [];
const ANBottomEntries = [];
const WIDepthEntries = [];
// Appends from insertion order 999 to 1. Use unshift for this purpose
// TODO (kingbri): Change to use WI Anchor positioning instead of separate top/bottom arrays
[...allActivatedEntries].sort(sortFn).forEach((entry) => {
switch (entry.position) {
case world_info_position.before:
@ -2527,6 +2537,16 @@ async function checkWorldInfo(chat, maxContext) {
case world_info_position.after:
WIAfterEntries.unshift(substituteParams(entry.content));
break;
case world_info_position.EMTop:
EMEntries.unshift(
{position: wi_anchor_position.before, content: entry.content}
);
break;
case world_info_position.EMBottom:
EMEntries.unshift(
{position: wi_anchor_position.after, content: entry.content}
);
break;
case world_info_position.ANTop:
ANTopEntries.unshift(entry.content);
break;
@ -2562,7 +2582,7 @@ async function checkWorldInfo(chat, maxContext) {
buffer.cleanExternalActivations();
return { worldInfoBefore, worldInfoAfter, WIDepthEntries, allActivatedEntries };
return { worldInfoBefore, worldInfoAfter, EMEntries, WIDepthEntries, allActivatedEntries };
}
/**