Save tool calls to visible chats.

0f8c1fa95d7bdfea5d69ffed3c0ee76eef444886

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

3 files changed, +71 -44Showing whitespace changes
public/script.js+15 -11
@@ -3571,7 +3571,9 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
35713571 }
35723572
35733573 // Collect messages with usable content
35743574 letconst coreChatcanUseTools = chatToolManager.filterisToolCallingSupported(x => !x.is_system);
3575+ const canPerformToolCalls = !dryRun && ToolManager.canPerformToolCalls(type);
3576+ let coreChat = chat.filter(x => !x.is_system || (canUseTools && Array.isArray(x.extra?.tool_invocations)));
35753577 if (type === 'swipe') {
35763578 coreChat.pop();
35773579 }
@@ -4406,8 +4408,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
44064408 getMessage = continue_mag + getMessage;
44074409 }
44084410
44094411 if (ToolManager.isFunctionCallingSupported()canPerformToolCalls && Array.isArray(streamingProcessor.toolCalls) && streamingProcessor.toolCalls.length) {
44104412 const invocations = await ToolManager.checkFunctionToolCallsinvokeFunctionTools(streamingProcessor.toolCalls);
44114413 if (Array.isArray(invocations) && invocations.length) {
44124414 const lastMessage = chat[chat.length - 1];
44134415 const shouldDeleteMessage = ['', '...'].includes(lastMessage?.mes) && ['', '...'].includes(streamingProcessor.result);
@@ -4455,14 +4457,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
44554457 throw new Error(data?.response);
44564458 }
44574459
4458- if (ToolManager.isFunctionCallingSupported()) {
4459- const invocations = await ToolManager.checkFunctionToolCalls(data);
4460- if (Array.isArray(invocations) && invocations.length) {
4461- ToolManager.saveFunctionToolInvocations(invocations);
4462- return Generate(type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage, quietName }, dryRun);
4463- }
4464- }
4465-
44664460 //const getData = await response.json();
44674461 let getMessage = extractMessageFromData(data);
44684462 let title = extractTitleFromData(data);
@@ -4502,6 +4496,16 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
45024496 parseAndSaveLogprobs(data, continue_mag);
45034497 }
45044498
4499+ if (canPerformToolCalls) {
4500+ const invocations = await ToolManager.invokeFunctionTools(data);
4501+ if (Array.isArray(invocations) && invocations.length) {
4502+ const shouldDeleteMessage = ['', '...'].includes(getMessage);
4503+ shouldDeleteMessage && await deleteLastMessage();
4504+ ToolManager.saveFunctionToolInvocations(invocations);
4505+ return Generate(type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage, quietName }, dryRun);
4506+ }
4507+ }
4508+
45054509 if (type !== 'quiet') {
45064510 playMessageSound();
45074511 }
public/scripts/openai.js+15 -8
@@ -703,7 +703,7 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul
703703 }
704704
705705 const imageInlining = isImageInliningSupported();
706706 const toolCallingcanUseTools = ToolManager.isFunctionCallingSupportedisToolCallingSupported();
707707
708708 // Insert chat messages as long as there is budget available
709709 const chatPool = [...messages].reverse();
@@ -725,10 +725,10 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul
725725 await chatMessage.addImage(chatPrompt.image);
726726 }
727727
728728 if (toolCallingcanUseTools && Array.isArray(chatPrompt.invocations)) {
729729 /** @type {import('./tool-calling.js').ToolInvocation[]} */
730730 const invocations = chatPrompt.invocations;
731731 const toolCallMessage = new Message('assistant'chatMessage.role, undefined, 'toolCall-' + chatMessage.identifier);
732732 toolCallMessage.setToolCalls(invocations);
733733 if (chatCompletion.canAfford(toolCallMessage)) {
734734 chatCompletion.reserveBudget(toolCallMessage);
@@ -1285,7 +1285,7 @@ export async function prepareOpenAIMessages({
12851285 const eventData = { chat, dryRun };
12861286 await eventSource.emit(event_types.CHAT_COMPLETION_PROMPT_READY, eventData);
12871287
12881288 openai_messages_count = chat.filter(x => !x?.tool_calls && (x?.role === 'user' || x?.role === 'assistant'))?.length || 0;
12891289
12901290 return [chat, promptManager.tokenHandler.counts];
12911291}
@@ -1886,7 +1886,7 @@ async function sendOpenAIRequest(type, messages, signal) {
18861886 generate_data['seed'] = oai_settings.seed;
18871887 }
18881888
18891889 if (!canMultiSwipe && ToolManager.isFunctionCallingSupportedcanPerformToolCalls(type)) {
18901890 await ToolManager.registerFunctionToolsOpenAI(generate_data);
18911891 }
18921892
@@ -2393,13 +2393,20 @@ class MessageCollection {
23932393 }
23942394
23952395 /**
23962396 * Get chat in the format of {role, name, content, tool_calls}.
23972397 * @returns {Array} Array of objects with role, name, and content properties.
23982398 */
23992399 getChat() {
24002400 return this.collection.reduce((acc, message) => {
2401- const name = message.name;
2401+ if (message.content || message.tool_calls) {
2402- if (message.content) acc.push({ role: message.role, ...(name && { name }), content: message.content });
2402+ acc.push({
2403+ role: message.role,
2404+ content: message.content,
2405+ ...(message.name && { name: message.name }),
2406+ ...(message.tool_calls && { tool_calls: message.tool_calls }),
2407+ ...(message.role === 'tool' && { tool_call_id: message.identifier }),
2408+ });
2409+ }
24032410 return acc;
24042411 }, []);
24052412 }
public/scripts/tool-calling.js+41 -25
@@ -1,4 +1,4 @@
11import { addOneMessage, chat, main_api, system_avatar, systemUserName } from '../script.js';
22import { chat_completion_sources, oai_settings } from './openai.js';
33
44/**
@@ -243,12 +243,12 @@ export class ToolManager {
243243 }
244244 }
245245
246- static isFunctionCallingSupported() {
246+ /**
247- if (main_api !== 'openai') {
247+ * Checks if tool calling is supported for the current settings and generation type.
248- return false;
248+ * @returns {boolean} Whether tool calling is supported for the given type
249- }
249+ */
250-
250+ static isToolCallingSupported() {
251251 if (main_api !== 'openai' || !oai_settings.function_calling) {
252252 return false;
253253 }
254254
@@ -264,6 +264,22 @@ export class ToolManager {
264264 return supportedSources.includes(oai_settings.chat_completion_source);
265265 }
266266
267+ /**
268+ * Checks if tool calls can be performed for the current settings and generation type.
269+ * @param {string} type Generation type
270+ * @returns {boolean} Whether tool calls can be performed for the given type
271+ */
272+ static canPerformToolCalls(type) {
273+ const noToolCallTypes = ['swipe', 'impersonate', 'quiet', 'continue'];
274+ const isSupported = ToolManager.isToolCallingSupported();
275+ return isSupported && !noToolCallTypes.includes(type);
276+ }
277+
278+ /**
279+ * Utility function to get tool calls from the response data.
280+ * @param {any} data Response data
281+ * @returns {any[]} Tool calls from the response data
282+ */
267283 static #getToolCallsFromData(data) {
268284 // Parsed tool calls from streaming data
269285 if (Array.isArray(data) && data.length > 0) {
@@ -290,15 +306,11 @@ export class ToolManager {
290306 * @param {any} data Reply data
291307 * @returns {Promise<ToolInvocation[]>} Successful tool invocations
292308 */
293309 static async checkFunctionToolCallsinvokeFunctionTools(data) {
294- if (!ToolManager.isFunctionCallingSupported()) {
295- return [];
296- }
297-
298310 /** @type {ToolInvocation[]} */
299311 const invocations = [];
300312 const toolCalls = ToolManager.#getToolCallsFromData(data);
301313 const oaiCompatoaiCompatibleSources = [
302314 chat_completion_sources.OPENAI,
303315 chat_completion_sources.CUSTOM,
304316 chat_completion_sources.MISTRALAI,
@@ -306,7 +318,7 @@ export class ToolManager {
306318 chat_completion_sources.GROQ,
307319 ];
308320
309321 if (oaiCompatoaiCompatibleSources.includes(oai_settings.chat_completion_source)) {
310322 if (!Array.isArray(toolCalls)) {
311323 return [];
312324 }
@@ -323,7 +335,7 @@ export class ToolManager {
323335
324336 toastr.info('Invoking function tool: ' + name);
325337 const result = await ToolManager.invokeFunctionTool(name, parameters);
326338 toastrconsole.infolog('Function tool result: ' +, result);
327339
328340 // Save a successful invocation
329341 if (result) {
@@ -367,15 +379,19 @@ export class ToolManager {
367379 * @param {ToolInvocation[]} invocations Successful tool invocations
368380 */
369381 static saveFunctionToolInvocations(invocations) {
370- for (let index = chat.length - 1; index >= 0; index--) {
382+ const toolNames = invocations.map(i => i.name).join(', ');
371383 const message = chat[index];{
372- if (message.is_user) {
384+ name: systemUserName,
373- if (!message.extra || typeof message.extra !== 'object') {
385+ force_avatar: system_avatar,
374- message.extra = {};
386+ is_system: true,
375- }
387+ is_user: false,
376- message.extra.tool_invocations = invocations;
388+ mes: `Performed tool calls: ${toolNames}`,
377- break;
389+ extra: {
378- }
390+ isSmallSys: true,
379- }
391+ tool_invocations: invocations,
392+ },
393+ };
394+ chat.push(message);
395+ addOneMessage(message);
380396 }
381397}