SimpleChatTC:NSChatMessage with tool response/result fields

Add a new static helper to create a ChatMessageEx for a given
tool response data.

Use the same when storing tool response in the chat session msgs
list.

This inturn avoids the need for creating a xml string with all
the fields corresponding to tool response. So also no need to
extract the individiual tool response fields from the all-in-one
xml string and populate the tool response fields in the network
structure equivalent ns data structure, when recent_chat_ns is
called.
This commit is contained in:
hanishkvc 2025-11-08 20:50:51 +05:30
parent 7a61ca6aac
commit b251f7de7c
1 changed files with 18 additions and 78 deletions

View File

@ -65,12 +65,26 @@ class NSChatMessage {
* @param {string|undefined} content
* @param {string|undefined} reasoning_content
* @param {Array<NSToolCall>|undefined} tool_calls
* @param {string|undefined} tool_call_id - toolcall response - the tool / function call id
* @param {string|undefined} name - toolcall response - the tool / function call name
*/
constructor(role = "", content=undefined, reasoning_content=undefined, tool_calls=undefined) {
constructor(role = "", content=undefined, reasoning_content=undefined, tool_calls=undefined, tool_call_id=undefined, name=undefined) {
this.role = role;
this.content = content;
this.reasoning_content = reasoning_content
this.tool_calls = structuredClone(tool_calls)
this.tool_call_id = tool_call_id
this.name = name
}
/**
* @param {string} role
* @param {string} tool_call_id
* @param {string} name
* @param {string} content
*/
static new_tool_response(role, tool_call_id, name, content) {
return new NSChatMessage(role, content, undefined, undefined, tool_call_id, name)
}
getContent() {
@ -240,74 +254,6 @@ class ChatMessageEx {
this.trimmedContent = undefined;
}
/**
* Create a all in one tool call result string
* Use browser's dom logic to handle strings in a xml/html safe way by escaping things where needed,
* so that extracting the same later doesnt create any problems.
* @param {string} toolCallId
* @param {string} toolName
* @param {string} toolResult
*/
static createToolCallResultAllInOne(toolCallId, toolName, toolResult) {
let dp = new DOMParser()
let doc = dp.parseFromString("<tool_response></tool_response>", "text/xml")
for (const k of [["id", toolCallId], ["name", toolName], ["content", toolResult]]) {
let el = doc.createElement(k[0])
el.appendChild(doc.createTextNode(k[1]))
doc.documentElement.appendChild(el)
}
let xmlStr = new XMLSerializer().serializeToString(doc);
xmlStr = xmlStr.replace(/\/name><content/, '\/name>\n<content');
return xmlStr;
}
/**
* Extract the elements of the all in one tool call result string
* @param {string} allInOne
*/
static extractToolCallResultAllInOneSimpleMinded(allInOne) {
const regex = /<tool_response>\s*<id>(.*?)<\/id>\s*<name>(.*?)<\/name>\s*<content>([\s\S]*?)<\/content>\s*<\/tool_response>/si;
const caught = allInOne.match(regex)
let data = { tool_call_id: "Error", name: "Error", content: "Error" }
if (caught) {
data = {
tool_call_id: caught[1].trim(),
name: caught[2].trim(),
content: caught[3].trim()
}
}
return data
}
/**
* Extract the elements of the all in one tool call result string
* This should potentially account for content tag having xml/html content within to an extent.
*
* NOTE: Rather text/html is a more relaxed/tolarent mode for parseFromString than text/xml.
* NOTE: Maybe better to switch to a json string format or use a more intelligent xml encoder
* in createToolCallResultAllInOne so that extractor like this dont have to worry about special
* xml chars like & as is, in the AllInOne content. For now text/html tolarence seems ok enough.
*
* @param {string} allInOne
*/
static extractToolCallResultAllInOne(allInOne) {
const dParser = new DOMParser();
const got = dParser.parseFromString(allInOne, 'text/html');
const parseErrors = got.querySelector('parseerror')
if (parseErrors) {
console.debug("WARN:ChatMessageEx:ExtractToolCallResultAllInOne:", parseErrors.textContent.trim())
}
const id = got.querySelector('id')?.textContent.trim();
const name = got.querySelector('name')?.textContent.trim();
const content = got.querySelector('content')?.textContent.trim();
let data = {
tool_call_id: id? id : "Error",
name: name? name : "Error",
content: content? content : "Error"
}
return data
}
/**
* Set extra members into the ns object
* @param {string | number} key
@ -584,12 +530,6 @@ class SimpleChat {
if (tmsg.ns.getReasoningContent() === "") {
tmsg.ns_delete("reasoning_content")
}
if (tmsg.ns.role == Roles.Tool) {
let res = ChatMessageEx.extractToolCallResultAllInOne(tmsg.ns.getContent())
tmsg.ns.content = res.content
tmsg.ns_set_extra("tool_call_id", res.tool_call_id)
tmsg.ns_set_extra("name", res.name)
}
chat.push(tmsg.ns);
}
return chat
@ -1241,7 +1181,7 @@ class MultiChatUI {
limitedData = data.slice(0, this.me.tools.iResultMaxDataLength) + `\n\n\nALERT: Data too long, was chopped ....`
}
}
chat.add(new ChatMessageEx(new NSChatMessage(Roles.ToolTemp, ChatMessageEx.createToolCallResultAllInOne(tcid, name, limitedData))))
chat.add(new ChatMessageEx(NSChatMessage.new_tool_response(Roles.ToolTemp, tcid, name, limitedData)))
if (this.chat_show(cid)) {
if (this.me.tools.autoSecs > 0) {
this.timers.toolcallResponseSubmitClick = setTimeout(()=>{
@ -1369,13 +1309,13 @@ class MultiChatUI {
}
let toolResult = await chat.handle_toolcall(toolCallId, toolname, this.elInToolArgs.value)
if (toolResult !== undefined) {
chat.add(new ChatMessageEx(new NSChatMessage(Roles.ToolTemp, ChatMessageEx.createToolCallResultAllInOne(toolCallId, toolname, toolResult))))
chat.add(new ChatMessageEx(NSChatMessage.new_tool_response(Roles.ToolTemp, toolCallId, toolname, toolResult)))
this.chat_show(chat.chatId)
this.ui_reset_userinput(false)
} else {
this.timers.toolcallResponseTimeout = setTimeout(() => {
this.me.toolsMgr.toolcallpending_found_cleared(chat.chatId, toolCallId, 'MCUI:HandleToolRun:TimeOut')
chat.add(new ChatMessageEx(new NSChatMessage(Roles.ToolTemp, ChatMessageEx.createToolCallResultAllInOne(toolCallId, toolname, `Tool/Function call ${toolname} taking too much time, aborting...`))))
chat.add(new ChatMessageEx(NSChatMessage.new_tool_response(Roles.ToolTemp, toolCallId, toolname, `Tool/Function call ${toolname} taking too much time, aborting...`)))
this.chat_show(chat.chatId)
this.ui_reset_userinput(false)
}, this.me.tools.toolCallResponseTimeoutMS)