mirror of
https://github.com/langgenius/dify.git
synced 2026-05-13 08:57:28 +08:00
feat: Add sub-graph component for workflow
This commit is contained in:
parent
f925266c1b
commit
cab7cd37b8
@ -0,0 +1,57 @@
|
|||||||
|
import type { FC } from 'react'
|
||||||
|
import type { SubGraphConfig } from '../types'
|
||||||
|
import { memo, useMemo } from 'react'
|
||||||
|
import { useStore as useReactFlowStore } from 'reactflow'
|
||||||
|
import { useShallow } from 'zustand/react/shallow'
|
||||||
|
import { Panel as NodePanel } from '@/app/components/workflow/nodes'
|
||||||
|
|
||||||
|
type SubGraphChildrenProps = {
|
||||||
|
toolNodeId: string
|
||||||
|
paramKey: string
|
||||||
|
onConfigChange: (config: Partial<SubGraphConfig>) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const SubGraphChildren: FC<SubGraphChildrenProps> = ({
|
||||||
|
toolNodeId: _toolNodeId,
|
||||||
|
paramKey: _paramKey,
|
||||||
|
onConfigChange: _onConfigChange,
|
||||||
|
}) => {
|
||||||
|
const selectedNode = useReactFlowStore(useShallow((s) => {
|
||||||
|
const nodes = s.getNodes()
|
||||||
|
const currentNode = nodes.find(node => node.data.selected)
|
||||||
|
|
||||||
|
if (currentNode) {
|
||||||
|
return {
|
||||||
|
id: currentNode.id,
|
||||||
|
type: currentNode.type,
|
||||||
|
data: currentNode.data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}))
|
||||||
|
|
||||||
|
const nodePanel = useMemo(() => {
|
||||||
|
if (!selectedNode)
|
||||||
|
return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NodePanel
|
||||||
|
id={selectedNode.id}
|
||||||
|
type={selectedNode.type}
|
||||||
|
data={selectedNode.data}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}, [selectedNode])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="pointer-events-none absolute inset-y-0 right-0 z-10 flex">
|
||||||
|
{nodePanel && (
|
||||||
|
<div className="pointer-events-auto">
|
||||||
|
{nodePanel}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(SubGraphChildren)
|
||||||
107
web/app/components/sub-graph/components/sub-graph-main.tsx
Normal file
107
web/app/components/sub-graph/components/sub-graph-main.tsx
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import type { FC } from 'react'
|
||||||
|
import type { Viewport } from 'reactflow'
|
||||||
|
import type { SubGraphConfig } from '../types'
|
||||||
|
import type { Edge, Node } from '@/app/components/workflow/types'
|
||||||
|
import { useCallback, useMemo } from 'react'
|
||||||
|
import { WorkflowWithInnerContext } from '@/app/components/workflow'
|
||||||
|
import { useAvailableNodesMetaData, useSubGraphPersistence } from '../hooks'
|
||||||
|
import SubGraphChildren from './sub-graph-children'
|
||||||
|
|
||||||
|
type SubGraphMainProps = {
|
||||||
|
nodes: Node[]
|
||||||
|
edges: Edge[]
|
||||||
|
viewport: Viewport
|
||||||
|
toolNodeId: string
|
||||||
|
paramKey: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const SubGraphMain: FC<SubGraphMainProps> = ({
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
viewport,
|
||||||
|
toolNodeId,
|
||||||
|
paramKey,
|
||||||
|
}) => {
|
||||||
|
const availableNodesMetaData = useAvailableNodesMetaData()
|
||||||
|
const {
|
||||||
|
saveSubGraphData,
|
||||||
|
loadSubGraphData,
|
||||||
|
updateSubGraphConfig,
|
||||||
|
} = useSubGraphPersistence({ toolNodeId, paramKey })
|
||||||
|
|
||||||
|
const handleNodesChange = useCallback((updatedNodes: Node[]) => {
|
||||||
|
const existingData = loadSubGraphData()
|
||||||
|
const defaultConfig: SubGraphConfig = {
|
||||||
|
enabled: true,
|
||||||
|
startNodeId: updatedNodes[0]?.id || '',
|
||||||
|
selectedOutputVar: [],
|
||||||
|
whenOutputNone: 'skip',
|
||||||
|
}
|
||||||
|
|
||||||
|
saveSubGraphData({
|
||||||
|
nodes: updatedNodes,
|
||||||
|
edges,
|
||||||
|
config: existingData?.config || defaultConfig,
|
||||||
|
})
|
||||||
|
}, [edges, loadSubGraphData, saveSubGraphData])
|
||||||
|
|
||||||
|
const hooksStore = useMemo(() => {
|
||||||
|
return {
|
||||||
|
availableNodesMetaData,
|
||||||
|
doSyncWorkflowDraft: async () => {
|
||||||
|
handleNodesChange(nodes)
|
||||||
|
},
|
||||||
|
syncWorkflowDraftWhenPageClose: () => {
|
||||||
|
handleNodesChange(nodes)
|
||||||
|
},
|
||||||
|
handleRefreshWorkflowDraft: () => {},
|
||||||
|
handleBackupDraft: () => {},
|
||||||
|
handleLoadBackupDraft: () => {},
|
||||||
|
handleRestoreFromPublishedWorkflow: () => {},
|
||||||
|
handleRun: () => {},
|
||||||
|
handleStopRun: () => {},
|
||||||
|
handleStartWorkflowRun: () => {},
|
||||||
|
handleWorkflowStartRunInWorkflow: () => {},
|
||||||
|
handleWorkflowStartRunInChatflow: () => {},
|
||||||
|
handleWorkflowTriggerScheduleRunInWorkflow: () => {},
|
||||||
|
handleWorkflowTriggerWebhookRunInWorkflow: () => {},
|
||||||
|
handleWorkflowTriggerPluginRunInWorkflow: () => {},
|
||||||
|
handleWorkflowRunAllTriggersInWorkflow: () => {},
|
||||||
|
getWorkflowRunAndTraceUrl: () => ({ runUrl: '', traceUrl: '' }),
|
||||||
|
exportCheck: async () => {},
|
||||||
|
handleExportDSL: async () => {},
|
||||||
|
fetchInspectVars: async () => {},
|
||||||
|
hasNodeInspectVars: () => false,
|
||||||
|
hasSetInspectVar: () => false,
|
||||||
|
fetchInspectVarValue: async () => {},
|
||||||
|
editInspectVarValue: async () => {},
|
||||||
|
renameInspectVarName: async () => {},
|
||||||
|
appendNodeInspectVars: () => {},
|
||||||
|
deleteInspectVar: async () => {},
|
||||||
|
deleteNodeInspectorVars: async () => {},
|
||||||
|
deleteAllInspectorVars: async () => {},
|
||||||
|
isInspectVarEdited: () => false,
|
||||||
|
resetToLastRunVar: async () => {},
|
||||||
|
invalidateSysVarValues: () => {},
|
||||||
|
resetConversationVar: async () => {},
|
||||||
|
invalidateConversationVarValues: () => {},
|
||||||
|
}
|
||||||
|
}, [availableNodesMetaData, handleNodesChange, nodes])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WorkflowWithInnerContext
|
||||||
|
nodes={nodes}
|
||||||
|
edges={edges}
|
||||||
|
viewport={viewport}
|
||||||
|
hooksStore={hooksStore as any}
|
||||||
|
>
|
||||||
|
<SubGraphChildren
|
||||||
|
toolNodeId={toolNodeId}
|
||||||
|
paramKey={paramKey}
|
||||||
|
onConfigChange={updateSubGraphConfig}
|
||||||
|
/>
|
||||||
|
</WorkflowWithInnerContext>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SubGraphMain
|
||||||
5
web/app/components/sub-graph/hooks/index.ts
Normal file
5
web/app/components/sub-graph/hooks/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export { useAvailableNodesMetaData } from './use-available-nodes-meta-data'
|
||||||
|
export { useSubGraphInit } from './use-sub-graph-init'
|
||||||
|
export { useSubGraphNodes } from './use-sub-graph-nodes'
|
||||||
|
export { useSubGraphPersistence } from './use-sub-graph-persistence'
|
||||||
|
export type { SubGraphData } from './use-sub-graph-persistence'
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
import type { AvailableNodesMetaData } from '@/app/components/workflow/hooks-store/store'
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { WORKFLOW_COMMON_NODES } from '@/app/components/workflow/constants/node'
|
||||||
|
import { BlockEnum } from '@/app/components/workflow/types'
|
||||||
|
|
||||||
|
export const useAvailableNodesMetaData = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const availableNodesMetaData = useMemo(() => WORKFLOW_COMMON_NODES.map((node) => {
|
||||||
|
const { metaData } = node
|
||||||
|
const title = t(`blocks.${metaData.type}`, { ns: 'workflow' })
|
||||||
|
const description = t(`blocksAbout.${metaData.type}`, { ns: 'workflow' })
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
metaData: {
|
||||||
|
...metaData,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
},
|
||||||
|
defaultValue: {
|
||||||
|
...node.defaultValue,
|
||||||
|
type: metaData.type,
|
||||||
|
title,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}), [t])
|
||||||
|
|
||||||
|
const availableNodesMetaDataMap = useMemo(() => availableNodesMetaData.reduce((acc, node) => {
|
||||||
|
acc![node.metaData.type] = node
|
||||||
|
return acc
|
||||||
|
}, {} as AvailableNodesMetaData['nodesMap']), [availableNodesMetaData])
|
||||||
|
|
||||||
|
return useMemo(() => {
|
||||||
|
return {
|
||||||
|
nodes: availableNodesMetaData,
|
||||||
|
nodesMap: {
|
||||||
|
...availableNodesMetaDataMap,
|
||||||
|
[BlockEnum.VariableAssigner]: availableNodesMetaDataMap?.[BlockEnum.VariableAggregator],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}, [availableNodesMetaData, availableNodesMetaDataMap])
|
||||||
|
}
|
||||||
90
web/app/components/sub-graph/hooks/use-sub-graph-init.ts
Normal file
90
web/app/components/sub-graph/hooks/use-sub-graph-init.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import type { SubGraphProps } from '../types'
|
||||||
|
import type { LLMNodeType } from '@/app/components/workflow/nodes/llm/types'
|
||||||
|
import type { StartNodeType } from '@/app/components/workflow/nodes/start/types'
|
||||||
|
import type { Edge, Node } from '@/app/components/workflow/types'
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import { BlockEnum, PromptRole } from '@/app/components/workflow/types'
|
||||||
|
import { AppModeEnum } from '@/types/app'
|
||||||
|
|
||||||
|
const SUBGRAPH_SOURCE_NODE_ID = 'subgraph-source'
|
||||||
|
const SUBGRAPH_LLM_NODE_ID = 'subgraph-llm'
|
||||||
|
|
||||||
|
export const useSubGraphInit = (props: SubGraphProps) => {
|
||||||
|
const { sourceVariable, agentName } = props
|
||||||
|
|
||||||
|
const initialNodes = useMemo((): Node[] => {
|
||||||
|
const sourceVarName = sourceVariable.length > 1
|
||||||
|
? sourceVariable.slice(1).join('.')
|
||||||
|
: 'output'
|
||||||
|
|
||||||
|
const startNode: Node<StartNodeType> = {
|
||||||
|
id: SUBGRAPH_SOURCE_NODE_ID,
|
||||||
|
type: 'custom',
|
||||||
|
position: { x: 100, y: 150 },
|
||||||
|
data: {
|
||||||
|
type: BlockEnum.Start,
|
||||||
|
title: `${agentName}: ${sourceVarName}`,
|
||||||
|
desc: 'Source variable from agent',
|
||||||
|
_connectedSourceHandleIds: ['source'],
|
||||||
|
_connectedTargetHandleIds: [],
|
||||||
|
variables: [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const llmNode: Node<LLMNodeType> = {
|
||||||
|
id: SUBGRAPH_LLM_NODE_ID,
|
||||||
|
type: 'custom',
|
||||||
|
position: { x: 450, y: 150 },
|
||||||
|
data: {
|
||||||
|
type: BlockEnum.LLM,
|
||||||
|
title: 'LLM',
|
||||||
|
desc: 'Transform the output',
|
||||||
|
_connectedSourceHandleIds: [],
|
||||||
|
_connectedTargetHandleIds: ['target'],
|
||||||
|
model: {
|
||||||
|
provider: '',
|
||||||
|
name: '',
|
||||||
|
mode: AppModeEnum.CHAT,
|
||||||
|
completion_params: {
|
||||||
|
temperature: 0.7,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
prompt_template: [{
|
||||||
|
role: PromptRole.system,
|
||||||
|
text: '',
|
||||||
|
}],
|
||||||
|
context: {
|
||||||
|
enabled: false,
|
||||||
|
variable_selector: [],
|
||||||
|
},
|
||||||
|
vision: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return [startNode, llmNode]
|
||||||
|
}, [sourceVariable, agentName])
|
||||||
|
|
||||||
|
const initialEdges = useMemo((): Edge[] => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: `${SUBGRAPH_SOURCE_NODE_ID}-${SUBGRAPH_LLM_NODE_ID}`,
|
||||||
|
source: SUBGRAPH_SOURCE_NODE_ID,
|
||||||
|
sourceHandle: 'source',
|
||||||
|
target: SUBGRAPH_LLM_NODE_ID,
|
||||||
|
targetHandle: 'target',
|
||||||
|
type: 'custom',
|
||||||
|
data: {
|
||||||
|
sourceType: BlockEnum.Start,
|
||||||
|
targetType: BlockEnum.LLM,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return {
|
||||||
|
initialNodes,
|
||||||
|
initialEdges,
|
||||||
|
}
|
||||||
|
}
|
||||||
20
web/app/components/sub-graph/hooks/use-sub-graph-nodes.ts
Normal file
20
web/app/components/sub-graph/hooks/use-sub-graph-nodes.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import type { Edge, Node } from '@/app/components/workflow/types'
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import { initialEdges, initialNodes } from '@/app/components/workflow/utils'
|
||||||
|
|
||||||
|
export const useSubGraphNodes = (nodes: Node[], edges: Edge[]) => {
|
||||||
|
const processedNodes = useMemo(
|
||||||
|
() => initialNodes(nodes, edges),
|
||||||
|
[nodes, edges],
|
||||||
|
)
|
||||||
|
|
||||||
|
const processedEdges = useMemo(
|
||||||
|
() => initialEdges(edges, nodes),
|
||||||
|
[edges, nodes],
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
nodes: processedNodes,
|
||||||
|
edges: processedEdges,
|
||||||
|
}
|
||||||
|
}
|
||||||
128
web/app/components/sub-graph/hooks/use-sub-graph-persistence.ts
Normal file
128
web/app/components/sub-graph/hooks/use-sub-graph-persistence.ts
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import type { SubGraphConfig } from '../types'
|
||||||
|
import type { ToolNodeType } from '@/app/components/workflow/nodes/tool/types'
|
||||||
|
import type { Edge, Node } from '@/app/components/workflow/types'
|
||||||
|
import { useCallback } from 'react'
|
||||||
|
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
|
||||||
|
import { VarKindType } from '@/app/components/workflow/nodes/_base/types'
|
||||||
|
|
||||||
|
type SubGraphPersistenceProps = {
|
||||||
|
toolNodeId: string
|
||||||
|
paramKey: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SubGraphData = {
|
||||||
|
nodes: Node[]
|
||||||
|
edges: Edge[]
|
||||||
|
config: SubGraphConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
const SUB_GRAPH_DATA_PREFIX = '__subgraph__'
|
||||||
|
|
||||||
|
export const useSubGraphPersistence = ({
|
||||||
|
toolNodeId,
|
||||||
|
paramKey,
|
||||||
|
}: SubGraphPersistenceProps) => {
|
||||||
|
const { inputs, setInputs } = useNodeCrud<ToolNodeType>(toolNodeId, {} as ToolNodeType)
|
||||||
|
|
||||||
|
const getSubGraphDataKey = useCallback(() => {
|
||||||
|
return `${SUB_GRAPH_DATA_PREFIX}${paramKey}`
|
||||||
|
}, [paramKey])
|
||||||
|
|
||||||
|
const loadSubGraphData = useCallback((): SubGraphData | null => {
|
||||||
|
const dataKey = getSubGraphDataKey()
|
||||||
|
const toolParameters = inputs.tool_parameters || {}
|
||||||
|
const storedData = toolParameters[dataKey]
|
||||||
|
|
||||||
|
if (!storedData || storedData.type !== VarKindType.constant) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsed = typeof storedData.value === 'string'
|
||||||
|
? JSON.parse(storedData.value)
|
||||||
|
: storedData.value
|
||||||
|
|
||||||
|
return parsed as SubGraphData
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}, [getSubGraphDataKey, inputs.tool_parameters])
|
||||||
|
|
||||||
|
const saveSubGraphData = useCallback((data: SubGraphData) => {
|
||||||
|
const dataKey = getSubGraphDataKey()
|
||||||
|
const newToolParameters = {
|
||||||
|
...inputs.tool_parameters,
|
||||||
|
[dataKey]: {
|
||||||
|
type: VarKindType.constant,
|
||||||
|
value: JSON.stringify(data),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
setInputs({
|
||||||
|
...inputs,
|
||||||
|
tool_parameters: newToolParameters,
|
||||||
|
})
|
||||||
|
}, [getSubGraphDataKey, inputs, setInputs])
|
||||||
|
|
||||||
|
const clearSubGraphData = useCallback(() => {
|
||||||
|
const dataKey = getSubGraphDataKey()
|
||||||
|
const newToolParameters = { ...inputs.tool_parameters }
|
||||||
|
delete newToolParameters[dataKey]
|
||||||
|
|
||||||
|
setInputs({
|
||||||
|
...inputs,
|
||||||
|
tool_parameters: newToolParameters,
|
||||||
|
})
|
||||||
|
}, [getSubGraphDataKey, inputs, setInputs])
|
||||||
|
|
||||||
|
const hasSubGraphData = useCallback(() => {
|
||||||
|
const dataKey = getSubGraphDataKey()
|
||||||
|
const toolParameters = inputs.tool_parameters || {}
|
||||||
|
return !!toolParameters[dataKey]
|
||||||
|
}, [getSubGraphDataKey, inputs.tool_parameters])
|
||||||
|
|
||||||
|
const updateSubGraphConfig = useCallback((
|
||||||
|
config: Partial<SubGraphConfig>,
|
||||||
|
) => {
|
||||||
|
const existingData = loadSubGraphData()
|
||||||
|
if (!existingData)
|
||||||
|
return
|
||||||
|
|
||||||
|
saveSubGraphData({
|
||||||
|
...existingData,
|
||||||
|
config: {
|
||||||
|
...existingData.config,
|
||||||
|
...config,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}, [loadSubGraphData, saveSubGraphData])
|
||||||
|
|
||||||
|
const updateSubGraphNodes = useCallback((
|
||||||
|
nodes: Node[],
|
||||||
|
edges: Edge[],
|
||||||
|
) => {
|
||||||
|
const existingData = loadSubGraphData()
|
||||||
|
const defaultConfig: SubGraphConfig = {
|
||||||
|
enabled: true,
|
||||||
|
startNodeId: nodes[0]?.id || '',
|
||||||
|
selectedOutputVar: [],
|
||||||
|
whenOutputNone: 'skip',
|
||||||
|
}
|
||||||
|
|
||||||
|
saveSubGraphData({
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
config: existingData?.config || defaultConfig,
|
||||||
|
})
|
||||||
|
}, [loadSubGraphData, saveSubGraphData])
|
||||||
|
|
||||||
|
return {
|
||||||
|
loadSubGraphData,
|
||||||
|
saveSubGraphData,
|
||||||
|
clearSubGraphData,
|
||||||
|
hasSubGraphData,
|
||||||
|
updateSubGraphConfig,
|
||||||
|
updateSubGraphNodes,
|
||||||
|
}
|
||||||
|
}
|
||||||
57
web/app/components/sub-graph/index.tsx
Normal file
57
web/app/components/sub-graph/index.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import type { FC } from 'react'
|
||||||
|
import type { Viewport } from 'reactflow'
|
||||||
|
import type { SubGraphProps } from './types'
|
||||||
|
import type { InjectWorkflowStoreSliceFn } from '@/app/components/workflow/store'
|
||||||
|
import { memo, useMemo } from 'react'
|
||||||
|
import WorkflowWithDefaultContext from '@/app/components/workflow'
|
||||||
|
import { WorkflowContextProvider } from '@/app/components/workflow/context'
|
||||||
|
import SubGraphMain from './components/sub-graph-main'
|
||||||
|
import { useSubGraphInit, useSubGraphNodes, useSubGraphPersistence } from './hooks'
|
||||||
|
import { createSubGraphSlice } from './store'
|
||||||
|
|
||||||
|
const defaultViewport: Viewport = {
|
||||||
|
x: 50,
|
||||||
|
y: 50,
|
||||||
|
zoom: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
const SubGraph: FC<SubGraphProps> = (props) => {
|
||||||
|
const { toolNodeId, paramKey } = props
|
||||||
|
|
||||||
|
const { loadSubGraphData } = useSubGraphPersistence({ toolNodeId, paramKey })
|
||||||
|
const savedData = useMemo(() => loadSubGraphData(), [loadSubGraphData])
|
||||||
|
|
||||||
|
const { initialNodes, initialEdges } = useSubGraphInit(props)
|
||||||
|
|
||||||
|
const nodesSource = savedData?.nodes || initialNodes
|
||||||
|
const edgesSource = savedData?.edges || initialEdges
|
||||||
|
|
||||||
|
const { nodes, edges } = useSubGraphNodes(nodesSource, edgesSource)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WorkflowWithDefaultContext
|
||||||
|
nodes={nodes}
|
||||||
|
edges={edges}
|
||||||
|
>
|
||||||
|
<SubGraphMain
|
||||||
|
nodes={nodes}
|
||||||
|
edges={edges}
|
||||||
|
viewport={defaultViewport}
|
||||||
|
toolNodeId={toolNodeId}
|
||||||
|
paramKey={paramKey}
|
||||||
|
/>
|
||||||
|
</WorkflowWithDefaultContext>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const SubGraphWrapper: FC<SubGraphProps> = (props) => {
|
||||||
|
return (
|
||||||
|
<WorkflowContextProvider
|
||||||
|
injectWorkflowStoreSliceFn={createSubGraphSlice as unknown as InjectWorkflowStoreSliceFn}
|
||||||
|
>
|
||||||
|
<SubGraph {...props} />
|
||||||
|
</WorkflowContextProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(SubGraphWrapper)
|
||||||
49
web/app/components/sub-graph/store/index.ts
Normal file
49
web/app/components/sub-graph/store/index.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import type { CreateSubGraphSlice, SubGraphSliceShape } from '../types'
|
||||||
|
|
||||||
|
const initialState: Omit<SubGraphSliceShape, 'setSubGraphContext' | 'setSubGraphNodes' | 'setSubGraphEdges' | 'setSelectedOutputVar' | 'setWhenOutputNone' | 'setDefaultValue' | 'setShowDebugPanel' | 'setIsRunning' | 'setParentAvailableVars' | 'resetSubGraph'> = {
|
||||||
|
parentToolNodeId: '',
|
||||||
|
parameterKey: '',
|
||||||
|
sourceAgentNodeId: '',
|
||||||
|
sourceVariable: [],
|
||||||
|
|
||||||
|
subGraphNodes: [],
|
||||||
|
subGraphEdges: [],
|
||||||
|
|
||||||
|
selectedOutputVar: [],
|
||||||
|
whenOutputNone: 'skip',
|
||||||
|
defaultValue: '',
|
||||||
|
|
||||||
|
showDebugPanel: false,
|
||||||
|
isRunning: false,
|
||||||
|
|
||||||
|
parentAvailableVars: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createSubGraphSlice: CreateSubGraphSlice = set => ({
|
||||||
|
...initialState,
|
||||||
|
|
||||||
|
setSubGraphContext: context => set(() => ({
|
||||||
|
parentToolNodeId: context.parentToolNodeId,
|
||||||
|
parameterKey: context.parameterKey,
|
||||||
|
sourceAgentNodeId: context.sourceAgentNodeId,
|
||||||
|
sourceVariable: context.sourceVariable,
|
||||||
|
})),
|
||||||
|
|
||||||
|
setSubGraphNodes: nodes => set(() => ({ subGraphNodes: nodes })),
|
||||||
|
|
||||||
|
setSubGraphEdges: edges => set(() => ({ subGraphEdges: edges })),
|
||||||
|
|
||||||
|
setSelectedOutputVar: selector => set(() => ({ selectedOutputVar: selector })),
|
||||||
|
|
||||||
|
setWhenOutputNone: option => set(() => ({ whenOutputNone: option })),
|
||||||
|
|
||||||
|
setDefaultValue: value => set(() => ({ defaultValue: value })),
|
||||||
|
|
||||||
|
setShowDebugPanel: show => set(() => ({ showDebugPanel: show })),
|
||||||
|
|
||||||
|
setIsRunning: running => set(() => ({ isRunning: running })),
|
||||||
|
|
||||||
|
setParentAvailableVars: vars => set(() => ({ parentAvailableVars: vars })),
|
||||||
|
|
||||||
|
resetSubGraph: () => set(() => ({ ...initialState })),
|
||||||
|
})
|
||||||
65
web/app/components/sub-graph/types.ts
Normal file
65
web/app/components/sub-graph/types.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import type { StateCreator } from 'zustand'
|
||||||
|
import type { Edge, Node, NodeOutPutVar, ValueSelector, VarType } from '@/app/components/workflow/types'
|
||||||
|
|
||||||
|
export type WhenOutputNoneOption = 'skip' | 'error' | 'default'
|
||||||
|
|
||||||
|
export type SubGraphConfig = {
|
||||||
|
enabled: boolean
|
||||||
|
startNodeId: string
|
||||||
|
selectedOutputVar: ValueSelector
|
||||||
|
whenOutputNone: WhenOutputNoneOption
|
||||||
|
defaultValue?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SubGraphOutputVariable = {
|
||||||
|
nodeId: string
|
||||||
|
nodeName: string
|
||||||
|
variable: string
|
||||||
|
type: VarType
|
||||||
|
description?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SubGraphProps = {
|
||||||
|
toolNodeId: string
|
||||||
|
paramKey: string
|
||||||
|
sourceVariable: ValueSelector
|
||||||
|
agentNodeId: string
|
||||||
|
agentName: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SubGraphSliceShape = {
|
||||||
|
parentToolNodeId: string
|
||||||
|
parameterKey: string
|
||||||
|
sourceAgentNodeId: string
|
||||||
|
sourceVariable: ValueSelector
|
||||||
|
|
||||||
|
subGraphNodes: Node[]
|
||||||
|
subGraphEdges: Edge[]
|
||||||
|
|
||||||
|
selectedOutputVar: ValueSelector
|
||||||
|
whenOutputNone: WhenOutputNoneOption
|
||||||
|
defaultValue: string
|
||||||
|
|
||||||
|
showDebugPanel: boolean
|
||||||
|
isRunning: boolean
|
||||||
|
|
||||||
|
parentAvailableVars: NodeOutPutVar[]
|
||||||
|
|
||||||
|
setSubGraphContext: (context: {
|
||||||
|
parentToolNodeId: string
|
||||||
|
parameterKey: string
|
||||||
|
sourceAgentNodeId: string
|
||||||
|
sourceVariable: ValueSelector
|
||||||
|
}) => void
|
||||||
|
setSubGraphNodes: (nodes: Node[]) => void
|
||||||
|
setSubGraphEdges: (edges: Edge[]) => void
|
||||||
|
setSelectedOutputVar: (selector: ValueSelector) => void
|
||||||
|
setWhenOutputNone: (option: WhenOutputNoneOption) => void
|
||||||
|
setDefaultValue: (value: string) => void
|
||||||
|
setShowDebugPanel: (show: boolean) => void
|
||||||
|
setIsRunning: (running: boolean) => void
|
||||||
|
setParentAvailableVars: (vars: NodeOutPutVar[]) => void
|
||||||
|
resetSubGraph: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CreateSubGraphSlice = StateCreator<SubGraphSliceShape>
|
||||||
@ -337,6 +337,8 @@ const FormInputItem: FC<Props> = ({
|
|||||||
showManageInputField={showManageInputField}
|
showManageInputField={showManageInputField}
|
||||||
onManageInputField={onManageInputField}
|
onManageInputField={onManageInputField}
|
||||||
disableVariableInsertion={disableVariableInsertion}
|
disableVariableInsertion={disableVariableInsertion}
|
||||||
|
toolNodeId={nodeId}
|
||||||
|
paramKey={variable}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isNumber && isConstant && (
|
{isNumber && isConstant && (
|
||||||
|
|||||||
@ -2,11 +2,11 @@ import type { AgentBlockType } from '@/app/components/base/prompt-editor/types'
|
|||||||
import type {
|
import type {
|
||||||
Node,
|
Node,
|
||||||
NodeOutPutVar,
|
NodeOutPutVar,
|
||||||
|
ValueSelector,
|
||||||
} from '@/app/components/workflow/types'
|
} from '@/app/components/workflow/types'
|
||||||
import {
|
import {
|
||||||
memo,
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
|
||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
@ -15,6 +15,7 @@ import PromptEditor from '@/app/components/base/prompt-editor'
|
|||||||
import { useStore } from '@/app/components/workflow/store'
|
import { useStore } from '@/app/components/workflow/store'
|
||||||
import { BlockEnum } from '@/app/components/workflow/types'
|
import { BlockEnum } from '@/app/components/workflow/types'
|
||||||
import { cn } from '@/utils/classnames'
|
import { cn } from '@/utils/classnames'
|
||||||
|
import SubGraphModal from '../sub-graph-modal'
|
||||||
import AgentHeaderBar from './agent-header-bar'
|
import AgentHeaderBar from './agent-header-bar'
|
||||||
import Placeholder from './placeholder'
|
import Placeholder from './placeholder'
|
||||||
|
|
||||||
@ -33,7 +34,8 @@ type MixedVariableTextInputProps = {
|
|||||||
showManageInputField?: boolean
|
showManageInputField?: boolean
|
||||||
onManageInputField?: () => void
|
onManageInputField?: () => void
|
||||||
disableVariableInsertion?: boolean
|
disableVariableInsertion?: boolean
|
||||||
onViewInternals?: () => void
|
toolNodeId?: string
|
||||||
|
paramKey?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const MixedVariableTextInput = ({
|
const MixedVariableTextInput = ({
|
||||||
@ -45,11 +47,13 @@ const MixedVariableTextInput = ({
|
|||||||
showManageInputField,
|
showManageInputField,
|
||||||
onManageInputField,
|
onManageInputField,
|
||||||
disableVariableInsertion = false,
|
disableVariableInsertion = false,
|
||||||
onViewInternals,
|
toolNodeId,
|
||||||
|
paramKey = '',
|
||||||
}: MixedVariableTextInputProps) => {
|
}: MixedVariableTextInputProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const controlPromptEditorRerenderKey = useStore(s => s.controlPromptEditorRerenderKey)
|
const controlPromptEditorRerenderKey = useStore(s => s.controlPromptEditorRerenderKey)
|
||||||
const setControlPromptEditorRerenderKey = useStore(s => s.setControlPromptEditorRerenderKey)
|
const setControlPromptEditorRerenderKey = useStore(s => s.setControlPromptEditorRerenderKey)
|
||||||
|
const [isSubGraphModalOpen, setIsSubGraphModalOpen] = useState(false)
|
||||||
|
|
||||||
const nodesByIdMap = useMemo(() => {
|
const nodesByIdMap = useMemo(() => {
|
||||||
return availableNodes.reduce((acc, node) => {
|
return availableNodes.reduce((acc, node) => {
|
||||||
@ -79,11 +83,6 @@ const MixedVariableTextInput = ({
|
|||||||
|
|
||||||
const [selectedAgent, setSelectedAgent] = useState<{ id: string, title: string } | null>(null)
|
const [selectedAgent, setSelectedAgent] = useState<{ id: string, title: string } | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!detectedAgentFromValue && selectedAgent)
|
|
||||||
setSelectedAgent(null)
|
|
||||||
}, [detectedAgentFromValue, selectedAgent])
|
|
||||||
|
|
||||||
const agentNodes = useMemo(() => {
|
const agentNodes = useMemo(() => {
|
||||||
return availableNodes
|
return availableNodes
|
||||||
.filter(node => node.data.type === BlockEnum.Agent)
|
.filter(node => node.data.type === BlockEnum.Agent)
|
||||||
@ -115,6 +114,18 @@ const MixedVariableTextInput = ({
|
|||||||
|
|
||||||
const displayedAgent = detectedAgentFromValue || (selectedAgent ? { nodeId: selectedAgent.id, name: selectedAgent.title } : null)
|
const displayedAgent = detectedAgentFromValue || (selectedAgent ? { nodeId: selectedAgent.id, name: selectedAgent.title } : null)
|
||||||
|
|
||||||
|
const handleOpenSubGraphModal = useCallback(() => {
|
||||||
|
setIsSubGraphModalOpen(true)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleCloseSubGraphModal = useCallback(() => {
|
||||||
|
setIsSubGraphModalOpen(false)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const sourceVariable: ValueSelector | undefined = displayedAgent
|
||||||
|
? [displayedAgent.nodeId, 'text']
|
||||||
|
: undefined
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn(
|
<div className={cn(
|
||||||
'w-full rounded-lg border border-transparent bg-components-input-bg-normal',
|
'w-full rounded-lg border border-transparent bg-components-input-bg-normal',
|
||||||
@ -126,7 +137,7 @@ const MixedVariableTextInput = ({
|
|||||||
<AgentHeaderBar
|
<AgentHeaderBar
|
||||||
agentName={displayedAgent.name}
|
agentName={displayedAgent.name}
|
||||||
onRemove={handleAgentRemove}
|
onRemove={handleAgentRemove}
|
||||||
onViewInternals={onViewInternals}
|
onViewInternals={handleOpenSubGraphModal}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<PromptEditor
|
<PromptEditor
|
||||||
@ -162,6 +173,17 @@ const MixedVariableTextInput = ({
|
|||||||
placeholder={<Placeholder disableVariableInsertion={disableVariableInsertion} hasSelectedAgent={!!displayedAgent} />}
|
placeholder={<Placeholder disableVariableInsertion={disableVariableInsertion} hasSelectedAgent={!!displayedAgent} />}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
|
{toolNodeId && displayedAgent && sourceVariable && (
|
||||||
|
<SubGraphModal
|
||||||
|
isOpen={isSubGraphModalOpen}
|
||||||
|
onClose={handleCloseSubGraphModal}
|
||||||
|
toolNodeId={toolNodeId}
|
||||||
|
paramKey={paramKey}
|
||||||
|
sourceVariable={sourceVariable}
|
||||||
|
agentName={displayedAgent.name}
|
||||||
|
agentNodeId={displayedAgent.nodeId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,83 @@
|
|||||||
|
'use client'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import type { ConfigPanelProps, WhenOutputNoneOption } from './types'
|
||||||
|
import { memo, useCallback, useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import Field from '@/app/components/workflow/nodes/_base/components/field'
|
||||||
|
import { cn } from '@/utils/classnames'
|
||||||
|
|
||||||
|
const outputVariables = [
|
||||||
|
{ name: 'text', type: 'string' },
|
||||||
|
{ name: 'structured_output', type: 'object' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const ConfigPanel: FC<ConfigPanelProps> = ({
|
||||||
|
toolNodeId: _toolNodeId,
|
||||||
|
paramKey: _paramKey,
|
||||||
|
activeTab,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const [whenOutputNone, setWhenOutputNone] = useState<WhenOutputNoneOption>('skip')
|
||||||
|
|
||||||
|
const handleWhenOutputNoneChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
|
setWhenOutputNone(e.target.value as WhenOutputNoneOption)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (activeTab === 'lastRun') {
|
||||||
|
return (
|
||||||
|
<div className="flex h-full items-center justify-center p-4">
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="system-sm-regular text-text-tertiary">
|
||||||
|
{t('subGraphModal.noRunHistory', { ns: 'workflow' })}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4 p-4">
|
||||||
|
<Field
|
||||||
|
title={t('subGraphModal.outputVariables', { ns: 'workflow' })}
|
||||||
|
>
|
||||||
|
<div className="space-y-2">
|
||||||
|
{outputVariables.map(variable => (
|
||||||
|
<div
|
||||||
|
key={variable.name}
|
||||||
|
className="flex items-center justify-between rounded-lg bg-components-input-bg-normal px-3 py-2"
|
||||||
|
>
|
||||||
|
<span className="system-sm-medium text-text-secondary">{variable.name}</span>
|
||||||
|
<span className="system-xs-regular text-text-tertiary">{variable.type}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
title={t('subGraphModal.whenOutputIsNone', { ns: 'workflow' })}
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
className={cn(
|
||||||
|
'w-full rounded-lg border border-components-input-border-active bg-components-input-bg-normal px-3 py-2',
|
||||||
|
'system-sm-regular text-text-secondary',
|
||||||
|
'focus:border-primary-600 focus:outline-none',
|
||||||
|
)}
|
||||||
|
value={whenOutputNone}
|
||||||
|
onChange={handleWhenOutputNoneChange}
|
||||||
|
>
|
||||||
|
<option value="skip">
|
||||||
|
{t('subGraphModal.whenOutputNone.skip', { ns: 'workflow' })}
|
||||||
|
</option>
|
||||||
|
<option value="error">
|
||||||
|
{t('subGraphModal.whenOutputNone.error', { ns: 'workflow' })}
|
||||||
|
</option>
|
||||||
|
<option value="default">
|
||||||
|
{t('subGraphModal.whenOutputNone.default', { ns: 'workflow' })}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</Field>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ConfigPanel)
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
'use client'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import type { SubGraphModalProps } from './types'
|
||||||
|
import { Dialog, DialogPanel, Transition, TransitionChild } from '@headlessui/react'
|
||||||
|
import { RiCloseLine } from '@remixicon/react'
|
||||||
|
import { noop } from 'es-toolkit/function'
|
||||||
|
import { Fragment, memo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Agent } from '@/app/components/base/icons/src/vender/workflow'
|
||||||
|
import SubGraphCanvas from './sub-graph-canvas'
|
||||||
|
|
||||||
|
const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||||
|
isOpen,
|
||||||
|
onClose,
|
||||||
|
toolNodeId,
|
||||||
|
paramKey,
|
||||||
|
sourceVariable,
|
||||||
|
agentName,
|
||||||
|
agentNodeId,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Transition appear show={isOpen} as={Fragment}>
|
||||||
|
<Dialog as="div" className="relative z-[60]" onClose={noop}>
|
||||||
|
<TransitionChild>
|
||||||
|
<div className="fixed inset-0 bg-background-overlay duration-300 ease-in data-[closed]:opacity-0 data-[enter]:opacity-100 data-[leave]:opacity-0" />
|
||||||
|
</TransitionChild>
|
||||||
|
<div className="fixed inset-0 overflow-hidden">
|
||||||
|
<div className="flex h-full w-full items-center justify-center px-[10px] pb-[4px] pt-[24px]">
|
||||||
|
<TransitionChild>
|
||||||
|
<DialogPanel className="flex h-full w-full flex-col overflow-hidden rounded-2xl bg-components-panel-bg shadow-xl duration-100 ease-in data-[closed]:scale-95 data-[enter]:scale-100 data-[leave]:scale-95 data-[closed]:opacity-0 data-[enter]:opacity-100 data-[leave]:opacity-0">
|
||||||
|
<div className="flex h-14 shrink-0 items-center justify-between border-b border-divider-subtle px-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="flex h-6 w-6 items-center justify-center rounded bg-util-colors-indigo-indigo-500">
|
||||||
|
<Agent className="h-4 w-4 text-text-primary-on-surface" />
|
||||||
|
</div>
|
||||||
|
<span className="system-md-semibold text-text-primary">
|
||||||
|
@
|
||||||
|
{agentName}
|
||||||
|
{' '}
|
||||||
|
{t('subGraphModal.title', { ns: 'workflow' })}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="flex h-8 w-8 items-center justify-center rounded-lg hover:bg-state-base-hover"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
<RiCloseLine className="h-5 w-5 text-text-tertiary" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-workflow-canvas-wrapper relative flex-1 overflow-hidden">
|
||||||
|
<SubGraphCanvas
|
||||||
|
toolNodeId={toolNodeId}
|
||||||
|
paramKey={paramKey}
|
||||||
|
sourceVariable={sourceVariable}
|
||||||
|
agentNodeId={agentNodeId}
|
||||||
|
agentName={agentName}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</DialogPanel>
|
||||||
|
</TransitionChild>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</Transition>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(SubGraphModal)
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import type { CreateSubGraphSlice, SubGraphSliceShape } from './types'
|
||||||
|
|
||||||
|
const initialState: Omit<SubGraphSliceShape, 'setSubGraphContext' | 'setSubGraphNodes' | 'setSubGraphEdges' | 'setSelectedOutputVar' | 'setWhenOutputNone' | 'setDefaultValue' | 'setShowDebugPanel' | 'setIsRunning' | 'setParentAvailableVars' | 'resetSubGraph'> = {
|
||||||
|
parentToolNodeId: '',
|
||||||
|
parameterKey: '',
|
||||||
|
sourceAgentNodeId: '',
|
||||||
|
sourceVariable: [],
|
||||||
|
|
||||||
|
subGraphNodes: [],
|
||||||
|
subGraphEdges: [],
|
||||||
|
|
||||||
|
selectedOutputVar: [],
|
||||||
|
whenOutputNone: 'skip',
|
||||||
|
defaultValue: '',
|
||||||
|
|
||||||
|
showDebugPanel: false,
|
||||||
|
isRunning: false,
|
||||||
|
|
||||||
|
parentAvailableVars: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createSubGraphSlice: CreateSubGraphSlice = set => ({
|
||||||
|
...initialState,
|
||||||
|
|
||||||
|
setSubGraphContext: context => set(() => ({
|
||||||
|
parentToolNodeId: context.parentToolNodeId,
|
||||||
|
parameterKey: context.parameterKey,
|
||||||
|
sourceAgentNodeId: context.sourceAgentNodeId,
|
||||||
|
sourceVariable: context.sourceVariable,
|
||||||
|
})),
|
||||||
|
|
||||||
|
setSubGraphNodes: nodes => set(() => ({ subGraphNodes: nodes })),
|
||||||
|
|
||||||
|
setSubGraphEdges: edges => set(() => ({ subGraphEdges: edges })),
|
||||||
|
|
||||||
|
setSelectedOutputVar: selector => set(() => ({ selectedOutputVar: selector })),
|
||||||
|
|
||||||
|
setWhenOutputNone: option => set(() => ({ whenOutputNone: option })),
|
||||||
|
|
||||||
|
setDefaultValue: value => set(() => ({ defaultValue: value })),
|
||||||
|
|
||||||
|
setShowDebugPanel: show => set(() => ({ showDebugPanel: show })),
|
||||||
|
|
||||||
|
setIsRunning: running => set(() => ({ isRunning: running })),
|
||||||
|
|
||||||
|
setParentAvailableVars: vars => set(() => ({ parentAvailableVars: vars })),
|
||||||
|
|
||||||
|
resetSubGraph: () => set(() => ({ ...initialState })),
|
||||||
|
})
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
'use client'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import type { SubGraphCanvasProps } from './types'
|
||||||
|
import { memo } from 'react'
|
||||||
|
import SubGraph from '@/app/components/sub-graph'
|
||||||
|
|
||||||
|
const SubGraphCanvas: FC<SubGraphCanvasProps> = ({
|
||||||
|
toolNodeId,
|
||||||
|
paramKey,
|
||||||
|
sourceVariable,
|
||||||
|
agentNodeId,
|
||||||
|
agentName,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="h-full w-full">
|
||||||
|
<SubGraph
|
||||||
|
toolNodeId={toolNodeId}
|
||||||
|
paramKey={paramKey}
|
||||||
|
sourceVariable={sourceVariable}
|
||||||
|
agentNodeId={agentNodeId}
|
||||||
|
agentName={agentName}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(SubGraphCanvas)
|
||||||
@ -0,0 +1,103 @@
|
|||||||
|
import type { StateCreator } from 'zustand'
|
||||||
|
import type { Edge, Node, NodeOutPutVar, ValueSelector, VarType } from '@/app/components/workflow/types'
|
||||||
|
|
||||||
|
export type SubGraphNodeData = {
|
||||||
|
isInSubGraph: boolean
|
||||||
|
subGraph_id: string
|
||||||
|
subGraphParamKey: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SubGraphNode = Node & {
|
||||||
|
data: Node['data'] & SubGraphNodeData
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SubGraphSourceNodeData = {
|
||||||
|
title: string
|
||||||
|
sourceAgentNodeId: string
|
||||||
|
sourceVariable: ValueSelector
|
||||||
|
sourceVarType: VarType
|
||||||
|
isReadOnly: true
|
||||||
|
isInSubGraph: true
|
||||||
|
subGraph_id: string
|
||||||
|
subGraphParamKey: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WhenOutputNoneOption = 'skip' | 'error' | 'default'
|
||||||
|
|
||||||
|
export type SubGraphConfig = {
|
||||||
|
enabled: boolean
|
||||||
|
startNodeId: string
|
||||||
|
selectedOutputVar: ValueSelector
|
||||||
|
whenOutputNone: WhenOutputNoneOption
|
||||||
|
defaultValue?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SubGraphOutputVariable = {
|
||||||
|
nodeId: string
|
||||||
|
nodeName: string
|
||||||
|
variable: string
|
||||||
|
type: VarType
|
||||||
|
description?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SubGraphModalProps = {
|
||||||
|
isOpen: boolean
|
||||||
|
onClose: () => void
|
||||||
|
toolNodeId: string
|
||||||
|
paramKey: string
|
||||||
|
sourceVariable: ValueSelector
|
||||||
|
agentName: string
|
||||||
|
agentNodeId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ConfigPanelProps = {
|
||||||
|
toolNodeId: string
|
||||||
|
paramKey: string
|
||||||
|
activeTab: 'settings' | 'lastRun'
|
||||||
|
onTabChange: (tab: 'settings' | 'lastRun') => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SubGraphCanvasProps = {
|
||||||
|
toolNodeId: string
|
||||||
|
paramKey: string
|
||||||
|
sourceVariable: ValueSelector
|
||||||
|
agentNodeId: string
|
||||||
|
agentName: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SubGraphSliceShape = {
|
||||||
|
parentToolNodeId: string
|
||||||
|
parameterKey: string
|
||||||
|
sourceAgentNodeId: string
|
||||||
|
sourceVariable: ValueSelector
|
||||||
|
|
||||||
|
subGraphNodes: SubGraphNode[]
|
||||||
|
subGraphEdges: Edge[]
|
||||||
|
|
||||||
|
selectedOutputVar: ValueSelector
|
||||||
|
whenOutputNone: WhenOutputNoneOption
|
||||||
|
defaultValue: string
|
||||||
|
|
||||||
|
showDebugPanel: boolean
|
||||||
|
isRunning: boolean
|
||||||
|
|
||||||
|
parentAvailableVars: NodeOutPutVar[]
|
||||||
|
|
||||||
|
setSubGraphContext: (context: {
|
||||||
|
parentToolNodeId: string
|
||||||
|
parameterKey: string
|
||||||
|
sourceAgentNodeId: string
|
||||||
|
sourceVariable: ValueSelector
|
||||||
|
}) => void
|
||||||
|
setSubGraphNodes: (nodes: SubGraphNode[]) => void
|
||||||
|
setSubGraphEdges: (edges: Edge[]) => void
|
||||||
|
setSelectedOutputVar: (selector: ValueSelector) => void
|
||||||
|
setWhenOutputNone: (option: WhenOutputNoneOption) => void
|
||||||
|
setDefaultValue: (value: string) => void
|
||||||
|
setShowDebugPanel: (show: boolean) => void
|
||||||
|
setIsRunning: (running: boolean) => void
|
||||||
|
setParentAvailableVars: (vars: NodeOutPutVar[]) => void
|
||||||
|
resetSubGraph: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CreateSubGraphSlice = StateCreator<SubGraphSliceShape>
|
||||||
@ -205,7 +205,7 @@
|
|||||||
"common.runApp": "Run App",
|
"common.runApp": "Run App",
|
||||||
"common.runHistory": "Run History",
|
"common.runHistory": "Run History",
|
||||||
"common.running": "Running",
|
"common.running": "Running",
|
||||||
"common.searchAgent": "Search agent",
|
"common.searchAgent": "Search agent...",
|
||||||
"common.searchVar": "Search variable",
|
"common.searchVar": "Search variable",
|
||||||
"common.setVarValuePlaceholder": "Set variable",
|
"common.setVarValuePlaceholder": "Set variable",
|
||||||
"common.showRunHistory": "Show Run History",
|
"common.showRunHistory": "Show Run History",
|
||||||
@ -217,7 +217,7 @@
|
|||||||
"common.variableNamePlaceholder": "Variable name",
|
"common.variableNamePlaceholder": "Variable name",
|
||||||
"common.versionHistory": "Version History",
|
"common.versionHistory": "Version History",
|
||||||
"common.viewDetailInTracingPanel": "View details",
|
"common.viewDetailInTracingPanel": "View details",
|
||||||
"common.viewInternals": "View internals",
|
"common.viewInternals": "View Internals",
|
||||||
"common.viewOnly": "View Only",
|
"common.viewOnly": "View Only",
|
||||||
"common.viewRunHistory": "View run history",
|
"common.viewRunHistory": "View run history",
|
||||||
"common.workflowAsTool": "Workflow as Tool",
|
"common.workflowAsTool": "Workflow as Tool",
|
||||||
@ -988,6 +988,18 @@
|
|||||||
"singleRun.testRun": "Test Run",
|
"singleRun.testRun": "Test Run",
|
||||||
"singleRun.testRunIteration": "Test Run Iteration",
|
"singleRun.testRunIteration": "Test Run Iteration",
|
||||||
"singleRun.testRunLoop": "Test Run Loop",
|
"singleRun.testRunLoop": "Test Run Loop",
|
||||||
|
"subGraphModal.canvasPlaceholder": "Click to configure the internal structure",
|
||||||
|
"subGraphModal.internalStructure": "Internal structure",
|
||||||
|
"subGraphModal.lastRun": "LAST RUN",
|
||||||
|
"subGraphModal.noRunHistory": "No run history yet",
|
||||||
|
"subGraphModal.outputVariables": "OUTPUT VARIABLES",
|
||||||
|
"subGraphModal.settings": "SETTINGS",
|
||||||
|
"subGraphModal.sourceNode": "SOURCE",
|
||||||
|
"subGraphModal.title": "INTERNAL STRUCTURE",
|
||||||
|
"subGraphModal.whenOutputIsNone": "WHEN OUTPUT IS NONE",
|
||||||
|
"subGraphModal.whenOutputNone.default": "Use default value",
|
||||||
|
"subGraphModal.whenOutputNone.error": "Raise an error",
|
||||||
|
"subGraphModal.whenOutputNone.skip": "Skip this step",
|
||||||
"tabs.-": "Default",
|
"tabs.-": "Default",
|
||||||
"tabs.addAll": "Add all",
|
"tabs.addAll": "Add all",
|
||||||
"tabs.agent": "Agent Strategy",
|
"tabs.agent": "Agent Strategy",
|
||||||
|
|||||||
@ -203,7 +203,7 @@
|
|||||||
"common.runApp": "アプリを実行",
|
"common.runApp": "アプリを実行",
|
||||||
"common.runHistory": "実行履歴",
|
"common.runHistory": "実行履歴",
|
||||||
"common.running": "実行中",
|
"common.running": "実行中",
|
||||||
"common.searchAgent": "エージェントを検索",
|
"common.searchAgent": "エージェントを検索...",
|
||||||
"common.searchVar": "変数を検索",
|
"common.searchVar": "変数を検索",
|
||||||
"common.setVarValuePlaceholder": "変数値を設定",
|
"common.setVarValuePlaceholder": "変数値を設定",
|
||||||
"common.showRunHistory": "実行履歴を表示",
|
"common.showRunHistory": "実行履歴を表示",
|
||||||
@ -215,7 +215,7 @@
|
|||||||
"common.variableNamePlaceholder": "変数名を入力",
|
"common.variableNamePlaceholder": "変数名を入力",
|
||||||
"common.versionHistory": "バージョン履歴",
|
"common.versionHistory": "バージョン履歴",
|
||||||
"common.viewDetailInTracingPanel": "詳細を表示",
|
"common.viewDetailInTracingPanel": "詳細を表示",
|
||||||
"common.viewInternals": "内部を表示",
|
"common.viewInternals": "内部構造を表示",
|
||||||
"common.viewOnly": "閲覧のみ",
|
"common.viewOnly": "閲覧のみ",
|
||||||
"common.viewRunHistory": "実行履歴を表示",
|
"common.viewRunHistory": "実行履歴を表示",
|
||||||
"common.workflowAsTool": "ワークフローをツールとして公開する",
|
"common.workflowAsTool": "ワークフローをツールとして公開する",
|
||||||
@ -985,6 +985,18 @@
|
|||||||
"singleRun.testRun": "テスト実行",
|
"singleRun.testRun": "テスト実行",
|
||||||
"singleRun.testRunIteration": "テスト実行(イテレーション)",
|
"singleRun.testRunIteration": "テスト実行(イテレーション)",
|
||||||
"singleRun.testRunLoop": "テスト実行ループ",
|
"singleRun.testRunLoop": "テスト実行ループ",
|
||||||
|
"subGraphModal.canvasPlaceholder": "クリックして内部構造を設定",
|
||||||
|
"subGraphModal.internalStructure": "内部構造",
|
||||||
|
"subGraphModal.lastRun": "前回の実行",
|
||||||
|
"subGraphModal.noRunHistory": "実行履歴がありません",
|
||||||
|
"subGraphModal.outputVariables": "出力変数",
|
||||||
|
"subGraphModal.settings": "設定",
|
||||||
|
"subGraphModal.sourceNode": "ソース",
|
||||||
|
"subGraphModal.title": "内部構造",
|
||||||
|
"subGraphModal.whenOutputIsNone": "出力が空の場合",
|
||||||
|
"subGraphModal.whenOutputNone.default": "デフォルト値を使用",
|
||||||
|
"subGraphModal.whenOutputNone.error": "エラーを発生させる",
|
||||||
|
"subGraphModal.whenOutputNone.skip": "このステップをスキップ",
|
||||||
"tabs.-": "デフォルト",
|
"tabs.-": "デフォルト",
|
||||||
"tabs.addAll": "すべてを追加する",
|
"tabs.addAll": "すべてを追加する",
|
||||||
"tabs.agent": "エージェント戦略",
|
"tabs.agent": "エージェント戦略",
|
||||||
|
|||||||
@ -171,7 +171,7 @@
|
|||||||
"common.needConnectTip": "此节点尚未连接到其他节点",
|
"common.needConnectTip": "此节点尚未连接到其他节点",
|
||||||
"common.needOutputNode": "必须添加输出节点",
|
"common.needOutputNode": "必须添加输出节点",
|
||||||
"common.needStartNode": "必须添加至少一个开始节点",
|
"common.needStartNode": "必须添加至少一个开始节点",
|
||||||
"common.noAgentNodes": "没有可用的代理节点",
|
"common.noAgentNodes": "没有可用的 Agent 节点",
|
||||||
"common.noHistory": "没有历史版本",
|
"common.noHistory": "没有历史版本",
|
||||||
"common.noVar": "没有变量",
|
"common.noVar": "没有变量",
|
||||||
"common.notRunning": "尚未运行",
|
"common.notRunning": "尚未运行",
|
||||||
@ -203,7 +203,7 @@
|
|||||||
"common.runApp": "运行",
|
"common.runApp": "运行",
|
||||||
"common.runHistory": "运行历史",
|
"common.runHistory": "运行历史",
|
||||||
"common.running": "运行中",
|
"common.running": "运行中",
|
||||||
"common.searchAgent": "搜索代理",
|
"common.searchAgent": "搜索 Agent...",
|
||||||
"common.searchVar": "搜索变量",
|
"common.searchVar": "搜索变量",
|
||||||
"common.setVarValuePlaceholder": "设置变量值",
|
"common.setVarValuePlaceholder": "设置变量值",
|
||||||
"common.showRunHistory": "显示运行历史",
|
"common.showRunHistory": "显示运行历史",
|
||||||
@ -215,7 +215,7 @@
|
|||||||
"common.variableNamePlaceholder": "变量名",
|
"common.variableNamePlaceholder": "变量名",
|
||||||
"common.versionHistory": "版本历史",
|
"common.versionHistory": "版本历史",
|
||||||
"common.viewDetailInTracingPanel": "查看详细信息",
|
"common.viewDetailInTracingPanel": "查看详细信息",
|
||||||
"common.viewInternals": "查看内部",
|
"common.viewInternals": "查看内部结构",
|
||||||
"common.viewOnly": "只读",
|
"common.viewOnly": "只读",
|
||||||
"common.viewRunHistory": "查看运行历史",
|
"common.viewRunHistory": "查看运行历史",
|
||||||
"common.workflowAsTool": "发布为工具",
|
"common.workflowAsTool": "发布为工具",
|
||||||
@ -986,6 +986,18 @@
|
|||||||
"singleRun.testRun": "测试运行",
|
"singleRun.testRun": "测试运行",
|
||||||
"singleRun.testRunIteration": "测试运行迭代",
|
"singleRun.testRunIteration": "测试运行迭代",
|
||||||
"singleRun.testRunLoop": "测试运行循环",
|
"singleRun.testRunLoop": "测试运行循环",
|
||||||
|
"subGraphModal.canvasPlaceholder": "点击配置内部结构",
|
||||||
|
"subGraphModal.internalStructure": "内部结构",
|
||||||
|
"subGraphModal.lastRun": "上次运行",
|
||||||
|
"subGraphModal.noRunHistory": "暂无运行记录",
|
||||||
|
"subGraphModal.outputVariables": "输出变量",
|
||||||
|
"subGraphModal.settings": "设置",
|
||||||
|
"subGraphModal.sourceNode": "来源",
|
||||||
|
"subGraphModal.title": "内部结构",
|
||||||
|
"subGraphModal.whenOutputIsNone": "当输出为空时",
|
||||||
|
"subGraphModal.whenOutputNone.default": "使用默认值",
|
||||||
|
"subGraphModal.whenOutputNone.error": "抛出错误",
|
||||||
|
"subGraphModal.whenOutputNone.skip": "跳过此步骤",
|
||||||
"tabs.-": "默认",
|
"tabs.-": "默认",
|
||||||
"tabs.addAll": "添加全部",
|
"tabs.addAll": "添加全部",
|
||||||
"tabs.agent": "Agent 策略",
|
"tabs.agent": "Agent 策略",
|
||||||
|
|||||||
@ -171,7 +171,7 @@
|
|||||||
"common.needConnectTip": "此節點尚未連接到其他節點",
|
"common.needConnectTip": "此節點尚未連接到其他節點",
|
||||||
"common.needOutputNode": "必須新增輸出節點",
|
"common.needOutputNode": "必須新增輸出節點",
|
||||||
"common.needStartNode": "至少必須新增一個起始節點",
|
"common.needStartNode": "至少必須新增一個起始節點",
|
||||||
"common.noAgentNodes": "沒有可用的代理節點",
|
"common.noAgentNodes": "沒有可用的 Agent 節點",
|
||||||
"common.noHistory": "無歷史記錄",
|
"common.noHistory": "無歷史記錄",
|
||||||
"common.noVar": "沒有變數",
|
"common.noVar": "沒有變數",
|
||||||
"common.notRunning": "尚未運行",
|
"common.notRunning": "尚未運行",
|
||||||
@ -203,7 +203,7 @@
|
|||||||
"common.runApp": "運行",
|
"common.runApp": "運行",
|
||||||
"common.runHistory": "運行歷史",
|
"common.runHistory": "運行歷史",
|
||||||
"common.running": "運行中",
|
"common.running": "運行中",
|
||||||
"common.searchAgent": "搜索代理",
|
"common.searchAgent": "搜尋 Agent...",
|
||||||
"common.searchVar": "搜索變數",
|
"common.searchVar": "搜索變數",
|
||||||
"common.setVarValuePlaceholder": "設置變數值",
|
"common.setVarValuePlaceholder": "設置變數值",
|
||||||
"common.showRunHistory": "顯示運行歷史",
|
"common.showRunHistory": "顯示運行歷史",
|
||||||
@ -215,7 +215,7 @@
|
|||||||
"common.variableNamePlaceholder": "變數名",
|
"common.variableNamePlaceholder": "變數名",
|
||||||
"common.versionHistory": "版本歷史",
|
"common.versionHistory": "版本歷史",
|
||||||
"common.viewDetailInTracingPanel": "查看詳細信息",
|
"common.viewDetailInTracingPanel": "查看詳細信息",
|
||||||
"common.viewInternals": "查看內部",
|
"common.viewInternals": "檢視內部結構",
|
||||||
"common.viewOnly": "只讀",
|
"common.viewOnly": "只讀",
|
||||||
"common.viewRunHistory": "查看運行歷史",
|
"common.viewRunHistory": "查看運行歷史",
|
||||||
"common.workflowAsTool": "發佈為工具",
|
"common.workflowAsTool": "發佈為工具",
|
||||||
@ -985,6 +985,18 @@
|
|||||||
"singleRun.testRun": "測試運行",
|
"singleRun.testRun": "測試運行",
|
||||||
"singleRun.testRunIteration": "測試運行迭代",
|
"singleRun.testRunIteration": "測試運行迭代",
|
||||||
"singleRun.testRunLoop": "測試運行循環",
|
"singleRun.testRunLoop": "測試運行循環",
|
||||||
|
"subGraphModal.canvasPlaceholder": "點擊配置內部結構",
|
||||||
|
"subGraphModal.internalStructure": "內部結構",
|
||||||
|
"subGraphModal.lastRun": "上次執行",
|
||||||
|
"subGraphModal.noRunHistory": "暫無執行記錄",
|
||||||
|
"subGraphModal.outputVariables": "輸出變數",
|
||||||
|
"subGraphModal.settings": "設定",
|
||||||
|
"subGraphModal.sourceNode": "來源",
|
||||||
|
"subGraphModal.title": "內部結構",
|
||||||
|
"subGraphModal.whenOutputIsNone": "當輸出為空時",
|
||||||
|
"subGraphModal.whenOutputNone.default": "使用預設值",
|
||||||
|
"subGraphModal.whenOutputNone.error": "拋出錯誤",
|
||||||
|
"subGraphModal.whenOutputNone.skip": "跳過此步驟",
|
||||||
"tabs.-": "預設",
|
"tabs.-": "預設",
|
||||||
"tabs.addAll": "全部新增",
|
"tabs.addAll": "全部新增",
|
||||||
"tabs.agent": "代理策略",
|
"tabs.agent": "代理策略",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user