balibabu
commited on
Commit
·
fa5e9f6
1
Parent(s):
e1017ef
feat: Add RetrievalDocuments to SearchPage #2247 (#2327)
Browse files### What problem does this PR solve?
feat: Add RetrievalDocuments to SearchPage #2247
feat: Click on the link in the reference to display the pdf drawer #2247
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
- web/src/components/pdf-drawer/hooks.ts +27 -0
- web/src/components/pdf-drawer/index.tsx +33 -0
- web/src/components/retrieval-documents/index.less +11 -0
- web/src/components/retrieval-documents/index.tsx +55 -0
- web/src/components/retrieval-documents/select-files.tsx +73 -0
- web/src/locales/en.ts +1 -1
- web/src/locales/zh-traditional.ts +1 -1
- web/src/locales/zh.ts +1 -1
- web/src/pages/chat/chat-container/index.tsx +9 -15
- web/src/pages/chat/hooks.ts +0 -25
- web/src/pages/flow/chat/box.tsx +10 -16
- web/src/pages/search/hooks.ts +19 -1
- web/src/pages/search/index.less +39 -1
- web/src/pages/search/index.tsx +111 -82
web/src/components/pdf-drawer/hooks.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useSetModalState } from '@/hooks/common-hooks';
|
| 2 |
+
import { IChunk } from '@/interfaces/database/knowledge';
|
| 3 |
+
import { useCallback, useState } from 'react';
|
| 4 |
+
|
| 5 |
+
export const useClickDrawer = () => {
|
| 6 |
+
const { visible, showModal, hideModal } = useSetModalState();
|
| 7 |
+
const [selectedChunk, setSelectedChunk] = useState<IChunk>({} as IChunk);
|
| 8 |
+
const [documentId, setDocumentId] = useState<string>('');
|
| 9 |
+
|
| 10 |
+
const clickDocumentButton = useCallback(
|
| 11 |
+
(documentId: string, chunk: IChunk) => {
|
| 12 |
+
showModal();
|
| 13 |
+
setSelectedChunk(chunk);
|
| 14 |
+
setDocumentId(documentId);
|
| 15 |
+
},
|
| 16 |
+
[showModal],
|
| 17 |
+
);
|
| 18 |
+
|
| 19 |
+
return {
|
| 20 |
+
clickDocumentButton,
|
| 21 |
+
visible,
|
| 22 |
+
showModal,
|
| 23 |
+
hideModal,
|
| 24 |
+
selectedChunk,
|
| 25 |
+
documentId,
|
| 26 |
+
};
|
| 27 |
+
};
|
web/src/components/pdf-drawer/index.tsx
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { IModalProps } from '@/interfaces/common';
|
| 2 |
+
import { IChunk } from '@/interfaces/database/knowledge';
|
| 3 |
+
import { Drawer } from 'antd';
|
| 4 |
+
import DocumentPreviewer from '../pdf-previewer';
|
| 5 |
+
|
| 6 |
+
interface IProps extends IModalProps<any> {
|
| 7 |
+
documentId: string;
|
| 8 |
+
chunk: IChunk;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
export const PdfDrawer = ({
|
| 12 |
+
visible = false,
|
| 13 |
+
hideModal,
|
| 14 |
+
documentId,
|
| 15 |
+
chunk,
|
| 16 |
+
}: IProps) => {
|
| 17 |
+
return (
|
| 18 |
+
<Drawer
|
| 19 |
+
title="Document Previewer"
|
| 20 |
+
onClose={hideModal}
|
| 21 |
+
open={visible}
|
| 22 |
+
width={'50vw'}
|
| 23 |
+
>
|
| 24 |
+
<DocumentPreviewer
|
| 25 |
+
documentId={documentId}
|
| 26 |
+
chunk={chunk}
|
| 27 |
+
visible={visible}
|
| 28 |
+
></DocumentPreviewer>
|
| 29 |
+
</Drawer>
|
| 30 |
+
);
|
| 31 |
+
};
|
| 32 |
+
|
| 33 |
+
export default PdfDrawer;
|
web/src/components/retrieval-documents/index.less
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.selectFilesCollapse {
|
| 2 |
+
:global(.ant-collapse-header) {
|
| 3 |
+
padding-left: 22px;
|
| 4 |
+
}
|
| 5 |
+
margin-bottom: 32px;
|
| 6 |
+
overflow-y: auto;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
.selectFilesTitle {
|
| 10 |
+
padding-right: 10px;
|
| 11 |
+
}
|
web/src/components/retrieval-documents/index.tsx
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { ReactComponent as SelectedFilesCollapseIcon } from '@/assets/svg/selected-files-collapse.svg';
|
| 2 |
+
import { Collapse, Flex, Space } from 'antd';
|
| 3 |
+
import SelectFiles from './select-files';
|
| 4 |
+
|
| 5 |
+
import { useSelectTestingResult } from '@/hooks/knowledge-hooks';
|
| 6 |
+
import { useState } from 'react';
|
| 7 |
+
import { useTranslation } from 'react-i18next';
|
| 8 |
+
import styles from './index.less';
|
| 9 |
+
|
| 10 |
+
interface IProps {
|
| 11 |
+
selectedDocumentIdsLength?: number;
|
| 12 |
+
onTesting(documentIds: string[]): void;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
const RetrievalDocuments = ({ onTesting }: IProps) => {
|
| 16 |
+
const { t } = useTranslation();
|
| 17 |
+
const { documents } = useSelectTestingResult();
|
| 18 |
+
const [selectedDocumentIds, setSelectedDocumentIds] = useState<string[]>([]);
|
| 19 |
+
|
| 20 |
+
return (
|
| 21 |
+
<Collapse
|
| 22 |
+
expandIcon={() => <SelectedFilesCollapseIcon></SelectedFilesCollapseIcon>}
|
| 23 |
+
className={styles.selectFilesCollapse}
|
| 24 |
+
items={[
|
| 25 |
+
{
|
| 26 |
+
key: '1',
|
| 27 |
+
label: (
|
| 28 |
+
<Flex
|
| 29 |
+
justify={'space-between'}
|
| 30 |
+
align="center"
|
| 31 |
+
className={styles.selectFilesTitle}
|
| 32 |
+
>
|
| 33 |
+
<Space>
|
| 34 |
+
<span>
|
| 35 |
+
{selectedDocumentIds.length ?? 0}/{documents.length}
|
| 36 |
+
</span>
|
| 37 |
+
{t('knowledgeDetails.filesSelected')}
|
| 38 |
+
</Space>
|
| 39 |
+
</Flex>
|
| 40 |
+
),
|
| 41 |
+
children: (
|
| 42 |
+
<div>
|
| 43 |
+
<SelectFiles
|
| 44 |
+
setSelectedDocumentIds={setSelectedDocumentIds}
|
| 45 |
+
handleTesting={onTesting}
|
| 46 |
+
></SelectFiles>
|
| 47 |
+
</div>
|
| 48 |
+
),
|
| 49 |
+
},
|
| 50 |
+
]}
|
| 51 |
+
/>
|
| 52 |
+
);
|
| 53 |
+
};
|
| 54 |
+
|
| 55 |
+
export default RetrievalDocuments;
|
web/src/components/retrieval-documents/select-files.tsx
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import NewDocumentLink from '@/components/new-document-link';
|
| 2 |
+
import { useTranslate } from '@/hooks/common-hooks';
|
| 3 |
+
import { useSelectTestingResult } from '@/hooks/knowledge-hooks';
|
| 4 |
+
import { ITestingDocument } from '@/interfaces/database/knowledge';
|
| 5 |
+
import { EyeOutlined } from '@ant-design/icons';
|
| 6 |
+
import { Button, Table, TableProps, Tooltip } from 'antd';
|
| 7 |
+
|
| 8 |
+
interface IProps {
|
| 9 |
+
handleTesting: (ids: string[]) => void;
|
| 10 |
+
setSelectedDocumentIds: (ids: string[]) => void;
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
const SelectFiles = ({ setSelectedDocumentIds, handleTesting }: IProps) => {
|
| 14 |
+
const { documents } = useSelectTestingResult();
|
| 15 |
+
const { t } = useTranslate('fileManager');
|
| 16 |
+
|
| 17 |
+
const columns: TableProps<ITestingDocument>['columns'] = [
|
| 18 |
+
{
|
| 19 |
+
title: 'Name',
|
| 20 |
+
dataIndex: 'doc_name',
|
| 21 |
+
key: 'doc_name',
|
| 22 |
+
render: (text) => <p>{text}</p>,
|
| 23 |
+
},
|
| 24 |
+
|
| 25 |
+
{
|
| 26 |
+
title: 'Hits',
|
| 27 |
+
dataIndex: 'count',
|
| 28 |
+
key: 'count',
|
| 29 |
+
width: 80,
|
| 30 |
+
},
|
| 31 |
+
{
|
| 32 |
+
title: 'View',
|
| 33 |
+
key: 'view',
|
| 34 |
+
width: 50,
|
| 35 |
+
render: (_, { doc_id, doc_name }) => (
|
| 36 |
+
<NewDocumentLink
|
| 37 |
+
documentName={doc_name}
|
| 38 |
+
documentId={doc_id}
|
| 39 |
+
prefix="document"
|
| 40 |
+
>
|
| 41 |
+
<Tooltip title={t('preview')}>
|
| 42 |
+
<Button type="text">
|
| 43 |
+
<EyeOutlined size={20} />
|
| 44 |
+
</Button>
|
| 45 |
+
</Tooltip>
|
| 46 |
+
</NewDocumentLink>
|
| 47 |
+
),
|
| 48 |
+
},
|
| 49 |
+
];
|
| 50 |
+
|
| 51 |
+
const rowSelection = {
|
| 52 |
+
onChange: (selectedRowKeys: React.Key[]) => {
|
| 53 |
+
handleTesting(selectedRowKeys as string[]);
|
| 54 |
+
setSelectedDocumentIds(selectedRowKeys as string[]);
|
| 55 |
+
},
|
| 56 |
+
getCheckboxProps: (record: ITestingDocument) => ({
|
| 57 |
+
disabled: record.doc_name === 'Disabled User', // Column configuration not to be checked
|
| 58 |
+
name: record.doc_name,
|
| 59 |
+
}),
|
| 60 |
+
};
|
| 61 |
+
|
| 62 |
+
return (
|
| 63 |
+
<Table
|
| 64 |
+
columns={columns}
|
| 65 |
+
dataSource={documents}
|
| 66 |
+
showHeader={false}
|
| 67 |
+
rowSelection={rowSelection}
|
| 68 |
+
rowKey={'doc_id'}
|
| 69 |
+
/>
|
| 70 |
+
);
|
| 71 |
+
};
|
| 72 |
+
|
| 73 |
+
export default SelectFiles;
|
web/src/locales/en.ts
CHANGED
|
@@ -646,7 +646,7 @@ The above is the content you need to summarize.`,
|
|
| 646 |
operation: 'operation',
|
| 647 |
run: 'Run',
|
| 648 |
save: 'Save',
|
| 649 |
-
title: '
|
| 650 |
beginDescription: 'This is where the flow begins.',
|
| 651 |
answerDescription: `A component that serves as the interface between human and bot, receiving user inputs and displaying the agent's responses.`,
|
| 652 |
retrievalDescription: `A component that retrieves information from a specified knowledge base and returns 'Empty response' if no information is found. Ensure the correct knowledge base is selected.`,
|
|
|
|
| 646 |
operation: 'operation',
|
| 647 |
run: 'Run',
|
| 648 |
save: 'Save',
|
| 649 |
+
title: 'ID:',
|
| 650 |
beginDescription: 'This is where the flow begins.',
|
| 651 |
answerDescription: `A component that serves as the interface between human and bot, receiving user inputs and displaying the agent's responses.`,
|
| 652 |
retrievalDescription: `A component that retrieves information from a specified knowledge base and returns 'Empty response' if no information is found. Ensure the correct knowledge base is selected.`,
|
web/src/locales/zh-traditional.ts
CHANGED
|
@@ -602,7 +602,7 @@ export default {
|
|
| 602 |
operation: '操作',
|
| 603 |
run: '運行',
|
| 604 |
save: '儲存',
|
| 605 |
-
title: '
|
| 606 |
|
| 607 |
beginDescription: '這是流程開始的地方',
|
| 608 |
answerDescription: `該組件用作機器人與人類之間的介面。它接收使用者的輸入並顯示機器人的計算結果。`,
|
|
|
|
| 602 |
operation: '操作',
|
| 603 |
run: '運行',
|
| 604 |
save: '儲存',
|
| 605 |
+
title: 'ID:',
|
| 606 |
|
| 607 |
beginDescription: '這是流程開始的地方',
|
| 608 |
answerDescription: `該組件用作機器人與人類之間的介面。它接收使用者的輸入並顯示機器人的計算結果。`,
|
web/src/locales/zh.ts
CHANGED
|
@@ -621,7 +621,7 @@ export default {
|
|
| 621 |
operation: '操作',
|
| 622 |
run: '运行',
|
| 623 |
save: '保存',
|
| 624 |
-
title: '
|
| 625 |
beginDescription: '这是流程开始的地方',
|
| 626 |
answerDescription: `该组件用作机器人与人类之间的接口。它接收用户的输入并显示机器人的计算结果。`,
|
| 627 |
retrievalDescription: `此组件用于从知识库中检索相关信息。选择知识库。如果没有检索到任何内容,将返回“空响应”。`,
|
|
|
|
| 621 |
operation: '操作',
|
| 622 |
run: '运行',
|
| 623 |
save: '保存',
|
| 624 |
+
title: 'ID:',
|
| 625 |
beginDescription: '这是流程开始的地方',
|
| 626 |
answerDescription: `该组件用作机器人与人类之间的接口。它接收用户的输入并显示机器人的计算结果。`,
|
| 627 |
retrievalDescription: `此组件用于从知识库中检索相关信息。选择知识库。如果没有检索到任何内容,将返回“空响应”。`,
|
web/src/pages/chat/chat-container/index.tsx
CHANGED
|
@@ -1,9 +1,7 @@
|
|
| 1 |
import MessageItem from '@/components/message-item';
|
| 2 |
-
import DocumentPreviewer from '@/components/pdf-previewer';
|
| 3 |
import { MessageType } from '@/constants/chat';
|
| 4 |
-
import {
|
| 5 |
import {
|
| 6 |
-
useClickDrawer,
|
| 7 |
useCreateConversationBeforeUploadDocument,
|
| 8 |
useGetFileIcon,
|
| 9 |
useGetSendButtonDisabled,
|
|
@@ -13,6 +11,8 @@ import {
|
|
| 13 |
import { buildMessageItemReference } from '../utils';
|
| 14 |
|
| 15 |
import MessageInput from '@/components/message-input';
|
|
|
|
|
|
|
| 16 |
import {
|
| 17 |
useFetchNextConversation,
|
| 18 |
useGetChatSearchParams,
|
|
@@ -96,18 +96,12 @@ const ChatContainer = () => {
|
|
| 96 |
}
|
| 97 |
></MessageInput>
|
| 98 |
</Flex>
|
| 99 |
-
<
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
>
|
| 105 |
-
<DocumentPreviewer
|
| 106 |
-
documentId={documentId}
|
| 107 |
-
chunk={selectedChunk}
|
| 108 |
-
visible={visible}
|
| 109 |
-
></DocumentPreviewer>
|
| 110 |
-
</Drawer>
|
| 111 |
</>
|
| 112 |
);
|
| 113 |
};
|
|
|
|
| 1 |
import MessageItem from '@/components/message-item';
|
|
|
|
| 2 |
import { MessageType } from '@/constants/chat';
|
| 3 |
+
import { Flex, Spin } from 'antd';
|
| 4 |
import {
|
|
|
|
| 5 |
useCreateConversationBeforeUploadDocument,
|
| 6 |
useGetFileIcon,
|
| 7 |
useGetSendButtonDisabled,
|
|
|
|
| 11 |
import { buildMessageItemReference } from '../utils';
|
| 12 |
|
| 13 |
import MessageInput from '@/components/message-input';
|
| 14 |
+
import PdfDrawer from '@/components/pdf-drawer';
|
| 15 |
+
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
| 16 |
import {
|
| 17 |
useFetchNextConversation,
|
| 18 |
useGetChatSearchParams,
|
|
|
|
| 96 |
}
|
| 97 |
></MessageInput>
|
| 98 |
</Flex>
|
| 99 |
+
<PdfDrawer
|
| 100 |
+
visible={visible}
|
| 101 |
+
hideModal={hideModal}
|
| 102 |
+
documentId={documentId}
|
| 103 |
+
chunk={selectedChunk}
|
| 104 |
+
></PdfDrawer>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
</>
|
| 106 |
);
|
| 107 |
};
|
web/src/pages/chat/hooks.ts
CHANGED
|
@@ -23,7 +23,6 @@ import {
|
|
| 23 |
useSendMessageWithSse,
|
| 24 |
} from '@/hooks/logic-hooks';
|
| 25 |
import { IConversation, IDialog, Message } from '@/interfaces/database/chat';
|
| 26 |
-
import { IChunk } from '@/interfaces/database/knowledge';
|
| 27 |
import { getFileExtension } from '@/utils';
|
| 28 |
import { useMutationState } from '@tanstack/react-query';
|
| 29 |
import { get } from 'lodash';
|
|
@@ -545,30 +544,6 @@ export const useRenameConversation = () => {
|
|
| 545 |
};
|
| 546 |
};
|
| 547 |
|
| 548 |
-
export const useClickDrawer = () => {
|
| 549 |
-
const { visible, showModal, hideModal } = useSetModalState();
|
| 550 |
-
const [selectedChunk, setSelectedChunk] = useState<IChunk>({} as IChunk);
|
| 551 |
-
const [documentId, setDocumentId] = useState<string>('');
|
| 552 |
-
|
| 553 |
-
const clickDocumentButton = useCallback(
|
| 554 |
-
(documentId: string, chunk: IChunk) => {
|
| 555 |
-
showModal();
|
| 556 |
-
setSelectedChunk(chunk);
|
| 557 |
-
setDocumentId(documentId);
|
| 558 |
-
},
|
| 559 |
-
[showModal],
|
| 560 |
-
);
|
| 561 |
-
|
| 562 |
-
return {
|
| 563 |
-
clickDocumentButton,
|
| 564 |
-
visible,
|
| 565 |
-
showModal,
|
| 566 |
-
hideModal,
|
| 567 |
-
selectedChunk,
|
| 568 |
-
documentId,
|
| 569 |
-
};
|
| 570 |
-
};
|
| 571 |
-
|
| 572 |
export const useGetSendButtonDisabled = () => {
|
| 573 |
const { dialogId, conversationId } = useGetChatSearchParams();
|
| 574 |
|
|
|
|
| 23 |
useSendMessageWithSse,
|
| 24 |
} from '@/hooks/logic-hooks';
|
| 25 |
import { IConversation, IDialog, Message } from '@/interfaces/database/chat';
|
|
|
|
| 26 |
import { getFileExtension } from '@/utils';
|
| 27 |
import { useMutationState } from '@tanstack/react-query';
|
| 28 |
import { get } from 'lodash';
|
|
|
|
| 544 |
};
|
| 545 |
};
|
| 546 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 547 |
export const useGetSendButtonDisabled = () => {
|
| 548 |
const { dialogId, conversationId } = useGetChatSearchParams();
|
| 549 |
|
web/src/pages/flow/chat/box.tsx
CHANGED
|
@@ -1,13 +1,14 @@
|
|
| 1 |
import MessageItem from '@/components/message-item';
|
| 2 |
-
import DocumentPreviewer from '@/components/pdf-previewer';
|
| 3 |
import { MessageType } from '@/constants/chat';
|
| 4 |
import { useTranslate } from '@/hooks/common-hooks';
|
| 5 |
-
import {
|
| 6 |
import { buildMessageItemReference } from '@/pages/chat/utils';
|
| 7 |
-
import { Button,
|
| 8 |
|
| 9 |
import { useSendNextMessage } from './hooks';
|
| 10 |
|
|
|
|
|
|
|
| 11 |
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
| 12 |
import styles from './index.less';
|
| 13 |
|
|
@@ -79,19 +80,12 @@ const FlowChatBox = () => {
|
|
| 79 |
onChange={handleInputChange}
|
| 80 |
/>
|
| 81 |
</Flex>
|
| 82 |
-
<
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
>
|
| 89 |
-
<DocumentPreviewer
|
| 90 |
-
documentId={documentId}
|
| 91 |
-
chunk={selectedChunk}
|
| 92 |
-
visible={visible}
|
| 93 |
-
></DocumentPreviewer>
|
| 94 |
-
</Drawer>
|
| 95 |
</>
|
| 96 |
);
|
| 97 |
};
|
|
|
|
| 1 |
import MessageItem from '@/components/message-item';
|
|
|
|
| 2 |
import { MessageType } from '@/constants/chat';
|
| 3 |
import { useTranslate } from '@/hooks/common-hooks';
|
| 4 |
+
import { useGetFileIcon } from '@/pages/chat/hooks';
|
| 5 |
import { buildMessageItemReference } from '@/pages/chat/utils';
|
| 6 |
+
import { Button, Flex, Input, Spin } from 'antd';
|
| 7 |
|
| 8 |
import { useSendNextMessage } from './hooks';
|
| 9 |
|
| 10 |
+
import PdfDrawer from '@/components/pdf-drawer';
|
| 11 |
+
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
| 12 |
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
| 13 |
import styles from './index.less';
|
| 14 |
|
|
|
|
| 80 |
onChange={handleInputChange}
|
| 81 |
/>
|
| 82 |
</Flex>
|
| 83 |
+
<PdfDrawer
|
| 84 |
+
visible={visible}
|
| 85 |
+
hideModal={hideModal}
|
| 86 |
+
documentId={documentId}
|
| 87 |
+
chunk={selectedChunk}
|
| 88 |
+
></PdfDrawer>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
</>
|
| 90 |
);
|
| 91 |
};
|
web/src/pages/search/hooks.ts
CHANGED
|
@@ -46,10 +46,27 @@ export const useSendQuestion = (kbIds: string[]) => {
|
|
| 46 |
|
| 47 |
const handleClickRelatedQuestion = useCallback(
|
| 48 |
(question: string) => () => {
|
|
|
|
|
|
|
| 49 |
setSearchStr(question);
|
| 50 |
sendQuestion(question);
|
| 51 |
},
|
| 52 |
-
[sendQuestion],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
);
|
| 54 |
|
| 55 |
useEffect(() => {
|
|
@@ -71,6 +88,7 @@ export const useSendQuestion = (kbIds: string[]) => {
|
|
| 71 |
sendQuestion,
|
| 72 |
handleSearchStrChange,
|
| 73 |
handleClickRelatedQuestion,
|
|
|
|
| 74 |
loading,
|
| 75 |
sendingLoading,
|
| 76 |
answer: currentAnswer,
|
|
|
|
| 46 |
|
| 47 |
const handleClickRelatedQuestion = useCallback(
|
| 48 |
(question: string) => () => {
|
| 49 |
+
if (sendingLoading) return;
|
| 50 |
+
|
| 51 |
setSearchStr(question);
|
| 52 |
sendQuestion(question);
|
| 53 |
},
|
| 54 |
+
[sendQuestion, sendingLoading],
|
| 55 |
+
);
|
| 56 |
+
|
| 57 |
+
const handleTestChunk = useCallback(
|
| 58 |
+
(documentIds: string[]) => {
|
| 59 |
+
const q = trim(searchStr);
|
| 60 |
+
if (sendingLoading || isEmpty(q)) return;
|
| 61 |
+
|
| 62 |
+
testChunk({
|
| 63 |
+
kb_id: kbIds,
|
| 64 |
+
highlight: true,
|
| 65 |
+
question: q,
|
| 66 |
+
doc_ids: Array.isArray(documentIds) ? documentIds : [],
|
| 67 |
+
});
|
| 68 |
+
},
|
| 69 |
+
[sendingLoading, searchStr, kbIds, testChunk],
|
| 70 |
);
|
| 71 |
|
| 72 |
useEffect(() => {
|
|
|
|
| 88 |
sendQuestion,
|
| 89 |
handleSearchStrChange,
|
| 90 |
handleClickRelatedQuestion,
|
| 91 |
+
handleTestChunk,
|
| 92 |
loading,
|
| 93 |
sendingLoading,
|
| 94 |
answer: currentAnswer,
|
web/src/pages/search/index.less
CHANGED
|
@@ -51,6 +51,9 @@
|
|
| 51 |
|
| 52 |
.firstRenderContent {
|
| 53 |
height: 100%;
|
|
|
|
|
|
|
|
|
|
| 54 |
}
|
| 55 |
|
| 56 |
.content {
|
|
@@ -79,10 +82,13 @@
|
|
| 79 |
|
| 80 |
.input() {
|
| 81 |
:global(.ant-input-affix-wrapper) {
|
| 82 |
-
padding: 4px
|
| 83 |
border-start-start-radius: 30px !important;
|
| 84 |
border-end-start-radius: 30px !important;
|
| 85 |
}
|
|
|
|
|
|
|
|
|
|
| 86 |
input {
|
| 87 |
height: 40px;
|
| 88 |
}
|
|
@@ -101,3 +107,35 @@
|
|
| 101 |
width: 100%;
|
| 102 |
.input();
|
| 103 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
.firstRenderContent {
|
| 53 |
height: 100%;
|
| 54 |
+
background-image: url(https://www.bing.com/th?id=OHR.IguazuRainbow_ZH-CN6524347982_1920x1080.webp&qlt=50);
|
| 55 |
+
background-position: center;
|
| 56 |
+
background-size: cover;
|
| 57 |
}
|
| 58 |
|
| 59 |
.content {
|
|
|
|
| 82 |
|
| 83 |
.input() {
|
| 84 |
:global(.ant-input-affix-wrapper) {
|
| 85 |
+
padding: 4px 12px;
|
| 86 |
border-start-start-radius: 30px !important;
|
| 87 |
border-end-start-radius: 30px !important;
|
| 88 |
}
|
| 89 |
+
:global(.ant-input-group-addon) {
|
| 90 |
+
background-color: transparent;
|
| 91 |
+
}
|
| 92 |
input {
|
| 93 |
height: 40px;
|
| 94 |
}
|
|
|
|
| 107 |
width: 100%;
|
| 108 |
.input();
|
| 109 |
}
|
| 110 |
+
|
| 111 |
+
.appIcon {
|
| 112 |
+
display: inline-block;
|
| 113 |
+
vertical-align: middle;
|
| 114 |
+
width: 60px;
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
.appName {
|
| 118 |
+
vertical-align: middle;
|
| 119 |
+
font-family: Inter;
|
| 120 |
+
font-size: 40px;
|
| 121 |
+
font-style: normal;
|
| 122 |
+
font-weight: 600;
|
| 123 |
+
line-height: 20px;
|
| 124 |
+
|
| 125 |
+
background: linear-gradient(to right, #095fab 10%, #25abe8 50%, #57d75b 60%);
|
| 126 |
+
background-size: auto auto;
|
| 127 |
+
background-clip: border-box;
|
| 128 |
+
background-size: 200% auto;
|
| 129 |
+
color: #fff;
|
| 130 |
+
background-clip: text;
|
| 131 |
+
text-fill-color: transparent;
|
| 132 |
+
-webkit-background-clip: text;
|
| 133 |
+
-webkit-text-fill-color: transparent;
|
| 134 |
+
animation: textclip 1.5s linear infinite;
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
@keyframes textclip {
|
| 138 |
+
to {
|
| 139 |
+
background-position: 200% center;
|
| 140 |
+
}
|
| 141 |
+
}
|
web/src/pages/search/index.tsx
CHANGED
|
@@ -19,18 +19,26 @@ import MarkdownContent from '../chat/markdown-content';
|
|
| 19 |
import { useSendQuestion } from './hooks';
|
| 20 |
import SearchSidebar from './sidebar';
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
import styles from './index.less';
|
| 23 |
|
| 24 |
const { Content } = Layout;
|
| 25 |
const { Search } = Input;
|
| 26 |
|
| 27 |
const SearchPage = () => {
|
|
|
|
| 28 |
const [checkedList, setCheckedList] = useState<string[]>([]);
|
| 29 |
const list = useSelectTestingResult();
|
|
|
|
| 30 |
const {
|
| 31 |
sendQuestion,
|
| 32 |
handleClickRelatedQuestion,
|
| 33 |
handleSearchStrChange,
|
|
|
|
| 34 |
answer,
|
| 35 |
sendingLoading,
|
| 36 |
relatedQuestions,
|
|
@@ -40,12 +48,14 @@ const SearchPage = () => {
|
|
| 40 |
loading,
|
| 41 |
isFirstRender,
|
| 42 |
} = useSendQuestion(checkedList);
|
|
|
|
|
|
|
| 43 |
|
| 44 |
const InputSearch = (
|
| 45 |
<Search
|
| 46 |
value={searchStr}
|
| 47 |
onChange={handleSearchStrChange}
|
| 48 |
-
placeholder=
|
| 49 |
allowClear
|
| 50 |
enterButton
|
| 51 |
onSearch={sendQuestion}
|
|
@@ -57,88 +67,107 @@ const SearchPage = () => {
|
|
| 57 |
);
|
| 58 |
|
| 59 |
return (
|
| 60 |
-
|
| 61 |
-
<
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
<
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
>
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
</Layout>
|
| 141 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
);
|
| 143 |
};
|
| 144 |
|
|
|
|
| 19 |
import { useSendQuestion } from './hooks';
|
| 20 |
import SearchSidebar from './sidebar';
|
| 21 |
|
| 22 |
+
import PdfDrawer from '@/components/pdf-drawer';
|
| 23 |
+
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
| 24 |
+
import RetrievalDocuments from '@/components/retrieval-documents';
|
| 25 |
+
import { useFetchAppConf } from '@/hooks/logic-hooks';
|
| 26 |
+
import { useTranslation } from 'react-i18next';
|
| 27 |
import styles from './index.less';
|
| 28 |
|
| 29 |
const { Content } = Layout;
|
| 30 |
const { Search } = Input;
|
| 31 |
|
| 32 |
const SearchPage = () => {
|
| 33 |
+
const { t } = useTranslation();
|
| 34 |
const [checkedList, setCheckedList] = useState<string[]>([]);
|
| 35 |
const list = useSelectTestingResult();
|
| 36 |
+
const appConf = useFetchAppConf();
|
| 37 |
const {
|
| 38 |
sendQuestion,
|
| 39 |
handleClickRelatedQuestion,
|
| 40 |
handleSearchStrChange,
|
| 41 |
+
handleTestChunk,
|
| 42 |
answer,
|
| 43 |
sendingLoading,
|
| 44 |
relatedQuestions,
|
|
|
|
| 48 |
loading,
|
| 49 |
isFirstRender,
|
| 50 |
} = useSendQuestion(checkedList);
|
| 51 |
+
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
| 52 |
+
useClickDrawer();
|
| 53 |
|
| 54 |
const InputSearch = (
|
| 55 |
<Search
|
| 56 |
value={searchStr}
|
| 57 |
onChange={handleSearchStrChange}
|
| 58 |
+
placeholder={t('header.search')}
|
| 59 |
allowClear
|
| 60 |
enterButton
|
| 61 |
onSearch={sendQuestion}
|
|
|
|
| 67 |
);
|
| 68 |
|
| 69 |
return (
|
| 70 |
+
<>
|
| 71 |
+
<Layout className={styles.searchPage}>
|
| 72 |
+
<SearchSidebar
|
| 73 |
+
checkedList={checkedList}
|
| 74 |
+
setCheckedList={setCheckedList}
|
| 75 |
+
></SearchSidebar>
|
| 76 |
+
<Layout>
|
| 77 |
+
<Content>
|
| 78 |
+
{isFirstRender ? (
|
| 79 |
+
<Flex
|
| 80 |
+
justify="center"
|
| 81 |
+
align="center"
|
| 82 |
+
className={styles.firstRenderContent}
|
| 83 |
+
>
|
| 84 |
+
<Flex vertical align="center" gap={'large'}>
|
| 85 |
+
<Space size={30}>
|
| 86 |
+
<img src="/logo.svg" alt="" className={styles.appIcon} />
|
| 87 |
+
<span className={styles.appName}>{appConf.appName}</span>
|
| 88 |
+
</Space>
|
| 89 |
+
{InputSearch}
|
| 90 |
+
</Flex>
|
| 91 |
+
</Flex>
|
| 92 |
+
) : (
|
| 93 |
+
<Flex className={styles.content}>
|
| 94 |
+
<section className={styles.main}>
|
| 95 |
+
{InputSearch}
|
| 96 |
+
{answer.answer && (
|
| 97 |
+
<div className={styles.answerWrapper}>
|
| 98 |
+
<MarkdownContent
|
| 99 |
+
loading={sendingLoading}
|
| 100 |
+
content={answer.answer}
|
| 101 |
+
reference={answer.reference ?? ({} as IReference)}
|
| 102 |
+
clickDocumentButton={clickDocumentButton}
|
| 103 |
+
></MarkdownContent>
|
| 104 |
+
</div>
|
| 105 |
+
)}
|
| 106 |
+
<Divider></Divider>
|
| 107 |
+
<RetrievalDocuments
|
| 108 |
+
selectedDocumentIdsLength={0}
|
| 109 |
+
onTesting={handleTestChunk}
|
| 110 |
+
></RetrievalDocuments>
|
| 111 |
+
<Divider></Divider>
|
| 112 |
+
{list.chunks.length > 0 && (
|
| 113 |
+
<List
|
| 114 |
+
dataSource={list.chunks}
|
| 115 |
+
loading={loading}
|
| 116 |
+
renderItem={(item) => (
|
| 117 |
+
<List.Item>
|
| 118 |
+
<Card className={styles.card}>
|
| 119 |
+
<Space>
|
| 120 |
+
<ImageWithPopover
|
| 121 |
+
id={item.img_id}
|
| 122 |
+
></ImageWithPopover>
|
| 123 |
+
<HightLightMarkdown>
|
| 124 |
+
{item.highlight}
|
| 125 |
+
</HightLightMarkdown>
|
| 126 |
+
</Space>
|
| 127 |
+
</Card>
|
| 128 |
+
</List.Item>
|
| 129 |
+
)}
|
| 130 |
+
/>
|
| 131 |
+
)}
|
| 132 |
+
{relatedQuestions?.length > 0 && (
|
| 133 |
+
<Card>
|
| 134 |
+
<Flex wrap="wrap" gap={'10px 0'}>
|
| 135 |
+
{relatedQuestions?.map((x, idx) => (
|
| 136 |
+
<Tag
|
| 137 |
+
key={idx}
|
| 138 |
+
className={styles.tag}
|
| 139 |
+
onClick={handleClickRelatedQuestion(x)}
|
| 140 |
+
>
|
| 141 |
+
{x}
|
| 142 |
+
</Tag>
|
| 143 |
+
))}
|
| 144 |
+
</Flex>
|
| 145 |
+
</Card>
|
| 146 |
+
)}
|
| 147 |
+
</section>
|
| 148 |
+
<section className={styles.graph}>
|
| 149 |
+
{mindMapLoading ? (
|
| 150 |
+
<Skeleton active />
|
| 151 |
+
) : (
|
| 152 |
+
<IndentedTree
|
| 153 |
+
data={mindMap}
|
| 154 |
+
show
|
| 155 |
+
style={{ width: '100%', height: '100%' }}
|
| 156 |
+
></IndentedTree>
|
| 157 |
+
)}
|
| 158 |
+
</section>
|
| 159 |
+
</Flex>
|
| 160 |
+
)}
|
| 161 |
+
</Content>
|
| 162 |
+
</Layout>
|
| 163 |
</Layout>
|
| 164 |
+
<PdfDrawer
|
| 165 |
+
visible={visible}
|
| 166 |
+
hideModal={hideModal}
|
| 167 |
+
documentId={documentId}
|
| 168 |
+
chunk={selectedChunk}
|
| 169 |
+
></PdfDrawer>
|
| 170 |
+
</>
|
| 171 |
);
|
| 172 |
};
|
| 173 |
|