Merge branch 'staging' into tc-split-generic

756f88b5aa612f49a81a6d473cb39fe8de9d3758

Cohee <18619528+Cohee1207@users.noreply.github.com>

6 files changed, +157 -52Showing whitespace changes
public/script.js+75 -33
@@ -2876,23 +2876,54 @@ function addPersonaDescriptionExtensionPrompt() {
2876 }2876 }
2877}2877}
28782878
2879function getAllExtensionPrompts() {2879/**
2880 const value = Object2880 * Returns all extension prompts combined.
2881 .values(extension_prompts)2881 * @returns {Promise<string>} Combined extension prompts
2882 .filter(x => x.value)2882 */
2883 .map(x => x.value.trim())2883async function getAllExtensionPrompts() {
2884 .join('\n');2884 const values = [];
28852885
2886 return value.length ? substituteParams(value) : '';2886 for (const prompt of Object.values(extension_prompts)) {
2887 const value = prompt?.value?.trim();
2888
2889 if (!value) {
2890 continue;
2887 }2891 }
28882892
2889// Wrapper to fetch extension prompts by module name2893 const hasFilter = typeof prompt.filter === 'function';
2890export function getExtensionPromptByName(moduleName) {2894 if (hasFilter && !await prompt.filter()) {
2891 if (moduleName) {2895 continue;
2892 return substituteParams(extension_prompts[moduleName]?.value);
2893 } else {
2894 return;
2895 }2896 }
2897
2898 values.push(value);
2899 }
2900
2901 return substituteParams(values.join('\n'));
2902}
2903
2904/**
2905 * Wrapper to fetch extension prompts by module name
2906 * @param {string} moduleName Module name
2907 * @returns {Promise<string>} Extension prompt
2908 */
2909export async function getExtensionPromptByName(moduleName) {
2910 if (!moduleName) {
2911 return '';
2912 }
2913
2914 const prompt = extension_prompts[moduleName];
2915
2916 if (!prompt) {
2917 return '';
2918 }
2919
2920 const hasFilter = typeof prompt.filter === 'function';
2921
2922 if (hasFilter && !await prompt.filter()) {
2923 return '';
2924 }
2925
2926 return substituteParams(prompt.value);
2896}2927}
28972928
2898/**2929/**
@@ -2903,27 +2934,36 @@ export function getExtensionPromptByName(moduleName) {
2903 * @param {string} [separator] Separator for joining multiple prompts2934 * @param {string} [separator] Separator for joining multiple prompts
2904 * @param {number} [role] Role of the prompt2935 * @param {number} [role] Role of the prompt
2905 * @param {boolean} [wrap] Wrap start and end with a separator2936 * @param {boolean} [wrap] Wrap start and end with a separator
2906 * @returns {string} Extension prompt2937 * @returns {Promise<string>} Extension prompt
2907 */2938 */
2908export function getExtensionPrompt(position = extension_prompt_types.IN_PROMPT, depth = undefined, separator = '\n', role = undefined, wrap = true) {2939export async function getExtensionPrompt(position = extension_prompt_types.IN_PROMPT, depth = undefined, separator = '\n', role = undefined, wrap = true) {
2909 let extension_prompt = Object.keys(extension_prompts)2940 const filterByFunction = async (prompt) => {
2941 const hasFilter = typeof prompt.filter === 'function';
2942 if (hasFilter && !await prompt.filter()) {
2943 return false;
2944 }
2945 return true;
2946 };
2947 const promptPromises = Object.keys(extension_prompts)
2910 .sort()2948 .sort()
2911 .map((x) => extension_prompts[x])2949 .map((x) => extension_prompts[x])
2912 .filter(x => x.position == position && x.value)2950 .filter(x => x.position == position && x.value)
2913 .filter(x => depth === undefined || x.depth === undefined || x.depth === depth)2951 .filter(x => depth === undefined || x.depth === undefined || x.depth === depth)
2914 .filter(x => role === undefined || x.role === undefined || x.role === role)2952 .filter(x => role === undefined || x.role === undefined || x.role === role)
2915 .map(x => x.value.trim())2953 .filter(filterByFunction);
2916 .join(separator);2954 const prompts = await Promise.all(promptPromises);
2917 if (wrap && extension_prompt.length && !extension_prompt.startsWith(separator)) {2955
2918 extension_prompt = separator + extension_prompt;2956 let values = prompts.map(x => x.value.trim()).join(separator);
2957 if (wrap && values.length && !values.startsWith(separator)) {
2958 values = separator + values;
2919 }2959 }
2920 if (wrap && extension_prompt.length && !extension_prompt.endsWith(separator)) {2960 if (wrap && values.length && !values.endsWith(separator)) {
2921 extension_prompt = extension_prompt + separator;2961 values = values + separator;
2922 }2962 }
2923 if (extension_prompt.length) {2963 if (values.length) {
2924 extension_prompt = substituteParams(extension_prompt);2964 values = substituteParams(values);
2925 }2965 }
2926 return extension_prompt;2966 return values;
2927}2967}
29282968
2929export function baseChatReplace(value, name1, name2) {2969export function baseChatReplace(value, name1, name2) {
@@ -3837,7 +3877,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3837 // Inject all Depth prompts. Chat Completion does it separately3877 // Inject all Depth prompts. Chat Completion does it separately
3838 let injectedIndices = [];3878 let injectedIndices = [];
3839 if (main_api !== 'openai') {3879 if (main_api !== 'openai') {
3840 injectedIndices = doChatInject(coreChat, isContinue);3880 injectedIndices = await doChatInject(coreChat, isContinue);
3841 }3881 }
38423882
3843 // Insert character jailbreak as the last user message (if exists, allowed, preferred, and not using Chat Completion)3883 // Insert character jailbreak as the last user message (if exists, allowed, preferred, and not using Chat Completion)
@@ -3910,8 +3950,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3910 }3950 }
39113951
3912 // Call combined AN into Generate3952 // Call combined AN into Generate
3913 const beforeScenarioAnchor = getExtensionPrompt(extension_prompt_types.BEFORE_PROMPT).trimStart();3953 const beforeScenarioAnchor = (await getExtensionPrompt(extension_prompt_types.BEFORE_PROMPT)).trimStart();
3914 const afterScenarioAnchor = getExtensionPrompt(extension_prompt_types.IN_PROMPT);3954 const afterScenarioAnchor = await getExtensionPrompt(extension_prompt_types.IN_PROMPT);
39153955
3916 const storyStringParams = {3956 const storyStringParams = {
3917 description: description,3957 description: description,
@@ -4474,7 +4514,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4474 ...thisPromptBits[currentArrayEntry],4514 ...thisPromptBits[currentArrayEntry],
4475 rawPrompt: generate_data.prompt || generate_data.input,4515 rawPrompt: generate_data.prompt || generate_data.input,
4476 mesId: getNextMessageId(type),4516 mesId: getNextMessageId(type),
4477 allAnchors: getAllExtensionPrompts(),4517 allAnchors: await getAllExtensionPrompts(),
4478 chatInjects: injectedIndices?.map(index => arrMes[arrMes.length - index - 1])?.join('') || '',4518 chatInjects: injectedIndices?.map(index => arrMes[arrMes.length - index - 1])?.join('') || '',
4479 summarizeString: (extension_prompts['1_memory']?.value || ''),4519 summarizeString: (extension_prompts['1_memory']?.value || ''),
4480 authorsNoteString: (extension_prompts['2_floating_prompt']?.value || ''),4520 authorsNoteString: (extension_prompts['2_floating_prompt']?.value || ''),
@@ -4743,9 +4783,9 @@ export function stopGeneration() {
4743 * Injects extension prompts into chat messages.4783 * Injects extension prompts into chat messages.
4744 * @param {object[]} messages Array of chat messages4784 * @param {object[]} messages Array of chat messages
4745 * @param {boolean} isContinue Whether the generation is a continuation. If true, the extension prompts of depth 0 are injected at position 1.4785 * @param {boolean} isContinue Whether the generation is a continuation. If true, the extension prompts of depth 0 are injected at position 1.
4746 * @returns {number[]} Array of indices where the extension prompts were injected4786 * @returns {Promise<number[]>} Array of indices where the extension prompts were injected
4747 */4787 */
4748function doChatInject(messages, isContinue) {4788async function doChatInject(messages, isContinue) {
4749 const injectedIndices = [];4789 const injectedIndices = [];
4750 let totalInsertedMessages = 0;4790 let totalInsertedMessages = 0;
4751 messages.reverse();4791 messages.reverse();
@@ -4763,7 +4803,7 @@ function doChatInject(messages, isContinue) {
4763 const wrap = false;4803 const wrap = false;
47644804
4765 for (const role of roles) {4805 for (const role of roles) {
4766 const extensionPrompt = String(getExtensionPrompt(extension_prompt_types.IN_CHAT, i, separator, role, wrap)).trimStart();4806 const extensionPrompt = String(await getExtensionPrompt(extension_prompt_types.IN_CHAT, i, separator, role, wrap)).trimStart();
4767 const isNarrator = role === extension_prompt_roles.SYSTEM;4807 const isNarrator = role === extension_prompt_roles.SYSTEM;
4768 const isUser = role === extension_prompt_roles.USER;4808 const isUser = role === extension_prompt_roles.USER;
4769 const name = names[role];4809 const name = names[role];
@@ -7456,14 +7496,16 @@ function select_rm_characters() {
7456 * @param {number} depth Insertion depth. 0 represets the last message in context. Expected values up to MAX_INJECTION_DEPTH.7496 * @param {number} depth Insertion depth. 0 represets the last message in context. Expected values up to MAX_INJECTION_DEPTH.
7457 * @param {number} role Extension prompt role. Defaults to SYSTEM.7497 * @param {number} role Extension prompt role. Defaults to SYSTEM.
7458 * @param {boolean} scan Should the prompt be included in the world info scan.7498 * @param {boolean} scan Should the prompt be included in the world info scan.
7499 * @param {(function(): Promise<boolean>|boolean)} filter Filter function to determine if the prompt should be injected.
7459 */7500 */
7460export function setExtensionPrompt(key, value, position, depth, scan = false, role = extension_prompt_roles.SYSTEM) {7501export function setExtensionPrompt(key, value, position, depth, scan = false, role = extension_prompt_roles.SYSTEM, filter = null) {
7461 extension_prompts[key] = {7502 extension_prompts[key] = {
7462 value: String(value),7503 value: String(value),
7463 position: Number(position),7504 position: Number(position),
7464 depth: Number(depth),7505 depth: Number(depth),
7465 scan: !!scan,7506 scan: !!scan,
7466 role: Number(role ?? extension_prompt_roles.SYSTEM),7507 role: Number(role ?? extension_prompt_roles.SYSTEM),
7508 filter: filter,
7467 };7509 };
7468}7510}
74697511
public/scripts/instruct-mode.js+5 -4
@@ -431,7 +431,8 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
431 return mesExamplesArray.map(x => x.replace(/<START>\n/i, blockHeading));431 return mesExamplesArray.map(x => x.replace(/<START>\n/i, blockHeading));
432 }432 }
433433
434 const includeNames = power_user.instruct.names_behavior === names_behavior_types.ALWAYS || (!!selected_group && power_user.instruct.names_behavior === names_behavior_types.FORCE);434 const includeNames = power_user.instruct.names_behavior === names_behavior_types.ALWAYS;
435 const includeGroupNames = selected_group && [names_behavior_types.ALWAYS, names_behavior_types.FORCE].includes(power_user.instruct.names_behavior);
435436
436 let inputPrefix = power_user.instruct.input_sequence || '';437 let inputPrefix = power_user.instruct.input_sequence || '';
437 let outputPrefix = power_user.instruct.output_sequence || '';438 let outputPrefix = power_user.instruct.output_sequence || '';
@@ -463,7 +464,7 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
463464
464 for (const item of mesExamplesArray) {465 for (const item of mesExamplesArray) {
465 const cleanedItem = item.replace(/<START>/i, '{Example Dialogue:}').replace(/\r/gm, '');466 const cleanedItem = item.replace(/<START>/i, '{Example Dialogue:}').replace(/\r/gm, '');
466 const blockExamples = parseExampleIntoIndividual(cleanedItem);467 const blockExamples = parseExampleIntoIndividual(cleanedItem, includeGroupNames);
467468
468 if (blockExamples.length === 0) {469 if (blockExamples.length === 0) {
469 continue;470 continue;
@@ -474,8 +475,9 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
474 }475 }
475476
476 for (const example of blockExamples) {477 for (const example of blockExamples) {
478 // If group names were already included, we don't want to add an additional prefix
477 // If force group/persona names is set, we should override the include names for the user placeholder479 // If force group/persona names is set, we should override the include names for the user placeholder
478 const includeThisName = includeNames || (power_user.instruct.names_behavior === names_behavior_types.FORCE && example.name == 'example_user');480 const includeThisName = (includeNames && !includeGroupNames) || (power_user.instruct.names_behavior === names_behavior_types.FORCE && example.name == 'example_user');
479481
480 const prefix = example.name == 'example_user' ? inputPrefix : outputPrefix;482 const prefix = example.name == 'example_user' ? inputPrefix : outputPrefix;
481 const suffix = example.name == 'example_user' ? inputSuffix : outputSuffix;483 const suffix = example.name == 'example_user' ? inputSuffix : outputSuffix;
@@ -489,7 +491,6 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
489 if (formattedExamples.length === 0) {491 if (formattedExamples.length === 0) {
490 return mesExamplesArray.map(x => x.replace(/<START>\n/i, blockHeading));492 return mesExamplesArray.map(x => x.replace(/<START>\n/i, blockHeading));
491 }493 }
492
493 return formattedExamples;494 return formattedExamples;
494}495}
495496
public/scripts/openai.js+10 -6
@@ -611,8 +611,9 @@ function formatWorldInfo(value) {
611 *611 *
612 * @param {Prompt[]} prompts - Array containing injection prompts.612 * @param {Prompt[]} prompts - Array containing injection prompts.
613 * @param {Object[]} messages - Array containing all messages.613 * @param {Object[]} messages - Array containing all messages.
614 * @returns {Promise<Object[]>} - Array containing all messages with injections.
614 */615 */
615function populationInjectionPrompts(prompts, messages) {616async function populationInjectionPrompts(prompts, messages) {
616 let totalInsertedMessages = 0;617 let totalInsertedMessages = 0;
617618
618 const roleTypes = {619 const roleTypes = {
@@ -635,7 +636,7 @@ function populationInjectionPrompts(prompts, messages) {
635 // Get prompts for current role636 // Get prompts for current role
636 const rolePrompts = depthPrompts.filter(prompt => prompt.role === role).map(x => x.content).join(separator);637 const rolePrompts = depthPrompts.filter(prompt => prompt.role === role).map(x => x.content).join(separator);
637 // Get extension prompt638 // Get extension prompt
638 const extensionPrompt = getExtensionPrompt(extension_prompt_types.IN_CHAT, i, separator, roleTypes[role], wrap);639 const extensionPrompt = await getExtensionPrompt(extension_prompt_types.IN_CHAT, i, separator, roleTypes[role], wrap);
639640
640 const jointPrompt = [rolePrompts, extensionPrompt].filter(x => x).map(x => x.trim()).join(separator);641 const jointPrompt = [rolePrompts, extensionPrompt].filter(x => x).map(x => x.trim()).join(separator);
641642
@@ -1020,7 +1021,7 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm
1020 }1021 }
10211022
1022 // Add in-chat injections1023 // Add in-chat injections
1023 messages = populationInjectionPrompts(absolutePrompts, messages);1024 messages = await populationInjectionPrompts(absolutePrompts, messages);
10241025
1025 // Decide whether dialogue examples should always be added1026 // Decide whether dialogue examples should always be added
1026 if (power_user.pin_examples) {1027 if (power_user.pin_examples) {
@@ -1051,9 +1052,9 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm
1051 * @param {string} options.systemPromptOverride1052 * @param {string} options.systemPromptOverride
1052 * @param {string} options.jailbreakPromptOverride1053 * @param {string} options.jailbreakPromptOverride
1053 * @param {string} options.personaDescription1054 * @param {string} options.personaDescription
1054 * @returns {Object} prompts - The prepared and merged system and user-defined prompts.1055 * @returns {Promise<Object>} prompts - The prepared and merged system and user-defined prompts.
1055 */1056 */
1056function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts, systemPromptOverride, jailbreakPromptOverride, personaDescription }) {1057async function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts, systemPromptOverride, jailbreakPromptOverride, personaDescription }) {
1057 const scenarioText = Scenario && oai_settings.scenario_format ? substituteParams(oai_settings.scenario_format) : '';1058 const scenarioText = Scenario && oai_settings.scenario_format ? substituteParams(oai_settings.scenario_format) : '';
1058 const charPersonalityText = charPersonality && oai_settings.personality_format ? substituteParams(oai_settings.personality_format) : '';1059 const charPersonalityText = charPersonality && oai_settings.personality_format ? substituteParams(oai_settings.personality_format) : '';
1059 const groupNudge = substituteParams(oai_settings.group_nudge_prompt);1060 const groupNudge = substituteParams(oai_settings.group_nudge_prompt);
@@ -1142,6 +1143,9 @@ function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, wor
1142 if (!extensionPrompts[key].value) continue;1143 if (!extensionPrompts[key].value) continue;
1143 if (![extension_prompt_types.BEFORE_PROMPT, extension_prompt_types.IN_PROMPT].includes(prompt.position)) continue;1144 if (![extension_prompt_types.BEFORE_PROMPT, extension_prompt_types.IN_PROMPT].includes(prompt.position)) continue;
11441145
1146 const hasFilter = typeof prompt.filter === 'function';
1147 if (hasFilter && !await prompt.filter()) continue;
1148
1145 systemPrompts.push({1149 systemPrompts.push({
1146 identifier: key.replace(/\W/g, '_'),1150 identifier: key.replace(/\W/g, '_'),
1147 position: getPromptPosition(prompt.position),1151 position: getPromptPosition(prompt.position),
@@ -1254,7 +1258,7 @@ export async function prepareOpenAIMessages({
12541258
1255 try {1259 try {
1256 // Merge markers and ordered user prompts with system prompts1260 // Merge markers and ordered user prompts with system prompts
1257 const prompts = preparePromptsForChatCompletion({1261 const prompts = await preparePromptsForChatCompletion({
1258 Scenario,1262 Scenario,
1259 charPersonality,1263 charPersonality,
1260 name2,1264 name2,
public/scripts/slash-commands.js+63 -7
@@ -84,6 +84,25 @@ export const parser = new SlashCommandParser();
84const registerSlashCommand = SlashCommandParser.addCommand.bind(SlashCommandParser);84const registerSlashCommand = SlashCommandParser.addCommand.bind(SlashCommandParser);
85const getSlashCommandsHelp = parser.getHelpString.bind(parser);85const getSlashCommandsHelp = parser.getHelpString.bind(parser);
8686
87/**
88 * Converts a SlashCommandClosure to a filter function that returns a boolean.
89 * @param {SlashCommandClosure} closure
90 * @returns {() => Promise<boolean>}
91 */
92function closureToFilter(closure) {
93 return async () => {
94 try {
95 const localClosure = closure.getCopy();
96 localClosure.onProgress = () => { };
97 const result = await localClosure.execute();
98 return isTrueBoolean(result.pipe);
99 } catch (e) {
100 console.error('Error executing filter closure', e);
101 return false;
102 }
103 };
104}
105
87export function initDefaultSlashCommands() {106export function initDefaultSlashCommands() {
88 SlashCommandParser.addCommandObject(SlashCommand.fromProps({107 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
89 name: '?',108 name: '?',
@@ -1611,6 +1630,13 @@ export function initDefaultSlashCommands() {
1611 new SlashCommandNamedArgument(1630 new SlashCommandNamedArgument(
1612 'ephemeral', 'remove injection after generation', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',1631 'ephemeral', 'remove injection after generation', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',
1613 ),1632 ),
1633 SlashCommandNamedArgument.fromProps({
1634 name: 'filter',
1635 description: 'if a filter is defined, an injection will only be performed if the closure returns true',
1636 typeList: [ARGUMENT_TYPE.CLOSURE],
1637 isRequired: false,
1638 acceptsMultiple: false,
1639 }),
1614 ],1640 ],
1615 unnamedArgumentList: [1641 unnamedArgumentList: [
1616 new SlashCommandArgument(1642 new SlashCommandArgument(
@@ -1901,6 +1927,11 @@ const NARRATOR_NAME_DEFAULT = 'System';
1901export const COMMENT_NAME_DEFAULT = 'Note';1927export const COMMENT_NAME_DEFAULT = 'Note';
1902const SCRIPT_PROMPT_KEY = 'script_inject_';1928const SCRIPT_PROMPT_KEY = 'script_inject_';
19031929
1930/**
1931 * Adds a new script injection to the chat.
1932 * @param {import('./slash-commands/SlashCommand.js').NamedArguments} args Named arguments
1933 * @param {import('./slash-commands/SlashCommand.js').UnnamedArguments} value Unnamed argument
1934 */
1904function injectCallback(args, value) {1935function injectCallback(args, value) {
1905 const positions = {1936 const positions = {
1906 'before': extension_prompt_types.BEFORE_PROMPT,1937 'before': extension_prompt_types.BEFORE_PROMPT,
@@ -1914,8 +1945,8 @@ function injectCallback(args, value) {
1914 'assistant': extension_prompt_roles.ASSISTANT,1945 'assistant': extension_prompt_roles.ASSISTANT,
1915 };1946 };
19161947
1917 const id = args?.id;1948 const id = String(args?.id);
1918 const ephemeral = isTrueBoolean(args?.ephemeral);1949 const ephemeral = isTrueBoolean(String(args?.ephemeral));
19191950
1920 if (!id) {1951 if (!id) {
1921 console.warn('WARN: No ID provided for /inject command');1952 console.warn('WARN: No ID provided for /inject command');
@@ -1931,9 +1962,15 @@ function injectCallback(args, value) {
1931 const depth = isNaN(depthValue) ? defaultDepth : depthValue;1962 const depth = isNaN(depthValue) ? defaultDepth : depthValue;
1932 const roleValue = typeof args?.role === 'string' ? args.role.toLowerCase().trim() : Number(args?.role ?? extension_prompt_roles.SYSTEM);1963 const roleValue = typeof args?.role === 'string' ? args.role.toLowerCase().trim() : Number(args?.role ?? extension_prompt_roles.SYSTEM);
1933 const role = roles[roleValue] ?? roles[extension_prompt_roles.SYSTEM];1964 const role = roles[roleValue] ?? roles[extension_prompt_roles.SYSTEM];
1934 const scan = isTrueBoolean(args?.scan);1965 const scan = isTrueBoolean(String(args?.scan));
1966 const filter = args?.filter instanceof SlashCommandClosure ? args.filter.rawText : null;
1967 const filterFunction = args?.filter instanceof SlashCommandClosure ? closureToFilter(args.filter) : null;
1935 value = value || '';1968 value = value || '';
19361969
1970 if (args?.filter && !String(filter ?? '').trim()) {
1971 throw new Error('Failed to parse the filter argument. Make sure it is a valid non-empty closure.');
1972 }
1973
1937 const prefixedId = `${SCRIPT_PROMPT_KEY}${id}`;1974 const prefixedId = `${SCRIPT_PROMPT_KEY}${id}`;
19381975
1939 if (!chat_metadata.script_injects) {1976 if (!chat_metadata.script_injects) {
@@ -1941,13 +1978,13 @@ function injectCallback(args, value) {
1941 }1978 }
19421979
1943 if (value) {1980 if (value) {
1944 const inject = { value, position, depth, scan, role };1981 const inject = { value, position, depth, scan, role, filter };
1945 chat_metadata.script_injects[id] = inject;1982 chat_metadata.script_injects[id] = inject;
1946 } else {1983 } else {
1947 delete chat_metadata.script_injects[id];1984 delete chat_metadata.script_injects[id];
1948 }1985 }
19491986
1950 setExtensionPrompt(prefixedId, value, position, depth, scan, role);1987 setExtensionPrompt(prefixedId, String(value), position, depth, scan, role, filterFunction);
1951 saveMetadataDebounced();1988 saveMetadataDebounced();
19521989
1953 if (ephemeral) {1990 if (ephemeral) {
@@ -1958,7 +1995,7 @@ function injectCallback(args, value) {
1958 }1995 }
1959 console.log('Removing ephemeral script injection', id);1996 console.log('Removing ephemeral script injection', id);
1960 delete chat_metadata.script_injects[id];1997 delete chat_metadata.script_injects[id];
1961 setExtensionPrompt(prefixedId, '', position, depth, scan, role);1998 setExtensionPrompt(prefixedId, '', position, depth, scan, role, filterFunction);
1962 saveMetadataDebounced();1999 saveMetadataDebounced();
1963 deleted = true;2000 deleted = true;
1964 };2001 };
@@ -2053,9 +2090,28 @@ export function processChatSlashCommands() {
2053 }2090 }
20542091
2055 for (const [id, inject] of Object.entries(context.chatMetadata.script_injects)) {2092 for (const [id, inject] of Object.entries(context.chatMetadata.script_injects)) {
2093 /**
2094 * Rehydrates a filter closure from a string.
2095 * @returns {SlashCommandClosure | null}
2096 */
2097 function reviveFilterClosure() {
2098 if (!inject.filter) {
2099 return null;
2100 }
2101
2102 try {
2103 return new SlashCommandParser().parse(inject.filter, true);
2104 } catch (error) {
2105 console.warn('Failed to revive filter closure for script injection', id, error);
2106 return null;
2107 }
2108 }
2109
2056 const prefixedId = `${SCRIPT_PROMPT_KEY}${id}`;2110 const prefixedId = `${SCRIPT_PROMPT_KEY}${id}`;
2111 const filterClosure = reviveFilterClosure();
2112 const filter = filterClosure ? closureToFilter(filterClosure) : null;
2057 console.log('Adding script injection', id);2113 console.log('Adding script injection', id);
2058 setExtensionPrompt(prefixedId, inject.value, inject.position, inject.depth, inject.scan, inject.role);2114 setExtensionPrompt(prefixedId, inject.value, inject.position, inject.depth, inject.scan, inject.role, filter);
2059 }2115 }
2060}2116}
20612117
public/scripts/st-context.js+3 -1
@@ -63,7 +63,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '
63import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';63import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
64import { tag_map, tags } from './tags.js';64import { tag_map, tags } from './tags.js';
65import { textgenerationwebui_settings } from './textgen-settings.js';65import { textgenerationwebui_settings } from './textgen-settings.js';
66import { getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js';66import { tokenizers, getTextTokens, getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js';
67import { ToolManager } from './tool-calling.js';67import { ToolManager } from './tool-calling.js';
68import { timestampToMoment } from './utils.js';68import { timestampToMoment } from './utils.js';
6969
@@ -95,6 +95,8 @@ export function getContext() {
95 sendStreamingRequest,95 sendStreamingRequest,
96 sendGenerationRequest,96 sendGenerationRequest,
97 stopGeneration,97 stopGeneration,
98 tokenizers,
99 getTextTokens,
98 /** @deprecated Use getTokenCountAsync instead */100 /** @deprecated Use getTokenCountAsync instead */
99 getTokenCount,101 getTokenCount,
100 getTokenCountAsync,102 getTokenCountAsync,
public/scripts/world-info.js+1 -1
@@ -3760,7 +3760,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) {
3760 // Put this code here since otherwise, the chat reference is modified3760 // Put this code here since otherwise, the chat reference is modified
3761 for (const key of Object.keys(context.extensionPrompts)) {3761 for (const key of Object.keys(context.extensionPrompts)) {
3762 if (context.extensionPrompts[key]?.scan) {3762 if (context.extensionPrompts[key]?.scan) {
3763 const prompt = getExtensionPromptByName(key);3763 const prompt = await getExtensionPromptByName(key);
3764 if (prompt) {3764 if (prompt) {
3765 buffer.addInject(prompt);3765 buffer.addInject(prompt);
3766 }3766 }