SimpleChatTC:ToolTemp and ChatShow
Add a new role ToolTemp, which is used to maintain any tool call response on the client ui side, without submitting it to the server ie till user or auto submit triggers the submitting of that tool call response. When ever a tool call response is got, create a ToolTemp role based message in the corresponding chat session. And dont directly update the user query input area, rather leave it to the updated simplechat show and the new multichatui chat_show helper and inturn whether the current chat session active in ui is same as the one for which the tool call response has been recieved. TODO: Currently the response message is added to the current active chat session, but this needs to be changed by tracking chatId/session through the full tool call cycle and then adding the tool call response in the related chat session, and inturn updating or not the ui based on whether that chat session is still the active chat session in ui or not, given that tool call gets handled in a asynchronous way. Now when that tool call response is submitted, promote the equiv tool temp role based message that should be in the session's chat history as the last message into becoming a normal tool response message. SimpleChat.show has been updated to take care of showing any ToolTemp role message in the user query input area. A newer chat_show helper added to MultiChatUI, that takes care of calling SimpleChat.show, provided the chat_show is being requested for the currently active in ui, chat session. As well as to take care of passing both the ChatDiv and elInUser. Converts users of SimpleChat.show to use MultiChatUI.chat_show
This commit is contained in:
parent
84403973cd
commit
e79faebde1
|
|
@ -12,6 +12,7 @@ class Roles {
|
||||||
static User = "user";
|
static User = "user";
|
||||||
static Assistant = "assistant";
|
static Assistant = "assistant";
|
||||||
static Tool = "tool";
|
static Tool = "tool";
|
||||||
|
static ToolTemp = "TOOL.TEMP";
|
||||||
}
|
}
|
||||||
|
|
||||||
class ApiEP {
|
class ApiEP {
|
||||||
|
|
@ -319,7 +320,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) {
|
if (cur.ns == undefined) { // this relates to the old on-disk-structure/format, needs to be removed later
|
||||||
/** @typedef {{role: string, content: string}} OldChatMessage */
|
/** @typedef {{role: string, content: string}} OldChatMessage */
|
||||||
let tcur = /** @type {OldChatMessage} */(/** @type {unknown} */(cur));
|
let tcur = /** @type {OldChatMessage} */(/** @type {unknown} */(cur));
|
||||||
this.xchat.push(new ChatMessageEx(tcur.role, tcur.content))
|
this.xchat.push(new ChatMessageEx(tcur.role, tcur.content))
|
||||||
|
|
@ -383,6 +384,12 @@ 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) {
|
||||||
|
// Skip (temp) tool response which has not yet been accepted by user
|
||||||
|
// In future need to check that it is the last message
|
||||||
|
// and not something in between, which shouldnt occur normally.
|
||||||
|
continue
|
||||||
|
}
|
||||||
let tmsg = ChatMessageEx.newFrom(msg);
|
let tmsg = ChatMessageEx.newFrom(msg);
|
||||||
if (!tmsg.has_toolcall()) {
|
if (!tmsg.has_toolcall()) {
|
||||||
tmsg.ns_delete("tool_calls")
|
tmsg.ns_delete("tool_calls")
|
||||||
|
|
@ -413,25 +420,55 @@ class SimpleChat {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the last message in the chat history is a ToolTemp role based one.
|
||||||
|
* If so, then
|
||||||
|
* * update that to a regular Tool role based message.
|
||||||
|
* * also update the content of that message to what is passed.
|
||||||
|
* @param {string} content
|
||||||
|
*/
|
||||||
|
promote_tooltemp(content) {
|
||||||
|
let lastIndex = this.xchat.length - 1;
|
||||||
|
if (lastIndex < 0) {
|
||||||
|
console.error("DBUG:SimpleChat:PromoteToolTemp:No chat messages including ToolTemp")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.xchat[lastIndex].ns.role != Roles.ToolTemp) {
|
||||||
|
console.error("DBUG:SimpleChat:PromoteToolTemp:LastChatMsg not ToolTemp")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.xchat[lastIndex].ns.role = Roles.Tool;
|
||||||
|
this.xchat[lastIndex].ns.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the chat contents in the specified div.
|
* Show the chat contents in the specified div.
|
||||||
|
* Also update the user query input box, with ToolTemp role message, if any.
|
||||||
|
*
|
||||||
* If requested to clear prev stuff and inturn no chat content then show
|
* If requested to clear prev stuff and inturn no chat content then show
|
||||||
* * usage info
|
* * usage info
|
||||||
* * option to load prev saved chat if any
|
* * option to load prev saved chat if any
|
||||||
* * as well as settings/info.
|
* * as well as settings/info.
|
||||||
* @param {HTMLDivElement} div
|
* @param {HTMLDivElement} div
|
||||||
|
* @param {HTMLInputElement} elInUser
|
||||||
* @param {boolean} bClear
|
* @param {boolean} bClear
|
||||||
* @param {boolean} bShowInfoAll
|
* @param {boolean} bShowInfoAll
|
||||||
*/
|
*/
|
||||||
show(div, bClear=true, bShowInfoAll=false) {
|
show(div, elInUser, bClear=true, bShowInfoAll=false) {
|
||||||
if (bClear) {
|
if (bClear) {
|
||||||
div.replaceChildren();
|
div.replaceChildren();
|
||||||
}
|
}
|
||||||
let last = undefined;
|
let last = undefined;
|
||||||
for(const x of this.recent_chat(gMe.chatProps.iRecentUserMsgCnt)) {
|
for(const x of this.recent_chat(gMe.chatProps.iRecentUserMsgCnt)) {
|
||||||
let entry = ui.el_create_append_p(`${x.ns.role}: ${x.content_equiv()}`, div);
|
if (x.ns.role != Roles.ToolTemp) {
|
||||||
entry.className = `role-${x.ns.role}`;
|
let entry = ui.el_create_append_p(`${x.ns.role}: ${x.content_equiv()}`, div);
|
||||||
last = entry;
|
entry.className = `role-${x.ns.role}`;
|
||||||
|
last = entry;
|
||||||
|
} else {
|
||||||
|
if (elInUser) {
|
||||||
|
elInUser.value = x.ns.content;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (last !== undefined) {
|
if (last !== undefined) {
|
||||||
last.scrollIntoView(false);
|
last.scrollIntoView(false);
|
||||||
|
|
@ -792,6 +829,20 @@ class MultiChatUI {
|
||||||
this.elInUser.focus();
|
this.elInUser.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh UI wrt given chatId, provided it matches the currently selected chatId
|
||||||
|
* @param {string} chatId
|
||||||
|
* @param {boolean} bClear
|
||||||
|
* @param {boolean} bShowInfoAll
|
||||||
|
*/
|
||||||
|
chat_show(chatId, bClear=true, bShowInfoAll=false) {
|
||||||
|
if (chatId != this.curChatId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let chat = this.simpleChats[this.curChatId];
|
||||||
|
chat.show(this.elDivChat, this.elInUser, bClear, bShowInfoAll)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup the needed callbacks wrt UI, curChatId to defaultChatId and
|
* Setup the needed callbacks wrt UI, curChatId to defaultChatId and
|
||||||
* optionally switch to specified defaultChatId.
|
* optionally switch to specified defaultChatId.
|
||||||
|
|
@ -839,7 +890,12 @@ class MultiChatUI {
|
||||||
tools.setup((id, name, data)=>{
|
tools.setup((id, name, data)=>{
|
||||||
clearTimeout(this.timers.toolcallResponseTimeout)
|
clearTimeout(this.timers.toolcallResponseTimeout)
|
||||||
this.timers.toolcallResponseTimeout = undefined
|
this.timers.toolcallResponseTimeout = undefined
|
||||||
this.elInUser.value = ChatMessageEx.createToolCallResultAllInOne(id, name, data);
|
// TODO: Check for chat id in future so as to
|
||||||
|
// identify the right chat session to add the tc response to
|
||||||
|
// as well as to decide whether to show this chat currently or not and same with auto submit
|
||||||
|
let chat = this.simpleChats[this.curChatId]; // rather we should pick chat based on tool response's chatId
|
||||||
|
chat.add(new ChatMessageEx(Roles.ToolTemp, ChatMessageEx.createToolCallResultAllInOne(id, name, data)))
|
||||||
|
this.chat_show(chat.chatId) // one needs to use tool response's chatId
|
||||||
this.ui_reset_userinput(false)
|
this.ui_reset_userinput(false)
|
||||||
if (gMe.tools.auto > 0) {
|
if (gMe.tools.auto > 0) {
|
||||||
this.timers.toolcallResponseSubmitClick = setTimeout(()=>{
|
this.timers.toolcallResponseSubmitClick = setTimeout(()=>{
|
||||||
|
|
@ -867,7 +923,7 @@ class MultiChatUI {
|
||||||
this.elInSystem.value = value.substring(0,value.length-1);
|
this.elInSystem.value = value.substring(0,value.length-1);
|
||||||
let chat = this.simpleChats[this.curChatId];
|
let chat = this.simpleChats[this.curChatId];
|
||||||
chat.add_system_anytime(this.elInSystem.value, this.curChatId);
|
chat.add_system_anytime(this.elInSystem.value, this.curChatId);
|
||||||
chat.show(this.elDivChat);
|
this.chat_show(chat.chatId)
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -922,11 +978,11 @@ class MultiChatUI {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (content.startsWith("<tool_response>")) {
|
if (content.startsWith("<tool_response>")) {
|
||||||
chat.add(new ChatMessageEx(Roles.Tool, content))
|
chat.promote_tooltemp(content)
|
||||||
} else {
|
} else {
|
||||||
chat.add(new ChatMessageEx(Roles.User, content))
|
chat.add(new ChatMessageEx(Roles.User, content))
|
||||||
}
|
}
|
||||||
chat.show(this.elDivChat);
|
this.chat_show(chat.chatId);
|
||||||
|
|
||||||
let theUrl = ApiEP.Url(gMe.baseURL, apiEP);
|
let theUrl = ApiEP.Url(gMe.baseURL, apiEP);
|
||||||
let theBody = chat.request_jsonstr(apiEP);
|
let theBody = chat.request_jsonstr(apiEP);
|
||||||
|
|
@ -943,7 +999,7 @@ class MultiChatUI {
|
||||||
|
|
||||||
let theResp = await chat.handle_response(resp, apiEP, this.elDivChat);
|
let theResp = await chat.handle_response(resp, apiEP, this.elDivChat);
|
||||||
if (chatId == this.curChatId) {
|
if (chatId == this.curChatId) {
|
||||||
chat.show(this.elDivChat);
|
this.chat_show(chatId);
|
||||||
if (theResp.trimmedContent.length > 0) {
|
if (theResp.trimmedContent.length > 0) {
|
||||||
let p = ui.el_create_append_p(`TRIMMED:${theResp.trimmedContent}`, this.elDivChat);
|
let p = ui.el_create_append_p(`TRIMMED:${theResp.trimmedContent}`, this.elDivChat);
|
||||||
p.className="role-trim";
|
p.className="role-trim";
|
||||||
|
|
@ -1053,9 +1109,9 @@ class MultiChatUI {
|
||||||
}
|
}
|
||||||
this.elInSystem.value = chat.get_system_latest().ns.content;
|
this.elInSystem.value = chat.get_system_latest().ns.content;
|
||||||
this.elInUser.value = "";
|
this.elInUser.value = "";
|
||||||
chat.show(this.elDivChat, true, true);
|
|
||||||
this.elInUser.focus();
|
|
||||||
this.curChatId = chatId;
|
this.curChatId = chatId;
|
||||||
|
this.chat_show(chatId, true, true);
|
||||||
|
this.elInUser.focus();
|
||||||
console.log(`INFO:SimpleChat:MCUI:HandleSessionSwitch:${chatId} entered...`);
|
console.log(`INFO:SimpleChat:MCUI:HandleSessionSwitch:${chatId} entered...`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1159,7 +1215,7 @@ class Me {
|
||||||
console.log("DBUG:SimpleChat:SC:Load", chat);
|
console.log("DBUG:SimpleChat:SC:Load", chat);
|
||||||
chat.load();
|
chat.load();
|
||||||
queueMicrotask(()=>{
|
queueMicrotask(()=>{
|
||||||
chat.show(div, true, true);
|
chat.show(div, this.multiChat.elInUser, true, true);
|
||||||
this.multiChat.elInSystem.value = chat.get_system_latest().ns.content;
|
this.multiChat.elInSystem.value = chat.get_system_latest().ns.content;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue