Merge branch 'feat/rag-pipeline' of https://github.com/langgenius/dify into feat/rag-pipeline

This commit is contained in:
twwu 2025-05-14 14:49:10 +08:00
commit df928772c0
15 changed files with 89 additions and 27 deletions

View File

@ -21,10 +21,12 @@ type Value = {
type WeightedScoreProps = { type WeightedScoreProps = {
value: Value value: Value
onChange: (value: Value) => void onChange: (value: Value) => void
readonly?: boolean
} }
const WeightedScore = ({ const WeightedScore = ({
value, value,
onChange = noop, onChange = noop,
readonly = false,
}: WeightedScoreProps) => { }: WeightedScoreProps) => {
const { t } = useTranslation() const { t } = useTranslation()
@ -37,7 +39,7 @@ const WeightedScore = ({
min={0} min={0}
step={0.1} step={0.1}
value={value.value[0]} value={value.value[0]}
onChange={v => onChange({ value: [v, (10 - v * 10) / 10] })} onChange={v => !readonly && onChange({ value: [v, (10 - v * 10) / 10] })}
trackClassName='weightedScoreSliderTrack' trackClassName='weightedScoreSliderTrack'
/> />
<div className='mt-3 flex justify-between'> <div className='mt-3 flex justify-between'>

View File

@ -18,3 +18,4 @@ export * from './use-format-time-from-now'
export * from './use-nodes-meta-data' export * from './use-nodes-meta-data'
export * from './use-available-blocks' export * from './use-available-blocks'
export * from './use-workflow-refresh-draft' export * from './use-workflow-refresh-draft'
export * from './use-tool-icon'

View File

@ -0,0 +1,34 @@
import {
useMemo,
} from 'react'
import type {
Node,
} from '../types'
import {
BlockEnum,
} from '../types'
import {
useStore,
} from '../store'
import { CollectionType } from '@/app/components/tools/types'
import { canFindTool } from '@/utils'
export const useToolIcon = (data: Node['data']) => {
const buildInTools = useStore(s => s.buildInTools)
const customTools = useStore(s => s.customTools)
const workflowTools = useStore(s => s.workflowTools)
const toolIcon = useMemo(() => {
if (data.type === BlockEnum.Tool) {
let targetTools = buildInTools
if (data.provider_type === CollectionType.builtIn)
targetTools = buildInTools
else if (data.provider_type === CollectionType.custom)
targetTools = customTools
else
targetTools = workflowTools
return targetTools.find(toolWithProvider => canFindTool(toolWithProvider.id, data.provider_id))?.icon
}
}, [data, buildInTools, customTools, workflowTools])
return toolIcon
}

View File

@ -1,6 +1,5 @@
import { import {
useCallback, useCallback,
useMemo,
} from 'react' } from 'react'
import { uniqBy } from 'lodash-es' import { uniqBy } from 'lodash-es'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -39,11 +38,9 @@ import {
fetchAllCustomTools, fetchAllCustomTools,
fetchAllWorkflowTools, fetchAllWorkflowTools,
} from '@/service/tools' } from '@/service/tools'
import { CollectionType } from '@/app/components/tools/types'
import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants' import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants'
import { CUSTOM_LOOP_START_NODE } from '@/app/components/workflow/nodes/loop-start/constants' import { CUSTOM_LOOP_START_NODE } from '@/app/components/workflow/nodes/loop-start/constants'
import { basePath } from '@/utils/var' import { basePath } from '@/utils/var'
import { canFindTool } from '@/utils'
export const useIsChatMode = () => { export const useIsChatMode = () => {
const appDetail = useAppStore(s => s.appDetail) const appDetail = useAppStore(s => s.appDetail)
@ -489,26 +486,6 @@ export const useNodesReadOnly = () => {
} }
} }
export const useToolIcon = (data: Node['data']) => {
const buildInTools = useStore(s => s.buildInTools)
const customTools = useStore(s => s.customTools)
const workflowTools = useStore(s => s.workflowTools)
const toolIcon = useMemo(() => {
if (data.type === BlockEnum.Tool) {
let targetTools = buildInTools
if (data.provider_type === CollectionType.builtIn)
targetTools = buildInTools
else if (data.provider_type === CollectionType.custom)
targetTools = customTools
else
targetTools = workflowTools
return targetTools.find(toolWithProvider => canFindTool(toolWithProvider.id, data.provider_id))?.icon
}
}, [data, buildInTools, customTools, workflowTools])
return toolIcon
}
export const useIsNodeInIteration = (iterationId: string) => { export const useIsNodeInIteration = (iterationId: string) => {
const store = useStoreApi() const store = useStoreApi()

View File

@ -8,10 +8,12 @@ import { useChunkStructure } from './hooks'
type ChunkStructureProps = { type ChunkStructureProps = {
chunkStructure: ChunkStructureEnum chunkStructure: ChunkStructureEnum
onChunkStructureChange: (value: ChunkStructureEnum) => void onChunkStructureChange: (value: ChunkStructureEnum) => void
readonly?: boolean
} }
const ChunkStructure = ({ const ChunkStructure = ({
chunkStructure, chunkStructure,
onChunkStructureChange, onChunkStructureChange,
readonly = false,
}: ChunkStructureProps) => { }: ChunkStructureProps) => {
const { const {
options, options,
@ -28,6 +30,7 @@ const ChunkStructure = ({
options={options} options={options}
value={chunkStructure} value={chunkStructure}
onChange={onChunkStructureChange} onChange={onChunkStructureChange}
readonly={readonly}
/> />
), ),
}} }}

View File

@ -13,11 +13,13 @@ type SelectorProps = {
options: Option[] options: Option[]
value: ChunkStructureEnum value: ChunkStructureEnum
onChange: (key: ChunkStructureEnum) => void onChange: (key: ChunkStructureEnum) => void
readonly?: boolean
} }
const Selector = ({ const Selector = ({
options, options,
value, value,
onChange, onChange,
readonly,
}: SelectorProps) => { }: SelectorProps) => {
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
@ -31,7 +33,11 @@ const Selector = ({
open={open} open={open}
onOpenChange={setOpen} onOpenChange={setOpen}
> >
<PortalToFollowElemTrigger onClick={() => setOpen(!open)}> <PortalToFollowElemTrigger onClick={() => {
if (readonly)
return
setOpen(!open)
}}>
<Button <Button
size='small' size='small'
variant='ghost-accent' variant='ghost-accent'
@ -54,6 +60,8 @@ const Selector = ({
title={option.title} title={option.title}
description={option.description} description={option.description}
onClick={() => { onClick={() => {
if (readonly)
return
onChange(option.id) onChange(option.id)
setOpen(false) setOpen(false)
}} }}

View File

@ -15,11 +15,13 @@ type EmbeddingModelProps = {
embeddingModel: string embeddingModel: string
embeddingModelProvider: string embeddingModelProvider: string
}) => void }) => void
readonly?: boolean
} }
const EmbeddingModel = ({ const EmbeddingModel = ({
embeddingModel, embeddingModel,
embeddingModelProvider, embeddingModelProvider,
onEmbeddingModelChange, onEmbeddingModelChange,
readonly = false,
}: EmbeddingModelProps) => { }: EmbeddingModelProps) => {
const { const {
modelList: embeddingModelList, modelList: embeddingModelList,
@ -51,6 +53,7 @@ const handleRerankingModelChange = (model: DefaultModel) => {
defaultModel={embeddingModelConfig && { provider: embeddingModelConfig.providerName, model: embeddingModelConfig.modelName }} defaultModel={embeddingModelConfig && { provider: embeddingModelConfig.providerName, model: embeddingModelConfig.modelName }}
modelList={embeddingModelList} modelList={embeddingModelList}
onSelect={handleRerankingModelChange} onSelect={handleRerankingModelChange}
readonly={readonly}
/> />
</Field> </Field>
) )

View File

@ -21,12 +21,14 @@ type IndexMethodProps = {
onIndexMethodChange: (value: IndexMethodEnum) => void onIndexMethodChange: (value: IndexMethodEnum) => void
keywordNumber: number keywordNumber: number
onKeywordNumberChange: (value: number) => void onKeywordNumberChange: (value: number) => void
readonly?: boolean
} }
const IndexMethod = ({ const IndexMethod = ({
indexMethod, indexMethod,
onIndexMethodChange, onIndexMethodChange,
keywordNumber, keywordNumber,
onKeywordNumberChange, onKeywordNumberChange,
readonly = false,
}: IndexMethodProps) => { }: IndexMethodProps) => {
const { t } = useTranslation() const { t } = useTranslation()
const isHighQuality = indexMethod === IndexMethodEnum.QUALIFIED const isHighQuality = indexMethod === IndexMethodEnum.QUALIFIED
@ -95,11 +97,13 @@ const IndexMethod = ({
</Tooltip> </Tooltip>
</div> </div>
<Slider <Slider
disabled={readonly}
className='mr-3 w-24 shrink-0' className='mr-3 w-24 shrink-0'
value={keywordNumber} value={keywordNumber}
onChange={onKeywordNumberChange} onChange={onKeywordNumberChange}
/> />
<Input <Input
disabled={readonly}
className='shrink-0' className='shrink-0'
wrapperClassName='shrink-0 w-[72px]' wrapperClassName='shrink-0 w-[72px]'
type='number' type='number'

View File

@ -7,11 +7,13 @@ type InputVariableProps = {
nodeId: string nodeId: string
inputVariable?: string[] inputVariable?: string[]
onInputVariableChange: (inputVariable: string | ValueSelector) => void onInputVariableChange: (inputVariable: string | ValueSelector) => void
readonly?: boolean
} }
const InputVariable = ({ const InputVariable = ({
nodeId, nodeId,
inputVariable = [], inputVariable = [],
onInputVariableChange, onInputVariableChange,
readonly = false,
}: InputVariableProps) => { }: InputVariableProps) => {
return ( return (
<Field <Field
@ -25,7 +27,7 @@ const InputVariable = ({
isShowNodeName isShowNodeName
value={inputVariable} value={inputVariable}
onChange={onInputVariableChange} onChange={onInputVariableChange}
readonly={false} readonly={readonly}
/> />
</Field> </Field>
) )

View File

@ -31,6 +31,7 @@ type OptionCardProps<T> = {
effectColor?: string effectColor?: string
showEffectColor?: boolean showEffectColor?: boolean
onClick?: (id: T) => void onClick?: (id: T) => void
readonly?: boolean
} }
const OptionCard = memo(({ const OptionCard = memo(({
id, id,
@ -47,14 +48,16 @@ const OptionCard = memo(({
effectColor, effectColor,
showEffectColor, showEffectColor,
onClick, onClick,
readonly,
}) => { }) => {
return ( return (
<div <div
className={cn( className={cn(
'cursor-pointer rounded-xl border border-components-option-card-option-border bg-components-option-card-option-bg', 'cursor-pointer rounded-xl border border-components-option-card-option-border bg-components-option-card-option-bg',
showHighlightBorder && 'border-[2px] border-components-option-card-option-selected-border', showHighlightBorder && 'border-[2px] border-components-option-card-option-selected-border',
readonly && 'cursor-not-allowed',
)} )}
onClick={() => onClick?.(id)} onClick={() => !readonly && onClick?.(id)}
> >
<div className={cn( <div className={cn(
'relative flex rounded-t-xl p-2', 'relative flex rounded-t-xl p-2',

View File

@ -15,6 +15,7 @@ import type { RerankingModelSelectorProps } from './reranking-model-selector'
import SearchMethodOption from './search-method-option' import SearchMethodOption from './search-method-option'
type RetrievalSettingProps = { type RetrievalSettingProps = {
readonly?: boolean
searchMethod: RetrievalSearchMethodEnum searchMethod: RetrievalSearchMethodEnum
onRetrievalSearchMethodChange: (value: RetrievalSearchMethodEnum) => void onRetrievalSearchMethodChange: (value: RetrievalSearchMethodEnum) => void
hybridSearchMode: HybridSearchModeEnum hybridSearchMode: HybridSearchModeEnum
@ -24,6 +25,7 @@ type RetrievalSettingProps = {
} & RerankingModelSelectorProps & TopKAndScoreThresholdProps } & RerankingModelSelectorProps & TopKAndScoreThresholdProps
const RetrievalSetting = ({ const RetrievalSetting = ({
readonly,
searchMethod, searchMethod,
onRetrievalSearchMethodChange, onRetrievalSearchMethodChange,
hybridSearchMode, hybridSearchMode,
@ -84,6 +86,7 @@ const RetrievalSetting = ({
onScoreThresholdEnabledChange={onScoreThresholdEnabledChange} onScoreThresholdEnabledChange={onScoreThresholdEnabledChange}
rerankingModel={rerankingModel} rerankingModel={rerankingModel}
onRerankingModelChange={onRerankingModelChange} onRerankingModelChange={onRerankingModelChange}
readonly={readonly}
/> />
)) ))
} }

View File

@ -11,10 +11,12 @@ import type { RerankingModel } from '../../types'
export type RerankingModelSelectorProps = { export type RerankingModelSelectorProps = {
rerankingModel?: RerankingModel rerankingModel?: RerankingModel
onRerankingModelChange?: (model: RerankingModel) => void onRerankingModelChange?: (model: RerankingModel) => void
readonly?: boolean
} }
const RerankingModelSelector = ({ const RerankingModelSelector = ({
rerankingModel, rerankingModel,
onRerankingModelChange, onRerankingModelChange,
readonly = false,
}: RerankingModelSelectorProps) => { }: RerankingModelSelectorProps) => {
const { const {
modelList: rerankModelList, modelList: rerankModelList,
@ -41,6 +43,7 @@ const RerankingModelSelector = ({
defaultModel={rerankModel && { provider: rerankModel.providerName, model: rerankModel.modelName }} defaultModel={rerankModel && { provider: rerankModel.providerName, model: rerankModel.modelName }}
modelList={rerankModelList} modelList={rerankModelList}
onSelect={handleRerankingModelChange} onSelect={handleRerankingModelChange}
readonly={readonly}
/> />
) )
} }

View File

@ -23,6 +23,7 @@ import type { RerankingModelSelectorProps } from './reranking-model-selector'
import RerankingModelSelector from './reranking-model-selector' import RerankingModelSelector from './reranking-model-selector'
type SearchMethodOptionProps = { type SearchMethodOptionProps = {
readonly?: boolean
option: Option option: Option
hybridSearchModeOptions: HybridSearchModeOption[] hybridSearchModeOptions: HybridSearchModeOption[]
searchMethod: RetrievalSearchMethodEnum searchMethod: RetrievalSearchMethodEnum
@ -33,6 +34,7 @@ type SearchMethodOptionProps = {
onWeightedScoreChange: (value: { value: number[] }) => void onWeightedScoreChange: (value: { value: number[] }) => void
} & RerankingModelSelectorProps & TopKAndScoreThresholdProps } & RerankingModelSelectorProps & TopKAndScoreThresholdProps
const SearchMethodOption = ({ const SearchMethodOption = ({
readonly,
option, option,
hybridSearchModeOptions, hybridSearchModeOptions,
searchMethod, searchMethod,
@ -89,6 +91,7 @@ const SearchMethodOption = ({
showChildren={isActive} showChildren={isActive}
showHighlightBorder={isActive} showHighlightBorder={isActive}
showEffectColor={isActive} showEffectColor={isActive}
readonly={readonly}
> >
<div className='space-y-3'> <div className='space-y-3'>
{ {
@ -105,6 +108,7 @@ const SearchMethodOption = ({
showRadio showRadio
radioIsActive={hybridOption.id === hybridSearchMode} radioIsActive={hybridOption.id === hybridSearchMode}
onClick={onHybridSearchModeChange} onClick={onHybridSearchModeChange}
readonly={readonly}
/> />
)) ))
} }
@ -116,6 +120,7 @@ const SearchMethodOption = ({
<WeightedScoreComponent <WeightedScoreComponent
value={weightedScoreValue} value={weightedScoreValue}
onChange={onWeightedScoreChange} onChange={onWeightedScoreChange}
readonly={readonly}
/> />
) )
} }
@ -124,6 +129,7 @@ const SearchMethodOption = ({
<RerankingModelSelector <RerankingModelSelector
rerankingModel={rerankingModel} rerankingModel={rerankingModel}
onRerankingModelChange={onRerankingModelChange} onRerankingModelChange={onRerankingModelChange}
readonly={readonly}
/> />
) )
} }
@ -134,6 +140,7 @@ const SearchMethodOption = ({
onScoreThresholdChange={onScoreThresholdChange} onScoreThresholdChange={onScoreThresholdChange}
isScoreThresholdEnabled={isScoreThresholdEnabled} isScoreThresholdEnabled={isScoreThresholdEnabled}
onScoreThresholdEnabledChange={onScoreThresholdEnabledChange} onScoreThresholdEnabledChange={onScoreThresholdEnabledChange}
readonly={readonly}
/> />
</div> </div>
</OptionCard> </OptionCard>

View File

@ -10,6 +10,7 @@ export type TopKAndScoreThresholdProps = {
onScoreThresholdChange?: (value: number) => void onScoreThresholdChange?: (value: number) => void
isScoreThresholdEnabled?: boolean isScoreThresholdEnabled?: boolean
onScoreThresholdEnabledChange?: (value: boolean) => void onScoreThresholdEnabledChange?: (value: boolean) => void
readonly?: boolean
} }
const TopKAndScoreThreshold = ({ const TopKAndScoreThreshold = ({
topK, topK,
@ -18,6 +19,7 @@ const TopKAndScoreThreshold = ({
onScoreThresholdChange, onScoreThresholdChange,
isScoreThresholdEnabled, isScoreThresholdEnabled,
onScoreThresholdEnabledChange, onScoreThresholdEnabledChange,
readonly,
}: TopKAndScoreThresholdProps) => { }: TopKAndScoreThresholdProps) => {
const handleTopKChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleTopKChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = Number(e.target.value) const value = Number(e.target.value)
@ -47,6 +49,7 @@ const TopKAndScoreThreshold = ({
type='number' type='number'
value={topK} value={topK}
onChange={handleTopKChange} onChange={handleTopKChange}
disabled={readonly}
/> />
</div> </div>
<div> <div>
@ -55,6 +58,7 @@ const TopKAndScoreThreshold = ({
className='mr-2' className='mr-2'
defaultValue={isScoreThresholdEnabled} defaultValue={isScoreThresholdEnabled}
onChange={onScoreThresholdEnabledChange} onChange={onScoreThresholdEnabledChange}
disabled={readonly}
/> />
<div className='system-sm-medium grow truncate text-text-secondary'> <div className='system-sm-medium grow truncate text-text-secondary'>
Score Threshold Score Threshold
@ -68,6 +72,7 @@ const TopKAndScoreThreshold = ({
type='number' type='number'
value={scoreThreshold} value={scoreThreshold}
onChange={handleScoreThresholdChange} onChange={handleScoreThresholdChange}
disabled={readonly}
/> />
</div> </div>
</div> </div>

View File

@ -18,11 +18,13 @@ import {
GroupWithBox, GroupWithBox,
} from '@/app/components/workflow/nodes/_base/components/layout' } from '@/app/components/workflow/nodes/_base/components/layout'
import Split from '../_base/components/split' import Split from '../_base/components/split'
import { useNodesReadOnly } from '@/app/components/workflow/hooks'
const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({
id, id,
data, data,
}) => { }) => {
const { nodesReadOnly } = useNodesReadOnly()
const { const {
handleChunkStructureChange, handleChunkStructureChange,
handleIndexMethodChange, handleIndexMethodChange,
@ -45,6 +47,7 @@ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({
nodeId={id} nodeId={id}
inputVariable={data.index_chunk_variable_selector} inputVariable={data.index_chunk_variable_selector}
onInputVariableChange={handleInputVariableChange} onInputVariableChange={handleInputVariableChange}
readonly={nodesReadOnly}
/> />
</GroupWithBox> </GroupWithBox>
<Group <Group
@ -54,6 +57,7 @@ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({
<ChunkStructure <ChunkStructure
chunkStructure={data.chunk_structure} chunkStructure={data.chunk_structure}
onChunkStructureChange={handleChunkStructureChange} onChunkStructureChange={handleChunkStructureChange}
readonly={nodesReadOnly}
/> />
</Group> </Group>
<GroupWithBox> <GroupWithBox>
@ -63,6 +67,7 @@ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({
onIndexMethodChange={handleIndexMethodChange} onIndexMethodChange={handleIndexMethodChange}
keywordNumber={data.keyword_number} keywordNumber={data.keyword_number}
onKeywordNumberChange={handleKeywordNumberChange} onKeywordNumberChange={handleKeywordNumberChange}
readonly={nodesReadOnly}
/> />
{ {
data.indexing_technique === IndexMethodEnum.QUALIFIED && ( data.indexing_technique === IndexMethodEnum.QUALIFIED && (
@ -70,6 +75,7 @@ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({
embeddingModel={data.embedding_model} embeddingModel={data.embedding_model}
embeddingModelProvider={data.embedding_model_provider} embeddingModelProvider={data.embedding_model_provider}
onEmbeddingModelChange={handleEmbeddingModelChange} onEmbeddingModelChange={handleEmbeddingModelChange}
readonly={nodesReadOnly}
/> />
) )
} }
@ -91,6 +97,7 @@ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({
onScoreThresholdChange={handleScoreThresholdChange} onScoreThresholdChange={handleScoreThresholdChange}
isScoreThresholdEnabled={data.retrieval_model.score_threshold_enabled} isScoreThresholdEnabled={data.retrieval_model.score_threshold_enabled}
onScoreThresholdEnabledChange={handleScoreThresholdEnabledChange} onScoreThresholdEnabledChange={handleScoreThresholdEnabledChange}
readonly={nodesReadOnly}
/> />
</div> </div>
</GroupWithBox> </GroupWithBox>