From 08d165427b2ad43ac8a05dc9de7f4e795cea8841 Mon Sep 17 00:00:00 2001 From: hanishkvc Date: Sat, 22 Nov 2025 00:37:10 +0530 Subject: [PATCH] SimpleChatTCRV:DivStream: Allow predetermined div for streamd resp This will help ensure that switching sessions etal wont lead to loosing handle to sessions that one exited from, with additional logic to be added later. Rather I am thinking, maybe it may make more sense to dynamically pick the currently active chat sessions latestResponse field to inturn show into a div element at end of the elDivChat. On thinking further, maybe making elDivStream into elDivStreams with seperate div for each session is the better thing, as the SC.handle_response will be able to update its corresponding div and the session switch can link the currently visible session's div to be mapped to elDivChat. NOTE: Rather this commit in itself should take care of the current normal flow, which wont allow user to switch sessions when a tool call or handshake with server is pending. However in future if I want the ui to be freely switched independent of if a tool call or server handshake is active, then I need the logic mentioned in previous para. --- tools/server/public_simplechat/simplechat.js | 28 +++++++++++++------- tools/server/public_simplechat/toolai.mjs | 2 +- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/tools/server/public_simplechat/simplechat.js b/tools/server/public_simplechat/simplechat.js index fa6abc7a82..948fb92965 100644 --- a/tools/server/public_simplechat/simplechat.js +++ b/tools/server/public_simplechat/simplechat.js @@ -874,13 +874,13 @@ class SimpleChat { * Handle the multipart response from server/ai-model * @param {Response} resp * @param {string} apiEP - * @param {HTMLDivElement} elDiv + * @param {HTMLDivElement} elDivStream */ - async handle_response_multipart(resp, apiEP, elDiv) { + async handle_response_multipart(resp, apiEP, elDivStream) { if (!resp.body) { throw Error("ERRR:SimpleChat:SC:HandleResponseMultiPart:No body..."); } - let elP = ui.el_create_append_p("", elDiv); + let elP = ui.el_create_append_p("", elDivStream); elP.classList.add("chat-message-content-live") try { let tdUtf8 = new TextDecoder("utf-8"); @@ -944,14 +944,15 @@ class SimpleChat { * Also take care of the optional garbage trimming. * @param {Response} resp * @param {string} apiEP - * @param {HTMLDivElement} elDiv + * @param {HTMLDivElement} elDivStream - place the ai server response as its being generated. */ - async handle_response(resp, apiEP, elDiv) { + async handle_response(resp, apiEP, elDivStream) { let theResp = null; try { if (this.handshakeProps.chatPropsStream) { - theResp = await this.handle_response_multipart(resp, apiEP, elDiv); + theResp = await this.handle_response_multipart(resp, apiEP, elDivStream); this.latestResponse.clear(); + elDivStream.replaceChildren() } else { theResp = await this.handle_response_oneshot(resp, apiEP); } @@ -960,6 +961,7 @@ class SimpleChat { theResp.ns.role = Roles.Assistant; this.add(theResp); this.latestResponse.clear(); + elDivStream.replaceChildren() throw error; } if (this.me.chatProps.bTrimGarbage) { @@ -979,9 +981,9 @@ class SimpleChat { * @param {string} baseURL * @param {string} apiEP * @param {SCHandshakeProps} hsProps - * @param {HTMLDivElement} elDivChat - used to show chat response as it is being generated/recieved in streaming mode + * @param {HTMLDivElement} elDivStream - used to show chat response as it is being generated/recieved in streaming mode */ - async handle_chat_hs(baseURL, apiEP, hsProps, elDivChat) { + async handle_chat_hs(baseURL, apiEP, hsProps, elDivStream) { class ChatHSError extends Error { constructor(/** @type {string} */message) { super(message); @@ -1007,7 +1009,7 @@ class SimpleChat { throw new ChatHSError(`HandleChatHS:GotResponse:NotOk:${resp.status}:${resp.statusText}:${respBody}`); } - return this.handle_response(resp, apiEP, elDivChat); + return this.handle_response(resp, apiEP, elDivStream); } /** @@ -1137,6 +1139,10 @@ class MultiChatUI { this.elInFileX.elB.title = "Image" this.elBtnUser.parentElement?.appendChild(this.elInFileX.elB) + // other ui elements + this.elDivStream = document.createElement("div") + this.elDivStream.id = "DivStream" + this.validate_element(this.elInSystem, "system-in"); this.validate_element(this.elDivChat, "chat-div"); this.validate_element(this.elInUser, "user-in"); @@ -1432,6 +1438,7 @@ class MultiChatUI { if (bClear) { this.elDivChat.replaceChildren(); this.ui_userinput_reset() + this.elDivStream.replaceChildren() } this.ui_reset_toolcall_as_needed(new ChatMessageEx()); this.elLastChatMessage = null @@ -1444,6 +1451,7 @@ class MultiChatUI { } this.show_message(this.elDivChat, x, iFromLast, nextMsg) } + this.elDivChat.appendChild(this.elDivStream) if (this.elLastChatMessage != null) { this.scroll_el_into_view(this.elLastChatMessage) } else { @@ -1750,7 +1758,7 @@ class MultiChatUI { this.elInUser.disabled = true; try { - let theResp = await chat.handle_chat_hs(this.me.baseURL, apiEP, { chatPropsStream: this.me.chatProps.stream, toolsEnabled: this.me.tools.enabled }, this.elDivChat) + let theResp = await chat.handle_chat_hs(this.me.baseURL, apiEP, { chatPropsStream: this.me.chatProps.stream, toolsEnabled: this.me.tools.enabled }, this.elDivStream) if (chatId == this.curChatId) { this.chat_uirefresh(chatId); if ((theResp.trimmedContent) && (theResp.trimmedContent.length > 0)) { diff --git a/tools/server/public_simplechat/toolai.mjs b/tools/server/public_simplechat/toolai.mjs index 5bdd9c9427..93553277fa 100644 --- a/tools/server/public_simplechat/toolai.mjs +++ b/tools/server/public_simplechat/toolai.mjs @@ -94,7 +94,7 @@ function externalai_run(chatid, toolcallid, toolname, obj) { sc.add_system_anytime(obj['system_prompt'], 'TC:ExternalAI') sc.add(new mChatMagic.ChatMessageEx(new mChatMagic.NSChatMessage(mChatMagic.Roles.User, obj['user_message']))) - sc.handle_chat_hs(gMe.baseURL, mChatMagic.ApiEP.Type.Chat, { chatPropsStream: gMe.chatProps.stream, toolsEnabled: false }, gMe.multiChat.elDivChat).then((resp)=>{ + sc.handle_chat_hs(gMe.baseURL, mChatMagic.ApiEP.Type.Chat, { chatPropsStream: gMe.chatProps.stream, toolsEnabled: false }, gMe.multiChat.elDivStream).then((resp)=>{ gMe.toolsMgr.workers_postmessage_for_main(gMe.toolsMgr.workers.js, chatid, toolcallid, toolname, resp.content_equiv()); }).catch((err)=>{ gMe.toolsMgr.workers_postmessage_for_main(gMe.toolsMgr.workers.js, chatid, toolcallid, toolname, `Error:TC:ExternalAI:${err}`);