Show an error when all tools fail

This commit is contained in:
Cohee
2024-10-04 00:11:36 +03:00
parent 576352817e
commit 6558b10675
3 changed files with 66 additions and 16 deletions

View File

@@ -4420,10 +4420,18 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
const lastMessage = chat[chat.length - 1]; const lastMessage = chat[chat.length - 1];
const shouldDeleteMessage = ['', '...'].includes(lastMessage?.mes) && ['', '...'].includes(streamingProcessor.result); const shouldDeleteMessage = ['', '...'].includes(lastMessage?.mes) && ['', '...'].includes(streamingProcessor.result);
shouldDeleteMessage && await deleteLastMessage(); shouldDeleteMessage && await deleteLastMessage();
const invocations = await ToolManager.invokeFunctionTools(streamingProcessor.toolCalls); const invocationResult = await ToolManager.invokeFunctionTools(streamingProcessor.toolCalls);
if (Array.isArray(invocations) && invocations.length) { if (invocationResult.hadToolCalls) {
if (!invocationResult.invocations.length && shouldDeleteMessage) {
ToolManager.showToolCallError(invocationResult.errors);
unblockGeneration(type);
generatedPromptCache = '';
streamingProcessor = null;
return;
}
streamingProcessor = null; streamingProcessor = null;
ToolManager.saveFunctionToolInvocations(invocations); ToolManager.saveFunctionToolInvocations(invocationResult.invocations);
return Generate(type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage, quietName }, dryRun); return Generate(type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage, quietName }, dryRun);
} }
} }
@@ -4505,9 +4513,16 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
if (canPerformToolCalls) { if (canPerformToolCalls) {
const shouldDeleteMessage = ['', '...'].includes(getMessage); const shouldDeleteMessage = ['', '...'].includes(getMessage);
shouldDeleteMessage && await deleteLastMessage(); shouldDeleteMessage && await deleteLastMessage();
const invocations = await ToolManager.invokeFunctionTools(data); const invocationResult = await ToolManager.invokeFunctionTools(data);
if (Array.isArray(invocations) && invocations.length) { if (invocationResult.hadToolCalls) {
ToolManager.saveFunctionToolInvocations(invocations); if (!invocationResult.invocations.length && shouldDeleteMessage) {
ToolManager.showToolCallError(invocationResult.errors);
unblockGeneration(type);
generatedPromptCache = '';
return;
}
ToolManager.saveFunctionToolInvocations(invocationResult.invocations);
return Generate(type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage, quietName }, dryRun); return Generate(type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage, quietName }, dryRun);
} }
} }

View File

@@ -1,5 +1,6 @@
import { addOneMessage, chat, main_api, system_avatar, systemUserName } from '../script.js'; import { addOneMessage, chat, main_api, system_avatar, systemUserName } from '../script.js';
import { chat_completion_sources, oai_settings } from './openai.js'; import { chat_completion_sources, oai_settings } from './openai.js';
import { Popup } from './popup.js';
/** /**
* @typedef {object} ToolInvocation * @typedef {object} ToolInvocation
@@ -9,6 +10,13 @@ import { chat_completion_sources, oai_settings } from './openai.js';
* @property {string} result - The result of the tool invocation. * @property {string} result - The result of the tool invocation.
*/ */
/**
* @typedef {object} ToolInvocationResult
* @property {ToolInvocation[]} invocations Successful tool invocations
* @property {boolean} hadToolCalls Whether any tool calls were found
* @property {Error[]} errors Errors that occurred during tool invocation
*/
/** /**
* A class that represents a tool definition. * A class that represents a tool definition.
*/ */
@@ -129,7 +137,7 @@ export class ToolManager {
* Invokes a tool by name. Returns the result of the tool's action function. * Invokes a tool by name. Returns the result of the tool's action function.
* @param {string} name The name of the tool to invoke. * @param {string} name The name of the tool to invoke.
* @param {object} parameters Function parameters. For example, if the tool requires a "name" parameter, you would pass {name: "value"}. * @param {object} parameters Function parameters. For example, if the tool requires a "name" parameter, you would pass {name: "value"}.
* @returns {Promise<string|null>} The result of the tool's action function. If an error occurs, null is returned. Non-string results are JSON-stringified. * @returns {Promise<string|Error>} The result of the tool's action function. If an error occurs, null is returned. Non-string results are JSON-stringified.
*/ */
static async invokeFunctionTool(name, parameters) { static async invokeFunctionTool(name, parameters) {
try { try {
@@ -143,7 +151,13 @@ export class ToolManager {
return typeof result === 'string' ? result : JSON.stringify(result); return typeof result === 'string' ? result : JSON.stringify(result);
} catch (error) { } catch (error) {
console.error(`An error occurred while invoking the tool "${name}":`, error); console.error(`An error occurred while invoking the tool "${name}":`, error);
return null;
if (error instanceof Error) {
error.cause = name;
return error;
}
return new Error('Unknown error occurred while invoking the tool.', { cause: name });
} }
} }
@@ -306,11 +320,15 @@ export class ToolManager {
/** /**
* Check for function tool calls in the response data and invoke them. * Check for function tool calls in the response data and invoke them.
* @param {any} data Reply data * @param {any} data Reply data
* @returns {Promise<ToolInvocation[]>} Successful tool invocations * @returns {Promise<ToolInvocationResult>} Successful tool invocations
*/ */
static async invokeFunctionTools(data) { static async invokeFunctionTools(data) {
/** @type {ToolInvocation[]} */ /** @type {ToolInvocationResult} */
const invocations = []; const result = {
invocations: [],
hadToolCalls: false,
errors: [],
};
const toolCalls = ToolManager.#getToolCallsFromData(data); const toolCalls = ToolManager.#getToolCallsFromData(data);
const oaiCompatibleSources = [ const oaiCompatibleSources = [
chat_completion_sources.OPENAI, chat_completion_sources.OPENAI,
@@ -322,7 +340,7 @@ export class ToolManager {
if (oaiCompatibleSources.includes(oai_settings.chat_completion_source)) { if (oaiCompatibleSources.includes(oai_settings.chat_completion_source)) {
if (!Array.isArray(toolCalls)) { if (!Array.isArray(toolCalls)) {
return []; return result;
} }
for (const toolCall of toolCalls) { for (const toolCall of toolCalls) {
@@ -334,16 +352,20 @@ export class ToolManager {
const id = toolCall.id; const id = toolCall.id;
const parameters = toolCall.function.arguments; const parameters = toolCall.function.arguments;
const name = toolCall.function.name; const name = toolCall.function.name;
result.hadToolCalls = true;
const toast = toastr.info(`Invoking function tool: ${name}`); const toast = toastr.info(`Invoking function tool: ${name}`);
const result = await ToolManager.invokeFunctionTool(name, parameters); const toolResult = await ToolManager.invokeFunctionTool(name, parameters);
toastr.clear(toast); toastr.clear(toast);
console.log('Function tool result:', result); console.log('Function tool result:', result);
// Save a successful invocation // Save a successful invocation
if (result) { if (toolResult instanceof Error) {
invocations.push({ id, name, parameters, result }); result.errors.push(toolResult);
continue;
} }
result.invocations.push({ id, name, parameters, result: toolResult });
} }
} }
@@ -374,7 +396,7 @@ export class ToolManager {
} }
*/ */
return invocations; return result;
} }
/** /**
@@ -418,4 +440,16 @@ export class ToolManager {
chat.push(message); chat.push(message);
addOneMessage(message); addOneMessage(message);
} }
/**
* Shows an error message for tool calls.
* @param {Error[]} errors Errors that occurred during tool invocation
* @returns {void}
*/
static showToolCallError(errors) {
toastr.error('An error occurred while invoking function tools. Click here for more details.', 'Tool Calling', {
onclick: () => Popup.show.text('Tool Calling Errors', DOMPurify.sanitize(errors.map(e => `${e.cause}: ${e.message}`).join('<br>'))),
timeOut: 5000,
});
}
} }

View File

@@ -421,6 +421,7 @@ small {
.mes.smallSysMes pre { .mes.smallSysMes pre {
text-align: initial; text-align: initial;
word-break: break-all; word-break: break-all;
margin-top: 5px;
} }
.mes.smallSysMes summary { .mes.smallSysMes summary {