From ebc7f88b5337b271c68047b834ccf306b7e27e60 Mon Sep 17 00:00:00 2001 From: hanishkvc Date: Wed, 15 Oct 2025 01:20:27 +0530 Subject: [PATCH] SimpleChatTC:Propogate toolcall id through tool call chain Use HTMLElement's dataset to maintain tool call id along with the element which maintains the toolname. Pass it along to the tools manager and inturn the actual tool calls and through them to the web worker handling the tool call related code and inturn returning it back as part of the obj which is used to return the tool call result. Embed the tool call id, function name and function result into the content field of chat message in terms of a xml structure Also make use of tool role to send back the tool call result. Do note that currently the id, name and content are all embedded into the content field of the tool role message sent to the ai engine on the server. NOTE: Use the user query entry area for showing tool call result in the above mentioned xml form, as well as for user to enter their own queries. Based on presence of the xml format data at beginning the logic will treat it has a tool result and if not then as a normal user query. The css has been updated to help show tool results/msgs in a lightyellow background --- tools/server/public_simplechat/simplechat.css | 3 ++ tools/server/public_simplechat/simplechat.js | 46 +++++++++++++++---- tools/server/public_simplechat/tooljs.mjs | 10 ++-- tools/server/public_simplechat/tools.mjs | 9 ++-- .../server/public_simplechat/toolsworker.mjs | 2 +- 5 files changed, 53 insertions(+), 17 deletions(-) diff --git a/tools/server/public_simplechat/simplechat.css b/tools/server/public_simplechat/simplechat.css index 13bfb80b48..d4755074b7 100644 --- a/tools/server/public_simplechat/simplechat.css +++ b/tools/server/public_simplechat/simplechat.css @@ -21,6 +21,9 @@ .role-user { background-color: lightgray; } +.role-tool { + background-color: lightyellow; +} .role-trim { background-color: lightpink; } diff --git a/tools/server/public_simplechat/simplechat.js b/tools/server/public_simplechat/simplechat.js index 2b82a4648d..281b2c15b9 100644 --- a/tools/server/public_simplechat/simplechat.js +++ b/tools/server/public_simplechat/simplechat.js @@ -77,6 +77,17 @@ class ChatMessageEx { this.trimmedContent = ""; } + /** + * Create a all in one tool call result string + * @param {string} toolCallId + * @param {string} toolName + * @param {string} toolResult + */ + static createToolCallResultAllInOne(toolCallId, toolName, toolResult) { + return ` ${toolCallId} ${toolName} ${toolResult} `; + } + + /** * Update based on the drip by drip data got from network in streaming mode. * Tries to support both Chat and Completion endpoints @@ -561,15 +572,16 @@ class SimpleChat { * Call the requested tool/function. * Returns undefined, if the call was placed successfully * Else some appropriate error message will be returned. + * @param {string} toolcallid * @param {string} toolname * @param {string} toolargs */ - async handle_toolcall(toolname, toolargs) { + async handle_toolcall(toolcallid, toolname, toolargs) { if (toolname === "") { return "Tool/Function call name not specified" } try { - return await tools.tool_call(toolname, toolargs) + return await tools.tool_call(toolcallid, toolname, toolargs) } catch (/** @type {any} */error) { return `Tool/Function call raised an exception:${error.name}:${error.message}` } @@ -633,11 +645,13 @@ class MultiChatUI { if (ar.has_toolcall()) { this.elDivTool.hidden = false this.elInToolName.value = ar.ns.tool_calls[0].function.name + this.elInToolName.dataset.tool_call_id = ar.ns.tool_calls[0].id this.elInToolArgs.value = ar.ns.tool_calls[0].function.arguments this.elBtnTool.disabled = false } else { this.elDivTool.hidden = true this.elInToolName.value = "" + this.elInToolName.dataset.tool_call_id = "" this.elInToolArgs.value = "" this.elBtnTool.disabled = true } @@ -697,9 +711,9 @@ class MultiChatUI { this.handle_tool_run(this.curChatId); }) - tools.setup((name, data)=>{ + tools.setup((id, name, data)=>{ clearTimeout(this.idTimeOut) - this.elInUser.value = `${data}` + this.elInUser.value = ChatMessageEx.createToolCallResultAllInOne(id, name, data); this.ui_reset_userinput(false) }) @@ -744,6 +758,14 @@ class MultiChatUI { /** * Handle user query submit request, wrt specified chat session. + * NOTE: Currently the user query entry area is used for + * * showing and allowing edits by user wrt tool call results + * in a predfined simple xml format, + * ie before they submit tool result to ai engine on server + * * as well as for user to enter their own queries. + * Based on presence of the predefined xml format data at beginning + * the logic will treat it has a tool result and if not then as a + * normal user query. * @param {string} chatId * @param {string} apiEP */ @@ -768,7 +790,11 @@ class MultiChatUI { console.debug(`WARN:SimpleChat:MCUI:${chatId}:HandleUserSubmit:Ignoring empty user input...`); return; } - chat.add(new ChatMessageEx(Roles.User, content)) + if (content.startsWith("")) { + chat.add(new ChatMessageEx(Roles.Tool, content)) + } else { + chat.add(new ChatMessageEx(Roles.User, content)) + } chat.show(this.elDivChat); let theUrl = ApiEP.Url(gMe.baseURL, apiEP); @@ -808,13 +834,17 @@ class MultiChatUI { this.elInUser.value = "toolcall in progress..."; this.elInUser.disabled = true; let toolname = this.elInToolName.value.trim() - let toolResult = await chat.handle_toolcall(toolname, this.elInToolArgs.value) + let toolCallId = this.elInToolName.dataset.tool_call_id; + if (toolCallId === undefined) { + toolCallId = "??? ToolCallId Missing ???" + } + let toolResult = await chat.handle_toolcall(toolCallId, toolname, this.elInToolArgs.value) if (toolResult !== undefined) { - this.elInUser.value = `${toolResult}` + this.elInUser.value = ChatMessageEx.createToolCallResultAllInOne(toolCallId, toolname, toolResult); this.ui_reset_userinput(false) } else { this.idTimeOut = setTimeout(() => { - this.elInUser.value = `Tool/Function call ${toolname} taking too much time, aborting...` + this.elInUser.value = ChatMessageEx.createToolCallResultAllInOne(toolCallId, toolname, `Tool/Function call ${toolname} taking too much time, aborting...`); this.ui_reset_userinput(false) }, 10000) } diff --git a/tools/server/public_simplechat/tooljs.mjs b/tools/server/public_simplechat/tooljs.mjs index 6aea9a5ee4..a44333ca1b 100644 --- a/tools/server/public_simplechat/tooljs.mjs +++ b/tools/server/public_simplechat/tooljs.mjs @@ -32,11 +32,12 @@ let js_meta = { /** * Implementation of the javascript interpretor logic. Minimal skeleton for now. * ALERT: Has access to the javascript web worker environment and can mess with it and beyond + * @param {string} toolcallid * @param {string} toolname * @param {any} obj */ -function js_run(toolname, obj) { - gToolsWorker.postMessage({ name: toolname, code: obj["code"]}) +function js_run(toolcallid, toolname, obj) { + gToolsWorker.postMessage({ id: toolcallid, name: toolname, code: obj["code"]}) } @@ -62,11 +63,12 @@ let calc_meta = { /** * Implementation of the simple calculator logic. Minimal skeleton for now. * ALERT: Has access to the javascript web worker environment and can mess with it and beyond + * @param {string} toolcallid * @param {string} toolname * @param {any} obj */ -function calc_run(toolname, obj) { - gToolsWorker.postMessage({ name: toolname, code: `console.log(${obj["arithexpr"]})`}) +function calc_run(toolcallid, toolname, obj) { + gToolsWorker.postMessage({ id: toolcallid, name: toolname, code: `console.log(${obj["arithexpr"]})`}) } diff --git a/tools/server/public_simplechat/tools.mjs b/tools/server/public_simplechat/tools.mjs index 75fe56e4f4..8c89e96525 100644 --- a/tools/server/public_simplechat/tools.mjs +++ b/tools/server/public_simplechat/tools.mjs @@ -32,11 +32,11 @@ export function meta() { /** * Setup the callback that will be called when ever message * is recieved from the Tools Web Worker. - * @param {(name: string, data: string) => void} cb + * @param {(id: string, name: string, data: string) => void} cb */ export function setup(cb) { gToolsWorker.onmessage = function (ev) { - cb(ev.data.name, ev.data.data) + cb(ev.data.id, ev.data.name, ev.data.data) } } @@ -45,14 +45,15 @@ export function setup(cb) { * Try call the specified tool/function call. * Returns undefined, if the call was placed successfully * Else some appropriate error message will be returned. + * @param {string} toolcallid * @param {string} toolname * @param {string} toolargs */ -export async function tool_call(toolname, toolargs) { +export async function tool_call(toolcallid, toolname, toolargs) { for (const fn in tc_switch) { if (fn == toolname) { try { - tc_switch[fn]["handler"](fn, JSON.parse(toolargs)) + tc_switch[fn]["handler"](toolcallid, fn, JSON.parse(toolargs)) return undefined } catch (/** @type {any} */error) { return `Tool/Function call raised an exception:${error.name}:${error.message}` diff --git a/tools/server/public_simplechat/toolsworker.mjs b/tools/server/public_simplechat/toolsworker.mjs index e370fd0a9d..590c45234b 100644 --- a/tools/server/public_simplechat/toolsworker.mjs +++ b/tools/server/public_simplechat/toolsworker.mjs @@ -21,5 +21,5 @@ self.onmessage = function (ev) { console.log(`\n\nTool/Function call "${ev.data.name}" raised an exception:${error.name}:${error.message}\n\n`) } tconsole.console_revert() - self.postMessage({ name: ev.data.name, data: tconsole.gConsoleStr}) + self.postMessage({ id: ev.data.id, name: ev.data.name, data: tconsole.gConsoleStr}) }