diff --git a/web/app/components/rag-pipeline/index.tsx b/web/app/components/rag-pipeline/index.tsx index 1359f10c89..abb8799803 100644 --- a/web/app/components/rag-pipeline/index.tsx +++ b/web/app/components/rag-pipeline/index.tsx @@ -4,7 +4,7 @@ import { } from '@/app/components/workflow/context' import type { InjectWorkflowStoreSliceFn } from '@/app/components/workflow/store' import { generateNewNode } from '@/app/components/workflow/utils' -import dataSourceNodeDefault from '@/app/components/workflow/nodes/data-source/default' +import knowledgeBaseNodeDefault from '@/app/components/workflow/nodes/knowledge-base/default' import { NODE_WIDTH_X_OFFSET, START_INITIAL_POSITION, @@ -13,11 +13,11 @@ import { createRagPipelineSliceSlice } from './store' import RagPipelineMain from './components/rag-pipeline-main' const RagPipeline = () => { - const { newNode: DataSourceNode } = generateNewNode({ + const { newNode: knowledgeBaseNode } = generateNewNode({ data: { - type: dataSourceNodeDefault.metaData.type, - title: 'data-source', - ...dataSourceNodeDefault.defaultValue, + type: knowledgeBaseNodeDefault.metaData.type, + title: 'knowledge-base', + ...knowledgeBaseNodeDefault.defaultValue, }, position: { x: START_INITIAL_POSITION.x + NODE_WIDTH_X_OFFSET, @@ -30,11 +30,11 @@ const RagPipeline = () => { > diff --git a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/hooks.tsx b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/hooks.tsx index 8e2ae4bb1b..2b9c1e7fd5 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/hooks.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/hooks.tsx @@ -1,15 +1,14 @@ -import { useState } from 'react' import { GeneralChunk, ParentChildChunk, QuestionAndAnswer, } from '@/app/components/base/icons/src/vender/knowledge' +import { ChunkStructureEnum } from '../../types' import type { Option } from './type' export const useChunkStructure = () => { - const [chunk, setChunk] = useState('general') const GeneralOption: Option = { - key: 'general', + id: ChunkStructureEnum.general, icon: , title: 'General', description: 'General text chunking mode, the chunks retrieved and recalled are the same.', @@ -17,7 +16,7 @@ export const useChunkStructure = () => { showEffectColor: true, } const ParentChildOption: Option = { - key: 'parent-child', + id: ChunkStructureEnum.parent_child, icon: , title: 'Parent-Child', description: 'Parent-child text chunking mode, the chunks retrieved and recalled are different.', @@ -25,16 +24,16 @@ export const useChunkStructure = () => { showEffectColor: true, } const QuestionAnswerOption: Option = { - key: 'question-answer', + id: ChunkStructureEnum.question_answer, icon: , title: 'Question-Answer', description: 'Question-answer text chunking mode, the chunks retrieved and recalled are different.', } - const optionMap: Record = { - 'general': GeneralOption, - 'parent-child': ParentChildOption, - 'question-answer': QuestionAnswerOption, + const optionMap: Record = { + [ChunkStructureEnum.general]: GeneralOption, + [ChunkStructureEnum.parent_child]: ParentChildOption, + [ChunkStructureEnum.question_answer]: QuestionAnswerOption, } const options = [ @@ -46,7 +45,5 @@ export const useChunkStructure = () => { return { options, optionMap, - chunk, - setChunk, } } diff --git a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/index.tsx b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/index.tsx index 5420cc77ee..3c972d177f 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/index.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/index.tsx @@ -1,12 +1,19 @@ +import { memo } from 'react' import { Field } from '@/app/components/workflow/nodes/_base/components/layout' +import type { ChunkStructureEnum } from '../../types' import OptionCard from '../option-card' import Selector from './selector' import { useChunkStructure } from './hooks' -const ChunkStructure = () => { +type ChunkStructureProps = { + chunkStructure: ChunkStructureEnum + onChunkStructureChange: (value: ChunkStructureEnum) => void +} +const ChunkStructure = ({ + chunkStructure, + onChunkStructureChange, +}: ChunkStructureProps) => { const { - chunk, - setChunk, options, optionMap, } = useChunkStructure() @@ -19,15 +26,15 @@ const ChunkStructure = () => { operation: ( ), }} > - + ) } -export default ChunkStructure +export default memo(ChunkStructure) diff --git a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/selector.tsx b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/selector.tsx index 7844d38a27..b05fd4775e 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/selector.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/selector.tsx @@ -5,13 +5,14 @@ import { PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' import Button from '@/app/components/base/button' +import type { ChunkStructureEnum } from '../../types' import OptionCard from '../option-card' import type { Option } from './type' type SelectorProps = { options: Option[] - value: string - onChange: (key: string) => void + value: ChunkStructureEnum + onChange: (key: ChunkStructureEnum) => void } const Selector = ({ options, @@ -47,15 +48,16 @@ const Selector = ({ { options.map(option => ( { - onChange(option.key) + onChange(option.id) setOpen(false) }} - showHighlightBorder={value === option.key} + showHighlightBorder={value === option.id} > )) } diff --git a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/type.ts b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/type.ts index db6fe122c9..c8a6f8f821 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/type.ts +++ b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/type.ts @@ -1,7 +1,8 @@ import type { ReactNode } from 'react' +import type { ChunkStructureEnum } from '../../types' export type Option = { - key: string + id: ChunkStructureEnum icon: ReactNode title: string description: string diff --git a/web/app/components/workflow/nodes/knowledge-base/components/index-method.tsx b/web/app/components/workflow/nodes/knowledge-base/components/index-method.tsx index c11f18c9de..2ec3686262 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/index-method.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/index-method.tsx @@ -1,4 +1,7 @@ -import { useState } from 'react' +import { + memo, + useCallback, +} from 'react' import { useTranslation } from 'react-i18next' import { RiQuestionLine } from '@remixicon/react' import { @@ -11,10 +14,33 @@ import Input from '@/app/components/base/input' import { Field } from '@/app/components/workflow/nodes/_base/components/layout' import OptionCard from './option-card' import cn from '@/utils/classnames' +import { IndexMethodEnum } from '../types' -const IndexMethod = () => { +type IndexMethodProps = { + indexMethod: IndexMethodEnum + onIndexMethodChange: (value: IndexMethodEnum) => void + keywordNumber: number + onKeywordNumberChange: (value: number) => void +} +const IndexMethod = ({ + indexMethod, + onIndexMethodChange, + keywordNumber, + onKeywordNumberChange, +}: IndexMethodProps) => { const { t } = useTranslation() - const [method, setMethod] = useState('high_quality') + const isHighQuality = indexMethod === IndexMethodEnum.QUALIFIED + const isEconomy = indexMethod === IndexMethodEnum.ECONOMICAL + + const handleIndexMethodChange = useCallback((newIndexMethod: IndexMethodEnum) => { + onIndexMethodChange(newIndexMethod) + }, [onIndexMethodChange]) + + const handleInputChange = useCallback((e: React.ChangeEvent) => { + const value = Number(e.target.value) + if (!Number.isNaN(value)) + onKeywordNumberChange(value) + }, [onKeywordNumberChange]) return ( { }} >
- + id={IndexMethodEnum.QUALIFIED} icon={ } title={t('datasetCreation.stepTwo.qualified')} description={t('datasetSettings.form.indexMethodHighQualityTip')} - showHighlightBorder={method === 'high_quality'} - onClick={() => setMethod('high_quality')} + showHighlightBorder={isHighQuality} + onClick={handleIndexMethodChange} isRecommended > } title={t('datasetSettings.form.indexMethodEconomy')} description={t('datasetSettings.form.indexMethodEconomyTip')} - showChildren={method === 'economy'} - showHighlightBorder={method === 'economy'} - onClick={() => setMethod('economy')} + showChildren={isEconomy} + showHighlightBorder={isEconomy} + onClick={handleIndexMethodChange} effectColor='blue' - showEffectColor={method === 'economy'} + showEffectColor={isEconomy} >
@@ -68,15 +96,15 @@ const IndexMethod = () => {
{ - console.log('change') - }} + value={keywordNumber} + onChange={onKeywordNumberChange} />
@@ -85,4 +113,4 @@ const IndexMethod = () => { ) } -export default IndexMethod +export default memo(IndexMethod) diff --git a/web/app/components/workflow/nodes/knowledge-base/components/option-card.tsx b/web/app/components/workflow/nodes/knowledge-base/components/option-card.tsx index 6a6bb98275..73c1c3ba17 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/option-card.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/option-card.tsx @@ -16,7 +16,9 @@ const HEADER_EFFECT_MAP: Record = { 'orange': , 'purple': , } -type OptionCardProps = { +type OptionCardProps = { + id: T + className?: string showHighlightBorder?: boolean showRadio?: boolean radioIsActive?: boolean @@ -28,9 +30,11 @@ type OptionCardProps = { showChildren?: boolean effectColor?: string showEffectColor?: boolean - onClick?: () => void + onClick?: (id: T) => void } -const OptionCard = ({ +const OptionCard = memo(({ + id, + className, showHighlightBorder, showRadio, radioIsActive, @@ -43,17 +47,18 @@ const OptionCard = ({ effectColor, showEffectColor, onClick, -}: OptionCardProps) => { +}) => { return (
onClick?.(id)} >
{ effectColor && showEffectColor && ( @@ -110,6 +115,6 @@ const OptionCard = ({ }
) -} +}) as (props: OptionCardProps) => JSX.Element -export default memo(OptionCard) +export default OptionCard diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/full-text-search.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/full-text-search.tsx deleted file mode 100644 index 13b602b08d..0000000000 --- a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/full-text-search.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { FullTextSearch } from '@/app/components/base/icons/src/vender/knowledge' -import OptionCard from '../option-card' - -const FullTextSearchCard = () => { - return ( - } - title='Full-Text Search' - description="Execute full-text search and vector searches simultaneously, re-rank to select the best match for the user's query. Users can choose to set weights or configure to a Rerank model." - effectColor='purple' - > -
-
Vector Search Settings
-
Additional Settings
-
-
- ) -} - -export default FullTextSearchCard diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/hooks.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/hooks.tsx new file mode 100644 index 0000000000..701c4115c1 --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/hooks.tsx @@ -0,0 +1,65 @@ +import { + FullTextSearch, + HybridSearch, + VectorSearch, +} from '@/app/components/base/icons/src/vender/knowledge' +import { + HybridSearchModeEnum, + RetrievalSearchMethodEnum, +} from '../../types' +import type { + HybridSearchModeOption, + Option, +} from './type' + +export const useRetrievalSetting = () => { + const VectorSearchOption: Option = { + id: RetrievalSearchMethodEnum.semantic, + icon: VectorSearch as any, + title: 'Vector Search', + description: 'Generate query embeddings and search for the text chunk most similar to its vector representation.', + effectColor: 'purple', + } + const FullTextSearchOption: Option = { + id: RetrievalSearchMethodEnum.fullText, + icon: FullTextSearch as any, + title: 'Full-Text Search', + description: 'Execute full-text search and vector searches simultaneously, re-rank to select the best match for the user\'s query. Users can choose to set weights or configure to a Rerank model.', + effectColor: 'purple', + } + const HybridSearchOption: Option = { + id: RetrievalSearchMethodEnum.hybrid, + icon: HybridSearch as any, + title: 'Hybrid Search', + description: 'Execute full-text search and vector searches simultaneously, re-rank to select the best match for the user\'s query. Users can choose to set weights or configure to a Rerank model.', + effectColor: 'purple', + } + + const options = [ + VectorSearchOption, + FullTextSearchOption, + HybridSearchOption, + ] + + const WeightedScoreModeOption: HybridSearchModeOption = { + id: HybridSearchModeEnum.WeightedScore, + title: 'Weighted Score', + description: 'By adjusting the weights assigned, this rerank strategy determines whether to prioritize semantic or keyword matching.', + } + + const RerankModelModeOption: HybridSearchModeOption = { + id: HybridSearchModeEnum.RerankingModel, + title: 'Rerank Model', + description: 'Rerank model will reorder the candidate document list based on the semantic match with user query, improving the results of semantic ranking.', + } + + const hybridSearchModeOptions = [ + WeightedScoreModeOption, + RerankModelModeOption, + ] + + return { + options, + hybridSearchModeOptions, + } +} diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/hybrid-search.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/hybrid-search.tsx deleted file mode 100644 index 7eec4f97c7..0000000000 --- a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/hybrid-search.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { HybridSearch } from '@/app/components/base/icons/src/vender/knowledge' -import OptionCard from '../option-card' - -const HybridSearchCard = () => { - return ( - } - title='Hybrid Search' - description="Execute full-text search and vector searches simultaneously, re-rank to select the best match for the user's query. Users can choose to set weights or configure to a Rerank model." - effectColor='purple' - isRecommended - > -
-
Vector Search Settings
-
Additional Settings
-
-
- ) -} - -export default HybridSearchCard diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/index.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/index.tsx index bbcba2c72a..1f8a50abbc 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/index.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/index.tsx @@ -1,9 +1,139 @@ +import { + memo, + useCallback, +} from 'react' import { Field } from '@/app/components/workflow/nodes/_base/components/layout' -import VectorSearchCard from './vector-search' -import FullTextSearchCard from './full-text-search' -import HybridSearchCard from './hybrid-search' +import cn from '@/utils/classnames' +import WeightedScoreComponent from '@/app/components/app/configuration/dataset-config/params-config/weighted-score' +import { DEFAULT_WEIGHTED_SCORE } from '@/models/datasets' +import { + HybridSearchModeEnum, + RetrievalSearchMethodEnum, +} from '../../types' +import type { + RerankingModel, + WeightedScore, +} from '../../types' +import OptionCard from '../option-card' +import { useRetrievalSetting } from './hooks' +import type { Option } from './type' +import TopKAndScoreThreshold from './top-k-and-score-threshold' +import RerankingModelSelector from './reranking-model-selector' + +type RetrievalSettingProps = { + searchMethod: RetrievalSearchMethodEnum + onRetrievalSearchMethodChange: (value: RetrievalSearchMethodEnum) => void + hybridSearchMode: HybridSearchModeEnum + onHybridSearchModeChange: (value: HybridSearchModeEnum) => void + rerankingModel?: RerankingModel + onRerankingModelChange: (model: RerankingModel) => void + weightedScore?: WeightedScore + onWeightedScoreChange: (value: { value: number[] }) => void +} +const RetrievalSetting = ({ + searchMethod, + onRetrievalSearchMethodChange, + hybridSearchMode, + onHybridSearchModeChange, + weightedScore, + onWeightedScoreChange, + rerankingModel, + onRerankingModelChange, +}: RetrievalSettingProps) => { + const { + options, + hybridSearchModeOptions, + } = useRetrievalSetting() + + const renderOptionCard = useCallback((option: Option) => { + const Icon = option.icon + const isActive = searchMethod === option.id + const isHybridSearch = searchMethod === RetrievalSearchMethodEnum.hybrid + const isHybridSearchWeightedScoreMode = hybridSearchMode === HybridSearchModeEnum.WeightedScore + const weightedScoreValue = (() => { + const sematicWeightedScore = weightedScore?.vector_setting.vector_weight ?? DEFAULT_WEIGHTED_SCORE.other.semantic + const keywordWeightedScore = weightedScore?.keyword_setting.keyword_weight ?? DEFAULT_WEIGHTED_SCORE.other.keyword + const mergedValue = [sematicWeightedScore, keywordWeightedScore] + + return { + value: mergedValue, + } + })() + + return ( + + } + title={option.title} + description={option.description} + effectColor={option.effectColor} + isRecommended={option.id === RetrievalSearchMethodEnum.hybrid} + onClick={onRetrievalSearchMethodChange} + showChildren={isActive} + showHighlightBorder={isActive} + showEffectColor={isActive} + > +
+ { + isHybridSearch && ( +
+ { + hybridSearchModeOptions.map(hybridOption => ( + + )) + } +
+ ) + } + { + isHybridSearch && isHybridSearchWeightedScoreMode && ( + + ) + } + { + !(isHybridSearch && hybridSearchMode === HybridSearchModeEnum.WeightedScore) && ( + + ) + } + +
+
+ ) + }, [ + searchMethod, + onRetrievalSearchMethodChange, + hybridSearchModeOptions, + hybridSearchMode, + onHybridSearchModeChange, + rerankingModel, + onRerankingModelChange, + weightedScore, + onWeightedScoreChange, + ]) -const RetrievalSetting = () => { return ( { }} >
- - - + {options.map(renderOptionCard)}
) } -export default RetrievalSetting +export default memo(RetrievalSetting) diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/reranking-model-selector.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/reranking-model-selector.tsx new file mode 100644 index 0000000000..609f26fbb2 --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/reranking-model-selector.tsx @@ -0,0 +1,48 @@ +import { + memo, + useMemo, +} from 'react' +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 type { DefaultModel } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { RerankingModel } from '../../types' + +type RerankingModelSelectorProps = { + rerankingModel?: RerankingModel + onRerankingModelChange?: (model: RerankingModel) => void +} +const RerankingModelSelector = ({ + rerankingModel, + onRerankingModelChange, +}: RerankingModelSelectorProps) => { + const { + modelList: rerankModelList, + } = useModelListAndDefaultModel(ModelTypeEnum.rerank) + const rerankModel = useMemo(() => { + if (!rerankingModel) + return undefined + + return { + provider_name: rerankingModel.reranking_provider_name, + model_name: rerankingModel.reranking_model_name, + } + }, [rerankingModel]) + + const handleRerankingModelChange = (model: DefaultModel) => { + onRerankingModelChange?.({ + reranking_provider_name: model.provider, + reranking_model_name: model.model, + }) + } + + return ( + + ) +} + +export default memo(RerankingModelSelector) diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/top-k-and-score-threshold.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/top-k-and-score-threshold.tsx new file mode 100644 index 0000000000..65ea8de195 --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/top-k-and-score-threshold.tsx @@ -0,0 +1,42 @@ +import { memo } from 'react' +import Tooltip from '@/app/components/base/tooltip' +import Input from '@/app/components/base/input' +import Switch from '@/app/components/base/switch' + +const TopKAndScoreThreshold = () => { + return ( +
+
+
+ Top k + +
+ +
+
+
+ +
+ Score Threshold +
+ +
+ +
+
+ ) +} + +export default memo(TopKAndScoreThreshold) diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/type.ts b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/type.ts new file mode 100644 index 0000000000..b72a830d4b --- /dev/null +++ b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/type.ts @@ -0,0 +1,20 @@ +import type { ComponentType } from 'react' +import type { + HybridSearchModeEnum, + RetrievalSearchMethodEnum, +} from '../../types' + +export type Option = { + id: RetrievalSearchMethodEnum + icon: ComponentType + title: any + description: string + effectColor?: string + showEffectColor?: boolean, +} + +export type HybridSearchModeOption = { + id: HybridSearchModeEnum + title: string + description: string +} diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/vector-search.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/vector-search.tsx deleted file mode 100644 index 8d17858b1c..0000000000 --- a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/vector-search.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { VectorSearch } from '@/app/components/base/icons/src/vender/knowledge' -import OptionCard from '../option-card' - -const VectorSearchCard = () => { - return ( - } - title='Vector Search' - description='Generate query embeddings and search for the text chunk most similar to its vector representation.' - effectColor='purple' - > -
-
Vector Search Settings
-
Additional Settings
-
-
- ) -} - -export default VectorSearchCard diff --git a/web/app/components/workflow/nodes/knowledge-base/default.ts b/web/app/components/workflow/nodes/knowledge-base/default.ts index f121972e20..48e2d7fca5 100644 --- a/web/app/components/workflow/nodes/knowledge-base/default.ts +++ b/web/app/components/workflow/nodes/knowledge-base/default.ts @@ -1,5 +1,11 @@ import type { NodeDefault } from '../../types' import type { KnowledgeBaseNodeType } from './types' +import { + ChunkStructureEnum, + HybridSearchModeEnum, + IndexMethodEnum, + RetrievalSearchMethodEnum, +} from './types' import { genNodeMetaData } from '@/app/components/workflow/utils' import { BlockEnum } from '@/app/components/workflow/types' @@ -9,7 +15,19 @@ const metaData = genNodeMetaData({ }) const nodeDefault: NodeDefault = { metaData, - defaultValue: {}, + defaultValue: { + index_chunk_variable_selector: [], + chunk_structure: ChunkStructureEnum.general, + indexing_technique: IndexMethodEnum.QUALIFIED, + keyword_number: 10, + retrieval_model: { + search_method: RetrievalSearchMethodEnum.hybrid, + top_k: 2, + score_threshold_enabled: false, + score_threshold: 0.5, + hybridSearchMode: HybridSearchModeEnum.WeightedScore, + }, + }, checkValid() { return { isValid: true, diff --git a/web/app/components/workflow/nodes/knowledge-base/hooks/use-config.ts b/web/app/components/workflow/nodes/knowledge-base/hooks/use-config.ts index 38b4d9fe91..12e8010afd 100644 --- a/web/app/components/workflow/nodes/knowledge-base/hooks/use-config.ts +++ b/web/app/components/workflow/nodes/knowledge-base/hooks/use-config.ts @@ -1,19 +1,107 @@ import { useCallback, - useRef, } from 'react' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' -import type { KnowledgeBaseNodeType } from '../types' +import { useStoreApi } from 'reactflow' +import { useNodeDataUpdate } from '@/app/components/workflow/hooks' +import type { + ChunkStructureEnum, + HybridSearchModeEnum, + IndexMethodEnum, + KnowledgeBaseNodeType, + RerankingModel, + RetrievalSearchMethodEnum, +} from '../types' -export const useConfig = (id: string, payload: KnowledgeBaseNodeType) => { - const { - inputs, - setInputs, - } = useNodeCrud(id, payload) - const ref = useRef(inputs) +export const useConfig = (id: string) => { + const store = useStoreApi() + const { handleNodeDataUpdateWithSyncDraft } = useNodeDataUpdate() - const handleInputsChange = useCallback((newInputs: KnowledgeBaseNodeType) => { - setInputs(newInputs) - ref.current = newInputs - }, [setInputs, ref]) + const getNodeData = useCallback(() => { + const { getNodes } = store.getState() + const nodes = getNodes() + + return nodes.find(node => node.id === id) + }, [store, id]) + + const handleNodeDataUpdate = useCallback((data: Partial) => { + handleNodeDataUpdateWithSyncDraft({ + id, + data, + }) + }, [id, handleNodeDataUpdateWithSyncDraft]) + + const handleChunkStructureChange = useCallback((chunkStructure: ChunkStructureEnum) => { + handleNodeDataUpdate({ chunk_structure: chunkStructure }) + }, [handleNodeDataUpdate]) + + const handleIndexMethodChange = useCallback((indexMethod: IndexMethodEnum) => { + handleNodeDataUpdate({ indexing_technique: indexMethod }) + }, [handleNodeDataUpdate]) + + const handleKeywordNumberChange = useCallback((keywordNumber: number) => { + handleNodeDataUpdate({ keyword_number: keywordNumber }) + }, [handleNodeDataUpdate]) + + const handleRetrievalSearchMethodChange = useCallback((searchMethod: RetrievalSearchMethodEnum) => { + const nodeData = getNodeData() + handleNodeDataUpdate({ + retrieval_model: { + ...nodeData?.data.retrieval_model, + search_method: searchMethod, + }, + }) + }, [getNodeData, handleNodeDataUpdate]) + + const handleHybridSearchModeChange = useCallback((hybridSearchMode: HybridSearchModeEnum) => { + const nodeData = getNodeData() + handleNodeDataUpdate({ + retrieval_model: { + ...nodeData?.data.retrieval_model, + hybridSearchMode, + }, + }) + }, [getNodeData, handleNodeDataUpdate]) + + const handleWeighedScoreChange = useCallback((weightedScore: { value: number[] }) => { + const nodeData = getNodeData() + handleNodeDataUpdate({ + retrieval_model: { + ...nodeData?.data.retrieval_model, + weights: { + weight_type: 'weighted_score', + vector_setting: { + vector_weight: weightedScore.value[0], + embedding_provider_name: '', + embedding_model_name: '', + }, + keyword_setting: { + keyword_weight: weightedScore.value[1], + }, + }, + }, + }) + }, [getNodeData, handleNodeDataUpdate]) + + const handleRerankingModelChange = useCallback((rerankingModel: RerankingModel) => { + const nodeData = getNodeData() + handleNodeDataUpdate({ + retrieval_model: { + ...nodeData?.data.retrieval_model, + reranking_model: { + reranking_provider_name: rerankingModel.reranking_provider_name, + reranking_model_name: rerankingModel.reranking_model_name, + }, + }, + }) + }, [getNodeData, handleNodeDataUpdate]) + + return { + handleChunkStructureChange, + handleIndexMethodChange, + handleKeywordNumberChange, + handleRetrievalSearchMethodChange, + handleHybridSearchModeChange, + handleWeighedScoreChange, + handleRerankingModelChange, + } } diff --git a/web/app/components/workflow/nodes/knowledge-base/panel.tsx b/web/app/components/workflow/nodes/knowledge-base/panel.tsx index 344a79a207..2e95176f44 100644 --- a/web/app/components/workflow/nodes/knowledge-base/panel.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/panel.tsx @@ -1,11 +1,17 @@ import type { FC } from 'react' -import { memo } from 'react' +import { + memo, +} from 'react' import type { KnowledgeBaseNodeType } from './types' +import { + IndexMethodEnum, +} from './types' import InputVariable from './components/input-variable' import ChunkStructure from './components/chunk-structure' import IndexMethod from './components/index-method' import RetrievalSetting from './components/retrieval-setting' import EmbeddingModel from './components/embedding-model' +import { useConfig } from './hooks/use-config' import type { NodePanelProps } from '@/app/components/workflow/types' import { Group, @@ -13,7 +19,20 @@ import { } from '@/app/components/workflow/nodes/_base/components/layout' import Split from '../_base/components/split' -const Panel: FC> = () => { +const Panel: FC> = ({ + id, + data, +}) => { + const { + handleChunkStructureChange, + handleIndexMethodChange, + handleKeywordNumberChange, + handleRetrievalSearchMethodChange, + handleHybridSearchModeChange, + handleWeighedScoreChange, + handleRerankingModelChange, + } = useConfig(id) + return (
@@ -23,16 +42,37 @@ const Panel: FC> = () => { className='py-3' withBorderBottom > - +
- - + + { + data.indexing_technique === IndexMethodEnum.QUALIFIED && ( + + ) + }
- +
diff --git a/web/app/components/workflow/nodes/knowledge-base/types.ts b/web/app/components/workflow/nodes/knowledge-base/types.ts index a1ceab42a5..a8f84d31a0 100644 --- a/web/app/components/workflow/nodes/knowledge-base/types.ts +++ b/web/app/components/workflow/nodes/knowledge-base/types.ts @@ -1,3 +1,52 @@ import type { CommonNodeType } from '@/app/components/workflow/types' +import type { IndexingType } from '@/app/components/datasets/create/step-two' +import type { RETRIEVE_METHOD } from '@/types/app' +import type { WeightedScoreEnum } from '@/models/datasets' +import type { RerankingModeEnum } from '@/models/datasets' +export { WeightedScoreEnum } from '@/models/datasets' +export { IndexingType as IndexMethodEnum } from '@/app/components/datasets/create/step-two' +export { RETRIEVE_METHOD as RetrievalSearchMethodEnum } from '@/types/app' +export { RerankingModeEnum as HybridSearchModeEnum } from '@/models/datasets' -export type KnowledgeBaseNodeType = CommonNodeType +export enum ChunkStructureEnum { + general = 'general', + parent_child = 'parent-child', + question_answer = 'question-answer', +} + +export type RerankingModel = { + reranking_provider_name: string + reranking_model_name: string +} + +export type WeightedScore = { + weight_type: WeightedScoreEnum + vector_setting: { + vector_weight: number + embedding_provider_name: string + embedding_model_name: string + } + keyword_setting: { + keyword_weight: number + } +} + +export type RetrievalSetting = { + search_method: RETRIEVE_METHOD + reranking_enable?: boolean + reranking_model?: RerankingModel + weights?: WeightedScore + top_k: number + score_threshold_enabled: boolean + score_threshold: number + hybridSearchMode: RerankingModeEnum +} +export type KnowledgeBaseNodeType = CommonNodeType & { + index_chunk_variable_selector: string[] + chunk_structure: ChunkStructureEnum + indexing_technique: IndexingType + embedding_model?: string + embedding_model_provider?: string + keyword_number: number + retrieval_model: RetrievalSetting +} diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index e6d019e8ca..d100cceafa 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -42,7 +42,7 @@ export enum BlockEnum { LoopStart = 'loop-start', LoopEnd = 'loop-end', DataSource = 'data-source', - KnowledgeBase = 'knowledge-base', + KnowledgeBase = 'knowledge-index', } export enum ControlMode {