Merge branch 'staging' into tc-split-generic
| @@ -2876,23 +2876,54 @@ function addPersonaDescriptionExtensionPrompt() { | |||
| 2876 | } | 2876 | } |
| 2877 | } | 2877 | } |
| 2878 | 2878 | ||
| 2879 | function getAllExtensionPrompts() { | 2879 | /** |
| 2880 | const value = Object | 2880 | * 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()) | 2883 | async function getAllExtensionPrompts() { |
| 2884 | .join('\n'); | 2884 | const values = []; |
| 2885 | 2885 | ||
| 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 | } |
| 2888 | 2892 | ||
| 2889 | // Wrapper to fetch extension prompts by module name | 2893 | const hasFilter = typeof prompt.filter === 'function'; |
| 2890 | export 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 | */ | ||
| 2909 | export 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 | } |
| 2897 | 2928 | ||
| 2898 | /** | 2929 | /** |
| @@ -2903,27 +2934,36 @@ export function getExtensionPromptByName(moduleName) { | |||
| 2903 | * @param {string} [separator] Separator for joining multiple prompts | 2934 | * @param {string} [separator] Separator for joining multiple prompts |
| 2904 | * @param {number} [role] Role of the prompt | 2935 | * @param {number} [role] Role of the prompt |
| 2905 | * @param {boolean} [wrap] Wrap start and end with a separator | 2936 | * @param {boolean} [wrap] Wrap start and end with a separator |
| 2906 | * @returns {string} Extension prompt | 2937 | * @returns {Promise<string>} Extension prompt |
| 2907 | */ | 2938 | */ |
| 2908 | export function getExtensionPrompt(position = extension_prompt_types.IN_PROMPT, depth = undefined, separator = '\n', role = undefined, wrap = true) { | 2939 | export 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 | } |
| 2928 | 2968 | ||
| 2929 | export function baseChatReplace(value, name1, name2) { | 2969 | export 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 separately | 3877 | // 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 | } |
| 3842 | 3882 | ||
| 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 | } |
| 3911 | 3951 | ||
| 3912 | // Call combined AN into Generate | 3952 | // 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); |
| 3915 | 3955 | ||
| 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 messages | 4784 | * @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 injected | 4786 | * @returns {Promise<number[]>} Array of indices where the extension prompts were injected |
| 4747 | */ | 4787 | */ |
| 4748 | function doChatInject(messages, isContinue) { | 4788 | async 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; |
| 4764 | 4804 | ||
| 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 | */ |
| 7460 | export function setExtensionPrompt(key, value, position, depth, scan = false, role = extension_prompt_roles.SYSTEM) { | 7501 | export 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 | } |
| 7469 | 7511 | ||
| @@ -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 | } |
| 433 | 433 | ||
| 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); | ||
| 435 | 436 | ||
| 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) { | |||
| 463 | 464 | ||
| 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); |
| 467 | 468 | ||
| 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 | } |
| 475 | 476 | ||
| 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 placeholder | 479 | // 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'); |
| 479 | 481 | ||
| 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 | } |
| 495 | 496 | ||
| @@ -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 | */ |
| 615 | function populationInjectionPrompts(prompts, messages) { | 616 | async function populationInjectionPrompts(prompts, messages) { |
| 616 | let totalInsertedMessages = 0; | 617 | let totalInsertedMessages = 0; |
| 617 | 618 | ||
| 618 | const roleTypes = { | 619 | const roleTypes = { |
| @@ -635,7 +636,7 @@ function populationInjectionPrompts(prompts, messages) { | |||
| 635 | // Get prompts for current role | 636 | // 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 prompt | 638 | // 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); |
| 639 | 640 | ||
| 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); |
| 641 | 642 | ||
| @@ -1020,7 +1021,7 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm | |||
| 1020 | } | 1021 | } |
| 1021 | 1022 | ||
| 1022 | // Add in-chat injections | 1023 | // Add in-chat injections |
| 1023 | messages = populationInjectionPrompts(absolutePrompts, messages); | 1024 | messages = await populationInjectionPrompts(absolutePrompts, messages); |
| 1024 | 1025 | ||
| 1025 | // Decide whether dialogue examples should always be added | 1026 | // 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.systemPromptOverride | 1052 | * @param {string} options.systemPromptOverride |
| 1052 | * @param {string} options.jailbreakPromptOverride | 1053 | * @param {string} options.jailbreakPromptOverride |
| 1053 | * @param {string} options.personaDescription | 1054 | * @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 | */ |
| 1056 | function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts, systemPromptOverride, jailbreakPromptOverride, personaDescription }) { | 1057 | async 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; |
| 1144 | 1145 | ||
| 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({ | |||
| 1254 | 1258 | ||
| 1255 | try { | 1259 | try { |
| 1256 | // Merge markers and ordered user prompts with system prompts | 1260 | // 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, |
| @@ -84,6 +84,25 @@ export const parser = new SlashCommandParser(); | |||
| 84 | const registerSlashCommand = SlashCommandParser.addCommand.bind(SlashCommandParser); | 84 | const registerSlashCommand = SlashCommandParser.addCommand.bind(SlashCommandParser); |
| 85 | const getSlashCommandsHelp = parser.getHelpString.bind(parser); | 85 | const getSlashCommandsHelp = parser.getHelpString.bind(parser); |
| 86 | 86 | ||
| 87 | /** | ||
| 88 | * Converts a SlashCommandClosure to a filter function that returns a boolean. | ||
| 89 | * @param {SlashCommandClosure} closure | ||
| 90 | * @returns {() => Promise<boolean>} | ||
| 91 | */ | ||
| 92 | function 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 | |||
| 87 | export function initDefaultSlashCommands() { | 106 | export 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'; | |||
| 1901 | export const COMMENT_NAME_DEFAULT = 'Note'; | 1927 | export const COMMENT_NAME_DEFAULT = 'Note'; |
| 1902 | const SCRIPT_PROMPT_KEY = 'script_inject_'; | 1928 | const SCRIPT_PROMPT_KEY = 'script_inject_'; |
| 1903 | 1929 | ||
| 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 | */ | ||
| 1904 | function injectCallback(args, value) { | 1935 | function 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 | }; |
| 1916 | 1947 | ||
| 1917 | const id = args?.id; | 1948 | const id = String(args?.id); |
| 1918 | const ephemeral = isTrueBoolean(args?.ephemeral); | 1949 | const ephemeral = isTrueBoolean(String(args?.ephemeral)); |
| 1919 | 1950 | ||
| 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 || ''; |
| 1936 | 1969 | ||
| 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}`; |
| 1938 | 1975 | ||
| 1939 | if (!chat_metadata.script_injects) { | 1976 | if (!chat_metadata.script_injects) { |
| @@ -1941,13 +1978,13 @@ function injectCallback(args, value) { | |||
| 1941 | } | 1978 | } |
| 1942 | 1979 | ||
| 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 | } |
| 1949 | 1986 | ||
| 1950 | setExtensionPrompt(prefixedId, value, position, depth, scan, role); | 1987 | setExtensionPrompt(prefixedId, String(value), position, depth, scan, role, filterFunction); |
| 1951 | saveMetadataDebounced(); | 1988 | saveMetadataDebounced(); |
| 1952 | 1989 | ||
| 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 | } |
| 2054 | 2091 | ||
| 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 | } |
| 2061 | 2117 | ||
| @@ -63,7 +63,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from ' | |||
| 63 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; | 63 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| 64 | import { tag_map, tags } from './tags.js'; | 64 | import { tag_map, tags } from './tags.js'; |
| 65 | import { textgenerationwebui_settings } from './textgen-settings.js'; | 65 | import { textgenerationwebui_settings } from './textgen-settings.js'; |
| 66 | import { getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js'; | 66 | import { tokenizers, getTextTokens, getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js'; |
| 67 | import { ToolManager } from './tool-calling.js'; | 67 | import { ToolManager } from './tool-calling.js'; |
| 68 | import { timestampToMoment } from './utils.js'; | 68 | import { timestampToMoment } from './utils.js'; |
| 69 | 69 | ||
| @@ -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, |
| @@ -3760,7 +3760,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) { | |||
| 3760 | // Put this code here since otherwise, the chat reference is modified | 3760 | // 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 | } |