diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx index 835fac462d3ce7..3458b14a628ebf 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx @@ -153,7 +153,7 @@ const DatasetDetailLayout: FC = (props) => { return return ( -
+
{!hideSideBar && = (props) => { dataset: datasetRes, mutateDatasetRes: () => mutateDatasetRes(), }}> -
{children}
+
{children}
) diff --git a/web/app/components/app/configuration/dataset-config/card-item/index.tsx b/web/app/components/app/configuration/dataset-config/card-item/index.tsx index 650d717553fe2b..767943303aefe1 100644 --- a/web/app/components/app/configuration/dataset-config/card-item/index.tsx +++ b/web/app/components/app/configuration/dataset-config/card-item/index.tsx @@ -16,7 +16,7 @@ export type ICardItemProps = { onRemove: (id: string) => void readonly?: boolean } - +// used in universal-chat const CardItem: FC = ({ className, config, diff --git a/web/app/components/app/configuration/dataset-config/card-item/style.module.css b/web/app/components/app/configuration/dataset-config/card-item/style.module.css index 9113f795bcfb42..4ddec9ea37ec2d 100644 --- a/web/app/components/app/configuration/dataset-config/card-item/style.module.css +++ b/web/app/components/app/configuration/dataset-config/card-item/style.module.css @@ -1,16 +1,22 @@ .card { box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); - width: calc(50% - 4px); + width: 100%; } .card:hover { box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06); } -.deleteBtn { +.btnWrap { + padding-left: 64px; visibility: hidden; + background: linear-gradient(270deg, #FFF 49.99%, rgba(255, 255, 255, 0.00) 98.1%); } -.card:hover .deleteBtn { +.card:hover .btnWrap { visibility: visible; +} + +.settingBtn:hover { + background-color: rgba(0, 0, 0, 0.05); } \ No newline at end of file diff --git a/web/app/components/app/configuration/dataset-config/index.tsx b/web/app/components/app/configuration/dataset-config/index.tsx index e7d6ead7934393..9b4dbf2b781abe 100644 --- a/web/app/components/app/configuration/dataset-config/index.tsx +++ b/web/app/components/app/configuration/dataset-config/index.tsx @@ -105,7 +105,6 @@ const DatasetConfig: FC = () => { onChange={handleSelectContextVar} /> )} - ) } diff --git a/web/app/components/app/configuration/dataset-config/params-config/index.tsx b/web/app/components/app/configuration/dataset-config/params-config/index.tsx index 147cc7b43ae634..d166a5b457b9a9 100644 --- a/web/app/components/app/configuration/dataset-config/params-config/index.tsx +++ b/web/app/components/app/configuration/dataset-config/params-config/index.tsx @@ -4,96 +4,23 @@ import { memo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import cn from 'classnames' -import { HelpCircle, Settings04 } from '@/app/components/base/icons/src/vender/line/general' -import { - PortalToFollowElem, - PortalToFollowElemContent, - PortalToFollowElemTrigger, -} from '@/app/components/base/portal-to-follow-elem' -import Tooltip from '@/app/components/base/tooltip-plus' -import Slider from '@/app/components/base/slider' -import Switch from '@/app/components/base/switch' +import { Settings04 } from '@/app/components/base/icons/src/vender/line/general' import ConfigContext from '@/context/debug-configuration' - -// TODO -const PARAMS_KEY = [ - 'top_k', - 'score_threshold', -] -const PARAMS = { - top_k: { - default: 2, - step: 1, - min: 1, - max: 10, - }, - score_threshold: { - default: 0.7, - step: 0.01, - min: 0, - max: 1, - }, -} as any - -export type IParamItemProps = { - id: string - name: string - tip: string - value: number - enable: boolean - step?: number - min?: number - max: number - onChange: (key: string, value: number) => void - onSwitchChange: (key: string, enable: boolean) => void -} - -const ParamItem: FC = ({ id, name, tip, step = 0.1, min = 0, max, value, enable, onChange, onSwitchChange }) => { - return ( -
-
-
- {id === 'score_threshold' && ( - { - onSwitchChange(id, val) - }} - /> - )} - {name} - {tip}
}> - - -
-
-
-
-
-
- onChange(id, value / (max < 5 ? 100 : 1))} - /> -
-
-
- { - const value = parseFloat(e.target.value) - if (value < min || value > max) - return - - onChange(id, value) - }} /> -
-
-
- ) -} +import TopKItem from '@/app/components/base/param-item/top-k-item' +import ScoreThresholdItem from '@/app/components/base/param-item/score-threshold-item' +import Modal from '@/app/components/base/modal' +import Button from '@/app/components/base/button' +import RadioCard from '@/app/components/base/radio-card/simple' +import { RETRIEVE_TYPE } from '@/types/app' +import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector' +import { useProviderContext } from '@/context/provider-context' +import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' +import Toast from '@/app/components/base/toast' +import { DATASET_DEFAULT } from '@/config' +import { + MultiPathRetrieval, + NTo1Retrieval, +} from '@/app/components/base/icons/src/public/common' const ParamsConfig: FC = () => { const { t } = useTranslation() @@ -102,24 +29,47 @@ const ParamsConfig: FC = () => { datasetConfigs, setDatasetConfigs, } = useContext(ConfigContext) + const [tempDataSetConfigs, setTempDataSetConfigs] = useState(datasetConfigs) + + const type = tempDataSetConfigs.retrieval_model + const setType = (value: RETRIEVE_TYPE) => { + setTempDataSetConfigs({ + ...tempDataSetConfigs, + retrieval_model: value, + }) + } + + const { + rerankDefaultModel, + isRerankDefaultModelVaild, + } = useProviderContext() + + const rerankModel = (() => { + if (tempDataSetConfigs.reranking_model) { + return { + provider_name: tempDataSetConfigs.reranking_model.reranking_provider_name, + model_name: tempDataSetConfigs.reranking_model.reranking_model_name, + } + } + else if (rerankDefaultModel) { + return { + provider_name: rerankDefaultModel.model_provider.provider_name, + model_name: rerankDefaultModel.model_name, + } + } + })() const handleParamChange = (key: string, value: number) => { - let notOutRangeValue = parseFloat(value.toFixed(2)) - notOutRangeValue = Math.max(PARAMS[key].min, notOutRangeValue) - notOutRangeValue = Math.min(PARAMS[key].max, notOutRangeValue) if (key === 'top_k') { - setDatasetConfigs({ - ...datasetConfigs, - top_k: notOutRangeValue, + setTempDataSetConfigs({ + ...tempDataSetConfigs, + top_k: value, }) } else if (key === 'score_threshold') { - setDatasetConfigs({ - ...datasetConfigs, - [key]: { - enable: datasetConfigs.score_threshold.enable, - value: notOutRangeValue, - }, + setTempDataSetConfigs({ + ...tempDataSetConfigs, + score_threshold: value, }) } } @@ -128,54 +78,133 @@ const ParamsConfig: FC = () => { if (key === 'top_k') return - setDatasetConfigs({ - ...datasetConfigs, - [key]: { - enable, - value: (datasetConfigs as any)[key].value, - }, + setTempDataSetConfigs({ + ...tempDataSetConfigs, + score_threshold_enabled: enable, }) } + const isValid = () => { + let errMsg = '' + if (tempDataSetConfigs.retrieval_model === RETRIEVE_TYPE.multiWay) { + if (!tempDataSetConfigs.reranking_model?.reranking_model_name && (!rerankDefaultModel && isRerankDefaultModelVaild)) + errMsg = t('appDebug.datasetConfig.rerankModelRequired') + } + if (errMsg) { + Toast.notify({ + type: 'error', + message: errMsg, + }) + } + return !errMsg + } + const handleSave = () => { + if (!isValid()) + return + + const config = { ...tempDataSetConfigs } + if (config.retrieval_model === RETRIEVE_TYPE.multiWay && !config.reranking_model) { + config.reranking_model = { + reranking_provider_name: rerankDefaultModel?.model_provider.provider_name, + reranking_model_name: rerankDefaultModel?.model_name, + } as any + } + setDatasetConfigs(config) + setOpen(false) + } return ( - - setOpen(v => !v)}> -
- -
- {t('appDebug.datasetConfig.params')} -
+
+
{ + setTempDataSetConfigs({ + ...datasetConfigs, + top_k: datasetConfigs.top_k || DATASET_DEFAULT.top_k, + score_threshold: datasetConfigs.score_threshold || DATASET_DEFAULT.score_threshold, + }) + setOpen(true) + }} + > + +
+ {t('appDebug.datasetConfig.params')}
- - -
- {PARAMS_KEY.map((key: string) => { - const currentValue = key === 'top_k' ? datasetConfigs[key] : (datasetConfigs as any)[key]?.value - const currentEnableState = key === 'top_k' ? true : (datasetConfigs as any)[key]?.enable - return ( - + { + open && ( + { + setOpen(false) + }} + className='min-w-[528px]' + wrapperClassName='z-50' + title={t('appDebug.datasetConfig.settingTitle')} + > +
+ } + title={t('appDebug.datasetConfig.retrieveOneWay.title')} + description={t('appDebug.datasetConfig.retrieveOneWay.description')} + isChosen={type === RETRIEVE_TYPE.oneWay} + onChosen={() => { setType(RETRIEVE_TYPE.oneWay) }} /> - ) - })} -
- - + } + title={t('appDebug.datasetConfig.retrieveMultiWay.title')} + description={t('appDebug.datasetConfig.retrieveMultiWay.description')} + isChosen={type === RETRIEVE_TYPE.multiWay} + onChosen={() => { setType(RETRIEVE_TYPE.multiWay) }} + /> +
+ {type === RETRIEVE_TYPE.multiWay && ( + <> +
+
{t('common.modelProvider.rerankModel.key')}
+
+ { + setTempDataSetConfigs({ + ...tempDataSetConfigs, + reranking_model: { + reranking_provider_name: v.model_provider.provider_name, + reranking_model_name: v.model_name, + }, + }) + }} + /> +
+
+
+ + +
+ + )} +
+ + +
+ + ) + } + +
) } export default memo(ParamsConfig) diff --git a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx index f75cdcf5966645..6d64945cd22141 100644 --- a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx +++ b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx @@ -1,8 +1,11 @@ import type { FC } from 'react' -import { useState } from 'react' +import { useRef, useState } from 'react' +import { useClickAway } from 'ahooks' import { useTranslation } from 'react-i18next' +import { isEqual } from 'lodash-es' +import cn from 'classnames' +import { BookOpenIcon } from '@heroicons/react/24/outline' import IndexMethodRadio from '@/app/components/datasets/settings/index-method-radio' -import Modal from '@/app/components/base/modal' import Button from '@/app/components/base/button' import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector' import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' @@ -11,12 +14,29 @@ import type { DataSet } from '@/models/datasets' import { useToastContext } from '@/app/components/base/toast' import { updateDatasetSetting } from '@/service/datasets' import { useModalContext } from '@/context/modal-context' +import { XClose } from '@/app/components/base/icons/src/vender/line/general' +import type { RetrievalConfig } from '@/types/app' +import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config' +import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/economical-retrieval-method-config' +import { useProviderContext } from '@/context/provider-context' +import { ensureRerankModelSelected, isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model' +import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' +import PermissionsRadio from '@/app/components/datasets/settings/permissions-radio' type SettingsModalProps = { currentDataset: DataSet onCancel: () => void onSave: (newDataset: DataSet) => void } + +const rowClass = ` + flex justify-between py-4 +` + +const labelClass = ` + flex w-[168px] shrink-0 +` + const SettingsModal: FC = ({ currentDataset, onCancel, @@ -24,13 +44,28 @@ const SettingsModal: FC = ({ }) => { const { t } = useTranslation() const { notify } = useToastContext() + const ref = useRef(null) + useClickAway(() => { + if (ref) + onCancel() + }, ref) + const { setShowAccountSettingModal } = useModalContext() const [loading, setLoading] = useState(false) const [localeCurrentDataset, setLocaleCurrentDataset] = useState({ ...currentDataset }) + const [indexMethod, setIndexMethod] = useState(currentDataset.indexing_technique) + const [retrievalConfig, setRetrievalConfig] = useState(localeCurrentDataset?.retrieval_model_dict as RetrievalConfig) + + const { + rerankDefaultModel, + isRerankDefaultModelVaild, + } = useProviderContext() const handleValueChange = (type: string, value: string) => { setLocaleCurrentDataset({ ...localeCurrentDataset, [type]: value }) } + const [isHideChangedTip, setIsHideChangedTip] = useState(false) + const isRetrievalChanged = !isEqual(retrievalConfig, localeCurrentDataset?.retrieval_model_dict) || indexMethod !== localeCurrentDataset?.indexing_technique const handleSave = async () => { if (loading) @@ -39,19 +74,41 @@ const SettingsModal: FC = ({ notify({ type: 'error', message: t('datasetSettings.form.nameError') }) return } + if ( + !isReRankModelSelected({ + rerankDefaultModel, + isRerankDefaultModelVaild, + retrievalConfig, + indexMethod, + }) + ) { + notify({ type: 'error', message: t('appDebug.datasetConfig.rerankModelRequired') }) + return + } + const postRetrievalConfig = ensureRerankModelSelected({ + rerankDefaultModel: rerankDefaultModel!, + retrievalConfig, + indexMethod, + }) try { setLoading(true) - const { id, name, description, indexing_technique } = localeCurrentDataset + const { id, name, description, permission } = localeCurrentDataset await updateDatasetSetting({ datasetId: id, body: { name, description, - indexing_technique, + permission, + indexing_technique: indexMethod, + retrieval_model: postRetrievalConfig, }, }) notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) - onSave(localeCurrentDataset) + onSave({ + ...localeCurrentDataset, + indexing_technique: indexMethod, + retrieval_model_dict: postRetrievalConfig, + }) } catch (e) { notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) @@ -62,74 +119,162 @@ const SettingsModal: FC = ({ } return ( - {}} - className='!p-8 !pb-6 !max-w-none !w-[640px]' +
-
- {t('datasetSettings.title')} -
-
-
- {t('datasetSettings.form.name')} +
+
+
{t('datasetSettings.title')}
- handleValueChange('name', e.target.value)} - className='block px-3 w-full h-9 bg-gray-100 rounded-lg text-sm text-gray-900 outline-none appearance-none' - placeholder={t('datasetSettings.form.namePlaceholder') || ''} - /> -
-
-
- {t('datasetSettings.form.desc')} -
-
- {t('datasetSettings.form.descInfo')}{t('common.operation.learnMore')} +
+
+ +
-