balibabu
feat: The same query conditions on the search page should not request the interface every time the mind map drawer is opened. #2759 (#2760)
ab13701
| import { useFetchMindMap, useFetchRelatedQuestions } from '@/hooks/chat-hooks'; | |
| import { useSetModalState } from '@/hooks/common-hooks'; | |
| import { useTestChunkRetrieval } from '@/hooks/knowledge-hooks'; | |
| import { | |
| useGetPaginationWithRouter, | |
| useSendMessageWithSse, | |
| } from '@/hooks/logic-hooks'; | |
| import { IAnswer } from '@/interfaces/database/chat'; | |
| import api from '@/utils/api'; | |
| import { get, isEmpty, isEqual, trim } from 'lodash'; | |
| import { | |
| ChangeEventHandler, | |
| useCallback, | |
| useEffect, | |
| useRef, | |
| useState, | |
| } from 'react'; | |
| export const useSendQuestion = (kbIds: string[]) => { | |
| const { send, answer, done } = useSendMessageWithSse(api.ask); | |
| const { testChunk, loading } = useTestChunkRetrieval(); | |
| const [sendingLoading, setSendingLoading] = useState(false); | |
| const [currentAnswer, setCurrentAnswer] = useState({} as IAnswer); | |
| const { fetchRelatedQuestions, data: relatedQuestions } = | |
| useFetchRelatedQuestions(); | |
| const [searchStr, setSearchStr] = useState<string>(''); | |
| const [isFirstRender, setIsFirstRender] = useState(true); | |
| const [selectedDocumentIds, setSelectedDocumentIds] = useState<string[]>([]); | |
| const { pagination, setPagination } = useGetPaginationWithRouter(); | |
| const sendQuestion = useCallback( | |
| (question: string) => { | |
| const q = trim(question); | |
| if (isEmpty(q)) return; | |
| setPagination({ page: 1 }); | |
| setIsFirstRender(false); | |
| setCurrentAnswer({} as IAnswer); | |
| setSendingLoading(true); | |
| send({ kb_ids: kbIds, question: q }); | |
| testChunk({ | |
| kb_id: kbIds, | |
| highlight: true, | |
| question: q, | |
| page: 1, | |
| size: pagination.pageSize, | |
| }); | |
| fetchRelatedQuestions(q); | |
| }, | |
| [ | |
| send, | |
| testChunk, | |
| kbIds, | |
| fetchRelatedQuestions, | |
| setPagination, | |
| pagination.pageSize, | |
| ], | |
| ); | |
| const handleSearchStrChange: ChangeEventHandler<HTMLInputElement> = | |
| useCallback((e) => { | |
| setSearchStr(e.target.value); | |
| }, []); | |
| const handleClickRelatedQuestion = useCallback( | |
| (question: string) => () => { | |
| if (sendingLoading) return; | |
| setSearchStr(question); | |
| sendQuestion(question); | |
| }, | |
| [sendQuestion, sendingLoading], | |
| ); | |
| const handleTestChunk = useCallback( | |
| (documentIds: string[], page: number = 1, size: number = 10) => { | |
| const q = trim(searchStr); | |
| if (sendingLoading || isEmpty(q)) return; | |
| testChunk({ | |
| kb_id: kbIds, | |
| highlight: true, | |
| question: q, | |
| doc_ids: documentIds ?? selectedDocumentIds, | |
| page, | |
| size, | |
| }); | |
| }, | |
| [sendingLoading, searchStr, kbIds, testChunk, selectedDocumentIds], | |
| ); | |
| useEffect(() => { | |
| if (!isEmpty(answer)) { | |
| setCurrentAnswer(answer); | |
| } | |
| }, [answer]); | |
| useEffect(() => { | |
| if (done) { | |
| setSendingLoading(false); | |
| } | |
| }, [done]); | |
| return { | |
| sendQuestion, | |
| handleSearchStrChange, | |
| handleClickRelatedQuestion, | |
| handleTestChunk, | |
| setSelectedDocumentIds, | |
| loading, | |
| sendingLoading, | |
| answer: currentAnswer, | |
| relatedQuestions: relatedQuestions?.slice(0, 5) ?? [], | |
| searchStr, | |
| isFirstRender, | |
| selectedDocumentIds, | |
| isSearchStrEmpty: isEmpty(trim(searchStr)), | |
| }; | |
| }; | |
| export const useFetchBackgroundImage = () => { | |
| const [imgUrl, setImgUrl] = useState<string>(''); | |
| const fetchImage = useCallback(async () => { | |
| try { | |
| const res = await fetch( | |
| '/HPImageArchive.aspx?format=js&idx=0&n=1&mkt=zh-CN', | |
| ); | |
| const ret = await res.json(); | |
| const url = get(ret, 'images.0.url'); | |
| if (url) { | |
| setImgUrl(url); | |
| } | |
| } catch (error) { | |
| console.log('🚀 ~ fetchImage ~ error:', error); | |
| } | |
| }, []); | |
| useEffect(() => { | |
| fetchImage(); | |
| }, [fetchImage]); | |
| 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, | |
| }; | |
| }; | |
| export const useShowMindMapDrawer = (kbIds: string[], question: string) => { | |
| const { visible, showModal, hideModal } = useSetModalState(); | |
| const ref = useRef<any>(); | |
| const { | |
| fetchMindMap, | |
| data: mindMap, | |
| loading: mindMapLoading, | |
| } = useFetchMindMap(); | |
| const handleShowModal = useCallback(() => { | |
| const searchParams = { question: trim(question), kb_ids: kbIds }; | |
| if ( | |
| !isEmpty(searchParams.question) && | |
| !isEqual(searchParams, ref.current) | |
| ) { | |
| ref.current = searchParams; | |
| fetchMindMap(searchParams); | |
| } | |
| showModal(); | |
| }, [fetchMindMap, showModal, question, kbIds]); | |
| return { | |
| mindMap, | |
| mindMapVisible: visible, | |
| mindMapLoading, | |
| showMindMapModal: handleShowModal, | |
| hideMindMapModal: hideModal, | |
| }; | |
| }; | |
| export const usePendingMindMap = () => { | |
| const [count, setCount] = useState<number>(0); | |
| const ref = useRef<NodeJS.Timeout>(); | |
| const setCountInterval = useCallback(() => { | |
| ref.current = setInterval(() => { | |
| setCount((pre) => { | |
| if (pre > 40) { | |
| clearInterval(ref?.current); | |
| } | |
| return pre + 1; | |
| }); | |
| }, 1000); | |
| }, []); | |
| useEffect(() => { | |
| setCountInterval(); | |
| return () => { | |
| clearInterval(ref?.current); | |
| }; | |
| }, [setCountInterval]); | |
| return Number(((count / 43) * 100).toFixed(0)); | |
| }; | |