| 'use client' | |
| import type { FC } from 'react' | |
| import React, { useCallback } from 'react' | |
| import { useTranslation } from 'react-i18next' | |
| import cn from '@/utils/classnames' | |
| import TopKItem from '@/app/components/base/param-item/top-k-item' | |
| import ScoreThresholdItem from '@/app/components/base/param-item/score-threshold-item' | |
| import { RETRIEVE_METHOD } from '@/types/app' | |
| import Switch from '@/app/components/base/switch' | |
| import Tooltip from '@/app/components/base/tooltip' | |
| import type { RetrievalConfig } from '@/types/app' | |
| import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' | |
| import { useCurrentProviderAndModel, useModelListAndDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks' | |
| import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' | |
| import { | |
| DEFAULT_WEIGHTED_SCORE, | |
| RerankingModeEnum, | |
| WeightedScoreEnum, | |
| } from '@/models/datasets' | |
| import WeightedScore from '@/app/components/app/configuration/dataset-config/params-config/weighted-score' | |
| import Toast from '@/app/components/base/toast' | |
| type Props = { | |
| type: RETRIEVE_METHOD | |
| value: RetrievalConfig | |
| onChange: (value: RetrievalConfig) => void | |
| } | |
| const RetrievalParamConfig: FC<Props> = ({ | |
| type, | |
| value, | |
| onChange, | |
| }) => { | |
| const { t } = useTranslation() | |
| const canToggleRerankModalEnable = type !== RETRIEVE_METHOD.hybrid | |
| const isEconomical = type === RETRIEVE_METHOD.invertedIndex | |
| const { | |
| defaultModel: rerankDefaultModel, | |
| modelList: rerankModelList, | |
| } = useModelListAndDefaultModel(ModelTypeEnum.rerank) | |
| const { | |
| currentModel, | |
| } = useCurrentProviderAndModel( | |
| rerankModelList, | |
| rerankDefaultModel | |
| ? { | |
| ...rerankDefaultModel, | |
| provider: rerankDefaultModel.provider.provider, | |
| } | |
| : undefined, | |
| ) | |
| const handleDisabledSwitchClick = useCallback(() => { | |
| if (!currentModel) | |
| Toast.notify({ type: 'error', message: t('workflow.errorMsg.rerankModelRequired') }) | |
| }, [currentModel, rerankDefaultModel, t]) | |
| const isHybridSearch = type === RETRIEVE_METHOD.hybrid | |
| const rerankModel = (() => { | |
| if (value.reranking_model) { | |
| return { | |
| provider_name: value.reranking_model.reranking_provider_name, | |
| model_name: value.reranking_model.reranking_model_name, | |
| } | |
| } | |
| else if (rerankDefaultModel) { | |
| return { | |
| provider_name: rerankDefaultModel.provider.provider, | |
| model_name: rerankDefaultModel.model, | |
| } | |
| } | |
| })() | |
| 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: DEFAULT_WEIGHTED_SCORE.other.semantic, | |
| embedding_provider_name: '', | |
| embedding_model_name: '', | |
| }, | |
| keyword_setting: { | |
| keyword_weight: DEFAULT_WEIGHTED_SCORE.other.keyword, | |
| }, | |
| } | |
| } | |
| 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 ( | |
| <div> | |
| {!isEconomical && !isHybridSearch && ( | |
| <div> | |
| <div className='flex h-8 items-center text-[13px] font-medium text-gray-900 space-x-2'> | |
| {canToggleRerankModalEnable && ( | |
| <div | |
| className='flex items-center' | |
| onClick={handleDisabledSwitchClick} | |
| > | |
| <Switch | |
| size='md' | |
| defaultValue={currentModel ? value.reranking_enable : false} | |
| onChange={(v) => { | |
| onChange({ | |
| ...value, | |
| reranking_enable: v, | |
| }) | |
| }} | |
| disabled={!currentModel} | |
| /> | |
| </div> | |
| )} | |
| <div className='flex items-center'> | |
| <span className='mr-0.5'>{t('common.modelProvider.rerankModel.key')}</span> | |
| <Tooltip | |
| popupContent={ | |
| <div className="w-[200px]">{t('common.modelProvider.rerankModel.tip')}</div> | |
| } | |
| /> | |
| </div> | |
| </div> | |
| <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> | |
| )} | |
| { | |
| !isHybridSearch && ( | |
| <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} | |
| /> | |
| {(!isEconomical && !(value.search_method === RETRIEVE_METHOD.fullText && !value.reranking_enable)) && ( | |
| <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> | |
| ) | |
| } | |
| { | |
| 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>} | |
| triggerClassName='ml-0.5 w-3.5 h-3.5' | |
| /> | |
| </div> | |
| )) | |
| } | |
| </div> | |
| { | |
| value.reranking_mode === RerankingModeEnum.WeightedScore && ( | |
| <WeightedScore | |
| value={{ | |
| value: [ | |
| value.weights!.vector_setting.vector_weight, | |
| value.weights!.keyword_setting.keyword_weight, | |
| ], | |
| }} | |
| onChange={(v) => { | |
| onChange({ | |
| ...value, | |
| weights: { | |
| ...value.weights!, | |
| 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> | |
| ) | |
| } | |
| export default React.memo(RetrievalParamConfig) | |