### What problem does this PR solve? fix: Change document status with @tanstack/react-query #13306 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) - [ ] New Feature (non-breaking change which adds functionality) - [ ] Documentation Update - [ ] Refactoring - [ ] Performance Improvement - [ ] Other (please describe):tags/v0.13.0
| @@ -18,8 +18,7 @@ export default defineConfig({ | |||
| history: { | |||
| type: 'browser', | |||
| }, | |||
| plugins: ['@react-dev-inspector/umi4-plugin', '@umijs/plugins/dist/dva'], | |||
| dva: {}, | |||
| plugins: ['@react-dev-inspector/umi4-plugin'], | |||
| jsMinifier: 'terser', | |||
| lessLoader: { | |||
| modifyVars: { | |||
| @@ -1,7 +1,5 @@ | |||
| import MaxTokenNumber from '@/components/max-token-number'; | |||
| import { IModalManagerChildrenProps } from '@/components/modal-manager'; | |||
| import { IKnowledgeFileParserConfig } from '@/interfaces/database/knowledge'; | |||
| import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; | |||
| import { | |||
| MinusCircleOutlined, | |||
| PlusOutlined, | |||
| @@ -22,6 +20,8 @@ import React, { useEffect, useMemo } from 'react'; | |||
| import { useFetchParserListOnMount } from './hooks'; | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { IParserConfig } from '@/interfaces/database/document'; | |||
| import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; | |||
| import Delimiter from '../delimiter'; | |||
| import EntityTypesItem from '../entity-types-item'; | |||
| import ExcelToHtml from '../excel-to-html'; | |||
| @@ -39,7 +39,7 @@ interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> { | |||
| ) => void; | |||
| showModal?(): void; | |||
| parserId: string; | |||
| parserConfig: IKnowledgeFileParserConfig; | |||
| parserConfig: IParserConfig; | |||
| documentExtension: string; | |||
| documentId: string; | |||
| } | |||
| @@ -115,7 +115,7 @@ const ChunkMethodModal: React.FC<IProps> = ({ | |||
| useEffect(() => { | |||
| if (visible) { | |||
| const pages = | |||
| parserConfig.pages?.map((x) => ({ from: x[0], to: x[1] })) ?? []; | |||
| parserConfig?.pages?.map((x) => ({ from: x[0], to: x[1] })) ?? []; | |||
| form.setFieldsValue({ | |||
| pages: pages.length > 0 ? pages : [{ from: 1, to: 1024 }], | |||
| parser_config: omit(parserConfig, 'pages'), | |||
| @@ -1,7 +1,8 @@ | |||
| import { getExtension } from '@/utils/document-util'; | |||
| import SvgIcon from '../svg-icon'; | |||
| import { useSelectFileThumbnails } from '@/hooks/knowledge-hooks'; | |||
| import { useFetchDocumentThumbnailsByIds } from '@/hooks/document-hooks'; | |||
| import { useEffect } from 'react'; | |||
| import styles from './index.less'; | |||
| interface IProps { | |||
| @@ -11,10 +12,15 @@ interface IProps { | |||
| const FileIcon = ({ name, id }: IProps) => { | |||
| const fileExtension = getExtension(name); | |||
| // TODO: replace this line with react query | |||
| const fileThumbnails = useSelectFileThumbnails(); | |||
| const { data: fileThumbnails, setDocumentIds } = | |||
| useFetchDocumentThumbnailsByIds(); | |||
| const fileThumbnail = fileThumbnails[id]; | |||
| useEffect(() => { | |||
| setDocumentIds([id]); | |||
| }, [id, setDocumentIds]); | |||
| return fileThumbnail ? ( | |||
| <img src={fileThumbnail} className={styles.thumbnailImg}></img> | |||
| ) : ( | |||
| @@ -1,7 +1,6 @@ | |||
| import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg'; | |||
| import { MessageType } from '@/constants/chat'; | |||
| import { useSetModalState } from '@/hooks/common-hooks'; | |||
| import { useSelectFileThumbnails } from '@/hooks/knowledge-hooks'; | |||
| import { IReference } from '@/interfaces/database/chat'; | |||
| import { IChunk } from '@/interfaces/database/knowledge'; | |||
| import classNames from 'classnames'; | |||
| @@ -50,7 +49,6 @@ const MessageItem = ({ | |||
| }: IProps) => { | |||
| const isAssistant = item.role === MessageType.Assistant; | |||
| const isUser = item.role === MessageType.User; | |||
| const fileThumbnails = useSelectFileThumbnails(); | |||
| const { data: documentList, setDocumentIds } = useFetchDocumentInfosByIds(); | |||
| const { data: documentThumbnails, setDocumentIds: setIds } = | |||
| useFetchDocumentThumbnailsByIds(); | |||
| @@ -77,12 +75,12 @@ const MessageItem = ({ | |||
| const ids = item?.doc_ids ?? []; | |||
| if (ids.length) { | |||
| setDocumentIds(ids); | |||
| const documentIds = ids.filter((x) => !(x in fileThumbnails)); | |||
| const documentIds = ids.filter((x) => !(x in documentThumbnails)); | |||
| if (documentIds.length) { | |||
| setIds(documentIds); | |||
| } | |||
| } | |||
| }, [item.doc_ids, setDocumentIds, setIds, fileThumbnails]); | |||
| }, [item.doc_ids, setDocumentIds, setIds, documentThumbnails]); | |||
| return ( | |||
| <div | |||
| @@ -184,7 +182,7 @@ const MessageItem = ({ | |||
| renderItem={(item) => { | |||
| // TODO: | |||
| const fileThumbnail = | |||
| documentThumbnails[item.id] || fileThumbnails[item.id]; | |||
| documentThumbnails[item.id] || documentThumbnails[item.id]; | |||
| const fileExtension = getExtension(item.name); | |||
| return ( | |||
| <List.Item> | |||
| @@ -1,17 +1,24 @@ | |||
| import { IDocumentInfo } from '@/interfaces/database/document'; | |||
| import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge'; | |||
| import { IChunk } from '@/interfaces/database/knowledge'; | |||
| import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; | |||
| import i18n from '@/locales/config'; | |||
| import chatService from '@/services/chat-service'; | |||
| import kbService from '@/services/knowledge-service'; | |||
| import { api_host } from '@/utils/api'; | |||
| import { buildChunkHighlights } from '@/utils/document-util'; | |||
| import { useMutation, useQuery } from '@tanstack/react-query'; | |||
| import { UploadFile } from 'antd'; | |||
| import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; | |||
| import { UploadFile, message } from 'antd'; | |||
| import { get } from 'lodash'; | |||
| import { useCallback, useMemo, useState } from 'react'; | |||
| import { IHighlight } from 'react-pdf-highlighter'; | |||
| import { useDispatch, useSelector } from 'umi'; | |||
| import { useGetKnowledgeSearchParams } from './route-hook'; | |||
| import { useOneNamespaceEffectsLoading } from './store-hooks'; | |||
| import { | |||
| useGetPaginationWithRouter, | |||
| useHandleSearchChange, | |||
| } from './logic-hooks'; | |||
| import { | |||
| useGetKnowledgeSearchParams, | |||
| useSetPaginationParams, | |||
| } from './route-hook'; | |||
| export const useGetDocumentUrl = (documentId?: string) => { | |||
| const getDocumentUrl = useCallback( | |||
| @@ -43,219 +50,284 @@ export const useGetChunkHighlights = (selectedChunk: IChunk) => { | |||
| return { highlights, setWidthAndHeight }; | |||
| }; | |||
| export const useFetchDocumentList = () => { | |||
| export const useFetchNextDocumentList = () => { | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const dispatch = useDispatch(); | |||
| const fetchKfList = useCallback(() => { | |||
| return dispatch<any>({ | |||
| type: 'kFModel/getKfList', | |||
| payload: { | |||
| const { searchString, handleInputChange } = useHandleSearchChange(); | |||
| const { pagination, setPagination } = useGetPaginationWithRouter(); | |||
| const { data, isFetching: loading } = useQuery<{ | |||
| docs: IDocumentInfo[]; | |||
| total: number; | |||
| }>({ | |||
| queryKey: ['fetchDocumentList', searchString, pagination], | |||
| initialData: { docs: [], total: 0 }, | |||
| refetchInterval: 15000, | |||
| queryFn: async () => { | |||
| const ret = await kbService.get_document_list({ | |||
| kb_id: knowledgeId, | |||
| }, | |||
| }); | |||
| }, [dispatch, knowledgeId]); | |||
| return fetchKfList; | |||
| }; | |||
| keywords: searchString, | |||
| page_size: pagination.pageSize, | |||
| page: pagination.current, | |||
| }); | |||
| if (ret.data.retcode === 0) { | |||
| return ret.data.data; | |||
| } | |||
| export const useSetDocumentStatus = () => { | |||
| const dispatch = useDispatch(); | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| return { | |||
| docs: [], | |||
| total: 0, | |||
| }; | |||
| }, | |||
| }); | |||
| const setDocumentStatus = useCallback( | |||
| (status: boolean, documentId: string) => { | |||
| dispatch({ | |||
| type: 'kFModel/updateDocumentStatus', | |||
| payload: { | |||
| doc_id: documentId, | |||
| status: Number(status), | |||
| kb_id: knowledgeId, | |||
| }, | |||
| }); | |||
| const onInputChange: React.ChangeEventHandler<HTMLInputElement> = useCallback( | |||
| (e) => { | |||
| setPagination({ page: 1 }); | |||
| handleInputChange(e); | |||
| }, | |||
| [dispatch, knowledgeId], | |||
| [handleInputChange, setPagination], | |||
| ); | |||
| return setDocumentStatus; | |||
| }; | |||
| export const useSelectDocumentList = () => { | |||
| const list: IKnowledgeFile[] = useSelector( | |||
| (state: any) => state.kFModel.data, | |||
| ); | |||
| return list; | |||
| return { | |||
| loading, | |||
| searchString, | |||
| documents: data.docs, | |||
| pagination: { ...pagination, total: data?.total }, | |||
| handleInputChange: onInputChange, | |||
| setPagination, | |||
| }; | |||
| }; | |||
| export const useSaveDocumentName = () => { | |||
| const dispatch = useDispatch(); | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| export const useSetNextDocumentStatus = () => { | |||
| const queryClient = useQueryClient(); | |||
| const saveName = useCallback( | |||
| (documentId: string, name: string) => { | |||
| return dispatch<any>({ | |||
| type: 'kFModel/document_rename', | |||
| payload: { | |||
| doc_id: documentId, | |||
| name: name, | |||
| kb_id: knowledgeId, | |||
| }, | |||
| const { | |||
| data, | |||
| isPending: loading, | |||
| mutateAsync, | |||
| } = useMutation({ | |||
| mutationKey: ['updateDocumentStatus'], | |||
| mutationFn: async ({ | |||
| status, | |||
| documentId, | |||
| }: { | |||
| status: boolean; | |||
| documentId: string; | |||
| }) => { | |||
| const { data } = await kbService.document_change_status({ | |||
| doc_id: documentId, | |||
| status: Number(status), | |||
| }); | |||
| if (data.retcode === 0) { | |||
| message.success(i18n.t('message.modified')); | |||
| queryClient.invalidateQueries({ queryKey: ['fetchDocumentList'] }); | |||
| } | |||
| return data; | |||
| }, | |||
| [dispatch, knowledgeId], | |||
| ); | |||
| }); | |||
| return saveName; | |||
| return { setDocumentStatus: mutateAsync, data, loading }; | |||
| }; | |||
| export const useCreateDocument = () => { | |||
| const dispatch = useDispatch(); | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| export const useSaveNextDocumentName = () => { | |||
| const queryClient = useQueryClient(); | |||
| const createDocument = useCallback( | |||
| (name: string) => { | |||
| try { | |||
| return dispatch<any>({ | |||
| type: 'kFModel/document_create', | |||
| payload: { | |||
| name, | |||
| kb_id: knowledgeId, | |||
| }, | |||
| }); | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| const { | |||
| data, | |||
| isPending: loading, | |||
| mutateAsync, | |||
| } = useMutation({ | |||
| mutationKey: ['saveDocumentName'], | |||
| mutationFn: async ({ | |||
| name, | |||
| documentId, | |||
| }: { | |||
| name: string; | |||
| documentId: string; | |||
| }) => { | |||
| const { data } = await kbService.document_rename({ | |||
| doc_id: documentId, | |||
| name: name, | |||
| }); | |||
| if (data.retcode === 0) { | |||
| message.success(i18n.t('message.renamed')); | |||
| queryClient.invalidateQueries({ queryKey: ['fetchDocumentList'] }); | |||
| } | |||
| return data.retcode; | |||
| }, | |||
| [dispatch, knowledgeId], | |||
| ); | |||
| }); | |||
| return createDocument; | |||
| return { loading, saveName: mutateAsync, data }; | |||
| }; | |||
| export const useSetDocumentParser = () => { | |||
| const dispatch = useDispatch(); | |||
| export const useCreateNextDocument = () => { | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const { setPaginationParams, page } = useSetPaginationParams(); | |||
| const queryClient = useQueryClient(); | |||
| const setDocumentParser = useCallback( | |||
| ( | |||
| parserId: string, | |||
| documentId: string, | |||
| parserConfig: IChangeParserConfigRequestBody, | |||
| ) => { | |||
| try { | |||
| return dispatch<any>({ | |||
| type: 'kFModel/document_change_parser', | |||
| payload: { | |||
| parser_id: parserId, | |||
| doc_id: documentId, | |||
| kb_id: knowledgeId, | |||
| parser_config: parserConfig, | |||
| }, | |||
| }); | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| const { | |||
| data, | |||
| isPending: loading, | |||
| mutateAsync, | |||
| } = useMutation({ | |||
| mutationKey: ['createDocument'], | |||
| mutationFn: async (name: string) => { | |||
| const { data } = await kbService.document_create({ | |||
| name, | |||
| kb_id: knowledgeId, | |||
| }); | |||
| if (data.retcode === 0) { | |||
| if (page === 1) { | |||
| queryClient.invalidateQueries({ queryKey: ['fetchDocumentList'] }); | |||
| } else { | |||
| setPaginationParams(); // fetch document list | |||
| } | |||
| message.success(i18n.t('message.created')); | |||
| } | |||
| return data.retcode; | |||
| }, | |||
| [dispatch, knowledgeId], | |||
| ); | |||
| }); | |||
| return setDocumentParser; | |||
| return { createDocument: mutateAsync, loading, data }; | |||
| }; | |||
| export const useRemoveDocument = () => { | |||
| const dispatch = useDispatch(); | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| export const useSetNextDocumentParser = () => { | |||
| const queryClient = useQueryClient(); | |||
| const removeDocument = useCallback( | |||
| (documentIds: string[]) => { | |||
| try { | |||
| return dispatch<any>({ | |||
| type: 'kFModel/document_rm', | |||
| payload: { | |||
| doc_id: documentIds, | |||
| kb_id: knowledgeId, | |||
| }, | |||
| }); | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| const { | |||
| data, | |||
| isPending: loading, | |||
| mutateAsync, | |||
| } = useMutation({ | |||
| mutationKey: ['setDocumentParser'], | |||
| mutationFn: async ({ | |||
| parserId, | |||
| documentId, | |||
| parserConfig, | |||
| }: { | |||
| parserId: string; | |||
| documentId: string; | |||
| parserConfig: IChangeParserConfigRequestBody; | |||
| }) => { | |||
| const { data } = await kbService.document_change_parser({ | |||
| parser_id: parserId, | |||
| doc_id: documentId, | |||
| parser_config: parserConfig, | |||
| }); | |||
| if (data.retcode === 0) { | |||
| queryClient.invalidateQueries({ queryKey: ['fetchDocumentList'] }); | |||
| message.success(i18n.t('message.modified')); | |||
| } | |||
| return data.retcode; | |||
| }, | |||
| [dispatch, knowledgeId], | |||
| ); | |||
| }); | |||
| return removeDocument; | |||
| return { setDocumentParser: mutateAsync, data, loading }; | |||
| }; | |||
| export const useUploadDocument = () => { | |||
| const dispatch = useDispatch(); | |||
| export const useUploadNextDocument = () => { | |||
| const queryClient = useQueryClient(); | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const uploadDocument = useCallback( | |||
| (fileList: UploadFile[]) => { | |||
| const { | |||
| data, | |||
| isPending: loading, | |||
| mutateAsync, | |||
| } = useMutation({ | |||
| mutationKey: ['uploadDocument'], | |||
| mutationFn: async (fileList: UploadFile[]) => { | |||
| const formData = new FormData(); | |||
| formData.append('kb_id', knowledgeId); | |||
| fileList.forEach((file: any) => { | |||
| formData.append('file', file); | |||
| }); | |||
| try { | |||
| return dispatch<any>({ | |||
| type: 'kFModel/upload_document', | |||
| payload: { | |||
| fileList, | |||
| kb_id: knowledgeId, | |||
| }, | |||
| }); | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| const ret = await kbService.document_upload(formData); | |||
| const retcode = get(ret, 'data.retcode'); | |||
| if (retcode === 0) { | |||
| message.success(i18n.t('message.uploaded')); | |||
| } | |||
| if (retcode === 0 || retcode === 500) { | |||
| queryClient.invalidateQueries({ queryKey: ['fetchDocumentList'] }); | |||
| } | |||
| return ret?.data; | |||
| } catch (error) { | |||
| console.warn(error); | |||
| return {}; | |||
| } | |||
| }, | |||
| [dispatch, knowledgeId], | |||
| ); | |||
| }); | |||
| return uploadDocument; | |||
| return { uploadDocument: mutateAsync, loading, data }; | |||
| }; | |||
| export const useWebCrawl = () => { | |||
| const dispatch = useDispatch(); | |||
| export const useNextWebCrawl = () => { | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| return useCallback( | |||
| (name: string, url: string) => { | |||
| try { | |||
| return dispatch<any>({ | |||
| type: 'kFModel/web_crawl', | |||
| payload: { | |||
| name, | |||
| url, | |||
| kb_id: knowledgeId, | |||
| }, | |||
| }); | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| const { | |||
| data, | |||
| isPending: loading, | |||
| mutateAsync, | |||
| } = useMutation({ | |||
| mutationKey: ['webCrawl'], | |||
| mutationFn: async ({ name, url }: { name: string; url: string }) => { | |||
| const formData = new FormData(); | |||
| formData.append('name', name); | |||
| formData.append('url', url); | |||
| formData.append('kb_id', knowledgeId); | |||
| const ret = await kbService.web_crawl(formData); | |||
| const retcode = get(ret, 'data.retcode'); | |||
| if (retcode === 0) { | |||
| message.success(i18n.t('message.uploaded')); | |||
| } | |||
| return retcode; | |||
| }, | |||
| [dispatch], | |||
| ); | |||
| }); | |||
| return { | |||
| data, | |||
| loading, | |||
| webCrawl: mutateAsync, | |||
| }; | |||
| }; | |||
| export const useRunDocument = () => { | |||
| const dispatch = useDispatch(); | |||
| export const useRunNextDocument = () => { | |||
| const queryClient = useQueryClient(); | |||
| const runDocumentByIds = useCallback( | |||
| (payload: any) => { | |||
| try { | |||
| return dispatch<any>({ | |||
| type: 'kFModel/document_run', | |||
| payload, | |||
| }); | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| const { | |||
| data, | |||
| isPending: loading, | |||
| mutateAsync, | |||
| } = useMutation({ | |||
| mutationKey: ['runDocumentByIds'], | |||
| mutationFn: async ({ | |||
| documentIds, | |||
| run, | |||
| }: { | |||
| documentIds: string[]; | |||
| run: number; | |||
| }) => { | |||
| const ret = await kbService.document_run({ | |||
| doc_ids: documentIds, | |||
| run, | |||
| }); | |||
| const retcode = get(ret, 'data.retcode'); | |||
| if (retcode === 0) { | |||
| queryClient.invalidateQueries({ queryKey: ['fetchDocumentList'] }); | |||
| message.success(i18n.t('message.operated')); | |||
| } | |||
| }, | |||
| [dispatch], | |||
| ); | |||
| return runDocumentByIds; | |||
| }; | |||
| return retcode; | |||
| }, | |||
| }); | |||
| export const useSelectRunDocumentLoading = () => { | |||
| const loading = useOneNamespaceEffectsLoading('kFModel', ['document_run']); | |||
| return loading; | |||
| return { runDocumentByIds: mutateAsync, loading, data }; | |||
| }; | |||
| export const useFetchDocumentInfosByIds = () => { | |||
| @@ -296,19 +368,20 @@ export const useFetchDocumentThumbnailsByIds = () => { | |||
| }; | |||
| export const useRemoveNextDocument = () => { | |||
| // const queryClient = useQueryClient(); | |||
| const queryClient = useQueryClient(); | |||
| const { | |||
| data, | |||
| isPending: loading, | |||
| mutateAsync, | |||
| } = useMutation({ | |||
| mutationKey: ['removeDocument'], | |||
| mutationFn: async (documentId: string) => { | |||
| const data = await kbService.document_rm({ doc_id: documentId }); | |||
| // if (data.retcode === 0) { | |||
| // queryClient.invalidateQueries({ queryKey: ['fetchFlowList'] }); | |||
| // } | |||
| return data; | |||
| mutationFn: async (documentIds: string | string[]) => { | |||
| const { data } = await kbService.document_rm({ doc_id: documentIds }); | |||
| if (data.retcode === 0) { | |||
| message.success(i18n.t('message.deleted')); | |||
| queryClient.invalidateQueries({ queryKey: ['fetchDocumentList'] }); | |||
| } | |||
| return data.retcode; | |||
| }, | |||
| }); | |||
| @@ -61,8 +61,7 @@ export const useFetchFileList = (): ResponseType<any> & IListResult => { | |||
| ], | |||
| initialData: {}, | |||
| gcTime: 0, | |||
| queryFn: async (params: any) => { | |||
| console.info(params); | |||
| queryFn: async () => { | |||
| const { data } = await fileManagerService.listFile({ | |||
| parent_id: id, | |||
| keywords: searchString, | |||
| @@ -1,4 +1,3 @@ | |||
| import { useShowDeleteConfirm } from '@/hooks/common-hooks'; | |||
| import { ResponsePostType } from '@/interfaces/database/base'; | |||
| import { IKnowledge, ITestingResult } from '@/interfaces/database/knowledge'; | |||
| import i18n from '@/locales/config'; | |||
| @@ -11,8 +10,7 @@ import { | |||
| useQueryClient, | |||
| } from '@tanstack/react-query'; | |||
| import { message } from 'antd'; | |||
| import { useCallback, useEffect } from 'react'; | |||
| import { useDispatch, useSearchParams, useSelector } from 'umi'; | |||
| import { useSearchParams } from 'umi'; | |||
| import { useSetPaginationParams } from './route-hook'; | |||
| export const useKnowledgeBaseId = (): string => { | |||
| @@ -22,32 +20,6 @@ export const useKnowledgeBaseId = (): string => { | |||
| return knowledgeBaseId || ''; | |||
| }; | |||
| export const useDeleteDocumentById = (): { | |||
| removeDocument: (documentId: string) => Promise<number>; | |||
| } => { | |||
| const dispatch = useDispatch(); | |||
| const knowledgeBaseId = useKnowledgeBaseId(); | |||
| const showDeleteConfirm = useShowDeleteConfirm(); | |||
| const removeDocument = (documentId: string) => () => { | |||
| return dispatch({ | |||
| type: 'kFModel/document_rm', | |||
| payload: { | |||
| doc_id: documentId, | |||
| kb_id: knowledgeBaseId, | |||
| }, | |||
| }); | |||
| }; | |||
| const onRmDocument = (documentId: string): Promise<number> => { | |||
| return showDeleteConfirm({ onOk: removeDocument(documentId) }); | |||
| }; | |||
| return { | |||
| removeDocument: onRmDocument, | |||
| }; | |||
| }; | |||
| export const useFetchKnowledgeBaseConfiguration = () => { | |||
| const knowledgeBaseId = useKnowledgeBaseId(); | |||
| @@ -132,37 +104,6 @@ export const useDeleteKnowledge = () => { | |||
| return { data, loading, deleteKnowledge: mutateAsync }; | |||
| }; | |||
| export const useSelectFileThumbnails = () => { | |||
| const fileThumbnails: Record<string, string> = useSelector( | |||
| (state: any) => state.kFModel.fileThumbnails, | |||
| ); | |||
| return fileThumbnails; | |||
| }; | |||
| export const useFetchFileThumbnails = (docIds?: Array<string>) => { | |||
| const dispatch = useDispatch(); | |||
| const fileThumbnails = useSelectFileThumbnails(); | |||
| const fetchFileThumbnails = useCallback( | |||
| (docIds: Array<string>) => { | |||
| dispatch({ | |||
| type: 'kFModel/fetch_document_thumbnails', | |||
| payload: { doc_ids: docIds.join(',') }, | |||
| }); | |||
| }, | |||
| [dispatch], | |||
| ); | |||
| useEffect(() => { | |||
| if (docIds) { | |||
| fetchFileThumbnails(docIds); | |||
| } | |||
| }, [docIds, fetchFileThumbnails]); | |||
| return { fileThumbnails, fetchFileThumbnails }; | |||
| }; | |||
| //#region knowledge configuration | |||
| export const useUpdateKnowledge = () => { | |||
| @@ -1,11 +1,9 @@ | |||
| import { Authorization } from '@/constants/authorization'; | |||
| import { MessageType } from '@/constants/chat'; | |||
| import { LanguageTranslationMap } from '@/constants/common'; | |||
| import { Pagination } from '@/interfaces/common'; | |||
| import { ResponseType } from '@/interfaces/database/base'; | |||
| import { IAnswer, Message } from '@/interfaces/database/chat'; | |||
| import { IKnowledgeFile } from '@/interfaces/database/knowledge'; | |||
| import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; | |||
| import { IClientConversation, IMessage } from '@/pages/chat/interface'; | |||
| import api from '@/utils/api'; | |||
| import { getAuthorization } from '@/utils/authorization-util'; | |||
| @@ -23,45 +21,11 @@ import { | |||
| useState, | |||
| } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { useDispatch } from 'umi'; | |||
| import { v4 as uuid } from 'uuid'; | |||
| import { useSetModalState, useTranslate } from './common-hooks'; | |||
| import { useSetDocumentParser } from './document-hooks'; | |||
| import { useTranslate } from './common-hooks'; | |||
| import { useSetPaginationParams } from './route-hook'; | |||
| import { useOneNamespaceEffectsLoading } from './store-hooks'; | |||
| import { useFetchTenantInfo, useSaveSetting } from './user-setting-hooks'; | |||
| export const useChangeDocumentParser = (documentId: string) => { | |||
| const setDocumentParser = useSetDocumentParser(); | |||
| const { | |||
| visible: changeParserVisible, | |||
| hideModal: hideChangeParserModal, | |||
| showModal: showChangeParserModal, | |||
| } = useSetModalState(); | |||
| const loading = useOneNamespaceEffectsLoading('kFModel', [ | |||
| 'document_change_parser', | |||
| ]); | |||
| const onChangeParserOk = useCallback( | |||
| async (parserId: string, parserConfig: IChangeParserConfigRequestBody) => { | |||
| const ret = await setDocumentParser(parserId, documentId, parserConfig); | |||
| if (ret === 0) { | |||
| hideChangeParserModal(); | |||
| } | |||
| }, | |||
| [hideChangeParserModal, setDocumentParser, documentId], | |||
| ); | |||
| return { | |||
| changeParserLoading: loading, | |||
| onChangeParserOk, | |||
| changeParserVisible, | |||
| hideChangeParserModal, | |||
| showChangeParserModal, | |||
| }; | |||
| }; | |||
| export const useSetSelectedRecord = <T = IKnowledgeFile>() => { | |||
| const [currentRecord, setCurrentRecord] = useState<T>({} as T); | |||
| @@ -170,28 +134,6 @@ export const useGetPagination = () => { | |||
| }; | |||
| }; | |||
| export const useSetPagination = (namespace: string) => { | |||
| const dispatch = useDispatch(); | |||
| const setPagination = useCallback( | |||
| (pageNumber = 1, pageSize?: number) => { | |||
| const pagination: Pagination = { | |||
| current: pageNumber, | |||
| } as Pagination; | |||
| if (pageSize) { | |||
| pagination.pageSize = pageSize; | |||
| } | |||
| dispatch({ | |||
| type: `${namespace}/setPagination`, | |||
| payload: pagination, | |||
| }); | |||
| }, | |||
| [dispatch, namespace], | |||
| ); | |||
| return setPagination; | |||
| }; | |||
| export interface AppConf { | |||
| appName: string; | |||
| } | |||
| @@ -1,11 +0,0 @@ | |||
| import { getOneNamespaceEffectsLoading } from '@/utils/store-util'; | |||
| import { useSelector } from 'umi'; | |||
| // Get the loading status of given effects under a certain namespace | |||
| export const useOneNamespaceEffectsLoading = ( | |||
| namespace: string, | |||
| effectNames: Array<string>, | |||
| ) => { | |||
| const effects = useSelector((state: any) => state.loading.effects); | |||
| return getOneNamespaceEffectsLoading(namespace, effects, effectNames); | |||
| }; | |||
| @@ -1,3 +1,5 @@ | |||
| import { RunningStatus } from '@/constants/knowledge'; | |||
| export interface IDocumentInfo { | |||
| chunk_num: number; | |||
| create_date: string; | |||
| @@ -7,13 +9,13 @@ export interface IDocumentInfo { | |||
| kb_id: string; | |||
| location: string; | |||
| name: string; | |||
| parser_config: Parserconfig; | |||
| parser_config: IParserConfig; | |||
| parser_id: string; | |||
| process_begin_at: null; | |||
| process_duation: number; | |||
| progress: number; | |||
| progress_msg: string; | |||
| run: string; | |||
| run: RunningStatus; | |||
| size: number; | |||
| source_type: string; | |||
| status: string; | |||
| @@ -24,9 +26,11 @@ export interface IDocumentInfo { | |||
| update_time: number; | |||
| } | |||
| interface Parserconfig { | |||
| chunk_token_num: number; | |||
| export interface IParserConfig { | |||
| delimiter: string; | |||
| html4excel: boolean; | |||
| layout_recognize: boolean; | |||
| pages: any[]; | |||
| raptor: Raptor; | |||
| } | |||
| @@ -5,11 +5,10 @@ import { ReactComponent as EnableIcon } from '@/assets/svg/enable.svg'; | |||
| import { ReactComponent as RunIcon } from '@/assets/svg/run.svg'; | |||
| import { useShowDeleteConfirm, useTranslate } from '@/hooks/common-hooks'; | |||
| import { | |||
| useRemoveDocument, | |||
| useRunDocument, | |||
| useSetDocumentStatus, | |||
| useRemoveNextDocument, | |||
| useRunNextDocument, | |||
| useSetNextDocumentStatus, | |||
| } from '@/hooks/document-hooks'; | |||
| import { useGetKnowledgeSearchParams } from '@/hooks/route-hook'; | |||
| import { | |||
| DownOutlined, | |||
| FileOutlined, | |||
| @@ -19,11 +18,7 @@ import { | |||
| } from '@ant-design/icons'; | |||
| import { Button, Dropdown, Flex, Input, MenuProps, Space } from 'antd'; | |||
| import { useCallback, useMemo } from 'react'; | |||
| import { | |||
| useFetchDocumentListOnMount, | |||
| useGetPagination, | |||
| useHandleSearchChange, | |||
| } from './hooks'; | |||
| import styles from './index.less'; | |||
| interface IProps { | |||
| @@ -31,23 +26,22 @@ interface IProps { | |||
| showCreateModal(): void; | |||
| showWebCrawlModal(): void; | |||
| showDocumentUploadModal(): void; | |||
| searchString: string; | |||
| handleInputChange: React.ChangeEventHandler<HTMLInputElement>; | |||
| } | |||
| const DocumentToolbar = ({ | |||
| searchString, | |||
| selectedRowKeys, | |||
| showCreateModal, | |||
| showWebCrawlModal, | |||
| showDocumentUploadModal, | |||
| handleInputChange, | |||
| }: IProps) => { | |||
| const { t } = useTranslate('knowledgeDetails'); | |||
| const { fetchDocumentList } = useFetchDocumentListOnMount(); | |||
| const { setPagination, searchString } = useGetPagination(fetchDocumentList); | |||
| const { handleInputChange } = useHandleSearchChange(setPagination); | |||
| const removeDocument = useRemoveDocument(); | |||
| const { removeDocument } = useRemoveNextDocument(); | |||
| const showDeleteConfirm = useShowDeleteConfirm(); | |||
| const runDocumentByIds = useRunDocument(); | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const changeStatus = useSetDocumentStatus(); | |||
| const { runDocumentByIds } = useRunNextDocument(); | |||
| const { setDocumentStatus } = useSetNextDocumentStatus(); | |||
| const actionItems: MenuProps['items'] = useMemo(() => { | |||
| return [ | |||
| @@ -66,19 +60,6 @@ const DocumentToolbar = ({ | |||
| ), | |||
| }, | |||
| { type: 'divider' }, | |||
| // { | |||
| // key: '2', | |||
| // onClick: showWebCrawlModal, | |||
| // label: ( | |||
| // <div> | |||
| // <Button type="link"> | |||
| // <FileTextOutlined /> | |||
| // {t('webCrawl')} | |||
| // </Button> | |||
| // </div> | |||
| // ), | |||
| // }, | |||
| { type: 'divider' }, | |||
| { | |||
| key: '3', | |||
| onClick: showCreateModal, | |||
| @@ -105,12 +86,11 @@ const DocumentToolbar = ({ | |||
| const runDocument = useCallback( | |||
| (run: number) => { | |||
| runDocumentByIds({ | |||
| doc_ids: selectedRowKeys, | |||
| documentIds: selectedRowKeys, | |||
| run, | |||
| knowledgeBaseId: knowledgeId, | |||
| }); | |||
| }, | |||
| [runDocumentByIds, selectedRowKeys, knowledgeId], | |||
| [runDocumentByIds, selectedRowKeys], | |||
| ); | |||
| const handleRunClick = useCallback(() => { | |||
| @@ -124,10 +104,10 @@ const DocumentToolbar = ({ | |||
| const onChangeStatus = useCallback( | |||
| (enabled: boolean) => { | |||
| selectedRowKeys.forEach((id) => { | |||
| changeStatus(enabled, id); | |||
| setDocumentStatus({ status: enabled, documentId: id }); | |||
| }); | |||
| }, | |||
| [selectedRowKeys, changeStatus], | |||
| [selectedRowKeys, setDocumentStatus], | |||
| ); | |||
| const handleEnableClick = useCallback(() => { | |||
| @@ -1,104 +1,20 @@ | |||
| import { useSetModalState, useTranslate } from '@/hooks/common-hooks'; | |||
| import { useSetModalState } from '@/hooks/common-hooks'; | |||
| import { | |||
| useCreateDocument, | |||
| useFetchDocumentList, | |||
| useRunDocument, | |||
| useSaveDocumentName, | |||
| useSelectRunDocumentLoading, | |||
| useSetDocumentParser, | |||
| useUploadDocument, | |||
| useWebCrawl, | |||
| useCreateNextDocument, | |||
| useNextWebCrawl, | |||
| useRunNextDocument, | |||
| useSaveNextDocumentName, | |||
| useSetNextDocumentParser, | |||
| useUploadNextDocument, | |||
| } from '@/hooks/document-hooks'; | |||
| import { useGetKnowledgeSearchParams } from '@/hooks/route-hook'; | |||
| import { useOneNamespaceEffectsLoading } from '@/hooks/store-hooks'; | |||
| import { Pagination } from '@/interfaces/common'; | |||
| import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; | |||
| import { getUnSupportedFilesCount } from '@/utils/document-util'; | |||
| import { PaginationProps, UploadFile } from 'antd'; | |||
| import { useCallback, useEffect, useMemo, useState } from 'react'; | |||
| import { useDispatch, useNavigate, useSelector } from 'umi'; | |||
| import { UploadFile } from 'antd'; | |||
| import { useCallback, useState } from 'react'; | |||
| import { useNavigate } from 'umi'; | |||
| import { KnowledgeRouteKey } from './constant'; | |||
| export const useFetchDocumentListOnMount = () => { | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const fetchDocumentList = useFetchDocumentList(); | |||
| const dispatch = useDispatch(); | |||
| useEffect(() => { | |||
| if (knowledgeId) { | |||
| fetchDocumentList(); | |||
| dispatch({ | |||
| type: 'kFModel/pollGetDocumentList-start', | |||
| payload: knowledgeId, | |||
| }); | |||
| } | |||
| return () => { | |||
| dispatch({ | |||
| type: 'kFModel/pollGetDocumentList-stop', | |||
| }); | |||
| }; | |||
| }, [knowledgeId, dispatch, fetchDocumentList]); | |||
| return { fetchDocumentList }; | |||
| }; | |||
| export const useGetPagination = (fetchDocumentList: () => void) => { | |||
| const dispatch = useDispatch(); | |||
| const kFModel = useSelector((state: any) => state.kFModel); | |||
| const { t } = useTranslate('common'); | |||
| const setPagination = useCallback( | |||
| (pageNumber = 1, pageSize?: number) => { | |||
| const pagination: Pagination = { | |||
| current: pageNumber, | |||
| } as Pagination; | |||
| if (pageSize) { | |||
| pagination.pageSize = pageSize; | |||
| } | |||
| dispatch({ | |||
| type: 'kFModel/setPagination', | |||
| payload: pagination, | |||
| }); | |||
| }, | |||
| [dispatch], | |||
| ); | |||
| const onPageChange: PaginationProps['onChange'] = useCallback( | |||
| (pageNumber: number, pageSize: number) => { | |||
| setPagination(pageNumber, pageSize); | |||
| fetchDocumentList(); | |||
| }, | |||
| [fetchDocumentList, setPagination], | |||
| ); | |||
| const pagination: PaginationProps = useMemo(() => { | |||
| return { | |||
| showQuickJumper: true, | |||
| total: kFModel.total, | |||
| showSizeChanger: true, | |||
| current: kFModel.pagination.current, | |||
| pageSize: kFModel.pagination.pageSize, | |||
| pageSizeOptions: [1, 2, 10, 20, 50, 100], | |||
| onChange: onPageChange, | |||
| showTotal: (total) => `${t('total')} ${total}`, | |||
| }; | |||
| }, [kFModel, onPageChange, t]); | |||
| return { | |||
| pagination, | |||
| setPagination, | |||
| total: kFModel.total, | |||
| searchString: kFModel.searchString, | |||
| }; | |||
| }; | |||
| export const useSelectDocumentListLoading = () => { | |||
| return useOneNamespaceEffectsLoading('kFModel', [ | |||
| 'getKfList', | |||
| 'updateDocumentStatus', | |||
| ]); | |||
| }; | |||
| export const useNavigateToOtherPage = () => { | |||
| const navigate = useNavigate(); | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| @@ -119,43 +35,18 @@ export const useNavigateToOtherPage = () => { | |||
| return { linkToUploadPage, toChunk }; | |||
| }; | |||
| export const useHandleSearchChange = (setPagination: () => void) => { | |||
| const dispatch = useDispatch(); | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const throttledGetDocumentList = useCallback(() => { | |||
| dispatch({ | |||
| type: 'kFModel/throttledGetDocumentList', | |||
| payload: knowledgeId, | |||
| }); | |||
| }, [dispatch, knowledgeId]); | |||
| const handleInputChange = useCallback( | |||
| (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { | |||
| const value = e.target.value; | |||
| dispatch({ type: 'kFModel/setSearchString', payload: value }); | |||
| setPagination(); | |||
| throttledGetDocumentList(); | |||
| }, | |||
| [setPagination, throttledGetDocumentList, dispatch], | |||
| ); | |||
| return { handleInputChange }; | |||
| }; | |||
| export const useRenameDocument = (documentId: string) => { | |||
| const saveName = useSaveDocumentName(); | |||
| const { saveName, loading } = useSaveNextDocumentName(); | |||
| const { | |||
| visible: renameVisible, | |||
| hideModal: hideRenameModal, | |||
| showModal: showRenameModal, | |||
| } = useSetModalState(); | |||
| const loading = useOneNamespaceEffectsLoading('kFModel', ['document_rename']); | |||
| const onRenameOk = useCallback( | |||
| async (name: string) => { | |||
| const ret = await saveName(documentId, name); | |||
| const ret = await saveName({ documentId, name }); | |||
| if (ret === 0) { | |||
| hideRenameModal(); | |||
| } | |||
| @@ -173,14 +64,13 @@ export const useRenameDocument = (documentId: string) => { | |||
| }; | |||
| export const useCreateEmptyDocument = () => { | |||
| const createDocument = useCreateDocument(); | |||
| const { createDocument, loading } = useCreateNextDocument(); | |||
| const { | |||
| visible: createVisible, | |||
| hideModal: hideCreateModal, | |||
| showModal: showCreateModal, | |||
| } = useSetModalState(); | |||
| const loading = useOneNamespaceEffectsLoading('kFModel', ['document_create']); | |||
| const onCreateOk = useCallback( | |||
| async (name: string) => { | |||
| @@ -202,20 +92,21 @@ export const useCreateEmptyDocument = () => { | |||
| }; | |||
| export const useChangeDocumentParser = (documentId: string) => { | |||
| const setDocumentParser = useSetDocumentParser(); | |||
| const { setDocumentParser, loading } = useSetNextDocumentParser(); | |||
| const { | |||
| visible: changeParserVisible, | |||
| hideModal: hideChangeParserModal, | |||
| showModal: showChangeParserModal, | |||
| } = useSetModalState(); | |||
| const loading = useOneNamespaceEffectsLoading('kFModel', [ | |||
| 'document_change_parser', | |||
| ]); | |||
| const onChangeParserOk = useCallback( | |||
| async (parserId: string, parserConfig: IChangeParserConfigRequestBody) => { | |||
| const ret = await setDocumentParser(parserId, documentId, parserConfig); | |||
| const ret = await setDocumentParser({ | |||
| parserId, | |||
| documentId, | |||
| parserConfig, | |||
| }); | |||
| if (ret === 0) { | |||
| hideChangeParserModal(); | |||
| } | |||
| @@ -251,18 +142,21 @@ export const useHandleUploadDocument = () => { | |||
| hideModal: hideDocumentUploadModal, | |||
| showModal: showDocumentUploadModal, | |||
| } = useSetModalState(); | |||
| const uploadDocument = useUploadDocument(); | |||
| const { uploadDocument, loading } = useUploadNextDocument(); | |||
| const onDocumentUploadOk = useCallback( | |||
| async (fileList: UploadFile[]): Promise<number | undefined> => { | |||
| if (fileList.length > 0) { | |||
| const ret: any = await uploadDocument(fileList); | |||
| const count = getUnSupportedFilesCount(ret.retmsg); | |||
| if (typeof ret?.retmsg !== 'string') { | |||
| return; | |||
| } | |||
| const count = getUnSupportedFilesCount(ret?.retmsg); | |||
| /// 500 error code indicates that some file types are not supported | |||
| let retcode = ret.retcode; | |||
| let retcode = ret?.retcode; | |||
| if ( | |||
| ret.retcode === 0 || | |||
| (ret.retcode === 500 && count !== fileList.length) // Some files were not uploaded successfully, but some were uploaded successfully. | |||
| ret?.retcode === 0 || | |||
| (ret?.retcode === 500 && count !== fileList.length) // Some files were not uploaded successfully, but some were uploaded successfully. | |||
| ) { | |||
| retcode = 0; | |||
| hideDocumentUploadModal(); | |||
| @@ -273,8 +167,6 @@ export const useHandleUploadDocument = () => { | |||
| [uploadDocument, hideDocumentUploadModal], | |||
| ); | |||
| const loading = useOneNamespaceEffectsLoading('kFModel', ['upload_document']); | |||
| return { | |||
| documentUploadLoading: loading, | |||
| onDocumentUploadOk, | |||
| @@ -290,11 +182,11 @@ export const useHandleWebCrawl = () => { | |||
| hideModal: hideWebCrawlUploadModal, | |||
| showModal: showWebCrawlUploadModal, | |||
| } = useSetModalState(); | |||
| const webCrawl = useWebCrawl(); | |||
| const { webCrawl, loading } = useNextWebCrawl(); | |||
| const onWebCrawlUploadOk = useCallback( | |||
| async (name: string, url: string) => { | |||
| const ret = await webCrawl(name, url); | |||
| const ret = await webCrawl({ name, url }); | |||
| if (ret === 0) { | |||
| hideWebCrawlUploadModal(); | |||
| return 0; | |||
| @@ -304,8 +196,6 @@ export const useHandleWebCrawl = () => { | |||
| [webCrawl, hideWebCrawlUploadModal], | |||
| ); | |||
| const loading = useOneNamespaceEffectsLoading('kFModel', ['web_crawl']); | |||
| return { | |||
| webCrawlUploadLoading: loading, | |||
| onWebCrawlUploadOk, | |||
| @@ -316,14 +206,12 @@ export const useHandleWebCrawl = () => { | |||
| }; | |||
| export const useHandleRunDocumentByIds = (id: string) => { | |||
| const loading = useSelectRunDocumentLoading(); | |||
| const runDocumentByIds = useRunDocument(); | |||
| const { runDocumentByIds, loading } = useRunNextDocument(); | |||
| const [currentId, setCurrentId] = useState<string>(''); | |||
| const isLoading = loading && currentId !== '' && currentId === id; | |||
| const handleRunDocumentByIds = async ( | |||
| documentId: string, | |||
| knowledgeBaseId: string, | |||
| isRunning: boolean, | |||
| ) => { | |||
| if (isLoading) { | |||
| @@ -332,9 +220,8 @@ export const useHandleRunDocumentByIds = (id: string) => { | |||
| setCurrentId(documentId); | |||
| try { | |||
| await runDocumentByIds({ | |||
| doc_ids: [documentId], | |||
| documentIds: [documentId], | |||
| run: isRunning ? 2 : 1, | |||
| knowledgeBaseId, | |||
| }); | |||
| setCurrentId(''); | |||
| } catch (error) { | |||
| @@ -1,12 +1,11 @@ | |||
| import ChunkMethodModal from '@/components/chunk-method-modal'; | |||
| import SvgIcon from '@/components/svg-icon'; | |||
| import { | |||
| useSelectDocumentList, | |||
| useSetDocumentStatus, | |||
| useFetchNextDocumentList, | |||
| useSetNextDocumentStatus, | |||
| } from '@/hooks/document-hooks'; | |||
| import { useSetSelectedRecord } from '@/hooks/logic-hooks'; | |||
| import { useSelectParserList } from '@/hooks/user-setting-hooks'; | |||
| import { IKnowledgeFile } from '@/interfaces/database/knowledge'; | |||
| import { getExtension } from '@/utils/document-util'; | |||
| import { Divider, Flex, Switch, Table, Typography } from 'antd'; | |||
| import type { ColumnsType } from 'antd/es/table'; | |||
| @@ -16,8 +15,6 @@ import DocumentToolbar from './document-toolbar'; | |||
| import { | |||
| useChangeDocumentParser, | |||
| useCreateEmptyDocument, | |||
| useFetchDocumentListOnMount, | |||
| useGetPagination, | |||
| useGetRowSelection, | |||
| useHandleUploadDocument, | |||
| useHandleWebCrawl, | |||
| @@ -30,19 +27,19 @@ import RenameModal from './rename-modal'; | |||
| import WebCrawlModal from './web-crawl-modal'; | |||
| import FileUploadModal from '@/components/file-upload-modal'; | |||
| import { IDocumentInfo } from '@/interfaces/database/document'; | |||
| import { formatDate } from '@/utils/date'; | |||
| import styles from './index.less'; | |||
| const { Text } = Typography; | |||
| const KnowledgeFile = () => { | |||
| const data = useSelectDocumentList(); | |||
| const { fetchDocumentList } = useFetchDocumentListOnMount(); | |||
| const { searchString, documents, pagination, handleInputChange } = | |||
| useFetchNextDocumentList(); | |||
| const parserList = useSelectParserList(); | |||
| const { pagination } = useGetPagination(fetchDocumentList); | |||
| const onChangeStatus = useSetDocumentStatus(); | |||
| const { setDocumentStatus } = useSetNextDocumentStatus(); | |||
| const { toChunk } = useNavigateToOtherPage(); | |||
| const { currentRecord, setRecord } = useSetSelectedRecord(); | |||
| const { currentRecord, setRecord } = useSetSelectedRecord<IDocumentInfo>(); | |||
| const { | |||
| renameLoading, | |||
| onRenameOk, | |||
| @@ -84,7 +81,7 @@ const KnowledgeFile = () => { | |||
| const rowSelection = useGetRowSelection(); | |||
| const columns: ColumnsType<IKnowledgeFile> = [ | |||
| const columns: ColumnsType<IDocumentInfo> = [ | |||
| { | |||
| title: t('name'), | |||
| dataIndex: 'name', | |||
| @@ -138,7 +135,7 @@ const KnowledgeFile = () => { | |||
| <Switch | |||
| checked={status === '1'} | |||
| onChange={(e) => { | |||
| onChangeStatus(e, id); | |||
| setDocumentStatus({ status: e, documentId: id }); | |||
| }} | |||
| /> | |||
| </> | |||
| @@ -181,12 +178,13 @@ const KnowledgeFile = () => { | |||
| showCreateModal={showCreateModal} | |||
| showWebCrawlModal={showWebCrawlUploadModal} | |||
| showDocumentUploadModal={showDocumentUploadModal} | |||
| searchString={searchString} | |||
| handleInputChange={handleInputChange} | |||
| ></DocumentToolbar> | |||
| <Table | |||
| rowKey="id" | |||
| columns={finalColumns} | |||
| dataSource={data} | |||
| // loading={loading} | |||
| dataSource={documents} | |||
| pagination={pagination} | |||
| rowSelection={rowSelection} | |||
| className={styles.documentTable} | |||
| @@ -1,276 +0,0 @@ | |||
| import { BaseState } from '@/interfaces/common'; | |||
| import { IKnowledgeFile } from '@/interfaces/database/knowledge'; | |||
| import i18n from '@/locales/config'; | |||
| import kbService, { getDocumentFile } from '@/services/knowledge-service'; | |||
| import { message } from 'antd'; | |||
| import omit from 'lodash/omit'; | |||
| import pick from 'lodash/pick'; | |||
| import { DvaModel } from 'umi'; | |||
| export interface KFModelState extends BaseState { | |||
| tenantIfo: any; | |||
| data: IKnowledgeFile[]; | |||
| total: number; | |||
| currentRecord: Nullable<IKnowledgeFile>; | |||
| fileThumbnails: Record<string, string>; | |||
| } | |||
| const model: DvaModel<KFModelState> = { | |||
| namespace: 'kFModel', | |||
| state: { | |||
| tenantIfo: {}, | |||
| data: [], | |||
| total: 0, | |||
| currentRecord: null, | |||
| searchString: '', | |||
| pagination: { | |||
| current: 1, | |||
| pageSize: 10, | |||
| }, | |||
| fileThumbnails: {} as Record<string, string>, | |||
| }, | |||
| reducers: { | |||
| updateState(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| ...payload, | |||
| }; | |||
| }, | |||
| setCurrentRecord(state, { payload }) { | |||
| return { ...state, currentRecord: payload }; | |||
| }, | |||
| setSearchString(state, { payload }) { | |||
| return { ...state, searchString: payload }; | |||
| }, | |||
| setPagination(state, { payload }) { | |||
| return { ...state, pagination: { ...state.pagination, ...payload } }; | |||
| }, | |||
| setFileThumbnails(state, { payload }) { | |||
| return { ...state, fileThumbnails: payload }; | |||
| }, | |||
| }, | |||
| effects: { | |||
| *createKf({ payload = {} }, { call }) { | |||
| const { data } = yield call(kbService.createKb, payload); | |||
| const { retcode } = data; | |||
| if (retcode === 0) { | |||
| message.success(i18n.t('message.created')); | |||
| } | |||
| }, | |||
| *updateKf({ payload = {} }, { call }) { | |||
| const { data } = yield call(kbService.updateKb, payload); | |||
| const { retcode } = data; | |||
| if (retcode === 0) { | |||
| message.success(i18n.t('message.modified')); | |||
| } | |||
| }, | |||
| *getKfDetail({ payload = {} }, { call }) { | |||
| const { data } = yield call(kbService.get_kb_detail, payload); | |||
| }, | |||
| *getKfList({ payload = {} }, { call, put, select }) { | |||
| const state: KFModelState = yield select((state: any) => state.kFModel); | |||
| const requestBody = { | |||
| ...payload, | |||
| page: state.pagination.current, | |||
| page_size: state.pagination.pageSize, | |||
| }; | |||
| if (state.searchString) { | |||
| requestBody['keywords'] = state.searchString; | |||
| } | |||
| const { data } = yield call(kbService.get_document_list, requestBody); | |||
| const { retcode, data: res } = data; | |||
| if (retcode === 0) { | |||
| yield put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| data: res.docs, | |||
| total: res.total, | |||
| }, | |||
| }); | |||
| } | |||
| }, | |||
| throttledGetDocumentList: [ | |||
| function* ({ payload }, { call, put }) { | |||
| yield put({ type: 'getKfList', payload: { kb_id: payload } }); | |||
| }, | |||
| { type: 'throttle', ms: 1000 }, // TODO: Provide type support for this effect | |||
| ], | |||
| pollGetDocumentList: [ | |||
| function* ({ payload }, { call, put }) { | |||
| yield put({ type: 'getKfList', payload: { kb_id: payload } }); | |||
| }, | |||
| { type: 'poll', delay: 15000 }, // TODO: Provide type support for this effect | |||
| ], | |||
| *updateDocumentStatus({ payload = {} }, { call, put }) { | |||
| const { data } = yield call( | |||
| kbService.document_change_status, | |||
| pick(payload, ['doc_id', 'status']), | |||
| ); | |||
| const { retcode } = data; | |||
| if (retcode === 0) { | |||
| message.success(i18n.t('message.modified')); | |||
| yield put({ | |||
| type: 'getKfList', | |||
| payload: { kb_id: payload.kb_id }, | |||
| }); | |||
| } | |||
| }, | |||
| *document_rm({ payload = {} }, { call, put }) { | |||
| const { data } = yield call(kbService.document_rm, { | |||
| doc_id: payload.doc_id, | |||
| }); | |||
| const { retcode } = data; | |||
| if (retcode === 0) { | |||
| message.success(i18n.t('message.deleted')); | |||
| yield put({ | |||
| type: 'getKfList', | |||
| payload: { kb_id: payload.kb_id }, | |||
| }); | |||
| } | |||
| return retcode; | |||
| }, | |||
| *document_rename({ payload = {} }, { call, put }) { | |||
| const { data } = yield call( | |||
| kbService.document_rename, | |||
| omit(payload, ['kb_id']), | |||
| ); | |||
| const { retcode } = data; | |||
| if (retcode === 0) { | |||
| message.success(i18n.t('message.renamed')); | |||
| yield put({ | |||
| type: 'getKfList', | |||
| payload: { kb_id: payload.kb_id }, | |||
| }); | |||
| } | |||
| return retcode; | |||
| }, | |||
| *document_create({ payload = {} }, { call, put }) { | |||
| const { data } = yield call(kbService.document_create, payload); | |||
| const { retcode } = data; | |||
| if (retcode === 0) { | |||
| yield put({ | |||
| type: 'getKfList', | |||
| payload: { kb_id: payload.kb_id }, | |||
| }); | |||
| message.success(i18n.t('message.created')); | |||
| } | |||
| return retcode; | |||
| }, | |||
| *document_run({ payload = {} }, { call, put }) { | |||
| const { data } = yield call( | |||
| kbService.document_run, | |||
| omit(payload, ['knowledgeBaseId']), | |||
| ); | |||
| const { retcode } = data; | |||
| if (retcode === 0) { | |||
| if (payload.knowledgeBaseId) { | |||
| yield put({ | |||
| type: 'getKfList', | |||
| payload: { kb_id: payload.knowledgeBaseId }, | |||
| }); | |||
| } | |||
| message.success(i18n.t('message.operated')); | |||
| } | |||
| return retcode; | |||
| }, | |||
| *document_change_parser({ payload = {} }, { call, put }) { | |||
| const { data } = yield call( | |||
| kbService.document_change_parser, | |||
| omit(payload, ['kb_id']), | |||
| ); | |||
| const { retcode } = data; | |||
| if (retcode === 0) { | |||
| yield put({ | |||
| type: 'getKfList', | |||
| payload: { kb_id: payload.kb_id }, | |||
| }); | |||
| message.success(i18n.t('message.modified')); | |||
| } | |||
| return retcode; | |||
| }, | |||
| *fetch_document_thumbnails({ payload = {} }, { call, put }) { | |||
| const { data } = yield call(kbService.document_thumbnails, payload); | |||
| if (data.retcode === 0) { | |||
| yield put({ type: 'setFileThumbnails', payload: data.data }); | |||
| } | |||
| }, | |||
| *fetch_document_file({ payload = {} }, { call }) { | |||
| const documentId = payload; | |||
| try { | |||
| const ret = yield call(getDocumentFile, documentId); | |||
| return ret; | |||
| } catch (error) { | |||
| console.warn(error); | |||
| } | |||
| }, | |||
| *upload_document({ payload = {} }, { call, put }) { | |||
| const fileList = payload.fileList; | |||
| const formData = new FormData(); | |||
| formData.append('kb_id', payload.kb_id); | |||
| fileList.forEach((file: any) => { | |||
| formData.append('file', file); | |||
| }); | |||
| const ret = yield call(kbService.document_upload, formData); | |||
| const succeed = ret?.data?.retcode === 0; | |||
| if (succeed) { | |||
| message.success(i18n.t('message.uploaded')); | |||
| } | |||
| if (succeed || ret?.data?.retcode === 500) { | |||
| yield put({ | |||
| type: 'getKfList', | |||
| payload: { kb_id: payload.kb_id }, | |||
| }); | |||
| } | |||
| return ret?.data; | |||
| }, | |||
| *web_crawl({ payload = {} }, { call, put }) { | |||
| const formData = new FormData(); | |||
| formData.append('name', payload.name); | |||
| formData.append('url', payload.url); | |||
| formData.append('kb_id', payload.kb_id); | |||
| const { data } = yield call(kbService.web_crawl, formData); | |||
| const succeed = data.retcode === 0; | |||
| if (succeed) { | |||
| message.success(i18n.t('message.uploaded')); | |||
| } | |||
| if (succeed || data.retcode === 500) { | |||
| yield put({ | |||
| type: 'getKfList', | |||
| payload: { kb_id: payload.kb_id }, | |||
| }); | |||
| } | |||
| return data.retcode; | |||
| }, | |||
| }, | |||
| subscriptions: { | |||
| setup({ dispatch, history }) { | |||
| history.listen(({ location }) => { | |||
| const state: { from: string } = (location.state ?? { | |||
| from: '', | |||
| }) as { from: string }; | |||
| if ( | |||
| state.from === '/knowledge' || // TODO: Just directly determine whether the current page is on the knowledge list page. | |||
| location.pathname === '/knowledge/dataset/upload' | |||
| ) { | |||
| dispatch({ | |||
| type: 'setPagination', | |||
| payload: { current: 1, pageSize: 10 }, | |||
| }); | |||
| } | |||
| }); | |||
| }, | |||
| }, | |||
| }; | |||
| export default model; | |||
| @@ -1,6 +1,6 @@ | |||
| import { useShowDeleteConfirm, useTranslate } from '@/hooks/common-hooks'; | |||
| import { useRemoveDocument } from '@/hooks/document-hooks'; | |||
| import { IKnowledgeFile } from '@/interfaces/database/knowledge'; | |||
| import { useRemoveNextDocument } from '@/hooks/document-hooks'; | |||
| import { IDocumentInfo } from '@/interfaces/database/document'; | |||
| import { api_host } from '@/utils/api'; | |||
| import { downloadFile } from '@/utils/file-util'; | |||
| import { | |||
| @@ -15,8 +15,8 @@ import { isParserRunning } from '../utils'; | |||
| import styles from './index.less'; | |||
| interface IProps { | |||
| record: IKnowledgeFile; | |||
| setCurrentRecord: (record: IKnowledgeFile) => void; | |||
| record: IDocumentInfo; | |||
| setCurrentRecord: (record: IDocumentInfo) => void; | |||
| showRenameModal: () => void; | |||
| showChangeParserModal: () => void; | |||
| } | |||
| @@ -30,7 +30,7 @@ const ParsingActionCell = ({ | |||
| const documentId = record.id; | |||
| const isRunning = isParserRunning(record.run); | |||
| const { t } = useTranslate('knowledgeDetails'); | |||
| const removeDocument = useRemoveDocument(); | |||
| const { removeDocument } = useRemoveNextDocument(); | |||
| const showDeleteConfirm = useShowDeleteConfirm(); | |||
| const onRmDocument = () => { | |||
| @@ -2,7 +2,7 @@ import { ReactComponent as CancelIcon } from '@/assets/svg/cancel.svg'; | |||
| import { ReactComponent as RefreshIcon } from '@/assets/svg/refresh.svg'; | |||
| import { ReactComponent as RunIcon } from '@/assets/svg/run.svg'; | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { IKnowledgeFile } from '@/interfaces/database/knowledge'; | |||
| import { IDocumentInfo } from '@/interfaces/database/document'; | |||
| import { Badge, DescriptionsProps, Flex, Popover, Space, Tag } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| @@ -21,7 +21,7 @@ const iconMap = { | |||
| }; | |||
| interface IProps { | |||
| record: IKnowledgeFile; | |||
| record: IDocumentInfo; | |||
| } | |||
| const PopoverContent = ({ record }: IProps) => { | |||
| @@ -93,7 +93,7 @@ export const ParsingStatusCell = ({ record }: IProps) => { | |||
| const label = t(`knowledgeDetails.runningStatus${text}`); | |||
| const handleOperationIconClick = () => { | |||
| handleRunDocumentByIds(record.id, record.kb_id, isRunning); | |||
| handleRunDocumentByIds(record.id, isRunning); | |||
| }; | |||
| return ( | |||
| @@ -6,19 +6,17 @@ import { | |||
| } from '@/hooks/route-hook'; | |||
| import { Breadcrumb } from 'antd'; | |||
| import { ItemType } from 'antd/es/breadcrumb/Breadcrumb'; | |||
| import { useEffect, useMemo } from 'react'; | |||
| import { useMemo } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { Link, Outlet, useDispatch, useLocation } from 'umi'; | |||
| import { Link, Outlet } from 'umi'; | |||
| import Siderbar from './components/knowledge-sidebar'; | |||
| import { KnowledgeDatasetRouteKey, KnowledgeRouteKey } from './constant'; | |||
| import styles from './index.less'; | |||
| const KnowledgeAdding = () => { | |||
| const dispatch = useDispatch(); | |||
| const knowledgeBaseId = useKnowledgeBaseId(); | |||
| const { t } = useTranslation(); | |||
| const location = useLocation(); | |||
| const activeKey: KnowledgeRouteKey = | |||
| (useSecondPathName() as KnowledgeRouteKey) || KnowledgeRouteKey.Dataset; | |||
| @@ -58,23 +56,6 @@ const KnowledgeAdding = () => { | |||
| return items; | |||
| }, [activeKey, datasetActiveKey, gotoList, knowledgeBaseId, t]); | |||
| useEffect(() => { | |||
| const search: string = location.search.slice(1); | |||
| const map = search.split('&').reduce<Record<string, string>>((obj, cur) => { | |||
| const [key, value] = cur.split('='); | |||
| obj[key] = value; | |||
| return obj; | |||
| }, {}); | |||
| dispatch({ | |||
| type: 'kAModel/updateState', | |||
| payload: { | |||
| doc_id: undefined, | |||
| ...map, | |||
| }, | |||
| }); | |||
| }, [location, dispatch]); | |||
| return ( | |||
| <> | |||
| <div className={styles.container}> | |||
| @@ -1,19 +1,19 @@ | |||
| import Image from '@/components/image'; | |||
| import SvgIcon from '@/components/svg-icon'; | |||
| import { useSelectFileThumbnails } from '@/hooks/knowledge-hooks'; | |||
| import { IReference } from '@/interfaces/database/chat'; | |||
| import { IChunk } from '@/interfaces/database/knowledge'; | |||
| import { getExtension } from '@/utils/document-util'; | |||
| import { InfoCircleOutlined } from '@ant-design/icons'; | |||
| import { Button, Flex, Popover, Space } from 'antd'; | |||
| import DOMPurify from 'dompurify'; | |||
| import { useCallback, useMemo } from 'react'; | |||
| import { useCallback, useEffect, useMemo } from 'react'; | |||
| import Markdown from 'react-markdown'; | |||
| import reactStringReplace from 'react-string-replace'; | |||
| import SyntaxHighlighter from 'react-syntax-highlighter'; | |||
| import remarkGfm from 'remark-gfm'; | |||
| import { visitParents } from 'unist-util-visit-parents'; | |||
| import { useFetchDocumentThumbnailsByIds } from '@/hooks/document-hooks'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import styles from './index.less'; | |||
| @@ -34,6 +34,8 @@ const MarkdownContent = ({ | |||
| clickDocumentButton?: (documentId: string, chunk: IChunk) => void; | |||
| }) => { | |||
| const { t } = useTranslation(); | |||
| const { setDocumentIds, data: fileThumbnails } = | |||
| useFetchDocumentThumbnailsByIds(); | |||
| const contentWithCursor = useMemo(() => { | |||
| let text = content; | |||
| if (text === '') { | |||
| @@ -42,7 +44,9 @@ const MarkdownContent = ({ | |||
| return loading ? text?.concat('~~2$$') : text; | |||
| }, [content, loading, t]); | |||
| const fileThumbnails = useSelectFileThumbnails(); | |||
| useEffect(() => { | |||
| setDocumentIds(reference?.doc_aggs.map((x) => x.doc_id) ?? []); | |||
| }, [reference, setDocumentIds]); | |||
| const handleDocumentButtonClick = useCallback( | |||
| (documentId: string, chunk: IChunk, isPdf: boolean) => () => { | |||
| @@ -1,5 +1,10 @@ | |||
| import { ReactComponent as DeleteIcon } from '@/assets/svg/delete.svg'; | |||
| import SvgIcon from '@/components/svg-icon'; | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { | |||
| IListResult, | |||
| useFetchParentFolderList, | |||
| } from '@/hooks/file-manager-hooks'; | |||
| import { | |||
| DownOutlined, | |||
| FileTextOutlined, | |||
| @@ -24,11 +29,6 @@ import { | |||
| useSelectBreadcrumbItems, | |||
| } from './hooks'; | |||
| import SvgIcon from '@/components/svg-icon'; | |||
| import { | |||
| IListResult, | |||
| useFetchParentFolderList, | |||
| } from '@/hooks/file-manager-hooks'; | |||
| import styles from './index.less'; | |||
| interface IProps | |||
| @@ -1,20 +1,5 @@ | |||
| import { KFModelState } from '@/pages/add-knowledge/components/knowledge-file/model'; | |||
| declare module 'lodash'; | |||
| function useSelector<TState = RootState, TSelected = unknown>( | |||
| selector: (state: TState) => TSelected, | |||
| equalityFn?: (left: TSelected, right: TSelected) => boolean, | |||
| ): TSelected; | |||
| export interface RootState { | |||
| kFModel: KFModelState; | |||
| } | |||
| declare global { | |||
| type Nullable<T> = T | null; | |||
| } | |||
| declare module 'umi' { | |||
| export { useSelector }; | |||
| } | |||