mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-14 12:36:00 +08:00
feat: n to 1 retrieval legacy (#6554)
This commit is contained in:
parent
e4bb943fe5
commit
6fe9aa69cc
@ -1,18 +1,20 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import {
|
||||||
import { RiDeleteBinLine } from '@remixicon/react'
|
RiDeleteBinLine,
|
||||||
|
RiEditLine,
|
||||||
|
} from '@remixicon/react'
|
||||||
import SettingsModal from '../settings-modal'
|
import SettingsModal from '../settings-modal'
|
||||||
import type { DataSet } from '@/models/datasets'
|
import type { DataSet } from '@/models/datasets'
|
||||||
import { DataSourceType } from '@/models/datasets'
|
import { DataSourceType } from '@/models/datasets'
|
||||||
import { formatNumber } from '@/utils/format'
|
|
||||||
import FileIcon from '@/app/components/base/file-icon'
|
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 { Folder } from '@/app/components/base/icons/src/vender/solid/files'
|
||||||
import { Globe06 } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
|
import { Globe06 } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
|
||||||
import Drawer from '@/app/components/base/drawer'
|
import Drawer from '@/app/components/base/drawer'
|
||||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
|
import Badge from '@/app/components/base/badge'
|
||||||
|
import { useKnowledge } from '@/hooks/use-knowledge'
|
||||||
|
|
||||||
type ItemProps = {
|
type ItemProps = {
|
||||||
className?: string
|
className?: string
|
||||||
@ -27,12 +29,10 @@ const Item: FC<ItemProps> = ({
|
|||||||
onSave,
|
onSave,
|
||||||
onRemove,
|
onRemove,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
const media = useBreakpoints()
|
const media = useBreakpoints()
|
||||||
const isMobile = media === MediaType.mobile
|
const isMobile = media === MediaType.mobile
|
||||||
|
|
||||||
const [showSettingsModal, setShowSettingsModal] = useState(false)
|
const [showSettingsModal, setShowSettingsModal] = useState(false)
|
||||||
|
const { formatIndexingTechniqueAndMethod } = useKnowledge()
|
||||||
|
|
||||||
const handleSave = (newDataset: DataSet) => {
|
const handleSave = (newDataset: DataSet) => {
|
||||||
onSave(newDataset)
|
onSave(newDataset)
|
||||||
@ -65,22 +65,17 @@ const Item: FC<ItemProps> = ({
|
|||||||
<div className='grow'>
|
<div className='grow'>
|
||||||
<div className='flex items-center h-[18px]'>
|
<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='grow text-[13px] font-medium text-gray-800 truncate' title={config.name}>{config.name}</div>
|
||||||
<div className='shrink-0 text-xs text-gray-500'>
|
<Badge
|
||||||
{formatNumber(config.word_count)} {t('appDebug.feature.dataSet.words')} · {formatNumber(config.document_count)} {t('appDebug.feature.dataSet.textBlocks')}
|
text={formatIndexingTechniqueAndMethod(config.indexing_technique, config.retrieval_model_dict?.search_method)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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='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
|
<div
|
||||||
className='flex items-center justify-center mr-1 w-6 h-6 hover:bg-black/5 rounded-md cursor-pointer'
|
className='flex items-center justify-center mr-1 w-6 h-6 hover:bg-black/5 rounded-md cursor-pointer'
|
||||||
onClick={() => setShowSettingsModal(true)}
|
onClick={() => setShowSettingsModal(true)}
|
||||||
>
|
>
|
||||||
<Settings01 className='w-4 h-4 text-gray-500' />
|
<RiEditLine className='w-4 h-4 text-gray-500' />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className='group/action flex items-center justify-center w-6 h-6 hover:bg-[#FEE4E2] rounded-md cursor-pointer'
|
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 handleSave = (newDataset: DataSet) => {
|
||||||
const index = dataSet.findIndex(item => item.id === newDataset.id)
|
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()
|
formattingChangedDispatcher()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ const DatasetConfig: FC = () => {
|
|||||||
title={t('appDebug.feature.dataSet.title')}
|
title={t('appDebug.feature.dataSet.title')}
|
||||||
headerRight={
|
headerRight={
|
||||||
<div className='flex items-center gap-1'>
|
<div className='flex items-center gap-1'>
|
||||||
{!isAgent && <ParamsConfig />}
|
{!isAgent && <ParamsConfig disabled={!hasData} selectedDatasets={dataSet} />}
|
||||||
<OperationBtn type="add" onClick={showSelectDataSet} />
|
<OperationBtn type="add" onClick={showSelectDataSet} />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React from 'react'
|
|
||||||
|
import { memo, useMemo } from 'react'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import {
|
import {
|
||||||
RiQuestionLine,
|
RiQuestionLine,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
|
import WeightedScore from './weighted-score'
|
||||||
import TopKItem from '@/app/components/base/param-item/top-k-item'
|
import TopKItem from '@/app/components/base/param-item/top-k-item'
|
||||||
import ScoreThresholdItem from '@/app/components/base/param-item/score-threshold-item'
|
import ScoreThresholdItem from '@/app/components/base/param-item/score-threshold-item'
|
||||||
import RadioCard from '@/app/components/base/radio-card/simple'
|
import RadioCard from '@/app/components/base/radio-card/simple'
|
||||||
@ -16,13 +18,20 @@ import {
|
|||||||
import type {
|
import type {
|
||||||
DatasetConfigs,
|
DatasetConfigs,
|
||||||
} from '@/models/debug'
|
} from '@/models/debug'
|
||||||
|
|
||||||
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
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 { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||||
import type { ModelConfig } from '@/app/components/workflow/types'
|
import type { ModelConfig } from '@/app/components/workflow/types'
|
||||||
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
|
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
|
||||||
import TooltipPlus from '@/app/components/base/tooltip-plus'
|
import TooltipPlus from '@/app/components/base/tooltip-plus'
|
||||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
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 = {
|
type Props = {
|
||||||
datasetConfigs: DatasetConfigs
|
datasetConfigs: DatasetConfigs
|
||||||
@ -31,6 +40,7 @@ type Props = {
|
|||||||
singleRetrievalModelConfig?: ModelConfig
|
singleRetrievalModelConfig?: ModelConfig
|
||||||
onSingleRetrievalModelChange?: (config: ModelConfig) => void
|
onSingleRetrievalModelChange?: (config: ModelConfig) => void
|
||||||
onSingleRetrievalModelParamsChange?: (config: ModelConfig) => void
|
onSingleRetrievalModelParamsChange?: (config: ModelConfig) => void
|
||||||
|
selectedDatasets?: DataSet[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConfigContent: FC<Props> = ({
|
const ConfigContent: FC<Props> = ({
|
||||||
@ -40,8 +50,10 @@ const ConfigContent: FC<Props> = ({
|
|||||||
singleRetrievalModelConfig: singleRetrievalConfig = {} as ModelConfig,
|
singleRetrievalModelConfig: singleRetrievalConfig = {} as ModelConfig,
|
||||||
onSingleRetrievalModelChange = () => { },
|
onSingleRetrievalModelChange = () => { },
|
||||||
onSingleRetrievalModelParamsChange = () => { },
|
onSingleRetrievalModelParamsChange = () => { },
|
||||||
|
selectedDatasets = [],
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const selectedDatasetsMode = useSelectedDatasetsMode(selectedDatasets)
|
||||||
const type = datasetConfigs.retrieval_model
|
const type = datasetConfigs.retrieval_model
|
||||||
const setType = (value: RETRIEVE_TYPE) => {
|
const setType = (value: RETRIEVE_TYPE) => {
|
||||||
onChange({
|
onChange({
|
||||||
@ -54,7 +66,7 @@ const ConfigContent: FC<Props> = ({
|
|||||||
defaultModel: rerankDefaultModel,
|
defaultModel: rerankDefaultModel,
|
||||||
} = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.rerank)
|
} = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.rerank)
|
||||||
const rerankModel = (() => {
|
const rerankModel = (() => {
|
||||||
if (datasetConfigs.reranking_model) {
|
if (datasetConfigs.reranking_model?.reranking_provider_name) {
|
||||||
return {
|
return {
|
||||||
provider_name: datasetConfigs.reranking_model.reranking_provider_name,
|
provider_name: datasetConfigs.reranking_model.reranking_provider_name,
|
||||||
model_name: datasetConfigs.reranking_model.reranking_model_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 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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<div className='system-xl-semibold text-text-primary'>{t('dataset.retrievalSettings')}</div>
|
||||||
<div className='mt-2 space-y-3'>
|
<div className='mt-2 space-y-3'>
|
||||||
<RadioCard
|
<RadioCard
|
||||||
icon={<NTo1Retrieval className='shrink-0 mr-3 w-9 h-9 rounded-lg' />}
|
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')}
|
description={t('appDebug.datasetConfig.retrieveOneWay.description')}
|
||||||
isChosen={type === RETRIEVE_TYPE.oneWay}
|
isChosen={type === RETRIEVE_TYPE.oneWay}
|
||||||
onChosen={() => { setType(RETRIEVE_TYPE.oneWay) }}
|
onChosen={() => { setType(RETRIEVE_TYPE.oneWay) }}
|
||||||
@ -115,8 +186,77 @@ const ConfigContent: FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
{type === RETRIEVE_TYPE.multiWay && (
|
{type === RETRIEVE_TYPE.multiWay && (
|
||||||
<>
|
<>
|
||||||
<div className='mt-6'>
|
<div className='mb-2 mt-4 h-[1px] bg-divider-subtle'></div>
|
||||||
<div className='leading-[32px] text-[13px] font-medium text-gray-900'>{t('common.modelProvider.rerankModel.key')}</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>
|
<div>
|
||||||
<ModelSelector
|
<ModelSelector
|
||||||
defaultModel={rerankModel && { provider: rerankModel?.provider_name, model: rerankModel?.model_name }}
|
defaultModel={rerankModel && { provider: rerankModel?.provider_name, model: rerankModel?.model_name }}
|
||||||
@ -133,7 +273,22 @@ const ConfigContent: FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-4 space-y-4'>
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
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
|
<TopKItem
|
||||||
value={datasetConfigs.top_k}
|
value={datasetConfigs.top_k}
|
||||||
onChange={handleParamChange}
|
onChange={handleParamChange}
|
||||||
@ -147,11 +302,36 @@ const ConfigContent: FC<Props> = ({
|
|||||||
onSwitchChange={handleSwitch}
|
onSwitchChange={handleSwitch}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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 && (
|
{isInWorkflow && type === RETRIEVE_TYPE.oneWay && (
|
||||||
<div className='mt-6'>
|
<div className='mt-4'>
|
||||||
<div className='flex items-center space-x-0.5'>
|
<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>
|
<div className='leading-[32px] text-[13px] font-medium text-gray-900'>{t('common.modelProvider.systemReasoningModel.key')}</div>
|
||||||
<TooltipPlus
|
<TooltipPlus
|
||||||
@ -180,4 +360,4 @@ const ConfigContent: FC<Props> = ({
|
|||||||
</div >
|
</div >
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default React.memo(ConfigContent)
|
export default memo(ConfigContent)
|
||||||
|
@ -1,29 +1,73 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { FC } from 'react'
|
import { memo, useEffect, useState } from 'react'
|
||||||
import { memo, useState } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
|
import { RiEqualizer2Line } from '@remixicon/react'
|
||||||
import ConfigContent from './config-content'
|
import ConfigContent from './config-content'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import { Settings04 } from '@/app/components/base/icons/src/vender/line/general'
|
|
||||||
import ConfigContext from '@/context/debug-configuration'
|
import ConfigContext from '@/context/debug-configuration'
|
||||||
import Modal from '@/app/components/base/modal'
|
import Modal from '@/app/components/base/modal'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import { RETRIEVE_TYPE } from '@/types/app'
|
import { RETRIEVE_TYPE } from '@/types/app'
|
||||||
import Toast from '@/app/components/base/toast'
|
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 { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
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 { t } = useTranslation()
|
||||||
const [open, setOpen] = useState(false)
|
|
||||||
const {
|
const {
|
||||||
datasetConfigs,
|
datasetConfigs,
|
||||||
setDatasetConfigs,
|
setDatasetConfigs,
|
||||||
|
rerankSettingModalOpen,
|
||||||
|
setRerankSettingModalOpen,
|
||||||
} = useContext(ConfigContext)
|
} = useContext(ConfigContext)
|
||||||
const [tempDataSetConfigs, setTempDataSetConfigs] = useState(datasetConfigs)
|
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 {
|
const {
|
||||||
defaultModel: rerankDefaultModel,
|
defaultModel: rerankDefaultModel,
|
||||||
currentModel: isRerankDefaultModelVaild,
|
currentModel: isRerankDefaultModelVaild,
|
||||||
@ -55,45 +99,68 @@ const ParamsConfig: FC = () => {
|
|||||||
} as any
|
} as any
|
||||||
}
|
}
|
||||||
setDatasetConfigs(config)
|
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div
|
<Button
|
||||||
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')}
|
variant='ghost'
|
||||||
|
size='small'
|
||||||
|
className={cn('h-7', rerankSettingModalOpen && 'bg-components-button-ghost-bg-hover')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setTempDataSetConfigs({
|
setRerankSettingModalOpen(true)
|
||||||
...datasetConfigs,
|
|
||||||
top_k: datasetConfigs.top_k || DATASET_DEFAULT.top_k,
|
|
||||||
score_threshold: datasetConfigs.score_threshold || DATASET_DEFAULT.score_threshold,
|
|
||||||
})
|
|
||||||
setOpen(true)
|
|
||||||
}}
|
}}
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<Settings04 className="w-[14px] h-[14px]" />
|
<RiEqualizer2Line className='mr-1 w-3.5 h-3.5' />
|
||||||
<div className='text-xs font-medium'>
|
{t('dataset.retrievalSettings')}
|
||||||
{t('appDebug.datasetConfig.params')}
|
</Button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{
|
{
|
||||||
open && (
|
rerankSettingModalOpen && (
|
||||||
<Modal
|
<Modal
|
||||||
isShow={open}
|
isShow={rerankSettingModalOpen}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setOpen(false)
|
setRerankSettingModalOpen(false)
|
||||||
}}
|
}}
|
||||||
className='sm:min-w-[528px]'
|
className='sm:min-w-[528px]'
|
||||||
title={t('appDebug.datasetConfig.settingTitle')}
|
|
||||||
>
|
>
|
||||||
<ConfigContent
|
<ConfigContent
|
||||||
datasetConfigs={tempDataSetConfigs}
|
datasetConfigs={tempDataSetConfigs}
|
||||||
onChange={setTempDataSetConfigs}
|
onChange={handleSetTempDataSetConfigs}
|
||||||
|
selectedDatasets={selectedDatasets}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='mt-6 flex justify-end'>
|
<div className='mt-6 flex justify-end'>
|
||||||
<Button className='mr-2 flex-shrink-0' onClick={() => {
|
<Button className='mr-2 flex-shrink-0' onClick={() => {
|
||||||
setOpen(false)
|
setRerankSettingModalOpen(false)
|
||||||
}}>{t('common.operation.cancel')}</Button>
|
}}>{t('common.operation.cancel')}</Button>
|
||||||
<Button variant='primary' className='flex-shrink-0' onClick={handleSave} >{t('common.operation.save')}</Button>
|
<Button variant='primary' className='flex-shrink-0' onClick={handleSave} >{t('common.operation.save')}</Button>
|
||||||
</div>
|
</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 Button from '@/app/components/base/button'
|
||||||
import { fetchDatasets } from '@/service/datasets'
|
import { fetchDatasets } from '@/service/datasets'
|
||||||
import Loading from '@/app/components/base/loading'
|
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 = {
|
export type ISelectDataSetProps = {
|
||||||
isShow: boolean
|
isShow: boolean
|
||||||
@ -38,6 +39,7 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
|
|||||||
const listRef = useRef<HTMLDivElement>(null)
|
const listRef = useRef<HTMLDivElement>(null)
|
||||||
const [page, setPage, getPage] = useGetState(1)
|
const [page, setPage, getPage] = useGetState(1)
|
||||||
const [isNoMore, setIsNoMore] = useState(false)
|
const [isNoMore, setIsNoMore] = useState(false)
|
||||||
|
const { formatIndexingTechniqueAndMethod } = useKnowledge()
|
||||||
|
|
||||||
useInfiniteScroll(
|
useInfiniteScroll(
|
||||||
async () => {
|
async () => {
|
||||||
@ -45,7 +47,7 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
|
|||||||
const { data, has_more } = await fetchDatasets({ url: '/datasets', params: { page } })
|
const { data, has_more } = await fetchDatasets({ url: '/datasets', params: { page } })
|
||||||
setPage(getPage() + 1)
|
setPage(getPage() + 1)
|
||||||
setIsNoMore(!has_more)
|
setIsNoMore(!has_more)
|
||||||
const newList = [...(datasets || []), ...data]
|
const newList = [...(datasets || []), ...data.filter(item => item.indexing_technique)]
|
||||||
setDataSets(newList)
|
setDataSets(newList)
|
||||||
setLoaded(true)
|
setLoaded(true)
|
||||||
if (!selected.find(item => !item.name))
|
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>
|
<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>
|
||||||
|
{
|
||||||
<div className={cn('shrink-0 flex text-xs text-gray-500 overflow-hidden whitespace-nowrap', !item.embedding_available && 'opacity-50')}>
|
item.indexing_technique && (
|
||||||
<span className='max-w-[100px] overflow-hidden text-ellipsis whitespace-nowrap'>{formatNumber(item.word_count)}</span>
|
<Badge
|
||||||
{t('appDebug.feature.dataSet.words')}
|
text={formatIndexingTechniqueAndMethod(item.indexing_technique, item.retrieval_model_dict?.search_method)}
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -259,7 +259,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
|||||||
|
|
||||||
{/* Retrieval Method Config */}
|
{/* Retrieval Method Config */}
|
||||||
<div className={rowClass}>
|
<div className={rowClass}>
|
||||||
<div className={labelClass}>
|
<div className={cn(labelClass, 'w-auto min-w-[168px]')}>
|
||||||
<div>
|
<div>
|
||||||
<div>{t('datasetSettings.form.retrievalSetting.title')}</div>
|
<div>{t('datasetSettings.form.retrievalSetting.title')}</div>
|
||||||
<div className='leading-[18px] text-xs font-normal text-gray-500'>
|
<div className='leading-[18px] text-xs font-normal text-gray-500'>
|
||||||
@ -268,7 +268,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='w-[480px]'>
|
<div>
|
||||||
{indexMethod === 'high_quality'
|
{indexMethod === 'high_quality'
|
||||||
? (
|
? (
|
||||||
<RetrievalMethodConfig
|
<RetrievalMethodConfig
|
||||||
|
@ -46,7 +46,7 @@ import { fetchDatasets } from '@/service/datasets'
|
|||||||
import { useProviderContext } from '@/context/provider-context'
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
import { AgentStrategy, AppType, ModelModeType, RETRIEVE_TYPE, Resolution, TransferMethod } from '@/types/app'
|
import { AgentStrategy, AppType, ModelModeType, RETRIEVE_TYPE, Resolution, TransferMethod } from '@/types/app'
|
||||||
import { PromptMode } from '@/models/debug'
|
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 SelectDataSet from '@/app/components/app/configuration/dataset-config/select-dataset'
|
||||||
import { useModalContext } from '@/context/modal-context'
|
import { useModalContext } from '@/context/modal-context'
|
||||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
@ -57,6 +57,10 @@ import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/comp
|
|||||||
import { fetchCollectionList } from '@/service/tools'
|
import { fetchCollectionList } from '@/service/tools'
|
||||||
import { type Collection } from '@/app/components/tools/types'
|
import { type Collection } from '@/app/components/tools/types'
|
||||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||||
|
import {
|
||||||
|
getMultipleRetrievalConfig,
|
||||||
|
getSelectedDatasetsMode,
|
||||||
|
} from '@/app/components/workflow/nodes/knowledge-retrieval/utils'
|
||||||
|
|
||||||
type PublishConfig = {
|
type PublishConfig = {
|
||||||
modelConfig: ModelConfig
|
modelConfig: ModelConfig
|
||||||
@ -174,14 +178,14 @@ const Configuration: FC = () => {
|
|||||||
|
|
||||||
}, [])
|
}, [])
|
||||||
const [datasetConfigs, setDatasetConfigs] = useState<DatasetConfigs>({
|
const [datasetConfigs, setDatasetConfigs] = useState<DatasetConfigs>({
|
||||||
retrieval_model: RETRIEVE_TYPE.oneWay,
|
retrieval_model: RETRIEVE_TYPE.multiWay,
|
||||||
reranking_model: {
|
reranking_model: {
|
||||||
reranking_provider_name: '',
|
reranking_provider_name: '',
|
||||||
reranking_model_name: '',
|
reranking_model_name: '',
|
||||||
},
|
},
|
||||||
top_k: 2,
|
top_k: DATASET_DEFAULT.top_k,
|
||||||
score_threshold_enabled: false,
|
score_threshold_enabled: false,
|
||||||
score_threshold: 0.7,
|
score_threshold: DATASET_DEFAULT.score_threshold,
|
||||||
datasets: {
|
datasets: {
|
||||||
datasets: [],
|
datasets: [],
|
||||||
},
|
},
|
||||||
@ -202,6 +206,7 @@ const Configuration: FC = () => {
|
|||||||
const hasSetContextVar = !!contextVar
|
const hasSetContextVar = !!contextVar
|
||||||
const [isShowSelectDataSet, { setTrue: showSelectDataSet, setFalse: hideSelectDataSet }] = useBoolean(false)
|
const [isShowSelectDataSet, { setTrue: showSelectDataSet, setFalse: hideSelectDataSet }] = useBoolean(false)
|
||||||
const selectedIds = dataSets.map(item => item.id)
|
const selectedIds = dataSets.map(item => item.id)
|
||||||
|
const [rerankSettingModalOpen, setRerankSettingModalOpen] = useState(false)
|
||||||
const handleSelect = (data: DataSet[]) => {
|
const handleSelect = (data: DataSet[]) => {
|
||||||
if (isEqual(data.map(item => item.id), dataSets.map(item => item.id))) {
|
if (isEqual(data.map(item => item.id), dataSets.map(item => item.id))) {
|
||||||
hideSelectDataSet()
|
hideSelectDataSet()
|
||||||
@ -209,6 +214,7 @@ const Configuration: FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
formattingChangedDispatcher()
|
formattingChangedDispatcher()
|
||||||
|
let newDatasets = data
|
||||||
if (data.find(item => !item.name)) { // has not loaded selected dataset
|
if (data.find(item => !item.name)) { // has not loaded selected dataset
|
||||||
const newSelected = produce(data, (draft: any) => {
|
const newSelected = produce(data, (draft: any) => {
|
||||||
data.forEach((item, index) => {
|
data.forEach((item, index) => {
|
||||||
@ -220,11 +226,45 @@ const Configuration: FC = () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
setDataSets(newSelected)
|
setDataSets(newSelected)
|
||||||
|
newDatasets = newSelected
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
setDataSets(data)
|
setDataSets(data)
|
||||||
}
|
}
|
||||||
hideSelectDataSet()
|
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)
|
const [isShowHistoryModal, { setTrue: showHistoryModal, setFalse: hideHistoryModal }] = useBoolean(false)
|
||||||
@ -509,7 +549,7 @@ const Configuration: FC = () => {
|
|||||||
syncToPublishedConfig(config)
|
syncToPublishedConfig(config)
|
||||||
setPublishedConfig(config)
|
setPublishedConfig(config)
|
||||||
setDatasetConfigs({
|
setDatasetConfigs({
|
||||||
retrieval_model: RETRIEVE_TYPE.oneWay,
|
retrieval_model: RETRIEVE_TYPE.multiWay,
|
||||||
...modelConfig.dataset_configs,
|
...modelConfig.dataset_configs,
|
||||||
})
|
})
|
||||||
setHasFetchedDetail(true)
|
setHasFetchedDetail(true)
|
||||||
@ -744,6 +784,8 @@ const Configuration: FC = () => {
|
|||||||
isShowVisionConfig,
|
isShowVisionConfig,
|
||||||
visionConfig,
|
visionConfig,
|
||||||
setVisionConfig: handleSetVisionConfig,
|
setVisionConfig: handleSetVisionConfig,
|
||||||
|
rerankSettingModalOpen,
|
||||||
|
setRerankSettingModalOpen,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
|
25
web/app/components/base/badge.tsx
Normal file
25
web/app/components/base/badge.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { memo } from 'react'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
|
type BadgeProps = {
|
||||||
|
className?: string
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const Badge = ({
|
||||||
|
className,
|
||||||
|
text,
|
||||||
|
}: BadgeProps) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'inline-flex items-center px-[5px] h-5 rounded-[5px] border border-divider-deep system-2xs-medium-uppercase leading-3 text-text-tertiary',
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(Badge)
|
@ -2,46 +2,185 @@
|
|||||||
|
|
||||||
@layer components {
|
@layer components {
|
||||||
.btn {
|
.btn {
|
||||||
@apply inline-flex justify-center items-center border-[0.5px] font-medium cursor-pointer whitespace-nowrap shadow;
|
@apply inline-flex justify-center items-center cursor-pointer whitespace-nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-disabled {
|
.btn-disabled {
|
||||||
@apply opacity-60 cursor-not-allowed;
|
@apply cursor-not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-small {
|
.btn-small {
|
||||||
@apply px-2 h-6 rounded-md text-xs
|
@apply px-2 h-6 rounded-md text-xs font-medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-medium {
|
.btn-medium {
|
||||||
@apply px-3.5 h-8 rounded-lg text-[13px]
|
@apply px-3.5 h-8 rounded-lg text-[13px] leading-4 font-medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-large {
|
.btn-large {
|
||||||
@apply px-4 h-9 rounded-[10px] text-sm font-semibold
|
@apply px-4 h-9 rounded-[10px] text-sm font-semibold;
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary {
|
|
||||||
@apply bg-white hover:bg-white/80 border-gray-200 hover:border-gray-300 text-gray-700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary-accent {
|
|
||||||
@apply bg-white hover:bg-white/80 border-gray-200 hover:border-gray-300 text-primary-600;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
@apply bg-primary-600 hover:bg-primary-700 text-white;
|
@apply
|
||||||
|
shadow
|
||||||
|
bg-components-button-primary-bg
|
||||||
|
border-components-button-primary-border
|
||||||
|
hover:bg-components-button-primary-bg-hover
|
||||||
|
hover:border-components-button-primary-border-hover
|
||||||
|
text-components-button-primary-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary.btn-destructive {
|
||||||
|
@apply
|
||||||
|
bg-components-button-destructive-primary-bg
|
||||||
|
border-components-button-destructive-primary-border
|
||||||
|
hover:bg-components-button-destructive-primary-bg-hover
|
||||||
|
hover:border-components-button-destructive-primary-border-hover
|
||||||
|
text-components-button-destructive-primary-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary.btn-disabled {
|
||||||
|
@apply
|
||||||
|
shadow-none
|
||||||
|
bg-components-button-primary-bg-disabled
|
||||||
|
border-components-button-primary-border-disabled
|
||||||
|
text-components-button-primary-text-disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary.btn-destructive.btn-disabled {
|
||||||
|
@apply
|
||||||
|
shadow-none
|
||||||
|
bg-components-button-destructive-primary-bg-disabled
|
||||||
|
border-components-button-destructive-primary-border-disabled
|
||||||
|
text-components-button-destructive-primary-text-disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
@apply
|
||||||
|
border-[0.5px]
|
||||||
|
shadow-xs
|
||||||
|
bg-components-button-secondary-bg
|
||||||
|
border-components-button-secondary-border
|
||||||
|
hover:bg-components-button-secondary-bg-hover
|
||||||
|
hover:border-components-button-secondary-border-hover
|
||||||
|
text-components-button-secondary-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary.btn-disabled {
|
||||||
|
@apply
|
||||||
|
bg-components-button-secondary-bg-disabled
|
||||||
|
border-components-button-secondary-border-disabled
|
||||||
|
text-components-button-secondary-text-disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary.btn-destructive {
|
||||||
|
@apply
|
||||||
|
bg-components-button-destructive-secondary-bg
|
||||||
|
border-components-button-destructive-secondary-border
|
||||||
|
hover:bg-components-button-destructive-secondary-bg-hover
|
||||||
|
hover:border-components-button-destructive-secondary-border-hover
|
||||||
|
text-components-button-destructive-secondary-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary.btn-destructive.btn-disabled {
|
||||||
|
@apply
|
||||||
|
bg-components-button-destructive-secondary-bg-disabled
|
||||||
|
border-components-button-destructive-secondary-border-disabled
|
||||||
|
text-components-button-destructive-secondary-text-disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-secondary-accent {
|
||||||
|
@apply
|
||||||
|
border-[0.5px]
|
||||||
|
shadow-xs
|
||||||
|
bg-components-button-secondary-bg
|
||||||
|
border-components-button-secondary-border
|
||||||
|
hover:bg-components-button-secondary-bg-hover
|
||||||
|
hover:border-components-button-secondary-border-hover
|
||||||
|
text-components-button-secondary-accent-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary-accent.btn-disabled {
|
||||||
|
@apply
|
||||||
|
bg-components-button-secondary-bg-disabled
|
||||||
|
border-components-button-secondary-border-disabled
|
||||||
|
text-components-button-secondary-accent-text-disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-warning {
|
.btn-warning {
|
||||||
@apply bg-red-600 hover:bg-red-700 text-white;
|
@apply
|
||||||
|
bg-components-button-destructive-primary-bg
|
||||||
|
border-components-button-destructive-primary-border
|
||||||
|
hover:bg-components-button-destructive-primary-bg-hover
|
||||||
|
hover:border-components-button-destructive-primary-border-hover
|
||||||
|
text-components-button-destructive-primary-text;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-ghost {
|
.btn-warning.btn-disabled {
|
||||||
@apply bg-transparent hover:bg-gray-200 border-transparent shadow-none text-gray-700;
|
@apply
|
||||||
|
bg-components-button-destructive-primary-bg-disabled
|
||||||
|
border-components-button-destructive-primary-border-disabled
|
||||||
|
text-components-button-destructive-primary-text-disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-tertiary {
|
.btn-tertiary {
|
||||||
@apply bg-[#F2F4F7] hover:bg-[#E9EBF0] border-transparent shadow-none text-gray-700;
|
@apply
|
||||||
|
bg-components-button-tertiary-bg
|
||||||
|
hover:bg-components-button-tertiary-bg-hover
|
||||||
|
text-components-button-tertiary-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-tertiary.btn-disabled {
|
||||||
|
@apply
|
||||||
|
bg-components-button-tertiary-bg-disabled
|
||||||
|
text-components-button-tertiary-text-disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-tertiary.btn-destructive {
|
||||||
|
@apply
|
||||||
|
bg-components-button-destructive-tertiary-bg
|
||||||
|
hover:bg-components-button-destructive-tertiary-bg-hover
|
||||||
|
text-components-button-destructive-tertiary-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-tertiary.btn-destructive.btn-disabled {
|
||||||
|
@apply
|
||||||
|
bg-components-button-destructive-tertiary-bg-disabled
|
||||||
|
text-components-button-destructive-tertiary-text-disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-ghost {
|
||||||
|
@apply
|
||||||
|
hover:bg-components-button-ghost-bg-hover
|
||||||
|
text-components-button-ghost-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-ghost.btn-disabled {
|
||||||
|
@apply
|
||||||
|
text-components-button-ghost-text-disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-ghost.btn-destructive {
|
||||||
|
@apply
|
||||||
|
hover:bg-components-button-destructive-ghost-bg-hover
|
||||||
|
text-components-button-destructive-ghost-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-ghost.btn-destructive.btn-disabled {
|
||||||
|
@apply
|
||||||
|
text-components-button-destructive-ghost-text-disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-ghost-accent {
|
||||||
|
@apply
|
||||||
|
hover:bg-state-accent-hover
|
||||||
|
text-components-button-secondary-accent-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-ghost-accent.btn-disabled {
|
||||||
|
@apply
|
||||||
|
text-components-button-secondary-accent-text-disabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,6 +14,7 @@ const buttonVariants = cva(
|
|||||||
'secondary': 'btn-secondary',
|
'secondary': 'btn-secondary',
|
||||||
'secondary-accent': 'btn-secondary-accent',
|
'secondary-accent': 'btn-secondary-accent',
|
||||||
'ghost': 'btn-ghost',
|
'ghost': 'btn-ghost',
|
||||||
|
'ghost-accent': 'btn-ghost-accent',
|
||||||
'tertiary': 'btn-tertiary',
|
'tertiary': 'btn-tertiary',
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
@ -30,16 +31,20 @@ const buttonVariants = cva(
|
|||||||
)
|
)
|
||||||
|
|
||||||
export type ButtonProps = {
|
export type ButtonProps = {
|
||||||
|
destructive?: boolean
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
styleCss?: CSSProperties
|
styleCss?: CSSProperties
|
||||||
} & React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<typeof buttonVariants>
|
} & React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<typeof buttonVariants>
|
||||||
|
|
||||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
({ className, variant, size, loading, styleCss, children, ...props }, ref) => {
|
({ className, variant, size, destructive, loading, styleCss, children, ...props }, ref) => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
className={classNames(buttonVariants({ variant, size, className }))}
|
className={classNames(
|
||||||
|
buttonVariants({ variant, size, className }),
|
||||||
|
destructive && 'btn-destructive',
|
||||||
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
style={styleCss}
|
style={styleCss}
|
||||||
{...props}
|
{...props}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import s from './style.module.css'
|
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -29,7 +28,12 @@ const RadioCard: FC<Props> = ({
|
|||||||
chosenConfigWrapClassName,
|
chosenConfigWrapClassName,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={cn(s.item, isChosen && s.active)}>
|
<div
|
||||||
|
className={cn(
|
||||||
|
'border border-components-option-card-option-border bg-components-option-card-option-bg rounded-xl hover:shadow-xs cursor-pointer',
|
||||||
|
isChosen && 'bg-components-option-card-option-selected-bg border-components-panel-border shadow-xs',
|
||||||
|
)}
|
||||||
|
>
|
||||||
<div className='flex py-3 pl-3 pr-4' onClick={onChosen}>
|
<div className='flex py-3 pl-3 pr-4' onClick={onChosen}>
|
||||||
<div className={cn(iconBgClassName, 'mr-3 shrink-0 flex w-8 justify-center h-8 items-center rounded-lg')}>
|
<div className={cn(iconBgClassName, 'mr-3 shrink-0 flex w-8 justify-center h-8 items-center rounded-lg')}>
|
||||||
{icon}
|
{icon}
|
||||||
@ -40,12 +44,15 @@ const RadioCard: FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
{!noRadio && (
|
{!noRadio && (
|
||||||
<div className='shrink-0 flex items-center h-8'>
|
<div className='shrink-0 flex items-center h-8'>
|
||||||
<div className={s.radio}></div>
|
<div className={cn(
|
||||||
|
'w-4 h-4 border border-components-radio-border bg-components-radio-bg shadow-xs rounded-full',
|
||||||
|
isChosen && 'border-[5px] border-components-radio-border-checked',
|
||||||
|
)}></div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{((isChosen && chosenConfig) || noRadio) && (
|
{((isChosen && chosenConfig) || noRadio) && (
|
||||||
<div className={cn(chosenConfigWrapClassName, 'pt-2 px-14 pb-6 border-t border-gray-200')}>
|
<div className={cn(chosenConfigWrapClassName, 'p-3 border-t border-gray-200')}>
|
||||||
{chosenConfig}
|
{chosenConfig}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -6,7 +6,7 @@ import cn from '@/utils/classnames'
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
className?: string
|
className?: string
|
||||||
title: string
|
title: string | JSX.Element | null
|
||||||
description: string
|
description: string
|
||||||
isChosen: boolean
|
isChosen: boolean
|
||||||
onChosen: () => void
|
onChosen: () => void
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
.item {
|
|
||||||
@apply relative rounded-xl border border-gray-100 cursor-pointer;
|
|
||||||
background-color: #fcfcfd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item.active {
|
|
||||||
border-width: 1.5px;
|
|
||||||
border-color: #528BFF;
|
|
||||||
box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), 0px 1px 2px rgba(16, 24, 40, 0.06);
|
|
||||||
}
|
|
||||||
|
|
||||||
.item:hover {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-color: #B2CCFF;
|
|
||||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio {
|
|
||||||
@apply w-4 h-4 border-[2px] border-gray-200 rounded-full;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item.active .radio {
|
|
||||||
border-width: 5px;
|
|
||||||
border-color: #155EEF;
|
|
||||||
}
|
|
@ -4,6 +4,8 @@ import './style.css'
|
|||||||
|
|
||||||
type ISliderProps = {
|
type ISliderProps = {
|
||||||
className?: string
|
className?: string
|
||||||
|
thumbClassName?: string
|
||||||
|
trackClassName?: string
|
||||||
value: number
|
value: number
|
||||||
max?: number
|
max?: number
|
||||||
min?: number
|
min?: number
|
||||||
@ -12,16 +14,26 @@ type ISliderProps = {
|
|||||||
onChange: (value: number) => void
|
onChange: (value: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const Slider: React.FC<ISliderProps> = ({ className, max, min, step, value, disabled, onChange }) => {
|
const Slider: React.FC<ISliderProps> = ({
|
||||||
|
className,
|
||||||
|
thumbClassName,
|
||||||
|
trackClassName,
|
||||||
|
max,
|
||||||
|
min,
|
||||||
|
step,
|
||||||
|
value,
|
||||||
|
disabled,
|
||||||
|
onChange,
|
||||||
|
}) => {
|
||||||
return <ReactSlider
|
return <ReactSlider
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={isNaN(value) ? 0 : value}
|
value={isNaN(value) ? 0 : value}
|
||||||
min={min || 0}
|
min={min || 0}
|
||||||
max={max || 100}
|
max={max || 100}
|
||||||
step={step || 1}
|
step={step || 1}
|
||||||
className={cn(className, 'slider')}
|
className={cn('slider', className)}
|
||||||
thumbClassName="slider-thumb"
|
thumbClassName={cn('slider-thumb', thumbClassName)}
|
||||||
trackClassName="slider-track"
|
trackClassName={cn('slider-track', trackClassName)}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import type {
|
|||||||
DefaultModelResponse,
|
DefaultModelResponse,
|
||||||
Model,
|
Model,
|
||||||
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
|
import { RerankingModeEnum } from '@/models/datasets'
|
||||||
|
|
||||||
export const isReRankModelSelected = ({
|
export const isReRankModelSelected = ({
|
||||||
rerankDefaultModel,
|
rerankDefaultModel,
|
||||||
@ -32,7 +33,7 @@ export const isReRankModelSelected = ({
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
indexMethod === 'high_quality'
|
indexMethod === 'high_quality'
|
||||||
&& (retrievalConfig.reranking_enable || retrievalConfig.search_method === RETRIEVE_METHOD.hybrid)
|
&& (retrievalConfig.search_method === RETRIEVE_METHOD.hybrid && retrievalConfig.reranking_mode !== RerankingModeEnum.WeightedScore)
|
||||||
&& !rerankModelSelected
|
&& !rerankModelSelected
|
||||||
)
|
)
|
||||||
return false
|
return false
|
||||||
|
@ -15,6 +15,11 @@ import type { RetrievalConfig } from '@/types/app'
|
|||||||
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
||||||
import { useModelListAndDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
import { useModelListAndDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
|
import {
|
||||||
|
RerankingModeEnum,
|
||||||
|
WeightedScoreEnum,
|
||||||
|
} from '@/models/datasets'
|
||||||
|
import WeightedScore from '@/app/components/app/configuration/dataset-config/params-config/weighted-score'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
type: RETRIEVE_METHOD
|
type: RETRIEVE_METHOD
|
||||||
@ -34,6 +39,7 @@ const RetrievalParamConfig: FC<Props> = ({
|
|||||||
defaultModel: rerankDefaultModel,
|
defaultModel: rerankDefaultModel,
|
||||||
modelList: rerankModelList,
|
modelList: rerankModelList,
|
||||||
} = useModelListAndDefaultModel(ModelTypeEnum.rerank)
|
} = useModelListAndDefaultModel(ModelTypeEnum.rerank)
|
||||||
|
const isHybridSearch = type === RETRIEVE_METHOD.hybrid
|
||||||
|
|
||||||
const rerankModel = (() => {
|
const rerankModel = (() => {
|
||||||
if (value.reranking_model) {
|
if (value.reranking_model) {
|
||||||
@ -50,9 +56,47 @@ const RetrievalParamConfig: FC<Props> = ({
|
|||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
const handleChangeRerankMode = (v: RerankingModeEnum) => {
|
||||||
|
if (v === value.reranking_mode)
|
||||||
|
return
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
...value,
|
||||||
|
reranking_mode: v,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.weights && v === RerankingModeEnum.WeightedScore) {
|
||||||
|
result.weights = {
|
||||||
|
weight_type: WeightedScoreEnum.Customized,
|
||||||
|
vector_setting: {
|
||||||
|
vector_weight: 0.5,
|
||||||
|
embedding_provider_name: '',
|
||||||
|
embedding_model_name: '',
|
||||||
|
},
|
||||||
|
keyword_setting: {
|
||||||
|
keyword_weight: 0.5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onChange(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
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'),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{!isEconomical && (
|
{!isEconomical && !isHybridSearch && (
|
||||||
<div>
|
<div>
|
||||||
<div className='flex h-8 items-center text-[13px] font-medium text-gray-900 space-x-2'>
|
<div className='flex h-8 items-center text-[13px] font-medium text-gray-900 space-x-2'>
|
||||||
{canToggleRerankModalEnable && (
|
{canToggleRerankModalEnable && (
|
||||||
@ -75,10 +119,10 @@ const RetrievalParamConfig: FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ModelSelector
|
<ModelSelector
|
||||||
triggerClassName={`${!value.reranking_enable && type !== RETRIEVE_METHOD.hybrid && '!opacity-60 !cursor-not-allowed'}`}
|
triggerClassName={`${!value.reranking_enable && '!opacity-60 !cursor-not-allowed'}`}
|
||||||
defaultModel={rerankModel && { provider: rerankModel.provider_name, model: rerankModel.model_name }}
|
defaultModel={rerankModel && { provider: rerankModel.provider_name, model: rerankModel.model_name }}
|
||||||
modelList={rerankModelList}
|
modelList={rerankModelList}
|
||||||
readonly={!value.reranking_enable && type !== RETRIEVE_METHOD.hybrid}
|
readonly={!value.reranking_enable}
|
||||||
onSelect={(v) => {
|
onSelect={(v) => {
|
||||||
onChange({
|
onChange({
|
||||||
...value,
|
...value,
|
||||||
@ -91,7 +135,8 @@ const RetrievalParamConfig: FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{
|
||||||
|
!isHybridSearch && (
|
||||||
<div className={cn(!isEconomical && 'mt-4', 'flex space-between space-x-6')}>
|
<div className={cn(!isEconomical && 'mt-4', 'flex space-between space-x-6')}>
|
||||||
<TopKItem
|
<TopKItem
|
||||||
className='grow'
|
className='grow'
|
||||||
@ -125,6 +170,117 @@ const RetrievalParamConfig: FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
isHybridSearch && (
|
||||||
|
<>
|
||||||
|
<div className='flex items-center justify-between'>
|
||||||
|
{
|
||||||
|
rerankingModeOptions.map(option => (
|
||||||
|
<div
|
||||||
|
key={option.value}
|
||||||
|
className={cn(
|
||||||
|
'flex items-center justify-center mb-4 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',
|
||||||
|
value.reranking_mode === RerankingModeEnum.WeightedScore && option.value === RerankingModeEnum.WeightedScore && 'border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary',
|
||||||
|
value.reranking_mode !== RerankingModeEnum.WeightedScore && option.value !== RerankingModeEnum.WeightedScore && 'border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary',
|
||||||
|
)}
|
||||||
|
onClick={() => handleChangeRerankMode(option.value)}
|
||||||
|
>
|
||||||
|
<div className='truncate'>{option.label}</div>
|
||||||
|
<Tooltip
|
||||||
|
popupContent={<div className='w-[200px]'>{option.tips}</div>}
|
||||||
|
hideArrow
|
||||||
|
>
|
||||||
|
<RiQuestionLine className='ml-0.5 w-3.5 h-4.5 text-text-quaternary' />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
value.reranking_mode === RerankingModeEnum.WeightedScore && (
|
||||||
|
<WeightedScore
|
||||||
|
value={{
|
||||||
|
type: value.weights!.weight_type,
|
||||||
|
value: [
|
||||||
|
value.weights!.vector_setting.vector_weight,
|
||||||
|
value.weights!.keyword_setting.keyword_weight,
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
onChange={(v) => {
|
||||||
|
onChange({
|
||||||
|
...value,
|
||||||
|
weights: {
|
||||||
|
...value.weights!,
|
||||||
|
weight_type: v.type,
|
||||||
|
vector_setting: {
|
||||||
|
...value.weights!.vector_setting,
|
||||||
|
vector_weight: v.value[0],
|
||||||
|
},
|
||||||
|
keyword_setting: {
|
||||||
|
...value.weights!.keyword_setting,
|
||||||
|
keyword_weight: v.value[1],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
value.reranking_mode !== RerankingModeEnum.WeightedScore && (
|
||||||
|
<ModelSelector
|
||||||
|
triggerClassName={`${!value.reranking_enable && '!opacity-60 !cursor-not-allowed'}`}
|
||||||
|
defaultModel={rerankModel && { provider: rerankModel.provider_name, model: rerankModel.model_name }}
|
||||||
|
modelList={rerankModelList}
|
||||||
|
readonly={!value.reranking_enable}
|
||||||
|
onSelect={(v) => {
|
||||||
|
onChange({
|
||||||
|
...value,
|
||||||
|
reranking_model: {
|
||||||
|
reranking_provider_name: v.provider,
|
||||||
|
reranking_model_name: v.model,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<div className={cn(!isEconomical && 'mt-4', 'flex space-between space-x-6')}>
|
||||||
|
<TopKItem
|
||||||
|
className='grow'
|
||||||
|
value={value.top_k}
|
||||||
|
onChange={(_key, v) => {
|
||||||
|
onChange({
|
||||||
|
...value,
|
||||||
|
top_k: v,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
enable={true}
|
||||||
|
/>
|
||||||
|
<ScoreThresholdItem
|
||||||
|
className='grow'
|
||||||
|
value={value.score_threshold}
|
||||||
|
onChange={(_key, v) => {
|
||||||
|
onChange({
|
||||||
|
...value,
|
||||||
|
score_threshold: v,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
enable={value.score_threshold_enabled}
|
||||||
|
hasSwitch={true}
|
||||||
|
onSwitchChange={(_key, v) => {
|
||||||
|
onChange({
|
||||||
|
...value,
|
||||||
|
score_threshold_enabled: v,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,10 @@ const Form = () => {
|
|||||||
retrievalConfig,
|
retrievalConfig,
|
||||||
indexMethod,
|
indexMethod,
|
||||||
})
|
})
|
||||||
|
if (postRetrievalConfig.weights) {
|
||||||
|
postRetrievalConfig.weights.vector_setting.embedding_provider_name = currentDataset?.embedding_model_provider || ''
|
||||||
|
postRetrievalConfig.weights.vector_setting.embedding_model_name = currentDataset?.embedding_model || ''
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
const requestParams = {
|
const requestParams = {
|
||||||
|
@ -22,7 +22,6 @@ const Icon = ({ svgString, active }: { svgString: string; active: boolean }) =>
|
|||||||
return null
|
return null
|
||||||
const parser = new DOMParser()
|
const parser = new DOMParser()
|
||||||
const doc = parser.parseFromString(svg, 'image/svg+xml')
|
const doc = parser.parseFromString(svg, 'image/svg+xml')
|
||||||
console.log(doc.documentElement)
|
|
||||||
return doc.documentElement
|
return doc.documentElement
|
||||||
}
|
}
|
||||||
useMount(() => {
|
useMount(() => {
|
||||||
|
@ -4,15 +4,17 @@ import React, { useCallback } from 'react'
|
|||||||
import { useBoolean } from 'ahooks'
|
import { useBoolean } from 'ahooks'
|
||||||
import {
|
import {
|
||||||
RiDeleteBinLine,
|
RiDeleteBinLine,
|
||||||
|
RiEditLine,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import type { DataSet } from '@/models/datasets'
|
import type { DataSet } from '@/models/datasets'
|
||||||
import { DataSourceType } from '@/models/datasets'
|
import { DataSourceType } from '@/models/datasets'
|
||||||
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
|
|
||||||
import FileIcon from '@/app/components/base/file-icon'
|
import FileIcon from '@/app/components/base/file-icon'
|
||||||
import { Folder } from '@/app/components/base/icons/src/vender/solid/files'
|
import { Folder } from '@/app/components/base/icons/src/vender/solid/files'
|
||||||
import SettingsModal from '@/app/components/app/configuration/dataset-config/settings-modal'
|
import SettingsModal from '@/app/components/app/configuration/dataset-config/settings-modal'
|
||||||
import Drawer from '@/app/components/base/drawer'
|
import Drawer from '@/app/components/base/drawer'
|
||||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
|
import Badge from '@/app/components/base/badge'
|
||||||
|
import { useKnowledge } from '@/hooks/use-knowledge'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
payload: DataSet
|
payload: DataSet
|
||||||
@ -29,6 +31,7 @@ const DatasetItem: FC<Props> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const media = useBreakpoints()
|
const media = useBreakpoints()
|
||||||
const isMobile = media === MediaType.mobile
|
const isMobile = media === MediaType.mobile
|
||||||
|
const { formatIndexingTechniqueAndMethod } = useKnowledge()
|
||||||
|
|
||||||
const [isShowSettingsModal, {
|
const [isShowSettingsModal, {
|
||||||
setTrue: showSettingsModal,
|
setTrue: showSettingsModal,
|
||||||
@ -62,7 +65,7 @@ const DatasetItem: FC<Props> = ({
|
|||||||
className='flex items-center justify-center w-6 h-6 hover:bg-black/5 rounded-md cursor-pointer'
|
className='flex items-center justify-center w-6 h-6 hover:bg-black/5 rounded-md cursor-pointer'
|
||||||
onClick={showSettingsModal}
|
onClick={showSettingsModal}
|
||||||
>
|
>
|
||||||
<Settings01 className='w-4 h-4 text-gray-500' />
|
<RiEditLine className='w-4 h-4 text-gray-500' />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className='flex items-center justify-center w-6 h-6 hover:bg-black/5 rounded-md cursor-pointer'
|
className='flex items-center justify-center w-6 h-6 hover:bg-black/5 rounded-md cursor-pointer'
|
||||||
@ -72,6 +75,10 @@ const DatasetItem: FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<Badge
|
||||||
|
className='group-hover/dataset-item:hidden shrink-0'
|
||||||
|
text={formatIndexingTechniqueAndMethod(payload.indexing_technique, payload.retrieval_model_dict?.search_method)}
|
||||||
|
/>
|
||||||
|
|
||||||
{isShowSettingsModal && (
|
{isShowSettingsModal && (
|
||||||
<Drawer isOpen={isShowSettingsModal} onClose={hideSettingsModal} footer={null} mask={isMobile} panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
|
<Drawer isOpen={isShowSettingsModal} onClose={hideSettingsModal} footer={null} mask={isMobile} panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React, { useCallback, useState } from 'react'
|
import React, { useCallback, useState } from 'react'
|
||||||
|
import { RiEqualizer2Line } from '@remixicon/react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { RiArrowDownSLine } from '@remixicon/react'
|
|
||||||
import type { MultipleRetrievalConfig, SingleRetrievalConfig } from '../types'
|
import type { MultipleRetrievalConfig, SingleRetrievalConfig } from '../types'
|
||||||
import type { ModelConfig } from '../../../types'
|
import type { ModelConfig } from '../../../types'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
@ -16,10 +16,9 @@ import { RETRIEVE_TYPE } from '@/types/app'
|
|||||||
import { DATASET_DEFAULT } from '@/config'
|
import { DATASET_DEFAULT } from '@/config'
|
||||||
import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
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 { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
import type {
|
import type { DatasetConfigs } from '@/models/debug'
|
||||||
DatasetConfigs,
|
import type { DataSet } from '@/models/datasets'
|
||||||
} from '@/models/debug'
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
payload: {
|
payload: {
|
||||||
@ -33,6 +32,9 @@ type Props = {
|
|||||||
onSingleRetrievalModelChange?: (config: ModelConfig) => void
|
onSingleRetrievalModelChange?: (config: ModelConfig) => void
|
||||||
onSingleRetrievalModelParamsChange?: (config: ModelConfig) => void
|
onSingleRetrievalModelParamsChange?: (config: ModelConfig) => void
|
||||||
readonly?: boolean
|
readonly?: boolean
|
||||||
|
openFromProps?: boolean
|
||||||
|
onOpenFromPropsChange?: (openFromProps: boolean) => void
|
||||||
|
selectedDatasets: DataSet[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const RetrievalConfig: FC<Props> = ({
|
const RetrievalConfig: FC<Props> = ({
|
||||||
@ -43,10 +45,18 @@ const RetrievalConfig: FC<Props> = ({
|
|||||||
onSingleRetrievalModelChange,
|
onSingleRetrievalModelChange,
|
||||||
onSingleRetrievalModelParamsChange,
|
onSingleRetrievalModelParamsChange,
|
||||||
readonly,
|
readonly,
|
||||||
|
openFromProps,
|
||||||
|
onOpenFromPropsChange,
|
||||||
|
selectedDatasets,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
|
const mergedOpen = openFromProps !== undefined ? openFromProps : open
|
||||||
|
|
||||||
|
const handleOpen = useCallback((newOpen: boolean) => {
|
||||||
|
setOpen(newOpen)
|
||||||
|
onOpenFromPropsChange?.(newOpen)
|
||||||
|
}, [onOpenFromPropsChange])
|
||||||
|
|
||||||
const {
|
const {
|
||||||
defaultModel: rerankDefaultModel,
|
defaultModel: rerankDefaultModel,
|
||||||
@ -72,16 +82,18 @@ const RetrievalConfig: FC<Props> = ({
|
|||||||
provider: configs.reranking_model?.reranking_provider_name,
|
provider: configs.reranking_model?.reranking_provider_name,
|
||||||
model: configs.reranking_model?.reranking_model_name,
|
model: configs.reranking_model?.reranking_model_name,
|
||||||
}),
|
}),
|
||||||
|
reranking_mode: configs.reranking_mode,
|
||||||
|
weights: configs.weights as any,
|
||||||
|
reranking_enable: configs.reranking_enable,
|
||||||
})
|
})
|
||||||
}, [onMultipleRetrievalConfigChange, payload.retrieval_mode, rerankDefaultModel?.provider?.provider, rerankDefaultModel?.model, onRetrievalModeChange])
|
}, [onMultipleRetrievalConfigChange, payload.retrieval_mode, rerankDefaultModel?.provider?.provider, rerankDefaultModel?.model, onRetrievalModeChange])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PortalToFollowElem
|
<PortalToFollowElem
|
||||||
open={open}
|
open={mergedOpen}
|
||||||
onOpenChange={setOpen}
|
onOpenChange={handleOpen}
|
||||||
placement='bottom-end'
|
placement='bottom-end'
|
||||||
offset={{
|
offset={{
|
||||||
// mainAxis: 12,
|
|
||||||
crossAxis: -2,
|
crossAxis: -2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -89,13 +101,18 @@ const RetrievalConfig: FC<Props> = ({
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (readonly)
|
if (readonly)
|
||||||
return
|
return
|
||||||
setOpen(v => !v)
|
handleOpen(!mergedOpen)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={cn(!readonly && 'cursor-pointer', open && 'bg-gray-100', 'flex items-center h-6 px-2 rounded-md hover:bg-gray-100 group select-none')}>
|
<Button
|
||||||
<div className={cn(open ? 'text-gray-700' : 'text-gray-500', 'leading-[18px] text-xs font-medium group-hover:bg-gray-100')}>{payload.retrieval_mode === RETRIEVE_TYPE.oneWay ? t('appDebug.datasetConfig.retrieveOneWay.title') : t('appDebug.datasetConfig.retrieveMultiWay.title')}</div>
|
variant='ghost'
|
||||||
{!readonly && <RiArrowDownSLine className='w-3 h-3 ml-1' />}
|
size='small'
|
||||||
</div>
|
disabled={readonly}
|
||||||
|
className={cn(open && 'bg-components-button-ghost-bg-hover')}
|
||||||
|
>
|
||||||
|
<RiEqualizer2Line className='mr-1 w-3.5 h-3.5' />
|
||||||
|
{t('dataset.retrievalSettings')}
|
||||||
|
</Button>
|
||||||
</PortalToFollowElemTrigger>
|
</PortalToFollowElemTrigger>
|
||||||
<PortalToFollowElemContent style={{ zIndex: 1001 }}>
|
<PortalToFollowElemContent style={{ zIndex: 1001 }}>
|
||||||
<div className='w-[404px] pt-3 pb-4 px-4 shadow-xl rounded-2xl border border-gray-200 bg-white'>
|
<div className='w-[404px] pt-3 pb-4 px-4 shadow-xl rounded-2xl border border-gray-200 bg-white'>
|
||||||
@ -103,21 +120,24 @@ const RetrievalConfig: FC<Props> = ({
|
|||||||
datasetConfigs={
|
datasetConfigs={
|
||||||
{
|
{
|
||||||
retrieval_model: payload.retrieval_mode,
|
retrieval_model: payload.retrieval_mode,
|
||||||
reranking_model: !multiple_retrieval_config?.reranking_model?.provider
|
reranking_model: multiple_retrieval_config?.reranking_model?.provider
|
||||||
? {
|
? {
|
||||||
reranking_provider_name: rerankDefaultModel?.provider?.provider || '',
|
reranking_provider_name: multiple_retrieval_config.reranking_model?.provider,
|
||||||
reranking_model_name: rerankDefaultModel?.model || '',
|
reranking_model_name: multiple_retrieval_config.reranking_model?.model,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
reranking_provider_name: multiple_retrieval_config?.reranking_model?.provider || '',
|
reranking_provider_name: '',
|
||||||
reranking_model_name: multiple_retrieval_config?.reranking_model?.model || '',
|
reranking_model_name: '',
|
||||||
},
|
},
|
||||||
top_k: multiple_retrieval_config?.top_k || DATASET_DEFAULT.top_k,
|
top_k: multiple_retrieval_config?.top_k || DATASET_DEFAULT.top_k,
|
||||||
score_threshold_enabled: !(multiple_retrieval_config?.score_threshold === undefined || multiple_retrieval_config?.score_threshold === null),
|
score_threshold_enabled: !(multiple_retrieval_config?.score_threshold === undefined || multiple_retrieval_config.score_threshold === null),
|
||||||
score_threshold: multiple_retrieval_config?.score_threshold,
|
score_threshold: multiple_retrieval_config?.score_threshold,
|
||||||
datasets: {
|
datasets: {
|
||||||
datasets: [],
|
datasets: [],
|
||||||
},
|
},
|
||||||
|
reranking_mode: multiple_retrieval_config?.reranking_mode,
|
||||||
|
weights: multiple_retrieval_config?.weights,
|
||||||
|
reranking_enable: multiple_retrieval_config?.reranking_enable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@ -125,6 +145,7 @@ const RetrievalConfig: FC<Props> = ({
|
|||||||
singleRetrievalModelConfig={singleRetrievalModelConfig}
|
singleRetrievalModelConfig={singleRetrievalModelConfig}
|
||||||
onSingleRetrievalModelChange={onSingleRetrievalModelChange}
|
onSingleRetrievalModelChange={onSingleRetrievalModelChange}
|
||||||
onSingleRetrievalModelParamsChange={onSingleRetrievalModelParamsChange}
|
onSingleRetrievalModelParamsChange={onSingleRetrievalModelParamsChange}
|
||||||
|
selectedDatasets={selectedDatasets}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</PortalToFollowElemContent>
|
</PortalToFollowElemContent>
|
||||||
|
@ -2,7 +2,7 @@ import { BlockEnum } from '../../types'
|
|||||||
import type { NodeDefault } from '../../types'
|
import type { NodeDefault } from '../../types'
|
||||||
import type { KnowledgeRetrievalNodeType } from './types'
|
import type { KnowledgeRetrievalNodeType } from './types'
|
||||||
import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
|
import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
|
||||||
|
import { DATASET_DEFAULT } from '@/config'
|
||||||
import { RETRIEVE_TYPE } from '@/types/app'
|
import { RETRIEVE_TYPE } from '@/types/app'
|
||||||
const i18nPrefix = 'workflow'
|
const i18nPrefix = 'workflow'
|
||||||
|
|
||||||
@ -10,7 +10,12 @@ const nodeDefault: NodeDefault<KnowledgeRetrievalNodeType> = {
|
|||||||
defaultValue: {
|
defaultValue: {
|
||||||
query_variable_selector: [],
|
query_variable_selector: [],
|
||||||
dataset_ids: [],
|
dataset_ids: [],
|
||||||
retrieval_mode: RETRIEVE_TYPE.oneWay,
|
retrieval_mode: RETRIEVE_TYPE.multiWay,
|
||||||
|
multiple_retrieval_config: {
|
||||||
|
top_k: DATASET_DEFAULT.top_k,
|
||||||
|
score_threshold: undefined,
|
||||||
|
reranking_enable: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
getAvailablePrevNodes(isChatMode: boolean) {
|
getAvailablePrevNodes(isChatMode: boolean) {
|
||||||
const nodes = isChatMode
|
const nodes = isChatMode
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
import { useMemo } from 'react'
|
||||||
|
import { getSelectedDatasetsMode } from './utils'
|
||||||
|
import type {
|
||||||
|
DataSet,
|
||||||
|
SelectedDatasetsMode,
|
||||||
|
} from '@/models/datasets'
|
||||||
|
|
||||||
|
export const useSelectedDatasetsMode = (datasets: DataSet[]) => {
|
||||||
|
const selectedDatasetsMode: SelectedDatasetsMode = useMemo(() => {
|
||||||
|
return getSelectedDatasetsMode(datasets)
|
||||||
|
}, [datasets])
|
||||||
|
|
||||||
|
return selectedDatasetsMode
|
||||||
|
}
|
@ -1,5 +1,8 @@
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React from 'react'
|
import {
|
||||||
|
memo,
|
||||||
|
useCallback,
|
||||||
|
} from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import VarReferencePicker from '../_base/components/variable/var-reference-picker'
|
import VarReferencePicker from '../_base/components/variable/var-reference-picker'
|
||||||
import useConfig from './use-config'
|
import useConfig from './use-config'
|
||||||
@ -41,8 +44,14 @@ const Panel: FC<NodePanelProps<KnowledgeRetrievalNodeType>> = ({
|
|||||||
query,
|
query,
|
||||||
setQuery,
|
setQuery,
|
||||||
runResult,
|
runResult,
|
||||||
|
rerankModelOpen,
|
||||||
|
setRerankModelOpen,
|
||||||
} = useConfig(id, data)
|
} = useConfig(id, data)
|
||||||
|
|
||||||
|
const handleOpenFromPropsChange = useCallback((openFromProps: boolean) => {
|
||||||
|
setRerankModelOpen(openFromProps)
|
||||||
|
}, [setRerankModelOpen])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-2'>
|
<div className='mt-2'>
|
||||||
<div className='px-4 pb-4 space-y-4'>
|
<div className='px-4 pb-4 space-y-4'>
|
||||||
@ -75,7 +84,10 @@ const Panel: FC<NodePanelProps<KnowledgeRetrievalNodeType>> = ({
|
|||||||
singleRetrievalModelConfig={inputs.single_retrieval_config?.model}
|
singleRetrievalModelConfig={inputs.single_retrieval_config?.model}
|
||||||
onSingleRetrievalModelChange={handleModelChanged as any}
|
onSingleRetrievalModelChange={handleModelChanged as any}
|
||||||
onSingleRetrievalModelParamsChange={handleCompletionParamsChange}
|
onSingleRetrievalModelParamsChange={handleCompletionParamsChange}
|
||||||
readonly={readOnly}
|
readonly={readOnly || !selectedDatasets.length}
|
||||||
|
openFromProps={rerankModelOpen}
|
||||||
|
onOpenFromPropsChange={handleOpenFromPropsChange}
|
||||||
|
selectedDatasets={selectedDatasets}
|
||||||
/>
|
/>
|
||||||
{!readOnly && (<div className='w-px h-3 bg-gray-200'></div>)}
|
{!readOnly && (<div className='w-px h-3 bg-gray-200'></div>)}
|
||||||
{!readOnly && (
|
{!readOnly && (
|
||||||
@ -162,4 +174,4 @@ const Panel: FC<NodePanelProps<KnowledgeRetrievalNodeType>> = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default React.memo(Panel)
|
export default memo(Panel)
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import type { CommonNodeType, ModelConfig, ValueSelector } from '@/app/components/workflow/types'
|
import type { CommonNodeType, ModelConfig, ValueSelector } from '@/app/components/workflow/types'
|
||||||
import type { RETRIEVE_TYPE } from '@/types/app'
|
import type { RETRIEVE_TYPE } from '@/types/app'
|
||||||
|
import type {
|
||||||
|
RerankingModeEnum,
|
||||||
|
WeightedScoreEnum,
|
||||||
|
} from '@/models/datasets'
|
||||||
|
|
||||||
export type MultipleRetrievalConfig = {
|
export type MultipleRetrievalConfig = {
|
||||||
top_k: number
|
top_k: number
|
||||||
@ -8,6 +12,19 @@ export type MultipleRetrievalConfig = {
|
|||||||
provider: string
|
provider: string
|
||||||
model: string
|
model: string
|
||||||
}
|
}
|
||||||
|
reranking_mode?: RerankingModeEnum
|
||||||
|
weights?: {
|
||||||
|
weight_type: WeightedScoreEnum
|
||||||
|
vector_setting: {
|
||||||
|
vector_weight: number
|
||||||
|
embedding_provider_name: string
|
||||||
|
embedding_model_name: string
|
||||||
|
}
|
||||||
|
keyword_setting: {
|
||||||
|
keyword_weight: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reranking_enable?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SingleRetrievalConfig = {
|
export type SingleRetrievalConfig = {
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react'
|
||||||
import produce from 'immer'
|
import produce from 'immer'
|
||||||
import { isEqual } from 'lodash-es'
|
import { isEqual } from 'lodash-es'
|
||||||
import type { ValueSelector, Var } from '../../types'
|
import type { ValueSelector, Var } from '../../types'
|
||||||
@ -8,6 +13,10 @@ import {
|
|||||||
useWorkflow,
|
useWorkflow,
|
||||||
} from '../../hooks'
|
} from '../../hooks'
|
||||||
import type { KnowledgeRetrievalNodeType, MultipleRetrievalConfig } from './types'
|
import type { KnowledgeRetrievalNodeType, MultipleRetrievalConfig } from './types'
|
||||||
|
import {
|
||||||
|
getMultipleRetrievalConfig,
|
||||||
|
getSelectedDatasetsMode,
|
||||||
|
} from './utils'
|
||||||
import { RETRIEVE_TYPE } from '@/types/app'
|
import { RETRIEVE_TYPE } from '@/types/app'
|
||||||
import { DATASET_DEFAULT } from '@/config'
|
import { DATASET_DEFAULT } from '@/config'
|
||||||
import type { DataSet } from '@/models/datasets'
|
import type { DataSet } from '@/models/datasets'
|
||||||
@ -126,34 +135,20 @@ const useConfig = (id: string, payload: KnowledgeRetrievalNodeType) => {
|
|||||||
draft.multiple_retrieval_config = {
|
draft.multiple_retrieval_config = {
|
||||||
top_k: multipleRetrievalConfig?.top_k || DATASET_DEFAULT.top_k,
|
top_k: multipleRetrievalConfig?.top_k || DATASET_DEFAULT.top_k,
|
||||||
score_threshold: multipleRetrievalConfig?.score_threshold,
|
score_threshold: multipleRetrievalConfig?.score_threshold,
|
||||||
reranking_model: payload.retrieval_mode === RETRIEVE_TYPE.oneWay
|
reranking_model: multipleRetrievalConfig?.reranking_model,
|
||||||
? undefined
|
|
||||||
: (!multipleRetrievalConfig?.reranking_model?.provider
|
|
||||||
? {
|
|
||||||
provider: rerankDefaultModel?.provider?.provider || '',
|
|
||||||
model: rerankDefaultModel?.model || '',
|
|
||||||
}
|
|
||||||
: multipleRetrievalConfig?.reranking_model),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
setInputs(newInput)
|
setInputs(newInput)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [currentProvider?.provider, currentModel, rerankDefaultModel])
|
}, [currentProvider?.provider, currentModel, rerankDefaultModel])
|
||||||
|
const [selectedDatasets, setSelectedDatasets] = useState<DataSet[]>([])
|
||||||
|
const [rerankModelOpen, setRerankModelOpen] = useState(false)
|
||||||
const handleRetrievalModeChange = useCallback((newMode: RETRIEVE_TYPE) => {
|
const handleRetrievalModeChange = useCallback((newMode: RETRIEVE_TYPE) => {
|
||||||
const newInputs = produce(inputs, (draft) => {
|
const newInputs = produce(inputs, (draft) => {
|
||||||
draft.retrieval_mode = newMode
|
draft.retrieval_mode = newMode
|
||||||
if (newMode === RETRIEVE_TYPE.multiWay) {
|
if (newMode === RETRIEVE_TYPE.multiWay) {
|
||||||
draft.multiple_retrieval_config = {
|
const multipleRetrievalConfig = draft.multiple_retrieval_config
|
||||||
top_k: draft.multiple_retrieval_config?.top_k || DATASET_DEFAULT.top_k,
|
draft.multiple_retrieval_config = getMultipleRetrievalConfig(multipleRetrievalConfig!, selectedDatasets)
|
||||||
score_threshold: draft.multiple_retrieval_config?.score_threshold,
|
|
||||||
reranking_model: !draft.multiple_retrieval_config?.reranking_model?.provider
|
|
||||||
? {
|
|
||||||
provider: rerankDefaultModel?.provider?.provider || '',
|
|
||||||
model: rerankDefaultModel?.model || '',
|
|
||||||
}
|
|
||||||
: draft.multiple_retrieval_config?.reranking_model,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const hasSetModel = draft.single_retrieval_config?.model?.provider
|
const hasSetModel = draft.single_retrieval_config?.model?.provider
|
||||||
@ -170,17 +165,16 @@ const useConfig = (id: string, payload: KnowledgeRetrievalNodeType) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
setInputs(newInputs)
|
setInputs(newInputs)
|
||||||
}, [currentModel?.model, currentModel?.model_properties?.mode, currentProvider?.provider, inputs, rerankDefaultModel?.model, rerankDefaultModel?.provider?.provider, setInputs])
|
}, [currentModel?.model, currentModel?.model_properties?.mode, currentProvider?.provider, inputs, setInputs, selectedDatasets])
|
||||||
|
|
||||||
const handleMultipleRetrievalConfigChange = useCallback((newConfig: MultipleRetrievalConfig) => {
|
const handleMultipleRetrievalConfigChange = useCallback((newConfig: MultipleRetrievalConfig) => {
|
||||||
const newInputs = produce(inputs, (draft) => {
|
const newInputs = produce(inputs, (draft) => {
|
||||||
draft.multiple_retrieval_config = newConfig
|
draft.multiple_retrieval_config = getMultipleRetrievalConfig(newConfig!, selectedDatasets)
|
||||||
})
|
})
|
||||||
setInputs(newInputs)
|
setInputs(newInputs)
|
||||||
}, [inputs, setInputs])
|
}, [inputs, setInputs, selectedDatasets])
|
||||||
|
|
||||||
// datasets
|
// datasets
|
||||||
const [selectedDatasets, setSelectedDatasets] = useState<DataSet[]>([])
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const inputs = inputRef.current
|
const inputs = inputRef.current
|
||||||
@ -210,12 +204,25 @@ const useConfig = (id: string, payload: KnowledgeRetrievalNodeType) => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleOnDatasetsChange = useCallback((newDatasets: DataSet[]) => {
|
const handleOnDatasetsChange = useCallback((newDatasets: DataSet[]) => {
|
||||||
|
const {
|
||||||
|
allEconomic,
|
||||||
|
mixtureHighQualityAndEconomic,
|
||||||
|
inconsistentEmbeddingModel,
|
||||||
|
} = getSelectedDatasetsMode(newDatasets)
|
||||||
const newInputs = produce(inputs, (draft) => {
|
const newInputs = produce(inputs, (draft) => {
|
||||||
draft.dataset_ids = newDatasets.map(d => d.id)
|
draft.dataset_ids = newDatasets.map(d => d.id)
|
||||||
|
|
||||||
|
if (payload.retrieval_mode === RETRIEVE_TYPE.multiWay && newDatasets.length > 0) {
|
||||||
|
const multipleRetrievalConfig = draft.multiple_retrieval_config
|
||||||
|
draft.multiple_retrieval_config = getMultipleRetrievalConfig(multipleRetrievalConfig!, newDatasets)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
setInputs(newInputs)
|
setInputs(newInputs)
|
||||||
setSelectedDatasets(newDatasets)
|
setSelectedDatasets(newDatasets)
|
||||||
}, [inputs, setInputs])
|
|
||||||
|
if (allEconomic || mixtureHighQualityAndEconomic || inconsistentEmbeddingModel)
|
||||||
|
setRerankModelOpen(true)
|
||||||
|
}, [inputs, setInputs, payload.retrieval_mode])
|
||||||
|
|
||||||
const filterVar = useCallback((varPayload: Var) => {
|
const filterVar = useCallback((varPayload: Var) => {
|
||||||
return varPayload.type === VarType.string
|
return varPayload.type === VarType.string
|
||||||
@ -266,6 +273,8 @@ const useConfig = (id: string, payload: KnowledgeRetrievalNodeType) => {
|
|||||||
query,
|
query,
|
||||||
setQuery,
|
setQuery,
|
||||||
runResult,
|
runResult,
|
||||||
|
rerankModelOpen,
|
||||||
|
setRerankModelOpen,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,124 @@
|
|||||||
import type { KnowledgeRetrievalNodeType } from './types'
|
import { uniq } from 'lodash-es'
|
||||||
|
import type { MultipleRetrievalConfig } from './types'
|
||||||
|
import type {
|
||||||
|
DataSet,
|
||||||
|
SelectedDatasetsMode,
|
||||||
|
} from '@/models/datasets'
|
||||||
|
import {
|
||||||
|
DEFAULT_WEIGHTED_SCORE,
|
||||||
|
RerankingModeEnum,
|
||||||
|
WeightedScoreEnum,
|
||||||
|
} from '@/models/datasets'
|
||||||
|
import { RETRIEVE_METHOD } from '@/types/app'
|
||||||
|
import { DATASET_DEFAULT } from '@/config'
|
||||||
|
|
||||||
export const checkNodeValid = (payload: KnowledgeRetrievalNodeType) => {
|
export const checkNodeValid = () => {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getSelectedDatasetsMode = (datasets: DataSet[]) => {
|
||||||
|
let allHighQuality = true
|
||||||
|
let allHighQualityVectorSearch = true
|
||||||
|
let allHighQualityFullTextSearch = true
|
||||||
|
let allEconomic = true
|
||||||
|
let mixtureHighQualityAndEconomic = true
|
||||||
|
let inconsistentEmbeddingModel = false
|
||||||
|
if (!datasets.length) {
|
||||||
|
allHighQuality = false
|
||||||
|
allHighQualityVectorSearch = false
|
||||||
|
allHighQualityFullTextSearch = false
|
||||||
|
allEconomic = false
|
||||||
|
mixtureHighQualityAndEconomic = false
|
||||||
|
inconsistentEmbeddingModel = false
|
||||||
|
}
|
||||||
|
datasets.forEach((dataset) => {
|
||||||
|
if (dataset.indexing_technique === 'economy') {
|
||||||
|
allHighQuality = false
|
||||||
|
allHighQualityVectorSearch = false
|
||||||
|
allHighQualityFullTextSearch = false
|
||||||
|
}
|
||||||
|
if (dataset.indexing_technique === 'high_quality') {
|
||||||
|
allEconomic = false
|
||||||
|
|
||||||
|
if (dataset.retrieval_model_dict.search_method !== RETRIEVE_METHOD.semantic)
|
||||||
|
allHighQualityVectorSearch = false
|
||||||
|
|
||||||
|
if (dataset.retrieval_model_dict.search_method !== RETRIEVE_METHOD.fullText)
|
||||||
|
allHighQualityFullTextSearch = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (allHighQuality || allEconomic)
|
||||||
|
mixtureHighQualityAndEconomic = false
|
||||||
|
|
||||||
|
if (allHighQuality)
|
||||||
|
inconsistentEmbeddingModel = uniq(datasets.map(item => item.embedding_model)).length > 1
|
||||||
|
|
||||||
|
return {
|
||||||
|
allHighQuality,
|
||||||
|
allHighQualityVectorSearch,
|
||||||
|
allHighQualityFullTextSearch,
|
||||||
|
allEconomic,
|
||||||
|
mixtureHighQualityAndEconomic,
|
||||||
|
inconsistentEmbeddingModel,
|
||||||
|
} as SelectedDatasetsMode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getMultipleRetrievalConfig = (multipleRetrievalConfig: MultipleRetrievalConfig, selectedDatasets: DataSet[]) => {
|
||||||
|
const {
|
||||||
|
allHighQuality,
|
||||||
|
allHighQualityVectorSearch,
|
||||||
|
allHighQualityFullTextSearch,
|
||||||
|
allEconomic,
|
||||||
|
mixtureHighQualityAndEconomic,
|
||||||
|
inconsistentEmbeddingModel,
|
||||||
|
} = getSelectedDatasetsMode(selectedDatasets)
|
||||||
|
|
||||||
|
const {
|
||||||
|
top_k = DATASET_DEFAULT.top_k,
|
||||||
|
score_threshold,
|
||||||
|
reranking_mode,
|
||||||
|
reranking_model,
|
||||||
|
weights,
|
||||||
|
reranking_enable,
|
||||||
|
} = multipleRetrievalConfig || { top_k: DATASET_DEFAULT.top_k }
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
top_k,
|
||||||
|
score_threshold,
|
||||||
|
reranking_mode,
|
||||||
|
reranking_model,
|
||||||
|
weights,
|
||||||
|
reranking_enable,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allEconomic || mixtureHighQualityAndEconomic || inconsistentEmbeddingModel)
|
||||||
|
result.reranking_mode = RerankingModeEnum.RerankingModel
|
||||||
|
|
||||||
|
if (allHighQuality && !inconsistentEmbeddingModel && reranking_mode === undefined)
|
||||||
|
result.reranking_mode = RerankingModeEnum.WeightedScore
|
||||||
|
|
||||||
|
if (allHighQuality && !inconsistentEmbeddingModel && (reranking_mode === RerankingModeEnum.WeightedScore || reranking_mode === undefined) && !weights) {
|
||||||
|
result.weights = {
|
||||||
|
weight_type: WeightedScoreEnum.Customized,
|
||||||
|
vector_setting: {
|
||||||
|
vector_weight: allHighQualityVectorSearch
|
||||||
|
? DEFAULT_WEIGHTED_SCORE.allHighQualityVectorSearch.semantic
|
||||||
|
: allHighQualityFullTextSearch
|
||||||
|
? DEFAULT_WEIGHTED_SCORE.allHighQualityFullTextSearch.semantic
|
||||||
|
: DEFAULT_WEIGHTED_SCORE.other.semantic,
|
||||||
|
embedding_provider_name: selectedDatasets[0].embedding_model_provider,
|
||||||
|
embedding_model_name: selectedDatasets[0].embedding_model,
|
||||||
|
},
|
||||||
|
keyword_setting: {
|
||||||
|
keyword_weight: allHighQualityVectorSearch
|
||||||
|
? DEFAULT_WEIGHTED_SCORE.allHighQualityVectorSearch.keyword
|
||||||
|
: allHighQualityFullTextSearch
|
||||||
|
? DEFAULT_WEIGHTED_SCORE.allHighQualityFullTextSearch.keyword
|
||||||
|
: DEFAULT_WEIGHTED_SCORE.other.keyword,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
@ -138,8 +138,8 @@ export const appDefaultIconBackground = '#D5F5F6'
|
|||||||
export const NEED_REFRESH_APP_LIST_KEY = 'needRefreshAppList'
|
export const NEED_REFRESH_APP_LIST_KEY = 'needRefreshAppList'
|
||||||
|
|
||||||
export const DATASET_DEFAULT = {
|
export const DATASET_DEFAULT = {
|
||||||
top_k: 2,
|
top_k: 4,
|
||||||
score_threshold: 0.5,
|
score_threshold: 0.8,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const APP_PAGE_LIMIT = 10
|
export const APP_PAGE_LIMIT = 10
|
||||||
|
@ -97,6 +97,8 @@ type IDebugConfiguration = {
|
|||||||
isShowVisionConfig: boolean
|
isShowVisionConfig: boolean
|
||||||
visionConfig: VisionSettings
|
visionConfig: VisionSettings
|
||||||
setVisionConfig: (visionConfig: VisionSettings, noNotice?: boolean) => void
|
setVisionConfig: (visionConfig: VisionSettings, noNotice?: boolean) => void
|
||||||
|
rerankSettingModalOpen: boolean
|
||||||
|
setRerankSettingModalOpen: (rerankSettingModalOpen: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const DebugConfigurationContext = createContext<IDebugConfiguration>({
|
const DebugConfigurationContext = createContext<IDebugConfiguration>({
|
||||||
@ -217,7 +219,7 @@ const DebugConfigurationContext = createContext<IDebugConfiguration>({
|
|||||||
showSelectDataSet: () => { },
|
showSelectDataSet: () => { },
|
||||||
setDataSets: () => { },
|
setDataSets: () => { },
|
||||||
datasetConfigs: {
|
datasetConfigs: {
|
||||||
retrieval_model: RETRIEVE_TYPE.oneWay,
|
retrieval_model: RETRIEVE_TYPE.multiWay,
|
||||||
reranking_model: {
|
reranking_model: {
|
||||||
reranking_provider_name: '',
|
reranking_provider_name: '',
|
||||||
reranking_model_name: '',
|
reranking_model_name: '',
|
||||||
@ -239,6 +241,8 @@ const DebugConfigurationContext = createContext<IDebugConfiguration>({
|
|||||||
transfer_methods: [TransferMethod.remote_url],
|
transfer_methods: [TransferMethod.remote_url],
|
||||||
},
|
},
|
||||||
setVisionConfig: () => { },
|
setVisionConfig: () => { },
|
||||||
|
rerankSettingModalOpen: false,
|
||||||
|
setRerankSettingModalOpen: () => { },
|
||||||
})
|
})
|
||||||
|
|
||||||
export const useDebugConfigurationContext = () => useContext(DebugConfigurationContext)
|
export const useDebugConfigurationContext = () => useContext(DebugConfigurationContext)
|
||||||
|
29
web/hooks/use-knowledge.ts
Normal file
29
web/hooks/use-knowledge.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { useCallback } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
export const useKnowledge = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const formatIndexingTechnique = useCallback((indexingTechnique: string) => {
|
||||||
|
return t(`dataset.indexingTechnique.${indexingTechnique}`)
|
||||||
|
}, [t])
|
||||||
|
|
||||||
|
const formatIndexingMethod = useCallback((indexingMethod: string) => {
|
||||||
|
return t(`dataset.indexingMethod.${indexingMethod}`)
|
||||||
|
}, [t])
|
||||||
|
|
||||||
|
const formatIndexingTechniqueAndMethod = useCallback((indexingTechnique: string, indexingMethod: string) => {
|
||||||
|
let result = formatIndexingTechnique(indexingTechnique)
|
||||||
|
|
||||||
|
if (indexingMethod)
|
||||||
|
result += ` · ${formatIndexingMethod(indexingMethod)}`
|
||||||
|
|
||||||
|
return result
|
||||||
|
}, [formatIndexingTechnique, formatIndexingMethod])
|
||||||
|
|
||||||
|
return {
|
||||||
|
formatIndexingTechnique,
|
||||||
|
formatIndexingMethod,
|
||||||
|
formatIndexingTechniqueAndMethod,
|
||||||
|
}
|
||||||
|
}
|
@ -45,6 +45,29 @@ const translation = {
|
|||||||
},
|
},
|
||||||
docsFailedNotice: 'documents failed to be indexed',
|
docsFailedNotice: 'documents failed to be indexed',
|
||||||
retry: 'Retry',
|
retry: 'Retry',
|
||||||
|
indexingTechnique: {
|
||||||
|
high_quality: 'HQ',
|
||||||
|
economy: 'ECO',
|
||||||
|
},
|
||||||
|
indexingMethod: {
|
||||||
|
semantic_search: 'VECTOR',
|
||||||
|
full_text_search: 'FULL TEXT',
|
||||||
|
hybrid_search: 'HYBRID',
|
||||||
|
},
|
||||||
|
mixtureHighQualityAndEconomicTip: 'The Rerank model is required for mixture of high quality and economical knowledge bases.',
|
||||||
|
inconsistentEmbeddingModelTip: 'The Rerank model is required if the Embedding models of the selected knowledge bases are inconsistent.',
|
||||||
|
retrievalSettings: 'Retrieval Setting',
|
||||||
|
rerankSettings: 'Rerank Setting',
|
||||||
|
weightedScore: {
|
||||||
|
title: 'Weighted Score',
|
||||||
|
description: 'By adjusting the weights assigned, this rerank strategy determines whether to prioritize semantic or keyword matching.',
|
||||||
|
semanticFirst: 'Semantic first',
|
||||||
|
keywordFirst: 'Keyword first',
|
||||||
|
customized: 'Customized',
|
||||||
|
semantic: 'Semantic',
|
||||||
|
keyword: 'Keyword',
|
||||||
|
},
|
||||||
|
nTo1RetrievalLegacy: 'According to product planning, N-to-1 retrieval will be officially deprecated in September. Until then you can still use it normally.',
|
||||||
}
|
}
|
||||||
|
|
||||||
export default translation
|
export default translation
|
||||||
|
@ -45,6 +45,29 @@ const translation = {
|
|||||||
},
|
},
|
||||||
docsFailedNotice: '文档无法被索引',
|
docsFailedNotice: '文档无法被索引',
|
||||||
retry: '重试',
|
retry: '重试',
|
||||||
|
indexingTechnique: {
|
||||||
|
high_quality: '高质量',
|
||||||
|
economy: '经济',
|
||||||
|
},
|
||||||
|
indexingMethod: {
|
||||||
|
semantic_search: '向量检索',
|
||||||
|
full_text_search: '全文检索',
|
||||||
|
hybrid_search: '混合检索',
|
||||||
|
},
|
||||||
|
mixtureHighQualityAndEconomicTip: '混合使用高质量和经济型知识库需要配置 Rerank 模型。',
|
||||||
|
inconsistentEmbeddingModelTip: '当所选知识库配置的 Embedding 模型不一致时,需要配置 Rerank 模型。',
|
||||||
|
retrievalSettings: '召回设置',
|
||||||
|
rerankSettings: 'Rerank 设置',
|
||||||
|
weightedScore: {
|
||||||
|
title: '权重设置',
|
||||||
|
description: '通过调整分配的权重,重新排序策略确定是优先进行语义匹配还是关键字匹配。',
|
||||||
|
semanticFirst: '语义优先',
|
||||||
|
keywordFirst: '关键词优先',
|
||||||
|
customized: '自定义',
|
||||||
|
semantic: '语义',
|
||||||
|
keyword: '关键词',
|
||||||
|
},
|
||||||
|
nTo1RetrievalLegacy: '根据产品规划,N 选 1 召回将于 9 月正式弃用。在那之前,您仍然可以正常使用它。',
|
||||||
}
|
}
|
||||||
|
|
||||||
export default translation
|
export default translation
|
||||||
|
@ -449,3 +449,46 @@ export type ErrorDocsResponse = {
|
|||||||
data: IndexingStatusResponse[]
|
data: IndexingStatusResponse[]
|
||||||
total: number
|
total: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SelectedDatasetsMode = {
|
||||||
|
allHighQuality: boolean
|
||||||
|
allHighQualityVectorSearch: boolean
|
||||||
|
allHighQualityFullTextSearch: boolean
|
||||||
|
allEconomic: boolean
|
||||||
|
mixtureHighQualityAndEconomic: boolean
|
||||||
|
inconsistentEmbeddingModel: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum WeightedScoreEnum {
|
||||||
|
SemanticFirst = 'semantic_first',
|
||||||
|
KeywordFirst = 'keyword_first',
|
||||||
|
Customized = 'customized',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum RerankingModeEnum {
|
||||||
|
RerankingModel = 'reranking_model',
|
||||||
|
WeightedScore = 'weighted_score',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_WEIGHTED_SCORE = {
|
||||||
|
allHighQualityVectorSearch: {
|
||||||
|
semantic: 1.0,
|
||||||
|
keyword: 0,
|
||||||
|
},
|
||||||
|
allHighQualityFullTextSearch: {
|
||||||
|
semantic: 0,
|
||||||
|
keyword: 1.0,
|
||||||
|
},
|
||||||
|
semanticFirst: {
|
||||||
|
semantic: 0.7,
|
||||||
|
keyword: 0.3,
|
||||||
|
},
|
||||||
|
keywordFirst: {
|
||||||
|
semantic: 0.3,
|
||||||
|
keyword: 0.7,
|
||||||
|
},
|
||||||
|
other: {
|
||||||
|
semantic: 0.7,
|
||||||
|
keyword: 0.3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import type { AgentStrategy, ModelModeType, RETRIEVE_TYPE, ToolItem, TtsAutoPlay } from '@/types/app'
|
import type { AgentStrategy, ModelModeType, RETRIEVE_TYPE, ToolItem, TtsAutoPlay } from '@/types/app'
|
||||||
|
import type {
|
||||||
|
RerankingModeEnum,
|
||||||
|
WeightedScoreEnum,
|
||||||
|
} from '@/models/datasets'
|
||||||
export type Inputs = Record<string, string | number | object>
|
export type Inputs = Record<string, string | number | object>
|
||||||
|
|
||||||
export enum PromptMode {
|
export enum PromptMode {
|
||||||
@ -144,13 +148,26 @@ export type DatasetConfigs = {
|
|||||||
}
|
}
|
||||||
top_k: number
|
top_k: number
|
||||||
score_threshold_enabled: boolean
|
score_threshold_enabled: boolean
|
||||||
score_threshold?: number | null
|
score_threshold: number | null | undefined
|
||||||
datasets: {
|
datasets: {
|
||||||
datasets: {
|
datasets: {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
id: string
|
id: string
|
||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
|
reranking_mode?: RerankingModeEnum
|
||||||
|
weights?: {
|
||||||
|
weight_type: WeightedScoreEnum
|
||||||
|
vector_setting: {
|
||||||
|
vector_weight: number
|
||||||
|
embedding_provider_name: string
|
||||||
|
embedding_model_name: string
|
||||||
|
}
|
||||||
|
keyword_setting: {
|
||||||
|
keyword_weight: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reranking_enable?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DebugRequestBody = {
|
export type DebugRequestBody = {
|
||||||
|
@ -2,6 +2,10 @@ import type { AnnotationReplyConfig, ChatPromptConfig, CompletionPromptConfig, D
|
|||||||
import type { CollectionType } from '@/app/components/tools/types'
|
import type { CollectionType } from '@/app/components/tools/types'
|
||||||
import type { LanguagesSupported } from '@/i18n/language'
|
import type { LanguagesSupported } from '@/i18n/language'
|
||||||
import type { Tag } from '@/app/components/base/tag-management/constant'
|
import type { Tag } from '@/app/components/base/tag-management/constant'
|
||||||
|
import type {
|
||||||
|
RerankingModeEnum,
|
||||||
|
WeightedScoreEnum,
|
||||||
|
} from '@/models/datasets'
|
||||||
|
|
||||||
export enum Theme {
|
export enum Theme {
|
||||||
light = 'light',
|
light = 'light',
|
||||||
@ -403,4 +407,16 @@ export type RetrievalConfig = {
|
|||||||
top_k: number
|
top_k: number
|
||||||
score_threshold_enabled: boolean
|
score_threshold_enabled: boolean
|
||||||
score_threshold: number
|
score_threshold: number
|
||||||
|
reranking_mode?: RerankingModeEnum
|
||||||
|
weights?: {
|
||||||
|
weight_type: WeightedScoreEnum
|
||||||
|
vector_setting: {
|
||||||
|
vector_weight: number
|
||||||
|
embedding_provider_name: string
|
||||||
|
embedding_model_name: string
|
||||||
|
}
|
||||||
|
keyword_setting: {
|
||||||
|
keyword_weight: number
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user