import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Box, Flex, Button, Textarea, useTheme, Grid, HStack } from '@chakra-ui/react'; import { Control, FieldArrayWithId, UseFieldArrayAppend, UseFieldArrayRemove, UseFormRegister, useFieldArray, useForm } from 'react-hook-form'; import { postInsertData2Dataset, putDatasetDataById, delOneDatasetDataById, getDatasetCollectionById, getDatasetDataItemById } from '@/web/core/dataset/api'; import { useToast } from '@fastgpt/web/hooks/useToast'; import MyIcon from '@fastgpt/web/components/common/Icon'; import MyModal from '@fastgpt/web/components/common/MyModal'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'next-i18next'; import { useRequest, useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import { getDefaultIndex, getSourceNameIcon } from '@fastgpt/global/core/dataset/utils'; import { DatasetDataIndexItemType } from '@fastgpt/global/core/dataset/type'; import DeleteIcon from '@fastgpt/web/components/common/Icon/delete'; import { defaultCollectionDetail } from '@/web/core/dataset/constants'; import { getDocPath } from '@/web/common/system/doc'; import MyBox from '@fastgpt/web/components/common/MyBox'; import { getErrText } from '@fastgpt/global/common/error/utils'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import { useSystem } from '@fastgpt/web/hooks/useSystem'; import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs'; import styles from './styles.module.scss'; export type InputDataType = { q: string; a: string; indexes: (Omit & { dataId?: string; // pg data id })[]; }; enum TabEnum { content = 'content', index = 'index' } const InputDataModal = ({ collectionId, dataId, defaultValue, onClose, onSuccess }: { collectionId: string; dataId?: string; defaultValue?: { q: string; a?: string }; onClose: () => void; onSuccess: (data: InputDataType & { dataId: string }) => void; }) => { const { t } = useTranslation(); const theme = useTheme(); const { toast } = useToast(); const [currentTab, setCurrentTab] = useState(TabEnum.content); const { vectorModelList } = useSystemStore(); const { isPc } = useSystem(); const { register, handleSubmit, reset, control } = useForm(); const { fields: indexes, append: appendIndexes, remove: removeIndexes } = useFieldArray({ control, name: 'indexes' }); const tabList = [ { label: ( {t('common:dataset.data.edit.divide_content')} ), value: TabEnum.content }, { label: ( {t('common:dataset.data.edit.Index', { amount: indexes.length })} window.open(getDocPath('/docs/guide/knowledge_base/dataset_engine/'), '_blank') } _hover={{ color: 'primary.600', cursor: 'pointer' }} /> ), value: TabEnum.index } ]; const { ConfirmModal, openConfirm } = useConfirm({ content: t('common:dataset.data.Delete Tip'), type: 'delete' }); const { data: collection = defaultCollectionDetail } = useQuery( ['loadCollectionId', collectionId], () => { return getDatasetCollectionById(collectionId); } ); const { isFetching: isFetchingData } = useQuery( ['getDatasetDataItemById', dataId], () => { if (dataId) return getDatasetDataItemById(dataId); return null; }, { onSuccess(res) { if (res) { reset({ q: res.q, a: res.a, indexes: res.indexes }); } else if (defaultValue) { reset({ q: defaultValue.q, a: defaultValue.a }); } }, onError(err) { toast({ status: 'error', title: t(getErrText(err) as any) }); onClose(); } } ); const maxToken = useMemo(() => { const vectorModel = vectorModelList.find((item) => item.model === collection.datasetId.vectorModel) || vectorModelList[0]; return vectorModel?.maxToken || 3000; }, [collection.datasetId.vectorModel, vectorModelList]); // import new data const { mutate: sureImportData, isLoading: isImporting } = useRequest({ mutationFn: async (e: InputDataType) => { if (!e.q) { setCurrentTab(TabEnum.content); return Promise.reject(t('common:dataset.data.input is empty')); } const totalLength = e.q.length + (e.a?.length || 0); if (totalLength >= maxToken * 1.4) { return Promise.reject(t('common:core.dataset.data.Too Long')); } const data = { ...e }; const dataId = await postInsertData2Dataset({ collectionId: collection._id, q: e.q, a: e.a, // remove dataId indexes: e.indexes?.map((index) => ({ ...index, dataId: undefined })) || [] }); return { ...data, dataId }; }, successToast: t('common:dataset.data.Input Success Tip'), onSuccess(e) { reset({ ...e, q: '', a: '', indexes: [] }); onSuccess(e); }, errorToast: t('common:common.error.unKnow') }); // update const { runAsync: onUpdateData, loading: isUpdating } = useRequest2( async (e: InputDataType) => { if (!dataId) return Promise.reject(t('common:common.error.unKnow')); // not exactly same await putDatasetDataById({ dataId, ...e, indexes: e.indexes?.map((index) => index.defaultIndex ? getDefaultIndex({ q: e.q, a: e.a, dataId: index.dataId }) : index ) || [] }); return { dataId, ...e }; }, { successToast: t('common:dataset.data.Update Success Tip'), onSuccess(data) { onSuccess(data); onClose(); } } ); const isLoading = isFetchingData; const icon = useMemo( () => getSourceNameIcon({ sourceName: collection.sourceName, sourceId: collection.sourceId }), [collection] ); return ( onClose()} closeOnOverlayClick={false} maxW={'1440px'} h={'46.25rem'} title={ {collection.sourceName || t('common:common.UnKnow Source')} } > list={tabList} p={0} value={currentTab} onChange={(e: TabEnum) => setCurrentTab(e)} /> {currentTab === TabEnum.index && ( )} {currentTab === TabEnum.content && } {currentTab === TabEnum.index && ( )} ); }; export default React.memo(InputDataModal); const InputTab = ({ maxToken, register }: { maxToken: number; register: UseFormRegister; }) => { const { t } = useTranslation(); return ( <> * {t('common:core.dataset.data.Main Content')}