balibabu
commited on
Commit
·
2eeb8b1
1
Parent(s):
dee924b
feat: Supports chatting with files/images #1880 (#1943)
Browse files### What problem does this PR solve?
feat: Supports chatting with files/images #1880
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
- web/src/{pages/add-knowledge/components/knowledge-chunk/components/knowledge-graph → components/indented-tree}/indented-tree.tsx +0 -0
- web/src/components/indented-tree/modal.tsx +31 -0
- web/src/components/message-input/index.tsx +129 -0
- web/src/components/message-item/index.tsx +78 -5
- web/src/hooks/chunk-hooks.ts +4 -3
- web/src/hooks/common-hooks.tsx +6 -6
- web/src/hooks/document-hooks.ts +37 -0
- web/src/interfaces/database/chat.ts +1 -0
- web/src/interfaces/database/document.ts +35 -0
- web/src/pages/add-knowledge/components/knowledge-chunk/components/knowledge-graph/modal.tsx +4 -2
- web/src/pages/chat/chat-container/index.tsx +13 -5
- web/src/pages/chat/hooks.ts +18 -13
- web/src/pages/force-graph/index.tsx +2 -1
- web/src/pages/force-graph/input-upload.tsx +118 -0
- web/src/services/knowledge-service.ts +5 -0
- web/src/utils/api.ts +1 -1
- web/src/utils/document-util.ts +5 -1
web/src/{pages/add-knowledge/components/knowledge-chunk/components/knowledge-graph → components/indented-tree}/indented-tree.tsx
RENAMED
|
File without changes
|
web/src/components/indented-tree/modal.tsx
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useFetchKnowledgeGraph } from '@/hooks/chunk-hooks';
|
| 2 |
+
import { Modal } from 'antd';
|
| 3 |
+
import { useTranslation } from 'react-i18next';
|
| 4 |
+
import IndentedTree from './indented-tree';
|
| 5 |
+
|
| 6 |
+
import { IModalProps } from '@/interfaces/common';
|
| 7 |
+
|
| 8 |
+
const IndentedTreeModal = ({
|
| 9 |
+
documentId,
|
| 10 |
+
visible,
|
| 11 |
+
hideModal,
|
| 12 |
+
}: IModalProps<any> & { documentId: string }) => {
|
| 13 |
+
const { data } = useFetchKnowledgeGraph(documentId);
|
| 14 |
+
const { t } = useTranslation();
|
| 15 |
+
|
| 16 |
+
return (
|
| 17 |
+
<Modal
|
| 18 |
+
title={t('chunk.graph')}
|
| 19 |
+
open={visible}
|
| 20 |
+
onCancel={hideModal}
|
| 21 |
+
width={'90vw'}
|
| 22 |
+
footer={null}
|
| 23 |
+
>
|
| 24 |
+
<section>
|
| 25 |
+
<IndentedTree data={data?.data?.mind_map} show></IndentedTree>
|
| 26 |
+
</section>
|
| 27 |
+
</Modal>
|
| 28 |
+
);
|
| 29 |
+
};
|
| 30 |
+
|
| 31 |
+
export default IndentedTreeModal;
|
web/src/components/message-input/index.tsx
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Authorization } from '@/constants/authorization';
|
| 2 |
+
import { useTranslate } from '@/hooks/common-hooks';
|
| 3 |
+
import { getAuthorization } from '@/utils/authorization-util';
|
| 4 |
+
import { PlusOutlined } from '@ant-design/icons';
|
| 5 |
+
import type { GetProp, UploadFile } from 'antd';
|
| 6 |
+
import { Button, Flex, Input, Upload, UploadProps } from 'antd';
|
| 7 |
+
import get from 'lodash/get';
|
| 8 |
+
import { ChangeEventHandler, useCallback, useState } from 'react';
|
| 9 |
+
|
| 10 |
+
type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];
|
| 11 |
+
|
| 12 |
+
interface IProps {
|
| 13 |
+
disabled: boolean;
|
| 14 |
+
value: string;
|
| 15 |
+
sendDisabled: boolean;
|
| 16 |
+
sendLoading: boolean;
|
| 17 |
+
onPressEnter(documentIds: string[]): Promise<any>;
|
| 18 |
+
onInputChange: ChangeEventHandler<HTMLInputElement>;
|
| 19 |
+
conversationId: string;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
const getBase64 = (file: FileType): Promise<string> =>
|
| 23 |
+
new Promise((resolve, reject) => {
|
| 24 |
+
const reader = new FileReader();
|
| 25 |
+
reader.readAsDataURL(file as any);
|
| 26 |
+
reader.onload = () => resolve(reader.result as string);
|
| 27 |
+
reader.onerror = (error) => reject(error);
|
| 28 |
+
});
|
| 29 |
+
|
| 30 |
+
const MessageInput = ({
|
| 31 |
+
disabled,
|
| 32 |
+
value,
|
| 33 |
+
onPressEnter,
|
| 34 |
+
sendDisabled,
|
| 35 |
+
sendLoading,
|
| 36 |
+
onInputChange,
|
| 37 |
+
conversationId,
|
| 38 |
+
}: IProps) => {
|
| 39 |
+
const { t } = useTranslate('chat');
|
| 40 |
+
|
| 41 |
+
const [fileList, setFileList] = useState<UploadFile[]>([
|
| 42 |
+
// {
|
| 43 |
+
// uid: '-1',
|
| 44 |
+
// name: 'image.png',
|
| 45 |
+
// status: 'done',
|
| 46 |
+
// url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
| 47 |
+
// },
|
| 48 |
+
// {
|
| 49 |
+
// uid: '-xxx',
|
| 50 |
+
// percent: 50,
|
| 51 |
+
// name: 'image.png',
|
| 52 |
+
// status: 'uploading',
|
| 53 |
+
// url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
| 54 |
+
// },
|
| 55 |
+
// {
|
| 56 |
+
// uid: '-5',
|
| 57 |
+
// name: 'image.png',
|
| 58 |
+
// status: 'error',
|
| 59 |
+
// },
|
| 60 |
+
]);
|
| 61 |
+
|
| 62 |
+
const handlePreview = async (file: UploadFile) => {
|
| 63 |
+
if (!file.url && !file.preview) {
|
| 64 |
+
file.preview = await getBase64(file.originFileObj as FileType);
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
// setPreviewImage(file.url || (file.preview as string));
|
| 68 |
+
// setPreviewOpen(true);
|
| 69 |
+
};
|
| 70 |
+
|
| 71 |
+
const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
|
| 72 |
+
console.log('🚀 ~ newFileList:', newFileList);
|
| 73 |
+
setFileList(newFileList);
|
| 74 |
+
};
|
| 75 |
+
|
| 76 |
+
const handlePressEnter = useCallback(async () => {
|
| 77 |
+
const ids = fileList.reduce((pre, cur) => {
|
| 78 |
+
return pre.concat(get(cur, 'response.data', []));
|
| 79 |
+
}, []);
|
| 80 |
+
|
| 81 |
+
await onPressEnter(ids);
|
| 82 |
+
setFileList([]);
|
| 83 |
+
}, [fileList, onPressEnter]);
|
| 84 |
+
|
| 85 |
+
const uploadButton = (
|
| 86 |
+
<button style={{ border: 0, background: 'none' }} type="button">
|
| 87 |
+
<PlusOutlined />
|
| 88 |
+
<div style={{ marginTop: 8 }}>Upload</div>
|
| 89 |
+
</button>
|
| 90 |
+
);
|
| 91 |
+
|
| 92 |
+
return (
|
| 93 |
+
<Flex gap={10} vertical>
|
| 94 |
+
<Input
|
| 95 |
+
size="large"
|
| 96 |
+
placeholder={t('sendPlaceholder')}
|
| 97 |
+
value={value}
|
| 98 |
+
disabled={disabled}
|
| 99 |
+
suffix={
|
| 100 |
+
<Button
|
| 101 |
+
type="primary"
|
| 102 |
+
onClick={handlePressEnter}
|
| 103 |
+
loading={sendLoading}
|
| 104 |
+
disabled={sendDisabled}
|
| 105 |
+
>
|
| 106 |
+
{t('send')}
|
| 107 |
+
</Button>
|
| 108 |
+
}
|
| 109 |
+
onPressEnter={handlePressEnter}
|
| 110 |
+
onChange={onInputChange}
|
| 111 |
+
/>
|
| 112 |
+
<Upload
|
| 113 |
+
action="/v1/document/upload_and_parse"
|
| 114 |
+
listType="picture-card"
|
| 115 |
+
fileList={fileList}
|
| 116 |
+
onPreview={handlePreview}
|
| 117 |
+
onChange={handleChange}
|
| 118 |
+
multiple
|
| 119 |
+
headers={{ [Authorization]: getAuthorization() }}
|
| 120 |
+
data={{ conversation_id: conversationId }}
|
| 121 |
+
method="post"
|
| 122 |
+
>
|
| 123 |
+
{fileList.length >= 8 ? null : uploadButton}
|
| 124 |
+
</Upload>
|
| 125 |
+
</Flex>
|
| 126 |
+
);
|
| 127 |
+
};
|
| 128 |
+
|
| 129 |
+
export default MessageInput;
|
web/src/components/message-item/index.tsx
CHANGED
|
@@ -1,15 +1,17 @@
|
|
| 1 |
import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg';
|
| 2 |
import { MessageType } from '@/constants/chat';
|
| 3 |
-
import { useTranslate } from '@/hooks/common-hooks';
|
| 4 |
import { useSelectFileThumbnails } from '@/hooks/knowledge-hooks';
|
| 5 |
import { IReference, Message } from '@/interfaces/database/chat';
|
| 6 |
import { IChunk } from '@/interfaces/database/knowledge';
|
| 7 |
import classNames from 'classnames';
|
| 8 |
-
import { useMemo } from 'react';
|
| 9 |
|
|
|
|
| 10 |
import MarkdownContent from '@/pages/chat/markdown-content';
|
| 11 |
-
import { getExtension } from '@/utils/document-util';
|
| 12 |
-
import { Avatar, Flex, List } from 'antd';
|
|
|
|
| 13 |
import NewDocumentLink from '../new-document-link';
|
| 14 |
import SvgIcon from '../svg-icon';
|
| 15 |
import styles from './index.less';
|
|
@@ -32,8 +34,13 @@ const MessageItem = ({
|
|
| 32 |
clickDocumentButton,
|
| 33 |
}: IProps) => {
|
| 34 |
const isAssistant = item.role === MessageType.Assistant;
|
|
|
|
| 35 |
const { t } = useTranslate('chat');
|
| 36 |
const fileThumbnails = useSelectFileThumbnails();
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
const referenceDocumentList = useMemo(() => {
|
| 39 |
return reference?.doc_aggs ?? [];
|
|
@@ -47,6 +54,21 @@ const MessageItem = ({
|
|
| 47 |
return loading ? text?.concat('~~2$$') : text;
|
| 48 |
}, [item.content, loading, t]);
|
| 49 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
return (
|
| 51 |
<div
|
| 52 |
className={classNames(styles.messageItem, {
|
|
@@ -124,11 +146,62 @@ const MessageItem = ({
|
|
| 124 |
}}
|
| 125 |
/>
|
| 126 |
)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
</Flex>
|
| 128 |
</div>
|
| 129 |
</section>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
</div>
|
| 131 |
);
|
| 132 |
};
|
| 133 |
|
| 134 |
-
export default MessageItem;
|
|
|
|
| 1 |
import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg';
|
| 2 |
import { MessageType } from '@/constants/chat';
|
| 3 |
+
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
|
| 4 |
import { useSelectFileThumbnails } from '@/hooks/knowledge-hooks';
|
| 5 |
import { IReference, Message } from '@/interfaces/database/chat';
|
| 6 |
import { IChunk } from '@/interfaces/database/knowledge';
|
| 7 |
import classNames from 'classnames';
|
| 8 |
+
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
| 9 |
|
| 10 |
+
import { useFetchDocumentInfosByIds } from '@/hooks/document-hooks';
|
| 11 |
import MarkdownContent from '@/pages/chat/markdown-content';
|
| 12 |
+
import { getExtension, isImage } from '@/utils/document-util';
|
| 13 |
+
import { Avatar, Button, Flex, List } from 'antd';
|
| 14 |
+
import IndentedTreeModal from '../indented-tree/modal';
|
| 15 |
import NewDocumentLink from '../new-document-link';
|
| 16 |
import SvgIcon from '../svg-icon';
|
| 17 |
import styles from './index.less';
|
|
|
|
| 34 |
clickDocumentButton,
|
| 35 |
}: IProps) => {
|
| 36 |
const isAssistant = item.role === MessageType.Assistant;
|
| 37 |
+
const isUser = item.role === MessageType.User;
|
| 38 |
const { t } = useTranslate('chat');
|
| 39 |
const fileThumbnails = useSelectFileThumbnails();
|
| 40 |
+
const { data: documentList, setDocumentIds } = useFetchDocumentInfosByIds();
|
| 41 |
+
console.log('🚀 ~ documentList:', documentList);
|
| 42 |
+
const { visible, hideModal, showModal } = useSetModalState();
|
| 43 |
+
const [clickedDocumentId, setClickedDocumentId] = useState('');
|
| 44 |
|
| 45 |
const referenceDocumentList = useMemo(() => {
|
| 46 |
return reference?.doc_aggs ?? [];
|
|
|
|
| 54 |
return loading ? text?.concat('~~2$$') : text;
|
| 55 |
}, [item.content, loading, t]);
|
| 56 |
|
| 57 |
+
const handleUserDocumentClick = useCallback(
|
| 58 |
+
(id: string) => () => {
|
| 59 |
+
setClickedDocumentId(id);
|
| 60 |
+
showModal();
|
| 61 |
+
},
|
| 62 |
+
[showModal],
|
| 63 |
+
);
|
| 64 |
+
|
| 65 |
+
useEffect(() => {
|
| 66 |
+
const ids = item?.doc_ids ?? [];
|
| 67 |
+
if (ids.length) {
|
| 68 |
+
setDocumentIds(ids);
|
| 69 |
+
}
|
| 70 |
+
}, [item.doc_ids, setDocumentIds]);
|
| 71 |
+
|
| 72 |
return (
|
| 73 |
<div
|
| 74 |
className={classNames(styles.messageItem, {
|
|
|
|
| 146 |
}}
|
| 147 |
/>
|
| 148 |
)}
|
| 149 |
+
{isUser && documentList.length > 0 && (
|
| 150 |
+
<List
|
| 151 |
+
bordered
|
| 152 |
+
dataSource={documentList}
|
| 153 |
+
renderItem={(item) => {
|
| 154 |
+
const fileThumbnail = fileThumbnails[item.id];
|
| 155 |
+
const fileExtension = getExtension(item.name);
|
| 156 |
+
return (
|
| 157 |
+
<List.Item>
|
| 158 |
+
<Flex gap={'small'} align="center">
|
| 159 |
+
{fileThumbnail ? (
|
| 160 |
+
<img
|
| 161 |
+
src={fileThumbnail}
|
| 162 |
+
className={styles.thumbnailImg}
|
| 163 |
+
></img>
|
| 164 |
+
) : (
|
| 165 |
+
<SvgIcon
|
| 166 |
+
name={`file-icon/${fileExtension}`}
|
| 167 |
+
width={24}
|
| 168 |
+
></SvgIcon>
|
| 169 |
+
)}
|
| 170 |
+
|
| 171 |
+
{isImage(fileExtension) ? (
|
| 172 |
+
<NewDocumentLink
|
| 173 |
+
documentId={item.id}
|
| 174 |
+
documentName={item.name}
|
| 175 |
+
prefix="document"
|
| 176 |
+
>
|
| 177 |
+
{item.name}
|
| 178 |
+
</NewDocumentLink>
|
| 179 |
+
) : (
|
| 180 |
+
<Button
|
| 181 |
+
type={'text'}
|
| 182 |
+
onClick={handleUserDocumentClick(item.id)}
|
| 183 |
+
>
|
| 184 |
+
{item.name}
|
| 185 |
+
</Button>
|
| 186 |
+
)}
|
| 187 |
+
</Flex>
|
| 188 |
+
</List.Item>
|
| 189 |
+
);
|
| 190 |
+
}}
|
| 191 |
+
/>
|
| 192 |
+
)}
|
| 193 |
</Flex>
|
| 194 |
</div>
|
| 195 |
</section>
|
| 196 |
+
{visible && (
|
| 197 |
+
<IndentedTreeModal
|
| 198 |
+
visible={visible}
|
| 199 |
+
hideModal={hideModal}
|
| 200 |
+
documentId={clickedDocumentId}
|
| 201 |
+
></IndentedTreeModal>
|
| 202 |
+
)}
|
| 203 |
</div>
|
| 204 |
);
|
| 205 |
};
|
| 206 |
|
| 207 |
+
export default memo(MessageItem);
|
web/src/hooks/chunk-hooks.ts
CHANGED
|
@@ -207,12 +207,13 @@ export const useFetchChunk = (chunkId?: string): ResponseType<any> => {
|
|
| 207 |
return data;
|
| 208 |
};
|
| 209 |
|
| 210 |
-
export const useFetchKnowledgeGraph = (
|
| 211 |
-
|
| 212 |
-
|
| 213 |
const { data } = useQuery({
|
| 214 |
queryKey: ['fetchKnowledgeGraph', documentId],
|
| 215 |
initialData: true,
|
|
|
|
| 216 |
gcTime: 0,
|
| 217 |
queryFn: async () => {
|
| 218 |
const data = await kbService.knowledge_graph({
|
|
|
|
| 207 |
return data;
|
| 208 |
};
|
| 209 |
|
| 210 |
+
export const useFetchKnowledgeGraph = (
|
| 211 |
+
documentId: string,
|
| 212 |
+
): ResponseType<any> => {
|
| 213 |
const { data } = useQuery({
|
| 214 |
queryKey: ['fetchKnowledgeGraph', documentId],
|
| 215 |
initialData: true,
|
| 216 |
+
enabled: !!documentId,
|
| 217 |
gcTime: 0,
|
| 218 |
queryFn: async () => {
|
| 219 |
const data = await kbService.knowledge_graph({
|
web/src/hooks/common-hooks.tsx
CHANGED
|
@@ -7,16 +7,16 @@ import { useTranslation } from 'react-i18next';
|
|
| 7 |
export const useSetModalState = () => {
|
| 8 |
const [visible, setVisible] = useState(false);
|
| 9 |
|
| 10 |
-
const showModal = () => {
|
| 11 |
setVisible(true);
|
| 12 |
-
};
|
| 13 |
-
const hideModal = () => {
|
| 14 |
setVisible(false);
|
| 15 |
-
};
|
| 16 |
|
| 17 |
-
const switchVisible = () => {
|
| 18 |
setVisible(!visible);
|
| 19 |
-
};
|
| 20 |
|
| 21 |
return { visible, showModal, hideModal, switchVisible };
|
| 22 |
};
|
|
|
|
| 7 |
export const useSetModalState = () => {
|
| 8 |
const [visible, setVisible] = useState(false);
|
| 9 |
|
| 10 |
+
const showModal = useCallback(() => {
|
| 11 |
setVisible(true);
|
| 12 |
+
}, []);
|
| 13 |
+
const hideModal = useCallback(() => {
|
| 14 |
setVisible(false);
|
| 15 |
+
}, []);
|
| 16 |
|
| 17 |
+
const switchVisible = useCallback(() => {
|
| 18 |
setVisible(!visible);
|
| 19 |
+
}, [visible]);
|
| 20 |
|
| 21 |
return { visible, showModal, hideModal, switchVisible };
|
| 22 |
};
|
web/src/hooks/document-hooks.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
|
|
| 1 |
import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge';
|
| 2 |
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
|
|
|
|
| 3 |
import { api_host } from '@/utils/api';
|
| 4 |
import { buildChunkHighlights } from '@/utils/document-util';
|
|
|
|
| 5 |
import { UploadFile } from 'antd';
|
| 6 |
import { useCallback, useMemo, useState } from 'react';
|
| 7 |
import { IHighlight } from 'react-pdf-highlighter';
|
|
@@ -253,3 +256,37 @@ export const useSelectRunDocumentLoading = () => {
|
|
| 253 |
const loading = useOneNamespaceEffectsLoading('kFModel', ['document_run']);
|
| 254 |
return loading;
|
| 255 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { IDocumentInfo } from '@/interfaces/database/document';
|
| 2 |
import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge';
|
| 3 |
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
|
| 4 |
+
import kbService from '@/services/knowledge-service';
|
| 5 |
import { api_host } from '@/utils/api';
|
| 6 |
import { buildChunkHighlights } from '@/utils/document-util';
|
| 7 |
+
import { useQuery } from '@tanstack/react-query';
|
| 8 |
import { UploadFile } from 'antd';
|
| 9 |
import { useCallback, useMemo, useState } from 'react';
|
| 10 |
import { IHighlight } from 'react-pdf-highlighter';
|
|
|
|
| 256 |
const loading = useOneNamespaceEffectsLoading('kFModel', ['document_run']);
|
| 257 |
return loading;
|
| 258 |
};
|
| 259 |
+
|
| 260 |
+
export const useFetchDocumentInfosByIds = () => {
|
| 261 |
+
const [ids, setDocumentIds] = useState<string[]>([]);
|
| 262 |
+
const { data } = useQuery<IDocumentInfo[]>({
|
| 263 |
+
queryKey: ['fetchDocumentInfos', ids],
|
| 264 |
+
enabled: ids.length > 0,
|
| 265 |
+
initialData: [],
|
| 266 |
+
queryFn: async () => {
|
| 267 |
+
const { data } = await kbService.document_infos({ doc_ids: ids });
|
| 268 |
+
if (data.retcode === 0) {
|
| 269 |
+
return data.data;
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
return [];
|
| 273 |
+
},
|
| 274 |
+
});
|
| 275 |
+
|
| 276 |
+
return { data, setDocumentIds };
|
| 277 |
+
};
|
| 278 |
+
|
| 279 |
+
export const useFetchDocumentThumbnailsByIds = () => {
|
| 280 |
+
const [ids, setDocumentIds] = useState<string[]>([]);
|
| 281 |
+
const { data } = useQuery({
|
| 282 |
+
queryKey: ['fetchDocumentThumbnails', ids],
|
| 283 |
+
initialData: [],
|
| 284 |
+
queryFn: async () => {
|
| 285 |
+
const { data } = await kbService.document_thumbnails({ doc_ids: ids });
|
| 286 |
+
|
| 287 |
+
return data;
|
| 288 |
+
},
|
| 289 |
+
});
|
| 290 |
+
|
| 291 |
+
return { data, setDocumentIds };
|
| 292 |
+
};
|
web/src/interfaces/database/chat.ts
CHANGED
|
@@ -66,6 +66,7 @@ export interface IConversation {
|
|
| 66 |
export interface Message {
|
| 67 |
content: string;
|
| 68 |
role: MessageType;
|
|
|
|
| 69 |
}
|
| 70 |
|
| 71 |
export interface IReference {
|
|
|
|
| 66 |
export interface Message {
|
| 67 |
content: string;
|
| 68 |
role: MessageType;
|
| 69 |
+
doc_ids?: string[];
|
| 70 |
}
|
| 71 |
|
| 72 |
export interface IReference {
|
web/src/interfaces/database/document.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export interface IDocumentInfo {
|
| 2 |
+
chunk_num: number;
|
| 3 |
+
create_date: string;
|
| 4 |
+
create_time: number;
|
| 5 |
+
created_by: string;
|
| 6 |
+
id: string;
|
| 7 |
+
kb_id: string;
|
| 8 |
+
location: string;
|
| 9 |
+
name: string;
|
| 10 |
+
parser_config: Parserconfig;
|
| 11 |
+
parser_id: string;
|
| 12 |
+
process_begin_at: null;
|
| 13 |
+
process_duation: number;
|
| 14 |
+
progress: number;
|
| 15 |
+
progress_msg: string;
|
| 16 |
+
run: string;
|
| 17 |
+
size: number;
|
| 18 |
+
source_type: string;
|
| 19 |
+
status: string;
|
| 20 |
+
thumbnail: string;
|
| 21 |
+
token_num: number;
|
| 22 |
+
type: string;
|
| 23 |
+
update_date: string;
|
| 24 |
+
update_time: number;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
interface Parserconfig {
|
| 28 |
+
chunk_token_num: number;
|
| 29 |
+
layout_recognize: boolean;
|
| 30 |
+
raptor: Raptor;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
interface Raptor {
|
| 34 |
+
use_raptor: boolean;
|
| 35 |
+
}
|
web/src/pages/add-knowledge/components/knowledge-chunk/components/knowledge-graph/modal.tsx
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
|
|
| 1 |
import { useFetchKnowledgeGraph } from '@/hooks/chunk-hooks';
|
|
|
|
| 2 |
import { Flex, Modal, Segmented } from 'antd';
|
| 3 |
import React, { useEffect, useMemo, useState } from 'react';
|
| 4 |
import { useTranslation } from 'react-i18next';
|
| 5 |
import ForceGraph from './force-graph';
|
| 6 |
-
import IndentedTree from './indented-tree';
|
| 7 |
import styles from './index.less';
|
| 8 |
import { isDataExist } from './util';
|
| 9 |
|
|
@@ -14,7 +15,8 @@ enum SegmentedValue {
|
|
| 14 |
|
| 15 |
const KnowledgeGraphModal: React.FC = () => {
|
| 16 |
const [isModalOpen, setIsModalOpen] = useState(false);
|
| 17 |
-
const {
|
|
|
|
| 18 |
const [value, setValue] = useState<SegmentedValue>(SegmentedValue.Graph);
|
| 19 |
const { t } = useTranslation();
|
| 20 |
|
|
|
|
| 1 |
+
import IndentedTree from '@/components/indented-tree/indented-tree';
|
| 2 |
import { useFetchKnowledgeGraph } from '@/hooks/chunk-hooks';
|
| 3 |
+
import { useGetKnowledgeSearchParams } from '@/hooks/route-hook';
|
| 4 |
import { Flex, Modal, Segmented } from 'antd';
|
| 5 |
import React, { useEffect, useMemo, useState } from 'react';
|
| 6 |
import { useTranslation } from 'react-i18next';
|
| 7 |
import ForceGraph from './force-graph';
|
|
|
|
| 8 |
import styles from './index.less';
|
| 9 |
import { isDataExist } from './util';
|
| 10 |
|
|
|
|
| 15 |
|
| 16 |
const KnowledgeGraphModal: React.FC = () => {
|
| 17 |
const [isModalOpen, setIsModalOpen] = useState(false);
|
| 18 |
+
const { documentId } = useGetKnowledgeSearchParams();
|
| 19 |
+
const { data } = useFetchKnowledgeGraph(documentId);
|
| 20 |
const [value, setValue] = useState<SegmentedValue>(SegmentedValue.Graph);
|
| 21 |
const { t } = useTranslation();
|
| 22 |
|
web/src/pages/chat/chat-container/index.tsx
CHANGED
|
@@ -1,8 +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 { Button, Drawer, Flex, Input, Spin } from 'antd';
|
| 6 |
import {
|
| 7 |
useClickDrawer,
|
| 8 |
useFetchConversationOnMount,
|
|
@@ -14,6 +13,7 @@ import {
|
|
| 14 |
} from '../hooks';
|
| 15 |
import { buildMessageItemReference } from '../utils';
|
| 16 |
|
|
|
|
| 17 |
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
| 18 |
import styles from './index.less';
|
| 19 |
|
|
@@ -42,7 +42,6 @@ const ChatContainer = () => {
|
|
| 42 |
const sendDisabled = useSendButtonDisabled(value);
|
| 43 |
useGetFileIcon();
|
| 44 |
const loading = useSelectConversationLoading();
|
| 45 |
-
const { t } = useTranslate('chat');
|
| 46 |
const { data: userInfo } = useFetchUserInfo();
|
| 47 |
|
| 48 |
return (
|
|
@@ -72,7 +71,16 @@ const ChatContainer = () => {
|
|
| 72 |
</div>
|
| 73 |
<div ref={ref} />
|
| 74 |
</Flex>
|
| 75 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
size="large"
|
| 77 |
placeholder={t('sendPlaceholder')}
|
| 78 |
value={value}
|
|
@@ -89,7 +97,7 @@ const ChatContainer = () => {
|
|
| 89 |
}
|
| 90 |
onPressEnter={handlePressEnter}
|
| 91 |
onChange={handleInputChange}
|
| 92 |
-
/>
|
| 93 |
</Flex>
|
| 94 |
<Drawer
|
| 95 |
title="Document Previewer"
|
|
|
|
| 1 |
import MessageItem from '@/components/message-item';
|
| 2 |
import DocumentPreviewer from '@/components/pdf-previewer';
|
| 3 |
import { MessageType } from '@/constants/chat';
|
| 4 |
+
import { Drawer, Flex, Spin } from 'antd';
|
|
|
|
| 5 |
import {
|
| 6 |
useClickDrawer,
|
| 7 |
useFetchConversationOnMount,
|
|
|
|
| 13 |
} from '../hooks';
|
| 14 |
import { buildMessageItemReference } from '../utils';
|
| 15 |
|
| 16 |
+
import MessageInput from '@/components/message-input';
|
| 17 |
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
| 18 |
import styles from './index.less';
|
| 19 |
|
|
|
|
| 42 |
const sendDisabled = useSendButtonDisabled(value);
|
| 43 |
useGetFileIcon();
|
| 44 |
const loading = useSelectConversationLoading();
|
|
|
|
| 45 |
const { data: userInfo } = useFetchUserInfo();
|
| 46 |
|
| 47 |
return (
|
|
|
|
| 71 |
</div>
|
| 72 |
<div ref={ref} />
|
| 73 |
</Flex>
|
| 74 |
+
<MessageInput
|
| 75 |
+
disabled={disabled}
|
| 76 |
+
sendDisabled={sendDisabled}
|
| 77 |
+
sendLoading={sendLoading}
|
| 78 |
+
value={value}
|
| 79 |
+
onInputChange={handleInputChange}
|
| 80 |
+
onPressEnter={handlePressEnter}
|
| 81 |
+
conversationId={conversation.id}
|
| 82 |
+
></MessageInput>
|
| 83 |
+
{/* <Input
|
| 84 |
size="large"
|
| 85 |
placeholder={t('sendPlaceholder')}
|
| 86 |
value={value}
|
|
|
|
| 97 |
}
|
| 98 |
onPressEnter={handlePressEnter}
|
| 99 |
onChange={handleInputChange}
|
| 100 |
+
/> */}
|
| 101 |
</Flex>
|
| 102 |
<Drawer
|
| 103 |
title="Document Previewer"
|
web/src/pages/chat/hooks.ts
CHANGED
|
@@ -547,7 +547,7 @@ export const useSendMessage = (
|
|
| 547 |
const { send, answer, done, setDone } = useSendMessageWithSse();
|
| 548 |
|
| 549 |
const sendMessage = useCallback(
|
| 550 |
-
async (message: string, id?: string) => {
|
| 551 |
const res = await send({
|
| 552 |
conversation_id: id ?? conversationId,
|
| 553 |
messages: [
|
|
@@ -555,6 +555,7 @@ export const useSendMessage = (
|
|
| 555 |
{
|
| 556 |
role: MessageType.User,
|
| 557 |
content: message,
|
|
|
|
| 558 |
},
|
| 559 |
],
|
| 560 |
});
|
|
@@ -586,14 +587,14 @@ export const useSendMessage = (
|
|
| 586 |
);
|
| 587 |
|
| 588 |
const handleSendMessage = useCallback(
|
| 589 |
-
async (message: string) => {
|
| 590 |
if (conversationId !== '') {
|
| 591 |
-
sendMessage(message);
|
| 592 |
} else {
|
| 593 |
const data = await setConversation(message);
|
| 594 |
if (data.retcode === 0) {
|
| 595 |
const id = data.data.id;
|
| 596 |
-
sendMessage(message, id);
|
| 597 |
}
|
| 598 |
}
|
| 599 |
},
|
|
@@ -614,15 +615,19 @@ export const useSendMessage = (
|
|
| 614 |
}
|
| 615 |
}, [setDone, conversationId]);
|
| 616 |
|
| 617 |
-
const handlePressEnter = useCallback(
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
|
| 622 |
-
|
| 623 |
-
|
| 624 |
-
|
| 625 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 626 |
|
| 627 |
return {
|
| 628 |
handlePressEnter,
|
|
|
|
| 547 |
const { send, answer, done, setDone } = useSendMessageWithSse();
|
| 548 |
|
| 549 |
const sendMessage = useCallback(
|
| 550 |
+
async (message: string, documentIds: string[], id?: string) => {
|
| 551 |
const res = await send({
|
| 552 |
conversation_id: id ?? conversationId,
|
| 553 |
messages: [
|
|
|
|
| 555 |
{
|
| 556 |
role: MessageType.User,
|
| 557 |
content: message,
|
| 558 |
+
doc_ids: documentIds,
|
| 559 |
},
|
| 560 |
],
|
| 561 |
});
|
|
|
|
| 587 |
);
|
| 588 |
|
| 589 |
const handleSendMessage = useCallback(
|
| 590 |
+
async (message: string, documentIds: string[]) => {
|
| 591 |
if (conversationId !== '') {
|
| 592 |
+
return sendMessage(message, documentIds);
|
| 593 |
} else {
|
| 594 |
const data = await setConversation(message);
|
| 595 |
if (data.retcode === 0) {
|
| 596 |
const id = data.data.id;
|
| 597 |
+
return sendMessage(message, documentIds, id);
|
| 598 |
}
|
| 599 |
}
|
| 600 |
},
|
|
|
|
| 615 |
}
|
| 616 |
}, [setDone, conversationId]);
|
| 617 |
|
| 618 |
+
const handlePressEnter = useCallback(
|
| 619 |
+
async (documentIds: string[]) => {
|
| 620 |
+
if (trim(value) === '') return;
|
| 621 |
+
let ret;
|
| 622 |
+
if (done) {
|
| 623 |
+
setValue('');
|
| 624 |
+
ret = await handleSendMessage(value.trim(), documentIds);
|
| 625 |
+
}
|
| 626 |
+
addNewestConversation(value);
|
| 627 |
+
return ret;
|
| 628 |
+
},
|
| 629 |
+
[addNewestConversation, handleSendMessage, done, setValue, value],
|
| 630 |
+
);
|
| 631 |
|
| 632 |
return {
|
| 633 |
handlePressEnter,
|
web/src/pages/force-graph/index.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import { Graph } from '@antv/g6';
|
|
| 2 |
import { useSize } from 'ahooks';
|
| 3 |
import { useEffect, useRef } from 'react';
|
| 4 |
import { graphData } from './constant';
|
|
|
|
| 5 |
|
| 6 |
import styles from './index.less';
|
| 7 |
import { Converter } from './util';
|
|
@@ -108,4 +109,4 @@ const ForceGraph = () => {
|
|
| 108 |
return <div ref={containerRef} className={styles.container} />;
|
| 109 |
};
|
| 110 |
|
| 111 |
-
export default
|
|
|
|
| 2 |
import { useSize } from 'ahooks';
|
| 3 |
import { useEffect, useRef } from 'react';
|
| 4 |
import { graphData } from './constant';
|
| 5 |
+
import InputWithUpload from './input-upload';
|
| 6 |
|
| 7 |
import styles from './index.less';
|
| 8 |
import { Converter } from './util';
|
|
|
|
| 109 |
return <div ref={containerRef} className={styles.container} />;
|
| 110 |
};
|
| 111 |
|
| 112 |
+
export default InputWithUpload;
|
web/src/pages/force-graph/input-upload.tsx
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Authorization } from '@/constants/authorization';
|
| 2 |
+
import { getAuthorization } from '@/utils/authorization-util';
|
| 3 |
+
import { PlusOutlined } from '@ant-design/icons';
|
| 4 |
+
import type { GetProp, UploadFile, UploadProps } from 'antd';
|
| 5 |
+
import { Image, Input, Upload } from 'antd';
|
| 6 |
+
import { useState } from 'react';
|
| 7 |
+
import { useGetChatSearchParams } from '../chat/hooks';
|
| 8 |
+
|
| 9 |
+
type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];
|
| 10 |
+
|
| 11 |
+
const getBase64 = (file: FileType): Promise<string> =>
|
| 12 |
+
new Promise((resolve, reject) => {
|
| 13 |
+
const reader = new FileReader();
|
| 14 |
+
reader.readAsDataURL(file);
|
| 15 |
+
reader.onload = () => resolve(reader.result as string);
|
| 16 |
+
reader.onerror = (error) => reject(error);
|
| 17 |
+
});
|
| 18 |
+
|
| 19 |
+
const InputWithUpload = () => {
|
| 20 |
+
const [previewOpen, setPreviewOpen] = useState(false);
|
| 21 |
+
const [previewImage, setPreviewImage] = useState('');
|
| 22 |
+
const { conversationId } = useGetChatSearchParams();
|
| 23 |
+
const [fileList, setFileList] = useState<UploadFile[]>([
|
| 24 |
+
{
|
| 25 |
+
uid: '-1',
|
| 26 |
+
name: 'image.png',
|
| 27 |
+
status: 'done',
|
| 28 |
+
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
uid: '-2',
|
| 32 |
+
name: 'image.png',
|
| 33 |
+
status: 'done',
|
| 34 |
+
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
| 35 |
+
},
|
| 36 |
+
{
|
| 37 |
+
uid: '-3',
|
| 38 |
+
name: 'image.png',
|
| 39 |
+
status: 'done',
|
| 40 |
+
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
uid: '-4',
|
| 44 |
+
name: 'image.png',
|
| 45 |
+
status: 'done',
|
| 46 |
+
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
uid: '-xxx',
|
| 50 |
+
percent: 50,
|
| 51 |
+
name: 'image.png',
|
| 52 |
+
status: 'uploading',
|
| 53 |
+
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
| 54 |
+
},
|
| 55 |
+
{
|
| 56 |
+
uid: '-5',
|
| 57 |
+
name: 'image.png',
|
| 58 |
+
status: 'error',
|
| 59 |
+
},
|
| 60 |
+
]);
|
| 61 |
+
|
| 62 |
+
const handlePreview = async (file: UploadFile) => {
|
| 63 |
+
if (!file.url && !file.preview) {
|
| 64 |
+
file.preview = await getBase64(file.originFileObj as FileType);
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
setPreviewImage(file.url || (file.preview as string));
|
| 68 |
+
setPreviewOpen(true);
|
| 69 |
+
};
|
| 70 |
+
|
| 71 |
+
const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) =>
|
| 72 |
+
setFileList(newFileList);
|
| 73 |
+
|
| 74 |
+
const uploadButton = (
|
| 75 |
+
<button style={{ border: 0, background: 'none' }} type="button">
|
| 76 |
+
<PlusOutlined />
|
| 77 |
+
<div style={{ marginTop: 8 }}>Upload</div>
|
| 78 |
+
</button>
|
| 79 |
+
);
|
| 80 |
+
return (
|
| 81 |
+
<>
|
| 82 |
+
<Input placeholder="Basic usage"></Input>
|
| 83 |
+
<Upload
|
| 84 |
+
action="/v1/document/upload_and_parse"
|
| 85 |
+
listType="picture-card"
|
| 86 |
+
fileList={fileList}
|
| 87 |
+
onPreview={handlePreview}
|
| 88 |
+
onChange={handleChange}
|
| 89 |
+
multiple
|
| 90 |
+
headers={{ [Authorization]: getAuthorization() }}
|
| 91 |
+
data={{ conversation_id: '9e9f7d2453e511efb18efa163e197198' }}
|
| 92 |
+
method="post"
|
| 93 |
+
>
|
| 94 |
+
{fileList.length >= 8 ? null : uploadButton}
|
| 95 |
+
</Upload>
|
| 96 |
+
{previewImage && (
|
| 97 |
+
<Image
|
| 98 |
+
wrapperStyle={{ display: 'none' }}
|
| 99 |
+
preview={{
|
| 100 |
+
visible: previewOpen,
|
| 101 |
+
onVisibleChange: (visible) => setPreviewOpen(visible),
|
| 102 |
+
afterOpenChange: (visible) => !visible && setPreviewImage(''),
|
| 103 |
+
}}
|
| 104 |
+
src={previewImage}
|
| 105 |
+
/>
|
| 106 |
+
)}
|
| 107 |
+
</>
|
| 108 |
+
);
|
| 109 |
+
};
|
| 110 |
+
|
| 111 |
+
export default () => {
|
| 112 |
+
return (
|
| 113 |
+
<section style={{ height: 500, width: 400 }}>
|
| 114 |
+
<div style={{ height: 200 }}></div>
|
| 115 |
+
<InputWithUpload></InputWithUpload>
|
| 116 |
+
</section>
|
| 117 |
+
);
|
| 118 |
+
};
|
web/src/services/knowledge-service.ts
CHANGED
|
@@ -28,6 +28,7 @@ const {
|
|
| 28 |
document_upload,
|
| 29 |
web_crawl,
|
| 30 |
knowledge_graph,
|
|
|
|
| 31 |
} = api;
|
| 32 |
|
| 33 |
const methods = {
|
|
@@ -93,6 +94,10 @@ const methods = {
|
|
| 93 |
url: web_crawl,
|
| 94 |
method: 'post',
|
| 95 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
// chunk管理
|
| 97 |
chunk_list: {
|
| 98 |
url: chunk_list,
|
|
|
|
| 28 |
document_upload,
|
| 29 |
web_crawl,
|
| 30 |
knowledge_graph,
|
| 31 |
+
document_infos,
|
| 32 |
} = api;
|
| 33 |
|
| 34 |
const methods = {
|
|
|
|
| 94 |
url: web_crawl,
|
| 95 |
method: 'post',
|
| 96 |
},
|
| 97 |
+
document_infos: {
|
| 98 |
+
url: document_infos,
|
| 99 |
+
method: 'post',
|
| 100 |
+
},
|
| 101 |
// chunk管理
|
| 102 |
chunk_list: {
|
| 103 |
url: chunk_list,
|
web/src/utils/api.ts
CHANGED
|
@@ -38,7 +38,6 @@ export default {
|
|
| 38 |
knowledge_graph: `${api_host}/chunk/knowledge_graph`,
|
| 39 |
|
| 40 |
// document
|
| 41 |
-
upload: `${api_host}/document/upload`,
|
| 42 |
get_document_list: `${api_host}/document/list`,
|
| 43 |
document_change_status: `${api_host}/document/change_status`,
|
| 44 |
document_rm: `${api_host}/document/rm`,
|
|
@@ -50,6 +49,7 @@ export default {
|
|
| 50 |
get_document_file: `${api_host}/document/get`,
|
| 51 |
document_upload: `${api_host}/document/upload`,
|
| 52 |
web_crawl: `${api_host}/document/web_crawl`,
|
|
|
|
| 53 |
|
| 54 |
// chat
|
| 55 |
setDialog: `${api_host}/dialog/set`,
|
|
|
|
| 38 |
knowledge_graph: `${api_host}/chunk/knowledge_graph`,
|
| 39 |
|
| 40 |
// document
|
|
|
|
| 41 |
get_document_list: `${api_host}/document/list`,
|
| 42 |
document_change_status: `${api_host}/document/change_status`,
|
| 43 |
document_rm: `${api_host}/document/rm`,
|
|
|
|
| 49 |
get_document_file: `${api_host}/document/get`,
|
| 50 |
document_upload: `${api_host}/document/upload`,
|
| 51 |
web_crawl: `${api_host}/document/web_crawl`,
|
| 52 |
+
document_infos: `${api_host}/document/infos`,
|
| 53 |
|
| 54 |
// chat
|
| 55 |
setDialog: `${api_host}/dialog/set`,
|
web/src/utils/document-util.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
import { SupportedPreviewDocumentTypes } from '@/constants/common';
|
| 2 |
import { IChunk } from '@/interfaces/database/knowledge';
|
| 3 |
import { UploadFile } from 'antd';
|
| 4 |
import { v4 as uuid } from 'uuid';
|
|
@@ -51,3 +51,7 @@ export const getUnSupportedFilesCount = (message: string) => {
|
|
| 51 |
export const isSupportedPreviewDocumentType = (fileExtension: string) => {
|
| 52 |
return SupportedPreviewDocumentTypes.includes(fileExtension);
|
| 53 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Images, SupportedPreviewDocumentTypes } from '@/constants/common';
|
| 2 |
import { IChunk } from '@/interfaces/database/knowledge';
|
| 3 |
import { UploadFile } from 'antd';
|
| 4 |
import { v4 as uuid } from 'uuid';
|
|
|
|
| 51 |
export const isSupportedPreviewDocumentType = (fileExtension: string) => {
|
| 52 |
return SupportedPreviewDocumentTypes.includes(fileExtension);
|
| 53 |
};
|
| 54 |
+
|
| 55 |
+
export const isImage = (image: string) => {
|
| 56 |
+
return [...Images, 'svg'].some((x) => x === image);
|
| 57 |
+
};
|