From d56a4a06b04ceeeb6a7e61a596c9430eb31f7577 Mon Sep 17 00:00:00 2001 From: hanishkvc Date: Wed, 5 Nov 2025 14:42:39 +0530 Subject: [PATCH] SimpleChatTC:Cleanup:Make main chat related classes importable Have main classes defined independent of and away from runtime flow Move out the entry point including runtime instantiation of the core Me class (which inturn brings other class instances as neede) into its own main.js file. With this one should be able to import simplechat.js into other files, where one might need the SimpleChat or MultiChat or Me class definitions. --- tools/server/public_simplechat/index.html | 3 +- tools/server/public_simplechat/main.js | 33 ++++++ tools/server/public_simplechat/simplechat.js | 104 ++++++++----------- 3 files changed, 81 insertions(+), 59 deletions(-) create mode 100644 tools/server/public_simplechat/main.js diff --git a/tools/server/public_simplechat/index.html b/tools/server/public_simplechat/index.html index 797530d72f..d34a80dfb8 100644 --- a/tools/server/public_simplechat/index.html +++ b/tools/server/public_simplechat/index.html @@ -11,12 +11,13 @@ - + diff --git a/tools/server/public_simplechat/main.js b/tools/server/public_simplechat/main.js new file mode 100644 index 0000000000..05efb4d061 --- /dev/null +++ b/tools/server/public_simplechat/main.js @@ -0,0 +1,33 @@ +// @ts-check +// A simple completions and chat/completions test related web front end logic +// by Humans for All + + +import * as mChatMagic from './simplechat.js' +import * as tools from "./tools.mjs" +import * as du from "./datautils.mjs"; + + + +/** @type {mChatMagic.Me} */ +let gMe; + +function startme() { + console.log("INFO:SimpleChat:StartMe:Starting..."); + gMe = new mChatMagic.Me(); + gMe.debug_disable(); + // @ts-ignore + document["gMe"] = gMe; + // @ts-ignore + document["du"] = du; + // @ts-ignore + document["tools"] = tools; + tools.init().then((toolNames)=>gMe.tools.toolNames=toolNames).then(()=>gMe.multiChat.chat_show(gMe.multiChat.curChatId)) + for (let cid of gMe.defaultChatIds) { + gMe.multiChat.new_chat_session(cid); + } + gMe.multiChat.setup_ui(gMe.defaultChatIds[0], true); + gMe.multiChat.show_sessions(); +} + +document.addEventListener("DOMContentLoaded", startme); diff --git a/tools/server/public_simplechat/simplechat.js b/tools/server/public_simplechat/simplechat.js index 17c27579ff..da4766ef43 100644 --- a/tools/server/public_simplechat/simplechat.js +++ b/tools/server/public_simplechat/simplechat.js @@ -1,5 +1,6 @@ // @ts-check -// A simple completions and chat/completions test related web front end logic +// Core classes which provide a simple implementation of handshake with ai server's completions and chat/completions endpoints +// as well as related web front end logic for basic usage and testing. // by Humans for All import * as du from "./datautils.mjs"; @@ -277,7 +278,10 @@ class ChatMessageEx { } -function usage_note() { +/** + * @param {number} iRecentUserMsgCnt + */ +function usage_note(iRecentUserMsgCnt) { let sUsageNote = `
Usage Note @@ -293,7 +297,7 @@ function usage_note() {
  • If ai assistant requests a tool call, verify same before triggering.
  • submit tool response placed into user query/response text area
  • -
  • ContextWindow = [System, Last[${gMe.chatProps.iRecentUserMsgCnt-1}] User Query/Resp, Cur Query].
  • +
  • ContextWindow = [System, Last[${iRecentUserMsgCnt}] User Query/Resp, Cur Query].
  • @@ -311,8 +315,9 @@ class SimpleChat { /** * @param {string} chatId + * @param {Me} me */ - constructor(chatId) { + constructor(chatId, me) { this.chatId = chatId; /** * Maintain in a form suitable for common LLM web service chat/completions' messages entry @@ -321,6 +326,7 @@ class SimpleChat { this.xchat = []; this.iLastSys = -1; this.latestResponse = new ChatMessageEx(); + this.me = me; } clear() { @@ -485,14 +491,14 @@ class SimpleChat { /** * Setup the fetch headers. - * It picks the headers from gMe.headers. + * It picks the headers from this.me.headers. * It inserts Authorization only if its non-empty. * @param {string} apiEP */ fetch_headers(apiEP) { let headers = new Headers(); - for(let k in gMe.headers) { - let v = gMe.headers[k]; + for(let k in this.me.headers) { + let v = this.me.headers[k]; if ((k == "Authorization") && (v.trim() == "")) { continue; } @@ -509,13 +515,13 @@ class SimpleChat { * @param {Object} obj */ request_jsonstr_extend(obj) { - for(let k in gMe.apiRequestOptions) { - obj[k] = gMe.apiRequestOptions[k]; + for(let k in this.me.apiRequestOptions) { + obj[k] = this.me.apiRequestOptions[k]; } - if (gMe.chatProps.stream) { + if (this.me.chatProps.stream) { obj["stream"] = true; } - if (gMe.tools.enabled) { + if (this.me.tools.enabled) { obj["tools"] = tools.meta(); } return JSON.stringify(obj); @@ -526,7 +532,7 @@ class SimpleChat { */ request_messages_jsonstr() { let req = { - messages: this.recent_chat_ns(gMe.chatProps.iRecentUserMsgCnt), + messages: this.recent_chat_ns(this.me.chatProps.iRecentUserMsgCnt), } return this.request_jsonstr_extend(req); } @@ -538,7 +544,7 @@ class SimpleChat { request_prompt_jsonstr(bInsertStandardRolePrefix) { let prompt = ""; let iCnt = 0; - for(const msg of this.recent_chat(gMe.chatProps.iRecentUserMsgCnt)) { + for(const msg of this.recent_chat(this.me.chatProps.iRecentUserMsgCnt)) { iCnt += 1; if (iCnt > 1) { prompt += "\n"; @@ -562,7 +568,7 @@ class SimpleChat { if (apiEP == ApiEP.Type.Chat) { return this.request_messages_jsonstr(); } else { - return this.request_prompt_jsonstr(gMe.chatProps.bCompletionInsertStandardRolePrefix); + return this.request_prompt_jsonstr(this.me.chatProps.bCompletionInsertStandardRolePrefix); } } @@ -676,7 +682,7 @@ class SimpleChat { */ async handle_response(resp, apiEP, elDiv) { let theResp = null; - if (gMe.chatProps.stream) { + if (this.me.chatProps.stream) { try { theResp = await this.handle_response_multipart(resp, apiEP, elDiv); this.latestResponse.clear(); @@ -690,7 +696,7 @@ class SimpleChat { } else { theResp = await this.handle_response_oneshot(resp, apiEP); } - if (gMe.chatProps.bTrimGarbage) { + if (this.me.chatProps.bTrimGarbage) { let origMsg = theResp.ns.content; theResp.ns.content = du.trim_garbage_at_end(origMsg); theResp.trimmedContent = origMsg.substring(theResp.ns.content.length); @@ -756,7 +762,11 @@ class SimpleChat { class MultiChatUI { - constructor() { + /** + * @param {Me} me + */ + constructor(me) { + this.me = me /** @type {Object} */ this.simpleChats = {}; /** @type {string} */ @@ -842,10 +852,10 @@ class MultiChatUI { 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 - if ((gMe.tools.autoSecs > 0) && (bAuto)) { + if ((this.me.tools.autoSecs > 0) && (bAuto)) { this.timers.toolcallTriggerClick = setTimeout(()=>{ this.elBtnTool.click() - }, gMe.tools.autoSecs*this.TimePeriods.ToolCallAutoSecsTimeUnit) + }, this.me.tools.autoSecs*this.TimePeriods.ToolCallAutoSecsTimeUnit) } } else { this.elDivTool.hidden = true @@ -992,7 +1002,7 @@ class MultiChatUI { this.ui_reset_toolcall_as_needed(new ChatMessageEx()); } this.elLastChatMessage = null - let chatToShow = chat.recent_chat(gMe.chatProps.iRecentUserMsgCnt); + let chatToShow = chat.recent_chat(this.me.chatProps.iRecentUserMsgCnt); for(const [i, x] of chatToShow.entries()) { let iFromLast = (chatToShow.length - 1)-i let nextMsg = undefined @@ -1005,9 +1015,9 @@ class MultiChatUI { /** @type{HTMLElement} */(this.elLastChatMessage).scrollIntoView(false); // Stupid ts-check js-doc intersection ??? } else { if (bClear) { - this.elDivChat.innerHTML = usage_note(); - gMe.setup_load(this.elDivChat, chat); - gMe.show_info(this.elDivChat, bShowInfoAll); + this.elDivChat.innerHTML = usage_note(this.me.chatProps.iRecentUserMsgCnt-1); + this.me.setup_load(this.elDivChat, chat); + this.me.show_info(this.elDivChat, bShowInfoAll); } } return true @@ -1030,7 +1040,7 @@ class MultiChatUI { this.elBtnSettings.addEventListener("click", (ev)=>{ this.elDivChat.replaceChildren(); - gMe.show_settings(this.elDivChat); + this.me.show_settings(this.elDivChat); }); this.elBtnClearChat.addEventListener("click", (ev)=>{ this.simpleChats[this.curChatId].clear() @@ -1043,7 +1053,7 @@ class MultiChatUI { if (this.elInUser.disabled) { return; } - this.handle_user_submit(this.curChatId, gMe.chatProps.apiEP).catch((/** @type{Error} */reason)=>{ + this.handle_user_submit(this.curChatId, this.me.chatProps.apiEP).catch((/** @type{Error} */reason)=>{ let msg = `ERRR:SimpleChat\nMCUI:HandleUserSubmit:${this.curChatId}\n${reason.name}:${reason.message}`; console.error(msg.replace("\n", ":")); alert(msg); @@ -1065,17 +1075,17 @@ class MultiChatUI { this.timers.toolcallResponseTimeout = undefined let chat = this.simpleChats[cid]; let limitedData = data - if (gMe.tools.iResultMaxDataLength > 0) { - if (data.length > gMe.tools.iResultMaxDataLength) { - limitedData = data.slice(0, gMe.tools.iResultMaxDataLength) + `\n\n\nALERT: Data too long, was chopped ....` + if (this.me.tools.iResultMaxDataLength > 0) { + if (data.length > this.me.tools.iResultMaxDataLength) { + limitedData = data.slice(0, this.me.tools.iResultMaxDataLength) + `\n\n\nALERT: Data too long, was chopped ....` } } chat.add(new ChatMessageEx(Roles.ToolTemp, ChatMessageEx.createToolCallResultAllInOne(tcid, name, limitedData))) if (this.chat_show(cid)) { - if (gMe.tools.autoSecs > 0) { + if (this.me.tools.autoSecs > 0) { this.timers.toolcallResponseSubmitClick = setTimeout(()=>{ this.elBtnUser.click() - }, gMe.tools.autoSecs*this.TimePeriods.ToolCallAutoSecsTimeUnit) + }, this.me.tools.autoSecs*this.TimePeriods.ToolCallAutoSecsTimeUnit) } } this.ui_reset_userinput(false) @@ -1113,7 +1123,7 @@ class MultiChatUI { * @param {boolean} bSwitchSession */ new_chat_session(chatId, bSwitchSession=false) { - this.simpleChats[chatId] = new SimpleChat(chatId); + this.simpleChats[chatId] = new SimpleChat(chatId, this.me); if (bSwitchSession) { this.handle_session_switch(chatId); } @@ -1143,7 +1153,7 @@ class MultiChatUI { // So if user wants to simulate a multi-chat based completion query, // they will have to enter the full thing, as a suitable multiline // user input/query. - if ((apiEP == ApiEP.Type.Completion) && (gMe.chatProps.bCompletionFreshChatAlways)) { + if ((apiEP == ApiEP.Type.Completion) && (this.me.chatProps.bCompletionFreshChatAlways)) { chat.clear(); } @@ -1167,7 +1177,7 @@ class MultiChatUI { this.elInUser.disabled = true; try { - let theResp = await chat.handle_chat_hs(gMe.baseURL, apiEP, this.elDivChat) + let theResp = await chat.handle_chat_hs(this.me.baseURL, apiEP, this.elDivChat) if (chatId == this.curChatId) { this.chat_show(chatId); if (theResp.trimmedContent.length > 0) { @@ -1206,7 +1216,7 @@ class MultiChatUI { chat.add(new ChatMessageEx(Roles.ToolTemp, ChatMessageEx.createToolCallResultAllInOne(toolCallId, toolname, `Tool/Function call ${toolname} taking too much time, aborting...`))) this.chat_show(chat.chatId) this.ui_reset_userinput(false) - }, gMe.tools.toolCallResponseTimeoutMS) + }, this.me.tools.toolCallResponseTimeoutMS) } } @@ -1312,12 +1322,12 @@ const SearchURLS = { } -class Me { +export class Me { constructor() { this.baseURL = "http://127.0.0.1:8080"; this.defaultChatIds = [ "Default", "Other" ]; - this.multiChat = new MultiChatUI(); + this.multiChat = new MultiChatUI(this); this.tools = { enabled: true, proxyUrl: "http://127.0.0.1:3128", @@ -1462,25 +1472,3 @@ class Me { } -/** @type {Me} */ -let gMe; - -function startme() { - console.log("INFO:SimpleChat:StartMe:Starting..."); - gMe = new Me(); - gMe.debug_disable(); - // @ts-ignore - document["gMe"] = gMe; - // @ts-ignore - document["du"] = du; - // @ts-ignore - document["tools"] = tools; - tools.init().then((toolNames)=>gMe.tools.toolNames=toolNames).then(()=>gMe.multiChat.chat_show(gMe.multiChat.curChatId)) - for (let cid of gMe.defaultChatIds) { - gMe.multiChat.new_chat_session(cid); - } - gMe.multiChat.setup_ui(gMe.defaultChatIds[0], true); - gMe.multiChat.show_sessions(); -} - -document.addEventListener("DOMContentLoaded", startme);