SimpleChatTCRV:Config:Tools: Adapt for SimpleChat with Config

Split init from setup wrt Tools. Init is called when the program
is started, while setup is called when ever a session is created
and or when ever any tools setup related config is changed (like
proxyUrl getting modified or so).

This allows one to modify tool related config and inturn update
the tools related states wrt same (like wrt the proxyUrl, bcas
the simpleproxy server will change, one needs to cross check which
and all web services are available with the new simpleproxy server
and so and so ...)

To help with the above, now ToolsManager maintains multiple tool
call switches, where there will be a tc_switch wrt each chat
session.

All tool related codes, where needed have been updated to work
with the chat session for which they have been called, instead
of any global states.

In future, this can enable one to allow user to enable or disable
the tool calls exposed in each of the chat sessions,

This immidiately needed toolweb and toolai to account for chat
session as needed.

In future, if we want to keep the data store isolated across
chat sessions, then this can be useful, in that it can create
uniq store names for each of the chat sessions, isntead of
sharing a common store name across all sessions.

The chat session creation as well as tool init/setup related
flows have been updated to chain through or account for the
promises as needed
This commit is contained in:
hanishkvc 2025-11-23 15:04:51 +05:30
parent 9e65261486
commit 8e39afb05e
7 changed files with 140 additions and 72 deletions

View File

@ -26,10 +26,12 @@ function startme() {
gMe = new mChatMagic.Me();
gMe.debug_disable();
devel_expose()
gMe.toolsMgr.init(gMe).then(()=>{
gMe.toolsMgr.init(gMe).then(async ()=>{
let sL = []
for (let cid of gMe.defaultChatIds) {
gMe.multiChat.new_chat_session(cid);
sL.push(gMe.multiChat.new_chat_session(cid));
}
await Promise.allSettled(sL)
gMe.multiChat.setup_ui(gMe.defaultChatIds[0]);
gMe.multiChat.show_sessions();
gMe.multiChat.handle_session_switch(gMe.multiChat.curChatId)

View File

@ -525,10 +525,13 @@ class ELDivStream {
export class SimpleChat {
/**
* Create an instance of SimpleChat
* * dont forget to call setup
* @param {string} chatId
* @param {Config} cfg
* @param {mTools.ToolsManager} toolsMgr
*/
constructor(chatId, cfg) {
constructor(chatId, cfg, toolsMgr) {
this.chatId = chatId;
/**
* Maintain in a form suitable for common LLM web service chat/completions' messages entry
@ -538,6 +541,7 @@ export class SimpleChat {
this.iLastSys = -1;
this.latestResponse = new ChatMessageEx();
this.cfg = Config.clone(cfg);
this.toolsMgr = toolsMgr
}
clear() {
@ -546,6 +550,10 @@ export class SimpleChat {
this.latestResponse = new ChatMessageEx();
}
setup() {
return this.toolsMgr.setup(this.chatId)
}
/**
* Given a string, gives the equivalent simplified string which could be used as chatId.
* * Converts the very 1st char into upper case.
@ -835,7 +843,7 @@ export class SimpleChat {
obj["stream"] = true;
}
if (this.cfg.tools.enabled) {
obj["tools"] = this.me.toolsMgr.meta();
obj["tools"] = this.toolsMgr.meta(this.chatId);
}
return JSON.stringify(obj);
}
@ -1072,9 +1080,9 @@ export class SimpleChat {
return "Tool/Function call name not specified"
}
try {
return await this.me.toolsMgr.tool_call(this.chatId, toolcallid, toolname, toolargs)
return await this.toolsMgr.tool_call(this.chatId, toolcallid, toolname, toolargs)
} catch (/** @type {any} */error) {
this.me.toolsMgr.toolcallpending_found_cleared(this.chatId, toolcallid, 'SC:HandleToolCall:Exc')
this.toolsMgr.toolcallpending_found_cleared(this.chatId, toolcallid, 'SC:HandleToolCall:Exc')
return `Tool/Function call raised an exception:${error.name}:${error.message}`
}
}
@ -1730,16 +1738,16 @@ class MultiChatUI {
* @param {string} chatId
* @param {boolean} bSwitchSession
*/
new_chat_session(chatId, bSwitchSession=false) {
this.simpleChats[chatId] = new SimpleChat(chatId, this.me.defaultCfg);
async new_chat_session(chatId, bSwitchSession=false) {
this.simpleChats[chatId] = new SimpleChat(chatId, this.me.defaultCfg, this.me.toolsMgr);
await this.simpleChats[chatId].setup();
this.elDivStreams[chatId] = new ELDivStream(chatId);
this.elDivStreams[chatId].clear()
this.elDivStreams[chatId].clear();
if (bSwitchSession) {
this.handle_session_switch(chatId);
}
}
/**
* Handle user query submit request, wrt specified chat session.
*
@ -1874,9 +1882,10 @@ class MultiChatUI {
return;
}
chatIdGot = SimpleChat.ChatIdClean(chatIdGot)
this.new_chat_session(chatIdGot, true);
this.create_session_btn(elDiv, chatIdGot);
ui.el_children_config_class(elDiv, chatIdGot, "session-selected", "");
this.new_chat_session(chatIdGot, true).then(()=>{
this.create_session_btn(elDiv, chatIdGot);
ui.el_children_config_class(elDiv, chatIdGot, "session-selected", "");
})
},"NewChat", "+ new");
btnNew.title = "start a new chat session"
elDiv.appendChild(btnNew);

View File

@ -6,6 +6,7 @@
//
import * as mChatMagic from './simplechat.js'
import * as mToolsMgr from './tools.mjs'
let gMe = /** @type{mChatMagic.Me} */(/** @type {unknown} */(null));
@ -104,9 +105,9 @@ function externalai_run(chatid, toolcallid, toolname, obj) {
/**
* @type {Object<string, Object<string, any>>}
* @type {mToolsMgr.TCSwitch}
*/
export let tc_switch = {
let tc_switch = {
"external_ai": {
"handler": externalai_run,
"meta": externalai_meta,
@ -125,3 +126,11 @@ export let tc_switch = {
export async function init(me) {
gMe = me
}
/**
* @param {string} chatId
*/
export async function setup(chatId) {
return tc_switch;
}

View File

@ -6,6 +6,7 @@
//
import * as mChatMagic from './simplechat.js'
import * as mToolsMgr from './tools.mjs'
let gMe = /** @type{mChatMagic.Me} */(/** @type {unknown} */(null));
@ -101,9 +102,9 @@ function dsops_run(chatid, toolcallid, toolname, obj) {
/**
* @type {Object<string, Object<string, any>>}
* @type {mToolsMgr.TCSwitch}
*/
export let tc_switch = {
let tc_switch = {
"data_store_get": {
"handler": dsops_run,
"meta": dsget_meta,
@ -135,3 +136,11 @@ export let tc_switch = {
export async function init(me) {
gMe = me
}
/**
* @param {string} chatId
*/
export async function setup(chatId) {
return tc_switch;
}

View File

@ -8,6 +8,7 @@
//
import * as mChatMagic from './simplechat.js'
import * as mToolsMgr from './tools.mjs'
let gMe = /** @type{mChatMagic.Me} */(/** @type {unknown} */(null));
@ -152,9 +153,9 @@ function calc_run(chatid, toolcallid, toolname, obj) {
/**
* @type {Object<string, Object<string, any>>}
* @type {mToolsMgr.TCSwitch}
*/
export let tc_switch = {
let tc_switch = {
"sys_date_time": {
"handler": sysdatetime_run,
"meta": sysdatetime_meta,
@ -181,3 +182,11 @@ export let tc_switch = {
export async function init(me) {
gMe = me
}
/**
* @param {string} chatId
*/
export async function setup(chatId) {
return tc_switch;
}

View File

@ -12,15 +12,17 @@ import * as tai from './toolai.mjs'
import * as mChatMagic from './simplechat.js'
/** @typedef {Object<string,Object<string,any>>} TCSwitch */
export class ToolsManager {
constructor() {
/**
* Maintain currently available tool/function calls
* @type {Object<string,Object<string,any>>}
* @type {Object<string, TCSwitch>}
*/
this.tc_switch = {}
this.tc_switchs = {}
this.workers = {
js: /** @type {Worker} */(/** @type {unknown} */(undefined)),
@ -46,43 +48,56 @@ export class ToolsManager {
* @param {mChatMagic.Me} me
*/
async init(me) {
this.me = me
this.setup_workers();
/**
* @type {string[]}
*/
me.defaultCfg.tools.toolNames = []
await tjs.init(me).then(()=>{
for (const key in tjs.tc_switch) {
this.tc_switch[key] = tjs.tc_switch[key]
me.defaultCfg.tools.toolNames.push(key)
let tcM = []
tcM.push(tjs.init(me))
tcM.push(tdb.init(me))
tcM.push(tai.init(me))
tcM.push(tweb.init(me))
return Promise.all(tcM)
}
/**
* @param {string} chatId
*/
async setup(chatId) {
this.tc_switchs[chatId] = {}
let chat = this.me?.multiChat.simpleChats[chatId]
await tjs.setup(chatId).then((tcs)=>{
for (const key in tcs) {
this.tc_switchs[chatId][key] = tcs[key]
chat?.cfg.tools.toolNames.push(key)
}
})
await tdb.init(me).then(()=>{
for (const key in tdb.tc_switch) {
this.tc_switch[key] = tdb.tc_switch[key]
me.defaultCfg.tools.toolNames.push(key)
await tdb.setup(chatId).then((tcs)=>{
for (const key in tcs) {
this.tc_switchs[chatId][key] = tcs[key]
chat?.cfg.tools.toolNames.push(key)
}
})
await tai.init(me).then(()=>{
for (const key in tai.tc_switch) {
this.tc_switch[key] = tai.tc_switch[key]
me.defaultCfg.tools.toolNames.push(key)
await tai.setup(chatId).then((tcs)=>{
for (const key in tcs) {
this.tc_switchs[chatId][key] = tcs[key]
chat?.cfg.tools.toolNames.push(key)
}
})
await tweb.setup(chatId).then((tcs)=>{
for (const key in tcs) {
this.tc_switchs[chatId][key] = tcs[key]
chat?.cfg.tools.toolNames.push(key)
}
})
let tNs = await tweb.init(me)
for (const key in tNs) {
this.tc_switch[key] = tNs[key]
me.defaultCfg.tools.toolNames.push(key)
}
}
/**
* Prepare the tools meta data that can be passed to the ai server.
* @param {string} chatId
*/
meta() {
meta(chatId) {
let tools = []
for (const key in this.tc_switch) {
tools.push(this.tc_switch[key]["meta"])
for (const key in this.tc_switchs[chatId]) {
tools.push(this.tc_switchs[chatId][key]["meta"])
}
return tools
}
@ -134,11 +149,11 @@ export class ToolsManager {
* @param {string} toolargs
*/
async tool_call(chatid, toolcallid, toolname, toolargs) {
for (const fn in this.tc_switch) {
for (const fn in this.tc_switchs[chatid]) {
if (fn == toolname) {
try {
this.toolcallpending_add(chatid, toolcallid);
this.tc_switch[fn]["handler"](chatid, toolcallid, fn, JSON.parse(toolargs))
this.tc_switchs[chatid][fn]["handler"](chatid, toolcallid, fn, JSON.parse(toolargs))
return undefined
} catch (/** @type {any} */error) {
this.toolcallpending_found_cleared(chatid, toolcallid, 'ToolsManager:ToolCall:Exc')

View File

@ -17,6 +17,7 @@
import * as mChatMagic from './simplechat.js'
import * as mToolsMgr from './tools.mjs'
/**
@ -77,13 +78,15 @@ async function proxyserver_get_anyargs(chatid, toolcallid, toolname, objSearchPa
* Setup a proxy server dependent tool call
* NOTE: Currently the logic is setup for the bundled simpleproxy.py
* @param {string} tag
* @param {string} chatId
* @param {string} tcPath
* @param {string} tcName
* @param {{ [x: string]: any; }} tcsData
* @param {Object<string, Object<string, any>>} tcs
* @param {mToolsMgr.TCSwitch} tcs
*/
async function proxyserver_tc_setup(tag, tcPath, tcName, tcsData, tcs) {
await fetch(`${gMe.defaultCfg.tools.proxyUrl}/aum?url=${tcPath}.jambudweepe.akashaganga.multiverse.987654321123456789`).then(resp=>{
async function proxyserver_tc_setup(tag, chatId, tcPath, tcName, tcsData, tcs) {
let chat = gMe.multiChat.simpleChats[chatId]
await fetch(`${chat.cfg.tools.proxyUrl}/aum?url=${tcPath}.jambudweepe.akashaganga.multiverse.987654321123456789`).then(resp=>{
if (resp.statusText != 'bharatavarshe') {
console.log(`WARN:ToolWeb:${tag}:Dont forget to run the bundled local.tools/simpleproxy.py to enable me`)
return
@ -141,10 +144,11 @@ function fetchweburlraw_run(chatid, toolcallid, toolname, obj) {
/**
* Setup fetch_web_url_raw for tool calling
* NOTE: Currently the logic is setup for the bundled simpleproxy.py
* @param {Object<string, Object<string, any>>} tcs
* @param {mToolsMgr.TCSwitch} tcs
* @param {string} chatId
*/
async function fetchweburlraw_setup(tcs) {
return proxyserver_tc_setup('FetchWebUrlRaw', 'urlraw', 'fetch_web_url_raw', {
async function fetchweburlraw_setup(tcs, chatId) {
return proxyserver_tc_setup('FetchWebUrlRaw', chatId, 'urlraw', 'fetch_web_url_raw', {
"handler": fetchweburlraw_run,
"meta": fetchweburlraw_meta,
"result": ""
@ -195,10 +199,11 @@ function fetchhtmltext_run(chatid, toolcallid, toolname, obj) {
/**
* Setup fetch_html_text for tool calling
* NOTE: Currently the logic is setup for the bundled simpleproxy.py
* @param {Object<string, Object<string, any>>} tcs
* @param {mToolsMgr.TCSwitch} tcs
* @param {string} chatId
*/
async function fetchhtmltext_setup(tcs) {
return proxyserver_tc_setup('FetchHtmlText', 'htmltext', 'fetch_html_text', {
async function fetchhtmltext_setup(tcs, chatId) {
return proxyserver_tc_setup('FetchHtmlText', chatId, 'htmltext', 'fetch_html_text', {
"handler": fetchhtmltext_run,
"meta": fetchhtmltext_meta,
"result": ""
@ -254,10 +259,11 @@ function searchwebtext_run(chatid, toolcallid, toolname, obj) {
/**
* Setup search_web_text for tool calling
* NOTE: Currently the logic is setup for the bundled simpleproxy.py
* @param {Object<string, Object<string, any>>} tcs
* @param {mToolsMgr.TCSwitch} tcs
* @param {string} chatId
*/
async function searchwebtext_setup(tcs) {
return proxyserver_tc_setup('SearchWebText', 'htmltext', 'search_web_text', {
async function searchwebtext_setup(tcs, chatId) {
return proxyserver_tc_setup('SearchWebText', chatId, 'htmltext', 'search_web_text', {
"handler": searchwebtext_run,
"meta": searchwebtext_meta,
"result": ""
@ -319,10 +325,11 @@ function fetchpdftext_run(chatid, toolcallid, toolname, obj) {
/**
* Setup fetchpdftext for tool calling
* NOTE: Currently the logic is setup for the bundled simpleproxy.py
* @param {Object<string, Object<string, any>>} tcs
* @param {mToolsMgr.TCSwitch} tcs
* @param {string} chatId
*/
async function fetchpdftext_setup(tcs) {
return proxyserver_tc_setup('FetchPdfAsText', 'pdftext', 'fetch_pdf_as_text', {
async function fetchpdftext_setup(tcs, chatId) {
return proxyserver_tc_setup('FetchPdfAsText', chatId, 'pdftext', 'fetch_pdf_as_text', {
"handler": fetchpdftext_run,
"meta": fetchpdftext_meta,
"result": ""
@ -391,10 +398,11 @@ function fetchxmlfiltered_run(chatid, toolcallid, toolname, obj) {
/**
* Setup fetch_xml_filtered for tool calling
* NOTE: Currently the logic is setup for the bundled simpleproxy.py
* @param {Object<string, Object<string, any>>} tcs
* @param {mToolsMgr.TCSwitch} tcs
* @param {string} chatId
*/
async function fetchxmlfiltered_setup(tcs) {
return proxyserver_tc_setup('FetchXmlFiltered', 'xmlfiltered', 'fetch_xml_filtered', {
async function fetchxmlfiltered_setup(tcs, chatId) {
return proxyserver_tc_setup('FetchXmlFiltered', chatId, 'xmlfiltered', 'fetch_xml_filtered', {
"handler": fetchxmlfiltered_run,
"meta": fetchxmlfiltered_meta,
"result": ""
@ -413,15 +421,22 @@ async function fetchxmlfiltered_setup(tcs) {
* @param {mChatMagic.Me} me
*/
export async function init(me) {
gMe = me
}
/**
* @param {string} chatId
*/
export async function setup(chatId) {
/**
* @type {Object<string, Object<string, any>>} tcs
* @type {mToolsMgr.TCSwitch} tcs
*/
let tc_switch = {}
gMe = me
await fetchweburlraw_setup(tc_switch)
await fetchhtmltext_setup(tc_switch)
await searchwebtext_setup(tc_switch)
await fetchpdftext_setup(tc_switch)
await fetchxmlfiltered_setup(tc_switch)
await fetchweburlraw_setup(tc_switch, chatId)
await fetchhtmltext_setup(tc_switch, chatId)
await searchwebtext_setup(tc_switch, chatId)
await fetchpdftext_setup(tc_switch, chatId)
await fetchxmlfiltered_setup(tc_switch, chatId)
return tc_switch
}