mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +08:00
Chore/slice workflow (#18351)
This commit is contained in:
@ -5,11 +5,8 @@ import {
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { setAutoFreeze } from 'immer'
|
||||
import {
|
||||
useEventListener,
|
||||
@ -31,17 +28,14 @@ import 'reactflow/dist/style.css'
|
||||
import './style.css'
|
||||
import type {
|
||||
Edge,
|
||||
EnvironmentVariable,
|
||||
Node,
|
||||
} from './types'
|
||||
import {
|
||||
ControlMode,
|
||||
SupportUploadFileTypes,
|
||||
} from './types'
|
||||
import { WorkflowContextProvider } from './context'
|
||||
import {
|
||||
useDSL,
|
||||
useEdgesInteractions,
|
||||
useFetchToolsData,
|
||||
useNodesInteractions,
|
||||
useNodesReadOnly,
|
||||
useNodesSyncDraft,
|
||||
@ -49,11 +43,9 @@ import {
|
||||
useSelectionInteractions,
|
||||
useShortcuts,
|
||||
useWorkflow,
|
||||
useWorkflowInit,
|
||||
useWorkflowReadOnly,
|
||||
useWorkflowUpdate,
|
||||
} from './hooks'
|
||||
import Header from './header'
|
||||
import CustomNode from './nodes'
|
||||
import CustomNoteNode from './note-node'
|
||||
import { CUSTOM_NOTE_NODE } from './note-node/constants'
|
||||
@ -66,42 +58,28 @@ import { CUSTOM_SIMPLE_NODE } from './simple-node/constants'
|
||||
import Operator from './operator'
|
||||
import CustomEdge from './custom-edge'
|
||||
import CustomConnectionLine from './custom-connection-line'
|
||||
import Panel from './panel'
|
||||
import Features from './features'
|
||||
import HelpLine from './help-line'
|
||||
import CandidateNode from './candidate-node'
|
||||
import PanelContextmenu from './panel-contextmenu'
|
||||
import NodeContextmenu from './node-contextmenu'
|
||||
import SyncingDataModal from './syncing-data-modal'
|
||||
import UpdateDSLModal from './update-dsl-modal'
|
||||
import DSLExportConfirmModal from './dsl-export-confirm-modal'
|
||||
import LimitTips from './limit-tips'
|
||||
import PluginDependency from './plugin-dependency'
|
||||
import {
|
||||
useStore,
|
||||
useWorkflowStore,
|
||||
} from './store'
|
||||
import {
|
||||
initialEdges,
|
||||
initialNodes,
|
||||
} from './utils'
|
||||
import {
|
||||
CUSTOM_EDGE,
|
||||
CUSTOM_NODE,
|
||||
DSL_EXPORT_CHECK,
|
||||
ITERATION_CHILDREN_Z_INDEX,
|
||||
WORKFLOW_DATA_UPDATE,
|
||||
} from './constants'
|
||||
import { WorkflowHistoryProvider } from './workflow-history-store'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { FeaturesProvider } from '@/app/components/base/features'
|
||||
import type { Features as FeaturesData } from '@/app/components/base/features/types'
|
||||
import { useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
|
||||
import { fetchFileUploadConfig } from '@/service/common'
|
||||
import DatasetsDetailProvider from './datasets-detail-store/provider'
|
||||
import { HooksStoreContextProvider } from './hooks-store'
|
||||
import type { Shape as HooksStoreShape } from './hooks-store'
|
||||
|
||||
const nodeTypes = {
|
||||
[CUSTOM_NODE]: CustomNode,
|
||||
@ -114,32 +92,32 @@ const edgeTypes = {
|
||||
[CUSTOM_EDGE]: CustomEdge,
|
||||
}
|
||||
|
||||
type WorkflowProps = {
|
||||
export type WorkflowProps = {
|
||||
nodes: Node[]
|
||||
edges: Edge[]
|
||||
viewport?: Viewport
|
||||
children?: React.ReactNode
|
||||
onWorkflowDataUpdate?: (v: any) => void
|
||||
}
|
||||
const Workflow: FC<WorkflowProps> = memo(({
|
||||
export const Workflow: FC<WorkflowProps> = memo(({
|
||||
nodes: originalNodes,
|
||||
edges: originalEdges,
|
||||
viewport,
|
||||
children,
|
||||
onWorkflowDataUpdate,
|
||||
}) => {
|
||||
const workflowContainerRef = useRef<HTMLDivElement>(null)
|
||||
const workflowStore = useWorkflowStore()
|
||||
const reactflow = useReactFlow()
|
||||
const featuresStore = useFeaturesStore()
|
||||
const [nodes, setNodes] = useNodesState(originalNodes)
|
||||
const [edges, setEdges] = useEdgesState(originalEdges)
|
||||
const showFeaturesPanel = useStore(state => state.showFeaturesPanel)
|
||||
const controlMode = useStore(s => s.controlMode)
|
||||
const nodeAnimation = useStore(s => s.nodeAnimation)
|
||||
const showConfirm = useStore(s => s.showConfirm)
|
||||
const showImportDSLModal = useStore(s => s.showImportDSLModal)
|
||||
|
||||
const {
|
||||
setShowConfirm,
|
||||
setControlPromptEditorRerenderKey,
|
||||
setShowImportDSLModal,
|
||||
setSyncWorkflowDraftHash,
|
||||
} = workflowStore.getState()
|
||||
const {
|
||||
@ -148,9 +126,6 @@ const Workflow: FC<WorkflowProps> = memo(({
|
||||
} = useNodesSyncDraft()
|
||||
const { workflowReadOnly } = useWorkflowReadOnly()
|
||||
const { nodesReadOnly } = useNodesReadOnly()
|
||||
|
||||
const [secretEnvList, setSecretEnvList] = useState<EnvironmentVariable[]>([])
|
||||
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
|
||||
eventEmitter?.useSubscription((v: any) => {
|
||||
@ -161,19 +136,13 @@ const Workflow: FC<WorkflowProps> = memo(({
|
||||
if (v.payload.viewport)
|
||||
reactflow.setViewport(v.payload.viewport)
|
||||
|
||||
if (v.payload.features && featuresStore) {
|
||||
const { setFeatures } = featuresStore.getState()
|
||||
|
||||
setFeatures(v.payload.features)
|
||||
}
|
||||
|
||||
if (v.payload.hash)
|
||||
setSyncWorkflowDraftHash(v.payload.hash)
|
||||
|
||||
onWorkflowDataUpdate?.(v.payload)
|
||||
|
||||
setTimeout(() => setControlPromptEditorRerenderKey(Date.now()))
|
||||
}
|
||||
if (v.type === DSL_EXPORT_CHECK)
|
||||
setSecretEnvList(v.payload.data as EnvironmentVariable[])
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
@ -231,6 +200,12 @@ const Workflow: FC<WorkflowProps> = memo(({
|
||||
})
|
||||
}
|
||||
})
|
||||
const { handleFetchAllTools } = useFetchToolsData()
|
||||
useEffect(() => {
|
||||
handleFetchAllTools('builtin')
|
||||
handleFetchAllTools('custom')
|
||||
handleFetchAllTools('workflow')
|
||||
}, [handleFetchAllTools])
|
||||
|
||||
const {
|
||||
handleNodeDragStart,
|
||||
@ -258,15 +233,10 @@ const Workflow: FC<WorkflowProps> = memo(({
|
||||
} = useSelectionInteractions()
|
||||
const {
|
||||
handlePaneContextMenu,
|
||||
handlePaneContextmenuCancel,
|
||||
} = usePanelInteractions()
|
||||
const {
|
||||
isValidConnection,
|
||||
} = useWorkflow()
|
||||
const {
|
||||
exportCheck,
|
||||
handleExportDSL,
|
||||
} = useDSL()
|
||||
|
||||
useOnViewportChange({
|
||||
onEnd: () => {
|
||||
@ -297,12 +267,7 @@ const Workflow: FC<WorkflowProps> = memo(({
|
||||
>
|
||||
<SyncingDataModal />
|
||||
<CandidateNode />
|
||||
<Header />
|
||||
<Panel />
|
||||
<Operator handleRedo={handleHistoryForward} handleUndo={handleHistoryBack} />
|
||||
{
|
||||
showFeaturesPanel && <Features />
|
||||
}
|
||||
<PanelContextmenu />
|
||||
<NodeContextmenu />
|
||||
<HelpLine />
|
||||
@ -317,26 +282,8 @@ const Workflow: FC<WorkflowProps> = memo(({
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
showImportDSLModal && (
|
||||
<UpdateDSLModal
|
||||
onCancel={() => setShowImportDSLModal(false)}
|
||||
onBackup={exportCheck}
|
||||
onImport={handlePaneContextmenuCancel}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
secretEnvList.length > 0 && (
|
||||
<DSLExportConfirmModal
|
||||
envList={secretEnvList}
|
||||
onConfirm={handleExportDSL}
|
||||
onClose={() => setSecretEnvList([])}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<LimitTips />
|
||||
<PluginDependency />
|
||||
{children}
|
||||
<ReactFlow
|
||||
nodeTypes={nodeTypes}
|
||||
edgeTypes={edgeTypes}
|
||||
@ -389,89 +336,43 @@ const Workflow: FC<WorkflowProps> = memo(({
|
||||
</div>
|
||||
)
|
||||
})
|
||||
Workflow.displayName = 'Workflow'
|
||||
|
||||
const WorkflowWrap = memo(() => {
|
||||
const {
|
||||
data,
|
||||
isLoading,
|
||||
} = useWorkflowInit()
|
||||
const { data: fileUploadConfigResponse } = useSWR({ url: '/files/upload' }, fetchFileUploadConfig)
|
||||
type WorkflowWithInnerContextProps = WorkflowProps & {
|
||||
hooksStore?: Partial<HooksStoreShape>
|
||||
}
|
||||
export const WorkflowWithInnerContext = memo(({
|
||||
hooksStore,
|
||||
...restProps
|
||||
}: WorkflowWithInnerContextProps) => {
|
||||
return (
|
||||
<HooksStoreContextProvider {...hooksStore}>
|
||||
<Workflow {...restProps} />
|
||||
</HooksStoreContextProvider>
|
||||
)
|
||||
})
|
||||
|
||||
const nodesData = useMemo(() => {
|
||||
if (data)
|
||||
return initialNodes(data.graph.nodes, data.graph.edges)
|
||||
|
||||
return []
|
||||
}, [data])
|
||||
const edgesData = useMemo(() => {
|
||||
if (data)
|
||||
return initialEdges(data.graph.edges, data.graph.nodes)
|
||||
|
||||
return []
|
||||
}, [data])
|
||||
|
||||
if (!data || isLoading) {
|
||||
return (
|
||||
<div className='relative flex h-full w-full items-center justify-center'>
|
||||
<Loading />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const features = data.features || {}
|
||||
const initialFeatures: FeaturesData = {
|
||||
file: {
|
||||
image: {
|
||||
enabled: !!features.file_upload?.image?.enabled,
|
||||
number_limits: features.file_upload?.image?.number_limits || 3,
|
||||
transfer_methods: features.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
|
||||
},
|
||||
enabled: !!(features.file_upload?.enabled || features.file_upload?.image?.enabled),
|
||||
allowed_file_types: features.file_upload?.allowed_file_types || [SupportUploadFileTypes.image],
|
||||
allowed_file_extensions: features.file_upload?.allowed_file_extensions || FILE_EXTS[SupportUploadFileTypes.image].map(ext => `.${ext}`),
|
||||
allowed_file_upload_methods: features.file_upload?.allowed_file_upload_methods || features.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
|
||||
number_limits: features.file_upload?.number_limits || features.file_upload?.image?.number_limits || 3,
|
||||
fileUploadConfig: fileUploadConfigResponse,
|
||||
},
|
||||
opening: {
|
||||
enabled: !!features.opening_statement,
|
||||
opening_statement: features.opening_statement,
|
||||
suggested_questions: features.suggested_questions,
|
||||
},
|
||||
suggested: features.suggested_questions_after_answer || { enabled: false },
|
||||
speech2text: features.speech_to_text || { enabled: false },
|
||||
text2speech: features.text_to_speech || { enabled: false },
|
||||
citation: features.retriever_resource || { enabled: false },
|
||||
moderation: features.sensitive_word_avoidance || { enabled: false },
|
||||
type WorkflowWithDefaultContextProps =
|
||||
Pick<WorkflowProps, 'edges' | 'nodes'>
|
||||
& {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const WorkflowWithDefaultContext = ({
|
||||
nodes,
|
||||
edges,
|
||||
children,
|
||||
}: WorkflowWithDefaultContextProps) => {
|
||||
return (
|
||||
<ReactFlowProvider>
|
||||
<WorkflowHistoryProvider
|
||||
nodes={nodesData}
|
||||
edges={edgesData} >
|
||||
<FeaturesProvider features={initialFeatures}>
|
||||
<DatasetsDetailProvider nodes={nodesData}>
|
||||
<Workflow
|
||||
nodes={nodesData}
|
||||
edges={edgesData}
|
||||
viewport={data?.graph.viewport}
|
||||
/>
|
||||
</DatasetsDetailProvider>
|
||||
</FeaturesProvider>
|
||||
nodes={nodes}
|
||||
edges={edges} >
|
||||
<DatasetsDetailProvider nodes={nodes}>
|
||||
{children}
|
||||
</DatasetsDetailProvider>
|
||||
</WorkflowHistoryProvider>
|
||||
</ReactFlowProvider>
|
||||
)
|
||||
})
|
||||
WorkflowWrap.displayName = 'WorkflowWrap'
|
||||
|
||||
const WorkflowContainer = () => {
|
||||
return (
|
||||
<WorkflowContextProvider>
|
||||
<WorkflowWrap />
|
||||
</WorkflowContextProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(WorkflowContainer)
|
||||
export default memo(WorkflowWithDefaultContext)
|
||||
|
||||
Reference in New Issue
Block a user