mirror of
https://github.com/langgenius/dify.git
synced 2026-05-10 05:56:31 +08:00
feat: add sub-graph config panel with variable selection and null
handling
This commit is contained in:
parent
b7025ad9d6
commit
b9052bc244
@ -1,85 +1,178 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { WhenOutputNoneOption } from '../types'
|
||||
import { memo, useCallback, useState } from 'react'
|
||||
import type { Item } from '@/app/components/base/select'
|
||||
import type { MentionConfig } from '@/app/components/workflow/nodes/_base/types'
|
||||
import type { Node, NodeOutPutVar, ValueSelector } from '@/app/components/workflow/types'
|
||||
import { RiCheckLine } from '@remixicon/react'
|
||||
import { memo, useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
|
||||
import Field from '@/app/components/workflow/nodes/_base/components/field'
|
||||
import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker'
|
||||
import Tab, { TabType } from '@/app/components/workflow/nodes/_base/components/workflow-panel/tab'
|
||||
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type ConfigPanelProps = {
|
||||
toolNodeId: string
|
||||
paramKey: string
|
||||
activeTab: 'settings' | 'lastRun'
|
||||
onTabChange: (tab: 'settings' | 'lastRun') => void
|
||||
agentName: string
|
||||
extractorNodeId: string
|
||||
mentionConfig: MentionConfig
|
||||
availableNodes: Node[]
|
||||
availableVars: NodeOutPutVar[]
|
||||
onMentionConfigChange: (config: MentionConfig) => void
|
||||
}
|
||||
|
||||
const outputVariables = [
|
||||
{ name: 'text', type: 'string' },
|
||||
{ name: 'structured_output', type: 'object' },
|
||||
]
|
||||
|
||||
const ConfigPanel: FC<ConfigPanelProps> = ({
|
||||
toolNodeId: _toolNodeId,
|
||||
paramKey: _paramKey,
|
||||
activeTab,
|
||||
agentName,
|
||||
extractorNodeId,
|
||||
mentionConfig,
|
||||
availableNodes,
|
||||
availableVars,
|
||||
onMentionConfigChange,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [whenOutputNone, setWhenOutputNone] = useState<WhenOutputNoneOption>('default')
|
||||
const [tabType, setTabType] = useState<TabType>(TabType.settings)
|
||||
|
||||
const handleWhenOutputNoneChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
setWhenOutputNone(e.target.value as WhenOutputNoneOption)
|
||||
}, [])
|
||||
const resolvedExtractorId = mentionConfig.extractor_node_id || extractorNodeId
|
||||
|
||||
if (activeTab === 'lastRun') {
|
||||
return (
|
||||
<div className="flex h-full items-center justify-center p-4">
|
||||
<div className="text-center">
|
||||
const selectedOutput = useMemo<ValueSelector>(() => {
|
||||
if (!resolvedExtractorId || !mentionConfig.output_selector?.length)
|
||||
return []
|
||||
|
||||
return [resolvedExtractorId, ...(mentionConfig.output_selector || [])]
|
||||
}, [mentionConfig.output_selector, resolvedExtractorId])
|
||||
|
||||
const handleOutputVarChange = useCallback((value: ValueSelector | string) => {
|
||||
const selector = Array.isArray(value) ? value : []
|
||||
const nextExtractorId = selector[0] || resolvedExtractorId
|
||||
const nextOutputSelector = selector.length > 1 ? selector.slice(1) : []
|
||||
|
||||
onMentionConfigChange({
|
||||
...mentionConfig,
|
||||
extractor_node_id: nextExtractorId,
|
||||
output_selector: nextOutputSelector,
|
||||
})
|
||||
}, [mentionConfig, onMentionConfigChange, resolvedExtractorId])
|
||||
|
||||
const whenOutputNoneOptions = useMemo(() => ([
|
||||
{
|
||||
value: 'raise_error',
|
||||
name: t('subGraphModal.whenOutputNone.error', { ns: 'workflow' }),
|
||||
description: t('subGraphModal.whenOutputNone.errorDesc', { ns: 'workflow' }),
|
||||
},
|
||||
{
|
||||
value: 'use_default',
|
||||
name: t('subGraphModal.whenOutputNone.default', { ns: 'workflow' }),
|
||||
description: t('subGraphModal.whenOutputNone.defaultDesc', { ns: 'workflow' }),
|
||||
},
|
||||
]), [t])
|
||||
|
||||
const handleNullStrategyChange = useCallback((item: Item) => {
|
||||
if (typeof item.value !== 'string')
|
||||
return
|
||||
onMentionConfigChange({
|
||||
...mentionConfig,
|
||||
null_strategy: item.value as MentionConfig['null_strategy'],
|
||||
})
|
||||
}, [mentionConfig, onMentionConfigChange])
|
||||
|
||||
const handleDefaultValueChange = useCallback((value: string) => {
|
||||
const trimmed = value.trim()
|
||||
let nextValue: unknown = value
|
||||
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
||||
try {
|
||||
nextValue = JSON.parse(trimmed)
|
||||
}
|
||||
catch {
|
||||
nextValue = value
|
||||
}
|
||||
}
|
||||
|
||||
onMentionConfigChange({
|
||||
...mentionConfig,
|
||||
default_value: nextValue,
|
||||
})
|
||||
}, [mentionConfig, onMentionConfigChange])
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
<div className="px-4 pb-2 pt-4">
|
||||
<div className="system-lg-semibold text-text-primary">
|
||||
{t('subGraphModal.internalStructure', { ns: 'workflow' })}
|
||||
</div>
|
||||
<div className="system-sm-regular text-text-tertiary">
|
||||
{t('subGraphModal.internalStructureDesc', { ns: 'workflow', name: agentName })}
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-4 pb-2">
|
||||
<Tab value={tabType} onChange={setTabType} />
|
||||
</div>
|
||||
{tabType === TabType.lastRun && (
|
||||
<div className="flex flex-1 items-center justify-center p-4">
|
||||
<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>
|
||||
))}
|
||||
)}
|
||||
{tabType === TabType.settings && (
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<div className="space-y-4 px-4 py-4">
|
||||
<Field title={t('subGraphModal.outputVariables', { ns: 'workflow' })}>
|
||||
<VarReferencePicker
|
||||
nodeId={extractorNodeId}
|
||||
readonly={false}
|
||||
isShowNodeName
|
||||
value={selectedOutput}
|
||||
onChange={handleOutputVarChange}
|
||||
availableNodes={availableNodes}
|
||||
availableVars={availableVars}
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
<div className="space-y-4 px-4 py-4">
|
||||
<Field title={t('subGraphModal.whenOutputIsNone', { ns: 'workflow' })}>
|
||||
<SimpleSelect
|
||||
items={whenOutputNoneOptions}
|
||||
defaultValue={mentionConfig.null_strategy}
|
||||
allowSearch={false}
|
||||
notClearable
|
||||
onSelect={handleNullStrategyChange}
|
||||
renderOption={({ item, selected }) => (
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center">
|
||||
{selected && (
|
||||
<RiCheckLine className="h-4 w-4 text-[14px] text-text-accent" />
|
||||
)}
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<div className="system-sm-medium text-text-secondary">{item.name}</div>
|
||||
<div className="system-xs-regular mt-0.5 text-text-tertiary">{item.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</Field>
|
||||
{mentionConfig.null_strategy === 'use_default' && (
|
||||
<div>
|
||||
<div className="system-xs-regular text-text-tertiary">
|
||||
{t('subGraphModal.defaultValueHint', { ns: 'workflow' })}
|
||||
</div>
|
||||
<div className={cn('mt-2 overflow-hidden rounded-lg border border-components-input-border-active bg-components-input-bg-normal p-1')}>
|
||||
<CodeEditor
|
||||
noWrapper
|
||||
language={CodeLanguage.json}
|
||||
value={mentionConfig.default_value ?? ''}
|
||||
onChange={handleDefaultValueChange}
|
||||
isJSONStringifyBeauty
|
||||
className="min-h-[160px]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</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="default">
|
||||
{t('subGraphModal.whenOutputNone.default', { ns: 'workflow' })}
|
||||
</option>
|
||||
<option value="error">
|
||||
{t('subGraphModal.whenOutputNone.error', { ns: 'workflow' })}
|
||||
</option>
|
||||
</select>
|
||||
</Field>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,36 +1,72 @@
|
||||
import type { FC } from 'react'
|
||||
import type { SubGraphConfig } from '../types'
|
||||
import type { MentionConfig } from '@/app/components/workflow/nodes/_base/types'
|
||||
import type { NodeOutPutVar } from '@/app/components/workflow/types'
|
||||
import { memo, useMemo } from 'react'
|
||||
import { useStore as useReactFlowStore } from 'reactflow'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import { useIsChatMode, useWorkflowVariables } from '@/app/components/workflow/hooks'
|
||||
import { Panel as NodePanel } from '@/app/components/workflow/nodes'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import ConfigPanel from './config-panel'
|
||||
|
||||
type SubGraphChildrenProps = {
|
||||
toolNodeId: string
|
||||
paramKey: string
|
||||
onConfigChange: (config: Partial<SubGraphConfig>) => void
|
||||
agentName: string
|
||||
extractorNodeId: string
|
||||
mentionConfig: MentionConfig
|
||||
onMentionConfigChange: (config: MentionConfig) => void
|
||||
}
|
||||
|
||||
const SubGraphChildren: FC<SubGraphChildrenProps> = ({
|
||||
toolNodeId: _toolNodeId,
|
||||
paramKey: _paramKey,
|
||||
onConfigChange: _onConfigChange,
|
||||
agentName,
|
||||
extractorNodeId,
|
||||
mentionConfig,
|
||||
onMentionConfigChange,
|
||||
}) => {
|
||||
const selectedNode = useReactFlowStore(useShallow((s) => {
|
||||
const { getNodeAvailableVars } = useWorkflowVariables()
|
||||
const isChatMode = useIsChatMode()
|
||||
const nodePanelWidth = useStore(s => s.nodePanelWidth)
|
||||
|
||||
const { selectedNode, nodes } = useReactFlowStore(useShallow((s) => {
|
||||
const nodes = s.getNodes()
|
||||
const currentNode = nodes.find(node => node.data.selected)
|
||||
|
||||
if (currentNode?.data.type === BlockEnum.LLM) {
|
||||
return {
|
||||
id: currentNode.id,
|
||||
type: currentNode.type,
|
||||
data: currentNode.data,
|
||||
selectedNode: {
|
||||
id: currentNode.id,
|
||||
type: currentNode.type,
|
||||
data: currentNode.data,
|
||||
},
|
||||
nodes,
|
||||
}
|
||||
}
|
||||
return null
|
||||
return {
|
||||
selectedNode: null,
|
||||
nodes,
|
||||
}
|
||||
}))
|
||||
|
||||
const extractorNode = useMemo(() => {
|
||||
return nodes.find(node => node.data.type === BlockEnum.LLM)
|
||||
}, [nodes])
|
||||
|
||||
const availableNodes = useMemo(() => {
|
||||
return extractorNode ? [extractorNode] : []
|
||||
}, [extractorNode])
|
||||
|
||||
const availableVars = useMemo<NodeOutPutVar[]>(() => {
|
||||
if (!extractorNode)
|
||||
return []
|
||||
|
||||
const vars = getNodeAvailableVars({
|
||||
beforeNodes: [extractorNode],
|
||||
isChatMode,
|
||||
filterVar: () => true,
|
||||
})
|
||||
return vars.filter(item => item.nodeId === extractorNode.id)
|
||||
}, [extractorNode, getNodeAvailableVars, isChatMode])
|
||||
|
||||
const nodePanel = useMemo(() => {
|
||||
if (!selectedNode)
|
||||
return null
|
||||
@ -46,11 +82,25 @@ const SubGraphChildren: FC<SubGraphChildrenProps> = ({
|
||||
|
||||
return (
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 z-10 flex">
|
||||
{nodePanel && (
|
||||
<div className="pointer-events-auto">
|
||||
{nodePanel}
|
||||
</div>
|
||||
)}
|
||||
<div className="pointer-events-auto">
|
||||
{nodePanel || (
|
||||
<div className="relative mr-1 h-full">
|
||||
<div
|
||||
className="flex h-full flex-col rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg"
|
||||
style={{ width: `${nodePanelWidth}px` }}
|
||||
>
|
||||
<ConfigPanel
|
||||
agentName={agentName}
|
||||
extractorNodeId={extractorNodeId}
|
||||
mentionConfig={mentionConfig}
|
||||
availableNodes={availableNodes}
|
||||
availableVars={availableVars}
|
||||
onMentionConfigChange={onMentionConfigChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,18 +1,21 @@
|
||||
import type { FC } from 'react'
|
||||
import type { Viewport } from 'reactflow'
|
||||
import type { MentionConfig } from '@/app/components/workflow/nodes/_base/types'
|
||||
import type { Edge, Node } from '@/app/components/workflow/types'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { useStoreApi } from 'reactflow'
|
||||
import { WorkflowWithInnerContext } from '@/app/components/workflow'
|
||||
import { useAvailableNodesMetaData, useSubGraphPersistence } from '../hooks'
|
||||
import { useAvailableNodesMetaData } from '../hooks'
|
||||
import SubGraphChildren from './sub-graph-children'
|
||||
|
||||
type SubGraphMainProps = {
|
||||
nodes: Node[]
|
||||
edges: Edge[]
|
||||
viewport: Viewport
|
||||
toolNodeId: string
|
||||
paramKey: string
|
||||
agentName: string
|
||||
extractorNodeId: string
|
||||
mentionConfig: MentionConfig
|
||||
onMentionConfigChange: (config: MentionConfig) => void
|
||||
onSave?: (nodes: Node[], edges: Edge[]) => void
|
||||
}
|
||||
|
||||
@ -20,13 +23,14 @@ const SubGraphMain: FC<SubGraphMainProps> = ({
|
||||
nodes,
|
||||
edges,
|
||||
viewport,
|
||||
toolNodeId,
|
||||
paramKey,
|
||||
agentName,
|
||||
extractorNodeId,
|
||||
mentionConfig,
|
||||
onMentionConfigChange,
|
||||
onSave,
|
||||
}) => {
|
||||
const reactFlowStore = useStoreApi()
|
||||
const availableNodesMetaData = useAvailableNodesMetaData()
|
||||
const { updateSubGraphConfig } = useSubGraphPersistence({ toolNodeId, paramKey })
|
||||
|
||||
const handleSyncSubGraphDraft = useCallback(() => {
|
||||
const { getNodes, edges } = reactFlowStore.getState()
|
||||
@ -53,9 +57,10 @@ const SubGraphMain: FC<SubGraphMainProps> = ({
|
||||
interactionMode="subgraph"
|
||||
>
|
||||
<SubGraphChildren
|
||||
toolNodeId={toolNodeId}
|
||||
paramKey={paramKey}
|
||||
onConfigChange={updateSubGraphConfig}
|
||||
agentName={agentName}
|
||||
extractorNodeId={extractorNodeId}
|
||||
mentionConfig={mentionConfig}
|
||||
onMentionConfigChange={onMentionConfigChange}
|
||||
/>
|
||||
</WorkflowWithInnerContext>
|
||||
)
|
||||
|
||||
@ -5,16 +5,27 @@ import type { InjectWorkflowStoreSliceFn } from '@/app/components/workflow/store
|
||||
import type { PromptItem } from '@/app/components/workflow/types'
|
||||
import { memo, useMemo } from 'react'
|
||||
import WorkflowWithDefaultContext from '@/app/components/workflow'
|
||||
import { NODE_WIDTH_X_OFFSET, START_INITIAL_POSITION } from '@/app/components/workflow/constants'
|
||||
import { WorkflowContextProvider } from '@/app/components/workflow/context'
|
||||
import { BlockEnum, EditionType, PromptRole } from '@/app/components/workflow/types'
|
||||
import SubGraphMain from './components/sub-graph-main'
|
||||
import { useSubGraphNodes } from './hooks'
|
||||
import { createSubGraphSlice } from './store'
|
||||
|
||||
const SUB_GRAPH_EDGE_GAP = 180
|
||||
const SUB_GRAPH_ENTRY_POSITION = {
|
||||
x: START_INITIAL_POSITION.x,
|
||||
y: 150,
|
||||
}
|
||||
const SUB_GRAPH_LLM_POSITION = {
|
||||
x: SUB_GRAPH_ENTRY_POSITION.x + NODE_WIDTH_X_OFFSET - SUB_GRAPH_EDGE_GAP,
|
||||
y: SUB_GRAPH_ENTRY_POSITION.y,
|
||||
}
|
||||
|
||||
const defaultViewport: Viewport = {
|
||||
x: 50,
|
||||
x: SUB_GRAPH_EDGE_GAP,
|
||||
y: 50,
|
||||
zoom: 1,
|
||||
zoom: 1.3,
|
||||
}
|
||||
|
||||
const SubGraph: FC<SubGraphProps> = (props) => {
|
||||
@ -23,6 +34,8 @@ const SubGraph: FC<SubGraphProps> = (props) => {
|
||||
paramKey,
|
||||
agentName,
|
||||
agentNodeId,
|
||||
mentionConfig,
|
||||
onMentionConfigChange,
|
||||
extractorNode,
|
||||
toolParamValue,
|
||||
onSave,
|
||||
@ -41,7 +54,7 @@ const SubGraph: FC<SubGraphProps> = (props) => {
|
||||
return {
|
||||
id: 'subgraph-source',
|
||||
type: 'custom',
|
||||
position: { x: 100, y: 150 },
|
||||
position: SUB_GRAPH_ENTRY_POSITION,
|
||||
data: {
|
||||
type: BlockEnum.Start,
|
||||
title: agentName,
|
||||
@ -50,8 +63,10 @@ const SubGraph: FC<SubGraphProps> = (props) => {
|
||||
_connectedTargetHandleIds: [],
|
||||
_subGraphEntry: true,
|
||||
_iconTypeOverride: BlockEnum.Agent,
|
||||
selected: false,
|
||||
variables: [],
|
||||
},
|
||||
selected: false,
|
||||
selectable: false,
|
||||
draggable: false,
|
||||
connectable: false,
|
||||
@ -110,9 +125,11 @@ const SubGraph: FC<SubGraphProps> = (props) => {
|
||||
return {
|
||||
...extractorNode,
|
||||
hidden: false,
|
||||
position: { x: 320, y: 150 },
|
||||
selected: false,
|
||||
position: SUB_GRAPH_LLM_POSITION,
|
||||
data: {
|
||||
...extractorNode.data,
|
||||
selected: false,
|
||||
prompt_template: nextPromptTemplate,
|
||||
},
|
||||
}
|
||||
@ -159,8 +176,10 @@ const SubGraph: FC<SubGraphProps> = (props) => {
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
viewport={defaultViewport}
|
||||
toolNodeId={toolNodeId}
|
||||
paramKey={paramKey}
|
||||
agentName={agentName}
|
||||
extractorNodeId={`${toolNodeId}_ext_${paramKey}`}
|
||||
mentionConfig={mentionConfig}
|
||||
onMentionConfigChange={onMentionConfigChange}
|
||||
onSave={onSave}
|
||||
/>
|
||||
</WorkflowWithDefaultContext>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { StateCreator } from 'zustand'
|
||||
import type { MentionConfig } from '@/app/components/workflow/nodes/_base/types'
|
||||
import type { LLMNodeType } from '@/app/components/workflow/nodes/llm/types'
|
||||
import type { Edge, Node, NodeOutPutVar, ValueSelector, VarType } from '@/app/components/workflow/types'
|
||||
|
||||
@ -26,6 +27,8 @@ export type SubGraphProps = {
|
||||
sourceVariable: ValueSelector
|
||||
agentNodeId: string
|
||||
agentName: string
|
||||
mentionConfig: MentionConfig
|
||||
onMentionConfigChange: (config: MentionConfig) => void
|
||||
extractorNode?: Node<LLMNodeType>
|
||||
toolParamValue?: string
|
||||
onSave?: (nodes: Node[], edges: Edge[]) => void
|
||||
|
||||
@ -1,17 +1,19 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { SubGraphModalProps } from './types'
|
||||
import type { MentionConfig } from '@/app/components/workflow/nodes/_base/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 } from '@/app/components/workflow/types'
|
||||
import { Dialog, DialogPanel, Transition, TransitionChild } from '@headlessui/react'
|
||||
import { RiCloseLine } from '@remixicon/react'
|
||||
import { noop } from 'es-toolkit/function'
|
||||
import { Fragment, memo, useCallback, useMemo } from 'react'
|
||||
import { Fragment, memo, useCallback, useEffect, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStoreApi } from 'reactflow'
|
||||
import { Agent } from '@/app/components/base/icons/src/vender/workflow'
|
||||
import { useNodesSyncDraft } from '@/app/components/workflow/hooks'
|
||||
import { VarKindType } from '@/app/components/workflow/nodes/_base/types'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
import { EditionType, PromptRole } from '@/app/components/workflow/types'
|
||||
import SubGraphCanvas from './sub-graph-canvas'
|
||||
@ -38,7 +40,64 @@ const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
const toolNode = useMemo(() => {
|
||||
return workflowNodes.find(node => node.id === toolNodeId)
|
||||
}, [toolNodeId, workflowNodes])
|
||||
const toolParamValue = (toolNode?.data as ToolNodeType | undefined)?.tool_parameters?.[paramKey]?.value as string | undefined
|
||||
const toolParam = (toolNode?.data as ToolNodeType | undefined)?.tool_parameters?.[paramKey]
|
||||
const toolParamValue = toolParam?.value as string | undefined
|
||||
|
||||
const mentionConfig = useMemo<MentionConfig>(() => {
|
||||
const current = toolParam?.mention_config
|
||||
const rawSelector = Array.isArray(current?.output_selector) ? current!.output_selector : []
|
||||
const outputSelector = rawSelector[0] === extractorNodeId ? rawSelector.slice(1) : rawSelector
|
||||
return {
|
||||
extractor_node_id: current?.extractor_node_id || extractorNodeId,
|
||||
output_selector: outputSelector,
|
||||
null_strategy: current?.null_strategy || 'use_default',
|
||||
default_value: current?.default_value ?? '',
|
||||
}
|
||||
}, [extractorNodeId, toolParam?.mention_config])
|
||||
|
||||
const handleMentionConfigChange = useCallback((config: MentionConfig) => {
|
||||
const { getNodes, setNodes } = reactflowStore.getState()
|
||||
const nextNodes = getNodes().map((node) => {
|
||||
if (node.id !== toolNodeId)
|
||||
return node
|
||||
|
||||
const toolData = node.data as ToolNodeType
|
||||
const currentParam = toolData.tool_parameters?.[paramKey]
|
||||
if (!currentParam)
|
||||
return node
|
||||
|
||||
return {
|
||||
...node,
|
||||
data: {
|
||||
...toolData,
|
||||
tool_parameters: {
|
||||
...toolData.tool_parameters,
|
||||
[paramKey]: {
|
||||
...currentParam,
|
||||
type: currentParam.type || VarKindType.mention,
|
||||
mention_config: config,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
setNodes(nextNodes)
|
||||
handleSyncWorkflowDraft(true)
|
||||
}, [handleSyncWorkflowDraft, paramKey, reactflowStore, toolNodeId])
|
||||
|
||||
useEffect(() => {
|
||||
if (!toolParam || (toolParam.type && toolParam.type !== VarKindType.mention))
|
||||
return
|
||||
|
||||
const current = toolParam.mention_config
|
||||
const needsExtractor = !current?.extractor_node_id
|
||||
const needsNullStrategy = !current?.null_strategy
|
||||
const needsOutputSelector = !Array.isArray(current?.output_selector)
|
||||
const needsDefaultValue = current?.default_value === undefined
|
||||
|
||||
if (needsExtractor || needsNullStrategy || needsOutputSelector || needsDefaultValue)
|
||||
handleMentionConfigChange(mentionConfig)
|
||||
}, [handleMentionConfigChange, mentionConfig, toolParam])
|
||||
|
||||
const getUserPromptText = useCallback((promptTemplate?: PromptItem[] | PromptItem) => {
|
||||
if (!promptTemplate)
|
||||
@ -147,6 +206,8 @@ const SubGraphModal: FC<SubGraphModalProps> = ({
|
||||
sourceVariable={sourceVariable}
|
||||
agentNodeId={agentNodeId}
|
||||
agentName={agentName}
|
||||
mentionConfig={mentionConfig}
|
||||
onMentionConfigChange={handleMentionConfigChange}
|
||||
extractorNode={extractorNode}
|
||||
toolParamValue={toolParamValue}
|
||||
onSave={handleSave}
|
||||
|
||||
@ -10,6 +10,8 @@ const SubGraphCanvas: FC<SubGraphCanvasProps> = ({
|
||||
sourceVariable,
|
||||
agentNodeId,
|
||||
agentName,
|
||||
mentionConfig,
|
||||
onMentionConfigChange,
|
||||
extractorNode,
|
||||
toolParamValue,
|
||||
onSave,
|
||||
@ -22,6 +24,8 @@ const SubGraphCanvas: FC<SubGraphCanvasProps> = ({
|
||||
sourceVariable={sourceVariable}
|
||||
agentNodeId={agentNodeId}
|
||||
agentName={agentName}
|
||||
mentionConfig={mentionConfig}
|
||||
onMentionConfigChange={onMentionConfigChange}
|
||||
extractorNode={extractorNode}
|
||||
toolParamValue={toolParamValue}
|
||||
onSave={onSave}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { MentionConfig } from '@/app/components/workflow/nodes/_base/types'
|
||||
import type { LLMNodeType } from '@/app/components/workflow/nodes/llm/types'
|
||||
import type { Edge as WorkflowEdge, Node as WorkflowNode } from '@/app/components/workflow/types'
|
||||
|
||||
@ -19,6 +20,8 @@ export type SubGraphCanvasProps = {
|
||||
sourceVariable: WorkflowValueSelector
|
||||
agentNodeId: string
|
||||
agentName: string
|
||||
mentionConfig: MentionConfig
|
||||
onMentionConfigChange: (config: MentionConfig) => void
|
||||
extractorNode?: WorkflowNode<LLMNodeType>
|
||||
toolParamValue?: string
|
||||
onSave?: (nodes: WorkflowNode[], edges: WorkflowEdge[]) => void
|
||||
|
||||
@ -989,7 +989,9 @@
|
||||
"singleRun.testRunIteration": "Test Run Iteration",
|
||||
"singleRun.testRunLoop": "Test Run Loop",
|
||||
"subGraphModal.canvasPlaceholder": "Click to configure the internal structure",
|
||||
"subGraphModal.defaultValueHint": "Returns the value below",
|
||||
"subGraphModal.internalStructure": "Internal structure",
|
||||
"subGraphModal.internalStructureDesc": "Internal structure of @{{name}}",
|
||||
"subGraphModal.lastRun": "LAST RUN",
|
||||
"subGraphModal.noRunHistory": "No run history yet",
|
||||
"subGraphModal.outputVariables": "OUTPUT VARIABLES",
|
||||
@ -998,7 +1000,9 @@
|
||||
"subGraphModal.title": "INTERNAL STRUCTURE",
|
||||
"subGraphModal.whenOutputIsNone": "WHEN OUTPUT IS NONE",
|
||||
"subGraphModal.whenOutputNone.default": "Use default value",
|
||||
"subGraphModal.whenOutputNone.defaultDesc": "Continue with a default value",
|
||||
"subGraphModal.whenOutputNone.error": "Raise an error",
|
||||
"subGraphModal.whenOutputNone.errorDesc": "Pass the error to the outer workflow",
|
||||
"subGraphModal.whenOutputNone.skip": "Skip this step",
|
||||
"tabs.-": "Default",
|
||||
"tabs.addAll": "Add all",
|
||||
|
||||
@ -986,7 +986,9 @@
|
||||
"singleRun.testRunIteration": "テスト実行(イテレーション)",
|
||||
"singleRun.testRunLoop": "テスト実行ループ",
|
||||
"subGraphModal.canvasPlaceholder": "クリックして内部構造を設定",
|
||||
"subGraphModal.defaultValueHint": "以下の値を返す",
|
||||
"subGraphModal.internalStructure": "内部構造",
|
||||
"subGraphModal.internalStructureDesc": "@{{name}} の内部構造",
|
||||
"subGraphModal.lastRun": "前回の実行",
|
||||
"subGraphModal.noRunHistory": "実行履歴がありません",
|
||||
"subGraphModal.outputVariables": "出力変数",
|
||||
@ -995,7 +997,9 @@
|
||||
"subGraphModal.title": "内部構造",
|
||||
"subGraphModal.whenOutputIsNone": "出力が空の場合",
|
||||
"subGraphModal.whenOutputNone.default": "デフォルト値を使用",
|
||||
"subGraphModal.whenOutputNone.defaultDesc": "デフォルト値で続行",
|
||||
"subGraphModal.whenOutputNone.error": "エラーを発生させる",
|
||||
"subGraphModal.whenOutputNone.errorDesc": "エラーを外部ワークフローに渡す",
|
||||
"subGraphModal.whenOutputNone.skip": "このステップをスキップ",
|
||||
"tabs.-": "デフォルト",
|
||||
"tabs.addAll": "すべてを追加する",
|
||||
|
||||
@ -987,7 +987,9 @@
|
||||
"singleRun.testRunIteration": "测试运行迭代",
|
||||
"singleRun.testRunLoop": "测试运行循环",
|
||||
"subGraphModal.canvasPlaceholder": "点击配置内部结构",
|
||||
"subGraphModal.defaultValueHint": "返回以下值",
|
||||
"subGraphModal.internalStructure": "内部结构",
|
||||
"subGraphModal.internalStructureDesc": "@{{name}} 的内部结构",
|
||||
"subGraphModal.lastRun": "上次运行",
|
||||
"subGraphModal.noRunHistory": "暂无运行记录",
|
||||
"subGraphModal.outputVariables": "输出变量",
|
||||
@ -996,7 +998,9 @@
|
||||
"subGraphModal.title": "内部结构",
|
||||
"subGraphModal.whenOutputIsNone": "当输出为空时",
|
||||
"subGraphModal.whenOutputNone.default": "使用默认值",
|
||||
"subGraphModal.whenOutputNone.defaultDesc": "使用默认值继续执行",
|
||||
"subGraphModal.whenOutputNone.error": "抛出错误",
|
||||
"subGraphModal.whenOutputNone.errorDesc": "将错误传递给外部工作流",
|
||||
"subGraphModal.whenOutputNone.skip": "跳过此步骤",
|
||||
"tabs.-": "默认",
|
||||
"tabs.addAll": "添加全部",
|
||||
|
||||
@ -986,7 +986,9 @@
|
||||
"singleRun.testRunIteration": "測試運行迭代",
|
||||
"singleRun.testRunLoop": "測試運行循環",
|
||||
"subGraphModal.canvasPlaceholder": "點擊配置內部結構",
|
||||
"subGraphModal.defaultValueHint": "返回以下值",
|
||||
"subGraphModal.internalStructure": "內部結構",
|
||||
"subGraphModal.internalStructureDesc": "@{{name}} 的內部結構",
|
||||
"subGraphModal.lastRun": "上次執行",
|
||||
"subGraphModal.noRunHistory": "暫無執行記錄",
|
||||
"subGraphModal.outputVariables": "輸出變數",
|
||||
@ -995,7 +997,9 @@
|
||||
"subGraphModal.title": "內部結構",
|
||||
"subGraphModal.whenOutputIsNone": "當輸出為空時",
|
||||
"subGraphModal.whenOutputNone.default": "使用預設值",
|
||||
"subGraphModal.whenOutputNone.defaultDesc": "使用預設值繼續執行",
|
||||
"subGraphModal.whenOutputNone.error": "拋出錯誤",
|
||||
"subGraphModal.whenOutputNone.errorDesc": "將錯誤傳遞給外部工作流程",
|
||||
"subGraphModal.whenOutputNone.skip": "跳過此步驟",
|
||||
"tabs.-": "預設",
|
||||
"tabs.addAll": "全部新增",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user