diff --git a/web/src/components/new-document-link.tsx b/web/src/components/new-document-link.tsx index 84c0c9378db6d668b1740181dcef4b6ad136df68..e64ef1424601b422a4e302dba6dd36620ac30a01 100644 --- a/web/src/components/new-document-link.tsx +++ b/web/src/components/new-document-link.tsx @@ -3,12 +3,18 @@ import React from 'react'; interface IProps extends React.PropsWithChildren { documentId: string; + preventDefault?: boolean; } -const NewDocumentLink = ({ children, documentId }: IProps) => { +const NewDocumentLink = ({ + children, + documentId, + preventDefault = false, +}: IProps) => { return ( <a target="_blank" + onClick={!preventDefault ? undefined : (e) => e.preventDefault()} href={`${api_host}/document/get/${documentId}`} rel="noreferrer" > diff --git a/web/src/hooks/knowledgeHook.ts b/web/src/hooks/knowledgeHook.ts index 1ef516771f826c6fb0916e3f59ae9c06b7ec66e2..8a0f7a32c0c409d996ed717e759d0cf746880611 100644 --- a/web/src/hooks/knowledgeHook.ts +++ b/web/src/hooks/knowledgeHook.ts @@ -206,3 +206,26 @@ export const useSelectKnowledgeDetails = () => { return knowledgeDetails; }; //#endregion + +//#region Retrieval testing + +export const useTestChunkRetrieval = () => { + const dispatch = useDispatch(); + const knowledgeBaseId = useKnowledgeBaseId(); + + const testChunk = useCallback( + (values: any) => { + dispatch({ + type: 'testingModel/testDocumentChunk', + payload: { + ...values, + kb_id: knowledgeBaseId, + }, + }); + }, + [dispatch, knowledgeBaseId], + ); + + return testChunk; +}; +//#endregion diff --git a/web/src/pages/add-knowledge/components/knowledge-chunk/index.less b/web/src/pages/add-knowledge/components/knowledge-chunk/index.less index b7380ed59f0438872903b17bc43d250ade3ca0ae..a41a35a81339cd698d1061c698a03c3fabf04d30 100644 --- a/web/src/pages/add-knowledge/components/knowledge-chunk/index.less +++ b/web/src/pages/add-knowledge/components/knowledge-chunk/index.less @@ -37,6 +37,7 @@ } .chunkContainer { + display: flex; height: calc(100vh - 332px); } diff --git a/web/src/pages/add-knowledge/components/knowledge-file/index.less b/web/src/pages/add-knowledge/components/knowledge-file/index.less index 05614a17ef90e5b94070f41dd0923cc8b725cfcf..d78737b3639e9dd6899fab6a205cd7bd1214f0b6 100644 --- a/web/src/pages/add-knowledge/components/knowledge-file/index.less +++ b/web/src/pages/add-knowledge/components/knowledge-file/index.less @@ -22,7 +22,6 @@ .img { height: 24px; width: 24px; - margin-right: 10px; display: inline-block; vertical-align: middle; } diff --git a/web/src/pages/add-knowledge/components/knowledge-file/index.tsx b/web/src/pages/add-knowledge/components/knowledge-file/index.tsx index 73200c222eb5997657ff665f87fb8820ffd9d706..6fa7f208d6126e193def06e4c23df6a153042dae 100644 --- a/web/src/pages/add-knowledge/components/knowledge-file/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-file/index.tsx @@ -1,9 +1,12 @@ +import ChunkMethodModal from '@/components/chunk-method-modal'; +import SvgIcon from '@/components/svg-icon'; import { useSelectDocumentList, useSetDocumentStatus, } from '@/hooks/documentHooks'; import { useSelectParserList } from '@/hooks/userSettingHook'; import { IKnowledgeFile } from '@/interfaces/database/knowledge'; +import { getExtension } from '@/utils/documentUtils'; import { FileOutlined, FileTextOutlined, @@ -15,6 +18,7 @@ import { Button, Divider, Dropdown, + Flex, Input, Space, Switch, @@ -38,8 +42,6 @@ import ParsingActionCell from './parsing-action-cell'; import ParsingStatusCell from './parsing-status-cell'; import RenameModal from './rename-modal'; -import ChunkMethodModal from '@/components/chunk-method-modal'; -import { getExtension } from '@/utils/documentUtils'; import styles from './index.less'; const KnowledgeFile = () => { @@ -114,10 +116,19 @@ const KnowledgeFile = () => { dataIndex: 'name', key: 'name', fixed: 'left', - render: (text: any, { id, thumbnail }) => ( + render: (text: any, { id, thumbnail, name }) => ( <div className={styles.tochunks} onClick={() => toChunk(id)}> - <img className={styles.img} src={thumbnail} alt="" /> - {text} + <Flex gap={10} align="center"> + {thumbnail ? ( + <img className={styles.img} src={thumbnail} alt="" /> + ) : ( + <SvgIcon + name={`file-icon/${getExtension(name)}`} + width={24} + ></SvgIcon> + )} + {text} + </Flex> </div> ), }, diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/index.tsx b/web/src/pages/add-knowledge/components/knowledge-testing/index.tsx index 4b449f252f57194f74a66670e3596b63c074b684..47168611fcaa87612cb4e3f4c1383d00e40beb6d 100644 --- a/web/src/pages/add-knowledge/components/knowledge-testing/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-testing/index.tsx @@ -1,28 +1,21 @@ +import { useTestChunkRetrieval } from '@/hooks/knowledgeHook'; import { Flex, Form } from 'antd'; +import { useEffect } from 'react'; +import { useDispatch } from 'umi'; import TestingControl from './testing-control'; import TestingResult from './testing-result'; -import { useKnowledgeBaseId } from '@/hooks/knowledgeHook'; -import { useEffect } from 'react'; -import { useDispatch } from 'umi'; import styles from './index.less'; const KnowledgeTesting = () => { const [form] = Form.useForm(); + const testChunk = useTestChunkRetrieval(); const dispatch = useDispatch(); - const knowledgeBaseId = useKnowledgeBaseId(); const handleTesting = async () => { const values = await form.validateFields(); - console.info(values); - dispatch({ - type: 'testingModel/testDocumentChunk', - payload: { - ...values, - kb_id: knowledgeBaseId, - }, - }); + testChunk(values); }; useEffect(() => { diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx b/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx index 4630e2e2417accb92f515af855de9015c388073f..a2d915ba19d4f99d760f971233c5e16cf773ae1d 100644 --- a/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx @@ -2,10 +2,9 @@ import SimilaritySlider from '@/components/similarity-slider'; import { Button, Card, Divider, Flex, Form, Input, Slider, Tag } from 'antd'; import { FormInstance } from 'antd/lib'; +import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks'; import styles from './index.less'; -const list = [1, 2, 3]; - type FieldType = { similarity_threshold?: number; vector_similarity_weight?: number; @@ -20,6 +19,9 @@ interface IProps { const TestingControl = ({ form, handleTesting }: IProps) => { const question = Form.useWatch('question', { form, preserve: true }); + const loading = useOneNamespaceEffectsLoading('testingModel', [ + 'testDocumentChunk', + ]); const buttonDisabled = !question || (typeof question === 'string' && question.trim() === ''); @@ -65,6 +67,7 @@ const TestingControl = ({ form, handleTesting }: IProps) => { size="small" onClick={handleTesting} disabled={buttonDisabled} + loading={loading} > Testing </Button> diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.less b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.less index 1059af55083d75ef78515a8c43991dde39c89320..464d30696466d23faa0adeebf545c6a31140f713 100644 --- a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.less +++ b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.less @@ -35,9 +35,11 @@ } .image { width: 100px; + object-fit: contain; } - .imagePreview { - display: block; - width: 260px; - } +} +.imagePreview { + display: block; + max-width: 45vw; + max-height: 40vh; } diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.tsx b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.tsx index 9277418c848e567d1effe86cd9220285ce1367b9..1dd4b5f2eea5aa2857cf138a411362314e3eca4b 100644 --- a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.tsx @@ -104,7 +104,7 @@ const TestingResult = ({ handleTesting }: IProps) => { <Flex gap={'middle'}> {x.img_id && ( <Popover - placement="topRight" + placement="left" content={ <Image id={x.img_id} diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/select-files.tsx b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/select-files.tsx index cb80d01750052e6b5f23961c9d3ba63c32088a05..a0e2816e1d48ccb694bef28f6061a893b856bc8d 100644 --- a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/select-files.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/select-files.tsx @@ -1,6 +1,7 @@ import { ReactComponent as NavigationPointerIcon } from '@/assets/svg/navigation-pointer.svg'; import NewDocumentLink from '@/components/new-document-link'; import { ITestingDocument } from '@/interfaces/database/knowledge'; +import { isPdf } from '@/utils/documentUtils'; import { Table, TableProps } from 'antd'; import { useDispatch, useSelector } from 'umi'; @@ -33,8 +34,8 @@ const SelectFiles = ({ handleTesting }: IProps) => { title: 'View', key: 'view', width: 50, - render: (_, { doc_id }) => ( - <NewDocumentLink documentId={doc_id}> + render: (_, { doc_id, doc_name }) => ( + <NewDocumentLink documentId={doc_id} preventDefault={!isPdf(doc_name)}> <NavigationPointerIcon /> </NewDocumentLink> ), diff --git a/web/src/pages/chat/chat-container/index.less b/web/src/pages/chat/chat-container/index.less index e13b72ddcae283031a203e96c29ac3447c95c402..b521e4b66ad889bf2108e5a0c8eb8acc3075a337 100644 --- a/web/src/pages/chat/chat-container/index.less +++ b/web/src/pages/chat/chat-container/index.less @@ -64,3 +64,6 @@ max-height: 45vh; overflow-y: auto; } +.documentLink { + padding: 0; +} diff --git a/web/src/pages/chat/chat-container/index.tsx b/web/src/pages/chat/chat-container/index.tsx index a92712194af34213b2cbe726407bbb323d565ea5..887e92f5876b87aab07ccda1dd8cba4e72da49eb 100644 --- a/web/src/pages/chat/chat-container/index.tsx +++ b/web/src/pages/chat/chat-container/index.tsx @@ -36,6 +36,8 @@ import { useSendMessage, } from '../hooks'; +import SvgIcon from '@/components/svg-icon'; +import { getExtension, isPdf } from '@/utils/documentUtils'; import styles from './index.less'; const reg = /(#{2}\d+\${2})/g; @@ -74,7 +76,10 @@ const MessageItem = ({ const isAssistant = item.role === MessageType.Assistant; const handleDocumentButtonClick = useCallback( - (documentId: string, chunk: IChunk) => () => { + (documentId: string, chunk: IChunk, isPdf: boolean) => () => { + if (!isPdf) { + return; + } clickDocumentButton(documentId, chunk); }, [clickDocumentButton], @@ -88,26 +93,31 @@ const MessageItem = ({ (x) => x?.doc_id === chunkItem?.doc_id, ); const documentId = document?.doc_id; + const fileThumbnail = documentId ? fileThumbnails[documentId] : ''; + const fileExtension = documentId ? getExtension(document?.doc_name) : ''; + const imageId = chunkItem?.img_id; return ( <Flex key={chunkItem?.chunk_id} gap={10} className={styles.referencePopoverWrapper} > - <Popover - placement="left" - content={ + {imageId && ( + <Popover + placement="left" + content={ + <Image + id={imageId} + className={styles.referenceImagePreview} + ></Image> + } + > <Image - id={chunkItem?.img_id} - className={styles.referenceImagePreview} + id={imageId} + className={styles.referenceChunkImage} ></Image> - } - > - <Image - id={chunkItem?.img_id} - className={styles.referenceChunkImage} - ></Image> - </Popover> + </Popover> + )} <Space direction={'vertical'}> <div dangerouslySetInnerHTML={{ @@ -116,11 +126,23 @@ const MessageItem = ({ className={styles.chunkContentText} ></div> {documentId && ( - <Flex gap={'middle'}> - <img src={fileThumbnails[documentId]} alt="" /> + <Flex gap={'small'}> + {fileThumbnail ? ( + <img src={fileThumbnail} alt="" /> + ) : ( + <SvgIcon + name={`file-icon/${fileExtension}`} + width={24} + ></SvgIcon> + )} <Button type="link" - onClick={handleDocumentButtonClick(documentId, chunkItem)} + className={styles.documentLink} + onClick={handleDocumentButtonClick( + documentId, + chunkItem, + fileExtension === 'pdf', + )} > {document?.doc_name} </Button> @@ -224,17 +246,31 @@ const MessageItem = ({ <List bordered dataSource={referenceDocumentList} - renderItem={(item) => ( - <List.Item> - {/* <SvgIcon name={getFileIcon(item.doc_name)}></SvgIcon> */} - <Flex gap={'middle'}> - <img src={fileThumbnails[item.doc_id]}></img> - <NewDocumentLink documentId={item.doc_id}> - {item.doc_name} - </NewDocumentLink> - </Flex> - </List.Item> - )} + renderItem={(item) => { + const fileThumbnail = fileThumbnails[item.doc_id]; + const fileExtension = getExtension(item.doc_name); + return ( + <List.Item> + <Flex gap={'small'} align="center"> + {fileThumbnail ? ( + <img src={fileThumbnail}></img> + ) : ( + <SvgIcon + name={`file-icon/${fileExtension}`} + width={24} + ></SvgIcon> + )} + + <NewDocumentLink + documentId={item.doc_id} + preventDefault={!isPdf(item.doc_name)} + > + {item.doc_name} + </NewDocumentLink> + </Flex> + </List.Item> + ); + }} /> )} </Flex> diff --git a/web/src/utils/documentUtils.ts b/web/src/utils/documentUtils.ts index 27e9964bd2f4c78a844270b7704e99808dfd06b1..f99d005730eaf4edb1bcc0d4d6354873def63ad7 100644 --- a/web/src/utils/documentUtils.ts +++ b/web/src/utils/documentUtils.ts @@ -38,3 +38,7 @@ export const isFileUploadDone = (file: UploadFile) => file.status === 'done'; export const getExtension = (name: string) => name?.slice(name.lastIndexOf('.') + 1).toLowerCase() ?? ''; + +export const isPdf = (name: string) => { + return getExtension(name) === 'pdf'; +};