Tool Calling: Implement stealth tool defintions (#3192) * Tool Calling: Implement stealth tool defintions * Move isStealth check up * Always stop generation on stealth tool calls * Image Generation: use stealth flag for tool registration * Update stealth property description to clarify no follow-up generation will be performed * Revert "Image Generation: use stealth flag for tool registration" This reverts commit 8d13445c0b66e4c0ef1ddcfaf18ab185464de600.

7e7b3e30c481e128d78c1e6391ecebc6e230ba6e

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

Signed
2 files changed, +66 -7Showing whitespace changes
public/script.js+8 -2
@@ -4579,9 +4579,12 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4579 const shouldDeleteMessage = type !== 'swipe' && ['', '...'].includes(lastMessage?.mes) && ['', '...'].includes(streamingProcessor?.result);4579 const shouldDeleteMessage = type !== 'swipe' && ['', '...'].includes(lastMessage?.mes) && ['', '...'].includes(streamingProcessor?.result);
4580 hasToolCalls && shouldDeleteMessage && await deleteLastMessage();4580 hasToolCalls && shouldDeleteMessage && await deleteLastMessage();
4581 const invocationResult = await ToolManager.invokeFunctionTools(streamingProcessor.toolCalls);4581 const invocationResult = await ToolManager.invokeFunctionTools(streamingProcessor.toolCalls);
4582 const shouldStopGeneration = (!invocationResult.invocations.length && shouldDeleteMessage) || invocationResult.stealthCalls.length;
4582 if (hasToolCalls) {4583 if (hasToolCalls) {
4583 if (!invocationResult.invocations.length && shouldDeleteMessage) {4584 if (shouldStopGeneration) {
4585 if (Array.isArray(invocationResult.errors) && invocationResult.errors.length) {
4584 ToolManager.showToolCallError(invocationResult.errors);4586 ToolManager.showToolCallError(invocationResult.errors);
4587 }
4585 unblockGeneration(type);4588 unblockGeneration(type);
4586 generatedPromptCache = '';4589 generatedPromptCache = '';
4587 streamingProcessor = null;4590 streamingProcessor = null;
@@ -4681,9 +4684,12 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4681 const shouldDeleteMessage = type !== 'swipe' && ['', '...'].includes(getMessage);4684 const shouldDeleteMessage = type !== 'swipe' && ['', '...'].includes(getMessage);
4682 hasToolCalls && shouldDeleteMessage && await deleteLastMessage();4685 hasToolCalls && shouldDeleteMessage && await deleteLastMessage();
4683 const invocationResult = await ToolManager.invokeFunctionTools(data);4686 const invocationResult = await ToolManager.invokeFunctionTools(data);
4687 const shouldStopGeneration = (!invocationResult.invocations.length && shouldDeleteMessage) || invocationResult.stealthCalls.length;
4684 if (hasToolCalls) {4688 if (hasToolCalls) {
4685 if (!invocationResult.invocations.length && shouldDeleteMessage) {4689 if (shouldStopGeneration) {
4690 if (Array.isArray(invocationResult.errors) && invocationResult.errors.length) {
4686 ToolManager.showToolCallError(invocationResult.errors);4691 ToolManager.showToolCallError(invocationResult.errors);
4692 }
4687 unblockGeneration(type);4693 unblockGeneration(type);
4688 generatedPromptCache = '';4694 generatedPromptCache = '';
4689 return;4695 return;
public/scripts/tool-calling.js+58 -5
@@ -25,6 +25,7 @@ import { isTrueBoolean } from './utils.js';
25 * @typedef {object} ToolInvocationResult25 * @typedef {object} ToolInvocationResult
26 * @property {ToolInvocation[]} invocations Successful tool invocations26 * @property {ToolInvocation[]} invocations Successful tool invocations
27 * @property {Error[]} errors Errors that occurred during tool invocation27 * @property {Error[]} errors Errors that occurred during tool invocation
28 * @property {string[]} stealthCalls Names of stealth tools that were invoked
28 */29 */
2930
30/**31/**
@@ -36,6 +37,7 @@ import { isTrueBoolean } from './utils.js';
36 * @property {function} action - The action to perform when the tool is invoked.37 * @property {function} action - The action to perform when the tool is invoked.
37 * @property {function} [formatMessage] - A function to format the tool call message.38 * @property {function} [formatMessage] - A function to format the tool call message.
38 * @property {function} [shouldRegister] - A function to determine if the tool should be registered.39 * @property {function} [shouldRegister] - A function to determine if the tool should be registered.
40 * @property {boolean} [stealth] - A tool call result will not be shown in the chat. No follow-up generation will be performed.
39 */41 */
4042
41/**43/**
@@ -148,6 +150,12 @@ class ToolDefinition {
148 #shouldRegister;150 #shouldRegister;
149151
150 /**152 /**
153 * A tool call result will not be shown in the chat. No follow-up generation will be performed.
154 * @type {boolean}
155 */
156 #stealth;
157
158 /**
151 * Creates a new ToolDefinition.159 * Creates a new ToolDefinition.
152 * @param {string} name A unique name for the tool.160 * @param {string} name A unique name for the tool.
153 * @param {string} displayName A user-friendly display name for the tool.161 * @param {string} displayName A user-friendly display name for the tool.
@@ -156,8 +164,9 @@ class ToolDefinition {
156 * @param {function} action A function that will be called when the tool is executed.164 * @param {function} action A function that will be called when the tool is executed.
157 * @param {function} formatMessage A function that will be called to format the tool call toast.165 * @param {function} formatMessage A function that will be called to format the tool call toast.
158 * @param {function} shouldRegister A function that will be called to determine if the tool should be registered.166 * @param {function} shouldRegister A function that will be called to determine if the tool should be registered.
167 * @param {boolean} stealth A tool call result will not be shown in the chat. No follow-up generation will be performed.
159 */168 */
160 constructor(name, displayName, description, parameters, action, formatMessage, shouldRegister) {169 constructor(name, displayName, description, parameters, action, formatMessage, shouldRegister, stealth) {
161 this.#name = name;170 this.#name = name;
162 this.#displayName = displayName;171 this.#displayName = displayName;
163 this.#description = description;172 this.#description = description;
@@ -165,6 +174,7 @@ class ToolDefinition {
165 this.#action = action;174 this.#action = action;
166 this.#formatMessage = formatMessage;175 this.#formatMessage = formatMessage;
167 this.#shouldRegister = shouldRegister;176 this.#shouldRegister = shouldRegister;
177 this.#stealth = stealth;
168 }178 }
169179
170 /**180 /**
@@ -214,6 +224,10 @@ class ToolDefinition {
214 get displayName() {224 get displayName() {
215 return this.#displayName;225 return this.#displayName;
216 }226 }
227
228 get stealth() {
229 return this.#stealth;
230 }
217}231}
218232
219/**233/**
@@ -246,7 +260,7 @@ export class ToolManager {
246 * Registers a new tool with the tool registry.260 * Registers a new tool with the tool registry.
247 * @param {ToolRegistration} tool The tool to register.261 * @param {ToolRegistration} tool The tool to register.
248 */262 */
249 static registerFunctionTool({ name, displayName, description, parameters, action, formatMessage, shouldRegister }) {263 static registerFunctionTool({ name, displayName, description, parameters, action, formatMessage, shouldRegister, stealth }) {
250 // Convert WIP arguments264 // Convert WIP arguments
251 if (typeof arguments[0] !== 'object') {265 if (typeof arguments[0] !== 'object') {
252 [name, description, parameters, action] = arguments;266 [name, description, parameters, action] = arguments;
@@ -256,7 +270,16 @@ export class ToolManager {
256 console.warn(`[ToolManager] A tool with the name "${name}" has already been registered. The definition will be overwritten.`);270 console.warn(`[ToolManager] A tool with the name "${name}" has already been registered. The definition will be overwritten.`);
257 }271 }
258272
259 const definition = new ToolDefinition(name, displayName, description, parameters, action, formatMessage, shouldRegister);273 const definition = new ToolDefinition(
274 name,
275 displayName,
276 description,
277 parameters,
278 action,
279 formatMessage,
280 shouldRegister,
281 stealth,
282 );
260 this.#tools.set(name, definition);283 this.#tools.set(name, definition);
261 console.log('[ToolManager] Registered function tool:', definition);284 console.log('[ToolManager] Registered function tool:', definition);
262 }285 }
@@ -303,6 +326,20 @@ export class ToolManager {
303 }326 }
304327
305 /**328 /**
329 * Checks if a tool is a stealth tool.
330 * @param {string} name The name of the tool to check.
331 * @returns {boolean} Whether the tool is a stealth tool.
332 */
333 static isStealthTool(name) {
334 if (!this.#tools.has(name)) {
335 return false;
336 }
337
338 const tool = this.#tools.get(name);
339 return !!tool.stealth;
340 }
341
342 /**
306 * Formats a message for a tool call by name.343 * Formats a message for a tool call by name.
307 * @param {string} name The name of the tool to format the message for.344 * @param {string} name The name of the tool to format the message for.
308 * @param {object} parameters Function tool call parameters.345 * @param {object} parameters Function tool call parameters.
@@ -608,6 +645,7 @@ export class ToolManager {
608 const result = {645 const result = {
609 invocations: [],646 invocations: [],
610 errors: [],647 errors: [],
648 stealthCalls: [],
611 };649 };
612 const toolCalls = ToolManager.#getToolCallsFromData(data);650 const toolCalls = ToolManager.#getToolCallsFromData(data);
613651
@@ -625,7 +663,7 @@ export class ToolManager {
625 const parameters = toolCall.function.arguments;663 const parameters = toolCall.function.arguments;
626 const name = toolCall.function.name;664 const name = toolCall.function.name;
627 const displayName = ToolManager.getDisplayName(name);665 const displayName = ToolManager.getDisplayName(name);
628666 const isStealth = ToolManager.isStealthTool(name);
629 const message = await ToolManager.formatToolCallMessage(name, parameters);667 const message = await ToolManager.formatToolCallMessage(name, parameters);
630 const toast = message && toastr.info(message, 'Tool Calling', { timeOut: 0 });668 const toast = message && toastr.info(message, 'Tool Calling', { timeOut: 0 });
631 const toolResult = await ToolManager.invokeFunctionTool(name, parameters);669 const toolResult = await ToolManager.invokeFunctionTool(name, parameters);
@@ -638,6 +676,12 @@ export class ToolManager {
638 continue;676 continue;
639 }677 }
640678
679 // Don't save stealth tool invocations
680 if (isStealth) {
681 result.stealthCalls.push(name);
682 continue;
683 }
684
641 const invocation = {685 const invocation = {
642 id,686 id,
643 displayName,687 displayName,
@@ -860,6 +904,14 @@ export class ToolManager {
860 isRequired: false,904 isRequired: false,
861 acceptsMultiple: false,905 acceptsMultiple: false,
862 }),906 }),
907 SlashCommandNamedArgument.fromProps({
908 name: 'stealth',
909 description: 'If true, a tool call result will not be shown in the chat and no follow-up generation will be performed.',
910 typeList: [ARGUMENT_TYPE.BOOLEAN],
911 isRequired: false,
912 acceptsMultiple: false,
913 defaultValue: String(false),
914 }),
863 ],915 ],
864 unnamedArgumentList: [916 unnamedArgumentList: [
865 SlashCommandArgument.fromProps({917 SlashCommandArgument.fromProps({
@@ -891,7 +943,7 @@ export class ToolManager {
891 };943 };
892 }944 }
893945
894 const { name, displayName, description, parameters, formatMessage, shouldRegister } = args;946 const { name, displayName, description, parameters, formatMessage, shouldRegister, stealth } = args;
895947
896 if (!(action instanceof SlashCommandClosure)) {948 if (!(action instanceof SlashCommandClosure)) {
897 throw new Error('The unnamed argument must be a closure.');949 throw new Error('The unnamed argument must be a closure.');
@@ -927,6 +979,7 @@ export class ToolManager {
927 action: actionFunc,979 action: actionFunc,
928 formatMessage: formatMessageFunc,980 formatMessage: formatMessageFunc,
929 shouldRegister: shouldRegisterFunc,981 shouldRegister: shouldRegisterFunc,
982 stealth: stealth && isTrueBoolean(String(stealth)),
930 });983 });
931984
932 return '';985 return '';