diff --git a/packages/server/package.json b/packages/server/package.json index 4d4293b0..4fcdfc9d 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -64,6 +64,7 @@ "socket.io": "^4.6.1", "sqlite3": "^5.1.6", "typeorm": "^0.3.6", + "uuid": "^9.0.1", "winston": "^3.9.0" }, "devDependencies": { diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 78efe657..6da37330 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -8,6 +8,7 @@ import basicAuth from 'express-basic-auth' import { Server } from 'socket.io' import logger from './utils/logger' import { expressRequestLogger } from './utils/logger' +import { v4 as uuidv4 } from 'uuid' import { IChatFlow, @@ -391,14 +392,13 @@ export class App { // Get all chatmessages from chatflowid this.app.get('/api/v1/chatmessage/:id', async (req: Request, res: Response) => { - const chatmessages = await this.AppDataSource.getRepository(ChatMessage).find({ - where: { - chatflowid: req.params.id - }, - order: { - createdDate: 'ASC' - } - }) + const chatmessages = await this.getChatMessage(req.params.id, undefined) + return res.json(chatmessages) + }) + + // Get internal chatmessages from chatflowid + this.app.get('/api/v1/internal-chatmessage/:id', async (req: Request, res: Response) => { + const chatmessages = await this.getChatMessage(req.params.id, chatType.INTERNAL) return res.json(chatmessages) }) @@ -815,10 +815,31 @@ export class App { } /** - * Add Chat Message - * @param {any} chatMessage + * Method that get chat messages. + * + * @param chatflowid - + * @param chatType - + * */ - async addChatMessage(chatMessage: any) { + async getChatMessage(chatflowid: string, chatType: chatType | undefined): Promise { + return await this.AppDataSource.getRepository(ChatMessage).find({ + where: { + chatflowid: chatflowid, + chatType: chatType + }, + order: { + createdDate: 'ASC' + } + }) + } + + /** + * Method that add chat messages. + * + * @param chatMessage - + * + */ + async addChatMessage(chatMessage: any): Promise { const newChatMessage = new ChatMessage() Object.assign(newChatMessage, chatMessage) @@ -826,6 +847,20 @@ export class App { return await this.AppDataSource.getRepository(ChatMessage).save(chatmessage) } + /** + * Method that find memory label. + * + * @param nodes - + * + */ + findMemoryLabel(nodes: any[]): string | undefined { + const memoryNode = nodes.find((node) => { + return node.data.category === 'Memory' + }) + + return memoryNode ? memoryNode.data.label : undefined + } + /** * Process Prediction * @param {Request} req @@ -845,20 +880,13 @@ export class App { }) if (!chatflow) return res.status(404).send(`Chatflow ${chatflowid} not found`) - let chatId = await getChatId(chatflow.id) - if (!chatId) chatId = chatflowid + const chatId = incomingInput.chatId ?? uuidv4() + const userMessageDateTime = new Date() if (!isInternal) { await this.validateKey(req, res, chatflow) } - await this.addChatMessage({ - role: 'userMessage', - content: incomingInput.question, - chatflowid: chatflowid, - chatType: isInternal ? chatType.INTERNAL : chatType.EXTERNAL - }) - let isStreamValid = false const files = (req.files as any[]) || [] @@ -986,9 +1014,12 @@ export class App { logger.debug(`[server]: Running ${nodeToExecuteData.label} (${nodeToExecuteData.id})`) - if (nodeToExecuteData.instance) checkMemorySessionId(nodeToExecuteData.instance, chatId) + let sessionId = undefined + let memoryLabel = undefined + if (nodeToExecuteData.instance) sessionId = checkMemorySessionId(nodeToExecuteData.instance, chatId) + if (sessionId) memoryLabel = this.findMemoryLabel(nodes) - const result = isStreamValid + let result = isStreamValid ? await nodeInstance.run(nodeToExecuteData, incomingInput.question, { chatHistory: incomingInput.history, socketIO, @@ -1006,16 +1037,33 @@ export class App { analytic: chatflow.analytic }) + result = typeof result === 'string' ? { text: result } : result + + await this.addChatMessage({ + role: 'userMessage', + content: incomingInput.question, + chatflowid: chatflowid, + chatType: isInternal ? chatType.INTERNAL : chatType.EXTERNAL, + chatId: chatId, + memoryType: memoryLabel ? memoryLabel : undefined, + sessionId: sessionId ? sessionId : undefined, + createdDate: userMessageDateTime + }) + const apiMessage: any = { role: 'apiMessage', - content: typeof result === 'string' ? result : result.text, + content: result.text, chatflowid: chatflowid, - chatType: isInternal ? chatType.INTERNAL : chatType.EXTERNAL + chatType: isInternal ? chatType.INTERNAL : chatType.EXTERNAL, + chatId: chatId, + memoryType: memoryLabel ? memoryLabel : undefined, + sessionId: sessionId ? sessionId : undefined } if (result?.sourceDocuments) apiMessage.sourceDocuments = JSON.stringify(result.sourceDocuments) await this.addChatMessage(apiMessage) logger.debug(`[server]: Finished running ${nodeToExecuteData.label} (${nodeToExecuteData.id})`) + result.chatId = chatId return res.json(result) } catch (e: any) { logger.error('[server]: Error:', e) diff --git a/packages/server/src/utils/index.ts b/packages/server/src/utils/index.ts index b1f7e5a2..0ece0a22 100644 --- a/packages/server/src/utils/index.ts +++ b/packages/server/src/utils/index.ts @@ -922,8 +922,9 @@ export const redactCredentialWithPasswordType = ( * @param {any} instance * @param {string} chatId */ -export const checkMemorySessionId = (instance: any, chatId: string) => { +export const checkMemorySessionId = (instance: any, chatId: string): string => { if (instance.memory && instance.memory.isSessionIdUsingChatMessageId && chatId) { instance.memory.sessionId = chatId } + return instance.memory.sessionId } diff --git a/packages/ui/src/api/chatmessage.js b/packages/ui/src/api/chatmessage.js index 72a186a6..4dcc7c88 100644 --- a/packages/ui/src/api/chatmessage.js +++ b/packages/ui/src/api/chatmessage.js @@ -1,6 +1,6 @@ import client from './client' -const getChatmessageFromChatflow = (id) => client.get(`/chatmessage/${id}`) +const getChatmessageFromChatflow = (id) => client.get(`/internal-chatmessage/${id}`) const deleteChatmessage = (id) => client.delete(`/chatmessage/${id}`) diff --git a/packages/ui/src/views/chatmessage/ChatMessage.js b/packages/ui/src/views/chatmessage/ChatMessage.js index cf5cea6c..f5940932 100644 --- a/packages/ui/src/views/chatmessage/ChatMessage.js +++ b/packages/ui/src/views/chatmessage/ChatMessage.js @@ -50,6 +50,7 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => { const [isChatFlowAvailableToStream, setIsChatFlowAvailableToStream] = useState(false) const [sourceDialogOpen, setSourceDialogOpen] = useState(false) const [sourceDialogProps, setSourceDialogProps] = useState({}) + const [chatId, setChatId] = useState(undefined) const inputRef = useRef(null) const getChatmessageApi = useApi(chatmessageApi.getChatmessageFromChatflow) @@ -148,7 +149,8 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => { try { const params = { question: userInput, - history: messages.filter((msg) => msg.message !== 'Hi there! How can I help?') + history: messages.filter((msg) => msg.message !== 'Hi there! How can I help?'), + chatId } if (isChatFlowAvailableToStream) params.socketIOClientId = socketIOClientId @@ -159,18 +161,16 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => { data = handleVectaraMetadata(data) - if (typeof data === 'object' && data.text && data.sourceDocuments) { - if (!isChatFlowAvailableToStream) { - setMessages((prevMessages) => [ - ...prevMessages, - { message: data.text, sourceDocuments: data.sourceDocuments, type: 'apiMessage' } - ]) - } - } else { - if (!isChatFlowAvailableToStream) { - setMessages((prevMessages) => [...prevMessages, { message: data, type: 'apiMessage' }]) - } + if (!chatId) { + setChatId(data.chatId) } + if (!isChatFlowAvailableToStream) { + setMessages((prevMessages) => [ + ...prevMessages, + { message: data.text, sourceDocuments: data?.sourceDocuments, type: 'apiMessage' } + ]) + } + setLoading(false) setUserInput('') setTimeout(() => { @@ -201,15 +201,15 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => { // Get chatmessages successful useEffect(() => { if (getChatmessageApi.data) { - const loadedMessages = [] - for (const message of getChatmessageApi.data) { + setChatId(getChatmessageApi.data[0]?.chatId) + const loadedMessages = getChatmessageApi.data.map((message) => { const obj = { message: message.content, type: message.role } if (message.sourceDocuments) obj.sourceDocuments = JSON.parse(message.sourceDocuments) - loadedMessages.push(obj) - } + return obj + }) setMessages((prevMessages) => [...prevMessages, ...loadedMessages]) }