mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-29 11:01:18 +03:00
Fix merge conflict and update how feedback is saved/retrieved
This commit is contained in:
@@ -41,7 +41,6 @@ export interface IChatMessage {
|
|||||||
memoryType?: string
|
memoryType?: string
|
||||||
sessionId?: string
|
sessionId?: string
|
||||||
createdDate: Date
|
createdDate: Date
|
||||||
feedbackId?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IChatMessageFeedback {
|
export interface IChatMessageFeedback {
|
||||||
@@ -49,6 +48,7 @@ export interface IChatMessageFeedback {
|
|||||||
content?: string
|
content?: string
|
||||||
chatflowid: string
|
chatflowid: string
|
||||||
chatId: string
|
chatId: string
|
||||||
|
messageId: string
|
||||||
rating: ChatMessageRatingType
|
rating: ChatMessageRatingType
|
||||||
createdDate: Date
|
createdDate: Date
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,4 @@ export class ChatMessage implements IChatMessage {
|
|||||||
|
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
createdDate: Date
|
createdDate: Date
|
||||||
|
|
||||||
@Column({ nullable: true })
|
|
||||||
feedbackId?: string
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import { Entity, Column, CreateDateColumn, PrimaryGeneratedColumn, Index } from 'typeorm'
|
import { Entity, Column, CreateDateColumn, PrimaryGeneratedColumn, Index, Unique } from 'typeorm'
|
||||||
import { ChatMessageRatingType, IChatMessageFeedback } from '../../Interface'
|
import { IChatMessageFeedback, ChatMessageRatingType } from '../../Interface'
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
|
@Unique(['messageId'])
|
||||||
export class ChatMessageFeedback implements IChatMessageFeedback {
|
export class ChatMessageFeedback implements IChatMessageFeedback {
|
||||||
@PrimaryGeneratedColumn('uuid')
|
@PrimaryGeneratedColumn('uuid')
|
||||||
id: string
|
id: string
|
||||||
@@ -11,15 +12,19 @@ export class ChatMessageFeedback implements IChatMessageFeedback {
|
|||||||
@Column()
|
@Column()
|
||||||
chatflowid: string
|
chatflowid: string
|
||||||
|
|
||||||
@Column({ type: 'text' })
|
@Index()
|
||||||
content?: string
|
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
chatId: string
|
chatId: string
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
|
messageId: string
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
rating: ChatMessageRatingType
|
rating: ChatMessageRatingType
|
||||||
|
|
||||||
|
@Column({ nullable: true, type: 'text' })
|
||||||
|
content?: string
|
||||||
|
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
createdDate: Date
|
createdDate: Date
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ import { MigrationInterface, QueryRunner } from 'typeorm'
|
|||||||
export class AddFeedback1707213619308 implements MigrationInterface {
|
export class AddFeedback1707213619308 implements MigrationInterface {
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`CREATE TABLE IF NOT EXISTS "chat_message_feedback" ("id" varchar PRIMARY KEY NOT NULL, "chatflowid" varchar NOT NULL, "content" text, "chatId" varchar NOT NULL, "rating" varchar NOT NULL, "createdDate" datetime NOT NULL DEFAULT (datetime('now')));`
|
`CREATE TABLE IF NOT EXISTS "chat_message_feedback" ("id" varchar PRIMARY KEY NOT NULL, "chatflowid" varchar NOT NULL, "chatId" varchar NOT NULL, "messageId" varchar NOT NULL, "rating" varchar NOT NULL, "content" text, "createdDate" datetime NOT NULL DEFAULT (datetime('now')));`
|
||||||
)
|
)
|
||||||
|
await queryRunner.query(`CREATE INDEX IF NOT EXISTS "IDX_e574527322272fd838f4f0f3d3" ON "chat_message_feedback" ("chatflowid") ;`)
|
||||||
|
await queryRunner.query(`CREATE INDEX IF NOT EXISTS "IDX_e574527322272fd838f4f0f3d3" ON "chat_message_feedback" ("chatId") ;`)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
|||||||
-20
@@ -1,20 +0,0 @@
|
|||||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
|
||||||
|
|
||||||
export class AddFeedbackToChatMessage1707986407818 implements MigrationInterface {
|
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
||||||
await queryRunner.query(
|
|
||||||
`CREATE TABLE "temp_chat_message" ("id" varchar PRIMARY KEY NOT NULL, "role" varchar NOT NULL, "chatflowid" varchar NOT NULL, "content" text NOT NULL, "sourceDocuments" text, "usedTools" text, "fileAnnotations" text, "createdDate" datetime NOT NULL DEFAULT (datetime('now')), "chatType" VARCHAR NOT NULL DEFAULT 'INTERNAL', "chatId" VARCHAR NOT NULL, "memoryType" VARCHAR, "sessionId" VARCHAR, "feedbackId" varchar);`
|
|
||||||
)
|
|
||||||
await queryRunner.query(
|
|
||||||
`INSERT INTO "temp_chat_message" ("id", "role", "chatflowid", "content", "sourceDocuments", "usedTools", "fileAnnotations", "createdDate", "chatType", "chatId", "memoryType", "sessionId") SELECT "id", "role", "chatflowid", "content", "sourceDocuments", "usedTools", "fileAnnotations", "createdDate", "chatType", "chatId", "memoryType", "sessionId" FROM "chat_message";`
|
|
||||||
)
|
|
||||||
await queryRunner.query(`DROP TABLE "chat_message";`)
|
|
||||||
await queryRunner.query(`ALTER TABLE "temp_chat_message" RENAME TO "chat_message";`)
|
|
||||||
await queryRunner.query(`CREATE INDEX "IDX_e574527322272fd838f4f0f3d3" ON "chat_message" ("chatflowid") ;`)
|
|
||||||
}
|
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
||||||
await queryRunner.query(`DROP TABLE IF EXISTS "temp_chat_message";`)
|
|
||||||
await queryRunner.query(`ALTER TABLE "chat_message" DROP COLUMN "feedbackId";`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,6 @@ import { AddCategoryToChatFlow1699900910291 } from './1699900910291-AddCategoryT
|
|||||||
import { AddFileAnnotationsToChatMessage1700271021237 } from './1700271021237-AddFileAnnotationsToChatMessage'
|
import { AddFileAnnotationsToChatMessage1700271021237 } from './1700271021237-AddFileAnnotationsToChatMessage'
|
||||||
import { AddVariableEntity1699325775451 } from './1702200925471-AddVariableEntity'
|
import { AddVariableEntity1699325775451 } from './1702200925471-AddVariableEntity'
|
||||||
import { AddFeedback1707213619308 } from './1707213619308-AddFeedback'
|
import { AddFeedback1707213619308 } from './1707213619308-AddFeedback'
|
||||||
import { AddFeedbackToChatMessage1707986407818 } from './1707986407818-AddFeedbackToChatMessage'
|
|
||||||
|
|
||||||
export const sqliteMigrations = [
|
export const sqliteMigrations = [
|
||||||
Init1693835579790,
|
Init1693835579790,
|
||||||
@@ -28,6 +27,5 @@ export const sqliteMigrations = [
|
|||||||
AddCategoryToChatFlow1699900910291,
|
AddCategoryToChatFlow1699900910291,
|
||||||
AddFileAnnotationsToChatMessage1700271021237,
|
AddFileAnnotationsToChatMessage1700271021237,
|
||||||
AddVariableEntity1699325775451,
|
AddVariableEntity1699325775451,
|
||||||
AddFeedback1707213619308,
|
AddFeedback1707213619308
|
||||||
AddFeedbackToChatMessage1707986407818
|
|
||||||
]
|
]
|
||||||
|
|||||||
+119
-41
@@ -10,7 +10,7 @@ import logger from './utils/logger'
|
|||||||
import { expressRequestLogger } from './utils/logger'
|
import { expressRequestLogger } from './utils/logger'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import OpenAI from 'openai'
|
import OpenAI from 'openai'
|
||||||
import { FindOptionsWhere, MoreThanOrEqual, LessThanOrEqual } from 'typeorm'
|
import { FindOptionsWhere, MoreThanOrEqual, LessThanOrEqual, IsNull, Between } from 'typeorm'
|
||||||
import {
|
import {
|
||||||
IChatFlow,
|
IChatFlow,
|
||||||
IncomingInput,
|
IncomingInput,
|
||||||
@@ -22,7 +22,8 @@ import {
|
|||||||
IChatMessage,
|
IChatMessage,
|
||||||
IChatMessageFeedback,
|
IChatMessageFeedback,
|
||||||
IDepthQueue,
|
IDepthQueue,
|
||||||
INodeDirectedGraph
|
INodeDirectedGraph,
|
||||||
|
ChatMessageRatingType
|
||||||
} from './Interface'
|
} from './Interface'
|
||||||
import {
|
import {
|
||||||
getNodeModulesPackagePath,
|
getNodeModulesPackagePath,
|
||||||
@@ -170,7 +171,8 @@ export class App {
|
|||||||
'/api/v1/components-credentials-icon/',
|
'/api/v1/components-credentials-icon/',
|
||||||
'/api/v1/chatflows-streaming',
|
'/api/v1/chatflows-streaming',
|
||||||
'/api/v1/openai-assistants-file',
|
'/api/v1/openai-assistants-file',
|
||||||
'/api/v1/ip'
|
'/api/v1/ip',
|
||||||
|
'/api/v1/feedback'
|
||||||
]
|
]
|
||||||
this.app.use((req, res, next) => {
|
this.app.use((req, res, next) => {
|
||||||
if (req.url.includes('/api/v1/')) {
|
if (req.url.includes('/api/v1/')) {
|
||||||
@@ -610,31 +612,62 @@ export class App {
|
|||||||
// Chat Message Feedback
|
// Chat Message Feedback
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
// Create new feedback
|
// Get all chatmessage feedback from chatflowid
|
||||||
|
this.app.get('/api/v1/feedback/:id', async (req: Request, res: Response) => {
|
||||||
|
const chatflowid = req.params.id
|
||||||
|
const chatId = req.query?.chatId as string | undefined
|
||||||
|
const sortOrder = req.query?.order as string | undefined
|
||||||
|
const startDate = req.query?.startDate as string | undefined
|
||||||
|
const endDate = req.query?.endDate as string | undefined
|
||||||
|
|
||||||
|
const feedback = await this.getChatMessageFeedback(chatflowid, chatId, sortOrder, startDate, endDate)
|
||||||
|
|
||||||
|
return res.json(feedback)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add chatmessage feedback for chatflowid
|
||||||
this.app.post('/api/v1/feedback/:id', async (req: Request, res: Response) => {
|
this.app.post('/api/v1/feedback/:id', async (req: Request, res: Response) => {
|
||||||
const body = req.body
|
const body = req.body
|
||||||
const results = await this.addChatMessageFeedback(body)
|
const results = await this.addChatMessageFeedback(body)
|
||||||
return res.json(results)
|
return res.json(results)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Update feedback
|
// Update chatmessage feedback for id
|
||||||
this.app.put('/api/v1/feedback/:id', async (req: Request, res: Response) => {
|
this.app.put('/api/v1/feedback/:id', async (req: Request, res: Response) => {
|
||||||
|
const id = req.params.id
|
||||||
const body = req.body
|
const body = req.body
|
||||||
const chatMessageFeedback = await this.AppDataSource.getRepository(ChatMessageFeedback).findOneBy({
|
await this.updateChatMessageFeedback(id, body)
|
||||||
id: req.params.id
|
return res.json({ status: 'OK' })
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!chatMessageFeedback) {
|
// ----------------------------------------
|
||||||
res.status(404).send(`Feedback ${req.params.id} not found`)
|
// stats
|
||||||
return
|
// ----------------------------------------
|
||||||
|
//
|
||||||
|
// get stats for showing in chatflow
|
||||||
|
this.app.get('/api/v1/stats/:id', async (req: Request, res: Response) => {
|
||||||
|
const chatflowid = req.params.id
|
||||||
|
const chatTypeFilter = chatType.EXTERNAL
|
||||||
|
|
||||||
|
const totalMessages = await this.AppDataSource.getRepository(ChatMessage).count({
|
||||||
|
where: {
|
||||||
|
chatflowid,
|
||||||
|
chatType: chatTypeFilter
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const chatMessageFeedbackRepo = this.AppDataSource.getRepository(ChatMessageFeedback)
|
||||||
|
|
||||||
|
const totalFeedback = await chatMessageFeedbackRepo.count()
|
||||||
|
const positiveFeedback = await chatMessageFeedbackRepo.countBy({ rating: ChatMessageRatingType.THUMBS_UP })
|
||||||
|
|
||||||
|
const results = {
|
||||||
|
totalMessages,
|
||||||
|
totalFeedback,
|
||||||
|
positiveFeedback
|
||||||
}
|
}
|
||||||
|
|
||||||
const newChatMessageFeedback = new ChatMessageFeedback()
|
res.json(results)
|
||||||
Object.assign(newChatMessageFeedback, body)
|
|
||||||
|
|
||||||
this.AppDataSource.getRepository(ChatMessageFeedback).merge(chatMessageFeedback, newChatMessageFeedback)
|
|
||||||
const results = await this.AppDataSource.getRepository(ChatMessageFeedback).save(chatMessageFeedback)
|
|
||||||
return res.json(results)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
@@ -1497,6 +1530,28 @@ export class App {
|
|||||||
let toDate
|
let toDate
|
||||||
if (endDate) toDate = setDateToStartOrEndOfDay(endDate, 'end')
|
if (endDate) toDate = setDateToStartOrEndOfDay(endDate, 'end')
|
||||||
|
|
||||||
|
if (feedback) {
|
||||||
|
const messages = await this.AppDataSource.getRepository(ChatMessage)
|
||||||
|
.createQueryBuilder('chat_message')
|
||||||
|
.leftJoinAndMapOne('chat_message.feedback', ChatMessageFeedback, 'feedback', 'feedback.messageId = chat_message.id')
|
||||||
|
.where('chat_message.chatflowid = :chatflowid', { chatflowid })
|
||||||
|
.andWhere(chatType ? 'chat_message.chatType = :chatType' : 'TRUE', { chatType })
|
||||||
|
.andWhere(chatId ? 'chat_message.chatId = :chatId' : 'TRUE', { chatId })
|
||||||
|
.andWhere(memoryType ? 'chat_message.memoryType = :memoryType' : 'TRUE', {
|
||||||
|
memoryType: memoryType ?? (chatId ? IsNull() : undefined)
|
||||||
|
})
|
||||||
|
.andWhere(sessionId ? 'chat_message.sessionId = :sessionId' : 'TRUE', {
|
||||||
|
sessionId: sessionId ?? (chatId ? IsNull() : undefined)
|
||||||
|
})
|
||||||
|
.andWhere(fromDate && toDate ? 'chat_message.createdDate = :createdDate' : 'TRUE', {
|
||||||
|
createdDate: toDate && fromDate ? Between(fromDate, toDate) : undefined
|
||||||
|
})
|
||||||
|
.orderBy('chat_message.createdDate', sortOrder === 'DESC' ? 'DESC' : 'ASC')
|
||||||
|
.getMany()
|
||||||
|
|
||||||
|
return messages
|
||||||
|
}
|
||||||
|
|
||||||
return await this.AppDataSource.getRepository(ChatMessage).find({
|
return await this.AppDataSource.getRepository(ChatMessage).find({
|
||||||
where: {
|
where: {
|
||||||
chatflowid,
|
chatflowid,
|
||||||
@@ -1526,39 +1581,61 @@ export class App {
|
|||||||
return await this.AppDataSource.getRepository(ChatMessage).save(chatmessage)
|
return await this.AppDataSource.getRepository(ChatMessage).save(chatmessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateChatMessage(id: string, update: Partial<IChatMessage>) {
|
/**
|
||||||
const chatMessage = await this.AppDataSource.getRepository(ChatMessage).findOneBy({
|
* Method that get chat messages.
|
||||||
id
|
* @param {string} chatflowid
|
||||||
|
* @param {string} sortOrder
|
||||||
|
* @param {string} chatId
|
||||||
|
* @param {string} startDate
|
||||||
|
* @param {string} endDate
|
||||||
|
*/
|
||||||
|
async getChatMessageFeedback(
|
||||||
|
chatflowid: string,
|
||||||
|
chatId?: string,
|
||||||
|
sortOrder: string = 'ASC',
|
||||||
|
startDate?: string,
|
||||||
|
endDate?: string
|
||||||
|
): Promise<ChatMessageFeedback[]> {
|
||||||
|
let fromDate
|
||||||
|
if (startDate) fromDate = new Date(startDate)
|
||||||
|
|
||||||
|
let toDate
|
||||||
|
if (endDate) toDate = new Date(endDate)
|
||||||
|
return await this.AppDataSource.getRepository(ChatMessageFeedback).find({
|
||||||
|
where: {
|
||||||
|
chatflowid,
|
||||||
|
chatId,
|
||||||
|
createdDate: toDate && fromDate ? Between(fromDate, toDate) : undefined
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
createdDate: sortOrder === 'DESC' ? 'DESC' : 'ASC'
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!chatMessage) return
|
|
||||||
|
|
||||||
const newChatMessage = new ChatMessage()
|
|
||||||
Object.assign(newChatMessage, update)
|
|
||||||
|
|
||||||
this.AppDataSource.getRepository(ChatMessage).merge(chatMessage, newChatMessage)
|
|
||||||
return await this.AppDataSource.getRepository(ChatMessage).save(chatMessage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method that adds feedback for a chat message and updates the chat message with the feedback id.
|
* Method that add chat message feedback.
|
||||||
* @param {Partial<IChatMessageFeedback>} chatMessageFeedback
|
* @param {Partial<IChatMessageFeedback>} chatMessageFeedback
|
||||||
*/
|
*/
|
||||||
async addChatMessageFeedback(chatMessageFeedback: Partial<IChatMessageFeedback> & { messageId: string }): Promise<ChatMessageFeedback> {
|
async addChatMessageFeedback(chatMessageFeedback: Partial<IChatMessageFeedback>): Promise<ChatMessageFeedback> {
|
||||||
const messageId = chatMessageFeedback.messageId
|
const newChatMessageFeedback = new ChatMessageFeedback()
|
||||||
const newFeedback = new ChatMessageFeedback()
|
Object.assign(newChatMessageFeedback, chatMessageFeedback)
|
||||||
Object.assign(newFeedback, chatMessageFeedback)
|
|
||||||
|
|
||||||
const feedback = this.AppDataSource.getRepository(ChatMessageFeedback).create(newFeedback)
|
const feedback = this.AppDataSource.getRepository(ChatMessageFeedback).create(newChatMessageFeedback)
|
||||||
const results = await this.AppDataSource.getRepository(ChatMessageFeedback).save(feedback)
|
return await this.AppDataSource.getRepository(ChatMessageFeedback).save(feedback)
|
||||||
|
|
||||||
// use the message id to update the chat message with feedback id
|
|
||||||
await this.updateChatMessage(messageId, { feedbackId: results.id })
|
|
||||||
|
|
||||||
return results
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateChatMessageFeedback(id: string, update: Partial<IChatMessageFeedback>) {}
|
/**
|
||||||
|
* Method that updates chat message feedback.
|
||||||
|
* @param {string} id
|
||||||
|
* @param {Partial<IChatMessageFeedback>} chatMessageFeedback
|
||||||
|
*/
|
||||||
|
async updateChatMessageFeedback(id: string, chatMessageFeedback: Partial<IChatMessageFeedback>) {
|
||||||
|
const newChatMessageFeedback = new ChatMessageFeedback()
|
||||||
|
Object.assign(newChatMessageFeedback, chatMessageFeedback)
|
||||||
|
|
||||||
|
await this.AppDataSource.getRepository(ChatMessageFeedback).update({ id }, chatMessageFeedback)
|
||||||
|
}
|
||||||
|
|
||||||
async upsertVector(req: Request, res: Response, isInternal: boolean = false) {
|
async upsertVector(req: Request, res: Response, isInternal: boolean = false) {
|
||||||
try {
|
try {
|
||||||
@@ -1941,7 +2018,7 @@ export class App {
|
|||||||
sessionId,
|
sessionId,
|
||||||
createdDate: userMessageDateTime
|
createdDate: userMessageDateTime
|
||||||
}
|
}
|
||||||
await this.addChatMessage(userMessage)
|
const newMessage = await this.addChatMessage(userMessage)
|
||||||
|
|
||||||
let resultText = ''
|
let resultText = ''
|
||||||
if (result.text) resultText = result.text
|
if (result.text) resultText = result.text
|
||||||
@@ -1974,6 +2051,7 @@ export class App {
|
|||||||
|
|
||||||
// Prepare response
|
// Prepare response
|
||||||
result.chatId = chatId
|
result.chatId = chatId
|
||||||
|
if (newMessage.id && !isInternal) result.messageId = newMessage.id
|
||||||
if (sessionId) result.sessionId = sessionId
|
if (sessionId) result.sessionId = sessionId
|
||||||
if (memoryType) result.memoryType = memoryType
|
if (memoryType) result.memoryType = memoryType
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import client from './client'
|
import client from './client'
|
||||||
|
|
||||||
const getInternalChatmessageFromChatflow = (id) => client.get(`/internal-chatmessage/${id}`)
|
const getInternalChatmessageFromChatflow = (id) => client.get(`/internal-chatmessage/${id}`)
|
||||||
const getAllChatmessageFromChatflow = (id, params = {}) => client.get(`/chatmessage/${id}`, { params: { order: 'DESC', ...params } })
|
const getAllChatmessageFromChatflow = (id, params = {}) =>
|
||||||
const getChatmessageFromPK = (id, params = {}) => client.get(`/chatmessage/${id}`, { params: { order: 'ASC', ...params } })
|
client.get(`/chatmessage/${id}`, { params: { order: 'DESC', feedback: true, ...params } })
|
||||||
|
const getChatmessageFromPK = (id, params = {}) => client.get(`/chatmessage/${id}`, { params: { order: 'ASC', feedback: true, ...params } })
|
||||||
const deleteChatmessage = (id, params = {}) => client.delete(`/chatmessage/${id}`, { params: { ...params } })
|
const deleteChatmessage = (id, params = {}) => client.delete(`/chatmessage/${id}`, { params: { ...params } })
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import client from './client'
|
||||||
|
|
||||||
|
const getStatsFromChatflow = (id) => client.get(`/stats/${id}`)
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getStatsFromChatflow
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
|
import Card from '@mui/material/Card'
|
||||||
|
import CardContent from '@mui/material/CardContent'
|
||||||
|
import Typography from '@mui/material/Typography'
|
||||||
|
|
||||||
|
const StatsCard = ({ title, stat }) => {
|
||||||
|
const customization = useSelector((state) => state.customization)
|
||||||
|
return (
|
||||||
|
<Card sx={{ border: '1px solid #e0e0e0', borderRadius: `${customization.borderRadius}px` }}>
|
||||||
|
<CardContent>
|
||||||
|
<Typography sx={{ fontSize: 14 }} color='text.secondary' gutterBottom>
|
||||||
|
{title}
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ fontSize: 30, fontWeight: 500 }} color='text.secondary'>
|
||||||
|
{stat}
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
StatsCard.propTypes = {
|
||||||
|
title: PropTypes.string,
|
||||||
|
stat: PropTypes.string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StatsCard
|
||||||
@@ -37,12 +37,15 @@ import { CodeBlock } from 'ui-component/markdown/CodeBlock'
|
|||||||
import SourceDocDialog from 'ui-component/dialog/SourceDocDialog'
|
import SourceDocDialog from 'ui-component/dialog/SourceDocDialog'
|
||||||
import { MultiDropdown } from 'ui-component/dropdown/MultiDropdown'
|
import { MultiDropdown } from 'ui-component/dropdown/MultiDropdown'
|
||||||
import { StyledButton } from 'ui-component/button/StyledButton'
|
import { StyledButton } from 'ui-component/button/StyledButton'
|
||||||
|
import StatsCard from 'ui-component/cards/StatsCard'
|
||||||
|
import Feedback from 'ui-component/extended/Feedback'
|
||||||
|
|
||||||
// store
|
// store
|
||||||
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions'
|
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions'
|
||||||
|
|
||||||
// API
|
// API
|
||||||
import chatmessageApi from 'api/chatmessage'
|
import chatmessageApi from 'api/chatmessage'
|
||||||
|
import feedbackApi from 'api/feedback'
|
||||||
import useApi from 'hooks/useApi'
|
import useApi from 'hooks/useApi'
|
||||||
import useConfirm from 'hooks/useConfirm'
|
import useConfirm from 'hooks/useConfirm'
|
||||||
|
|
||||||
@@ -83,6 +86,7 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
|||||||
const [chatlogs, setChatLogs] = useState([])
|
const [chatlogs, setChatLogs] = useState([])
|
||||||
const [allChatlogs, setAllChatLogs] = useState([])
|
const [allChatlogs, setAllChatLogs] = useState([])
|
||||||
const [chatMessages, setChatMessages] = useState([])
|
const [chatMessages, setChatMessages] = useState([])
|
||||||
|
const [stats, setStats] = useState([])
|
||||||
const [selectedMessageIndex, setSelectedMessageIndex] = useState(0)
|
const [selectedMessageIndex, setSelectedMessageIndex] = useState(0)
|
||||||
const [sourceDialogOpen, setSourceDialogOpen] = useState(false)
|
const [sourceDialogOpen, setSourceDialogOpen] = useState(false)
|
||||||
const [sourceDialogProps, setSourceDialogProps] = useState({})
|
const [sourceDialogProps, setSourceDialogProps] = useState({})
|
||||||
@@ -92,6 +96,7 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
|||||||
|
|
||||||
const getChatmessageApi = useApi(chatmessageApi.getAllChatmessageFromChatflow)
|
const getChatmessageApi = useApi(chatmessageApi.getAllChatmessageFromChatflow)
|
||||||
const getChatmessageFromPKApi = useApi(chatmessageApi.getChatmessageFromPK)
|
const getChatmessageFromPKApi = useApi(chatmessageApi.getChatmessageFromPK)
|
||||||
|
const getStatsApi = useApi(feedbackApi.getStatsFromChatflow)
|
||||||
|
|
||||||
const onStartDateSelected = (date) => {
|
const onStartDateSelected = (date) => {
|
||||||
setStartDate(date)
|
setStartDate(date)
|
||||||
@@ -366,9 +371,16 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [getChatmessageApi.data])
|
}, [getChatmessageApi.data])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (getStatsApi.data) {
|
||||||
|
setStats(getStatsApi.data)
|
||||||
|
}
|
||||||
|
}, [getStatsApi.data])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dialogProps.chatflow) {
|
if (dialogProps.chatflow) {
|
||||||
getChatmessageApi.request(dialogProps.chatflow.id)
|
getChatmessageApi.request(dialogProps.chatflow.id)
|
||||||
|
getStatsApi.request(dialogProps.chatflow.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@@ -410,7 +422,33 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
|||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<>
|
<>
|
||||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', width: '100%', marginBottom: 10 }}>
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: 'repeat(3, minmax(0, 1fr))',
|
||||||
|
gap: 10,
|
||||||
|
marginBottom: 16,
|
||||||
|
marginLeft: 8,
|
||||||
|
marginRight: 8
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<StatsCard title='Total Messages (External)' stat={`${stats.totalMessages}`} />
|
||||||
|
<StatsCard title='Total Feedback Received' stat={`${stats.totalFeedback}`} />
|
||||||
|
<StatsCard
|
||||||
|
title='Positive Feedback'
|
||||||
|
stat={`${((stats.positiveFeedback / stats.totalFeedback) * 100 || 0).toFixed(2)}%`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: 16,
|
||||||
|
marginLeft: 8,
|
||||||
|
marginRight: 8
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div style={{ marginRight: 10 }}>
|
<div style={{ marginRight: 10 }}>
|
||||||
<b style={{ marginRight: 10 }}>From Date</b>
|
<b style={{ marginRight: 10 }}>From Date</b>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
@@ -728,6 +766,12 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{message.type === 'apiMessage' && message.feedback ? (
|
||||||
|
<Feedback
|
||||||
|
content={message.feedback?.content || ''}
|
||||||
|
rating={message.feedback?.rating}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { Alert, IconButton } from '@mui/material'
|
||||||
|
import { useTheme } from '@mui/material/styles'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
const ThumbsUpIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns='http://www.w3.org/2000/svg'
|
||||||
|
width='20'
|
||||||
|
height='20'
|
||||||
|
viewBox='0 0 24 24'
|
||||||
|
fill='none'
|
||||||
|
stroke='currentColor'
|
||||||
|
strokeWidth='2'
|
||||||
|
strokeLinecap='round'
|
||||||
|
strokeLinejoin='round'
|
||||||
|
>
|
||||||
|
<path d='M7 10v12' />
|
||||||
|
<path d='M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2h0a3.13 3.13 0 0 1 3 3.88Z' />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThumbsDownIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns='http://www.w3.org/2000/svg'
|
||||||
|
width='20'
|
||||||
|
height='20'
|
||||||
|
viewBox='0 0 24 24'
|
||||||
|
fill='none'
|
||||||
|
stroke='currentColor'
|
||||||
|
strokeWidth='2'
|
||||||
|
strokeLinecap='round'
|
||||||
|
strokeLinejoin='round'
|
||||||
|
>
|
||||||
|
<path d='M17 14V2' />
|
||||||
|
<path d='M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22h0a3.13 3.13 0 0 1-3-3.88Z' />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Feedback = ({ content, rating }) => {
|
||||||
|
const theme = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'start' }}>
|
||||||
|
{content ? (
|
||||||
|
<Alert
|
||||||
|
icon={rating === 'THUMBS_UP' ? <ThumbsUpIcon /> : <ThumbsDownIcon />}
|
||||||
|
severity={rating === 'THUMBS_UP' ? 'success' : 'error'}
|
||||||
|
style={{ marginBottom: 14 }}
|
||||||
|
variant='outlined'
|
||||||
|
>
|
||||||
|
{content ? <span style={{ color: theme.palette.text.primary }}>{content}</span> : null}
|
||||||
|
</Alert>
|
||||||
|
) : (
|
||||||
|
<IconButton color={rating === 'THUMBS_UP' ? 'success' : 'error'} style={{ marginBottom: 14 }}>
|
||||||
|
{rating === 'THUMBS_UP' ? <ThumbsUpIcon /> : <ThumbsDownIcon />}
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Feedback.propTypes = {
|
||||||
|
rating: PropTypes.oneOf(['THUMBS_UP', 'THUMBS_DOWN']),
|
||||||
|
content: PropTypes.string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Feedback
|
||||||
Reference in New Issue
Block a user