Show an error when all tools fail
| @@ -4420,10 +4420,18 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4420 | 4420 | const lastMessage = chat[chat.length - 1]; |
| 4421 | 4421 | const shouldDeleteMessage = ['', '...'].includes(lastMessage?.mes) && ['', '...'].includes(streamingProcessor.result); |
| 4422 | 4422 | shouldDeleteMessage && await deleteLastMessage(); |
| 4423 | 4423 | const invocationsinvocationResult = await ToolManager.invokeFunctionTools(streamingProcessor.toolCalls); |
| 4424 | 4424 | 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 | + | |
| 4425 | 4433 | streamingProcessor = null; |
| 4426 | 4434 | ToolManager.saveFunctionToolInvocations(invocationResult.invocations); |
| 4427 | 4435 | return Generate(type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage, quietName }, dryRun); |
| 4428 | 4436 | } |
| 4429 | 4437 | } |
| @@ -4505,9 +4513,16 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4505 | 4513 | if (canPerformToolCalls) { |
| 4506 | 4514 | const shouldDeleteMessage = ['', '...'].includes(getMessage); |
| 4507 | 4515 | shouldDeleteMessage && await deleteLastMessage(); |
| 4508 | 4516 | const invocationsinvocationResult = await ToolManager.invokeFunctionTools(data); |
| 4509 | 4517 | 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); | |
| 4511 | 4526 | return Generate(type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage, quietName }, dryRun); |
| 4512 | 4527 | } |
| 4513 | 4528 | } |
| @@ -1,5 +1,6 @@ | ||
| 1 | 1 | import { addOneMessage, chat, main_api, system_avatar, systemUserName } from '../script.js'; |
| 2 | 2 | import { chat_completion_sources, oai_settings } from './openai.js'; |
| 3 | +import { Popup } from './popup.js'; | |
| 3 | 4 | |
| 4 | 5 | /** |
| 5 | 6 | * @typedef {object} ToolInvocation |
| @@ -10,6 +11,13 @@ import { chat_completion_sources, oai_settings } from './openai.js'; | ||
| 10 | 11 | */ |
| 11 | 12 | |
| 12 | 13 | /** |
| 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 | +/** | |
| 13 | 21 | * A class that represents a tool definition. |
| 14 | 22 | */ |
| 15 | 23 | class ToolDefinition { |
| @@ -129,7 +137,7 @@ export class ToolManager { | ||
| 129 | 137 | * Invokes a tool by name. Returns the result of the tool's action function. |
| 130 | 138 | * @param {string} name The name of the tool to invoke. |
| 131 | 139 | * @param {object} parameters Function parameters. For example, if the tool requires a "name" parameter, you would pass {name: "value"}. |
| 132 | 140 | * @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. |
| 133 | 141 | */ |
| 134 | 142 | static async invokeFunctionTool(name, parameters) { |
| 135 | 143 | try { |
| @@ -143,7 +151,13 @@ export class ToolManager { | ||
| 143 | 151 | return typeof result === 'string' ? result : JSON.stringify(result); |
| 144 | 152 | } catch (error) { |
| 145 | 153 | 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 }); | |
| 147 | 161 | } |
| 148 | 162 | } |
| 149 | 163 | |
| @@ -306,11 +320,15 @@ export class ToolManager { | ||
| 306 | 320 | /** |
| 307 | 321 | * Check for function tool calls in the response data and invoke them. |
| 308 | 322 | * @param {any} data Reply data |
| 309 | 323 | * @returns {Promise<ToolInvocation[]ToolInvocationResult>} Successful tool invocations |
| 310 | 324 | */ |
| 311 | 325 | static async invokeFunctionTools(data) { |
| 312 | 326 | /** @type {ToolInvocation[]ToolInvocationResult} */ |
| 313 | 327 | const invocationsresult = [];{ |
| 328 | + invocations: [], | |
| 329 | + hadToolCalls: false, | |
| 330 | + errors: [], | |
| 331 | + }; | |
| 314 | 332 | const toolCalls = ToolManager.#getToolCallsFromData(data); |
| 315 | 333 | const oaiCompatibleSources = [ |
| 316 | 334 | chat_completion_sources.OPENAI, |
| @@ -322,7 +340,7 @@ export class ToolManager { | ||
| 322 | 340 | |
| 323 | 341 | if (oaiCompatibleSources.includes(oai_settings.chat_completion_source)) { |
| 324 | 342 | if (!Array.isArray(toolCalls)) { |
| 325 | 343 | return []result; |
| 326 | 344 | } |
| 327 | 345 | |
| 328 | 346 | for (const toolCall of toolCalls) { |
| @@ -334,16 +352,20 @@ export class ToolManager { | ||
| 334 | 352 | const id = toolCall.id; |
| 335 | 353 | const parameters = toolCall.function.arguments; |
| 336 | 354 | const name = toolCall.function.name; |
| 355 | + result.hadToolCalls = true; | |
| 337 | 356 | |
| 338 | 357 | const toast = toastr.info(`Invoking function tool: ${name}`); |
| 339 | 358 | const resulttoolResult = await ToolManager.invokeFunctionTool(name, parameters); |
| 340 | 359 | toastr.clear(toast); |
| 341 | 360 | console.log('Function tool result:', result); |
| 342 | 361 | |
| 343 | 362 | // Save a successful invocation |
| 344 | 363 | if (resulttoolResult instanceof Error) { |
| 345 | - invocations.push({ id, name, parameters, result }); | |
| 364 | + result.errors.push(toolResult); | |
| 365 | + continue; | |
| 346 | 366 | } |
| 367 | + | |
| 368 | + result.invocations.push({ id, name, parameters, result: toolResult }); | |
| 347 | 369 | } |
| 348 | 370 | } |
| 349 | 371 | |
| @@ -374,7 +396,7 @@ export class ToolManager { | ||
| 374 | 396 | } |
| 375 | 397 | */ |
| 376 | 398 | |
| 377 | 399 | return invocationsresult; |
| 378 | 400 | } |
| 379 | 401 | |
| 380 | 402 | /** |
| @@ -418,4 +440,16 @@ export class ToolManager { | ||
| 418 | 440 | chat.push(message); |
| 419 | 441 | addOneMessage(message); |
| 420 | 442 | } |
| 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 | + } | |
| 421 | 455 | } |
| @@ -421,6 +421,7 @@ small { | ||
| 421 | 421 | .mes.smallSysMes pre { |
| 422 | 422 | text-align: initial; |
| 423 | 423 | word-break: break-all; |
| 424 | + margin-top: 5px; | |
| 424 | 425 | } |
| 425 | 426 | |
| 426 | 427 | .mes.smallSysMes summary { |