balibabu
feat: After selecting the parsing method as knowledge graph, the delimiter and chunk token number are displayed. #1594 (#1929)
7b030d6
| import { Authorization } from '@/constants/authorization'; | |
| import { LanguageTranslationMap } from '@/constants/common'; | |
| import { Pagination } from '@/interfaces/common'; | |
| import { ResponseType } from '@/interfaces/database/base'; | |
| import { IAnswer } from '@/interfaces/database/chat'; | |
| import { IKnowledgeFile } from '@/interfaces/database/knowledge'; | |
| import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; | |
| import api from '@/utils/api'; | |
| import { getAuthorization } from '@/utils/authorization-util'; | |
| import { PaginationProps } from 'antd'; | |
| import { FormInstance } from 'antd/lib'; | |
| import axios from 'axios'; | |
| import { EventSourceParserStream } from 'eventsource-parser/stream'; | |
| import { | |
| ChangeEventHandler, | |
| useCallback, | |
| useEffect, | |
| useMemo, | |
| useRef, | |
| useState, | |
| } from 'react'; | |
| import { useTranslation } from 'react-i18next'; | |
| import { useDispatch } from 'umi'; | |
| import { useSetModalState, useTranslate } from './common-hooks'; | |
| import { useSetDocumentParser } from './document-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); | |
| const setRecord = (record: T) => { | |
| setCurrentRecord(record); | |
| }; | |
| return { currentRecord, setRecord }; | |
| }; | |
| export const useHandleSearchChange = () => { | |
| const [searchString, setSearchString] = useState(''); | |
| const handleInputChange = useCallback( | |
| (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { | |
| const value = e.target.value; | |
| setSearchString(value); | |
| }, | |
| [], | |
| ); | |
| return { handleInputChange, searchString }; | |
| }; | |
| export const useChangeLanguage = () => { | |
| const { i18n } = useTranslation(); | |
| const { saveSetting } = useSaveSetting(); | |
| const changeLanguage = (lng: string) => { | |
| i18n.changeLanguage( | |
| LanguageTranslationMap[lng as keyof typeof LanguageTranslationMap], | |
| ); | |
| saveSetting({ language: lng }); | |
| }; | |
| return changeLanguage; | |
| }; | |
| export const useGetPaginationWithRouter = () => { | |
| const { t } = useTranslate('common'); | |
| const { | |
| setPaginationParams, | |
| page, | |
| size: pageSize, | |
| } = useSetPaginationParams(); | |
| const onPageChange: PaginationProps['onChange'] = useCallback( | |
| (pageNumber: number, pageSize: number) => { | |
| setPaginationParams(pageNumber, pageSize); | |
| }, | |
| [setPaginationParams], | |
| ); | |
| const setCurrentPagination = useCallback( | |
| (pagination: { page: number; pageSize?: number }) => { | |
| setPaginationParams(pagination.page, pagination.pageSize); | |
| }, | |
| [setPaginationParams], | |
| ); | |
| const pagination: PaginationProps = useMemo(() => { | |
| return { | |
| showQuickJumper: true, | |
| total: 0, | |
| showSizeChanger: true, | |
| current: page, | |
| pageSize: pageSize, | |
| pageSizeOptions: [1, 2, 10, 20, 50, 100], | |
| onChange: onPageChange, | |
| showTotal: (total) => `${t('total')} ${total}`, | |
| }; | |
| }, [t, onPageChange, page, pageSize]); | |
| return { | |
| pagination, | |
| setPagination: setCurrentPagination, | |
| }; | |
| }; | |
| export const useGetPagination = () => { | |
| const [pagination, setPagination] = useState({ page: 1, pageSize: 10 }); | |
| const { t } = useTranslate('common'); | |
| const onPageChange: PaginationProps['onChange'] = useCallback( | |
| (pageNumber: number, pageSize: number) => { | |
| setPagination({ page: pageNumber, pageSize }); | |
| }, | |
| [], | |
| ); | |
| const currentPagination: PaginationProps = useMemo(() => { | |
| return { | |
| showQuickJumper: true, | |
| total: 0, | |
| showSizeChanger: true, | |
| current: pagination.page, | |
| pageSize: pagination.pageSize, | |
| pageSizeOptions: [1, 2, 10, 20, 50, 100], | |
| onChange: onPageChange, | |
| showTotal: (total) => `${t('total')} ${total}`, | |
| }; | |
| }, [t, onPageChange, pagination]); | |
| return { | |
| pagination: currentPagination, | |
| }; | |
| }; | |
| 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; | |
| } | |
| export const useFetchAppConf = () => { | |
| const [appConf, setAppConf] = useState<AppConf>({} as AppConf); | |
| const fetchAppConf = useCallback(async () => { | |
| const ret = await axios.get('/conf.json'); | |
| setAppConf(ret.data); | |
| }, []); | |
| useEffect(() => { | |
| fetchAppConf(); | |
| }, [fetchAppConf]); | |
| return appConf; | |
| }; | |
| export const useSendMessageWithSse = ( | |
| url: string = api.completeConversation, | |
| ) => { | |
| const [answer, setAnswer] = useState<IAnswer>({} as IAnswer); | |
| const [done, setDone] = useState(true); | |
| const send = useCallback( | |
| async ( | |
| body: any, | |
| ): Promise<{ response: Response; data: ResponseType } | undefined> => { | |
| try { | |
| setDone(false); | |
| const response = await fetch(url, { | |
| method: 'POST', | |
| headers: { | |
| [Authorization]: getAuthorization(), | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify(body), | |
| }); | |
| const res = response.clone().json(); | |
| const reader = response?.body | |
| ?.pipeThrough(new TextDecoderStream()) | |
| .pipeThrough(new EventSourceParserStream()) | |
| .getReader(); | |
| while (true) { | |
| const x = await reader?.read(); | |
| if (x) { | |
| const { done, value } = x; | |
| try { | |
| const val = JSON.parse(value?.data || ''); | |
| const d = val?.data; | |
| if (typeof d !== 'boolean') { | |
| console.info('data:', d); | |
| setAnswer({ | |
| ...d, | |
| conversationId: body?.conversation_id, | |
| }); | |
| } | |
| } catch (e) { | |
| console.warn(e); | |
| } | |
| if (done) { | |
| console.info('done'); | |
| break; | |
| } | |
| } | |
| } | |
| console.info('done?'); | |
| setDone(true); | |
| return { data: await res, response }; | |
| } catch (e) { | |
| setDone(true); | |
| console.warn(e); | |
| } | |
| }, | |
| [url], | |
| ); | |
| return { send, answer, done, setDone }; | |
| }; | |
| //#region chat hooks | |
| export const useScrollToBottom = (messages?: unknown) => { | |
| const ref = useRef<HTMLDivElement>(null); | |
| const scrollToBottom = useCallback(() => { | |
| if (messages) { | |
| ref.current?.scrollIntoView({ behavior: 'instant' }); | |
| } | |
| }, [messages]); // If the message changes, scroll to the bottom | |
| useEffect(() => { | |
| scrollToBottom(); | |
| }, [scrollToBottom]); | |
| return ref; | |
| }; | |
| export const useHandleMessageInputChange = () => { | |
| const [value, setValue] = useState(''); | |
| const handleInputChange: ChangeEventHandler<HTMLInputElement> = (e) => { | |
| const value = e.target.value; | |
| const nextValue = value.replaceAll('\\n', '\n').replaceAll('\\t', '\t'); | |
| setValue(nextValue); | |
| }; | |
| return { | |
| handleInputChange, | |
| value, | |
| setValue, | |
| }; | |
| }; | |
| // #endregion | |
| /** | |
| * | |
| * @param defaultId | |
| * used to switch between different items, similar to radio | |
| * @returns | |
| */ | |
| export const useSelectItem = (defaultId?: string) => { | |
| const [selectedId, setSelectedId] = useState(''); | |
| const handleItemClick = useCallback( | |
| (id: string) => () => { | |
| setSelectedId(id); | |
| }, | |
| [], | |
| ); | |
| useEffect(() => { | |
| if (defaultId) { | |
| setSelectedId(defaultId); | |
| } | |
| }, [defaultId]); | |
| return { selectedId, handleItemClick }; | |
| }; | |
| export const useFetchModelId = () => { | |
| const { data: tenantInfo } = useFetchTenantInfo(); | |
| return tenantInfo?.llm_id ?? ''; | |
| }; | |
| const ChunkTokenNumMap = { | |
| naive: 128, | |
| knowledge_graph: 8192, | |
| }; | |
| export const useHandleChunkMethodSelectChange = (form: FormInstance) => { | |
| // const form = Form.useFormInstance(); | |
| const handleChange = useCallback( | |
| (value: string) => { | |
| if (value in ChunkTokenNumMap) { | |
| form.setFieldValue( | |
| ['parser_config', 'chunk_token_num'], | |
| ChunkTokenNumMap[value as keyof typeof ChunkTokenNumMap], | |
| ); | |
| } | |
| }, | |
| [form], | |
| ); | |
| return handleChange; | |
| }; | |