diff --git a/web/app/components/rag-pipeline/components/rag-pipeline-header/index.tsx b/web/app/components/rag-pipeline/components/rag-pipeline-header/index.tsx index 02479e2198..d614ac8173 100644 --- a/web/app/components/rag-pipeline/components/rag-pipeline-header/index.tsx +++ b/web/app/components/rag-pipeline/components/rag-pipeline-header/index.tsx @@ -1,25 +1,35 @@ import { memo, + useCallback, useMemo, } from 'react' import type { HeaderProps } from '@/app/components/workflow/header' import Header from '@/app/components/workflow/header' import { fetchWorkflowRunHistory } from '@/service/workflow' -import { useStore } from '@/app/components/workflow/store' +import { + useStore, + useWorkflowStore, +} from '@/app/components/workflow/store' import InputFieldButton from './input-field-button' import Publisher from './publisher' const RagPipelineHeader = () => { + const workflowStore = useWorkflowStore() const pipelineId = useStore(s => s.pipelineId) + const showDebugAndPreviewPanel = useStore(s => s.showDebugAndPreviewPanel) const viewHistoryProps = useMemo(() => { return { - historyUrl: '', - // historyUrl: `/rag/pipeline/${pipelineId}/workflow-runs`, + historyUrl: `/rag/pipelines/${pipelineId}/workflow-runs`, historyFetcher: fetchWorkflowRunHistory, } }, [pipelineId]) + const handleStopRun = useCallback(() => { + const { setShowDebugAndPreviewPanel } = workflowStore.getState() + setShowDebugAndPreviewPanel(false) + }, [workflowStore]) + const headerProps: HeaderProps = useMemo(() => { return { normal: { @@ -31,13 +41,15 @@ const RagPipelineHeader = () => { showRunButton: true, runButtonText: 'Test Run', viewHistoryProps, + isRunning: showDebugAndPreviewPanel, + onStopRun: handleStopRun, }, }, viewHistory: { viewHistoryProps, }, } - }, [viewHistoryProps]) + }, [viewHistoryProps, showDebugAndPreviewPanel, handleStopRun]) return (
diff --git a/web/app/components/rag-pipeline/hooks/use-nodes-sync-draft.ts b/web/app/components/rag-pipeline/hooks/use-nodes-sync-draft.ts index 798bda4dda..eb2a862be6 100644 --- a/web/app/components/rag-pipeline/hooks/use-nodes-sync-draft.ts +++ b/web/app/components/rag-pipeline/hooks/use-nodes-sync-draft.ts @@ -93,8 +93,8 @@ export const useNodesSyncDraft = () => { ) => { if (getNodesReadOnly()) return - const postParams = getPostParams() + const postParams = getPostParams() if (postParams) { const { setSyncWorkflowDraftHash, diff --git a/web/app/components/rag-pipeline/hooks/use-pipeline-start-run.tsx b/web/app/components/rag-pipeline/hooks/use-pipeline-start-run.tsx index ea1add4233..38538109e4 100644 --- a/web/app/components/rag-pipeline/hooks/use-pipeline-start-run.tsx +++ b/web/app/components/rag-pipeline/hooks/use-pipeline-start-run.tsx @@ -1,21 +1,16 @@ import { useCallback } from 'react' -import { useStoreApi } from 'reactflow' import { useWorkflowStore } from '@/app/components/workflow/store' import { - BlockEnum, WorkflowRunningStatus, } from '@/app/components/workflow/types' import { useWorkflowInteractions } from '@/app/components/workflow/hooks' import { useNodesSyncDraft, - usePipelineRun, } from '.' export const usePipelineStartRun = () => { - const store = useStoreApi() const workflowStore = useWorkflowStore() const { handleCancelDebugAndPreviewPanel } = useWorkflowInteractions() - const { handleRun } = usePipelineRun() const { doSyncWorkflowDraft } = useNodesSyncDraft() const handleWorkflowStartRunInWorkflow = useCallback(async () => { @@ -26,13 +21,8 @@ export const usePipelineStartRun = () => { if (workflowRunningData?.result.status === WorkflowRunningStatus.Running) return - const { getNodes } = store.getState() - const nodes = getNodes() - const startNode = nodes.find(node => node.data.type === BlockEnum.Start) - const startVariables = startNode?.data.variables || [] const { showDebugAndPreviewPanel, - setShowInputsPanel, setShowEnvPanel, setShowDebugAndPreviewPanel, } = workflowStore.getState() @@ -44,17 +34,9 @@ export const usePipelineStartRun = () => { return } - if (!startVariables.length) { - await doSyncWorkflowDraft() - handleRun({ inputs: {}, files: [] }) - setShowDebugAndPreviewPanel(true) - setShowInputsPanel(false) - } - else { - setShowDebugAndPreviewPanel(true) - setShowInputsPanel(true) - } - }, [store, workflowStore, handleCancelDebugAndPreviewPanel, handleRun, doSyncWorkflowDraft]) + await doSyncWorkflowDraft() + setShowDebugAndPreviewPanel(true) + }, [workflowStore, handleCancelDebugAndPreviewPanel, doSyncWorkflowDraft]) const handleStartWorkflowRun = useCallback(() => { handleWorkflowStartRunInWorkflow() diff --git a/web/app/components/workflow/header/run-and-history.tsx b/web/app/components/workflow/header/run-and-history.tsx index cf894052e7..54704cbb30 100644 --- a/web/app/components/workflow/header/run-and-history.tsx +++ b/web/app/components/workflow/header/run-and-history.tsx @@ -21,30 +21,36 @@ import { type RunModeProps = { text?: string + isRunning?: boolean + onStopRun?: () => void } const RunMode = memo(({ text, + isRunning: running, + onStopRun, }: RunModeProps) => { const { t } = useTranslation() const { handleWorkflowStartRunInWorkflow } = useWorkflowStartRun() const { handleStopRun } = useWorkflowRun() const workflowRunningData = useStore(s => s.workflowRunningData) const isRunning = workflowRunningData?.result.status === WorkflowRunningStatus.Running + const mergedRunning = isRunning || running return ( <>
{ handleWorkflowStartRunInWorkflow() }} > { - isRunning + mergedRunning ? ( <> @@ -60,12 +66,14 @@ const RunMode = memo(({ }
{ - isRunning && ( + mergedRunning && (
handleStopRun(workflowRunningData?.task_id || '')} + className={cn( + 'ml-[1px] flex h-7 w-7 cursor-pointer items-center justify-center rounded-r-md bg-state-accent-active', + )} + onClick={() => onStopRun ? onStopRun() : handleStopRun(workflowRunningData?.task_id || '')} > - +
) } @@ -94,12 +102,16 @@ const PreviewMode = memo(() => { export type RunAndHistoryProps = { showRunButton?: boolean runButtonText?: string + isRunning?: boolean + onStopRun?: () => void showPreviewButton?: boolean viewHistoryProps?: ViewHistoryProps } const RunAndHistory = ({ showRunButton, runButtonText, + isRunning, + onStopRun, showPreviewButton, viewHistoryProps, }: RunAndHistoryProps) => { @@ -108,7 +120,7 @@ const RunAndHistory = ({ return (
{ - showRunButton && + showRunButton && } { showPreviewButton && 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 2b9c1e7fd5..3c5189b49f 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 @@ -3,25 +3,37 @@ import { ParentChildChunk, QuestionAndAnswer, } from '@/app/components/base/icons/src/vender/knowledge' +import cn from '@/utils/classnames' import { ChunkStructureEnum } from '../../types' import type { Option } from './type' export const useChunkStructure = () => { const GeneralOption: Option = { id: ChunkStructureEnum.general, - icon: , + icon: (isActive: boolean) => ( + + ), title: 'General', description: 'General text chunking mode, the chunks retrieved and recalled are the same.', effectColor: 'blue', - showEffectColor: true, } const ParentChildOption: Option = { id: ChunkStructureEnum.parent_child, - icon: , + icon: (isActive: boolean) => ( + + ), title: 'Parent-Child', description: 'Parent-child text chunking mode, the chunks retrieved and recalled are different.', effectColor: 'blue-light', - showEffectColor: true, } const QuestionAnswerOption: Option = { id: ChunkStructureEnum.question_answer, @@ -39,7 +51,7 @@ export const useChunkStructure = () => { const options = [ GeneralOption, ParentChildOption, - QuestionAnswerOption, + // QuestionAnswerOption, ] return { 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 6e4555d56a..b4e2e59ede 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 @@ -35,7 +35,12 @@ const 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 99dc49f6e3..99c4ca5e33 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 @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useCallback, useState } from 'react' import { PortalToFollowElem, PortalToFollowElemContent, @@ -23,6 +23,11 @@ const Selector = ({ }: SelectorProps) => { const [open, setOpen] = useState(false) + const handleSelect = useCallback((optionId: ChunkStructureEnum) => { + onChange(optionId) + setOpen(false) + }, [onChange]) + return ( { - if (readonly) - return - onChange(option.id) - setOpen(false) - }} - showHighlightBorder={value === option.id} + readonly={readonly} + onClick={handleSelect} + effectColor={option.effectColor} > )) } 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 c8a6f8f821..94afc22dc9 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 @@ -3,9 +3,8 @@ import type { ChunkStructureEnum } from '../../types' export type Option = { id: ChunkStructureEnum - icon: ReactNode + icon: ReactNode | ((isActive: boolean) => ReactNode) title: string description: string effectColor?: string - showEffectColor?: boolean, } 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 d0783168f0..1be9cb87b7 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 @@ -14,9 +14,13 @@ 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' +import { + ChunkStructureEnum, + IndexMethodEnum, +} from '../types' type IndexMethodProps = { + chunkStructure: ChunkStructureEnum indexMethod: IndexMethodEnum onIndexMethodChange: (value: IndexMethodEnum) => void keywordNumber: number @@ -24,6 +28,7 @@ type IndexMethodProps = { readonly?: boolean } const IndexMethod = ({ + chunkStructure, indexMethod, onIndexMethodChange, keywordNumber, @@ -53,65 +58,68 @@ const IndexMethod = ({
id={IndexMethodEnum.QUALIFIED} + selectedId={indexMethod} icon={ } title={t('datasetCreation.stepTwo.qualified')} description={t('datasetSettings.form.indexMethodHighQualityTip')} - showHighlightBorder={isHighQuality} onClick={handleIndexMethodChange} isRecommended + effectColor='orange' > - - } - title={t('datasetSettings.form.indexMethodEconomy')} - description={t('datasetSettings.form.indexMethodEconomyTip')} - showChildren={isEconomy} - showHighlightBorder={isEconomy} - onClick={handleIndexMethodChange} - effectColor='blue' - showEffectColor={isEconomy} - > -
-
-
- Number of Keywords + { + chunkStructure !== ChunkStructureEnum.parent_child && ( + + } + title={t('datasetSettings.form.indexMethodEconomy')} + description={t('datasetSettings.form.indexMethodEconomyTip')} + onClick={handleIndexMethodChange} + effectColor='blue' + > +
+
+
+ Number of Keywords +
+ + + +
+ +
- - - -
- - -
- + + ) + }
) 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 7ba7a28154..59e3e5cee2 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 @@ -1,5 +1,8 @@ import type { ReactNode } from 'react' -import { memo } from 'react' +import { + memo, + useMemo, +} from 'react' import cn from '@/utils/classnames' import Badge from '@/app/components/base/badge' import { @@ -17,63 +20,79 @@ const HEADER_EFFECT_MAP: Record = { 'purple': , } type OptionCardProps = { - id: T - className?: string - showHighlightBorder?: boolean - showRadio?: boolean - radioIsActive?: boolean - icon?: ReactNode + id?: T + selectedId?: T + enableSelect?: boolean + enableHighlightBorder?: boolean + enableRadio?: boolean + wrapperClassName?: string | ((isActive: boolean) => string) + className?: string | ((isActive: boolean) => string) + icon?: ReactNode | ((isActive: boolean) => ReactNode) title: string description?: string isRecommended?: boolean children?: ReactNode - showChildren?: boolean effectColor?: string - showEffectColor?: boolean onClick?: (id: T) => void readonly?: boolean } const OptionCard = memo(({ id, + selectedId, + enableSelect = true, + enableHighlightBorder = true, + enableRadio, + wrapperClassName, className, - showHighlightBorder, - showRadio, - radioIsActive, icon, title, description, isRecommended, children, - showChildren, effectColor, - showEffectColor, onClick, readonly, }) => { + const isActive = useMemo(() => { + return id === selectedId + }, [id, selectedId]) + + const effectElement = useMemo(() => { + if (effectColor) { + return ( + + ) + } + + return null + }, [effectColor, isActive]) + return (
!readonly && onClick?.(id)} + onClick={() => !readonly && enableSelect && id && onClick?.(id)} >
- { - effectColor && showEffectColor && ( -
- {HEADER_EFFECT_MAP[effectColor]} -
- ) - } + {effectElement} { icon && (
- {icon} + {typeof icon === 'function' ? icon(isActive) : icon}
) } @@ -90,10 +109,10 @@ const OptionCard = memo(({ }
{ - showRadio && ( + enableRadio && (
) @@ -109,7 +128,7 @@ const OptionCard = memo(({
{ - children && showChildren && ( + children && isActive && (
{children} 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 index c426313605..53d16be61a 100644 --- 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 @@ -6,6 +6,7 @@ import { } from '@/app/components/base/icons/src/vender/knowledge' import { HybridSearchModeEnum, + IndexMethodEnum, RetrievalSearchMethodEnum, } from '../../types' import type { @@ -13,7 +14,7 @@ import type { Option, } from './type' -export const useRetrievalSetting = () => { +export const useRetrievalSetting = (indexMethod: IndexMethodEnum) => { const VectorSearchOption: Option = useMemo(() => { return { id: RetrievalSearchMethodEnum.semantic, @@ -41,6 +42,15 @@ export const useRetrievalSetting = () => { effectColor: 'purple', } }, []) + const InvertedIndexOption: Option = useMemo(() => { + return { + id: RetrievalSearchMethodEnum.invertedIndex, + icon: HybridSearch as any, + title: 'Inverted Index', + description: 'Use inverted index to search for the most relevant text chunks.', + effectColor: 'purple', + } + }, []) const WeightedScoreModeOption: HybridSearchModeOption = useMemo(() => { return { @@ -58,7 +68,9 @@ export const useRetrievalSetting = () => { }, []) return useMemo(() => ({ - options: [ + options: indexMethod === IndexMethodEnum.ECONOMICAL ? [ + InvertedIndexOption, + ] : [ VectorSearchOption, FullTextSearchOption, HybridSearchOption, @@ -71,6 +83,8 @@ export const useRetrievalSetting = () => { VectorSearchOption, FullTextSearchOption, HybridSearchOption, + InvertedIndexOption, + indexMethod, WeightedScoreModeOption, RerankModelModeOption, ]) 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 40808a3b99..8bc82509c1 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 @@ -7,6 +7,7 @@ import type { RetrievalSearchMethodEnum, } from '../../types' import type { + IndexMethodEnum, WeightedScore, } from '../../types' import { useRetrievalSetting } from './hooks' @@ -15,6 +16,7 @@ import type { RerankingModelSelectorProps } from './reranking-model-selector' import SearchMethodOption from './search-method-option' type RetrievalSettingProps = { + indexMethod: IndexMethodEnum readonly?: boolean searchMethod: RetrievalSearchMethodEnum onRetrievalSearchMethodChange: (value: RetrievalSearchMethodEnum) => void @@ -25,6 +27,7 @@ type RetrievalSettingProps = { } & RerankingModelSelectorProps & TopKAndScoreThresholdProps const RetrievalSetting = ({ + indexMethod, readonly, searchMethod, onRetrievalSearchMethodChange, @@ -44,7 +47,7 @@ const RetrievalSetting = ({ const { options, hybridSearchModeOptions, - } = useRetrievalSetting() + } = useRetrievalSetting(indexMethod) return ( { const Icon = option.icon - const isActive = searchMethod === option.id const isHybridSearch = option.id === RetrievalSearchMethodEnum.hybrid const isHybridSearchWeightedScoreMode = hybridSearchMode === HybridSearchModeEnum.WeightedScore @@ -67,30 +67,32 @@ const SearchMethodOption = ({ } }, [weightedScore]) - const icon = useMemo(() => { + const icon = useCallback((isActive: boolean) => { return ( ) - }, [isActive, Icon]) + }, [Icon]) + + const hybridSearchModeWrapperClassName = useCallback((isActive: boolean) => { + return isActive ? 'border-[1.5px] bg-components-option-card-option-selected-bg' : '' + }, []) return (
@@ -102,11 +104,13 @@ const SearchMethodOption = ({ 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 55cf8e0cb9..670bde7418 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 @@ -4,10 +4,12 @@ import { import { useStoreApi } from 'reactflow' import { useNodeDataUpdate } from '@/app/components/workflow/hooks' import type { ValueSelector } from '@/app/components/workflow/types' -import type { +import { ChunkStructureEnum, - HybridSearchModeEnum, IndexMethodEnum, +} from '../types' +import type { + HybridSearchModeEnum, KnowledgeBaseNodeType, RerankingModel, RetrievalSearchMethodEnum, @@ -32,8 +34,13 @@ export const useConfig = (id: string) => { }, [id, handleNodeDataUpdateWithSyncDraft]) const handleChunkStructureChange = useCallback((chunkStructure: ChunkStructureEnum) => { - handleNodeDataUpdate({ chunk_structure: chunkStructure }) - }, [handleNodeDataUpdate]) + const nodeData = getNodeData() + const { indexing_technique } = nodeData?.data + handleNodeDataUpdate({ + chunk_structure: chunkStructure, + indexing_technique: chunkStructure === ChunkStructureEnum.parent_child ? IndexMethodEnum.QUALIFIED : indexing_technique, + }) + }, [handleNodeDataUpdate, getNodeData]) const handleIndexMethodChange = useCallback((indexMethod: IndexMethodEnum) => { handleNodeDataUpdate({ indexing_technique: indexMethod }) diff --git a/web/app/components/workflow/nodes/knowledge-base/panel.tsx b/web/app/components/workflow/nodes/knowledge-base/panel.tsx index a0d24d713a..e47f6d3ebb 100644 --- a/web/app/components/workflow/nodes/knowledge-base/panel.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/panel.tsx @@ -63,6 +63,7 @@ const Panel: FC> = ({
> = ({