diff --git a/packages/components/nodes/agentflow/Tool/Tool.ts b/packages/components/nodes/agentflow/Tool/Tool.ts index c3945ff3e..f2ab96e75 100644 --- a/packages/components/nodes/agentflow/Tool/Tool.ts +++ b/packages/components/nodes/agentflow/Tool/Tool.ts @@ -28,7 +28,7 @@ class Tool_Agentflow implements INode { constructor() { this.label = 'Tool' this.name = 'toolAgentflow' - this.version = 1.0 + this.version = 1.1 this.type = 'Tool' this.category = 'Agent Flows' this.description = 'Tools allow LLM to interact with external systems' @@ -37,7 +37,7 @@ class Tool_Agentflow implements INode { this.inputs = [ { label: 'Tool', - name: 'selectedTool', + name: 'toolAgentflowSelectedTool', type: 'asyncOptions', loadMethod: 'listTools', loadConfig: true @@ -64,7 +64,7 @@ class Tool_Agentflow implements INode { } ], show: { - selectedTool: '.+' + toolAgentflowSelectedTool: '.+' } }, { @@ -124,7 +124,7 @@ class Tool_Agentflow implements INode { }, async listToolInputArgs(nodeData: INodeData, options: ICommonObject): Promise { const currentNode = options.currentNode as ICommonObject - const selectedTool = currentNode?.inputs?.selectedTool as string + const selectedTool = (currentNode?.inputs?.selectedTool as string) || (currentNode?.inputs?.toolAgentflowSelectedTool as string) const selectedToolConfig = currentNode?.inputs?.selectedToolConfig as ICommonObject const nodeInstanceFilePath = options.componentNodes[selectedTool].filePath as string @@ -183,7 +183,7 @@ class Tool_Agentflow implements INode { } async run(nodeData: INodeData, input: string, options: ICommonObject): Promise { - const selectedTool = nodeData.inputs?.selectedTool as string + const selectedTool = (nodeData.inputs?.selectedTool as string) || (nodeData.inputs?.toolAgentflowSelectedTool as string) const selectedToolConfig = nodeData.inputs?.selectedToolConfig as ICommonObject const toolInputArgs = nodeData.inputs?.toolInputArgs as IToolInputArgs[] diff --git a/packages/server/marketplaces/agentflowsv2/Slack Agent.json b/packages/server/marketplaces/agentflowsv2/Slack Agent.json index ec95fb4b9..a36876f8b 100644 --- a/packages/server/marketplaces/agentflowsv2/Slack Agent.json +++ b/packages/server/marketplaces/agentflowsv2/Slack Agent.json @@ -526,7 +526,7 @@ "data": { "id": "toolAgentflow_0", "label": "Slack Reply", - "version": 1, + "version": 1.1, "name": "toolAgentflow", "type": "Tool", "color": "#d4a373", @@ -536,11 +536,11 @@ "inputParams": [ { "label": "Tool", - "name": "selectedTool", + "name": "toolAgentflowSelectedTool", "type": "asyncOptions", "loadMethod": "listTools", "loadConfig": true, - "id": "toolAgentflow_0-input-selectedTool-asyncOptions", + "id": "toolAgentflow_0-input-toolAgentflowSelectedTool-asyncOptions", "display": true }, { @@ -565,7 +565,7 @@ } ], "show": { - "selectedTool": ".+" + "toolAgentflowSelectedTool": ".+" }, "id": "toolAgentflow_0-input-toolInputArgs-array", "display": true @@ -599,7 +599,7 @@ ], "inputAnchors": [], "inputs": { - "selectedTool": "slackMCP", + "toolAgentflowSelectedTool": "slackMCP", "toolInputArgs": [ { "inputArgName": "channel_id", @@ -611,9 +611,9 @@ } ], "toolUpdateState": "", - "selectedToolConfig": { + "toolAgentflowSelectedToolConfig": { "mcpActions": "[\"slack_post_message\"]", - "selectedTool": "slackMCP" + "toolAgentflowSelectedTool": "slackMCP" } }, "outputAnchors": [ diff --git a/packages/ui/src/views/agentflowsv2/ConfigInput.jsx b/packages/ui/src/views/agentflowsv2/ConfigInput.jsx index 0c5ef7135..94b022356 100644 --- a/packages/ui/src/views/agentflowsv2/ConfigInput.jsx +++ b/packages/ui/src/views/agentflowsv2/ConfigInput.jsx @@ -1,4 +1,4 @@ -import { useContext, useState, useEffect, useRef } from 'react' +import { useContext, useState, useEffect, useMemo } from 'react' import PropTypes from 'prop-types' import { cloneDeep } from 'lodash' @@ -26,8 +26,12 @@ export const ConfigInput = ({ data, inputParam, disabled = false, arrayIndex = n const [expanded, setExpanded] = useState(false) const [selectedComponentNodeData, setSelectedComponentNodeData] = useState({}) - // Track the last processed input values to prevent infinite loops - const lastProcessedInputsRef = useRef({}) + // Track the last processed input values to prevent infinite loops using useState + const [lastProcessedInputs, setLastProcessedInputs] = useState({ + mainValue: null, + configValue: null, + arrayValue: null + }) const handleAccordionChange = (event, isExpanded) => { setExpanded(isExpanded) @@ -64,6 +68,18 @@ export const ConfigInput = ({ data, inputParam, disabled = false, arrayIndex = n setSelectedComponentNodeData(nodeData) } + // Memoize current input values for reliable comparison + const currentInputValues = useMemo( + () => ({ + mainValue: data.inputs[inputParam.name], + configValue: data.inputs[`${inputParam.name}Config`], + arrayValue: parentParamForArray ? data.inputs[parentParamForArray.name] : null + }), + + // eslint-disable-next-line react-hooks/exhaustive-deps + [data.inputs, inputParam.name, parentParamForArray?.name] + ) + // Load initial component data when the component mounts useEffect(() => { const loadComponentData = async () => { @@ -138,11 +154,11 @@ export const ConfigInput = ({ data, inputParam, disabled = false, arrayIndex = n setSelectedComponentNodeData(componentNodeData) // Store the processed inputs to track changes - lastProcessedInputsRef.current = { + setLastProcessedInputs({ mainValue: data.inputs[inputParam.name], configValue: data.inputs[`${inputParam.name}Config`], arrayValue: parentParamForArray ? data.inputs[parentParamForArray.name] : null - } + }) } loadComponentData() @@ -154,15 +170,10 @@ export const ConfigInput = ({ data, inputParam, disabled = false, arrayIndex = n useEffect(() => { if (!selectedComponentNodeData.inputParams) return - // Get current input values - const currentMainValue = data.inputs[inputParam.name] - const currentConfigValue = data.inputs[`${inputParam.name}Config`] - const currentArrayValue = parentParamForArray ? data.inputs[parentParamForArray.name] : null - - // Check if relevant inputs have changed - const hasMainValueChanged = lastProcessedInputsRef.current.mainValue !== currentMainValue - const hasConfigValueChanged = lastProcessedInputsRef.current.configValue !== currentConfigValue - const hasArrayValueChanged = lastProcessedInputsRef.current.arrayValue !== currentArrayValue + // Check if relevant inputs have changed using strict equality comparison + const hasMainValueChanged = lastProcessedInputs.mainValue !== currentInputValues.mainValue + const hasConfigValueChanged = lastProcessedInputs.configValue !== currentInputValues.configValue + const hasArrayValueChanged = lastProcessedInputs.arrayValue !== currentInputValues.arrayValue if (!hasMainValueChanged && !hasConfigValueChanged && !hasArrayValueChanged) { return // No relevant changes @@ -224,17 +235,17 @@ export const ConfigInput = ({ data, inputParam, disabled = false, arrayIndex = n setSelectedComponentNodeData(updatedComponentData) // Update the tracked values - lastProcessedInputsRef.current = { - mainValue: currentMainValue, - configValue: currentConfigValue, - arrayValue: currentArrayValue - } + setLastProcessedInputs({ + mainValue: currentInputValues.mainValue, + configValue: currentInputValues.configValue, + arrayValue: currentInputValues.arrayValue + }) } updateComponentData() // eslint-disable-next-line react-hooks/exhaustive-deps - }, [data.inputs, inputParam.name, parentParamForArray?.name, arrayIndex]) + }, [currentInputValues, selectedComponentNodeData.inputParams, inputParam.name, parentParamForArray?.name, arrayIndex]) // Update node configuration when selected component data changes useEffect(() => {