feat: Trigger single run in sub-graph after modal opens

This commit is contained in:
zhsama
2026-01-29 18:56:41 +08:00
parent e47f690cd2
commit 8f7b9e2de4
4 changed files with 59 additions and 27 deletions

View File

@ -4,11 +4,13 @@ import type { SyncWorkflowDraft, SyncWorkflowDraftCallback } from '../types'
import type { Shape as HooksStoreShape } from '@/app/components/workflow/hooks-store'
import type { NestedNodeConfig } from '@/app/components/workflow/nodes/_base/types'
import type { Edge, Node } from '@/app/components/workflow/types'
import { useCallback, useMemo } from 'react'
import { useCallback, useEffect, useMemo } from 'react'
import { useStoreApi } from 'reactflow'
import { InteractionMode, WorkflowWithInnerContext } from '@/app/components/workflow'
import { useNodesInteractions } from '@/app/components/workflow/hooks'
import { useSetWorkflowVarsWithValue } from '@/app/components/workflow/hooks/use-fetch-workflow-inspect-vars'
import { useInspectVarsCrudCommon } from '@/app/components/workflow/hooks/use-inspect-vars-crud-common'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { BlockEnum } from '@/app/components/workflow/types'
import { FlowType } from '@/types/common'
import { useAvailableNodesMetaData } from '../hooks'
@ -24,6 +26,9 @@ type SubGraphMainBaseProps = {
selectableNodeTypes?: BlockEnum[]
onSave?: (nodes: Node[], edges: Edge[]) => void
onSyncWorkflowDraft?: SyncWorkflowDraft
isOpen: boolean
pendingSingleRun?: boolean
onPendingSingleRunHandled?: () => void
}
type SubGraphMainProps
@ -50,8 +55,13 @@ const SubGraphMain: FC<SubGraphMainProps> = (props) => {
selectableNodeTypes,
onSave,
onSyncWorkflowDraft,
isOpen,
pendingSingleRun,
onPendingSingleRunHandled,
} = props
const reactFlowStore = useStoreApi()
const workflowStore = useWorkflowStore()
const { handleNodeSelect } = useNodesInteractions()
const availableNodesMetaData = useAvailableNodesMetaData()
const flowType = configsMap?.flowType ?? FlowType.appFlow
const flowId = configsMap?.flowId ?? ''
@ -91,6 +101,32 @@ const SubGraphMain: FC<SubGraphMainProps> = (props) => {
}
}, [handleSyncSubGraphDraft, onSyncWorkflowDraft])
useEffect(() => {
if (!isOpen || !pendingSingleRun)
return
const { getNodes } = reactFlowStore.getState()
const currentNodes = getNodes()
const hasExtractorNode = currentNodes.some(node => node.id === extractorNodeId)
if (!hasExtractorNode)
return
// NodePanel listens for pendingSingleRun only when the extractor node is selected in this subgraph.
handleNodeSelect(extractorNodeId, false, true)
// Defer run until the selection is applied and the panel is ready.
const frame = requestAnimationFrame(() => {
const store = workflowStore.getState()
store.setPendingSingleRun({
nodeId: extractorNodeId,
action: 'run',
})
onPendingSingleRunHandled?.()
})
return () => cancelAnimationFrame(frame)
}, [extractorNodeId, handleNodeSelect, isOpen, onPendingSingleRunHandled, pendingSingleRun, reactFlowStore, workflowStore])
const resolvedSelectableTypes = useMemo(() => {
if (selectableNodeTypes && selectableNodeTypes.length > 0)
return selectableNodeTypes

View File

@ -32,6 +32,7 @@ const defaultViewport: Viewport = {
const SubGraphContent: FC<SubGraphProps> = (props) => {
const {
isOpen,
toolNodeId,
paramKey,
toolParamValue,
@ -41,6 +42,8 @@ const SubGraphContent: FC<SubGraphProps> = (props) => {
selectableNodeTypes,
onSave,
onSyncWorkflowDraft,
pendingSingleRun,
onPendingSingleRunHandled,
} = props
const isAgentVariant = props.variant === 'agent'
@ -230,6 +233,9 @@ const SubGraphContent: FC<SubGraphProps> = (props) => {
title={sourceTitle}
extractorNodeId={`${toolNodeId}_ext_${paramKey}`}
configsMap={configsMap}
isOpen={isOpen}
pendingSingleRun={pendingSingleRun}
onPendingSingleRunHandled={onPendingSingleRunHandled}
nestedNodeConfig={props.nestedNodeConfig}
onNestedNodeConfigChange={props.onNestedNodeConfigChange}
selectableNodeTypes={selectableNodeTypes}
@ -253,6 +259,9 @@ const SubGraphContent: FC<SubGraphProps> = (props) => {
title={sourceTitle}
extractorNodeId={`${toolNodeId}_ext_${paramKey}`}
configsMap={configsMap}
isOpen={isOpen}
pendingSingleRun={pendingSingleRun}
onPendingSingleRunHandled={onPendingSingleRunHandled}
nestedNodeConfig={props.nestedNodeConfig}
onNestedNodeConfigChange={props.onNestedNodeConfigChange}
selectableNodeTypes={selectableNodeTypes}

View File

@ -19,6 +19,7 @@ export type SyncWorkflowDraft = (
export type SubGraphVariant = 'agent' | 'assemble'
type BaseSubGraphProps = {
isOpen: boolean
toolNodeId: string
paramKey: string
configsMap?: HooksStoreShape['configsMap']
@ -28,6 +29,8 @@ type BaseSubGraphProps = {
selectableNodeTypes?: BlockEnum[]
onSave?: (nodes: Node[], edges: Edge[]) => void
onSyncWorkflowDraft?: SyncWorkflowDraft
pendingSingleRun?: boolean
onPendingSingleRunHandled?: () => void
}
export type AgentSubGraphProps = BaseSubGraphProps & {

View File

@ -13,7 +13,6 @@ import type {
import {
memo,
useCallback,
useEffect,
useMemo,
useRef,
useState,
@ -26,7 +25,7 @@ import { useHooksStore } from '@/app/components/workflow/hooks-store'
import { NULL_STRATEGY } from '@/app/components/workflow/nodes/_base/constants'
import { VarKindType as VarKindTypeEnum } from '@/app/components/workflow/nodes/_base/types'
import { Type } from '@/app/components/workflow/nodes/llm/types'
import { useStore, useWorkflowStore } from '@/app/components/workflow/store'
import { useStore } from '@/app/components/workflow/store'
import { BlockEnum } from '@/app/components/workflow/types'
import { useGetLanguage } from '@/context/i18n'
import { useStrategyProviders } from '@/service/use-strategy'
@ -99,7 +98,6 @@ const MixedVariableTextInput = ({
const [isContextGenerateModalOpen, setIsContextGenerateModalOpen] = useState(false)
const contextGenerateModalRef = useRef<ContextGenerateModalHandle>(null)
const [pendingRunAfterSubGraphOpen, setPendingRunAfterSubGraphOpen] = useState(false)
const workflowStore = useWorkflowStore()
const nodesByIdMap = useMemo(() => {
return availableNodes.reduce((acc, node) => {
@ -359,35 +357,19 @@ const MixedVariableTextInput = ({
setPendingRunAfterSubGraphOpen(false)
}, [])
const handlePendingSingleRunHandled = useCallback(() => {
setPendingRunAfterSubGraphOpen(false)
}, [])
const handleCloseContextGenerateModal = useCallback(() => {
setIsContextGenerateModalOpen(false)
}, [])
const handleOpenInternalViewAndRun = useCallback(() => {
setIsSubGraphModalOpen(true)
if (!isSubGraphModalOpen)
setIsSubGraphModalOpen(true)
setPendingRunAfterSubGraphOpen(true)
}, [])
useEffect(() => {
if (!isSubGraphModalOpen || !pendingRunAfterSubGraphOpen)
return
const extractorNodeId = assembleExtractorNodeId || (toolNodeId && paramKey ? `${toolNodeId}_ext_${paramKey}` : '')
if (!extractorNodeId)
return
const timer = setTimeout(() => {
const store = workflowStore()
store.setInitShowLastRunTab(true)
store.setPendingSingleRun({
nodeId: extractorNodeId,
action: 'run',
})
setPendingRunAfterSubGraphOpen(false)
}, 300)
return () => clearTimeout(timer)
}, [isSubGraphModalOpen, pendingRunAfterSubGraphOpen, assembleExtractorNodeId, toolNodeId, paramKey, workflowStore])
}, [isSubGraphModalOpen])
const sourceVariable: ValueSelector | undefined = detectedAgentFromValue
? [detectedAgentFromValue.nodeId, 'context']
@ -465,6 +447,8 @@ const MixedVariableTextInput = ({
<SubGraphModal
isOpen={isSubGraphModalOpen}
onClose={handleCloseSubGraphModal}
pendingSingleRun={pendingRunAfterSubGraphOpen}
onPendingSingleRunHandled={handlePendingSingleRunHandled}
variant="assemble"
toolNodeId={toolNodeId}
paramKey={paramKey}