* ui: kai ui thing ui: fixes for picking existing chat, feedback and retry buttons ui: connect finding, creating threads ui: more ui tuning for chat window, socket manager ui: get/delete chats logic, create testing socket ui: testing ui: use on click query ui: minor fixed for chat display, rebase ui: start kai thing * ui: add logs, add threadid * ui: feedback methods and ui * ui: store, replacing messages and giving feedback * ui: move retry btn to right corner * ui: move kai service out for ease of code splitting * ui: add thread id to socket connection * ui: support state messages * ui: cancel response generation method * ui: fix toast str * ui: add gfm plugin * ui: ensure md table has max sizes to prevent overflow * ui: revert tailwind styles on markdown block layer * ui: export as pdf, copy text contents of a message * ui: try to save text with formatting in secure contexts * ui: fix types * ui: fixup dark mode colors * ui: add duration for msgs * ui: take out custom jwt * ui: removing hardcode... * ui: change endpoints to prod * ui: swap socket path * ui: flip vis toggle * ui: lock file regenerate
231 lines
6 KiB
TypeScript
231 lines
6 KiB
TypeScript
import { makeAutoObservable, runInAction } from 'mobx';
|
|
import { BotChunk, ChatManager, Message } from './SocketManager';
|
|
import { kaiService as aiService, kaiService } from 'App/services';
|
|
import { toast } from 'react-toastify';
|
|
|
|
class KaiStore {
|
|
chatManager: ChatManager | null = null;
|
|
processingStage: BotChunk | null = null;
|
|
messages: Message[] = [];
|
|
queryText = '';
|
|
loadingChat = false;
|
|
replacing = false;
|
|
|
|
constructor() {
|
|
makeAutoObservable(this);
|
|
}
|
|
|
|
get lastHumanMessage() {
|
|
let msg = null;
|
|
let index = null;
|
|
for (let i = this.messages.length - 1; i >= 0; i--) {
|
|
const message = this.messages[i];
|
|
if (message.isUser) {
|
|
msg = message;
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
return { msg, index };
|
|
}
|
|
|
|
get lastKaiMessage() {
|
|
let msg = null;
|
|
let index = null;
|
|
for (let i = this.messages.length - 1; i >= 0; i--) {
|
|
const message = this.messages[i];
|
|
if (!message.isUser) {
|
|
msg = message;
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
return { msg, index };
|
|
}
|
|
|
|
setQueryText = (text: string) => {
|
|
this.queryText = text;
|
|
};
|
|
|
|
setLoadingChat = (loading: boolean) => {
|
|
this.loadingChat = loading;
|
|
};
|
|
|
|
setChatManager = (chatManager: ChatManager) => {
|
|
this.chatManager = chatManager;
|
|
};
|
|
|
|
setProcessingStage = (stage: BotChunk | null) => {
|
|
this.processingStage = stage;
|
|
};
|
|
|
|
setMessages = (messages: Message[]) => {
|
|
this.messages = messages;
|
|
};
|
|
|
|
addMessage = (message: Message) => {
|
|
this.messages.push(message);
|
|
};
|
|
|
|
editMessage = (text: string) => {
|
|
this.setQueryText(text);
|
|
this.setReplacing(true);
|
|
};
|
|
|
|
replaceAtIndex = (message: Message, index: number) => {
|
|
const messages = [...this.messages];
|
|
messages[index] = message;
|
|
this.setMessages(messages);
|
|
};
|
|
|
|
deleteAtIndex = (indexes: number[]) => {
|
|
if (!indexes.length) return;
|
|
const messages = this.messages.filter((_, i) => !indexes.includes(i));
|
|
console.log(messages, indexes)
|
|
runInAction(() => {
|
|
this.messages = messages;
|
|
})
|
|
}
|
|
|
|
getChat = async (projectId: string, threadId: string) => {
|
|
this.setLoadingChat(true);
|
|
try {
|
|
const res = await aiService.getKaiChat(projectId, threadId);
|
|
if (res && res.length) {
|
|
this.setMessages(
|
|
res.map((m) => {
|
|
const isUser = m.role === 'human';
|
|
return {
|
|
text: m.content,
|
|
isUser: isUser,
|
|
messageId: m.message_id,
|
|
duration: m.duration,
|
|
};
|
|
}),
|
|
);
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
toast.error("Couldn't load chat history. Please try again later.");
|
|
} finally {
|
|
this.setLoadingChat(false);
|
|
}
|
|
};
|
|
|
|
createChatManager = (
|
|
settings: { projectId: string; threadId: string },
|
|
setTitle: (title: string) => void,
|
|
initialMsg: string | null,
|
|
) => {
|
|
const token = kaiService.client.getJwt()
|
|
this.chatManager = new ChatManager({ ...settings, token });
|
|
this.chatManager.setOnMsgHook({
|
|
msgCallback: (msg) => {
|
|
if ('state' in msg) {
|
|
if (msg.state === 'running') {
|
|
this.setProcessingStage({
|
|
content: 'Processing your request...',
|
|
stage: 'chart',
|
|
messageId: Date.now().toPrecision(),
|
|
duration: msg.start_time ? Date.now() - msg.start_time : 0
|
|
})
|
|
} else {
|
|
this.setProcessingStage(null)
|
|
}
|
|
} else {
|
|
if (msg.stage === 'start') {
|
|
this.setProcessingStage({
|
|
...msg,
|
|
content: 'Processing your request...',
|
|
});
|
|
}
|
|
if (msg.stage === 'chart') {
|
|
this.setProcessingStage(msg);
|
|
}
|
|
if (msg.stage === 'final') {
|
|
const msgObj = {
|
|
text: msg.content,
|
|
isUser: false,
|
|
messageId: msg.messageId,
|
|
duration: msg.duration
|
|
}
|
|
this.addMessage(msgObj);
|
|
this.setProcessingStage(null);
|
|
}
|
|
}
|
|
},
|
|
titleCallback: setTitle,
|
|
});
|
|
|
|
if (initialMsg) {
|
|
this.sendMessage(initialMsg);
|
|
}
|
|
};
|
|
|
|
setReplacing = (replacing: boolean) => {
|
|
this.replacing = replacing;
|
|
};
|
|
|
|
sendMessage = (message: string) => {
|
|
if (this.chatManager) {
|
|
this.chatManager.sendMessage(message, this.replacing);
|
|
}
|
|
if (this.replacing) {
|
|
console.log(this.lastHumanMessage, this.lastKaiMessage, 'replacing these two')
|
|
const deleting = []
|
|
if (this.lastHumanMessage.index !== null) {
|
|
deleting.push(this.lastHumanMessage.index);
|
|
}
|
|
if (this.lastKaiMessage.index !== null) {
|
|
deleting.push(this.lastKaiMessage.index)
|
|
}
|
|
this.deleteAtIndex(deleting);
|
|
this.setReplacing(false)
|
|
}
|
|
this.addMessage({
|
|
text: message,
|
|
isUser: true,
|
|
messageId: Date.now().toString(),
|
|
});
|
|
};
|
|
|
|
sendMsgFeedback = (feedback: string, messageId: string) => {
|
|
const settings = { projectId: '2325', userId: '0' };
|
|
aiService
|
|
.feedback(
|
|
feedback === 'like',
|
|
messageId,
|
|
settings.projectId,
|
|
)
|
|
.then(() => {
|
|
toast.success('Feedback saved.');
|
|
})
|
|
.catch((e) => {
|
|
console.error(e);
|
|
toast.error('Failed to send feedback. Please try again later.');
|
|
});
|
|
};
|
|
|
|
cancelGeneration = async (settings: { projectId: string; userId: string; threadId: string }) => {
|
|
try {
|
|
await kaiService.cancelGeneration(settings.projectId, settings.threadId, settings.userId)
|
|
this.setProcessingStage(null)
|
|
} catch (e) {
|
|
console.error(e)
|
|
toast.error('Failed to cancel the response generation, please try again later.')
|
|
}
|
|
}
|
|
|
|
clearChat = () => {
|
|
this.setMessages([]);
|
|
this.setProcessingStage(null);
|
|
this.setLoadingChat(false);
|
|
this.setQueryText('');
|
|
if (this.chatManager) {
|
|
this.chatManager.disconnect();
|
|
this.chatManager = null;
|
|
}
|
|
};
|
|
}
|
|
|
|
export const kaiStore = new KaiStore();
|