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();
8484const registerSlashCommand = SlashCommandParser.addCommand.bind(SlashCommandParser);
8585const 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+ */
92+function 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+
87101export function initDefaultSlashCommands() {
88102 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
89103 name: '?',
@@ -1611,6 +1625,13 @@ export function initDefaultSlashCommands() {
16111625 new SlashCommandNamedArgument(
16121626 'ephemeral', 'remove injection after generation', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',
16131627 ),
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+ }),
16141635 ],
16151636 unnamedArgumentList: [
16161637 new SlashCommandArgument(
@@ -1901,6 +1922,11 @@ const NARRATOR_NAME_DEFAULT = 'System';
19011922export const COMMENT_NAME_DEFAULT = 'Note';
19021923const 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+ */
19041930function injectCallback(args, value) {
19051931 const positions = {
19061932 'before': extension_prompt_types.BEFORE_PROMPT,
@@ -1914,8 +1940,8 @@ function injectCallback(args, value) {
19141940 'assistant': extension_prompt_roles.ASSISTANT,
19151941 };
19161942
19171943 const id = String(args?.id);
19181944 const ephemeral = isTrueBoolean(String(args?.ephemeral));
19191945
19201946 if (!id) {
19211947 console.warn('WARN: No ID provided for /inject command');
@@ -1931,7 +1957,9 @@ function injectCallback(args, value) {
19311957 const depth = isNaN(depthValue) ? defaultDepth : depthValue;
19321958 const roleValue = typeof args?.role === 'string' ? args.role.toLowerCase().trim() : Number(args?.role ?? extension_prompt_roles.SYSTEM);
19331959 const role = roles[roleValue] ?? roles[extension_prompt_roles.SYSTEM];
19341960 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;
19351963 value = value || '';
19361964
19371965 const prefixedId = `${SCRIPT_PROMPT_KEY}${id}`;
@@ -1941,13 +1969,13 @@ function injectCallback(args, value) {
19411969 }
19421970
19431971 if (value) {
19441972 const inject = { value, position, depth, scan, role, filter };
19451973 chat_metadata.script_injects[id] = inject;
19461974 } else {
19471975 delete chat_metadata.script_injects[id];
19481976 }
19491977
19501978 setExtensionPrompt(prefixedId, String(value), position, depth, scan, role, filterFunction);
19511979 saveMetadataDebounced();
19521980
19531981 if (ephemeral) {
@@ -1958,7 +1986,7 @@ function injectCallback(args, value) {
19581986 }
19591987 console.log('Removing ephemeral script injection', id);
19601988 delete chat_metadata.script_injects[id];
19611989 setExtensionPrompt(prefixedId, '', position, depth, scan, role, filterFunction);
19621990 saveMetadataDebounced();
19631991 deleted = true;
19641992 };
@@ -2053,9 +2081,28 @@ export function processChatSlashCommands() {
20532081 }
20542082
20552083 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+
20562101 const prefixedId = `${SCRIPT_PROMPT_KEY}${id}`;
2102+ const filterClosure = reviveFilterClosure();
2103+ const filter = filterClosure ? closureToFilter(filterClosure) : null;
20572104 console.log('Adding script injection', id);
20582105 setExtensionPrompt(prefixedId, inject.value, inject.position, inject.depth, inject.scan, inject.role, filter);
20592106 }
20602107}
20612108