From 27d1174dbb1ef38d566055daf2f70ba3e0968aa8 Mon Sep 17 00:00:00 2001 From: zhaoying Date: Fri, 23 Jan 2026 13:48:51 +0800 Subject: [PATCH] fix(web): workflow's variables bugfix --- .../components/Properties/VariableSelect.tsx | 10 +- .../Properties/hooks/useVariableList.ts | 76 ++++++++- .../Workflow/components/Properties/index.tsx | 145 ++---------------- 3 files changed, 92 insertions(+), 139 deletions(-) diff --git a/web/src/views/Workflow/components/Properties/VariableSelect.tsx b/web/src/views/Workflow/components/Properties/VariableSelect.tsx index 59fd7d5a..f56180a0 100644 --- a/web/src/views/Workflow/components/Properties/VariableSelect.tsx +++ b/web/src/views/Workflow/components/Properties/VariableSelect.tsx @@ -91,15 +91,11 @@ const VariableSelect: FC = ({ onChange={handleChange} showSearch allowClear={allowClear} + optionFilterProp="value" filterOption={(input, option) => { if (input === '/') return true; - if (option?.options) { - return option.label?.toLowerCase().includes(input.toLowerCase()) || - option.options.some((opt: any) => - opt.value.toLowerCase().includes(input.toLowerCase()) - ); - } - return option?.label?.toLowerCase().includes(input.toLowerCase()) ?? false; + const value = 'value' in option! ? option.value as string : ''; + return value.toLowerCase().includes(input.toLowerCase()); }} /> ) diff --git a/web/src/views/Workflow/components/Properties/hooks/useVariableList.ts b/web/src/views/Workflow/components/Properties/hooks/useVariableList.ts index 16c32b7c..37574f75 100644 --- a/web/src/views/Workflow/components/Properties/hooks/useVariableList.ts +++ b/web/src/views/Workflow/components/Properties/hooks/useVariableList.ts @@ -135,6 +135,78 @@ export const getCurrentNodeVariables = (nodeData: any, values: any): Suggestion[ return nodeData.type === 'var-aggregator' && !nodeData.config.group.defaultValue ? [] : list; }; +export const getChildNodeVariables = ( + selectedNode: Node, + graphRef: React.MutableRefObject +): Suggestion[] => { + const graph = graphRef.current; + if (!graph) return []; + + const list: Suggestion[] = []; + const nodes = graph.getNodes(); + const edges = graph.getEdges(); + const keys = new Set(); + + const childNodes = nodes.filter(node => node.getData()?.cycle === selectedNode.id); + + const getConnectedNodes = (nodeId: string, visited = new Set()): string[] => { + if (visited.has(nodeId)) return []; + visited.add(nodeId); + const prev = edges.filter(e => e.getTargetCellId() === nodeId).map(e => e.getSourceCellId()); + return [...prev, ...prev.flatMap(id => getConnectedNodes(id, visited))]; + }; + + const relevantIds = new Set(); + childNodes.forEach(child => { + relevantIds.add(child.id); + getConnectedNodes(child.id).forEach(id => relevantIds.add(id)); + }); + + relevantIds.forEach(id => { + const node = nodes.find(n => n.id === id); + if (!node) return; + + const nodeData = node.getData(); + const nodeId = nodeData.id; + const { type } = nodeData; + + if (type in NODE_VARIABLES) { + NODE_VARIABLES[type as keyof typeof NODE_VARIABLES].forEach(({ label, dataType, field }) => { + const varKey = `${nodeId}_${label}`; + if (!keys.has(varKey)) { + keys.add(varKey); + list.push({ + key: varKey, + label, + type: 'variable', + dataType, + value: `${nodeId}.${field}`, + nodeData, + }); + } + }); + } + + if (type === 'parameter-extractor') { + (nodeData.config?.params?.defaultValue || []).forEach((p: any) => { + if (p?.name && !keys.has(`${nodeId}_${p.name}`)) { + keys.add(`${nodeId}_${p.name}`); + list.push({ + key: `${nodeId}_${p.name}`, + label: p.name, + type: 'variable', + dataType: p.type || 'string', + value: `${nodeId}.${p.name}`, + nodeData, + }); + } + }); + } + }); + + return list; +}; + export const useVariableList = ( selectedNode: Node | null | undefined, graphRef: React.MutableRefObject, @@ -187,13 +259,13 @@ export const useVariableList = ( } else if (pd.type === 'iteration' && pd.config.input.defaultValue) { let itemType = 'object'; const iv = list.find(v => `{{${v.value}}}` === pd.config.input.defaultValue); - if (iv?.dataType.startsWith('array[')) itemType = iv.dataType.replace(/^array\[(.+)\]$/, '$1'); + if (iv?.dataType.startsWith('array[')) {itemType = iv.dataType.replace(/^array\[(.+)\]$/, '$1');} addVariable(list, keys, `${pid}_item`, 'item', itemType, `${pid}.item`, pd); addVariable(list, keys, `${pid}_index`, 'index', 'number', `${pid}.index`, pd); } else if (pd.type === 'iteration' && !pd.config.input.defaultValue) { let itemType = 'object'; const iv = list.find(v => `{{${v.value}}}` === pd.config.input.defaultValue); - if (iv?.dataType.startsWith('array[')) itemType = iv.dataType.replace(/^array\[(.+)\]$/, '$1'); + if (iv?.dataType.startsWith('array[')) {itemType = iv.dataType.replace(/^array\[(.+)\]$/, '$1');} addVariable(list, keys, `${pid}_item`, 'item', 'string', `${pid}.item`, pd); addVariable(list, keys, `${pid}_index`, 'index', 'number', `${pid}.index`, pd); } diff --git a/web/src/views/Workflow/components/Properties/index.tsx b/web/src/views/Workflow/components/Properties/index.tsx index 26ad0470..38fd3005 100644 --- a/web/src/views/Workflow/components/Properties/index.tsx +++ b/web/src/views/Workflow/components/Properties/index.tsx @@ -24,7 +24,7 @@ import AssignmentList from './AssignmentList' import ToolConfig from './ToolConfig' import MemoryConfig from './MemoryConfig' import VariableList from './VariableList' -import { useVariableList, getCurrentNodeVariables } from './hooks/useVariableList' +import { useVariableList, getCurrentNodeVariables, getChildNodeVariables } from './hooks/useVariableList' import styles from './properties.module.css' import Editor from "../Editor"; import RbSlider from './RbSlider' @@ -290,141 +290,26 @@ const Properties: FC = ({ let filteredList = addParentIterationVars(variableList).filter(variable => variable.dataType === 'string' || variable.dataType === 'number'); return filteredList; } - if (nodeType === 'iteration' && key === 'output') { - let filteredList = variableList.filter(variable => variable.value.includes('sys.')); - // Add child node output variables for loop nodes - if (selectedNode) { - const graph = graphRef.current; - if (graph) { - const nodes = graph.getNodes(); - const childNodes = nodes.filter(node => { - const nodeData = node.getData(); - return nodeData?.cycle === selectedNode.id; - }); - - // Add output variables from child nodes - childNodes.forEach(childNode => { - const childData = childNode.getData(); - const childNodeId = childData.id; - - // Add child node output variables based on their type - switch (childData.type) { - case 'llm': - case 'jinja-render': - case 'tool': - const outputKey = `${childNodeId}_output`; - const existingOutput = filteredList.find(v => v.key === outputKey); - if (!existingOutput) { - filteredList.push({ - key: outputKey, - label: 'output', - type: 'variable', - dataType: 'string', - value: `${childNodeId}.output`, - nodeData: childData, - }); - } - break; - case 'http-request': - const bodyKey = `${childNodeId}_body`; - const statusKey = `${childNodeId}_status_code`; - if (!filteredList.find(v => v.key === bodyKey)) { - filteredList.push({ - key: bodyKey, - label: 'body', - type: 'variable', - dataType: 'string', - value: `${childNodeId}.body`, - nodeData: childData, - }); - } - if (!filteredList.find(v => v.key === statusKey)) { - filteredList.push({ - key: statusKey, - label: 'status_code', - type: 'variable', - dataType: 'number', - value: `${childNodeId}.status_code`, - nodeData: childData, - }); - } - break; - } - }); + if (nodeType === 'iteration' && key === 'output' || nodeType === 'loop' && key === 'condition') { + if (!selectedNode) return []; + let filteredList = nodeType === 'iteration' + ? variableList.filter(variable => variable.value.includes('sys.')) + : addParentIterationVars(variableList).filter(variable => variable.nodeData.type !== 'loop'); + + const childVariables = getChildNodeVariables(selectedNode, graphRef); + const existingKeys = new Set(filteredList.map(v => v.key)); + childVariables.forEach(v => { + if (!existingKeys.has(v.key)) { + filteredList.push(v); + existingKeys.add(v.key); } - } + }); + return filteredList; } if (nodeType === 'iteration') { return variableList.filter(variable => variable.dataType.includes('array')); } - if (nodeType === 'loop' && key === 'condition') { - let filteredList = addParentIterationVars(variableList).filter(variable => variable.nodeData.type !== 'loop'); - - // Add child node output variables for loop nodes - if (selectedNode) { - const graph = graphRef.current; - if (graph) { - const nodes = graph.getNodes(); - const childNodes = nodes.filter(node => { - const nodeData = node.getData(); - return nodeData?.cycle === selectedNode.id; - }); - - // Add output variables from child nodes - childNodes.forEach(childNode => { - const childData = childNode.getData(); - const childNodeId = childData.id; - - // Add child node output variables based on their type - switch(childData.type) { - case 'llm': - case 'jinja-render': - case 'tool': - const outputKey = `${childNodeId}_output`; - const existingOutput = filteredList.find(v => v.key === outputKey); - if (!existingOutput) { - filteredList.push({ - key: outputKey, - label: 'output', - type: 'variable', - dataType: 'string', - value: `${childNodeId}.output`, - nodeData: childData, - }); - } - break; - case 'http-request': - const bodyKey = `${childNodeId}_body`; - const statusKey = `${childNodeId}_status_code`; - if (!filteredList.find(v => v.key === bodyKey)) { - filteredList.push({ - key: bodyKey, - label: 'body', - type: 'variable', - dataType: 'string', - value: `${childNodeId}.body`, - nodeData: childData, - }); - } - if (!filteredList.find(v => v.key === statusKey)) { - filteredList.push({ - key: statusKey, - label: 'status_code', - type: 'variable', - dataType: 'number', - value: `${childNodeId}.status_code`, - nodeData: childData, - }); - } - break; - } - }); - } - } - - return filteredList; - } // For all other node types, add parent iteration variables if applicable let baseList = variableList;