### What problem does this PR solve? feat: Retrieval chunks by page #2247 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.11.0
| import SelectFiles from './select-files'; | import SelectFiles from './select-files'; | ||||
| import { useSelectTestingResult } from '@/hooks/knowledge-hooks'; | import { useSelectTestingResult } from '@/hooks/knowledge-hooks'; | ||||
| import { useState } from 'react'; | |||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| interface IProps { | interface IProps { | ||||
| selectedDocumentIdsLength?: number; | |||||
| onTesting(documentIds: string[]): void; | onTesting(documentIds: string[]): void; | ||||
| setSelectedDocumentIds(documentIds: string[]): void; | |||||
| selectedDocumentIds: string[]; | |||||
| } | } | ||||
| const RetrievalDocuments = ({ onTesting }: IProps) => { | |||||
| const RetrievalDocuments = ({ | |||||
| onTesting, | |||||
| selectedDocumentIds, | |||||
| setSelectedDocumentIds, | |||||
| }: IProps) => { | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const { documents } = useSelectTestingResult(); | const { documents } = useSelectTestingResult(); | ||||
| const [selectedDocumentIds, setSelectedDocumentIds] = useState<string[]>([]); | |||||
| return ( | return ( | ||||
| <Collapse | <Collapse |
| import { useTranslate } from '@/hooks/common-hooks'; | import { useTranslate } from '@/hooks/common-hooks'; | ||||
| import { useFetchAppConf } from '@/hooks/logic-hooks'; | import { useFetchAppConf } from '@/hooks/logic-hooks'; | ||||
| import { useNavigateWithFromState } from '@/hooks/route-hook'; | import { useNavigateWithFromState } from '@/hooks/route-hook'; | ||||
| import { MessageOutlined } from '@ant-design/icons'; | |||||
| import { MessageOutlined, SearchOutlined } from '@ant-design/icons'; | |||||
| import { Flex, Layout, Radio, Space, theme } from 'antd'; | import { Flex, Layout, Radio, Space, theme } from 'antd'; | ||||
| import { useCallback, useMemo } from 'react'; | import { useCallback, useMemo } from 'react'; | ||||
| import { useLocation } from 'umi'; | import { useLocation } from 'umi'; | ||||
| () => [ | () => [ | ||||
| { path: '/knowledge', name: t('knowledgeBase'), icon: KnowledgeBaseIcon }, | { path: '/knowledge', name: t('knowledgeBase'), icon: KnowledgeBaseIcon }, | ||||
| { path: '/chat', name: t('chat'), icon: MessageOutlined }, | { path: '/chat', name: t('chat'), icon: MessageOutlined }, | ||||
| // { path: '/search', name: t('search'), icon: SearchOutlined }, | |||||
| { path: '/search', name: t('search'), icon: SearchOutlined }, | |||||
| { path: '/flow', name: t('flow'), icon: GraphIcon }, | { path: '/flow', name: t('flow'), icon: GraphIcon }, | ||||
| { path: '/file', name: t('fileManager'), icon: FileIcon }, | { path: '/file', name: t('fileManager'), icon: FileIcon }, | ||||
| ], | ], |
| import { useFetchMindMap, useFetchRelatedQuestions } from '@/hooks/chat-hooks'; | import { useFetchMindMap, useFetchRelatedQuestions } from '@/hooks/chat-hooks'; | ||||
| import { useTestChunkRetrieval } from '@/hooks/knowledge-hooks'; | import { useTestChunkRetrieval } from '@/hooks/knowledge-hooks'; | ||||
| import { useSendMessageWithSse } from '@/hooks/logic-hooks'; | |||||
| import { | |||||
| useGetPaginationWithRouter, | |||||
| useSendMessageWithSse, | |||||
| } from '@/hooks/logic-hooks'; | |||||
| import { IAnswer } from '@/interfaces/database/chat'; | import { IAnswer } from '@/interfaces/database/chat'; | ||||
| import api from '@/utils/api'; | import api from '@/utils/api'; | ||||
| import { get, isEmpty, trim } from 'lodash'; | import { get, isEmpty, trim } from 'lodash'; | ||||
| } = useFetchMindMap(); | } = useFetchMindMap(); | ||||
| const [searchStr, setSearchStr] = useState<string>(''); | const [searchStr, setSearchStr] = useState<string>(''); | ||||
| const [isFirstRender, setIsFirstRender] = useState(true); | const [isFirstRender, setIsFirstRender] = useState(true); | ||||
| const [selectedDocumentIds, setSelectedDocumentIds] = useState<string[]>([]); | |||||
| const { pagination } = useGetPaginationWithRouter(); | |||||
| const sendQuestion = useCallback( | const sendQuestion = useCallback( | ||||
| (question: string) => { | (question: string) => { | ||||
| ); | ); | ||||
| const handleTestChunk = useCallback( | const handleTestChunk = useCallback( | ||||
| (documentIds: string[]) => { | |||||
| (documentIds: string[], page: number = 1, size: number = 10) => { | |||||
| const q = trim(searchStr); | const q = trim(searchStr); | ||||
| if (sendingLoading || isEmpty(q)) return; | if (sendingLoading || isEmpty(q)) return; | ||||
| kb_id: kbIds, | kb_id: kbIds, | ||||
| highlight: true, | highlight: true, | ||||
| question: q, | question: q, | ||||
| doc_ids: Array.isArray(documentIds) ? documentIds : [], | |||||
| doc_ids: documentIds ?? selectedDocumentIds, | |||||
| page, | |||||
| size, | |||||
| }); | }); | ||||
| }, | }, | ||||
| [sendingLoading, searchStr, kbIds, testChunk], | |||||
| [sendingLoading, searchStr, kbIds, testChunk, selectedDocumentIds], | |||||
| ); | ); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| handleSearchStrChange, | handleSearchStrChange, | ||||
| handleClickRelatedQuestion, | handleClickRelatedQuestion, | ||||
| handleTestChunk, | handleTestChunk, | ||||
| setSelectedDocumentIds, | |||||
| loading, | loading, | ||||
| sendingLoading, | sendingLoading, | ||||
| answer: currentAnswer, | answer: currentAnswer, | ||||
| mindMapLoading, | mindMapLoading, | ||||
| searchStr, | searchStr, | ||||
| isFirstRender, | isFirstRender, | ||||
| selectedDocumentIds, | |||||
| }; | }; | ||||
| }; | }; | ||||
| return `https://cn.bing.com${imgUrl}`; | return `https://cn.bing.com${imgUrl}`; | ||||
| }; | }; | ||||
| export const useTestRetrieval = ( | |||||
| kbIds: string[], | |||||
| searchStr: string, | |||||
| sendingLoading: boolean, | |||||
| ) => { | |||||
| const { testChunk, loading } = useTestChunkRetrieval(); | |||||
| const { pagination } = useGetPaginationWithRouter(); | |||||
| const [selectedDocumentIds, setSelectedDocumentIds] = useState<string[]>([]); | |||||
| const handleTestChunk = useCallback(() => { | |||||
| const q = trim(searchStr); | |||||
| if (sendingLoading || isEmpty(q)) return; | |||||
| testChunk({ | |||||
| kb_id: kbIds, | |||||
| highlight: true, | |||||
| question: q, | |||||
| doc_ids: Array.isArray(selectedDocumentIds) ? selectedDocumentIds : [], | |||||
| page: pagination.current, | |||||
| size: pagination.pageSize, | |||||
| }); | |||||
| }, [ | |||||
| sendingLoading, | |||||
| searchStr, | |||||
| kbIds, | |||||
| testChunk, | |||||
| selectedDocumentIds, | |||||
| pagination, | |||||
| ]); | |||||
| useEffect(() => { | |||||
| handleTestChunk(); | |||||
| }, [handleTestChunk]); | |||||
| return { | |||||
| loading, | |||||
| selectedDocumentIds, | |||||
| setSelectedDocumentIds, | |||||
| }; | |||||
| }; |
| // background-color: aqua; | // background-color: aqua; | ||||
| overflow: auto; | overflow: auto; | ||||
| padding: 20px 10px 10px; | padding: 20px 10px 10px; | ||||
| .chunks { | |||||
| // overflow: auto; | |||||
| // height: 60vh; | |||||
| } | |||||
| } | } | ||||
| .graph { | .graph { | ||||
| .globalInput { | .globalInput { | ||||
| width: 600px; | width: 600px; | ||||
| position: sticky; | |||||
| top: 0; | |||||
| z-index: 1; | |||||
| .input(); | .input(); | ||||
| } | } | ||||
| .partialInput { | .partialInput { |
| import HightLightMarkdown from '@/components/highlight-markdown'; | import HightLightMarkdown from '@/components/highlight-markdown'; | ||||
| import { ImageWithPopover } from '@/components/image'; | import { ImageWithPopover } from '@/components/image'; | ||||
| import IndentedTree from '@/components/indented-tree/indented-tree'; | import IndentedTree from '@/components/indented-tree/indented-tree'; | ||||
| import PdfDrawer from '@/components/pdf-drawer'; | |||||
| import { useClickDrawer } from '@/components/pdf-drawer/hooks'; | |||||
| import RetrievalDocuments from '@/components/retrieval-documents'; | |||||
| import { useSelectTestingResult } from '@/hooks/knowledge-hooks'; | import { useSelectTestingResult } from '@/hooks/knowledge-hooks'; | ||||
| import { useGetPaginationWithRouter } from '@/hooks/logic-hooks'; | |||||
| import { IReference } from '@/interfaces/database/chat'; | import { IReference } from '@/interfaces/database/chat'; | ||||
| import { | import { | ||||
| Card, | Card, | ||||
| Input, | Input, | ||||
| Layout, | Layout, | ||||
| List, | List, | ||||
| Pagination, | |||||
| PaginationProps, | |||||
| Skeleton, | Skeleton, | ||||
| Space, | Space, | ||||
| Tag, | Tag, | ||||
| } from 'antd'; | } from 'antd'; | ||||
| import { useState } from 'react'; | import { useState } from 'react'; | ||||
| import { useTranslation } from 'react-i18next'; | |||||
| import MarkdownContent from '../chat/markdown-content'; | import MarkdownContent from '../chat/markdown-content'; | ||||
| import { useFetchBackgroundImage, useSendQuestion } from './hooks'; | import { useFetchBackgroundImage, useSendQuestion } from './hooks'; | ||||
| import SearchSidebar from './sidebar'; | import SearchSidebar from './sidebar'; | ||||
| import PdfDrawer from '@/components/pdf-drawer'; | |||||
| import { useClickDrawer } from '@/components/pdf-drawer/hooks'; | |||||
| import RetrievalDocuments from '@/components/retrieval-documents'; | |||||
| import { useTranslation } from 'react-i18next'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| const { Content } = Layout; | const { Content } = Layout; | ||||
| const SearchPage = () => { | const SearchPage = () => { | ||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const [checkedList, setCheckedList] = useState<string[]>([]); | const [checkedList, setCheckedList] = useState<string[]>([]); | ||||
| const list = useSelectTestingResult(); | |||||
| const { chunks, total } = useSelectTestingResult(); | |||||
| // const appConf = useFetchAppConf(); | // const appConf = useFetchAppConf(); | ||||
| const { | const { | ||||
| sendQuestion, | sendQuestion, | ||||
| handleClickRelatedQuestion, | handleClickRelatedQuestion, | ||||
| handleSearchStrChange, | handleSearchStrChange, | ||||
| handleTestChunk, | handleTestChunk, | ||||
| setSelectedDocumentIds, | |||||
| answer, | answer, | ||||
| sendingLoading, | sendingLoading, | ||||
| relatedQuestions, | relatedQuestions, | ||||
| searchStr, | searchStr, | ||||
| loading, | loading, | ||||
| isFirstRender, | isFirstRender, | ||||
| selectedDocumentIds, | |||||
| } = useSendQuestion(checkedList); | } = useSendQuestion(checkedList); | ||||
| const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } = | const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } = | ||||
| useClickDrawer(); | useClickDrawer(); | ||||
| const imgUrl = useFetchBackgroundImage(); | const imgUrl = useFetchBackgroundImage(); | ||||
| const { pagination } = useGetPaginationWithRouter(); | |||||
| const onChange: PaginationProps['onChange'] = (pageNumber, pageSize) => { | |||||
| pagination.onChange?.(pageNumber, pageSize); | |||||
| handleTestChunk(selectedDocumentIds, pageNumber, pageSize); | |||||
| }; | |||||
| const InputSearch = ( | const InputSearch = ( | ||||
| <Search | <Search | ||||
| )} | )} | ||||
| <Divider></Divider> | <Divider></Divider> | ||||
| <RetrievalDocuments | <RetrievalDocuments | ||||
| selectedDocumentIdsLength={0} | |||||
| selectedDocumentIds={selectedDocumentIds} | |||||
| setSelectedDocumentIds={setSelectedDocumentIds} | |||||
| onTesting={handleTestChunk} | onTesting={handleTestChunk} | ||||
| ></RetrievalDocuments> | ></RetrievalDocuments> | ||||
| <Divider></Divider> | <Divider></Divider> | ||||
| {list.chunks.length > 0 && ( | |||||
| {chunks.length > 0 && ( | |||||
| <List | <List | ||||
| dataSource={list.chunks} | |||||
| dataSource={chunks} | |||||
| loading={loading} | loading={loading} | ||||
| className={styles.chunks} | |||||
| renderItem={(item) => ( | renderItem={(item) => ( | ||||
| <List.Item> | <List.Item> | ||||
| <Card className={styles.card}> | <Card className={styles.card}> | ||||
| </Flex> | </Flex> | ||||
| </Card> | </Card> | ||||
| )} | )} | ||||
| <Divider></Divider> | |||||
| <Pagination | |||||
| {...pagination} | |||||
| total={total} | |||||
| onChange={onChange} | |||||
| /> | |||||
| </section> | </section> | ||||
| <section className={styles.graph}> | <section className={styles.graph}> | ||||
| {mindMapLoading ? ( | {mindMapLoading ? ( |