From 888e8c6dac41a4da339dd25ad79c88b468e0ec78 Mon Sep 17 00:00:00 2001 From: Joel Date: Sat, 18 Nov 2023 11:53:35 +0800 Subject: [PATCH] feat: add retriever rank fe (#1557) Co-authored-by: StyleZhang --- .../[datasetId]/layout.tsx | 4 +- .../dataset-config/card-item/index.tsx | 2 +- .../dataset-config/card-item/style.module.css | 12 +- .../configuration/dataset-config/index.tsx | 1 - .../dataset-config/params-config/index.tsx | 315 ++++++++++-------- .../dataset-config/settings-modal/index.tsx | 273 +++++++++++---- .../components/app/configuration/index.tsx | 18 +- .../public/common/multi-path-retrieval.svg | 19 ++ .../assets/public/common/n-to-1-retrieval.svg | 18 + .../icons/assets/public/llm/cohere-text.svg | 11 + .../base/icons/assets/public/llm/cohere.svg | 16 + .../vender/solid/arrows/high-priority.svg | 6 + .../solid/development/pattern-recognition.svg | 11 + .../vender/solid/development/semantic.svg | 6 + .../src/public/common/MultiPathRetrieval.json | 153 +++++++++ .../src/public/common/MultiPathRetrieval.tsx | 16 + .../src/public/common/NTo1Retrieval.json | 146 ++++++++ .../icons/src/public/common/NTo1Retrieval.tsx | 16 + .../base/icons/src/public/common/index.ts | 2 + .../base/icons/src/public/llm/Cohere.json | 112 +++++++ .../base/icons/src/public/llm/Cohere.tsx | 16 + .../base/icons/src/public/llm/CohereText.json | 90 +++++ .../base/icons/src/public/llm/CohereText.tsx | 16 + .../base/icons/src/public/llm/index.ts | 2 + .../src/vender/solid/arrows/HighPriority.json | 53 +++ .../src/vender/solid/arrows/HighPriority.tsx | 16 + .../icons/src/vender/solid/arrows/index.ts | 1 + .../solid/development/PatternRecognition.json | 98 ++++++ .../solid/development/PatternRecognition.tsx | 16 + .../vender/solid/development/Semantic.json | 53 +++ .../src/vender/solid/development/Semantic.tsx | 16 + .../src/vender/solid/development/index.ts | 2 + .../base/param-item/score-threshold-item.tsx | 2 +- .../components/base/param-item/top-k-item.tsx | 2 +- web/app/components/base/radio-card/index.tsx | 55 +++ .../base/radio-card/simple/index.tsx | 40 +++ .../base/radio-card/simple/style.module.css | 25 ++ .../base/radio-card/style.module.css | 25 ++ web/app/components/base/slider/index.tsx | 1 + .../datasets/common/check-rerank-model.ts | 50 +++ .../index.tsx | 40 +++ .../common/retrieval-method-config/index.tsx | 91 +++++ .../common/retrieval-method-info/index.tsx | 64 ++++ .../common/retrieval-param-config/index.tsx | 131 ++++++++ .../datasets/create/step-two/index.tsx | 106 +++++- .../detail/completed/SegmentCard.tsx | 14 +- .../components/datasets/hit-testing/index.tsx | 24 ++ .../hit-testing/modify-retrieval-modal.tsx | 95 ++++++ .../datasets/hit-testing/style.module.css | 6 +- .../datasets/hit-testing/textarea.tsx | 184 +++++----- .../datasets/settings/form/index.tsx | 56 +++- .../settings/permissions-radio/index.tsx | 3 + .../model-page/configs/cohere.tsx | 53 +++ .../model-page/configs/index.ts | 2 + .../model-page/declarations.ts | 2 + .../account-setting/model-page/index.tsx | 111 +----- .../model-page/model-selector/index.tsx | 32 +- .../model-page/system-model/index.tsx | 205 ++++++++++++ web/config/index.ts | 5 + web/context/debug-configuration.ts | 13 +- web/context/provider-context.tsx | 39 ++- web/i18n/lang/app-debug.en.ts | 11 + web/i18n/lang/app-debug.zh.ts | 11 + web/i18n/lang/common.en.ts | 14 +- web/i18n/lang/common.zh.ts | 14 +- web/i18n/lang/dataset-creation.en.ts | 1 + web/i18n/lang/dataset-creation.zh.ts | 1 + web/i18n/lang/dataset-documents.en.ts | 6 +- web/i18n/lang/dataset-documents.zh.ts | 6 +- web/i18n/lang/dataset-hit-testing.en.ts | 18 +- web/i18n/lang/dataset-hit-testing.zh.ts | 14 +- web/i18n/lang/dataset-settings.en.ts | 6 + web/i18n/lang/dataset-settings.zh.ts | 6 + web/i18n/lang/dataset.en.ts | 21 ++ web/i18n/lang/dataset.zh.ts | 21 ++ web/models/datasets.ts | 4 + web/models/debug.ts | 11 +- web/service/common.ts | 8 + web/service/datasets.ts | 7 +- web/types/app.ts | 24 ++ 80 files changed, 2753 insertions(+), 463 deletions(-) create mode 100644 web/app/components/base/icons/assets/public/common/multi-path-retrieval.svg create mode 100644 web/app/components/base/icons/assets/public/common/n-to-1-retrieval.svg create mode 100644 web/app/components/base/icons/assets/public/llm/cohere-text.svg create mode 100644 web/app/components/base/icons/assets/public/llm/cohere.svg create mode 100644 web/app/components/base/icons/assets/vender/solid/arrows/high-priority.svg create mode 100644 web/app/components/base/icons/assets/vender/solid/development/pattern-recognition.svg create mode 100644 web/app/components/base/icons/assets/vender/solid/development/semantic.svg create mode 100644 web/app/components/base/icons/src/public/common/MultiPathRetrieval.json create mode 100644 web/app/components/base/icons/src/public/common/MultiPathRetrieval.tsx create mode 100644 web/app/components/base/icons/src/public/common/NTo1Retrieval.json create mode 100644 web/app/components/base/icons/src/public/common/NTo1Retrieval.tsx create mode 100644 web/app/components/base/icons/src/public/llm/Cohere.json create mode 100644 web/app/components/base/icons/src/public/llm/Cohere.tsx create mode 100644 web/app/components/base/icons/src/public/llm/CohereText.json create mode 100644 web/app/components/base/icons/src/public/llm/CohereText.tsx create mode 100644 web/app/components/base/icons/src/vender/solid/arrows/HighPriority.json create mode 100644 web/app/components/base/icons/src/vender/solid/arrows/HighPriority.tsx create mode 100644 web/app/components/base/icons/src/vender/solid/arrows/index.ts create mode 100644 web/app/components/base/icons/src/vender/solid/development/PatternRecognition.json create mode 100644 web/app/components/base/icons/src/vender/solid/development/PatternRecognition.tsx create mode 100644 web/app/components/base/icons/src/vender/solid/development/Semantic.json create mode 100644 web/app/components/base/icons/src/vender/solid/development/Semantic.tsx create mode 100644 web/app/components/base/radio-card/index.tsx create mode 100644 web/app/components/base/radio-card/simple/index.tsx create mode 100644 web/app/components/base/radio-card/simple/style.module.css create mode 100644 web/app/components/base/radio-card/style.module.css create mode 100644 web/app/components/datasets/common/check-rerank-model.ts create mode 100644 web/app/components/datasets/common/economical-retrieval-method-config/index.tsx create mode 100644 web/app/components/datasets/common/retrieval-method-config/index.tsx create mode 100644 web/app/components/datasets/common/retrieval-method-info/index.tsx create mode 100644 web/app/components/datasets/common/retrieval-param-config/index.tsx create mode 100644 web/app/components/datasets/hit-testing/modify-retrieval-modal.tsx create mode 100644 web/app/components/header/account-setting/model-page/configs/cohere.tsx create mode 100644 web/app/components/header/account-setting/model-page/system-model/index.tsx 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')} +
+
+ +
-