diff --git a/packages/server/src/controllers/chat-messages/index.ts b/packages/server/src/controllers/chat-messages/index.ts index 8ed67399..6e9bebd8 100644 --- a/packages/server/src/controllers/chat-messages/index.ts +++ b/packages/server/src/controllers/chat-messages/index.ts @@ -1,5 +1,5 @@ import { Request, Response, NextFunction } from 'express' -import { chatType, IReactFlowObject } from '../../Interface' +import { ChatMessageRatingType, chatType, IReactFlowObject } from '../../Interface' import chatflowsService from '../../services/chatflows' import chatMessagesService from '../../services/chat-messages' import { clearSessionMemory } from '../../utils' @@ -49,6 +49,26 @@ const getAllChatMessages = async (req: Request, res: Response, next: NextFunctio const startDate = req.query?.startDate as string | undefined const endDate = req.query?.endDate as string | undefined const feedback = req.query?.feedback as boolean | undefined + let feedbackTypeFilters = req.query?.feedbackType as ChatMessageRatingType[] | undefined + if (feedbackTypeFilters) { + try { + const feedbackTypeFilterArray = JSON.parse(JSON.stringify(feedbackTypeFilters)) + if ( + feedbackTypeFilterArray.includes(ChatMessageRatingType.THUMBS_UP) && + feedbackTypeFilterArray.includes(ChatMessageRatingType.THUMBS_DOWN) + ) { + feedbackTypeFilters = [ChatMessageRatingType.THUMBS_UP, ChatMessageRatingType.THUMBS_DOWN] + } else if (feedbackTypeFilterArray.includes(ChatMessageRatingType.THUMBS_UP)) { + feedbackTypeFilters = [ChatMessageRatingType.THUMBS_UP] + } else if (feedbackTypeFilterArray.includes(ChatMessageRatingType.THUMBS_DOWN)) { + feedbackTypeFilters = [ChatMessageRatingType.THUMBS_DOWN] + } else { + feedbackTypeFilters = undefined + } + } catch (e) { + return res.status(500).send(e) + } + } if (typeof req.params === 'undefined' || !req.params.id) { throw new InternalFlowiseError( StatusCodes.PRECONDITION_FAILED, @@ -65,7 +85,8 @@ const getAllChatMessages = async (req: Request, res: Response, next: NextFunctio startDate, endDate, messageId, - feedback + feedback, + feedbackTypeFilters ) return res.json(apiResponse) } catch (error) { diff --git a/packages/server/src/controllers/stats/index.ts b/packages/server/src/controllers/stats/index.ts index e4dd4dad..a9646451 100644 --- a/packages/server/src/controllers/stats/index.ts +++ b/packages/server/src/controllers/stats/index.ts @@ -1,7 +1,7 @@ import { StatusCodes } from 'http-status-codes' import { Request, Response, NextFunction } from 'express' import statsService from '../../services/stats' -import { chatType } from '../../Interface' +import { ChatMessageRatingType, chatType } from '../../Interface' import { InternalFlowiseError } from '../../errors/internalFlowiseError' import { getErrorMessage } from '../../errors/utils' @@ -14,6 +14,7 @@ const getChatflowStats = async (req: Request, res: Response, next: NextFunction) let chatTypeFilter = req.query?.chatType as chatType | undefined const startDate = req.query?.startDate as string | undefined const endDate = req.query?.endDate as string | undefined + let feedbackTypeFilters = req.query?.feedbackType as ChatMessageRatingType[] | undefined if (chatTypeFilter) { try { const chatTypeFilterArray = JSON.parse(chatTypeFilter) @@ -31,7 +32,34 @@ const getChatflowStats = async (req: Request, res: Response, next: NextFunction) ) } } - const apiResponse = await statsService.getChatflowStats(chatflowid, chatTypeFilter, startDate, endDate, '', true) + if (feedbackTypeFilters) { + try { + const feedbackTypeFilterArray = JSON.parse(JSON.stringify(feedbackTypeFilters)) + if ( + feedbackTypeFilterArray.includes(ChatMessageRatingType.THUMBS_UP) && + feedbackTypeFilterArray.includes(ChatMessageRatingType.THUMBS_DOWN) + ) { + feedbackTypeFilters = [ChatMessageRatingType.THUMBS_UP, ChatMessageRatingType.THUMBS_DOWN] + } else if (feedbackTypeFilterArray.includes(ChatMessageRatingType.THUMBS_UP)) { + feedbackTypeFilters = [ChatMessageRatingType.THUMBS_UP] + } else if (feedbackTypeFilterArray.includes(ChatMessageRatingType.THUMBS_DOWN)) { + feedbackTypeFilters = [ChatMessageRatingType.THUMBS_DOWN] + } else { + feedbackTypeFilters = undefined + } + } catch (e) { + return res.status(500).send(e) + } + } + const apiResponse = await statsService.getChatflowStats( + chatflowid, + chatTypeFilter, + startDate, + endDate, + '', + true, + feedbackTypeFilters + ) return res.json(apiResponse) } catch (error) { next(error) diff --git a/packages/server/src/services/chat-messages/index.ts b/packages/server/src/services/chat-messages/index.ts index 39d0606c..9f2d53f7 100644 --- a/packages/server/src/services/chat-messages/index.ts +++ b/packages/server/src/services/chat-messages/index.ts @@ -1,6 +1,6 @@ import { DeleteResult, FindOptionsWhere } from 'typeorm' import { StatusCodes } from 'http-status-codes' -import { chatType, IChatMessage } from '../../Interface' +import { ChatMessageRatingType, chatType, IChatMessage } from '../../Interface' import { utilGetChatMessage } from '../../utils/getChatMessage' import { utilAddChatMessage } from '../../utils/addChatMesage' import { getRunningExpressApp } from '../../utils/getRunningExpressApp' @@ -35,7 +35,8 @@ const getAllChatMessages = async ( startDate?: string, endDate?: string, messageId?: string, - feedback?: boolean + feedback?: boolean, + feedbackTypes?: ChatMessageRatingType[] ): Promise => { try { const dbResponse = await utilGetChatMessage( @@ -48,7 +49,8 @@ const getAllChatMessages = async ( startDate, endDate, messageId, - feedback + feedback, + feedbackTypes ) return dbResponse } catch (error) { @@ -70,7 +72,8 @@ const getAllInternalChatMessages = async ( startDate?: string, endDate?: string, messageId?: string, - feedback?: boolean + feedback?: boolean, + feedbackTypes?: ChatMessageRatingType[] ): Promise => { try { const dbResponse = await utilGetChatMessage( @@ -83,7 +86,8 @@ const getAllInternalChatMessages = async ( startDate, endDate, messageId, - feedback + feedback, + feedbackTypes ) return dbResponse } catch (error) { diff --git a/packages/server/src/services/stats/index.ts b/packages/server/src/services/stats/index.ts index 2f6faff2..8d9b99da 100644 --- a/packages/server/src/services/stats/index.ts +++ b/packages/server/src/services/stats/index.ts @@ -1,5 +1,5 @@ import { StatusCodes } from 'http-status-codes' -import { chatType } from '../../Interface' +import { ChatMessageRatingType, chatType } from '../../Interface' import { ChatMessage } from '../../database/entities/ChatMessage' import { utilGetChatMessage } from '../../utils/getChatMessage' import { ChatMessageFeedback } from '../../database/entities/ChatMessageFeedback' @@ -13,7 +13,8 @@ const getChatflowStats = async ( startDate?: string, endDate?: string, messageId?: string, - feedback?: boolean + feedback?: boolean, + feedbackTypes?: ChatMessageRatingType[] ): Promise => { try { const chatmessages = (await utilGetChatMessage( @@ -26,7 +27,8 @@ const getChatflowStats = async ( startDate, endDate, messageId, - feedback + feedback, + feedbackTypes )) as Array const totalMessages = chatmessages.length const totalFeedback = chatmessages.filter((message) => message?.feedback).length diff --git a/packages/server/src/utils/getChatMessage.ts b/packages/server/src/utils/getChatMessage.ts index 29335cbf..459d62f7 100644 --- a/packages/server/src/utils/getChatMessage.ts +++ b/packages/server/src/utils/getChatMessage.ts @@ -1,8 +1,9 @@ import { MoreThanOrEqual, LessThanOrEqual } from 'typeorm' -import { chatType } from '../Interface' +import { ChatMessageRatingType, chatType } from '../Interface' import { ChatMessage } from '../database/entities/ChatMessage' import { ChatMessageFeedback } from '../database/entities/ChatMessageFeedback' import { getRunningExpressApp } from '../utils/getRunningExpressApp' + /** * Method that get chat messages. * @param {string} chatflowid @@ -14,6 +15,7 @@ import { getRunningExpressApp } from '../utils/getRunningExpressApp' * @param {string} startDate * @param {string} endDate * @param {boolean} feedback + * @param {ChatMessageRatingType[]} feedbackTypes */ export const utilGetChatMessage = async ( chatflowid: string, @@ -25,7 +27,8 @@ export const utilGetChatMessage = async ( startDate?: string, endDate?: string, messageId?: string, - feedback?: boolean + feedback?: boolean, + feedbackTypes?: ChatMessageRatingType[] ): Promise => { const appServer = getRunningExpressApp() const setDateToStartOrEndOfDay = (dateTimeStr: string, setHours: 'start' | 'end') => { @@ -79,7 +82,23 @@ export const utilGetChatMessage = async ( // sort query.orderBy('chat_message.createdDate', sortOrder === 'DESC' ? 'DESC' : 'ASC') - const messages = await query.getMany() + const messages = (await query.getMany()) as Array + + if (feedbackTypes && feedbackTypes.length > 0) { + // just applying a filter to the messages array will only return the messages that have feedback, + // but we also want the message before the feedback message which is the user message. + const indicesToKeep = new Set() + + messages.forEach((message, index) => { + if (message.role === 'apiMessage' && message.feedback && feedbackTypes.includes(message.feedback.rating)) { + if (index > 0) indicesToKeep.add(index - 1) + indicesToKeep.add(index) + } + }) + + return messages.filter((_, index) => indicesToKeep.has(index)) + } + return messages } diff --git a/packages/ui/src/ui-component/dialog/ViewMessagesDialog.jsx b/packages/ui/src/ui-component/dialog/ViewMessagesDialog.jsx index e12d79e5..25246ddd 100644 --- a/packages/ui/src/ui-component/dialog/ViewMessagesDialog.jsx +++ b/packages/ui/src/ui-component/dialog/ViewMessagesDialog.jsx @@ -103,6 +103,7 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { const [sourceDialogOpen, setSourceDialogOpen] = useState(false) const [sourceDialogProps, setSourceDialogProps] = useState({}) const [chatTypeFilter, setChatTypeFilter] = useState([]) + const [feedbackTypeFilter, setFeedbackTypeFilter] = useState([]) const [startDate, setStartDate] = useState(new Date().setMonth(new Date().getMonth() - 1)) const [endDate, setEndDate] = useState(new Date()) const [leadEmail, setLeadEmail] = useState('') @@ -155,6 +156,24 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { }) } + const onFeedbackTypeSelected = (feedbackTypes) => { + setFeedbackTypeFilter(feedbackTypes) + + getChatmessageApi.request(dialogProps.chatflow.id, { + chatType: chatTypeFilter.length ? chatTypeFilter : undefined, + feedbackType: feedbackTypes.length ? feedbackTypes : undefined, + startDate: startDate, + endDate: endDate, + order: 'ASC' + }) + getStatsApi.request(dialogProps.chatflow.id, { + chatType: chatTypeFilter.length ? chatTypeFilter : undefined, + feedbackType: feedbackTypes.length ? feedbackTypes : undefined, + startDate: startDate, + endDate: endDate + }) + } + const exportMessages = async () => { if (!storagePath && getStoragePathFromServer.data) { storagePath = getStoragePathFromServer.data.storagePath @@ -382,7 +401,14 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { const handleItemClick = (idx, chatmsg) => { setSelectedMessageIndex(idx) - getChatmessageFromPKApi.request(dialogProps.chatflow.id, transformChatPKToParams(getChatPK(chatmsg))) + if (feedbackTypeFilter.length > 0) { + getChatmessageFromPKApi.request(dialogProps.chatflow.id, { + ...transformChatPKToParams(getChatPK(chatmsg)), + feedbackType: feedbackTypeFilter + }) + } else { + getChatmessageFromPKApi.request(dialogProps.chatflow.id, transformChatPKToParams(getChatPK(chatmsg))) + } } const onURLClick = (data) => { @@ -436,7 +462,16 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { setAllChatLogs(getChatmessageApi.data) const chatPK = processChatLogs(getChatmessageApi.data) setSelectedMessageIndex(0) - if (chatPK) getChatmessageFromPKApi.request(dialogProps.chatflow.id, transformChatPKToParams(chatPK)) + if (chatPK) { + if (feedbackTypeFilter.length > 0) { + getChatmessageFromPKApi.request(dialogProps.chatflow.id, { + ...transformChatPKToParams(chatPK), + feedbackType: feedbackTypeFilter + }) + } else { + getChatmessageFromPKApi.request(dialogProps.chatflow.id, transformChatPKToParams(chatPK)) + } + } } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -459,6 +494,7 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { setAllChatLogs([]) setChatMessages([]) setChatTypeFilter([]) + setFeedbackTypeFilter([]) setSelectedMessageIndex(0) setSelectedChatId('') setStartDate(new Date().setMonth(new Date().getMonth() - 1)) @@ -476,6 +512,17 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { return () => dispatch({ type: HIDE_CANVAS_DIALOG }) }, [show, dispatch]) + useEffect(() => { + if (dialogProps.chatflow) { + // when the filter is cleared fetch all messages + if (feedbackTypeFilter.length === 0) { + getChatmessageApi.request(dialogProps.chatflow.id) + getStatsApi.request(dialogProps.chatflow.id) + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [feedbackTypeFilter]) + const component = show ? ( { customInput={} /> -
+
Source { formControlSx={{ mt: 0 }} />
+
+ Feedback + onFeedbackTypeSelected(newValue)} + value={feedbackTypeFilter} + formControlSx={{ mt: 0 }} + /> +