import { useHandleFilterSubmit } from '@/components/list-filter-bar/use-handle-filter-submit'; import message from '@/components/ui/message'; import { ResponsePostType } from '@/interfaces/database/base'; import { IKnowledge, IKnowledgeGraph, IKnowledgeResult, INextTestingResult, IRenameTag, ITestingResult, } from '@/interfaces/database/knowledge'; import { ITestRetrievalRequestBody } from '@/interfaces/request/knowledge'; import i18n from '@/locales/config'; import kbService, { deleteKnowledgeGraph, getKnowledgeGraph, listDataset, listTag, removeTag, renameTag, } from '@/services/knowledge-service'; import { useIsMutating, useMutation, useMutationState, useQuery, useQueryClient, } from '@tanstack/react-query'; import { useDebounce } from 'ahooks'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useParams, useSearchParams } from 'umi'; import { useGetPaginationWithRouter, useHandleSearchChange, } from './logic-hooks'; import { useSetPaginationParams } from './route-hook'; export const enum KnowledgeApiAction { TestRetrieval = 'testRetrieval', FetchKnowledgeListByPage = 'fetchKnowledgeListByPage', CreateKnowledge = 'createKnowledge', DeleteKnowledge = 'deleteKnowledge', SaveKnowledge = 'saveKnowledge', FetchKnowledgeDetail = 'fetchKnowledgeDetail', FetchKnowledgeGraph = 'fetchKnowledgeGraph', FetchMetadata = 'fetchMetadata', FetchKnowledgeList = 'fetchKnowledgeList', RemoveKnowledgeGraph = 'removeKnowledgeGraph', } export const useKnowledgeBaseId = (): string => { const { id } = useParams(); return (id as string) || ''; }; export const useTestRetrieval = () => { const knowledgeBaseId = useKnowledgeBaseId(); const [values, setValues] = useState(); const mountedRef = useRef(false); const { filterValue, handleFilterSubmit } = useHandleFilterSubmit(); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(10); const onPaginationChange = useCallback((page: number, pageSize: number) => { setPage(page); setPageSize(pageSize); }, []); const queryParams = useMemo(() => { return { ...values, kb_id: values?.kb_id || knowledgeBaseId, page, size: pageSize, doc_ids: filterValue.doc_ids, }; }, [filterValue, knowledgeBaseId, page, pageSize, values]); const { data, isFetching: loading, refetch, } = useQuery({ queryKey: [KnowledgeApiAction.TestRetrieval, queryParams, page, pageSize], initialData: { chunks: [], doc_aggs: [], total: 0, isRuned: false, }, enabled: false, gcTime: 0, queryFn: async () => { const { data } = await kbService.retrieval_test(queryParams); const result = data?.data ?? {}; return { ...result, isRuned: true }; }, }); useEffect(() => { if (mountedRef.current) { refetch(); } mountedRef.current = true; }, [page, pageSize, refetch, filterValue]); return { data, loading, setValues, refetch, onPaginationChange, page, pageSize, handleFilterSubmit, filterValue, }; }; export const useFetchNextKnowledgeListByPage = () => { const { searchString, handleInputChange } = useHandleSearchChange(); const { pagination, setPagination } = useGetPaginationWithRouter(); const debouncedSearchString = useDebounce(searchString, { wait: 500 }); const { filterValue, handleFilterSubmit } = useHandleFilterSubmit(); const { data, isFetching: loading } = useQuery({ queryKey: [ KnowledgeApiAction.FetchKnowledgeListByPage, { debouncedSearchString, ...pagination, filterValue, }, ], initialData: { kbs: [], total: 0, }, gcTime: 0, queryFn: async () => { const { data } = await listDataset( { keywords: debouncedSearchString, page_size: pagination.pageSize, page: pagination.current, }, { owner_ids: filterValue.owner, }, ); return data?.data; }, }); const onInputChange: React.ChangeEventHandler = useCallback( (e) => { // setPagination({ page: 1 }); // TODO: This results in repeated requests handleInputChange(e); }, [handleInputChange], ); return { ...data, searchString, handleInputChange: onInputChange, pagination: { ...pagination, total: data?.total }, setPagination, loading, filterValue, handleFilterSubmit, }; }; export const useCreateKnowledge = () => { const queryClient = useQueryClient(); const { data, isPending: loading, mutateAsync, } = useMutation({ mutationKey: [KnowledgeApiAction.CreateKnowledge], mutationFn: async (params: { id?: string; name: string }) => { const { data = {} } = await kbService.createKb(params); if (data.code === 0) { message.success( i18n.t(`message.${params?.id ? 'modified' : 'created'}`), ); queryClient.invalidateQueries({ queryKey: ['fetchKnowledgeList'] }); } return data; }, }); return { data, loading, createKnowledge: mutateAsync }; }; export const useDeleteKnowledge = () => { const queryClient = useQueryClient(); const { data, isPending: loading, mutateAsync, } = useMutation({ mutationKey: [KnowledgeApiAction.DeleteKnowledge], mutationFn: async (id: string) => { const { data } = await kbService.rmKb({ kb_id: id }); if (data.code === 0) { message.success(i18n.t(`message.deleted`)); queryClient.invalidateQueries({ queryKey: [KnowledgeApiAction.FetchKnowledgeListByPage], }); } return data?.data ?? []; }, }); return { data, loading, deleteKnowledge: mutateAsync }; }; export const useUpdateKnowledge = (shouldFetchList = false) => { const knowledgeBaseId = useKnowledgeBaseId(); const queryClient = useQueryClient(); const { data, isPending: loading, mutateAsync, } = useMutation({ mutationKey: [KnowledgeApiAction.SaveKnowledge], mutationFn: async (params: Record) => { const { data = {} } = await kbService.updateKb({ kb_id: params?.kb_id ? params?.kb_id : knowledgeBaseId, ...params, }); if (data.code === 0) { message.success(i18n.t(`message.updated`)); if (shouldFetchList) { queryClient.invalidateQueries({ queryKey: [KnowledgeApiAction.FetchKnowledgeListByPage], }); } else { queryClient.invalidateQueries({ queryKey: ['fetchKnowledgeDetail'] }); } } return data; }, }); return { data, loading, saveKnowledgeConfiguration: mutateAsync }; }; export const useFetchKnowledgeBaseConfiguration = (props?: { isEdit?: boolean; refreshCount?: number; }) => { const { isEdit = true, refreshCount } = props || { isEdit: true }; const { id } = useParams(); const [searchParams] = useSearchParams(); const knowledgeBaseId = searchParams.get('id') || id; let queryKey: (KnowledgeApiAction | number)[] = [ KnowledgeApiAction.FetchKnowledgeDetail, ]; if (typeof refreshCount === 'number') { queryKey = [KnowledgeApiAction.FetchKnowledgeDetail, refreshCount]; } const { data, isFetching: loading } = useQuery({ queryKey, initialData: {} as IKnowledge, gcTime: 0, queryFn: async () => { if (isEdit) { const { data } = await kbService.get_kb_detail({ kb_id: knowledgeBaseId, }); return data?.data ?? {}; } else { return {}; } }, }); return { data, loading }; }; export function useFetchKnowledgeGraph() { const knowledgeBaseId = useKnowledgeBaseId(); const { data, isFetching: loading } = useQuery({ queryKey: [KnowledgeApiAction.FetchKnowledgeGraph, knowledgeBaseId], initialData: { graph: {}, mind_map: {} } as IKnowledgeGraph, enabled: !!knowledgeBaseId, gcTime: 0, queryFn: async () => { const { data } = await getKnowledgeGraph(knowledgeBaseId); return data?.data; }, }); return { data, loading }; } export function useFetchKnowledgeMetadata(kbIds: string[] = []) { const { data, isFetching: loading } = useQuery< Record> >({ queryKey: [KnowledgeApiAction.FetchMetadata, kbIds], initialData: {}, enabled: kbIds.length > 0, gcTime: 0, queryFn: async () => { const { data } = await kbService.getMeta({ kb_ids: kbIds.join(',') }); return data?.data ?? {}; }, }); return { data, loading }; } export const useRemoveKnowledgeGraph = () => { const knowledgeBaseId = useKnowledgeBaseId(); const queryClient = useQueryClient(); const { data, isPending: loading, mutateAsync, } = useMutation({ mutationKey: [KnowledgeApiAction.RemoveKnowledgeGraph], mutationFn: async () => { const { data } = await deleteKnowledgeGraph(knowledgeBaseId); if (data.code === 0) { message.success(i18n.t(`message.deleted`)); queryClient.invalidateQueries({ queryKey: [KnowledgeApiAction.FetchKnowledgeGraph], }); } return data?.code; }, }); return { data, loading, removeKnowledgeGraph: mutateAsync }; }; export const useFetchKnowledgeList = ( shouldFilterListWithoutDocument: boolean = false, ): { list: IKnowledge[]; loading: boolean; } => { const { data, isFetching: loading } = useQuery({ queryKey: [KnowledgeApiAction.FetchKnowledgeList], initialData: [], gcTime: 0, // https://tanstack.com/query/latest/docs/framework/react/guides/caching?from=reactQueryV3 queryFn: async () => { const { data } = await listDataset(); const list = data?.data?.kbs ?? []; return shouldFilterListWithoutDocument ? list.filter((x: IKnowledge) => x.chunk_num > 0) : list; }, }); return { list: data, loading }; }; export const useSelectKnowledgeOptions = () => { const { list } = useFetchKnowledgeList(); const options = list?.map((item) => ({ label: item.name, value: item.id, })); return options; }; //#region tags export const useRenameTag = () => { const knowledgeBaseId = useKnowledgeBaseId(); const queryClient = useQueryClient(); const { data, isPending: loading, mutateAsync, } = useMutation({ mutationKey: ['renameTag'], mutationFn: async (params: IRenameTag) => { const { data } = await renameTag(knowledgeBaseId, params); if (data.code === 0) { message.success(i18n.t(`message.modified`)); queryClient.invalidateQueries({ queryKey: ['fetchTagList'], }); } return data?.data ?? []; }, }); return { data, loading, renameTag: mutateAsync }; }; export const useTagIsRenaming = () => { return useIsMutating({ mutationKey: ['renameTag'] }) > 0; }; export const useFetchTagListByKnowledgeIds = () => { const [knowledgeIds, setKnowledgeIds] = useState([]); const { data, isFetching: loading } = useQuery>({ queryKey: ['fetchTagListByKnowledgeIds'], enabled: knowledgeIds.length > 0, initialData: [], gcTime: 0, // https://tanstack.com/query/latest/docs/framework/react/guides/caching?from=reactQueryV3 queryFn: async () => { const { data } = await kbService.listTagByKnowledgeIds({ kb_ids: knowledgeIds.join(','), }); const list = data?.data || []; return list; }, }); return { list: data, loading, setKnowledgeIds }; }; export const useFetchTagList = () => { const knowledgeBaseId = useKnowledgeBaseId(); const { data, isFetching: loading } = useQuery>({ queryKey: ['fetchTagList'], initialData: [], gcTime: 0, // https://tanstack.com/query/latest/docs/framework/react/guides/caching?from=reactQueryV3 queryFn: async () => { const { data } = await listTag(knowledgeBaseId); const list = data?.data || []; return list; }, }); return { list: data, loading }; }; export const useDeleteTag = () => { const knowledgeBaseId = useKnowledgeBaseId(); const queryClient = useQueryClient(); const { data, isPending: loading, mutateAsync, } = useMutation({ mutationKey: ['deleteTag'], mutationFn: async (tags: string[]) => { const { data } = await removeTag(knowledgeBaseId, tags); if (data.code === 0) { message.success(i18n.t(`message.deleted`)); queryClient.invalidateQueries({ queryKey: ['fetchTagList'], }); } return data?.data ?? []; }, }); return { data, loading, deleteTag: mutateAsync }; }; // #endregion //#region Retrieval testing export const useTestChunkRetrieval = (): ResponsePostType & { testChunk: (...params: any[]) => void; } => { const knowledgeBaseId = useKnowledgeBaseId(); const { page, size: pageSize } = useSetPaginationParams(); const { data, isPending: loading, mutateAsync, } = useMutation({ mutationKey: ['testChunk'], // This method is invalid gcTime: 0, mutationFn: async (values: any) => { const { data } = await kbService.retrieval_test({ ...values, kb_id: values.kb_id ?? knowledgeBaseId, page, size: pageSize, }); if (data.code === 0) { const res = data.data; return { ...res, documents: res.doc_aggs, }; } return ( data?.data ?? { chunks: [], documents: [], total: 0, } ); }, }); return { data: data ?? { chunks: [], documents: [], total: 0 }, loading, testChunk: mutateAsync, }; }; export const useTestChunkAllRetrieval = (): ResponsePostType & { testChunkAll: (...params: any[]) => void; } => { const knowledgeBaseId = useKnowledgeBaseId(); const { page, size: pageSize } = useSetPaginationParams(); const { data, isPending: loading, mutateAsync, } = useMutation({ mutationKey: ['testChunkAll'], // This method is invalid gcTime: 0, mutationFn: async (values: any) => { const { data } = await kbService.retrieval_test({ ...values, kb_id: values.kb_id ?? knowledgeBaseId, doc_ids: [], page, size: pageSize, }); if (data.code === 0) { const res = data.data; return { ...res, documents: res.doc_aggs, }; } return ( data?.data ?? { chunks: [], documents: [], total: 0, } ); }, }); return { data: data ?? { chunks: [], documents: [], total: 0 }, loading, testChunkAll: mutateAsync, }; }; export const useChunkIsTesting = () => { return useIsMutating({ mutationKey: ['testChunk'] }) > 0; }; export const useSelectTestingResult = (): ITestingResult => { const data = useMutationState({ filters: { mutationKey: ['testChunk'] }, select: (mutation) => { return mutation.state.data; }, }); return (data.at(-1) ?? { chunks: [], documents: [], total: 0, }) as ITestingResult; }; export const useSelectIsTestingSuccess = () => { const status = useMutationState({ filters: { mutationKey: ['testChunk'] }, select: (mutation) => { return mutation.state.status; }, }); return status.at(-1) === 'success'; }; export const useAllTestingSuccess = () => { const status = useMutationState({ filters: { mutationKey: ['testChunkAll'] }, select: (mutation) => { return mutation.state.status; }, }); return status.at(-1) === 'success'; }; export const useAllTestingResult = (): ITestingResult => { const data = useMutationState({ filters: { mutationKey: ['testChunkAll'] }, select: (mutation) => { return mutation.state.data; }, }); return (data.at(-1) ?? { chunks: [], documents: [], total: 0, }) as ITestingResult; }; //#endregion