feat: n to 1 retrieval legacy (#6554)
This commit is contained in:
@@ -1,18 +1,20 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiDeleteBinLine } from '@remixicon/react'
|
||||
import {
|
||||
RiDeleteBinLine,
|
||||
RiEditLine,
|
||||
} from '@remixicon/react'
|
||||
import SettingsModal from '../settings-modal'
|
||||
import type { DataSet } from '@/models/datasets'
|
||||
import { DataSourceType } from '@/models/datasets'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
import FileIcon from '@/app/components/base/file-icon'
|
||||
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { Folder } from '@/app/components/base/icons/src/vender/solid/files'
|
||||
import { Globe06 } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
|
||||
import Drawer from '@/app/components/base/drawer'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import Badge from '@/app/components/base/badge'
|
||||
import { useKnowledge } from '@/hooks/use-knowledge'
|
||||
|
||||
type ItemProps = {
|
||||
className?: string
|
||||
@@ -27,12 +29,10 @@ const Item: FC<ItemProps> = ({
|
||||
onSave,
|
||||
onRemove,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const media = useBreakpoints()
|
||||
const isMobile = media === MediaType.mobile
|
||||
|
||||
const [showSettingsModal, setShowSettingsModal] = useState(false)
|
||||
const { formatIndexingTechniqueAndMethod } = useKnowledge()
|
||||
|
||||
const handleSave = (newDataset: DataSet) => {
|
||||
onSave(newDataset)
|
||||
@@ -65,22 +65,17 @@ const Item: FC<ItemProps> = ({
|
||||
<div className='grow'>
|
||||
<div className='flex items-center h-[18px]'>
|
||||
<div className='grow text-[13px] font-medium text-gray-800 truncate' title={config.name}>{config.name}</div>
|
||||
<div className='shrink-0 text-xs text-gray-500'>
|
||||
{formatNumber(config.word_count)} {t('appDebug.feature.dataSet.words')} · {formatNumber(config.document_count)} {t('appDebug.feature.dataSet.textBlocks')}
|
||||
</div>
|
||||
<Badge
|
||||
text={formatIndexingTechniqueAndMethod(config.indexing_technique, config.retrieval_model_dict?.search_method)}
|
||||
/>
|
||||
</div>
|
||||
{/* {
|
||||
config.description && (
|
||||
<div className='text-xs text-gray-500'>{config.description}</div>
|
||||
)
|
||||
} */}
|
||||
</div>
|
||||
<div className='hidden rounded-lg group-hover:flex items-center justify-end absolute right-0 top-0 bottom-0 pr-2 w-[124px] bg-gradient-to-r from-white/50 to-white to-50%'>
|
||||
<div
|
||||
className='flex items-center justify-center mr-1 w-6 h-6 hover:bg-black/5 rounded-md cursor-pointer'
|
||||
onClick={() => setShowSettingsModal(true)}
|
||||
>
|
||||
<Settings01 className='w-4 h-4 text-gray-500' />
|
||||
<RiEditLine className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
<div
|
||||
className='group/action flex items-center justify-center w-6 h-6 hover:bg-[#FEE4E2] rounded-md cursor-pointer'
|
||||
|
||||
@@ -44,7 +44,8 @@ const DatasetConfig: FC = () => {
|
||||
const handleSave = (newDataset: DataSet) => {
|
||||
const index = dataSet.findIndex(item => item.id === newDataset.id)
|
||||
|
||||
setDataSet([...dataSet.slice(0, index), newDataset, ...dataSet.slice(index + 1)])
|
||||
const newDatasets = [...dataSet.slice(0, index), newDataset, ...dataSet.slice(index + 1)]
|
||||
setDataSet(newDatasets)
|
||||
formattingChangedDispatcher()
|
||||
}
|
||||
|
||||
@@ -74,7 +75,7 @@ const DatasetConfig: FC = () => {
|
||||
title={t('appDebug.feature.dataSet.title')}
|
||||
headerRight={
|
||||
<div className='flex items-center gap-1'>
|
||||
{!isAgent && <ParamsConfig />}
|
||||
{!isAgent && <ParamsConfig disabled={!hasData} selectedDatasets={dataSet} />}
|
||||
<OperationBtn type="add" onClick={showSelectDataSet} />
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
|
||||
import { memo, useMemo } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiQuestionLine,
|
||||
} from '@remixicon/react'
|
||||
import WeightedScore from './weighted-score'
|
||||
import TopKItem from '@/app/components/base/param-item/top-k-item'
|
||||
import ScoreThresholdItem from '@/app/components/base/param-item/score-threshold-item'
|
||||
import RadioCard from '@/app/components/base/radio-card/simple'
|
||||
@@ -16,13 +18,20 @@ import {
|
||||
import type {
|
||||
DatasetConfigs,
|
||||
} from '@/models/debug'
|
||||
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
||||
import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import type { ModelConfig } from '@/app/components/workflow/types'
|
||||
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
|
||||
import TooltipPlus from '@/app/components/base/tooltip-plus'
|
||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type {
|
||||
DataSet,
|
||||
WeightedScoreEnum,
|
||||
} from '@/models/datasets'
|
||||
import { RerankingModeEnum } from '@/models/datasets'
|
||||
import cn from '@/utils/classnames'
|
||||
import { useSelectedDatasetsMode } from '@/app/components/workflow/nodes/knowledge-retrieval/hooks'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
|
||||
type Props = {
|
||||
datasetConfigs: DatasetConfigs
|
||||
@@ -31,6 +40,7 @@ type Props = {
|
||||
singleRetrievalModelConfig?: ModelConfig
|
||||
onSingleRetrievalModelChange?: (config: ModelConfig) => void
|
||||
onSingleRetrievalModelParamsChange?: (config: ModelConfig) => void
|
||||
selectedDatasets?: DataSet[]
|
||||
}
|
||||
|
||||
const ConfigContent: FC<Props> = ({
|
||||
@@ -40,8 +50,10 @@ const ConfigContent: FC<Props> = ({
|
||||
singleRetrievalModelConfig: singleRetrievalConfig = {} as ModelConfig,
|
||||
onSingleRetrievalModelChange = () => { },
|
||||
onSingleRetrievalModelParamsChange = () => { },
|
||||
selectedDatasets = [],
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const selectedDatasetsMode = useSelectedDatasetsMode(selectedDatasets)
|
||||
const type = datasetConfigs.retrieval_model
|
||||
const setType = (value: RETRIEVE_TYPE) => {
|
||||
onChange({
|
||||
@@ -54,7 +66,7 @@ const ConfigContent: FC<Props> = ({
|
||||
defaultModel: rerankDefaultModel,
|
||||
} = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.rerank)
|
||||
const rerankModel = (() => {
|
||||
if (datasetConfigs.reranking_model) {
|
||||
if (datasetConfigs.reranking_model?.reranking_provider_name) {
|
||||
return {
|
||||
provider_name: datasetConfigs.reranking_model.reranking_provider_name,
|
||||
model_name: datasetConfigs.reranking_model.reranking_model_name,
|
||||
@@ -93,14 +105,73 @@ const ConfigContent: FC<Props> = ({
|
||||
})
|
||||
}
|
||||
|
||||
const handleWeightedScoreChange = (value: { type: WeightedScoreEnum; value: number[] }) => {
|
||||
const configs = {
|
||||
...datasetConfigs,
|
||||
weights: {
|
||||
...datasetConfigs.weights!,
|
||||
weight_type: value.type,
|
||||
vector_setting: {
|
||||
...datasetConfigs.weights!.vector_setting!,
|
||||
vector_weight: value.value[0],
|
||||
},
|
||||
keyword_setting: {
|
||||
keyword_weight: value.value[1],
|
||||
},
|
||||
},
|
||||
}
|
||||
onChange(configs)
|
||||
}
|
||||
|
||||
const handleRerankModeChange = (mode: RerankingModeEnum) => {
|
||||
onChange({
|
||||
...datasetConfigs,
|
||||
reranking_mode: mode,
|
||||
})
|
||||
}
|
||||
|
||||
const model = singleRetrievalConfig
|
||||
|
||||
const rerankingModeOptions = [
|
||||
{
|
||||
value: RerankingModeEnum.WeightedScore,
|
||||
label: t('dataset.weightedScore.title'),
|
||||
tips: t('dataset.weightedScore.description'),
|
||||
},
|
||||
{
|
||||
value: RerankingModeEnum.RerankingModel,
|
||||
label: t('common.modelProvider.rerankModel.key'),
|
||||
tips: t('common.modelProvider.rerankModel.tip'),
|
||||
},
|
||||
]
|
||||
|
||||
const showWeightedScore = selectedDatasetsMode.allHighQuality
|
||||
&& !selectedDatasetsMode.inconsistentEmbeddingModel
|
||||
|
||||
const showWeightedScorePanel = showWeightedScore && datasetConfigs.reranking_mode === RerankingModeEnum.WeightedScore && datasetConfigs.weights
|
||||
const selectedRerankMode = datasetConfigs.reranking_mode || RerankingModeEnum.RerankingModel
|
||||
|
||||
const showRerankModel = useMemo(() => {
|
||||
if (datasetConfigs.reranking_enable === false && selectedDatasetsMode.allEconomic)
|
||||
return false
|
||||
|
||||
return true
|
||||
}, [datasetConfigs.reranking_enable, selectedDatasetsMode.allEconomic])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='system-xl-semibold text-text-primary'>{t('dataset.retrievalSettings')}</div>
|
||||
<div className='mt-2 space-y-3'>
|
||||
<RadioCard
|
||||
icon={<NTo1Retrieval className='shrink-0 mr-3 w-9 h-9 rounded-lg' />}
|
||||
title={t('appDebug.datasetConfig.retrieveOneWay.title')}
|
||||
title={(
|
||||
<div className='flex items-center'>
|
||||
{t('appDebug.datasetConfig.retrieveOneWay.title')}
|
||||
<TooltipPlus popupContent={<div className='w-[320px]'>{t('dataset.nTo1RetrievalLegacy')}</div>}>
|
||||
<div className='ml-1 flex items-center px-[5px] h-[18px] rounded-[5px] border border-text-accent-secondary system-2xs-medium-uppercase text-text-accent-secondary'>legacy</div>
|
||||
</TooltipPlus>
|
||||
</div>
|
||||
)}
|
||||
description={t('appDebug.datasetConfig.retrieveOneWay.description')}
|
||||
isChosen={type === RETRIEVE_TYPE.oneWay}
|
||||
onChosen={() => { setType(RETRIEVE_TYPE.oneWay) }}
|
||||
@@ -115,43 +186,152 @@ const ConfigContent: FC<Props> = ({
|
||||
</div>
|
||||
{type === RETRIEVE_TYPE.multiWay && (
|
||||
<>
|
||||
<div className='mt-6'>
|
||||
<div className='leading-[32px] text-[13px] font-medium text-gray-900'>{t('common.modelProvider.rerankModel.key')}</div>
|
||||
<div>
|
||||
<ModelSelector
|
||||
defaultModel={rerankModel && { provider: rerankModel?.provider_name, model: rerankModel?.model_name }}
|
||||
onSelect={(v) => {
|
||||
onChange({
|
||||
...datasetConfigs,
|
||||
reranking_model: {
|
||||
reranking_provider_name: v.provider,
|
||||
reranking_model_name: v.model,
|
||||
},
|
||||
})
|
||||
}}
|
||||
modelList={rerankModelList}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-4 space-y-4'>
|
||||
<TopKItem
|
||||
value={datasetConfigs.top_k}
|
||||
onChange={handleParamChange}
|
||||
enable={true}
|
||||
/>
|
||||
<ScoreThresholdItem
|
||||
value={datasetConfigs.score_threshold as number}
|
||||
onChange={handleParamChange}
|
||||
enable={datasetConfigs.score_threshold_enabled}
|
||||
hasSwitch={true}
|
||||
onSwitchChange={handleSwitch}
|
||||
/>
|
||||
<div className='mb-2 mt-4 h-[1px] bg-divider-subtle'></div>
|
||||
<div
|
||||
className='flex items-center mb-2 h-6 system-md-semibold text-text-secondary'
|
||||
>
|
||||
{t('dataset.rerankSettings')}
|
||||
</div>
|
||||
{
|
||||
selectedDatasetsMode.inconsistentEmbeddingModel
|
||||
&& (
|
||||
<div className='mt-4 system-xs-regular text-text-warning'>
|
||||
{t('dataset.inconsistentEmbeddingModelTip')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
selectedDatasetsMode.mixtureHighQualityAndEconomic
|
||||
&& (
|
||||
<div className='mt-4 system-xs-regular text-text-warning'>
|
||||
{t('dataset.mixtureHighQualityAndEconomicTip')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
showWeightedScore && (
|
||||
<div className='flex items-center justify-between'>
|
||||
{
|
||||
rerankingModeOptions.map(option => (
|
||||
<div
|
||||
key={option.value}
|
||||
className={cn(
|
||||
'flex items-center justify-center w-[calc((100%-8px)/2)] h-8 rounded-lg border border-components-option-card-option-border bg-components-option-card-option-bg cursor-pointer system-sm-medium text-text-secondary',
|
||||
selectedRerankMode === option.value && 'border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary',
|
||||
)}
|
||||
onClick={() => handleRerankModeChange(option.value)}
|
||||
>
|
||||
<div className='truncate'>{option.label}</div>
|
||||
<TooltipPlus
|
||||
popupContent={<div className='w-[200px]'>{option.tips}</div>}
|
||||
hideArrow
|
||||
>
|
||||
<RiQuestionLine className='ml-0.5 w-3.5 h-4.5 text-text-quaternary' />
|
||||
</TooltipPlus>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!showWeightedScorePanel && (
|
||||
<div className='mt-2'>
|
||||
<div className='flex items-center'>
|
||||
{
|
||||
selectedDatasetsMode.allEconomic && (
|
||||
<Switch
|
||||
size='md'
|
||||
defaultValue={showRerankModel}
|
||||
onChange={(v) => {
|
||||
onChange({
|
||||
...datasetConfigs,
|
||||
reranking_enable: v,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<div className='ml-2 leading-[32px] text-[13px] font-medium text-gray-900'>{t('common.modelProvider.rerankModel.key')}</div>
|
||||
<TooltipPlus popupContent={<div className="w-[200px]">{t('common.modelProvider.rerankModel.tip')}</div>}>
|
||||
<RiQuestionLine className='ml-0.5 w-[14px] h-[14px] text-gray-400' />
|
||||
</TooltipPlus>
|
||||
</div>
|
||||
<div>
|
||||
<ModelSelector
|
||||
defaultModel={rerankModel && { provider: rerankModel?.provider_name, model: rerankModel?.model_name }}
|
||||
onSelect={(v) => {
|
||||
onChange({
|
||||
...datasetConfigs,
|
||||
reranking_model: {
|
||||
reranking_provider_name: v.provider,
|
||||
reranking_model_name: v.model,
|
||||
},
|
||||
})
|
||||
}}
|
||||
modelList={rerankModelList}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
showWeightedScorePanel
|
||||
&& (
|
||||
<div className='mt-2 space-y-4'>
|
||||
<WeightedScore
|
||||
value={{
|
||||
type: datasetConfigs.weights!.weight_type,
|
||||
value: [
|
||||
datasetConfigs.weights!.vector_setting.vector_weight,
|
||||
datasetConfigs.weights!.keyword_setting.keyword_weight,
|
||||
],
|
||||
}}
|
||||
onChange={handleWeightedScoreChange}
|
||||
/>
|
||||
<TopKItem
|
||||
value={datasetConfigs.top_k}
|
||||
onChange={handleParamChange}
|
||||
enable={true}
|
||||
/>
|
||||
<ScoreThresholdItem
|
||||
value={datasetConfigs.score_threshold as number}
|
||||
onChange={handleParamChange}
|
||||
enable={datasetConfigs.score_threshold_enabled}
|
||||
hasSwitch={true}
|
||||
onSwitchChange={handleSwitch}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!showWeightedScorePanel
|
||||
&& (
|
||||
<div className='mt-4 space-y-4'>
|
||||
<TopKItem
|
||||
value={datasetConfigs.top_k}
|
||||
onChange={handleParamChange}
|
||||
enable={true}
|
||||
/>
|
||||
{
|
||||
showRerankModel && (
|
||||
<ScoreThresholdItem
|
||||
value={datasetConfigs.score_threshold as number}
|
||||
onChange={handleParamChange}
|
||||
enable={datasetConfigs.score_threshold_enabled}
|
||||
hasSwitch={true}
|
||||
onSwitchChange={handleSwitch}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)}
|
||||
|
||||
{isInWorkflow && type === RETRIEVE_TYPE.oneWay && (
|
||||
<div className='mt-6'>
|
||||
<div className='mt-4'>
|
||||
<div className='flex items-center space-x-0.5'>
|
||||
<div className='leading-[32px] text-[13px] font-medium text-gray-900'>{t('common.modelProvider.systemReasoningModel.key')}</div>
|
||||
<TooltipPlus
|
||||
@@ -180,4 +360,4 @@ const ConfigContent: FC<Props> = ({
|
||||
</div >
|
||||
)
|
||||
}
|
||||
export default React.memo(ConfigContent)
|
||||
export default memo(ConfigContent)
|
||||
|
||||
@@ -1,29 +1,73 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import { memo, useState } from 'react'
|
||||
import { memo, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { RiEqualizer2Line } from '@remixicon/react'
|
||||
import ConfigContent from './config-content'
|
||||
import cn from '@/utils/classnames'
|
||||
import { Settings04 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { RETRIEVE_TYPE } from '@/types/app'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { DATASET_DEFAULT } from '@/config'
|
||||
import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type { DataSet } from '@/models/datasets'
|
||||
import type { DatasetConfigs } from '@/models/debug'
|
||||
import {
|
||||
getMultipleRetrievalConfig,
|
||||
getSelectedDatasetsMode,
|
||||
} from '@/app/components/workflow/nodes/knowledge-retrieval/utils'
|
||||
|
||||
const ParamsConfig: FC = () => {
|
||||
type ParamsConfigProps = {
|
||||
disabled?: boolean
|
||||
selectedDatasets: DataSet[]
|
||||
}
|
||||
const ParamsConfig = ({
|
||||
disabled,
|
||||
selectedDatasets,
|
||||
}: ParamsConfigProps) => {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
const {
|
||||
datasetConfigs,
|
||||
setDatasetConfigs,
|
||||
rerankSettingModalOpen,
|
||||
setRerankSettingModalOpen,
|
||||
} = useContext(ConfigContext)
|
||||
const [tempDataSetConfigs, setTempDataSetConfigs] = useState(datasetConfigs)
|
||||
|
||||
useEffect(() => {
|
||||
const {
|
||||
allEconomic,
|
||||
} = getSelectedDatasetsMode(selectedDatasets)
|
||||
const { datasets, retrieval_model, score_threshold_enabled, ...restConfigs } = datasetConfigs
|
||||
let rerankEnable = restConfigs.reranking_enable
|
||||
|
||||
if (allEconomic && !restConfigs.reranking_model?.reranking_provider_name && rerankEnable === undefined)
|
||||
rerankEnable = false
|
||||
|
||||
setTempDataSetConfigs({
|
||||
...getMultipleRetrievalConfig({
|
||||
top_k: restConfigs.top_k,
|
||||
score_threshold: restConfigs.score_threshold,
|
||||
reranking_model: restConfigs.reranking_model && {
|
||||
provider: restConfigs.reranking_model.reranking_provider_name,
|
||||
model: restConfigs.reranking_model.reranking_model_name,
|
||||
},
|
||||
reranking_mode: restConfigs.reranking_mode,
|
||||
weights: restConfigs.weights,
|
||||
reranking_enable: rerankEnable,
|
||||
}, selectedDatasets),
|
||||
reranking_model: restConfigs.reranking_model && {
|
||||
reranking_provider_name: restConfigs.reranking_model.reranking_provider_name,
|
||||
reranking_model_name: restConfigs.reranking_model.reranking_model_name,
|
||||
},
|
||||
retrieval_model,
|
||||
score_threshold_enabled,
|
||||
datasets,
|
||||
})
|
||||
}, [selectedDatasets, datasetConfigs])
|
||||
|
||||
const {
|
||||
defaultModel: rerankDefaultModel,
|
||||
currentModel: isRerankDefaultModelVaild,
|
||||
@@ -55,45 +99,68 @@ const ParamsConfig: FC = () => {
|
||||
} as any
|
||||
}
|
||||
setDatasetConfigs(config)
|
||||
setOpen(false)
|
||||
setRerankSettingModalOpen(false)
|
||||
}
|
||||
|
||||
const handleSetTempDataSetConfigs = (newDatasetConfigs: DatasetConfigs) => {
|
||||
const { datasets, retrieval_model, score_threshold_enabled, ...restConfigs } = newDatasetConfigs
|
||||
|
||||
const retrievalConfig = getMultipleRetrievalConfig({
|
||||
top_k: restConfigs.top_k,
|
||||
score_threshold: restConfigs.score_threshold,
|
||||
reranking_model: restConfigs.reranking_model && {
|
||||
provider: restConfigs.reranking_model.reranking_provider_name,
|
||||
model: restConfigs.reranking_model.reranking_model_name,
|
||||
},
|
||||
reranking_mode: restConfigs.reranking_mode,
|
||||
weights: restConfigs.weights,
|
||||
reranking_enable: restConfigs.reranking_enable,
|
||||
}, selectedDatasets)
|
||||
|
||||
setTempDataSetConfigs({
|
||||
...retrievalConfig,
|
||||
reranking_model: restConfigs.reranking_model && {
|
||||
reranking_provider_name: restConfigs.reranking_model.reranking_provider_name,
|
||||
reranking_model_name: restConfigs.reranking_model.reranking_model_name,
|
||||
},
|
||||
retrieval_model,
|
||||
score_threshold_enabled,
|
||||
datasets,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className={cn('flex items-center rounded-md h-7 px-3 space-x-1 text-gray-700 cursor-pointer hover:bg-gray-200', open && 'bg-gray-200')}
|
||||
<Button
|
||||
variant='ghost'
|
||||
size='small'
|
||||
className={cn('h-7', rerankSettingModalOpen && 'bg-components-button-ghost-bg-hover')}
|
||||
onClick={() => {
|
||||
setTempDataSetConfigs({
|
||||
...datasetConfigs,
|
||||
top_k: datasetConfigs.top_k || DATASET_DEFAULT.top_k,
|
||||
score_threshold: datasetConfigs.score_threshold || DATASET_DEFAULT.score_threshold,
|
||||
})
|
||||
setOpen(true)
|
||||
setRerankSettingModalOpen(true)
|
||||
}}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Settings04 className="w-[14px] h-[14px]" />
|
||||
<div className='text-xs font-medium'>
|
||||
{t('appDebug.datasetConfig.params')}
|
||||
</div>
|
||||
</div>
|
||||
<RiEqualizer2Line className='mr-1 w-3.5 h-3.5' />
|
||||
{t('dataset.retrievalSettings')}
|
||||
</Button>
|
||||
{
|
||||
open && (
|
||||
rerankSettingModalOpen && (
|
||||
<Modal
|
||||
isShow={open}
|
||||
isShow={rerankSettingModalOpen}
|
||||
onClose={() => {
|
||||
setOpen(false)
|
||||
setRerankSettingModalOpen(false)
|
||||
}}
|
||||
className='sm:min-w-[528px]'
|
||||
title={t('appDebug.datasetConfig.settingTitle')}
|
||||
>
|
||||
<ConfigContent
|
||||
datasetConfigs={tempDataSetConfigs}
|
||||
onChange={setTempDataSetConfigs}
|
||||
onChange={handleSetTempDataSetConfigs}
|
||||
selectedDatasets={selectedDatasets}
|
||||
/>
|
||||
|
||||
<div className='mt-6 flex justify-end'>
|
||||
<Button className='mr-2 flex-shrink-0' onClick={() => {
|
||||
setOpen(false)
|
||||
setRerankSettingModalOpen(false)
|
||||
}}>{t('common.operation.cancel')}</Button>
|
||||
<Button variant='primary' className='flex-shrink-0' onClick={handleSave} >{t('common.operation.save')}</Button>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import { memo, useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
DEFAULT_WEIGHTED_SCORE,
|
||||
WeightedScoreEnum,
|
||||
} from '@/models/datasets'
|
||||
import Slider from '@/app/components/base/slider'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
const formatNumber = (value: number) => {
|
||||
if (value > 0 && value < 1)
|
||||
return `0.${value * 10}`
|
||||
else if (value === 1)
|
||||
return '1.0'
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
type Value = {
|
||||
type: WeightedScoreEnum
|
||||
value: number[]
|
||||
}
|
||||
|
||||
type WeightedScoreProps = {
|
||||
value: Value
|
||||
onChange: (value: Value) => void
|
||||
}
|
||||
const WeightedScore = ({
|
||||
value,
|
||||
onChange = () => {},
|
||||
}: WeightedScoreProps) => {
|
||||
const { t } = useTranslation()
|
||||
const options = [
|
||||
{
|
||||
value: WeightedScoreEnum.SemanticFirst,
|
||||
label: t('dataset.weightedScore.semanticFirst'),
|
||||
},
|
||||
{
|
||||
value: WeightedScoreEnum.KeywordFirst,
|
||||
label: t('dataset.weightedScore.keywordFirst'),
|
||||
},
|
||||
{
|
||||
value: WeightedScoreEnum.Customized,
|
||||
label: t('dataset.weightedScore.customized'),
|
||||
},
|
||||
]
|
||||
|
||||
const disabled = value.type !== WeightedScoreEnum.Customized
|
||||
|
||||
const handleTypeChange = useCallback((type: WeightedScoreEnum) => {
|
||||
const result = { ...value, type }
|
||||
|
||||
if (type === WeightedScoreEnum.SemanticFirst)
|
||||
result.value = [DEFAULT_WEIGHTED_SCORE.semanticFirst.semantic, DEFAULT_WEIGHTED_SCORE.semanticFirst.keyword]
|
||||
|
||||
if (type === WeightedScoreEnum.KeywordFirst)
|
||||
result.value = [DEFAULT_WEIGHTED_SCORE.keywordFirst.semantic, DEFAULT_WEIGHTED_SCORE.keywordFirst.keyword]
|
||||
|
||||
onChange(result)
|
||||
}, [value, onChange])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='flex items-center mb-1 space-x-4'>
|
||||
{
|
||||
options.map(option => (
|
||||
<div
|
||||
key={option.value}
|
||||
className='flex py-1.5 max-w-[calc((100%-32px)/3)] system-sm-regular text-text-secondary cursor-pointer'
|
||||
onClick={() => handleTypeChange(option.value)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'shrink-0 mr-2 w-4 h-4 bg-components-radio-bg border border-components-radio-border rounded-full shadow-xs',
|
||||
value.type === option.value && 'border-[5px] border-components-radio-border-checked',
|
||||
)}
|
||||
></div>
|
||||
<div className='truncate' title={option.label}>{option.label}</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<div className='flex items-center px-3 h-9 space-x-3 rounded-lg border border-components-panel-border'>
|
||||
<div className='shrink-0 flex items-center w-[90px] system-xs-semibold-uppercase text-util-colors-blue-blue-500'>
|
||||
<div className='mr-1 truncate uppercase' title={t('dataset.weightedScore.semantic') || ''}>
|
||||
{t('dataset.weightedScore.semantic')}
|
||||
</div>
|
||||
{formatNumber(value.value[0])}
|
||||
</div>
|
||||
<Slider
|
||||
className={cn('grow h-0.5 bg-gradient-to-r from-[#53B1FD] to-[#2ED3B7]', disabled && 'cursor-not-allowed')}
|
||||
max={1.0}
|
||||
min={0}
|
||||
step={0.1}
|
||||
value={value.value[0]}
|
||||
onChange={v => onChange({ type: value.type, value: [v, (10 - v * 10) / 10] })}
|
||||
disabled={disabled}
|
||||
thumbClassName={cn(disabled && '!cursor-not-allowed')}
|
||||
trackClassName='!bg-transparent'
|
||||
/>
|
||||
<div className='shrink-0 flex items-center justify-end w-[90px] system-xs-semibold-uppercase text-util-colors-cyan-cyan-500'>
|
||||
{formatNumber(value.value[1])}
|
||||
<div className='ml-1 truncate uppercase' title={t('dataset.weightedScore.keyword') || ''}>
|
||||
{t('dataset.weightedScore.keyword')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(WeightedScore)
|
||||
@@ -13,7 +13,8 @@ import type { DataSet } from '@/models/datasets'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { fetchDatasets } from '@/service/datasets'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
import Badge from '@/app/components/base/badge'
|
||||
import { useKnowledge } from '@/hooks/use-knowledge'
|
||||
|
||||
export type ISelectDataSetProps = {
|
||||
isShow: boolean
|
||||
@@ -38,6 +39,7 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
|
||||
const listRef = useRef<HTMLDivElement>(null)
|
||||
const [page, setPage, getPage] = useGetState(1)
|
||||
const [isNoMore, setIsNoMore] = useState(false)
|
||||
const { formatIndexingTechniqueAndMethod } = useKnowledge()
|
||||
|
||||
useInfiniteScroll(
|
||||
async () => {
|
||||
@@ -45,7 +47,7 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
|
||||
const { data, has_more } = await fetchDatasets({ url: '/datasets', params: { page } })
|
||||
setPage(getPage() + 1)
|
||||
setIsNoMore(!has_more)
|
||||
const newList = [...(datasets || []), ...data]
|
||||
const newList = [...(datasets || []), ...data.filter(item => item.indexing_technique)]
|
||||
setDataSets(newList)
|
||||
setLoaded(true)
|
||||
if (!selected.find(item => !item.name))
|
||||
@@ -136,14 +138,13 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
|
||||
<span className='ml-1 shrink-0 px-1 border boder-gray-200 rounded-md text-gray-500 text-xs font-normal leading-[18px]'>{t('dataset.unavailable')}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={cn('shrink-0 flex text-xs text-gray-500 overflow-hidden whitespace-nowrap', !item.embedding_available && 'opacity-50')}>
|
||||
<span className='max-w-[100px] overflow-hidden text-ellipsis whitespace-nowrap'>{formatNumber(item.word_count)}</span>
|
||||
{t('appDebug.feature.dataSet.words')}
|
||||
<span className='px-0.5'>·</span>
|
||||
<span className='max-w-[100px] min-w-[8px] overflow-hidden text-ellipsis whitespace-nowrap'>{formatNumber(item.document_count)} </span>
|
||||
{t('appDebug.feature.dataSet.textBlocks')}
|
||||
</div>
|
||||
{
|
||||
item.indexing_technique && (
|
||||
<Badge
|
||||
text={formatIndexingTechniqueAndMethod(item.indexing_technique, item.retrieval_model_dict?.search_method)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -259,7 +259,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
||||
|
||||
{/* Retrieval Method Config */}
|
||||
<div className={rowClass}>
|
||||
<div className={labelClass}>
|
||||
<div className={cn(labelClass, 'w-auto min-w-[168px]')}>
|
||||
<div>
|
||||
<div>{t('datasetSettings.form.retrievalSetting.title')}</div>
|
||||
<div className='leading-[18px] text-xs font-normal text-gray-500'>
|
||||
@@ -268,7 +268,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-[480px]'>
|
||||
<div>
|
||||
{indexMethod === 'high_quality'
|
||||
? (
|
||||
<RetrievalMethodConfig
|
||||
|
||||
@@ -46,7 +46,7 @@ import { fetchDatasets } from '@/service/datasets'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { AgentStrategy, AppType, ModelModeType, RETRIEVE_TYPE, Resolution, TransferMethod } from '@/types/app'
|
||||
import { PromptMode } from '@/models/debug'
|
||||
import { ANNOTATION_DEFAULT, DEFAULT_AGENT_SETTING, DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config'
|
||||
import { ANNOTATION_DEFAULT, DATASET_DEFAULT, DEFAULT_AGENT_SETTING, DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config'
|
||||
import SelectDataSet from '@/app/components/app/configuration/dataset-config/select-dataset'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
@@ -57,6 +57,10 @@ import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/comp
|
||||
import { fetchCollectionList } from '@/service/tools'
|
||||
import { type Collection } from '@/app/components/tools/types'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import {
|
||||
getMultipleRetrievalConfig,
|
||||
getSelectedDatasetsMode,
|
||||
} from '@/app/components/workflow/nodes/knowledge-retrieval/utils'
|
||||
|
||||
type PublishConfig = {
|
||||
modelConfig: ModelConfig
|
||||
@@ -174,14 +178,14 @@ const Configuration: FC = () => {
|
||||
|
||||
}, [])
|
||||
const [datasetConfigs, setDatasetConfigs] = useState<DatasetConfigs>({
|
||||
retrieval_model: RETRIEVE_TYPE.oneWay,
|
||||
retrieval_model: RETRIEVE_TYPE.multiWay,
|
||||
reranking_model: {
|
||||
reranking_provider_name: '',
|
||||
reranking_model_name: '',
|
||||
},
|
||||
top_k: 2,
|
||||
top_k: DATASET_DEFAULT.top_k,
|
||||
score_threshold_enabled: false,
|
||||
score_threshold: 0.7,
|
||||
score_threshold: DATASET_DEFAULT.score_threshold,
|
||||
datasets: {
|
||||
datasets: [],
|
||||
},
|
||||
@@ -202,6 +206,7 @@ const Configuration: FC = () => {
|
||||
const hasSetContextVar = !!contextVar
|
||||
const [isShowSelectDataSet, { setTrue: showSelectDataSet, setFalse: hideSelectDataSet }] = useBoolean(false)
|
||||
const selectedIds = dataSets.map(item => item.id)
|
||||
const [rerankSettingModalOpen, setRerankSettingModalOpen] = useState(false)
|
||||
const handleSelect = (data: DataSet[]) => {
|
||||
if (isEqual(data.map(item => item.id), dataSets.map(item => item.id))) {
|
||||
hideSelectDataSet()
|
||||
@@ -209,6 +214,7 @@ const Configuration: FC = () => {
|
||||
}
|
||||
|
||||
formattingChangedDispatcher()
|
||||
let newDatasets = data
|
||||
if (data.find(item => !item.name)) { // has not loaded selected dataset
|
||||
const newSelected = produce(data, (draft: any) => {
|
||||
data.forEach((item, index) => {
|
||||
@@ -220,11 +226,45 @@ const Configuration: FC = () => {
|
||||
})
|
||||
})
|
||||
setDataSets(newSelected)
|
||||
newDatasets = newSelected
|
||||
}
|
||||
else {
|
||||
setDataSets(data)
|
||||
}
|
||||
hideSelectDataSet()
|
||||
const {
|
||||
allEconomic,
|
||||
mixtureHighQualityAndEconomic,
|
||||
inconsistentEmbeddingModel,
|
||||
} = getSelectedDatasetsMode(newDatasets)
|
||||
|
||||
if (allEconomic || mixtureHighQualityAndEconomic || inconsistentEmbeddingModel)
|
||||
setRerankSettingModalOpen(true)
|
||||
|
||||
const { datasets, retrieval_model, score_threshold_enabled, ...restConfigs } = datasetConfigs
|
||||
|
||||
const retrievalConfig = getMultipleRetrievalConfig({
|
||||
top_k: restConfigs.top_k,
|
||||
score_threshold: restConfigs.score_threshold,
|
||||
reranking_model: restConfigs.reranking_model && {
|
||||
provider: restConfigs.reranking_model.reranking_provider_name,
|
||||
model: restConfigs.reranking_model.reranking_model_name,
|
||||
},
|
||||
reranking_mode: restConfigs.reranking_mode,
|
||||
weights: restConfigs.weights,
|
||||
reranking_enable: restConfigs.reranking_enable,
|
||||
}, newDatasets)
|
||||
|
||||
setDatasetConfigs({
|
||||
...retrievalConfig,
|
||||
reranking_model: restConfigs.reranking_model && {
|
||||
reranking_provider_name: restConfigs.reranking_model.reranking_provider_name,
|
||||
reranking_model_name: restConfigs.reranking_model.reranking_model_name,
|
||||
},
|
||||
retrieval_model,
|
||||
score_threshold_enabled,
|
||||
datasets,
|
||||
})
|
||||
}
|
||||
|
||||
const [isShowHistoryModal, { setTrue: showHistoryModal, setFalse: hideHistoryModal }] = useBoolean(false)
|
||||
@@ -509,7 +549,7 @@ const Configuration: FC = () => {
|
||||
syncToPublishedConfig(config)
|
||||
setPublishedConfig(config)
|
||||
setDatasetConfigs({
|
||||
retrieval_model: RETRIEVE_TYPE.oneWay,
|
||||
retrieval_model: RETRIEVE_TYPE.multiWay,
|
||||
...modelConfig.dataset_configs,
|
||||
})
|
||||
setHasFetchedDetail(true)
|
||||
@@ -744,6 +784,8 @@ const Configuration: FC = () => {
|
||||
isShowVisionConfig,
|
||||
visionConfig,
|
||||
setVisionConfig: handleSetVisionConfig,
|
||||
rerankSettingModalOpen,
|
||||
setRerankSettingModalOpen,
|
||||
}}
|
||||
>
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user