From 18529445ce2c8632da80ea55f304afa987b7b6bf Mon Sep 17 00:00:00 2001 From: hanishkvc Date: Fri, 21 Nov 2025 17:39:11 +0530 Subject: [PATCH] SimpleChatTCRV:ToolCall:ExternalAi: initial go --- tools/server/public_simplechat/simplechat.js | 16 ++- tools/server/public_simplechat/toolai.mjs | 126 +++++++++++++++++++ tools/server/public_simplechat/tools.mjs | 7 ++ 3 files changed, 143 insertions(+), 6 deletions(-) create mode 100644 tools/server/public_simplechat/toolai.mjs diff --git a/tools/server/public_simplechat/simplechat.js b/tools/server/public_simplechat/simplechat.js index 4d05f807ef..31edce6a4d 100644 --- a/tools/server/public_simplechat/simplechat.js +++ b/tools/server/public_simplechat/simplechat.js @@ -9,12 +9,16 @@ import * as mTools from "./tools.mjs" import * as mIdb from "./idb.mjs" +const TEMP_MARKER = "-TEMP" + const DB_NAME = "SimpleChatTCRV" const DB_STORE = "Sessions" -const ROLES_TEMP_ENDSWITH = "-TEMP" +export const AI_TC_SESSIONNAME = `ExternalAI${TEMP_MARKER}` -class Roles { +const ROLES_TEMP_ENDSWITH = TEMP_MARKER + +export class Roles { static System = "system"; static User = "user"; static Assistant = "assistant"; @@ -33,7 +37,7 @@ class Roles { } -class ApiEP { +export class ApiEP { static Type = { Chat: "chat", Completion: "completion", @@ -69,7 +73,7 @@ class ApiEP { * @typedef {{id: string, type: string, function: {name: string, arguments: string}}} NSToolCall */ -class NSChatMessage { +export class NSChatMessage { /** * Represents a Message as seen in the http server Chat handshake * @param {string} role @@ -260,7 +264,7 @@ class NSChatMessage { } -class ChatMessageEx { +export class ChatMessageEx { static uniqCounter = 0 @@ -1888,7 +1892,7 @@ export class Me { constructor() { this.baseURL = "http://127.0.0.1:8080"; - this.defaultChatIds = [ "Default", "Other" ]; + this.defaultChatIds = [ "Default", "Other", AI_TC_SESSIONNAME ]; this.multiChat = new MultiChatUI(this); this.tools = { enabled: true, diff --git a/tools/server/public_simplechat/toolai.mjs b/tools/server/public_simplechat/toolai.mjs new file mode 100644 index 0000000000..e3dd8c9374 --- /dev/null +++ b/tools/server/public_simplechat/toolai.mjs @@ -0,0 +1,126 @@ +//@ts-check +// ALERT - Simple Stupid flow - Using from a discardable VM is better +// Helpers to handle tools/functions calling wrt +// * calling a external / independent ai session +// by Humans for All +// + +import * as mChatMagic from './simplechat.js' + + +let gMe = /** @type{mChatMagic.Me} */(/** @type {unknown} */(null)); + + +let externalai_meta = { + "type": "function", + "function": { + "name": "external_ai", + "description": "Delegates a task to another AI instance using a custom system prompt and user message, that you as the caller define. Useful for tasks like summarization, structured data generation, or any custom AI workflow.", + "parameters": { + "type": "object", + "properties": { + "system_prompt": { + "type": "string", + "description": "The system prompt to define the role and expected behavior of the external AI.", + "required": true, + "example": "You are a professional summarizer. Summarize the following text in 100 words:" + }, + "user_message": { + "type": "string", + "description": "The message to be processed by the external AI.", + "required": true, + "example": "This is a long document about climate change. It discusses rising temperatures, policy responses, and future projections. The remaining part of the document is captured here..." + }, + "model_name": { + "type": "string", + "description": "Optional identifier for the specific AI model to use (e.g., 'gpt-4', 'claude-3').", + "required": false, + "example": "gpt-4" + }, + "max_tokens": { + "type": "integer", + "description": "Maximum number of tokens to generate in the response.", + "required": false, + "example": 500 + }, + }, + required: [ "system_prompt", "user_message" ] + }, + "examples": [ + { + "description": "Custom summarization", + "tool_call": { + "name": "external_ai", + "arguments": { + "system_prompt": "You are a professional summarizer. Summarize the following text in 100 words:", + "user_message": "The long text to summarise is passed here..." + } + } + }, + { + "description": "Structured data generation", + "tool_call": { + "name": "external_ai", + "arguments": { + "system_prompt": "You are a data structurer. Convert the following text into a JSON object with fields: title, author, year, and summary.", + "user_message": "The Indian epic 'Ramayana' by Valmiki is from eons back. It explores the fight of good against evil as well as dharma including how kings should conduct themselves and their responsibilities." + } + } + }, + { + "description": "Literary critic", + "tool_call": { + "name": "external_ai", + "arguments": { + "system_prompt": "You are a professional literary critic. Evaluate the provided summary of the Ramayana against key criteria: accuracy of core themes, completeness of major elements, and clarity. Provide a concise assessment.", + "user_message": "The Indian epic 'Ramayana' by Valmiki is from eons back. It explores the fight of good against evil as well as dharma including how kings should conduct themselves and their responsibilities." + } + } + }, + ] + } +}; + + +/** + * Implementation of the external ai tool call. + * @param {string} chatid + * @param {string} toolcallid + * @param {string} toolname + * @param {any} obj + */ +function externalai_run(chatid, toolcallid, toolname, obj) { + let sc = gMe.multiChat.simpleChats[mChatMagic.AI_TC_SESSIONNAME]; + + 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, gMe.multiChat.elDivChat).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}`); + }) +} + + +/** + * @type {Object>} + */ +export let tc_switch = { + "external_ai": { + "handler": externalai_run, + "meta": externalai_meta, + "result": "" + }, +} + + +/** + * Used to get hold of the + * * needed Ai SimpleChat instance + * * the web worker path to use for returning result of tool call + * Also to setup tool calls, which need to cross check things at runtime + * @param {mChatMagic.Me} me + */ +export async function init(me) { + gMe = me +} diff --git a/tools/server/public_simplechat/tools.mjs b/tools/server/public_simplechat/tools.mjs index 76f95e0947..f193a363ce 100644 --- a/tools/server/public_simplechat/tools.mjs +++ b/tools/server/public_simplechat/tools.mjs @@ -8,6 +8,7 @@ import * as tjs from './tooljs.mjs' import * as tweb from './toolweb.mjs' import * as tdb from './tooldb.mjs' +import * as tai from './toolai.mjs' import * as mChatMagic from './simplechat.js' @@ -62,6 +63,12 @@ export class ToolsManager { me.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.tools.toolNames.push(key) + } + }) let tNs = await tweb.init(me) for (const key in tNs) { this.tc_switch[key] = tNs[key]