Add filter arg for inject command

205f1d7adb0d86b4510e0be8fe6163b70296ebfa

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

1 files changed, +54 -7Ignore whitespace
public/scripts/slash-commands.js+54 -7
@@ -84,6 +84,20 @@ 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 const localClosure = closure.getCopy();
95 localClosure.onProgress = () => { };
96 const result = await localClosure.execute();
97 return isTrueBoolean(result.pipe);
98 };
99}
100
87export function initDefaultSlashCommands() {101export function initDefaultSlashCommands() {
88 SlashCommandParser.addCommandObject(SlashCommand.fromProps({102 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
89 name: '?',103 name: '?',
@@ -1611,6 +1625,13 @@ export function initDefaultSlashCommands() {
1611 new SlashCommandNamedArgument(1625 new SlashCommandNamedArgument(
1612 'ephemeral', 'remove injection after generation', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',1626 'ephemeral', 'remove injection after generation', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',
1613 ),1627 ),
1628 SlashCommandNamedArgument.fromProps({
1629 name: 'filter',
1630 description: 'if a filter is defined, an injection will only be performed if the closure returns true',
1631 typeList: [ARGUMENT_TYPE.CLOSURE],
1632 isRequired: false,
1633 acceptsMultiple: false,
1634 }),
1614 ],1635 ],
1615 unnamedArgumentList: [1636 unnamedArgumentList: [
1616 new SlashCommandArgument(1637 new SlashCommandArgument(
@@ -1901,6 +1922,11 @@ const NARRATOR_NAME_DEFAULT = 'System';
1901export const COMMENT_NAME_DEFAULT = 'Note';1922export const COMMENT_NAME_DEFAULT = 'Note';
1902const SCRIPT_PROMPT_KEY = 'script_inject_';1923const SCRIPT_PROMPT_KEY = 'script_inject_';
19031924
1925/**
1926 * Adds a new script injection to the chat.
1927 * @param {import('./slash-commands/SlashCommand.js').NamedArguments} args Named arguments
1928 * @param {import('./slash-commands/SlashCommand.js').UnnamedArguments} value Unnamed argument
1929 */
1904function injectCallback(args, value) {1930function injectCallback(args, value) {
1905 const positions = {1931 const positions = {
1906 'before': extension_prompt_types.BEFORE_PROMPT,1932 'before': extension_prompt_types.BEFORE_PROMPT,
@@ -1914,8 +1940,8 @@ function injectCallback(args, value) {
1914 'assistant': extension_prompt_roles.ASSISTANT,1940 'assistant': extension_prompt_roles.ASSISTANT,
1915 };1941 };
19161942
1917 const id = args?.id;1943 const id = String(args?.id);
1918 const ephemeral = isTrueBoolean(args?.ephemeral);1944 const ephemeral = isTrueBoolean(String(args?.ephemeral));
19191945
1920 if (!id) {1946 if (!id) {
1921 console.warn('WARN: No ID provided for /inject command');1947 console.warn('WARN: No ID provided for /inject command');
@@ -1931,7 +1957,9 @@ function injectCallback(args, value) {
1931 const depth = isNaN(depthValue) ? defaultDepth : depthValue;1957 const depth = isNaN(depthValue) ? defaultDepth : depthValue;
1932 const roleValue = typeof args?.role === 'string' ? args.role.toLowerCase().trim() : Number(args?.role ?? extension_prompt_roles.SYSTEM);1958 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];1959 const role = roles[roleValue] ?? roles[extension_prompt_roles.SYSTEM];
1934 const scan = isTrueBoolean(args?.scan);1960 const scan = isTrueBoolean(String(args?.scan));
1961 const filter = args?.filter instanceof SlashCommandClosure ? args.filter.rawText : null;
1962 const filterFunction = args?.filter instanceof SlashCommandClosure ? closureToFilter(args.filter) : null;
1935 value = value || '';1963 value = value || '';
19361964
1937 const prefixedId = `${SCRIPT_PROMPT_KEY}${id}`;1965 const prefixedId = `${SCRIPT_PROMPT_KEY}${id}`;
@@ -1941,13 +1969,13 @@ function injectCallback(args, value) {
1941 }1969 }
19421970
1943 if (value) {1971 if (value) {
1944 const inject = { value, position, depth, scan, role };1972 const inject = { value, position, depth, scan, role, filter };
1945 chat_metadata.script_injects[id] = inject;1973 chat_metadata.script_injects[id] = inject;
1946 } else {1974 } else {
1947 delete chat_metadata.script_injects[id];1975 delete chat_metadata.script_injects[id];
1948 }1976 }
19491977
1950 setExtensionPrompt(prefixedId, value, position, depth, scan, role);1978 setExtensionPrompt(prefixedId, String(value), position, depth, scan, role, filterFunction);
1951 saveMetadataDebounced();1979 saveMetadataDebounced();
19521980
1953 if (ephemeral) {1981 if (ephemeral) {
@@ -1958,7 +1986,7 @@ function injectCallback(args, value) {
1958 }1986 }
1959 console.log('Removing ephemeral script injection', id);1987 console.log('Removing ephemeral script injection', id);
1960 delete chat_metadata.script_injects[id];1988 delete chat_metadata.script_injects[id];
1961 setExtensionPrompt(prefixedId, '', position, depth, scan, role);1989 setExtensionPrompt(prefixedId, '', position, depth, scan, role, filterFunction);
1962 saveMetadataDebounced();1990 saveMetadataDebounced();
1963 deleted = true;1991 deleted = true;
1964 };1992 };
@@ -2053,9 +2081,28 @@ export function processChatSlashCommands() {
2053 }2081 }
20542082
2055 for (const [id, inject] of Object.entries(context.chatMetadata.script_injects)) {2083 for (const [id, inject] of Object.entries(context.chatMetadata.script_injects)) {
2084 /**
2085 * Rehydrates a filter closure from a string.
2086 * @returns {SlashCommandClosure | null}
2087 */
2088 function reviveFilterClosure() {
2089 if (!inject.filter) {
2090 return null;
2091 }
2092
2093 try {
2094 return new SlashCommandParser().parse(inject.filter, true);
2095 } catch (error) {
2096 console.warn('Failed to revive filter closure for script injection', id, error);
2097 return null;
2098 }
2099 }
2100
2056 const prefixedId = `${SCRIPT_PROMPT_KEY}${id}`;2101 const prefixedId = `${SCRIPT_PROMPT_KEY}${id}`;
2102 const filterClosure = reviveFilterClosure();
2103 const filter = filterClosure ? closureToFilter(filterClosure) : null;
2057 console.log('Adding script injection', id);2104 console.log('Adding script injection', id);
2058 setExtensionPrompt(prefixedId, inject.value, inject.position, inject.depth, inject.scan, inject.role);2105 setExtensionPrompt(prefixedId, inject.value, inject.position, inject.depth, inject.scan, inject.role, filter);
2059 }2106 }
2060}2107}
20612108