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}) }