Skip to content

Commit

Permalink
feat: Submit Feedback infiniflow#2088 (infiniflow#2134)
Browse files Browse the repository at this point in the history
### What problem does this PR solve?

feat: Submit Feedback infiniflow#2088

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
  • Loading branch information
cike8899 authored Aug 28, 2024
1 parent f843dd0 commit 54f7c6e
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 26 deletions.
30 changes: 22 additions & 8 deletions web/src/components/message-item/feedback-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,34 @@
import { Form, Input, Modal } from 'antd';

import { IModalProps } from '@/interfaces/common';
import { IFeedbackRequestBody } from '@/interfaces/request/chat';
import { useCallback } from 'react';

type FieldType = {
username?: string;
feedback?: string;
};

const FeedbackModal = ({ visible, hideModal }: IModalProps<any>) => {
const FeedbackModal = ({
visible,
hideModal,
onOk,
loading,
}: IModalProps<IFeedbackRequestBody>) => {
const [form] = Form.useForm();

const handleOk = async () => {
const handleOk = useCallback(async () => {
const ret = await form.validateFields();
};
return onOk?.({ thumbup: false, feedback: ret.feedback });
}, [onOk, form]);

return (
<Modal title="Feedback" open={visible} onOk={handleOk} onCancel={hideModal}>
<Modal
title="Feedback"
open={visible}
onOk={handleOk}
onCancel={hideModal}
confirmLoading={loading}
>
<Form
name="basic"
labelCol={{ span: 0 }}
Expand All @@ -24,10 +38,10 @@ const FeedbackModal = ({ visible, hideModal }: IModalProps<any>) => {
form={form}
>
<Form.Item<FieldType>
name="username"
rules={[{ required: true, message: 'Please input your username!' }]}
name="feedback"
rules={[{ required: true, message: 'Please input your feedback!' }]}
>
<Input.TextArea rows={8} placeholder="Please input your username!" />
<Input.TextArea rows={8} placeholder="Please input your feedback!" />
</Form.Item>
</Form>
</Modal>
Expand Down
28 changes: 22 additions & 6 deletions web/src/components/message-item/group-button.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import CopyToClipboard from '@/components/copy-to-clipboard';
import { useSetModalState } from '@/hooks/common-hooks';
import {
DeleteOutlined,
DislikeOutlined,
Expand All @@ -8,29 +7,46 @@ import {
SyncOutlined,
} from '@ant-design/icons';
import { Radio } from 'antd';
import { useCallback } from 'react';
import FeedbackModal from './feedback-modal';
import { useSendFeedback } from './hooks';

export const AssistantGroupButton = () => {
const { visible, hideModal, showModal } = useSetModalState();
interface IProps {
messageId: string;
content: string;
}

export const AssistantGroupButton = ({ messageId, content }: IProps) => {
const { visible, hideModal, showModal, onFeedbackOk, loading } =
useSendFeedback(messageId);

const handleLike = useCallback(() => {
onFeedbackOk({ thumbup: true });
}, [onFeedbackOk]);

return (
<>
<Radio.Group size="small">
<Radio.Button value="a">
<CopyToClipboard text="xxx"></CopyToClipboard>
<CopyToClipboard text={content}></CopyToClipboard>
</Radio.Button>
<Radio.Button value="b">
<SoundOutlined />
</Radio.Button>
<Radio.Button value="c">
<Radio.Button value="c" onClick={handleLike}>
<LikeOutlined />
</Radio.Button>
<Radio.Button value="d" onClick={showModal}>
<DislikeOutlined />
</Radio.Button>
</Radio.Group>
{visible && (
<FeedbackModal visible={visible} hideModal={hideModal}></FeedbackModal>
<FeedbackModal
visible={visible}
hideModal={hideModal}
onOk={onFeedbackOk}
loading={loading}
></FeedbackModal>
)}
</>
);
Expand Down
32 changes: 32 additions & 0 deletions web/src/components/message-item/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useFeedback } from '@/hooks/chat-hooks';
import { useSetModalState } from '@/hooks/common-hooks';
import { IFeedbackRequestBody } from '@/interfaces/request/chat';
import { getMessagePureId } from '@/utils/chat';
import { useCallback } from 'react';

export const useSendFeedback = (messageId: string) => {
const { visible, hideModal, showModal } = useSetModalState();
const { feedback, loading } = useFeedback();

const onFeedbackOk = useCallback(
async (params: IFeedbackRequestBody) => {
const ret = await feedback({
...params,
messageId: getMessagePureId(messageId),
});

if (ret === 0) {
hideModal();
}
},
[feedback, hideModal, messageId],
);

return {
loading,
onFeedbackOk,
visible,
hideModal,
showModal,
};
};
19 changes: 11 additions & 8 deletions web/src/components/message-item/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg';
import { MessageType } from '@/constants/chat';
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
import { useSelectFileThumbnails } from '@/hooks/knowledge-hooks';
import { IReference, Message } from '@/interfaces/database/chat';
import { IReference } from '@/interfaces/database/chat';
import { IChunk } from '@/interfaces/database/knowledge';
import classNames from 'classnames';
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
Expand All @@ -11,19 +11,20 @@ import {
useFetchDocumentInfosByIds,
useFetchDocumentThumbnailsByIds,
} from '@/hooks/document-hooks';
import { IMessage } from '@/pages/chat/interface';
import MarkdownContent from '@/pages/chat/markdown-content';
import { getExtension, isImage } from '@/utils/document-util';
import { Avatar, Button, Flex, List, Space, Typography } from 'antd';
import FileIcon from '../file-icon';
import IndentedTreeModal from '../indented-tree/modal';
import NewDocumentLink from '../new-document-link';
// import { AssistantGroupButton, UserGroupButton } from './group-button';
import { AssistantGroupButton, UserGroupButton } from './group-button';
import styles from './index.less';

const { Text } = Typography;

interface IProps {
item: Message;
item: IMessage;
reference: IReference;
loading?: boolean;
nickname?: string;
Expand All @@ -36,7 +37,6 @@ const MessageItem = ({
reference,
loading = false,
avatar = '',
nickname = '',
clickDocumentButton,
}: IProps) => {
const isAssistant = item.role === MessageType.Assistant;
Expand Down Expand Up @@ -111,13 +111,16 @@ const MessageItem = ({
)}
<Flex vertical gap={8} flex={1}>
<Space>
{/* {isAssistant ? (
<AssistantGroupButton></AssistantGroupButton>
{isAssistant ? (
<AssistantGroupButton
messageId={item.id}
content={item.content}
></AssistantGroupButton>
) : (
<UserGroupButton></UserGroupButton>
)} */}
)}

<b>{isAssistant ? '' : nickname}</b>
{/* <b>{isAssistant ? '' : nickname}</b> */}
</Space>
<div
className={
Expand Down
56 changes: 53 additions & 3 deletions web/src/hooks/chat-hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import {
IToken,
Message,
} from '@/interfaces/database/chat';
import { IFeedbackRequestBody } from '@/interfaces/request/chat';
import i18n from '@/locales/config';
import { IClientConversation, IMessage } from '@/pages/chat/interface';
import chatService from '@/services/chat-service';
import { isConversationIdExist } from '@/utils/chat';
import { buildMessageUuid, isConversationIdExist } from '@/utils/chat';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { message } from 'antd';
import dayjs, { Dayjs } from 'dayjs';
import { useCallback, useMemo, useState } from 'react';
import { useSearchParams } from 'umi';
import { v4 as uuid } from 'uuid';

//#region logic

Expand Down Expand Up @@ -218,7 +218,7 @@ export const useFetchNextConversation = () => {
const messageList =
conversation?.message?.map((x: Message | IMessage) => ({
...x,
id: 'id' in x && x.id ? x.id : uuid(),
id: buildMessageUuid(x),
})) ?? [];

return { ...conversation, message: messageList };
Expand Down Expand Up @@ -292,6 +292,56 @@ export const useRemoveNextConversation = () => {

return { data, loading, removeConversation: mutateAsync };
};

export const useDeleteMessage = () => {
// const queryClient = useQueryClient();
const { conversationId } = useGetChatSearchParams();

const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: ['deleteMessage'],
mutationFn: async (messageId: string) => {
const { data } = await chatService.deleteMessage({
messageId,
conversationId,
});
if (data.retcode === 0) {
// queryClient.invalidateQueries({ queryKey: ['fetchConversationList'] });
}
return data.retcode;
},
});

return { data, loading, deleteMessage: mutateAsync };
};

export const useFeedback = () => {
const { conversationId } = useGetChatSearchParams();

const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: ['feedback'],
mutationFn: async (params: IFeedbackRequestBody) => {
const { data } = await chatService.thumbup({
...params,
conversationId,
});
if (data.retcode === 0) {
message.success(i18n.t(`message.operated`));
}
return data.retcode;
},
});

return { data, loading, feedback: mutateAsync };
};

//#endregion

// #region API provided for external calls
Expand Down
5 changes: 5 additions & 0 deletions web/src/interfaces/request/chat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface IFeedbackRequestBody {
messageId?: string;
thumbup?: boolean;
feedback?: string;
}
1 change: 1 addition & 0 deletions web/src/pages/chat/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ export const useSendMessage = (
messages: [
...(conversation?.message ?? []).map((x: IMessage) => omit(x, 'id')),
{
id: uuid(),
role: MessageType.User,
content: message,
doc_ids: documentIds,
Expand Down
15 changes: 15 additions & 0 deletions web/src/services/chat-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ const {
getExternalConversation,
completeExternalConversation,
uploadAndParseExternal,
deleteMessage,
thumbup,
tts,
} = api;

const methods = {
Expand Down Expand Up @@ -91,6 +94,18 @@ const methods = {
url: uploadAndParseExternal,
method: 'post',
},
deleteMessage: {
url: deleteMessage,
method: 'post',
},
thumbup: {
url: thumbup,
method: 'post',
},
tts: {
url: tts,
method: 'post',
},
} as const;

const chatService = registerServer<keyof typeof methods>(methods, request);
Expand Down
3 changes: 3 additions & 0 deletions web/src/utils/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ export default {
listConversation: `${api_host}/conversation/list`,
removeConversation: `${api_host}/conversation/rm`,
completeConversation: `${api_host}/conversation/completion`,
deleteMessage: `${api_host}/conversation/delete_msg`,
thumbup: `${api_host}/conversation/thumbup`,
tts: `${api_host}/conversation/tts`,
// chat for external
createToken: `${api_host}/api/new_token`,
listToken: `${api_host}/api/token_list`,
Expand Down
22 changes: 21 additions & 1 deletion web/src/utils/chat.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
import { EmptyConversationId } from '@/constants/chat';
import { EmptyConversationId, MessageType } from '@/constants/chat';
import { Message } from '@/interfaces/database/chat';
import { IMessage } from '@/pages/chat/interface';
import { v4 as uuid } from 'uuid';

export const isConversationIdExist = (conversationId: string) => {
return conversationId !== EmptyConversationId && conversationId !== '';
};

export const buildMessageUuid = (message: Message | IMessage) => {
if ('id' in message && message.id) {
return message.role === MessageType.User
? `${MessageType.User}_${message.id}`
: `${MessageType.Assistant}_${message.id}`;
}
return uuid();
};

export const getMessagePureId = (id: string) => {
const strings = id.split('_');
if (strings.length > 0) {
return strings.at(-1);
}
return id;
};

0 comments on commit 54f7c6e

Please sign in to comment.