From 7d00f6fbe9b620bc3afd4c2558f05efbd1ef92e4 Mon Sep 17 00:00:00 2001 From: Ilango Date: Tue, 6 Feb 2024 17:07:22 +0530 Subject: [PATCH 01/19] Add database entity for chat message feedback --- packages/server/src/Interface.ts | 15 ++++++++++ .../database/entities/ChatMessageFeedback.ts | 28 +++++++++++++++++++ .../server/src/database/entities/index.ts | 2 ++ 3 files changed, 45 insertions(+) create mode 100644 packages/server/src/database/entities/ChatMessageFeedback.ts diff --git a/packages/server/src/Interface.ts b/packages/server/src/Interface.ts index 126aac38..aa5852b2 100644 --- a/packages/server/src/Interface.ts +++ b/packages/server/src/Interface.ts @@ -6,6 +6,11 @@ export enum chatType { INTERNAL = 'INTERNAL', EXTERNAL = 'EXTERNAL' } + +export enum ChatMessageRatingType { + THUMBS_UP = 'THUMBS_UP', + THUMBS_DOWN = 'THUMBS_DOWN' +} /** * Databases */ @@ -38,6 +43,16 @@ export interface IChatMessage { createdDate: Date } +export interface IChatMessageFeedback { + id: string + content?: string + chatflowid: string + chatId: string + messageId: string + rating: ChatMessageRatingType + createdDate: Date +} + export interface ITool { id: string name: string diff --git a/packages/server/src/database/entities/ChatMessageFeedback.ts b/packages/server/src/database/entities/ChatMessageFeedback.ts new file mode 100644 index 00000000..4011972d --- /dev/null +++ b/packages/server/src/database/entities/ChatMessageFeedback.ts @@ -0,0 +1,28 @@ +/* eslint-disable */ +import { Entity, Column, CreateDateColumn, PrimaryGeneratedColumn, Index } from 'typeorm' +import { ChatMessageRatingType, IChatMessageFeedback } from '../../Interface' + +@Entity() +export class ChatMessageFeedback implements IChatMessageFeedback { + @PrimaryGeneratedColumn('uuid') + id: string + + @Index() + @Column() + chatflowid: string + + @Column({ type: 'text' }) + content?: string + + @Column() + chatId: string + + @Column() + messageId: string + + @Column() + rating: ChatMessageRatingType + + @CreateDateColumn() + createdDate: Date +} diff --git a/packages/server/src/database/entities/index.ts b/packages/server/src/database/entities/index.ts index af5c559f..6a0e2d22 100644 --- a/packages/server/src/database/entities/index.ts +++ b/packages/server/src/database/entities/index.ts @@ -1,5 +1,6 @@ import { ChatFlow } from './ChatFlow' import { ChatMessage } from './ChatMessage' +import { ChatMessageFeedback } from './ChatMessageFeedback' import { Credential } from './Credential' import { Tool } from './Tool' import { Assistant } from './Assistant' @@ -8,6 +9,7 @@ import { Variable } from './Variable' export const entities = { ChatFlow, ChatMessage, + ChatMessageFeedback, Credential, Tool, Assistant, From 336b4174dcc97ab67af0f2976d20af953ed20211 Mon Sep 17 00:00:00 2001 From: Ilango Date: Tue, 6 Feb 2024 17:09:18 +0530 Subject: [PATCH 02/19] Add migrations --- .../mysql/1707213626553-AddFeedback.ts | 22 +++++++++++++++++++ .../src/database/migrations/mysql/index.ts | 4 +++- .../postgres/1707213601923-AddFeedback.ts | 22 +++++++++++++++++++ .../src/database/migrations/postgres/index.ts | 4 +++- .../sqlite/1707213619308-AddFeedback.ts | 13 +++++++++++ .../src/database/migrations/sqlite/index.ts | 4 +++- 6 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 packages/server/src/database/migrations/mysql/1707213626553-AddFeedback.ts create mode 100644 packages/server/src/database/migrations/postgres/1707213601923-AddFeedback.ts create mode 100644 packages/server/src/database/migrations/sqlite/1707213619308-AddFeedback.ts diff --git a/packages/server/src/database/migrations/mysql/1707213626553-AddFeedback.ts b/packages/server/src/database/migrations/mysql/1707213626553-AddFeedback.ts new file mode 100644 index 00000000..f2c86734 --- /dev/null +++ b/packages/server/src/database/migrations/mysql/1707213626553-AddFeedback.ts @@ -0,0 +1,22 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddFeedback1707213626553 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE IF NOT EXISTS \`chat_message_feedback\` ( + \`id\` varchar(36) NOT NULL, + \`chatflowid\` varchar(255) NOT NULL, + \`content\` text, + \`chatId\` varchar(255) NOT NULL, + \`messageId\` varchar(255) NOT NULL, + \`rating\` varchar(255) NOT NULL, + \`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + PRIMARY KEY (\`id\`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;` + ) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE chat_message_feedback`) + } +} diff --git a/packages/server/src/database/migrations/mysql/index.ts b/packages/server/src/database/migrations/mysql/index.ts index a5220ad8..6421365c 100644 --- a/packages/server/src/database/migrations/mysql/index.ts +++ b/packages/server/src/database/migrations/mysql/index.ts @@ -11,6 +11,7 @@ import { AddUsedToolsToChatMessage1699481607341 } from './1699481607341-AddUsedT import { AddCategoryToChatFlow1699900910291 } from './1699900910291-AddCategoryToChatFlow' import { AddFileAnnotationsToChatMessage1700271021237 } from './1700271021237-AddFileAnnotationsToChatMessage' import { AddVariableEntity1699325775451 } from './1702200925471-AddVariableEntity' +import { AddFeedback1707213626553 } from './1707213626553-AddFeedback' export const mysqlMigrations = [ Init1693840429259, @@ -25,5 +26,6 @@ export const mysqlMigrations = [ AddUsedToolsToChatMessage1699481607341, AddCategoryToChatFlow1699900910291, AddFileAnnotationsToChatMessage1700271021237, - AddVariableEntity1699325775451 + AddVariableEntity1699325775451, + AddFeedback1707213626553 ] diff --git a/packages/server/src/database/migrations/postgres/1707213601923-AddFeedback.ts b/packages/server/src/database/migrations/postgres/1707213601923-AddFeedback.ts new file mode 100644 index 00000000..779de42e --- /dev/null +++ b/packages/server/src/database/migrations/postgres/1707213601923-AddFeedback.ts @@ -0,0 +1,22 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddFeedback1707213601923 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE IF NOT EXISTS chat_message_feedback ( + id uuid NOT NULL DEFAULT uuid_generate_v4(), + "chatflowid" varchar NOT NULL, + "content" text, + "chatId" varchar NOT NULL, + "messageId" varchar NOT NULL, + "rating" varchar NOT NULL, + "createdDate" timestamp NOT NULL DEFAULT now(), + CONSTRAINT "PK_98419043dd704f54-9830ab78f8" PRIMARY KEY (id) + );` + ) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE chat_message_feedback`) + } +} diff --git a/packages/server/src/database/migrations/postgres/index.ts b/packages/server/src/database/migrations/postgres/index.ts index 3c3fa396..9e28980e 100644 --- a/packages/server/src/database/migrations/postgres/index.ts +++ b/packages/server/src/database/migrations/postgres/index.ts @@ -11,6 +11,7 @@ import { AddUsedToolsToChatMessage1699481607341 } from './1699481607341-AddUsedT import { AddCategoryToChatFlow1699900910291 } from './1699900910291-AddCategoryToChatFlow' import { AddFileAnnotationsToChatMessage1700271021237 } from './1700271021237-AddFileAnnotationsToChatMessage' import { AddVariableEntity1699325775451 } from './1702200925471-AddVariableEntity' +import { AddFeedback1707213601923 } from './1707213601923-AddFeedback' export const postgresMigrations = [ Init1693891895163, @@ -25,5 +26,6 @@ export const postgresMigrations = [ AddUsedToolsToChatMessage1699481607341, AddCategoryToChatFlow1699900910291, AddFileAnnotationsToChatMessage1700271021237, - AddVariableEntity1699325775451 + AddVariableEntity1699325775451, + AddFeedback1707213601923 ] diff --git a/packages/server/src/database/migrations/sqlite/1707213619308-AddFeedback.ts b/packages/server/src/database/migrations/sqlite/1707213619308-AddFeedback.ts new file mode 100644 index 00000000..b240ec1b --- /dev/null +++ b/packages/server/src/database/migrations/sqlite/1707213619308-AddFeedback.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddFeedback1707213619308 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + 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, "messageId" varchar NOT NULL, "rating" varchar NOT NULL, "createdDate" datetime NOT NULL DEFAULT (datetime('now')));` + ) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE IF EXISTS "chat_message_feedback";`) + } +} diff --git a/packages/server/src/database/migrations/sqlite/index.ts b/packages/server/src/database/migrations/sqlite/index.ts index c0ade080..4e6dc72c 100644 --- a/packages/server/src/database/migrations/sqlite/index.ts +++ b/packages/server/src/database/migrations/sqlite/index.ts @@ -11,6 +11,7 @@ import { AddUsedToolsToChatMessage1699481607341 } from './1699481607341-AddUsedT import { AddCategoryToChatFlow1699900910291 } from './1699900910291-AddCategoryToChatFlow' import { AddFileAnnotationsToChatMessage1700271021237 } from './1700271021237-AddFileAnnotationsToChatMessage' import { AddVariableEntity1699325775451 } from './1702200925471-AddVariableEntity' +import { AddFeedback1707213619308 } from './1707213619308-AddFeedback' export const sqliteMigrations = [ Init1693835579790, @@ -25,5 +26,6 @@ export const sqliteMigrations = [ AddUsedToolsToChatMessage1699481607341, AddCategoryToChatFlow1699900910291, AddFileAnnotationsToChatMessage1700271021237, - AddVariableEntity1699325775451 + AddVariableEntity1699325775451, + AddFeedback1707213619308 ] From 28b33f2c6b00da16ed03f7cfff9fa1d75060dfd7 Mon Sep 17 00:00:00 2001 From: Ilango Date: Mon, 12 Feb 2024 11:27:29 +0530 Subject: [PATCH 03/19] Add feedback endpoints --- .../src/database/entities/ChatMessage.ts | 7 ++- packages/server/src/index.ts | 45 +++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/packages/server/src/database/entities/ChatMessage.ts b/packages/server/src/database/entities/ChatMessage.ts index 4054a26d..f25f893c 100644 --- a/packages/server/src/database/entities/ChatMessage.ts +++ b/packages/server/src/database/entities/ChatMessage.ts @@ -1,6 +1,7 @@ /* eslint-disable */ -import { Entity, Column, CreateDateColumn, PrimaryGeneratedColumn, Index } from 'typeorm' +import { Entity, Column, CreateDateColumn, PrimaryGeneratedColumn, Index, OneToOne, JoinColumn } from 'typeorm' import { IChatMessage, MessageType } from '../../Interface' +import { ChatMessageFeedback } from './ChatMessageFeedback' @Entity() export class ChatMessage implements IChatMessage { @@ -40,4 +41,8 @@ export class ChatMessage implements IChatMessage { @CreateDateColumn() createdDate: Date + + @OneToOne(() => ChatMessageFeedback) + @JoinColumn() + feedback?: ChatMessageFeedback } diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index dbb5717d..f2f5fea7 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -10,7 +10,7 @@ import logger from './utils/logger' import { expressRequestLogger } from './utils/logger' import { v4 as uuidv4 } from 'uuid' import OpenAI from 'openai' -import { Between, IsNull, FindOptionsWhere } from 'typeorm' +import { Between, IsNull, FindOptionsWhere, createQueryBuilder } from 'typeorm' import { IChatFlow, IncomingInput, @@ -20,6 +20,7 @@ import { ICredentialReturnResponse, chatType, IChatMessage, + IChatMessageFeedback, IDepthQueue, INodeDirectedGraph } from './Interface' @@ -54,6 +55,7 @@ import { getDataSource } from './DataSource' import { NodesPool } from './NodesPool' import { ChatFlow } from './database/entities/ChatFlow' import { ChatMessage } from './database/entities/ChatMessage' +import { ChatMessageFeedback } from './database/entities/ChatMessageFeedback' import { Credential } from './database/entities/Credential' import { Tool } from './database/entities/Tool' import { Assistant } from './database/entities/Assistant' @@ -512,6 +514,7 @@ export class App { const sessionId = req.query?.sessionId as string | undefined 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 chatTypeFilter = req.query?.chatType as chatType | undefined if (chatTypeFilter) { @@ -537,7 +540,8 @@ export class App { memoryType, sessionId, startDate, - endDate + endDate, + feedback ) return res.json(chatmessages) }) @@ -599,6 +603,24 @@ export class App { return res.json(results) }) + // ---------------------------------------- + // Chat Message Feedback + // ---------------------------------------- + this.app.get('/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 results = await this.addChatMessageFeedback(body) + return res.json(results) + }) + + this.app.put('/api/v1/feedback/:id', async (req: Request, res: Response) => { + const chatflowid = req.params.id + const body = req.body + const results = await this.addChatMessageFeedback(body) + return res.json(results) + }) + // ---------------------------------------- // Credentials // ---------------------------------------- @@ -1422,6 +1444,7 @@ export class App { * @param {string} sessionId * @param {string} startDate * @param {string} endDate + * @param {boolean} feedback */ async getChatMessage( chatflowid: string, @@ -1431,7 +1454,8 @@ export class App { memoryType?: string, sessionId?: string, startDate?: string, - endDate?: string + endDate?: string, + feedback?: boolean ): Promise { let fromDate if (startDate) fromDate = new Date(startDate) @@ -1450,6 +1474,9 @@ export class App { }, order: { createdDate: sortOrder === 'DESC' ? 'DESC' : 'ASC' + }, + relations: { + feedback } }) } @@ -1466,6 +1493,18 @@ export class App { return await this.AppDataSource.getRepository(ChatMessage).save(chatmessage) } + /** + * Method that adds feedback for a chat message. + * @param {Partial} chatMessageFeedback + */ + async addChatMessageFeedback(chatMessageFeedback: Partial): Promise { + const newFeedback = new ChatMessageFeedback() + Object.assign(newFeedback, chatMessageFeedback) + + const feedback = this.AppDataSource.getRepository(ChatMessageFeedback).create(newFeedback) + return await this.AppDataSource.getRepository(ChatMessageFeedback).save(feedback) + } + async upsertVector(req: Request, res: Response, isInternal: boolean = false) { try { const chatflowid = req.params.id From 26d5d6d6a2cfa67b5844b8ec647240826bc40641 Mon Sep 17 00:00:00 2001 From: Ilango Date: Thu, 15 Feb 2024 15:13:47 +0530 Subject: [PATCH 04/19] Save feedback id to message and update feedback --- packages/server/src/Interface.ts | 2 +- .../src/database/entities/ChatMessage.ts | 8 ++- .../database/entities/ChatMessageFeedback.ts | 3 -- .../sqlite/1707213619308-AddFeedback.ts | 2 +- .../1707986407818-AddFeedbackToChatMessage.ts | 20 +++++++ .../src/database/migrations/sqlite/index.ts | 4 +- packages/server/src/index.ts | 52 +++++++++++++++---- 7 files changed, 70 insertions(+), 21 deletions(-) create mode 100644 packages/server/src/database/migrations/sqlite/1707986407818-AddFeedbackToChatMessage.ts diff --git a/packages/server/src/Interface.ts b/packages/server/src/Interface.ts index aa5852b2..c705cf79 100644 --- a/packages/server/src/Interface.ts +++ b/packages/server/src/Interface.ts @@ -41,6 +41,7 @@ export interface IChatMessage { memoryType?: string sessionId?: string createdDate: Date + feedbackId?: string } export interface IChatMessageFeedback { @@ -48,7 +49,6 @@ export interface IChatMessageFeedback { content?: string chatflowid: string chatId: string - messageId: string rating: ChatMessageRatingType createdDate: Date } diff --git a/packages/server/src/database/entities/ChatMessage.ts b/packages/server/src/database/entities/ChatMessage.ts index f25f893c..55a1c0e5 100644 --- a/packages/server/src/database/entities/ChatMessage.ts +++ b/packages/server/src/database/entities/ChatMessage.ts @@ -1,7 +1,6 @@ /* eslint-disable */ -import { Entity, Column, CreateDateColumn, PrimaryGeneratedColumn, Index, OneToOne, JoinColumn } from 'typeorm' +import { Entity, Column, CreateDateColumn, PrimaryGeneratedColumn, Index } from 'typeorm' import { IChatMessage, MessageType } from '../../Interface' -import { ChatMessageFeedback } from './ChatMessageFeedback' @Entity() export class ChatMessage implements IChatMessage { @@ -42,7 +41,6 @@ export class ChatMessage implements IChatMessage { @CreateDateColumn() createdDate: Date - @OneToOne(() => ChatMessageFeedback) - @JoinColumn() - feedback?: ChatMessageFeedback + @Column({ nullable: true }) + feedbackId?: string } diff --git a/packages/server/src/database/entities/ChatMessageFeedback.ts b/packages/server/src/database/entities/ChatMessageFeedback.ts index 4011972d..972994f9 100644 --- a/packages/server/src/database/entities/ChatMessageFeedback.ts +++ b/packages/server/src/database/entities/ChatMessageFeedback.ts @@ -17,9 +17,6 @@ export class ChatMessageFeedback implements IChatMessageFeedback { @Column() chatId: string - @Column() - messageId: string - @Column() rating: ChatMessageRatingType diff --git a/packages/server/src/database/migrations/sqlite/1707213619308-AddFeedback.ts b/packages/server/src/database/migrations/sqlite/1707213619308-AddFeedback.ts index b240ec1b..29ac76bc 100644 --- a/packages/server/src/database/migrations/sqlite/1707213619308-AddFeedback.ts +++ b/packages/server/src/database/migrations/sqlite/1707213619308-AddFeedback.ts @@ -3,7 +3,7 @@ import { MigrationInterface, QueryRunner } from 'typeorm' export class AddFeedback1707213619308 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { 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, "messageId" 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, "content" text, "chatId" varchar NOT NULL, "rating" varchar NOT NULL, "createdDate" datetime NOT NULL DEFAULT (datetime('now')));` ) } diff --git a/packages/server/src/database/migrations/sqlite/1707986407818-AddFeedbackToChatMessage.ts b/packages/server/src/database/migrations/sqlite/1707986407818-AddFeedbackToChatMessage.ts new file mode 100644 index 00000000..24af4ada --- /dev/null +++ b/packages/server/src/database/migrations/sqlite/1707986407818-AddFeedbackToChatMessage.ts @@ -0,0 +1,20 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddFeedbackToChatMessage1707986407818 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + 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 { + await queryRunner.query(`DROP TABLE IF EXISTS "temp_chat_message";`) + await queryRunner.query(`ALTER TABLE "chat_message" DROP COLUMN "feedbackId";`) + } +} diff --git a/packages/server/src/database/migrations/sqlite/index.ts b/packages/server/src/database/migrations/sqlite/index.ts index 4e6dc72c..5a54a5cd 100644 --- a/packages/server/src/database/migrations/sqlite/index.ts +++ b/packages/server/src/database/migrations/sqlite/index.ts @@ -12,6 +12,7 @@ import { AddCategoryToChatFlow1699900910291 } from './1699900910291-AddCategoryT import { AddFileAnnotationsToChatMessage1700271021237 } from './1700271021237-AddFileAnnotationsToChatMessage' import { AddVariableEntity1699325775451 } from './1702200925471-AddVariableEntity' import { AddFeedback1707213619308 } from './1707213619308-AddFeedback' +import { AddFeedbackToChatMessage1707986407818 } from './1707986407818-AddFeedbackToChatMessage' export const sqliteMigrations = [ Init1693835579790, @@ -27,5 +28,6 @@ export const sqliteMigrations = [ AddCategoryToChatFlow1699900910291, AddFileAnnotationsToChatMessage1700271021237, AddVariableEntity1699325775451, - AddFeedback1707213619308 + AddFeedback1707213619308, + AddFeedbackToChatMessage1707986407818 ] diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index d63b82a9..331b4fd0 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -10,7 +10,7 @@ import logger from './utils/logger' import { expressRequestLogger } from './utils/logger' import { v4 as uuidv4 } from 'uuid' import OpenAI from 'openai' -import { Between, IsNull, FindOptionsWhere, createQueryBuilder } from 'typeorm' +import { Between, IsNull, FindOptionsWhere } from 'typeorm' import { IChatFlow, IncomingInput, @@ -606,18 +606,31 @@ export class App { // ---------------------------------------- // Chat Message Feedback // ---------------------------------------- - this.app.get('/api/v1/feedback/:id', async (req: Request, res: Response) => {}) + // Create new feedback this.app.post('/api/v1/feedback/:id', async (req: Request, res: Response) => { const body = req.body const results = await this.addChatMessageFeedback(body) return res.json(results) }) + // Update feedback this.app.put('/api/v1/feedback/:id', async (req: Request, res: Response) => { - const chatflowid = req.params.id const body = req.body - const results = await this.addChatMessageFeedback(body) + const chatMessageFeedback = await this.AppDataSource.getRepository(ChatMessageFeedback).findOneBy({ + id: req.params.id + }) + + if (!chatMessageFeedback) { + res.status(404).send(`Feedback ${req.params.id} not found`) + return + } + + const newChatMessageFeedback = new ChatMessageFeedback() + Object.assign(newChatMessageFeedback, body) + + this.AppDataSource.getRepository(ChatMessageFeedback).merge(chatMessageFeedback, newChatMessageFeedback) + const results = await this.AppDataSource.getRepository(ChatMessageFeedback).save(chatMessageFeedback) return res.json(results) }) @@ -1476,9 +1489,6 @@ export class App { }, order: { createdDate: sortOrder === 'DESC' ? 'DESC' : 'ASC' - }, - relations: { - feedback } }) } @@ -1495,18 +1505,40 @@ export class App { return await this.AppDataSource.getRepository(ChatMessage).save(chatmessage) } + async updateChatMessage(id: string, update: Partial) { + const chatMessage = await this.AppDataSource.getRepository(ChatMessage).findOneBy({ + id + }) + + 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. + * Method that adds feedback for a chat message and updates the chat message with the feedback id. * @param {Partial} chatMessageFeedback */ - async addChatMessageFeedback(chatMessageFeedback: Partial): Promise { + async addChatMessageFeedback(chatMessageFeedback: Partial & { messageId: string }): Promise { + const messageId = chatMessageFeedback.messageId const newFeedback = new ChatMessageFeedback() Object.assign(newFeedback, chatMessageFeedback) const feedback = this.AppDataSource.getRepository(ChatMessageFeedback).create(newFeedback) - return await this.AppDataSource.getRepository(ChatMessageFeedback).save(feedback) + const results = 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) {} + async upsertVector(req: Request, res: Response, isInternal: boolean = false) { try { const chatflowid = req.params.id From 3bb2b398964942a81d9bbb8df034328799ab62f6 Mon Sep 17 00:00:00 2001 From: Ilango Date: Tue, 20 Feb 2024 14:38:36 +0530 Subject: [PATCH 05/19] Fix merge conflict and update how feedback is saved/retrieved --- packages/server/src/Interface.ts | 2 +- .../src/database/entities/ChatMessage.ts | 3 - .../database/entities/ChatMessageFeedback.ts | 15 +- .../sqlite/1707213619308-AddFeedback.ts | 4 +- .../1707986407818-AddFeedbackToChatMessage.ts | 20 --- .../src/database/migrations/sqlite/index.ts | 4 +- packages/server/src/index.ts | 160 +++++++++++++----- packages/ui/src/api/chatmessage.js | 5 +- packages/ui/src/api/feedback.js | 7 + .../ui/src/ui-component/cards/StatsCard.js | 29 ++++ .../ui-component/dialog/ViewMessagesDialog.js | 46 ++++- .../ui/src/ui-component/extended/Feedback.js | 71 ++++++++ 12 files changed, 289 insertions(+), 77 deletions(-) delete mode 100644 packages/server/src/database/migrations/sqlite/1707986407818-AddFeedbackToChatMessage.ts create mode 100644 packages/ui/src/api/feedback.js create mode 100644 packages/ui/src/ui-component/cards/StatsCard.js create mode 100644 packages/ui/src/ui-component/extended/Feedback.js diff --git a/packages/server/src/Interface.ts b/packages/server/src/Interface.ts index c705cf79..aa5852b2 100644 --- a/packages/server/src/Interface.ts +++ b/packages/server/src/Interface.ts @@ -41,7 +41,6 @@ export interface IChatMessage { memoryType?: string sessionId?: string createdDate: Date - feedbackId?: string } export interface IChatMessageFeedback { @@ -49,6 +48,7 @@ export interface IChatMessageFeedback { content?: string chatflowid: string chatId: string + messageId: string rating: ChatMessageRatingType createdDate: Date } diff --git a/packages/server/src/database/entities/ChatMessage.ts b/packages/server/src/database/entities/ChatMessage.ts index 55a1c0e5..4054a26d 100644 --- a/packages/server/src/database/entities/ChatMessage.ts +++ b/packages/server/src/database/entities/ChatMessage.ts @@ -40,7 +40,4 @@ export class ChatMessage implements IChatMessage { @CreateDateColumn() createdDate: Date - - @Column({ nullable: true }) - feedbackId?: string } diff --git a/packages/server/src/database/entities/ChatMessageFeedback.ts b/packages/server/src/database/entities/ChatMessageFeedback.ts index 972994f9..811f3104 100644 --- a/packages/server/src/database/entities/ChatMessageFeedback.ts +++ b/packages/server/src/database/entities/ChatMessageFeedback.ts @@ -1,8 +1,9 @@ /* eslint-disable */ -import { Entity, Column, CreateDateColumn, PrimaryGeneratedColumn, Index } from 'typeorm' -import { ChatMessageRatingType, IChatMessageFeedback } from '../../Interface' +import { Entity, Column, CreateDateColumn, PrimaryGeneratedColumn, Index, Unique } from 'typeorm' +import { IChatMessageFeedback, ChatMessageRatingType } from '../../Interface' @Entity() +@Unique(['messageId']) export class ChatMessageFeedback implements IChatMessageFeedback { @PrimaryGeneratedColumn('uuid') id: string @@ -11,15 +12,19 @@ export class ChatMessageFeedback implements IChatMessageFeedback { @Column() chatflowid: string - @Column({ type: 'text' }) - content?: string - + @Index() @Column() chatId: string @Column() + messageId: string + + @Column({ nullable: true }) rating: ChatMessageRatingType + @Column({ nullable: true, type: 'text' }) + content?: string + @CreateDateColumn() createdDate: Date } diff --git a/packages/server/src/database/migrations/sqlite/1707213619308-AddFeedback.ts b/packages/server/src/database/migrations/sqlite/1707213619308-AddFeedback.ts index 29ac76bc..b9002697 100644 --- a/packages/server/src/database/migrations/sqlite/1707213619308-AddFeedback.ts +++ b/packages/server/src/database/migrations/sqlite/1707213619308-AddFeedback.ts @@ -3,8 +3,10 @@ import { MigrationInterface, QueryRunner } from 'typeorm' export class AddFeedback1707213619308 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { 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 { diff --git a/packages/server/src/database/migrations/sqlite/1707986407818-AddFeedbackToChatMessage.ts b/packages/server/src/database/migrations/sqlite/1707986407818-AddFeedbackToChatMessage.ts deleted file mode 100644 index 24af4ada..00000000 --- a/packages/server/src/database/migrations/sqlite/1707986407818-AddFeedbackToChatMessage.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class AddFeedbackToChatMessage1707986407818 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - 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 { - await queryRunner.query(`DROP TABLE IF EXISTS "temp_chat_message";`) - await queryRunner.query(`ALTER TABLE "chat_message" DROP COLUMN "feedbackId";`) - } -} diff --git a/packages/server/src/database/migrations/sqlite/index.ts b/packages/server/src/database/migrations/sqlite/index.ts index 5a54a5cd..4e6dc72c 100644 --- a/packages/server/src/database/migrations/sqlite/index.ts +++ b/packages/server/src/database/migrations/sqlite/index.ts @@ -12,7 +12,6 @@ import { AddCategoryToChatFlow1699900910291 } from './1699900910291-AddCategoryT import { AddFileAnnotationsToChatMessage1700271021237 } from './1700271021237-AddFileAnnotationsToChatMessage' import { AddVariableEntity1699325775451 } from './1702200925471-AddVariableEntity' import { AddFeedback1707213619308 } from './1707213619308-AddFeedback' -import { AddFeedbackToChatMessage1707986407818 } from './1707986407818-AddFeedbackToChatMessage' export const sqliteMigrations = [ Init1693835579790, @@ -28,6 +27,5 @@ export const sqliteMigrations = [ AddCategoryToChatFlow1699900910291, AddFileAnnotationsToChatMessage1700271021237, AddVariableEntity1699325775451, - AddFeedback1707213619308, - AddFeedbackToChatMessage1707986407818 + AddFeedback1707213619308 ] diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 498b2e2c..ab998a15 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -10,7 +10,7 @@ import logger from './utils/logger' import { expressRequestLogger } from './utils/logger' import { v4 as uuidv4 } from 'uuid' import OpenAI from 'openai' -import { FindOptionsWhere, MoreThanOrEqual, LessThanOrEqual } from 'typeorm' +import { FindOptionsWhere, MoreThanOrEqual, LessThanOrEqual, IsNull, Between } from 'typeorm' import { IChatFlow, IncomingInput, @@ -22,7 +22,8 @@ import { IChatMessage, IChatMessageFeedback, IDepthQueue, - INodeDirectedGraph + INodeDirectedGraph, + ChatMessageRatingType } from './Interface' import { getNodeModulesPackagePath, @@ -170,7 +171,8 @@ export class App { '/api/v1/components-credentials-icon/', '/api/v1/chatflows-streaming', '/api/v1/openai-assistants-file', - '/api/v1/ip' + '/api/v1/ip', + '/api/v1/feedback' ] this.app.use((req, res, next) => { if (req.url.includes('/api/v1/')) { @@ -610,31 +612,62 @@ export class App { // 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) => { const body = req.body const results = await this.addChatMessageFeedback(body) return res.json(results) }) - // Update feedback + // Update chatmessage feedback for id this.app.put('/api/v1/feedback/:id', async (req: Request, res: Response) => { + const id = req.params.id const body = req.body - const chatMessageFeedback = await this.AppDataSource.getRepository(ChatMessageFeedback).findOneBy({ - id: req.params.id + await this.updateChatMessageFeedback(id, body) + return res.json({ status: 'OK' }) + }) + + // ---------------------------------------- + // stats + // ---------------------------------------- + // + // 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 + } }) - if (!chatMessageFeedback) { - res.status(404).send(`Feedback ${req.params.id} not found`) - return + 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() - Object.assign(newChatMessageFeedback, body) - - this.AppDataSource.getRepository(ChatMessageFeedback).merge(chatMessageFeedback, newChatMessageFeedback) - const results = await this.AppDataSource.getRepository(ChatMessageFeedback).save(chatMessageFeedback) - return res.json(results) + res.json(results) }) // ---------------------------------------- @@ -1497,6 +1530,28 @@ export class App { let toDate 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({ where: { chatflowid, @@ -1526,39 +1581,61 @@ export class App { return await this.AppDataSource.getRepository(ChatMessage).save(chatmessage) } - async updateChatMessage(id: string, update: Partial) { - const chatMessage = await this.AppDataSource.getRepository(ChatMessage).findOneBy({ - id + /** + * Method that get chat messages. + * @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 { + 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} chatMessageFeedback */ - async addChatMessageFeedback(chatMessageFeedback: Partial & { messageId: string }): Promise { - const messageId = chatMessageFeedback.messageId - const newFeedback = new ChatMessageFeedback() - Object.assign(newFeedback, chatMessageFeedback) + async addChatMessageFeedback(chatMessageFeedback: Partial): Promise { + const newChatMessageFeedback = new ChatMessageFeedback() + Object.assign(newChatMessageFeedback, chatMessageFeedback) - const feedback = this.AppDataSource.getRepository(ChatMessageFeedback).create(newFeedback) - const results = 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 + const feedback = this.AppDataSource.getRepository(ChatMessageFeedback).create(newChatMessageFeedback) + return await this.AppDataSource.getRepository(ChatMessageFeedback).save(feedback) } - async updateChatMessageFeedback(id: string, update: Partial) {} + /** + * Method that updates chat message feedback. + * @param {string} id + * @param {Partial} chatMessageFeedback + */ + async updateChatMessageFeedback(id: string, chatMessageFeedback: Partial) { + 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) { try { @@ -1941,7 +2018,7 @@ export class App { sessionId, createdDate: userMessageDateTime } - await this.addChatMessage(userMessage) + const newMessage = await this.addChatMessage(userMessage) let resultText = '' if (result.text) resultText = result.text @@ -1974,6 +2051,7 @@ export class App { // Prepare response result.chatId = chatId + if (newMessage.id && !isInternal) result.messageId = newMessage.id if (sessionId) result.sessionId = sessionId if (memoryType) result.memoryType = memoryType diff --git a/packages/ui/src/api/chatmessage.js b/packages/ui/src/api/chatmessage.js index 5f1a4bad..7b084c46 100644 --- a/packages/ui/src/api/chatmessage.js +++ b/packages/ui/src/api/chatmessage.js @@ -1,8 +1,9 @@ import client from './client' const getInternalChatmessageFromChatflow = (id) => client.get(`/internal-chatmessage/${id}`) -const getAllChatmessageFromChatflow = (id, params = {}) => client.get(`/chatmessage/${id}`, { params: { order: 'DESC', ...params } }) -const getChatmessageFromPK = (id, params = {}) => client.get(`/chatmessage/${id}`, { params: { order: 'ASC', ...params } }) +const getAllChatmessageFromChatflow = (id, 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 } }) export default { diff --git a/packages/ui/src/api/feedback.js b/packages/ui/src/api/feedback.js new file mode 100644 index 00000000..5b32ca26 --- /dev/null +++ b/packages/ui/src/api/feedback.js @@ -0,0 +1,7 @@ +import client from './client' + +const getStatsFromChatflow = (id) => client.get(`/stats/${id}`) + +export default { + getStatsFromChatflow +} diff --git a/packages/ui/src/ui-component/cards/StatsCard.js b/packages/ui/src/ui-component/cards/StatsCard.js new file mode 100644 index 00000000..69433505 --- /dev/null +++ b/packages/ui/src/ui-component/cards/StatsCard.js @@ -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 ( + + + + {title} + + + {stat} + + + + ) +} + +StatsCard.propTypes = { + title: PropTypes.string, + stat: PropTypes.string +} + +export default StatsCard diff --git a/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js b/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js index cadd4abd..f52723b2 100644 --- a/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js +++ b/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js @@ -37,12 +37,15 @@ import { CodeBlock } from 'ui-component/markdown/CodeBlock' import SourceDocDialog from 'ui-component/dialog/SourceDocDialog' import { MultiDropdown } from 'ui-component/dropdown/MultiDropdown' import { StyledButton } from 'ui-component/button/StyledButton' +import StatsCard from 'ui-component/cards/StatsCard' +import Feedback from 'ui-component/extended/Feedback' // store import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions' // API import chatmessageApi from 'api/chatmessage' +import feedbackApi from 'api/feedback' import useApi from 'hooks/useApi' import useConfirm from 'hooks/useConfirm' @@ -83,6 +86,7 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { const [chatlogs, setChatLogs] = useState([]) const [allChatlogs, setAllChatLogs] = useState([]) const [chatMessages, setChatMessages] = useState([]) + const [stats, setStats] = useState([]) const [selectedMessageIndex, setSelectedMessageIndex] = useState(0) const [sourceDialogOpen, setSourceDialogOpen] = useState(false) const [sourceDialogProps, setSourceDialogProps] = useState({}) @@ -92,6 +96,7 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { const getChatmessageApi = useApi(chatmessageApi.getAllChatmessageFromChatflow) const getChatmessageFromPKApi = useApi(chatmessageApi.getChatmessageFromPK) + const getStatsApi = useApi(feedbackApi.getStatsFromChatflow) const onStartDateSelected = (date) => { setStartDate(date) @@ -366,9 +371,16 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [getChatmessageApi.data]) + useEffect(() => { + if (getStatsApi.data) { + setStats(getStatsApi.data) + } + }, [getStatsApi.data]) + useEffect(() => { if (dialogProps.chatflow) { getChatmessageApi.request(dialogProps.chatflow.id) + getStatsApi.request(dialogProps.chatflow.id) } return () => { @@ -410,7 +422,33 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { <> -
+
+ + + +
+
From Date { })}
)} + {message.type === 'apiMessage' && message.feedback ? ( + + ) : null}
) diff --git a/packages/ui/src/ui-component/extended/Feedback.js b/packages/ui/src/ui-component/extended/Feedback.js new file mode 100644 index 00000000..5a943ec5 --- /dev/null +++ b/packages/ui/src/ui-component/extended/Feedback.js @@ -0,0 +1,71 @@ +import { Alert, IconButton } from '@mui/material' +import { useTheme } from '@mui/material/styles' +import PropTypes from 'prop-types' + +const ThumbsUpIcon = () => { + return ( + + + + + ) +} + +const ThumbsDownIcon = () => { + return ( + + + + + ) +} + +const Feedback = ({ content, rating }) => { + const theme = useTheme() + + return ( +
+ {content ? ( + : } + severity={rating === 'THUMBS_UP' ? 'success' : 'error'} + style={{ marginBottom: 14 }} + variant='outlined' + > + {content ? {content} : null} + + ) : ( + + {rating === 'THUMBS_UP' ? : } + + )} +
+ ) +} + +Feedback.propTypes = { + rating: PropTypes.oneOf(['THUMBS_UP', 'THUMBS_DOWN']), + content: PropTypes.string +} + +export default Feedback From 78677d9ee51b4427fd793b4b0233125d99698e0d Mon Sep 17 00:00:00 2001 From: Ilango Date: Tue, 20 Feb 2024 15:23:15 +0530 Subject: [PATCH 06/19] Add dialog for controlling chat feedback settings --- packages/ui/src/menu-items/settings.js | 20 ++- .../ui-component/dialog/ChatFeedbackDialog.js | 137 ++++++++++++++++++ packages/ui/src/ui-component/switch/Switch.js | 17 ++- packages/ui/src/views/canvas/CanvasHeader.js | 15 ++ 4 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 packages/ui/src/ui-component/dialog/ChatFeedbackDialog.js diff --git a/packages/ui/src/menu-items/settings.js b/packages/ui/src/menu-items/settings.js index 1e0f58dd..5003392c 100644 --- a/packages/ui/src/menu-items/settings.js +++ b/packages/ui/src/menu-items/settings.js @@ -1,8 +1,17 @@ // assets -import { IconTrash, IconFileUpload, IconFileExport, IconCopy, IconSearch, IconMessage, IconPictureInPictureOff } from '@tabler/icons' +import { + IconTrash, + IconFileUpload, + IconFileExport, + IconCopy, + IconSearch, + IconMessage, + IconPictureInPictureOff, + IconThumbUp +} from '@tabler/icons' // constant -const icons = { IconTrash, IconFileUpload, IconFileExport, IconCopy, IconSearch, IconMessage, IconPictureInPictureOff } +const icons = { IconTrash, IconFileUpload, IconFileExport, IconCopy, IconSearch, IconMessage, IconPictureInPictureOff, IconThumbUp } // ==============================|| SETTINGS MENU ITEMS ||============================== // @@ -25,6 +34,13 @@ const settings = { url: '', icon: icons.IconMessage }, + { + id: 'chatFeedback', + title: 'Chat Feedback', + type: 'item', + url: '', + icon: icons.IconThumbUp + }, { id: 'duplicateChatflow', title: 'Duplicate Chatflow', diff --git a/packages/ui/src/ui-component/dialog/ChatFeedbackDialog.js b/packages/ui/src/ui-component/dialog/ChatFeedbackDialog.js new file mode 100644 index 00000000..a88c6f35 --- /dev/null +++ b/packages/ui/src/ui-component/dialog/ChatFeedbackDialog.js @@ -0,0 +1,137 @@ +import { createPortal } from 'react-dom' +import { useDispatch } from 'react-redux' +import { useState, useEffect } from 'react' +import PropTypes from 'prop-types' +import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction, SET_CHATFLOW } from 'store/actions' + +// material-ui +import { Button, Dialog, DialogContent, DialogTitle, DialogActions, Box } from '@mui/material' +import { IconX } from '@tabler/icons' + +// Project import +import { StyledButton } from 'ui-component/button/StyledButton' +import { SwitchInput } from 'ui-component/switch/Switch' + +// store +import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions' +import useNotifier from 'utils/useNotifier' + +// API +import chatflowsApi from 'api/chatflows' + +const ChatFeedbackDialog = ({ show, dialogProps, onCancel, onConfirm }) => { + const portalElement = document.getElementById('portal') + const dispatch = useDispatch() + + useNotifier() + + const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args)) + const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args)) + + const [chatFeedbackStatus, setChatFeedbackStatus] = useState(false) + const [chatbotConfig, setChatbotConfig] = useState({}) + + const handleChange = (value) => { + setChatFeedbackStatus(value) + } + + const onSave = async () => { + try { + let value = { + chatFeedback: { + status: chatFeedbackStatus + } + } + chatbotConfig.chatFeedback = value.chatFeedback + const saveResp = await chatflowsApi.updateChatflow(dialogProps.chatflow.id, { + chatbotConfig: JSON.stringify(chatbotConfig) + }) + if (saveResp.data) { + enqueueSnackbar({ + message: 'Chat Feedback Settings Saved', + options: { + key: new Date().getTime() + Math.random(), + variant: 'success', + action: (key) => ( + + ) + } + }) + dispatch({ type: SET_CHATFLOW, chatflow: saveResp.data }) + } + onConfirm() + } catch (error) { + const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}` + enqueueSnackbar({ + message: `Failed to save Chat Feedback Settings: ${errorData}`, + options: { + key: new Date().getTime() + Math.random(), + variant: 'error', + persist: true, + action: (key) => ( + + ) + } + }) + } + } + + useEffect(() => { + if (dialogProps.chatflow && dialogProps.chatflow.chatbotConfig) { + let chatbotConfig = JSON.parse(dialogProps.chatflow.chatbotConfig) + setChatbotConfig(chatbotConfig || {}) + if (chatbotConfig.chatFeedback) { + setChatFeedbackStatus(chatbotConfig.chatFeedback.status) + } + } + + return () => {} + }, [dialogProps]) + + useEffect(() => { + if (show) dispatch({ type: SHOW_CANVAS_DIALOG }) + else dispatch({ type: HIDE_CANVAS_DIALOG }) + return () => dispatch({ type: HIDE_CANVAS_DIALOG }) + }, [show, dispatch]) + + const component = show ? ( + + + {dialogProps.title || 'Chat Feedback'} + + + + + + + + + + Save + + + + ) : null + + return createPortal(component, portalElement) +} + +ChatFeedbackDialog.propTypes = { + show: PropTypes.bool, + dialogProps: PropTypes.object, + onCancel: PropTypes.func, + onConfirm: PropTypes.func +} + +export default ChatFeedbackDialog diff --git a/packages/ui/src/ui-component/switch/Switch.js b/packages/ui/src/ui-component/switch/Switch.js index 16a923f1..50ace119 100644 --- a/packages/ui/src/ui-component/switch/Switch.js +++ b/packages/ui/src/ui-component/switch/Switch.js @@ -1,13 +1,21 @@ -import { useState } from 'react' +import { useEffect, useState } from 'react' import PropTypes from 'prop-types' -import { FormControl, Switch } from '@mui/material' +import { FormControl, Switch, Typography } from '@mui/material' -export const SwitchInput = ({ value, onChange, disabled = false }) => { +export const SwitchInput = ({ label, value, onChange, disabled = false }) => { const [myValue, setMyValue] = useState(!!value ?? false) + useEffect(() => { + setMyValue(value) + }, [value]) + return ( <> - + + {label && {label}} { } SwitchInput.propTypes = { + label: PropTypes.string, value: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), onChange: PropTypes.func, disabled: PropTypes.bool diff --git a/packages/ui/src/views/canvas/CanvasHeader.js b/packages/ui/src/views/canvas/CanvasHeader.js index 85408cd8..af9d5e2f 100644 --- a/packages/ui/src/views/canvas/CanvasHeader.js +++ b/packages/ui/src/views/canvas/CanvasHeader.js @@ -17,6 +17,7 @@ import APICodeDialog from 'views/chatflows/APICodeDialog' import AnalyseFlowDialog from 'ui-component/dialog/AnalyseFlowDialog' import ViewMessagesDialog from 'ui-component/dialog/ViewMessagesDialog' import StarterPromptsDialog from 'ui-component/dialog/StarterPromptsDialog' +import ChatFeedbackDialog from 'ui-component/dialog/ChatFeedbackDialog' // API import chatflowsApi from 'api/chatflows' @@ -50,6 +51,8 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl const [conversationStartersDialogProps, setConversationStartersDialogProps] = useState({}) const [viewMessagesDialogOpen, setViewMessagesDialogOpen] = useState(false) const [viewMessagesDialogProps, setViewMessagesDialogProps] = useState({}) + const [chatFeedbackDialogOpen, setChatFeedbackDialogOpen] = useState(false) + const [chatFeedbackDialogProps, setChatFeedbackDialogProps] = useState({}) const updateChatflowApi = useApi(chatflowsApi.updateChatflow) const canvas = useSelector((state) => state.canvas) @@ -65,6 +68,12 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl chatflow: chatflow }) setConversationStartersDialogOpen(true) + } else if (setting === 'chatFeedback') { + setChatFeedbackDialogProps({ + title: `Chat Feedback - ${chatflow.name}`, + chatflow: chatflow + }) + setChatFeedbackDialogOpen(true) } else if (setting === 'analyseChatflow') { setAnalyseDialogProps({ title: 'Analyse Chatflow', @@ -391,6 +400,12 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl onConfirm={() => setConversationStartersDialogOpen(false)} onCancel={() => setConversationStartersDialogOpen(false)} /> + setChatFeedbackDialogOpen(false)} + onCancel={() => setChatFeedbackDialogOpen(false)} + /> Date: Wed, 21 Feb 2024 14:22:50 +0530 Subject: [PATCH 07/19] Fix text color in feedback stats --- packages/ui/src/ui-component/cards/StatsCard.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/ui-component/cards/StatsCard.js b/packages/ui/src/ui-component/cards/StatsCard.js index 69433505..57e8205d 100644 --- a/packages/ui/src/ui-component/cards/StatsCard.js +++ b/packages/ui/src/ui-component/cards/StatsCard.js @@ -10,10 +10,10 @@ const StatsCard = ({ title, stat }) => { return ( - + {title} - + {stat} From 68d56be7c0a1485d7cc0127452ab9c865e8cd70e Mon Sep 17 00:00:00 2001 From: Ilango Date: Thu, 22 Feb 2024 16:13:46 +0530 Subject: [PATCH 08/19] Remove related feedback and update stats when clearing chat in view messages --- packages/server/src/index.ts | 4 ++++ packages/ui/src/ui-component/dialog/ViewMessagesDialog.js | 1 + 2 files changed, 5 insertions(+) diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index ab998a15..abfc66ad 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -604,6 +604,10 @@ export class App { if (sessionId) deleteOptions.sessionId = sessionId if (chatType) deleteOptions.chatType = chatType + // remove all related feedback records + const feedbackDeleteOptions: FindOptionsWhere = { chatId } + await this.AppDataSource.getRepository(ChatMessageFeedback).delete(feedbackDeleteOptions) + const results = await this.AppDataSource.getRepository(ChatMessage).delete(deleteOptions) return res.json(results) }) diff --git a/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js b/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js index f52723b2..a9490d27 100644 --- a/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js +++ b/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js @@ -214,6 +214,7 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { } }) getChatmessageApi.request(chatflowid) + getStatsApi.request(chatflowid) // update stats } catch (error) { const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}` enqueueSnackbar({ From c5c396a0ed2dbd6aa9469e9e5eb20b3d033e8f79 Mon Sep 17 00:00:00 2001 From: Henry Date: Tue, 27 Feb 2024 01:00:24 +0800 Subject: [PATCH 09/19] add feedback to exported chat --- packages/ui/src/ui-component/dialog/ViewMessagesDialog.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js b/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js index a9490d27..10b9f276 100644 --- a/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js +++ b/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js @@ -138,6 +138,7 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { if (chatmsg.sourceDocuments) msg.sourceDocuments = JSON.parse(chatmsg.sourceDocuments) if (chatmsg.usedTools) msg.usedTools = JSON.parse(chatmsg.usedTools) if (chatmsg.fileAnnotations) msg.fileAnnotations = JSON.parse(chatmsg.fileAnnotations) + if (chatmsg.feedback) msg.feedback = chatmsg.feedback?.content if (!Object.prototype.hasOwnProperty.call(obj, chatPK)) { obj[chatPK] = { From 256e3250869a8aeb2f46e0edbc477551cac10e80 Mon Sep 17 00:00:00 2001 From: Ilango Date: Tue, 27 Feb 2024 13:11:52 +0530 Subject: [PATCH 10/19] Fix incorrect stats --- packages/server/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index abfc66ad..6f1f03a9 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -662,8 +662,8 @@ export class App { const chatMessageFeedbackRepo = this.AppDataSource.getRepository(ChatMessageFeedback) - const totalFeedback = await chatMessageFeedbackRepo.count() - const positiveFeedback = await chatMessageFeedbackRepo.countBy({ rating: ChatMessageRatingType.THUMBS_UP }) + const totalFeedback = await chatMessageFeedbackRepo.count({ where: { chatflowid } }) + const positiveFeedback = await chatMessageFeedbackRepo.countBy({ chatflowid, rating: ChatMessageRatingType.THUMBS_UP }) const results = { totalMessages, From 5698a626186e56827afea4f86df4b3d6d40ca8d8 Mon Sep 17 00:00:00 2001 From: Ilango Date: Tue, 27 Feb 2024 15:35:49 +0530 Subject: [PATCH 11/19] Fix date filters not working --- packages/server/src/index.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index f57f6c00..893f5054 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -1542,14 +1542,11 @@ export class App { .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 + .andWhere(memoryType ? 'chat_message.memoryType = :memoryType' : 'TRUE', { memoryType }) + .andWhere(sessionId ? 'chat_message.sessionId = :sessionId' : 'TRUE', { sessionId }) + .andWhere('chat_message.createdDate BETWEEN :fromDate AND :toDate', { + fromDate: fromDate ?? new Date().setMonth(new Date().getMonth() - 1), + toDate: toDate ?? new Date() }) .orderBy('chat_message.createdDate', sortOrder === 'DESC' ? 'DESC' : 'ASC') .getMany() From c684cec59648394f38ab8524dd1ed7f9f5f01a60 Mon Sep 17 00:00:00 2001 From: Ilango Date: Tue, 27 Feb 2024 16:58:13 +0530 Subject: [PATCH 12/19] Update how chat message feedback are queried for view messages dialog --- packages/server/src/index.ts | 39 +++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 893f5054..4fed5fe1 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -1536,21 +1536,36 @@ export class App { if (endDate) toDate = setDateToStartOrEndOfDay(endDate, 'end') if (feedback) { - const messages = await this.AppDataSource.getRepository(ChatMessage) - .createQueryBuilder('chat_message') + const query = this.AppDataSource.getRepository(ChatMessage).createQueryBuilder('chat_message') + + // do the join with chat message feedback based on messageId for each chat message in the chatflow + query .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 }) - .andWhere(sessionId ? 'chat_message.sessionId = :sessionId' : 'TRUE', { sessionId }) - .andWhere('chat_message.createdDate BETWEEN :fromDate AND :toDate', { - fromDate: fromDate ?? new Date().setMonth(new Date().getMonth() - 1), - toDate: toDate ?? new Date() - }) - .orderBy('chat_message.createdDate', sortOrder === 'DESC' ? 'DESC' : 'ASC') - .getMany() + // based on which parameters are available add `andWhere` clauses to the query + if (chatType) { + query.andWhere('chat_message.chatType = :chatType', { chatType }) + } + if (chatId) { + query.andWhere('chat_message.chatId = :chatId', { chatId }) + } + if (memoryType) { + query.andWhere('chat_message.memoryType = :memoryType', { memoryType }) + } + if (sessionId) { + query.andWhere('chat_message.sessionId = :sessionId', { sessionId }) + } + + // set date range + query.andWhere('chat_message.createdDate BETWEEN :fromDate AND :toDate', { + fromDate: fromDate ?? new Date().setMonth(new Date().getMonth() - 1), + toDate: toDate ?? new Date() + }) + // sort + query.orderBy('chat_message.createdDate', sortOrder === 'DESC' ? 'DESC' : 'ASC') + + const messages = await query.getMany() return messages } From f0d129d22d84ea015d81ebfd05a5e6d4efc88818 Mon Sep 17 00:00:00 2001 From: Ilango Date: Wed, 28 Feb 2024 22:16:13 +0530 Subject: [PATCH 13/19] Pin mui versions since they were causing styling issues --- packages/ui/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/ui/package.json b/packages/ui/package.json index df32d7fa..872077a5 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -14,10 +14,10 @@ "@emotion/cache": "^11.4.0", "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", - "@mui/icons-material": "^5.0.3", - "@mui/lab": "^5.0.0-alpha.156", - "@mui/material": "^5.15.0", - "@mui/x-data-grid": "^6.8.0", + "@mui/icons-material": "5.0.3", + "@mui/lab": "5.0.0-alpha.156", + "@mui/material": "5.15.0", + "@mui/x-data-grid": "6.8.0", "@tabler/icons": "^1.39.1", "@uiw/codemirror-theme-sublime": "^4.21.21", "@uiw/codemirror-theme-vscode": "^4.21.21", From 9c10822546c84b54f40c97d294800c289a8990c7 Mon Sep 17 00:00:00 2001 From: Ilango Date: Thu, 29 Feb 2024 05:10:52 +0530 Subject: [PATCH 14/19] Update stats card text in view messages dialog --- packages/ui/src/ui-component/dialog/ViewMessagesDialog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js b/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js index cf184271..a0b3e396 100644 --- a/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js +++ b/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js @@ -473,7 +473,7 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { marginRight: 8 }} > - + Date: Mon, 11 Mar 2024 21:07:20 +0530 Subject: [PATCH 15/19] Implement chat message feedback for UI chat window --- packages/server/src/index.ts | 22 ++- packages/ui/src/api/chatmessage.js | 3 +- packages/ui/src/api/chatmessagefeedback.js | 9 ++ .../button/CopyToClipboardButton.js | 31 ++++ .../ui-component/button/ThumbsDownButton.js | 31 ++++ .../src/ui-component/button/ThumbsUpButton.js | 31 ++++ .../dialog/ChatFeedbackContentDialog.js | 73 ++++++++++ .../ui/src/views/chatmessage/ChatMessage.js | 133 +++++++++++++++++- 8 files changed, 330 insertions(+), 3 deletions(-) create mode 100644 packages/ui/src/api/chatmessagefeedback.js create mode 100644 packages/ui/src/ui-component/button/CopyToClipboardButton.js create mode 100644 packages/ui/src/ui-component/button/ThumbsDownButton.js create mode 100644 packages/ui/src/ui-component/button/ThumbsUpButton.js create mode 100644 packages/ui/src/ui-component/dialog/ChatFeedbackContentDialog.js diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index f087e40b..c40581bf 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -595,7 +595,27 @@ export class App { // 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) + const sortOrder = req.query?.order as string | undefined + const chatId = req.query?.chatId as string | undefined + const memoryType = req.query?.memoryType as string | undefined + const sessionId = req.query?.sessionId as string | undefined + const messageId = req.query?.messageId as string | undefined + const startDate = req.query?.startDate as string | undefined + const endDate = req.query?.endDate as string | undefined + const feedback = req.query?.feedback as boolean | undefined + + const chatmessages = await this.getChatMessage( + req.params.id, + chatType.INTERNAL, + sortOrder, + chatId, + memoryType, + sessionId, + startDate, + endDate, + messageId, + feedback + ) return res.json(chatmessages) }) diff --git a/packages/ui/src/api/chatmessage.js b/packages/ui/src/api/chatmessage.js index 7941ccf5..8760ce07 100644 --- a/packages/ui/src/api/chatmessage.js +++ b/packages/ui/src/api/chatmessage.js @@ -1,6 +1,7 @@ import client from './client' -const getInternalChatmessageFromChatflow = (id) => client.get(`/internal-chatmessage/${id}`) +const getInternalChatmessageFromChatflow = (id, params = {}) => + client.get(`/internal-chatmessage/${id}`, { params: { feedback: true, ...params } }) const getAllChatmessageFromChatflow = (id, 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 } }) diff --git a/packages/ui/src/api/chatmessagefeedback.js b/packages/ui/src/api/chatmessagefeedback.js new file mode 100644 index 00000000..1916cfed --- /dev/null +++ b/packages/ui/src/api/chatmessagefeedback.js @@ -0,0 +1,9 @@ +import client from './client' + +const addFeedback = (id, body) => client.post(`/feedback/${id}`, body) +const updateFeedback = (id, body) => client.put(`/feedback/${id}`, body) + +export default { + addFeedback, + updateFeedback +} diff --git a/packages/ui/src/ui-component/button/CopyToClipboardButton.js b/packages/ui/src/ui-component/button/CopyToClipboardButton.js new file mode 100644 index 00000000..ee9cc752 --- /dev/null +++ b/packages/ui/src/ui-component/button/CopyToClipboardButton.js @@ -0,0 +1,31 @@ +import PropTypes from 'prop-types' +import { useSelector } from 'react-redux' +import { IconButton } from '@mui/material' +import { IconClipboard } from '@tabler/icons' + +const CopyToClipboardButton = (props) => { + const customization = useSelector((state) => state.customization) + + return ( + + + + ) +} + +CopyToClipboardButton.propTypes = { + isDisabled: PropTypes.bool, + isLoading: PropTypes.bool, + onClick: PropTypes.func +} + +export default CopyToClipboardButton diff --git a/packages/ui/src/ui-component/button/ThumbsDownButton.js b/packages/ui/src/ui-component/button/ThumbsDownButton.js new file mode 100644 index 00000000..6ee9e09a --- /dev/null +++ b/packages/ui/src/ui-component/button/ThumbsDownButton.js @@ -0,0 +1,31 @@ +import PropTypes from 'prop-types' +import { useSelector } from 'react-redux' +import { IconButton } from '@mui/material' +import { IconThumbDown } from '@tabler/icons' + +const ThumbsDownButton = (props) => { + const customization = useSelector((state) => state.customization) + return ( + + + + ) +} + +ThumbsDownButton.propTypes = { + isDisabled: PropTypes.bool, + isLoading: PropTypes.bool, + onClick: PropTypes.func, + rating: PropTypes.string +} + +export default ThumbsDownButton diff --git a/packages/ui/src/ui-component/button/ThumbsUpButton.js b/packages/ui/src/ui-component/button/ThumbsUpButton.js new file mode 100644 index 00000000..c1b5106e --- /dev/null +++ b/packages/ui/src/ui-component/button/ThumbsUpButton.js @@ -0,0 +1,31 @@ +import PropTypes from 'prop-types' +import { useSelector } from 'react-redux' +import { IconButton } from '@mui/material' +import { IconThumbUp } from '@tabler/icons' + +const ThumbsUpButton = (props) => { + const customization = useSelector((state) => state.customization) + return ( + + + + ) +} + +ThumbsUpButton.propTypes = { + isDisabled: PropTypes.bool, + isLoading: PropTypes.bool, + onClick: PropTypes.func, + rating: PropTypes.string +} + +export default ThumbsUpButton diff --git a/packages/ui/src/ui-component/dialog/ChatFeedbackContentDialog.js b/packages/ui/src/ui-component/dialog/ChatFeedbackContentDialog.js new file mode 100644 index 00000000..56d81d14 --- /dev/null +++ b/packages/ui/src/ui-component/dialog/ChatFeedbackContentDialog.js @@ -0,0 +1,73 @@ +import { useCallback, useEffect } from 'react' +import { createPortal } from 'react-dom' +import { useDispatch } from 'react-redux' + +// material-ui +import { Button, Dialog, DialogContent, DialogTitle, DialogActions, Box, OutlinedInput } from '@mui/material' +import { useState } from 'react' + +// Project import +import { StyledButton } from 'ui-component/button/StyledButton' + +// store +import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions' + +const ChatFeedbackContentDialog = ({ show, onCancel, onConfirm }) => { + const portalElement = document.getElementById('portal') + const dispatch = useDispatch() + + const [feedbackContent, setFeedbackContent] = useState('') + + const onChange = useCallback((e) => setFeedbackContent(e.target.value), [setFeedbackContent]) + + const onSave = () => { + onConfirm(feedbackContent) + } + + useEffect(() => { + if (show) dispatch({ type: SHOW_CANVAS_DIALOG }) + else dispatch({ type: HIDE_CANVAS_DIALOG }) + return () => dispatch({ type: HIDE_CANVAS_DIALOG }) + }, [show, dispatch]) + + const component = show ? ( + + + Provide additional feedback + + + + + + + + + + Submit Feedback + + + + ) : null + + return createPortal(component, portalElement) +} + +export default ChatFeedbackContentDialog diff --git a/packages/ui/src/views/chatmessage/ChatMessage.js b/packages/ui/src/views/chatmessage/ChatMessage.js index 75a466d3..55a157d1 100644 --- a/packages/ui/src/views/chatmessage/ChatMessage.js +++ b/packages/ui/src/views/chatmessage/ChatMessage.js @@ -32,9 +32,13 @@ import audioUploadSVG from 'assets/images/wave-sound.jpg' import { CodeBlock } from 'ui-component/markdown/CodeBlock' import { MemoizedReactMarkdown } from 'ui-component/markdown/MemoizedReactMarkdown' import SourceDocDialog from 'ui-component/dialog/SourceDocDialog' +import ChatFeedbackContentDialog from 'ui-component/dialog/ChatFeedbackContentDialog' import StarterPromptsCard from 'ui-component/cards/StarterPromptsCard' import { cancelAudioRecording, startAudioRecording, stopAudioRecording } from './audio-recording' import { ImageButton, ImageSrc, ImageBackdrop, ImageMarked } from 'ui-component/button/ImageButton' +import CopyToClipboardButton from 'ui-component/button/CopyToClipboardButton' +import ThumbsUpButton from 'ui-component/button/ThumbsUpButton' +import ThumbsDownButton from 'ui-component/button/ThumbsDownButton' import './ChatMessage.css' import './audio-recording.css' @@ -42,6 +46,7 @@ import './audio-recording.css' import chatmessageApi from 'api/chatmessage' import chatflowsApi from 'api/chatflows' import predictionApi from 'api/prediction' +import chatmessagefeedbackApi from 'api/chatmessagefeedback' // Hooks import useApi from 'hooks/useApi' @@ -86,6 +91,9 @@ export const ChatMessage = ({ open, chatflowid, isDialog, previews, setPreviews const getChatflowConfig = useApi(chatflowsApi.getSpecificChatflow) const [starterPrompts, setStarterPrompts] = useState([]) + const [chatFeedbackStatus, setChatFeedbackStatus] = useState(false) + const [feedbackId, setFeedbackId] = useState('') + const [showFeedbackContentDialog, setShowFeedbackContentDialog] = useState(false) // drag & drop and file input const fileUploadRef = useRef(null) @@ -318,6 +326,7 @@ export const ChatMessage = ({ open, chatflowid, isDialog, previews, setPreviews let allMessages = [...cloneDeep(prevMessages)] if (allMessages[allMessages.length - 1].type === 'userMessage') return allMessages allMessages[allMessages.length - 1].message += text + allMessages[allMessages.length - 1].feedback = null return allMessages }) } @@ -389,6 +398,14 @@ export const ChatMessage = ({ open, chatflowid, isDialog, previews, setPreviews if (response.data) { const data = response.data + setMessages((prevMessages) => { + let allMessages = [...cloneDeep(prevMessages)] + if (allMessages[allMessages.length - 1].type === 'apiMessage') { + allMessages[allMessages.length - 1].id = data?.chatMessageId + } + return allMessages + }) + if (!chatId) setChatId(data.chatId) if (input === '' && data.question) { @@ -412,10 +429,12 @@ export const ChatMessage = ({ open, chatflowid, isDialog, previews, setPreviews ...prevMessages, { message: text, + id: data?.chatMessageId, sourceDocuments: data?.sourceDocuments, usedTools: data?.usedTools, fileAnnotations: data?.fileAnnotations, - type: 'apiMessage' + type: 'apiMessage', + feedback: null } ]) } @@ -474,7 +493,9 @@ export const ChatMessage = ({ open, chatflowid, isDialog, previews, setPreviews setChatId(chatId) const loadedMessages = getChatmessageApi.data.map((message) => { const obj = { + id: message.id, message: message.content, + feedback: message.feedback, type: message.role } if (message.sourceDocuments) obj.sourceDocuments = JSON.parse(message.sourceDocuments) @@ -527,6 +548,9 @@ export const ChatMessage = ({ open, chatflowid, isDialog, previews, setPreviews }) setStarterPrompts(inputFields) } + if (config.chatFeedback) { + setChatFeedbackStatus(config.chatFeedback.status) + } } } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -604,6 +628,83 @@ export const ChatMessage = ({ open, chatflowid, isDialog, previews, setPreviews // eslint-disable-next-line }, [previews]) + const copyMessageToClipboard = async (text) => { + try { + await navigator.clipboard.writeText(text || '') + } catch (error) { + console.error('Error copying to clipboard:', error) + } + } + + const onThumbsUpClick = async (messageId) => { + const body = { + chatflowid, + chatId, + messageId, + rating: 'THUMBS_UP', + content: '' + } + const result = await chatmessagefeedbackApi.addFeedback(chatflowid, body) + if (result.data) { + const data = result.data + let id = '' + if (data && data.id) id = data.id + setMessages((prevMessages) => { + const allMessages = [...cloneDeep(prevMessages)] + return allMessages.map((message) => { + if (message.id === messageId) { + message.feedback = { + rating: 'THUMBS_UP' + } + } + return message + }) + }) + setFeedbackId(id) + setShowFeedbackContentDialog(true) + } + } + + const onThumbsDownClick = async (messageId) => { + const body = { + chatflowid, + chatId, + messageId, + rating: 'THUMBS_DOWN', + content: '' + } + const result = await chatmessagefeedbackApi.addFeedback(chatflowid, body) + if (result.data) { + const data = result.data + let id = '' + if (data && data.id) id = data.id + setMessages((prevMessages) => { + const allMessages = [...cloneDeep(prevMessages)] + return allMessages.map((message) => { + if (message.id === messageId) { + message.feedback = { + rating: 'THUMBS_DOWN' + } + } + return message + }) + }) + setFeedbackId(id) + setShowFeedbackContentDialog(true) + } + } + + const submitFeedbackContent = async (text) => { + const body = { + content: text + } + const result = await chatmessagefeedbackApi.updateFeedback(feedbackId, body) + if (result.data) { + setFeedbackId('') + setShowFeedbackContentDialog(false) + } + } + return (
{isDragActive && ( @@ -747,6 +848,36 @@ export const ChatMessage = ({ open, chatflowid, isDialog, previews, setPreviews {message.message}
+ {message.type === 'apiMessage' && message.id && chatFeedbackStatus ? ( + <> + + copyMessageToClipboard(message.message)} /> + {!message.feedback || + message.feedback.rating === '' || + message.feedback.rating === 'THUMBS_UP' ? ( + onThumbsUpClick(message.id)} + /> + ) : null} + {!message.feedback || + message.feedback.rating === '' || + message.feedback.rating === 'THUMBS_DOWN' ? ( + onThumbsDownClick(message.id)} + /> + ) : null} + + setShowFeedbackContentDialog(false)} + onConfirm={submitFeedbackContent} + /> + + ) : null} {message.fileAnnotations && (
{message.fileAnnotations.map((fileAnnotation, index) => { From ed6b3b2a6d7becc887b534cdbcf1919d5822b697 Mon Sep 17 00:00:00 2001 From: Ilango Date: Mon, 11 Mar 2024 21:54:42 +0530 Subject: [PATCH 16/19] Update stats API - date and source filters apply to stats --- packages/server/src/index.ts | 40 +++++++++++---- packages/ui/src/api/feedback.js | 2 +- .../ui-component/dialog/ViewMessagesDialog.js | 50 ++++++++++++------- 3 files changed, 65 insertions(+), 27 deletions(-) diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index c40581bf..1877eca8 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -723,19 +723,41 @@ export class App { // 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 + let chatTypeFilter = req.query?.chatType as chatType | undefined + const startDate = req.query?.startDate as string | undefined + const endDate = req.query?.endDate as string | undefined - const totalMessages = await this.AppDataSource.getRepository(ChatMessage).count({ - where: { - chatflowid, - chatType: chatTypeFilter + if (chatTypeFilter) { + try { + const chatTypeFilterArray = JSON.parse(chatTypeFilter) + if (chatTypeFilterArray.includes(chatType.EXTERNAL) && chatTypeFilterArray.includes(chatType.INTERNAL)) { + chatTypeFilter = undefined + } else if (chatTypeFilterArray.includes(chatType.EXTERNAL)) { + chatTypeFilter = chatType.EXTERNAL + } else if (chatTypeFilterArray.includes(chatType.INTERNAL)) { + chatTypeFilter = chatType.INTERNAL + } + } catch (e) { + return res.status(500).send(e) } - }) + } - const chatMessageFeedbackRepo = this.AppDataSource.getRepository(ChatMessageFeedback) + const chatmessages = (await this.getChatMessage( + chatflowid, + chatTypeFilter, + undefined, + undefined, + undefined, + undefined, + startDate, + endDate, + '', + true + )) as Array + const totalMessages = chatmessages.length - const totalFeedback = await chatMessageFeedbackRepo.count({ where: { chatflowid } }) - const positiveFeedback = await chatMessageFeedbackRepo.countBy({ chatflowid, rating: ChatMessageRatingType.THUMBS_UP }) + const totalFeedback = chatmessages.filter((message) => !message?.feedback).length + const positiveFeedback = chatmessages.filter((message) => message?.feedback?.rating === 'THUMBS_UP').length const results = { totalMessages, diff --git a/packages/ui/src/api/feedback.js b/packages/ui/src/api/feedback.js index 5b32ca26..4c2becf8 100644 --- a/packages/ui/src/api/feedback.js +++ b/packages/ui/src/api/feedback.js @@ -1,6 +1,6 @@ import client from './client' -const getStatsFromChatflow = (id) => client.get(`/stats/${id}`) +const getStatsFromChatflow = (id, params) => client.get(`/stats/${id}`, { params: { ...params } }) export default { getStatsFromChatflow diff --git a/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js b/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js index a0b3e396..240d1421 100644 --- a/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js +++ b/packages/ui/src/ui-component/dialog/ViewMessagesDialog.js @@ -115,6 +115,11 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { endDate: endDate, chatType: chatTypeFilter.length ? chatTypeFilter : undefined }) + getStatsApi.request(dialogProps.chatflow.id, { + startDate: date, + endDate: endDate, + chatType: chatTypeFilter.length ? chatTypeFilter : undefined + }) } const onEndDateSelected = (date) => { @@ -124,6 +129,11 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { startDate: startDate, chatType: chatTypeFilter.length ? chatTypeFilter : undefined }) + getStatsApi.request(dialogProps.chatflow.id, { + endDate: date, + startDate: startDate, + chatType: chatTypeFilter.length ? chatTypeFilter : undefined + }) } const onChatTypeSelected = (chatTypes) => { @@ -133,6 +143,11 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { startDate: startDate, endDate: endDate }) + getStatsApi.request(dialogProps.chatflow.id, { + chatType: chatTypes.length ? chatTypes : undefined, + startDate: startDate, + endDate: endDate + }) } const exportMessages = async () => { @@ -432,6 +447,7 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { setSelectedMessageIndex(0) setStartDate(new Date().setMonth(new Date().getMonth() - 1)) setEndDate(new Date()) + setStats([]) } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -463,23 +479,6 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => { <> -
- - - -
{
+
+ + + +
{chatlogs && chatlogs.length == 0 && ( From 2a30dbfd4c693b257fb65526f588e8c66a599750 Mon Sep 17 00:00:00 2001 From: Ilango Date: Mon, 11 Mar 2024 22:00:43 +0530 Subject: [PATCH 17/19] Fix issue in total feedback received stat --- packages/server/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 1877eca8..56a3451a 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -756,7 +756,7 @@ export class App { )) as Array const totalMessages = chatmessages.length - const totalFeedback = chatmessages.filter((message) => !message?.feedback).length + const totalFeedback = chatmessages.filter((message) => message?.feedback).length const positiveFeedback = chatmessages.filter((message) => message?.feedback?.rating === 'THUMBS_UP').length const results = { From f16254203e3543b6e332f7abc42333e01dfae5e6 Mon Sep 17 00:00:00 2001 From: Ilango Date: Tue, 12 Mar 2024 13:33:47 +0530 Subject: [PATCH 18/19] Fix build errors --- ...boardButton.js => CopyToClipboardButton.jsx} | 0 ...ThumbsDownButton.js => ThumbsDownButton.jsx} | 0 .../{ThumbsUpButton.js => ThumbsUpButton.jsx} | 0 .../cards/{StatsCard.js => StatsCard.jsx} | 0 ...tDialog.js => ChatFeedbackContentDialog.jsx} | 4 ++-- ...FeedbackDialog.js => ChatFeedbackDialog.jsx} | 17 +++++++++++------ .../extended/{Feedback.js => Feedback.jsx} | 0 7 files changed, 13 insertions(+), 8 deletions(-) rename packages/ui/src/ui-component/button/{CopyToClipboardButton.js => CopyToClipboardButton.jsx} (100%) rename packages/ui/src/ui-component/button/{ThumbsDownButton.js => ThumbsDownButton.jsx} (100%) rename packages/ui/src/ui-component/button/{ThumbsUpButton.js => ThumbsUpButton.jsx} (100%) rename packages/ui/src/ui-component/cards/{StatsCard.js => StatsCard.jsx} (100%) rename packages/ui/src/ui-component/dialog/{ChatFeedbackContentDialog.js => ChatFeedbackContentDialog.jsx} (94%) rename packages/ui/src/ui-component/dialog/{ChatFeedbackDialog.js => ChatFeedbackDialog.jsx} (91%) rename packages/ui/src/ui-component/extended/{Feedback.js => Feedback.jsx} (100%) diff --git a/packages/ui/src/ui-component/button/CopyToClipboardButton.js b/packages/ui/src/ui-component/button/CopyToClipboardButton.jsx similarity index 100% rename from packages/ui/src/ui-component/button/CopyToClipboardButton.js rename to packages/ui/src/ui-component/button/CopyToClipboardButton.jsx diff --git a/packages/ui/src/ui-component/button/ThumbsDownButton.js b/packages/ui/src/ui-component/button/ThumbsDownButton.jsx similarity index 100% rename from packages/ui/src/ui-component/button/ThumbsDownButton.js rename to packages/ui/src/ui-component/button/ThumbsDownButton.jsx diff --git a/packages/ui/src/ui-component/button/ThumbsUpButton.js b/packages/ui/src/ui-component/button/ThumbsUpButton.jsx similarity index 100% rename from packages/ui/src/ui-component/button/ThumbsUpButton.js rename to packages/ui/src/ui-component/button/ThumbsUpButton.jsx diff --git a/packages/ui/src/ui-component/cards/StatsCard.js b/packages/ui/src/ui-component/cards/StatsCard.jsx similarity index 100% rename from packages/ui/src/ui-component/cards/StatsCard.js rename to packages/ui/src/ui-component/cards/StatsCard.jsx diff --git a/packages/ui/src/ui-component/dialog/ChatFeedbackContentDialog.js b/packages/ui/src/ui-component/dialog/ChatFeedbackContentDialog.jsx similarity index 94% rename from packages/ui/src/ui-component/dialog/ChatFeedbackContentDialog.js rename to packages/ui/src/ui-component/dialog/ChatFeedbackContentDialog.jsx index 56d81d14..3bb5524d 100644 --- a/packages/ui/src/ui-component/dialog/ChatFeedbackContentDialog.js +++ b/packages/ui/src/ui-component/dialog/ChatFeedbackContentDialog.jsx @@ -7,10 +7,10 @@ import { Button, Dialog, DialogContent, DialogTitle, DialogActions, Box, Outline import { useState } from 'react' // Project import -import { StyledButton } from 'ui-component/button/StyledButton' +import { StyledButton } from '@/ui-component/button/StyledButton' // store -import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions' +import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '@/store/actions' const ChatFeedbackContentDialog = ({ show, onCancel, onConfirm }) => { const portalElement = document.getElementById('portal') diff --git a/packages/ui/src/ui-component/dialog/ChatFeedbackDialog.js b/packages/ui/src/ui-component/dialog/ChatFeedbackDialog.jsx similarity index 91% rename from packages/ui/src/ui-component/dialog/ChatFeedbackDialog.js rename to packages/ui/src/ui-component/dialog/ChatFeedbackDialog.jsx index a88c6f35..54bb4408 100644 --- a/packages/ui/src/ui-component/dialog/ChatFeedbackDialog.js +++ b/packages/ui/src/ui-component/dialog/ChatFeedbackDialog.jsx @@ -2,22 +2,27 @@ import { createPortal } from 'react-dom' import { useDispatch } from 'react-redux' import { useState, useEffect } from 'react' import PropTypes from 'prop-types' -import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction, SET_CHATFLOW } from 'store/actions' // material-ui import { Button, Dialog, DialogContent, DialogTitle, DialogActions, Box } from '@mui/material' import { IconX } from '@tabler/icons' // Project import -import { StyledButton } from 'ui-component/button/StyledButton' -import { SwitchInput } from 'ui-component/switch/Switch' +import { StyledButton } from '@/ui-component/button/StyledButton' +import { SwitchInput } from '@/ui-component/switch/Switch' // store -import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions' -import useNotifier from 'utils/useNotifier' +import { + enqueueSnackbar as enqueueSnackbarAction, + closeSnackbar as closeSnackbarAction, + SET_CHATFLOW, + HIDE_CANVAS_DIALOG, + SHOW_CANVAS_DIALOG +} from '@/store/actions' +import useNotifier from '@/utils/useNotifier' // API -import chatflowsApi from 'api/chatflows' +import chatflowsApi from '@/api/chatflows' const ChatFeedbackDialog = ({ show, dialogProps, onCancel, onConfirm }) => { const portalElement = document.getElementById('portal') diff --git a/packages/ui/src/ui-component/extended/Feedback.js b/packages/ui/src/ui-component/extended/Feedback.jsx similarity index 100% rename from packages/ui/src/ui-component/extended/Feedback.js rename to packages/ui/src/ui-component/extended/Feedback.jsx From 2853b3149bf3b049e1db9b0e5704ce4644f59776 Mon Sep 17 00:00:00 2001 From: Henry Date: Tue, 12 Mar 2024 18:46:34 +0800 Subject: [PATCH 19/19] update dialog position, and clear feedback content after dialog disappear --- .../ui-component/dialog/ChatFeedbackContentDialog.jsx | 5 ++++- packages/ui/src/views/chatmessage/ChatMessage.jsx | 10 +++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/ui/src/ui-component/dialog/ChatFeedbackContentDialog.jsx b/packages/ui/src/ui-component/dialog/ChatFeedbackContentDialog.jsx index 3bb5524d..6b34aeca 100644 --- a/packages/ui/src/ui-component/dialog/ChatFeedbackContentDialog.jsx +++ b/packages/ui/src/ui-component/dialog/ChatFeedbackContentDialog.jsx @@ -27,7 +27,10 @@ const ChatFeedbackContentDialog = ({ show, onCancel, onConfirm }) => { useEffect(() => { if (show) dispatch({ type: SHOW_CANVAS_DIALOG }) else dispatch({ type: HIDE_CANVAS_DIALOG }) - return () => dispatch({ type: HIDE_CANVAS_DIALOG }) + return () => { + dispatch({ type: HIDE_CANVAS_DIALOG }) + setFeedbackContent('') + } }, [show, dispatch]) const component = show ? ( diff --git a/packages/ui/src/views/chatmessage/ChatMessage.jsx b/packages/ui/src/views/chatmessage/ChatMessage.jsx index 9e748fc0..e083552a 100644 --- a/packages/ui/src/views/chatmessage/ChatMessage.jsx +++ b/packages/ui/src/views/chatmessage/ChatMessage.jsx @@ -871,11 +871,6 @@ export const ChatMessage = ({ open, chatflowid, isDialog, previews, setPreviews /> ) : null} - setShowFeedbackContentDialog(false)} - onConfirm={submitFeedbackContent} - /> ) : null} {message.fileAnnotations && ( @@ -1124,6 +1119,11 @@ export const ChatMessage = ({ open, chatflowid, isDialog, previews, setPreviews )}
setSourceDialogOpen(false)} /> + setShowFeedbackContentDialog(false)} + onConfirm={submitFeedbackContent} + />
) }