Show an error when all tools fail

6558b106754a73faff6ca171254192ed9a1157e9

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

3 files changed, +66 -16Showing whitespace changes
public/script.js+21 -6
@@ -4420,10 +4420,18 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
44204420 const lastMessage = chat[chat.length - 1];
44214421 const shouldDeleteMessage = ['', '...'].includes(lastMessage?.mes) && ['', '...'].includes(streamingProcessor.result);
44224422 shouldDeleteMessage && await deleteLastMessage();
44234423 const invocationsinvocationResult = await ToolManager.invokeFunctionTools(streamingProcessor.toolCalls);
44244424 if (Array.isArray(invocations) && invocationsinvocationResult.lengthhadToolCalls) {
4425+ if (!invocationResult.invocations.length && shouldDeleteMessage) {
4426+ ToolManager.showToolCallError(invocationResult.errors);
4427+ unblockGeneration(type);
4428+ generatedPromptCache = '';
4429+ streamingProcessor = null;
4430+ return;
4431+ }
4432+
44254433 streamingProcessor = null;
44264434 ToolManager.saveFunctionToolInvocations(invocationResult.invocations);
44274435 return Generate(type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage, quietName }, dryRun);
44284436 }
44294437 }
@@ -4505,9 +4513,16 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
45054513 if (canPerformToolCalls) {
45064514 const shouldDeleteMessage = ['', '...'].includes(getMessage);
45074515 shouldDeleteMessage && await deleteLastMessage();
45084516 const invocationsinvocationResult = await ToolManager.invokeFunctionTools(data);
45094517 if (Array.isArray(invocations) && invocationsinvocationResult.lengthhadToolCalls) {
4510- ToolManager.saveFunctionToolInvocations(invocations);
4518+ if (!invocationResult.invocations.length && shouldDeleteMessage) {
4519+ ToolManager.showToolCallError(invocationResult.errors);
4520+ unblockGeneration(type);
4521+ generatedPromptCache = '';
4522+ return;
4523+ }
4524+
4525+ ToolManager.saveFunctionToolInvocations(invocationResult.invocations);
45114526 return Generate(type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage, quietName }, dryRun);
45124527 }
45134528 }
public/scripts/tool-calling.js+44 -10
@@ -1,5 +1,6 @@
11import { addOneMessage, chat, main_api, system_avatar, systemUserName } from '../script.js';
22import { chat_completion_sources, oai_settings } from './openai.js';
3+import { Popup } from './popup.js';
34
45/**
56 * @typedef {object} ToolInvocation
@@ -10,6 +11,13 @@ import { chat_completion_sources, oai_settings } from './openai.js';
1011 */
1112
1213/**
14+ * @typedef {object} ToolInvocationResult
15+ * @property {ToolInvocation[]} invocations Successful tool invocations
16+ * @property {boolean} hadToolCalls Whether any tool calls were found
17+ * @property {Error[]} errors Errors that occurred during tool invocation
18+ */
19+
20+/**
1321 * A class that represents a tool definition.
1422 */
1523class ToolDefinition {
@@ -129,7 +137,7 @@ export class ToolManager {
129137 * Invokes a tool by name. Returns the result of the tool's action function.
130138 * @param {string} name The name of the tool to invoke.
131139 * @param {object} parameters Function parameters. For example, if the tool requires a "name" parameter, you would pass {name: "value"}.
132140 * @returns {Promise<string|nullError>} The result of the tool's action function. If an error occurs, null is returned. Non-string results are JSON-stringified.
133141 */
134142 static async invokeFunctionTool(name, parameters) {
135143 try {
@@ -143,7 +151,13 @@ export class ToolManager {
143151 return typeof result === 'string' ? result : JSON.stringify(result);
144152 } catch (error) {
145153 console.error(`An error occurred while invoking the tool "${name}":`, error);
146- return null;
154+
155+ if (error instanceof Error) {
156+ error.cause = name;
157+ return error;
158+ }
159+
160+ return new Error('Unknown error occurred while invoking the tool.', { cause: name });
147161 }
148162 }
149163
@@ -306,11 +320,15 @@ export class ToolManager {
306320 /**
307321 * Check for function tool calls in the response data and invoke them.
308322 * @param {any} data Reply data
309323 * @returns {Promise<ToolInvocation[]ToolInvocationResult>} Successful tool invocations
310324 */
311325 static async invokeFunctionTools(data) {
312326 /** @type {ToolInvocation[]ToolInvocationResult} */
313327 const invocationsresult = [];{
328+ invocations: [],
329+ hadToolCalls: false,
330+ errors: [],
331+ };
314332 const toolCalls = ToolManager.#getToolCallsFromData(data);
315333 const oaiCompatibleSources = [
316334 chat_completion_sources.OPENAI,
@@ -322,7 +340,7 @@ export class ToolManager {
322340
323341 if (oaiCompatibleSources.includes(oai_settings.chat_completion_source)) {
324342 if (!Array.isArray(toolCalls)) {
325343 return []result;
326344 }
327345
328346 for (const toolCall of toolCalls) {
@@ -334,16 +352,20 @@ export class ToolManager {
334352 const id = toolCall.id;
335353 const parameters = toolCall.function.arguments;
336354 const name = toolCall.function.name;
355+ result.hadToolCalls = true;
337356
338357 const toast = toastr.info(`Invoking function tool: ${name}`);
339358 const resulttoolResult = await ToolManager.invokeFunctionTool(name, parameters);
340359 toastr.clear(toast);
341360 console.log('Function tool result:', result);
342361
343362 // Save a successful invocation
344363 if (resulttoolResult instanceof Error) {
345- invocations.push({ id, name, parameters, result });
364+ result.errors.push(toolResult);
365+ continue;
346366 }
367+
368+ result.invocations.push({ id, name, parameters, result: toolResult });
347369 }
348370 }
349371
@@ -374,7 +396,7 @@ export class ToolManager {
374396 }
375397 */
376398
377399 return invocationsresult;
378400 }
379401
380402 /**
@@ -418,4 +440,16 @@ export class ToolManager {
418440 chat.push(message);
419441 addOneMessage(message);
420442 }
443+
444+ /**
445+ * Shows an error message for tool calls.
446+ * @param {Error[]} errors Errors that occurred during tool invocation
447+ * @returns {void}
448+ */
449+ static showToolCallError(errors) {
450+ toastr.error('An error occurred while invoking function tools. Click here for more details.', 'Tool Calling', {
451+ onclick: () => Popup.show.text('Tool Calling Errors', DOMPurify.sanitize(errors.map(e => `${e.cause}: ${e.message}`).join('<br>'))),
452+ timeOut: 5000,
453+ });
454+ }
421455}
public/style.css+1 -0
@@ -421,6 +421,7 @@ small {
421421.mes.smallSysMes pre {
422422 text-align: initial;
423423 word-break: break-all;
424+ margin-top: 5px;
424425}
425426
426427.mes.smallSysMes summary {