SimpleChatTC:NSChatMessage: Update SimpleChat to use same

Remove the load from disk support that was previously retained
wrt the old on-disk-storage format for the chat session messages.

Make a note to allow non ai server handshake roles to be maintained
wrt NSChatMessage. Add helper to cross check if a message belongs
to such temp roles or not.

Update SimpleChat class to make use of the new NSChatMessage based
needed flow.
This commit is contained in:
hanishkvc 2025-11-08 18:38:34 +05:30
parent 70b754c5a6
commit 9bd754d594
1 changed files with 45 additions and 25 deletions

View File

@ -7,13 +7,24 @@ import * as du from "./datautils.mjs";
import * as ui from "./ui.mjs" import * as ui from "./ui.mjs"
import * as mTools from "./tools.mjs" import * as mTools from "./tools.mjs"
const ROLES_TEMP_ENDSWITH = ".TEMP"
class Roles { class Roles {
static System = "system"; static System = "system";
static User = "user"; static User = "user";
static Assistant = "assistant"; static Assistant = "assistant";
static Tool = "tool"; static Tool = "tool";
static ToolTemp = "TOOL.TEMP";
// List of roles for messages that shouldnt be sent to the ai server.
// Ensure all these end with .TEMP
/** Used to identify tool call response, which has not yet been accepted and submitted by users */
static ToolTemp = `TOOL${ROLES_TEMP_ENDSWITH}`;
/**
* Used to maintain errors that wont be normally communicated back to ai server
* like error got during handshake with ai server or so.
*/
static ErrorTemp = `ERROR${ROLES_TEMP_ENDSWITH}`;
} }
@ -69,6 +80,7 @@ class NSChatMessage {
return "" return ""
} }
/** Returns trimmed reasoning content, else empty string */
getReasoningContent() { getReasoningContent() {
if (this.reasoning_content) { if (this.reasoning_content) {
return this.reasoning_content.trim() return this.reasoning_content.trim()
@ -176,18 +188,29 @@ class NSChatMessage {
return false return false
} }
has_role_temp() {
if (this.role.endsWith(ROLES_TEMP_ENDSWITH)) {
return true;
}
return false;
}
} }
class ChatMessageEx { class ChatMessageEx {
/** /**
* Represent a Message in the Chat * Represent a Message in the Chat.
* @param {NSChatMessage} nsChatMsg * @param {NSChatMessage|undefined} nsChatMsg - will create a default NSChatMessage instance, if undefined
* @param {string|undefined} trimmedContent * @param {string|undefined} trimmedContent
*/ */
constructor(nsChatMsg, trimmedContent=undefined) { constructor(nsChatMsg=undefined, trimmedContent=undefined) {
this.ns = nsChatMsg if (nsChatMsg) {
this.ns = nsChatMsg
} else {
this.ns = new NSChatMessage()
}
this.trimmedContent = trimmedContent; this.trimmedContent = trimmedContent;
} }
@ -476,13 +499,7 @@ class SimpleChat {
this.iLastSys = ods.iLastSys; this.iLastSys = ods.iLastSys;
this.xchat = []; this.xchat = [];
for (const cur of ods.xchat) { for (const cur of ods.xchat) {
if (cur.ns == undefined) { // this relates to the old on-disk-structure/format, needs to be removed later this.xchat.push(new ChatMessageEx(new NSChatMessage(cur.ns.role, cur.ns.content, cur.ns.reasoning_content, cur.ns.tool_calls), cur.trimmedContent))
/** @typedef {{role: string, content: string}} OldChatMessage */
let tcur = /** @type {OldChatMessage} */(/** @type {unknown} */(cur));
this.xchat.push(new ChatMessageEx(tcur.role, tcur.content))
} else {
this.xchat.push(new ChatMessageEx(cur.ns.role, cur.ns.content, cur.ns.reasoning_content, cur.ns.tool_calls, cur.trimmedContent))
}
} }
} }
@ -505,7 +522,7 @@ class SimpleChat {
/** @type {ChatMessages} */ /** @type {ChatMessages} */
let rchat = []; let rchat = [];
let sysMsg = this.get_system_latest(); let sysMsg = this.get_system_latest();
if (sysMsg.ns.content.length != 0) { if (sysMsg.ns.getContent().length != 0) {
rchat.push(sysMsg) rchat.push(sysMsg)
} }
let iUserCnt = 0; let iUserCnt = 0;
@ -540,21 +557,22 @@ class SimpleChat {
let xchat = this.recent_chat(iRecentUserMsgCnt); let xchat = this.recent_chat(iRecentUserMsgCnt);
let chat = []; let chat = [];
for (const msg of xchat) { for (const msg of xchat) {
if (msg.ns.role == Roles.ToolTemp) { if (msg.ns.has_role_temp()) {
// Skip (temp) tool response which has not yet been accepted by user // Skip Temp Role messages
// In future need to check that it is the last message // ex: tool response which has not yet been accepted by user
// In future need to check that non accepted tool response is the last message
// and not something in between, which shouldnt occur normally. // and not something in between, which shouldnt occur normally.
continue continue
} }
let tmsg = ChatMessageEx.newFrom(msg); let tmsg = ChatMessageEx.newFrom(msg);
if (!tmsg.has_toolcall()) { if (!tmsg.ns.has_toolcalls()) {
tmsg.ns_delete("tool_calls") tmsg.ns_delete("tool_calls")
} }
if (tmsg.ns.reasoning_content.trim() === "") { if (tmsg.ns.getReasoningContent() === "") {
tmsg.ns_delete("reasoning_content") tmsg.ns_delete("reasoning_content")
} }
if (tmsg.ns.role == Roles.Tool) { if (tmsg.ns.role == Roles.Tool) {
let res = ChatMessageEx.extractToolCallResultAllInOne(tmsg.ns.content) let res = ChatMessageEx.extractToolCallResultAllInOne(tmsg.ns.getContent())
tmsg.ns.content = res.content tmsg.ns.content = res.content
tmsg.ns_set_extra("tool_call_id", res.tool_call_id) tmsg.ns_set_extra("tool_call_id", res.tool_call_id)
tmsg.ns_set_extra("name", res.name) tmsg.ns_set_extra("name", res.name)
@ -706,12 +724,12 @@ class SimpleChat {
} }
if (this.iLastSys < 0) { if (this.iLastSys < 0) {
return this.add(new ChatMessageEx(Roles.System, sysPrompt)); return this.add(new ChatMessageEx(new NSChatMessage(Roles.System, sysPrompt)));
} }
let lastSys = this.xchat[this.iLastSys].ns.content; let lastSys = this.xchat[this.iLastSys].ns.content;
if (lastSys !== sysPrompt) { if (lastSys !== sysPrompt) {
return this.add(new ChatMessageEx(Roles.System, sysPrompt)); return this.add(new ChatMessageEx(new NSChatMessage(Roles.System, sysPrompt)));
} }
return false; return false;
} }
@ -721,7 +739,7 @@ class SimpleChat {
*/ */
get_system_latest() { get_system_latest() {
if (this.iLastSys == -1) { if (this.iLastSys == -1) {
return new ChatMessageEx(Roles.System); return new ChatMessageEx(new NSChatMessage(Roles.System));
} }
return this.xchat[this.iLastSys]; return this.xchat[this.iLastSys];
} }
@ -787,7 +805,7 @@ class SimpleChat {
async handle_response_oneshot(resp, apiEP) { async handle_response_oneshot(resp, apiEP) {
let respBody = await resp.json(); let respBody = await resp.json();
console.debug(`DBUG:SimpleChat:SC:${this.chatId}:HandleUserSubmit:RespBody:${JSON.stringify(respBody)}`); console.debug(`DBUG:SimpleChat:SC:${this.chatId}:HandleUserSubmit:RespBody:${JSON.stringify(respBody)}`);
let cm = new ChatMessageEx(Roles.Assistant) let cm = new ChatMessageEx(new NSChatMessage(Roles.Assistant))
cm.update_oneshot(respBody, apiEP) cm.update_oneshot(respBody, apiEP)
return cm return cm
} }
@ -819,8 +837,10 @@ class SimpleChat {
} }
if (this.me.chatProps.bTrimGarbage) { if (this.me.chatProps.bTrimGarbage) {
let origMsg = theResp.ns.content; let origMsg = theResp.ns.content;
theResp.ns.content = du.trim_garbage_at_end(origMsg); if (origMsg) {
theResp.trimmedContent = origMsg.substring(theResp.ns.content.length); theResp.ns.content = du.trim_garbage_at_end(origMsg);
theResp.trimmedContent = origMsg.substring(theResp.ns.content.length);
}
} }
theResp.ns.role = Roles.Assistant; theResp.ns.role = Roles.Assistant;
this.add(theResp); this.add(theResp);