### What problem does this PR solve? https://github.com/infiniflow/ragflow/issues/7753 The internal is due to when the selected row keys change will trigger a testing, but I do not know why. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)tags/v0.19.1
| @@ -254,7 +254,7 @@ class CommonService: | |||
| # Returns: | |||
| # Number of records deleted | |||
| return cls.model.delete().where(cls.model.id == pid).execute() | |||
| @classmethod | |||
| @DB.connection_context() | |||
| def delete_by_ids(cls, pids): | |||
| @@ -2,7 +2,10 @@ import { ReactComponent as SelectedFilesCollapseIcon } from '@/assets/svg/select | |||
| import { Collapse, Flex, Space } from 'antd'; | |||
| import SelectFiles from './select-files'; | |||
| import { useSelectTestingResult } from '@/hooks/knowledge-hooks'; | |||
| import { | |||
| useAllTestingResult, | |||
| useSelectTestingResult, | |||
| } from '@/hooks/knowledge-hooks'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import styles from './index.less'; | |||
| @@ -18,7 +21,12 @@ const RetrievalDocuments = ({ | |||
| setSelectedDocumentIds, | |||
| }: IProps) => { | |||
| const { t } = useTranslation(); | |||
| const { documents: documentsAll } = useAllTestingResult(); | |||
| const { documents } = useSelectTestingResult(); | |||
| const { documents: useDocuments } = { | |||
| documents: | |||
| documentsAll?.length > documents?.length ? documentsAll : documents, | |||
| }; | |||
| return ( | |||
| <Collapse | |||
| @@ -35,7 +43,7 @@ const RetrievalDocuments = ({ | |||
| > | |||
| <Space> | |||
| <span> | |||
| {selectedDocumentIds?.length ?? 0}/{documents?.length ?? 0} | |||
| {selectedDocumentIds?.length ?? 0}/{useDocuments?.length ?? 0} | |||
| </span> | |||
| {t('knowledgeDetails.filesSelected')} | |||
| </Space> | |||
| @@ -1,6 +1,9 @@ | |||
| import NewDocumentLink from '@/components/new-document-link'; | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { useSelectTestingResult } from '@/hooks/knowledge-hooks'; | |||
| import { | |||
| useAllTestingResult, | |||
| useSelectTestingResult, | |||
| } from '@/hooks/knowledge-hooks'; | |||
| import { ITestingDocument } from '@/interfaces/database/knowledge'; | |||
| import { EyeOutlined } from '@ant-design/icons'; | |||
| import { Button, Table, TableProps, Tooltip } from 'antd'; | |||
| @@ -12,6 +15,9 @@ interface IProps { | |||
| const SelectFiles = ({ setSelectedDocumentIds, handleTesting }: IProps) => { | |||
| const { documents } = useSelectTestingResult(); | |||
| const { documents: documentsAll } = useAllTestingResult(); | |||
| const useDocuments = | |||
| documentsAll?.length > documents?.length ? documentsAll : documents; | |||
| const { t } = useTranslate('fileManager'); | |||
| const columns: TableProps<ITestingDocument>['columns'] = [ | |||
| @@ -62,7 +68,7 @@ const SelectFiles = ({ setSelectedDocumentIds, handleTesting }: IProps) => { | |||
| return ( | |||
| <Table | |||
| columns={columns} | |||
| dataSource={documents} | |||
| dataSource={useDocuments} | |||
| showHeader={false} | |||
| rowSelection={rowSelection} | |||
| rowKey={'doc_id'} | |||
| @@ -261,6 +261,51 @@ export const useTestChunkRetrieval = (): ResponsePostType<ITestingResult> & { | |||
| }; | |||
| }; | |||
| export const useTestChunkAllRetrieval = (): ResponsePostType<ITestingResult> & { | |||
| 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; | |||
| }; | |||
| @@ -288,6 +333,30 @@ export const useSelectIsTestingSuccess = () => { | |||
| }); | |||
| 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 | |||
| //#region tags | |||
| @@ -1,13 +1,19 @@ | |||
| import { useTestChunkRetrieval } from '@/hooks/knowledge-hooks'; | |||
| import { | |||
| useTestChunkAllRetrieval, | |||
| useTestChunkRetrieval, | |||
| } from '@/hooks/knowledge-hooks'; | |||
| import { Flex, Form } from 'antd'; | |||
| import TestingControl from './testing-control'; | |||
| import TestingResult from './testing-result'; | |||
| import { useState } from 'react'; | |||
| import styles from './index.less'; | |||
| const KnowledgeTesting = () => { | |||
| const [form] = Form.useForm(); | |||
| const { testChunk } = useTestChunkRetrieval(); | |||
| const { testChunkAll } = useTestChunkAllRetrieval(); | |||
| const [selectedDocumentIds, setSelectedDocumentIds] = useState<string[]>([]); | |||
| const handleTesting = async (documentIds: string[] = []) => { | |||
| const values = await form.validateFields(); | |||
| @@ -16,6 +22,12 @@ const KnowledgeTesting = () => { | |||
| doc_ids: Array.isArray(documentIds) ? documentIds : [], | |||
| vector_similarity_weight: 1 - values.vector_similarity_weight, | |||
| }); | |||
| testChunkAll({ | |||
| ...values, | |||
| doc_ids: [], | |||
| vector_similarity_weight: 1 - values.vector_similarity_weight, | |||
| }); | |||
| }; | |||
| return ( | |||
| @@ -23,8 +35,13 @@ const KnowledgeTesting = () => { | |||
| <TestingControl | |||
| form={form} | |||
| handleTesting={handleTesting} | |||
| selectedDocumentIds={selectedDocumentIds} | |||
| ></TestingControl> | |||
| <TestingResult handleTesting={handleTesting}></TestingResult> | |||
| <TestingResult | |||
| handleTesting={handleTesting} | |||
| selectedDocumentIds={selectedDocumentIds} | |||
| setSelectedDocumentIds={setSelectedDocumentIds} | |||
| ></TestingResult> | |||
| </Flex> | |||
| ); | |||
| }; | |||
| @@ -18,10 +18,15 @@ type FieldType = { | |||
| interface IProps { | |||
| form: FormInstance; | |||
| handleTesting: () => Promise<any>; | |||
| handleTesting: (documentIds?: string[]) => Promise<any>; | |||
| selectedDocumentIds: string[]; | |||
| } | |||
| const TestingControl = ({ form, handleTesting }: IProps) => { | |||
| const TestingControl = ({ | |||
| form, | |||
| handleTesting, | |||
| selectedDocumentIds, | |||
| }: IProps) => { | |||
| const question = Form.useWatch('question', { form, preserve: true }); | |||
| const loading = useChunkIsTesting(); | |||
| const { t } = useTranslate('knowledgeDetails'); | |||
| @@ -29,6 +34,10 @@ const TestingControl = ({ form, handleTesting }: IProps) => { | |||
| const buttonDisabled = | |||
| !question || (typeof question === 'string' && question.trim() === ''); | |||
| const onClick = () => { | |||
| handleTesting(selectedDocumentIds); | |||
| }; | |||
| return ( | |||
| <section className={styles.testingControlWrapper}> | |||
| <div> | |||
| @@ -53,7 +62,7 @@ const TestingControl = ({ form, handleTesting }: IProps) => { | |||
| <Button | |||
| type="primary" | |||
| size="small" | |||
| onClick={handleTesting} | |||
| onClick={onClick} | |||
| disabled={buttonDisabled} | |||
| loading={loading} | |||
| > | |||
| @@ -15,13 +15,15 @@ import camelCase from 'lodash/camelCase'; | |||
| import SelectFiles from './select-files'; | |||
| import { | |||
| useAllTestingResult, | |||
| useAllTestingSuccess, | |||
| useSelectIsTestingSuccess, | |||
| useSelectTestingResult, | |||
| } from '@/hooks/knowledge-hooks'; | |||
| import { useGetPaginationWithRouter } from '@/hooks/logic-hooks'; | |||
| import { api_host } from '@/utils/api'; | |||
| import { showImage } from '@/utils/chat'; | |||
| import { useCallback, useState } from 'react'; | |||
| import { useCallback } from 'react'; | |||
| import styles from './index.less'; | |||
| const similarityList: Array<{ field: keyof ITestingChunk; label: string }> = [ | |||
| @@ -48,14 +50,21 @@ const ChunkTitle = ({ item }: { item: ITestingChunk }) => { | |||
| interface IProps { | |||
| handleTesting: (documentIds?: string[]) => Promise<any>; | |||
| selectedDocumentIds: string[]; | |||
| setSelectedDocumentIds: (ids: string[]) => void; | |||
| } | |||
| const TestingResult = ({ handleTesting }: IProps) => { | |||
| const [selectedDocumentIds, setSelectedDocumentIds] = useState<string[]>([]); | |||
| const TestingResult = ({ | |||
| handleTesting, | |||
| selectedDocumentIds, | |||
| setSelectedDocumentIds, | |||
| }: IProps) => { | |||
| const { documents, chunks, total } = useSelectTestingResult(); | |||
| const { documents: documentsAll, total: totalAll } = useAllTestingResult(); | |||
| const { t } = useTranslate('knowledgeDetails'); | |||
| const { pagination, setPagination } = useGetPaginationWithRouter(); | |||
| const isSuccess = useSelectIsTestingSuccess(); | |||
| const isAllSuccess = useAllTestingSuccess(); | |||
| const onChange: PaginationProps['onChange'] = (pageNumber, pageSize) => { | |||
| pagination.onChange?.(pageNumber, pageSize); | |||
| @@ -88,7 +97,8 @@ const TestingResult = ({ handleTesting }: IProps) => { | |||
| > | |||
| <Space> | |||
| <span> | |||
| {selectedDocumentIds?.length ?? 0}/{documents?.length ?? 0} | |||
| {selectedDocumentIds?.length ?? 0}/ | |||
| {documentsAll?.length ?? 0} | |||
| </span> | |||
| {t('filesSelected')} | |||
| </Space> | |||
| @@ -1,6 +1,6 @@ | |||
| import NewDocumentLink from '@/components/new-document-link'; | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { useSelectTestingResult } from '@/hooks/knowledge-hooks'; | |||
| import { useAllTestingResult } from '@/hooks/knowledge-hooks'; | |||
| import { ITestingDocument } from '@/interfaces/database/knowledge'; | |||
| import { EyeOutlined } from '@ant-design/icons'; | |||
| import { Button, Table, TableProps, Tooltip } from 'antd'; | |||
| @@ -11,7 +11,7 @@ interface IProps { | |||
| } | |||
| const SelectFiles = ({ setSelectedDocumentIds, handleTesting }: IProps) => { | |||
| const { documents } = useSelectTestingResult(); | |||
| const { documents } = useAllTestingResult(); | |||
| const { t } = useTranslate('fileManager'); | |||
| const columns: TableProps<ITestingDocument>['columns'] = [ | |||
| @@ -50,8 +50,8 @@ const SelectFiles = ({ setSelectedDocumentIds, handleTesting }: IProps) => { | |||
| const rowSelection = { | |||
| onChange: (selectedRowKeys: React.Key[]) => { | |||
| handleTesting(selectedRowKeys as string[]); | |||
| setSelectedDocumentIds(selectedRowKeys as string[]); | |||
| handleTesting(selectedRowKeys as string[]); | |||
| }, | |||
| getCheckboxProps: (record: ITestingDocument) => ({ | |||
| disabled: record.doc_name === 'Disabled User', // Column configuration not to be checked | |||
| @@ -1,6 +1,9 @@ | |||
| import { useFetchMindMap, useFetchRelatedQuestions } from '@/hooks/chat-hooks'; | |||
| import { useSetModalState } from '@/hooks/common-hooks'; | |||
| import { useTestChunkRetrieval } from '@/hooks/knowledge-hooks'; | |||
| import { | |||
| useTestChunkAllRetrieval, | |||
| useTestChunkRetrieval, | |||
| } from '@/hooks/knowledge-hooks'; | |||
| import { | |||
| useGetPaginationWithRouter, | |||
| useSendMessageWithSse, | |||
| @@ -21,6 +24,7 @@ export const useSendQuestion = (kbIds: string[]) => { | |||
| api.ask, | |||
| ); | |||
| const { testChunk, loading } = useTestChunkRetrieval(); | |||
| const { testChunkAll, loading: loadingAll } = useTestChunkAllRetrieval(); | |||
| const [sendingLoading, setSendingLoading] = useState(false); | |||
| const [currentAnswer, setCurrentAnswer] = useState({} as IAnswer); | |||
| const { fetchRelatedQuestions, data: relatedQuestions } = | |||
| @@ -88,6 +92,15 @@ export const useSendQuestion = (kbIds: string[]) => { | |||
| page, | |||
| size, | |||
| }); | |||
| testChunkAll({ | |||
| kb_id: kbIds, | |||
| highlight: true, | |||
| question: q, | |||
| doc_ids: [], | |||
| page, | |||
| size, | |||
| }); | |||
| }, | |||
| [sendingLoading, searchStr, kbIds, testChunk, selectedDocumentIds], | |||
| ); | |||