mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 10:28:10 +08:00
feat: add AssembleVariablesAlt icon and integrate into sub-graph
components.
This commit is contained in:
@ -351,6 +351,25 @@ const VarReferenceVars: FC<Props> = ({
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
showAssembleVariables && (
|
||||
<div className="flex items-center border-t border-divider-subtle pt-1">
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-6 w-full items-center rounded-md pl-3 pr-[18px] text-text-secondary hover:bg-state-base-hover"
|
||||
onClick={handleAssembleVariables}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
>
|
||||
<span className="mr-1 flex h-4 w-4 items-center justify-center rounded bg-util-colors-blue-blue-500">
|
||||
<AssembleVariables className="h-3 w-3 text-text-primary-on-surface" />
|
||||
</span>
|
||||
<span className="system-xs-medium truncate" title={t('nodes.tool.assembleVariables', { ns: 'workflow' })}>
|
||||
{t('nodes.tool.assembleVariables', { ns: 'workflow' })}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{filteredVars.length > 0
|
||||
? (
|
||||
<div className={cn('max-h-[85vh] overflow-y-auto', maxHeightClass)}>
|
||||
@ -404,25 +423,6 @@ const VarReferenceVars: FC<Props> = ({
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
showAssembleVariables && (
|
||||
<div className="flex items-center border-t border-divider-subtle pt-1">
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-6 w-full items-center rounded-md pl-3 pr-[18px] text-text-secondary hover:bg-state-base-hover"
|
||||
onClick={handleAssembleVariables}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
>
|
||||
<span className="mr-1 flex h-4 w-4 items-center justify-center rounded bg-util-colors-blue-blue-500">
|
||||
<AssembleVariables className="h-3 w-3 text-text-primary-on-surface" />
|
||||
</span>
|
||||
<span className="system-xs-medium truncate" title={t('nodes.tool.assembleVariables', { ns: 'workflow' })}>
|
||||
{t('nodes.tool.assembleVariables', { ns: 'workflow' })}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export const CUSTOM_SUB_GRAPH_START_NODE = 'custom-sub-graph-start'
|
||||
60
web/app/components/workflow/nodes/sub-graph-start/index.tsx
Normal file
60
web/app/components/workflow/nodes/sub-graph-start/index.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
import type { NodeProps } from 'reactflow'
|
||||
import type { CommonNodeType } from '@/app/components/workflow/types'
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AssembleVariablesAlt } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { Agent } from '@/app/components/base/icons/src/vender/workflow'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { NodeSourceHandle } from '@/app/components/workflow/nodes/_base/components/node-handle'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type SubGraphStartNodeData = CommonNodeType<{
|
||||
tooltip?: string
|
||||
iconType?: string
|
||||
}>
|
||||
|
||||
type IconComponent = typeof Agent
|
||||
|
||||
const iconMap: Record<string, IconComponent> = {
|
||||
agent: Agent,
|
||||
assemble: AssembleVariablesAlt,
|
||||
}
|
||||
|
||||
const SubGraphStartNode = ({ id, data }: NodeProps<SubGraphStartNodeData>) => {
|
||||
const { t } = useTranslation()
|
||||
const iconType = data?.iconType || 'agent'
|
||||
const Icon = iconMap[iconType] || Agent
|
||||
const rawTitle = data?.title?.trim() || ''
|
||||
const showTitle = iconType === 'agent' && !!rawTitle
|
||||
const displayTitle = showTitle && (rawTitle.startsWith('@') ? rawTitle : `@${rawTitle}`)
|
||||
const tooltip = data?.tooltip
|
||||
|| (iconType === 'assemble' ? t('blocks.start', { ns: 'workflow' }) : (data?.title || t('blocks.start', { ns: 'workflow' })))
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'nodrag group mt-1 flex h-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg shadow-xs',
|
||||
showTitle ? 'gap-1.5 px-2' : 'w-11',
|
||||
)}
|
||||
>
|
||||
<Tooltip popupContent={tooltip} asChild={false}>
|
||||
<div className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500">
|
||||
<Icon className="h-3 w-3 text-text-primary-on-surface" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
{showTitle && (
|
||||
<span className="system-xs-medium max-w-[160px] truncate text-text-secondary">
|
||||
{displayTitle}
|
||||
</span>
|
||||
)}
|
||||
<NodeSourceHandle
|
||||
id={id}
|
||||
data={data}
|
||||
handleClassName="!top-1/2 !-right-[9px] !-translate-y-1/2"
|
||||
handleId="source"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(SubGraphStartNode)
|
||||
@ -2,6 +2,7 @@ import type { FC } from 'react'
|
||||
import { RiCloseLine, RiEqualizer2Line } from '@remixicon/react'
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AssembleVariables } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import AlertTriangle from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback/AlertTriangle'
|
||||
import { Agent } from '@/app/components/base/icons/src/vender/workflow'
|
||||
import { cn } from '@/utils/classnames'
|
||||
@ -34,8 +35,11 @@ const AgentHeaderBar: FC<AgentHeaderBarProps> = ({
|
||||
: 'border-components-panel-border-subtle bg-components-badge-white-to-dark',
|
||||
)}
|
||||
>
|
||||
<div className="flex h-4 w-4 items-center justify-center rounded bg-util-colors-indigo-indigo-500">
|
||||
<Agent className="h-3 w-3 text-text-primary-on-surface" />
|
||||
<div className={cn('flex h-4 w-4 items-center justify-center rounded', showAtPrefix
|
||||
? 'bg-util-colors-indigo-indigo-500'
|
||||
: 'bg-util-colors-blue-blue-500')}
|
||||
>
|
||||
{showAtPrefix ? <Agent className="h-3 w-3 text-text-primary-on-surface" /> : <AssembleVariables className="h-3 w-3 text-text-primary-on-surface" />}
|
||||
</div>
|
||||
<span className="system-xs-medium text-text-secondary">
|
||||
{showAtPrefix && '@'}
|
||||
|
||||
@ -36,9 +36,16 @@ import Placeholder from './placeholder'
|
||||
|
||||
/**
|
||||
* Matches agent context variable syntax: {{@nodeId.context@}}
|
||||
* Example: {{@agent-123.context@}} -> captures "agent-123"
|
||||
* Example: {{@agent-123.context@}}
|
||||
*/
|
||||
const AGENT_CONTEXT_VAR_PATTERN = /\{\{@([^.@#]+)\.context@\}\}/g
|
||||
const AGENT_CONTEXT_VAR_PATTERN = /\{\{@[^.@#]+\.context@\}\}/g
|
||||
const AGENT_CONTEXT_VAR_PREFIX = '{{@'
|
||||
const AGENT_CONTEXT_VAR_SUFFIX = '.context@}}'
|
||||
const getAgentNodeIdFromContextVar = (placeholder: string) => {
|
||||
if (!placeholder.startsWith(AGENT_CONTEXT_VAR_PREFIX) || !placeholder.endsWith(AGENT_CONTEXT_VAR_SUFFIX))
|
||||
return ''
|
||||
return placeholder.slice(AGENT_CONTEXT_VAR_PREFIX.length, -AGENT_CONTEXT_VAR_SUFFIX.length)
|
||||
}
|
||||
|
||||
const buildAssemblePlaceholder = (toolNodeId?: string, paramKey?: string) => {
|
||||
if (!toolNodeId || !paramKey)
|
||||
@ -309,8 +316,9 @@ const MixedVariableTextInput = ({
|
||||
|
||||
const matches = text.matchAll(AGENT_CONTEXT_VAR_PATTERN)
|
||||
for (const match of matches) {
|
||||
const variablePath = match[1]
|
||||
const nodeId = variablePath.split('.')[0]
|
||||
const nodeId = getAgentNodeIdFromContextVar(match[0])
|
||||
if (!nodeId)
|
||||
continue
|
||||
const node = nodesByIdMap[nodeId]
|
||||
if (node && contextNodeIds.has(nodeId)) {
|
||||
return {
|
||||
@ -461,8 +469,8 @@ const MixedVariableTextInput = ({
|
||||
if (!agentNodeId || !onChange)
|
||||
return
|
||||
|
||||
const valueWithoutAgentVars = value.replace(AGENT_CONTEXT_VAR_PATTERN, (match, variablePath) => {
|
||||
const nodeId = variablePath.split('.')[0]
|
||||
const valueWithoutAgentVars = value.replace(AGENT_CONTEXT_VAR_PATTERN, (match) => {
|
||||
const nodeId = getAgentNodeIdFromContextVar(match)
|
||||
return nodeId === agentNodeId ? '' : match
|
||||
})
|
||||
|
||||
@ -552,6 +560,7 @@ const MixedVariableTextInput = ({
|
||||
<AgentHeaderBar
|
||||
agentName={t('nodes.tool.assembleVariables', { ns: 'workflow' })}
|
||||
onRemove={handleAssembleRemove}
|
||||
onViewInternals={handleOpenSubGraphModal}
|
||||
hasWarning={hasAssembleWarning}
|
||||
showAtPrefix={false}
|
||||
/>
|
||||
@ -599,10 +608,21 @@ const MixedVariableTextInput = ({
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{toolNodeId && detectedAgentFromValue && sourceVariable && (
|
||||
{toolNodeId && paramKey && isAssembleValue && (
|
||||
<SubGraphModal
|
||||
isOpen={isSubGraphModalOpen}
|
||||
onClose={handleCloseSubGraphModal}
|
||||
variant="assemble"
|
||||
toolNodeId={toolNodeId}
|
||||
paramKey={paramKey}
|
||||
title={t('nodes.tool.assembleVariables', { ns: 'workflow' })}
|
||||
/>
|
||||
)}
|
||||
{toolNodeId && paramKey && !isAssembleValue && detectedAgentFromValue && sourceVariable && (
|
||||
<SubGraphModal
|
||||
isOpen={isSubGraphModalOpen}
|
||||
onClose={handleCloseSubGraphModal}
|
||||
variant="agent"
|
||||
toolNodeId={toolNodeId}
|
||||
paramKey={paramKey}
|
||||
sourceVariable={sourceVariable}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
import type { FC } from 'react'
|
||||
import type { SubGraphModalProps } from './types'
|
||||
import type { MentionConfig } from '@/app/components/workflow/nodes/_base/types'
|
||||
import type { CodeNodeType } from '@/app/components/workflow/nodes/code/types'
|
||||
import type { LLMNodeType } from '@/app/components/workflow/nodes/llm/types'
|
||||
import type { ToolNodeType } from '@/app/components/workflow/nodes/tool/types'
|
||||
import type { Node, PromptItem, PromptTemplateItem } from '@/app/components/workflow/types'
|
||||
@ -11,24 +12,29 @@ import { noop } from 'es-toolkit/function'
|
||||
import { Fragment, memo, useCallback, useEffect, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStore as useReactFlowStore, useStoreApi } from 'reactflow'
|
||||
import { AssembleVariablesAlt } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { Agent } from '@/app/components/base/icons/src/vender/workflow'
|
||||
import { useIsChatMode, useNodesSyncDraft, useWorkflow, useWorkflowVariables } from '@/app/components/workflow/hooks'
|
||||
import { useHooksStore } from '@/app/components/workflow/hooks-store'
|
||||
import { VarKindType } from '@/app/components/workflow/nodes/_base/types'
|
||||
import { useStore as useWorkflowStore } from '@/app/components/workflow/store'
|
||||
import { BlockEnum, EditionType, isPromptMessageContext, PromptRole } from '@/app/components/workflow/types'
|
||||
import { BlockEnum, EditionType, isPromptMessageContext, PromptRole, VarType } from '@/app/components/workflow/types'
|
||||
import SubGraphCanvas from './sub-graph-canvas'
|
||||
|
||||
const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
toolNodeId,
|
||||
paramKey,
|
||||
sourceVariable,
|
||||
agentName,
|
||||
agentNodeId,
|
||||
}) => {
|
||||
const SubGraphModal: FC<SubGraphModalProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
const { isOpen, onClose, variant, toolNodeId, paramKey } = props
|
||||
const isAgentVariant = variant === 'agent'
|
||||
const resolvedAgentNodeId = isAgentVariant ? props.agentNodeId : ''
|
||||
const agentName = isAgentVariant ? props.agentName : ''
|
||||
const assembleTitle = !isAgentVariant ? props.title : ''
|
||||
const modalTitle = useMemo(() => {
|
||||
const baseTitle = isAgentVariant
|
||||
? agentName
|
||||
: (assembleTitle || t('nodes.tool.assembleVariables', { ns: 'workflow' }))
|
||||
const prefix = isAgentVariant && baseTitle ? '@' : ''
|
||||
return `${prefix}${baseTitle} ${t('subGraphModal.title', { ns: 'workflow' })}`.trim()
|
||||
}, [agentName, assembleTitle, isAgentVariant, t])
|
||||
const reactflowStore = useStoreApi()
|
||||
const workflowNodes = useWorkflowStore(state => state.nodes)
|
||||
const workflowEdges = useReactFlowStore(state => state.edges)
|
||||
@ -41,13 +47,16 @@ const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
|
||||
const extractorNodeId = `${toolNodeId}_ext_${paramKey}`
|
||||
const extractorNode = useMemo(() => {
|
||||
return workflowNodes.find(node => node.id === extractorNodeId) as Node<LLMNodeType> | undefined
|
||||
return workflowNodes.find(node => node.id === extractorNodeId) as Node<LLMNodeType | CodeNodeType> | undefined
|
||||
}, [extractorNodeId, workflowNodes])
|
||||
const toolNode = useMemo(() => {
|
||||
return workflowNodes.find(node => node.id === toolNodeId)
|
||||
}, [toolNodeId, workflowNodes])
|
||||
const toolParam = (toolNode?.data as ToolNodeType | undefined)?.tool_parameters?.[paramKey]
|
||||
const toolParamValue = toolParam?.value as string | undefined
|
||||
const assemblePlaceholder = useMemo(() => {
|
||||
return `{{#${toolNodeId}_ext_${paramKey}.result#}}`
|
||||
}, [paramKey, toolNodeId])
|
||||
|
||||
const parentBeforeNodes = useMemo(() => {
|
||||
if (!isOpen)
|
||||
@ -56,25 +65,28 @@ const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
}, [getBeforeNodesInSameBranch, isOpen, toolNodeId, workflowEdges, workflowNodes])
|
||||
|
||||
const parentContextNodes = useMemo(() => {
|
||||
if (!parentBeforeNodes.length)
|
||||
if (!parentBeforeNodes.length || !isAgentVariant)
|
||||
return []
|
||||
return parentBeforeNodes.filter(node => node.data.type === BlockEnum.Agent || node.data.type === BlockEnum.LLM)
|
||||
}, [parentBeforeNodes])
|
||||
}, [isAgentVariant, parentBeforeNodes])
|
||||
|
||||
const parentContextNodeIds = useMemo(() => {
|
||||
return parentContextNodes.map(node => node.id)
|
||||
}, [parentContextNodes])
|
||||
const parentAvailableNodes = useMemo(() => {
|
||||
if (!isOpen)
|
||||
return []
|
||||
return isAgentVariant ? parentContextNodes : parentBeforeNodes
|
||||
}, [isAgentVariant, isOpen, parentBeforeNodes, parentContextNodes])
|
||||
|
||||
const parentAvailableVars = useMemo(() => {
|
||||
if (!parentContextNodeIds.length)
|
||||
if (!parentAvailableNodes.length)
|
||||
return []
|
||||
const vars = getNodeAvailableVars({
|
||||
beforeNodes: parentContextNodes,
|
||||
beforeNodes: parentAvailableNodes,
|
||||
isChatMode,
|
||||
filterVar: () => true,
|
||||
})
|
||||
return vars.filter(nodeVar => parentContextNodeIds.includes(nodeVar.nodeId))
|
||||
}, [getNodeAvailableVars, isChatMode, parentContextNodeIds, parentContextNodes])
|
||||
const availableNodeIds = new Set(parentAvailableNodes.map(node => node.id))
|
||||
return vars.filter(nodeVar => availableNodeIds.has(nodeVar.nodeId))
|
||||
}, [getNodeAvailableVars, isChatMode, parentAvailableNodes])
|
||||
|
||||
const mentionConfig = useMemo<MentionConfig>(() => {
|
||||
const current = toolParam?.mention_config
|
||||
@ -91,6 +103,9 @@ const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
}, [extractorNodeId, paramKey, toolParam?.mention_config])
|
||||
|
||||
const handleMentionConfigChange = useCallback((config: MentionConfig) => {
|
||||
if (!isAgentVariant)
|
||||
return
|
||||
|
||||
const { getNodes, setNodes } = reactflowStore.getState()
|
||||
const nextNodes = getNodes().map((node) => {
|
||||
if (node.id !== toolNodeId)
|
||||
@ -118,10 +133,10 @@ const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
})
|
||||
setNodes(nextNodes)
|
||||
handleSyncWorkflowDraft()
|
||||
}, [handleSyncWorkflowDraft, paramKey, reactflowStore, toolNodeId])
|
||||
}, [handleSyncWorkflowDraft, isAgentVariant, paramKey, reactflowStore, toolNodeId])
|
||||
|
||||
useEffect(() => {
|
||||
if (!toolParam || (toolParam.type && toolParam.type !== VarKindType.mention))
|
||||
if (!isAgentVariant || !toolParam || (toolParam.type && toolParam.type !== VarKindType.mention))
|
||||
return
|
||||
|
||||
const current = toolParam.mention_config
|
||||
@ -132,7 +147,7 @@ const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
|
||||
if (needsExtractor || needsNullStrategy || needsOutputSelector || needsDefaultValue)
|
||||
handleMentionConfigChange(mentionConfig)
|
||||
}, [handleMentionConfigChange, mentionConfig, toolParam])
|
||||
}, [handleMentionConfigChange, isAgentVariant, mentionConfig, toolParam])
|
||||
|
||||
const getUserPromptText = useCallback((promptTemplate?: PromptTemplateItem[] | PromptItem) => {
|
||||
if (!promptTemplate)
|
||||
@ -156,23 +171,46 @@ const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
|
||||
// TODO: handle external workflow updates while sub-graph modal is open.
|
||||
const handleSave = useCallback((subGraphNodes: Node[]) => {
|
||||
const extractorNodeData = subGraphNodes.find(node => node.id === extractorNodeId) as Node<LLMNodeType> | undefined
|
||||
const extractorNodeData = subGraphNodes.find(node => node.id === extractorNodeId) as Node<LLMNodeType | CodeNodeType> | undefined
|
||||
if (!extractorNodeData)
|
||||
return
|
||||
|
||||
const userPromptText = getUserPromptText(extractorNodeData.data?.prompt_template)
|
||||
const placeholder = `{{@${agentNodeId}.context@}}`
|
||||
const nextValue = `${placeholder}${userPromptText}`
|
||||
const ensureAssembleOutputs = (payload: CodeNodeType) => {
|
||||
const outputs = payload.outputs || {}
|
||||
if (outputs.result)
|
||||
return payload
|
||||
return {
|
||||
...payload,
|
||||
outputs: {
|
||||
...outputs,
|
||||
result: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const userPromptText = isAgentVariant
|
||||
? getUserPromptText((extractorNodeData.data as LLMNodeType).prompt_template)
|
||||
: ''
|
||||
const placeholder = isAgentVariant && resolvedAgentNodeId ? `{{@${resolvedAgentNodeId}.context@}}` : ''
|
||||
const nextValue = isAgentVariant
|
||||
? `${placeholder}${userPromptText}`
|
||||
: assemblePlaceholder
|
||||
|
||||
const { getNodes, setNodes } = reactflowStore.getState()
|
||||
const nextNodes = getNodes().map((node) => {
|
||||
if (node.id === extractorNodeId) {
|
||||
const nextData = isAgentVariant
|
||||
? extractorNodeData.data
|
||||
: ensureAssembleOutputs(extractorNodeData.data as CodeNodeType)
|
||||
return {
|
||||
...node,
|
||||
hidden: true,
|
||||
data: {
|
||||
...node.data,
|
||||
...extractorNodeData.data,
|
||||
...nextData,
|
||||
parent_node_id: toolNodeId,
|
||||
},
|
||||
}
|
||||
@ -200,7 +238,7 @@ const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
})
|
||||
setNodes(nextNodes)
|
||||
setControlPromptEditorRerenderKey(Date.now())
|
||||
}, [agentNodeId, extractorNodeId, getUserPromptText, paramKey, reactflowStore, setControlPromptEditorRerenderKey, toolNodeId])
|
||||
}, [assemblePlaceholder, extractorNodeId, getUserPromptText, isAgentVariant, paramKey, reactflowStore, resolvedAgentNodeId, setControlPromptEditorRerenderKey, toolNodeId])
|
||||
|
||||
return (
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
@ -215,13 +253,12 @@ const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
<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" />
|
||||
{isAgentVariant
|
||||
? <Agent className="h-4 w-4 text-text-primary-on-surface" />
|
||||
: <AssembleVariablesAlt 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' })}
|
||||
{modalTitle}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
@ -234,22 +271,41 @@ const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
</div>
|
||||
|
||||
<div className="bg-workflow-canvas-wrapper relative flex-1 overflow-hidden">
|
||||
<SubGraphCanvas
|
||||
toolNodeId={toolNodeId}
|
||||
paramKey={paramKey}
|
||||
sourceVariable={sourceVariable}
|
||||
agentNodeId={agentNodeId}
|
||||
agentName={agentName}
|
||||
configsMap={configsMap}
|
||||
mentionConfig={mentionConfig}
|
||||
onMentionConfigChange={handleMentionConfigChange}
|
||||
extractorNode={extractorNode}
|
||||
toolParamValue={toolParamValue}
|
||||
parentAvailableNodes={parentContextNodes}
|
||||
parentAvailableVars={parentAvailableVars}
|
||||
onSave={handleSave}
|
||||
onSyncWorkflowDraft={doSyncWorkflowDraft}
|
||||
/>
|
||||
{variant === 'agent'
|
||||
? (
|
||||
<SubGraphCanvas
|
||||
variant="agent"
|
||||
toolNodeId={toolNodeId}
|
||||
paramKey={paramKey}
|
||||
sourceVariable={props.sourceVariable}
|
||||
agentNodeId={props.agentNodeId}
|
||||
agentName={props.agentName}
|
||||
configsMap={configsMap}
|
||||
mentionConfig={mentionConfig}
|
||||
onMentionConfigChange={handleMentionConfigChange}
|
||||
extractorNode={extractorNode as Node<LLMNodeType> | undefined}
|
||||
toolParamValue={toolParamValue}
|
||||
parentAvailableNodes={parentAvailableNodes}
|
||||
parentAvailableVars={parentAvailableVars}
|
||||
onSave={handleSave}
|
||||
onSyncWorkflowDraft={doSyncWorkflowDraft}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<SubGraphCanvas
|
||||
variant="assemble"
|
||||
toolNodeId={toolNodeId}
|
||||
paramKey={paramKey}
|
||||
title={props.title}
|
||||
configsMap={configsMap}
|
||||
extractorNode={extractorNode as Node<CodeNodeType> | undefined}
|
||||
toolParamValue={toolParamValue}
|
||||
parentAvailableNodes={parentAvailableNodes}
|
||||
parentAvailableVars={parentAvailableVars}
|
||||
onSave={handleSave}
|
||||
onSyncWorkflowDraft={doSyncWorkflowDraft}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</DialogPanel>
|
||||
</TransitionChild>
|
||||
|
||||
@ -4,40 +4,10 @@ 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,
|
||||
configsMap,
|
||||
mentionConfig,
|
||||
onMentionConfigChange,
|
||||
extractorNode,
|
||||
toolParamValue,
|
||||
parentAvailableNodes,
|
||||
parentAvailableVars,
|
||||
onSave,
|
||||
onSyncWorkflowDraft,
|
||||
}) => {
|
||||
const SubGraphCanvas: FC<SubGraphCanvasProps> = (props) => {
|
||||
return (
|
||||
<div className="h-full w-full">
|
||||
<SubGraph
|
||||
toolNodeId={toolNodeId}
|
||||
paramKey={paramKey}
|
||||
sourceVariable={sourceVariable}
|
||||
agentNodeId={agentNodeId}
|
||||
agentName={agentName}
|
||||
configsMap={configsMap}
|
||||
mentionConfig={mentionConfig}
|
||||
onMentionConfigChange={onMentionConfigChange}
|
||||
extractorNode={extractorNode}
|
||||
toolParamValue={toolParamValue}
|
||||
parentAvailableNodes={parentAvailableNodes}
|
||||
parentAvailableVars={parentAvailableVars}
|
||||
onSave={onSave}
|
||||
onSyncWorkflowDraft={onSyncWorkflowDraft}
|
||||
/>
|
||||
<SubGraph {...props} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,34 +1,25 @@
|
||||
import type { SyncWorkflowDraft } from '@/app/components/sub-graph/types'
|
||||
import type { Shape as HooksStoreShape } from '@/app/components/workflow/hooks-store'
|
||||
import type { MentionConfig } from '@/app/components/workflow/nodes/_base/types'
|
||||
import type { LLMNodeType } from '@/app/components/workflow/nodes/llm/types'
|
||||
import type { NodeOutPutVar, Edge as WorkflowEdge, Node as WorkflowNode } from '@/app/components/workflow/types'
|
||||
import type { SubGraphProps } from '@/app/components/sub-graph/types'
|
||||
import type { ValueSelector } from '@/app/components/workflow/types'
|
||||
|
||||
type WorkflowValueSelector = string[]
|
||||
|
||||
export type SubGraphModalProps = {
|
||||
type BaseSubGraphModalProps = {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
toolNodeId: string
|
||||
paramKey: string
|
||||
sourceVariable: WorkflowValueSelector
|
||||
}
|
||||
|
||||
type AgentSubGraphModalProps = BaseSubGraphModalProps & {
|
||||
variant: 'agent'
|
||||
sourceVariable: ValueSelector
|
||||
agentName: string
|
||||
agentNodeId: string
|
||||
}
|
||||
|
||||
export type SubGraphCanvasProps = {
|
||||
toolNodeId: string
|
||||
paramKey: string
|
||||
sourceVariable: WorkflowValueSelector
|
||||
agentNodeId: string
|
||||
agentName: string
|
||||
configsMap?: HooksStoreShape['configsMap']
|
||||
mentionConfig: MentionConfig
|
||||
onMentionConfigChange: (config: MentionConfig) => void
|
||||
extractorNode?: WorkflowNode<LLMNodeType>
|
||||
toolParamValue?: string
|
||||
parentAvailableNodes?: WorkflowNode[]
|
||||
parentAvailableVars?: NodeOutPutVar[]
|
||||
onSave?: (nodes: WorkflowNode[], edges: WorkflowEdge[]) => void
|
||||
onSyncWorkflowDraft?: SyncWorkflowDraft
|
||||
type AssembleSubGraphModalProps = BaseSubGraphModalProps & {
|
||||
variant: 'assemble'
|
||||
title: string
|
||||
}
|
||||
|
||||
export type SubGraphModalProps = AgentSubGraphModalProps | AssembleSubGraphModalProps
|
||||
|
||||
export type SubGraphCanvasProps = SubGraphProps
|
||||
|
||||
@ -20,7 +20,14 @@ import { useStrategyProviders } from '@/service/use-strategy'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { VarType } from './types'
|
||||
|
||||
const AGENT_CONTEXT_VAR_PATTERN = /\{\{@([^.@#]+)\.context@\}\}/g
|
||||
const AGENT_CONTEXT_VAR_PATTERN = /\{\{@[^.@#]+\.context@\}\}/g
|
||||
const AGENT_CONTEXT_VAR_PREFIX = '{{@'
|
||||
const AGENT_CONTEXT_VAR_SUFFIX = '.context@}}'
|
||||
const getAgentNodeIdFromContextVar = (placeholder: string) => {
|
||||
if (!placeholder.startsWith(AGENT_CONTEXT_VAR_PREFIX) || !placeholder.endsWith(AGENT_CONTEXT_VAR_SUFFIX))
|
||||
return ''
|
||||
return placeholder.slice(AGENT_CONTEXT_VAR_PREFIX.length, -AGENT_CONTEXT_VAR_SUFFIX.length)
|
||||
}
|
||||
type AgentCheckValidContext = {
|
||||
provider?: StrategyPluginDetail
|
||||
strategy?: StrategyDetail
|
||||
@ -80,7 +87,7 @@ const Node: FC<NodeProps<ToolNodeType>> = ({
|
||||
return
|
||||
const matches = value.matchAll(AGENT_CONTEXT_VAR_PATTERN)
|
||||
for (const match of matches) {
|
||||
const agentNodeId = match[1]
|
||||
const agentNodeId = getAgentNodeIdFromContextVar(match[0])
|
||||
if (!agentNodeId)
|
||||
continue
|
||||
const entryKey = `${paramKey}:${agentNodeId}`
|
||||
|
||||
Reference in New Issue
Block a user