diff --git a/packages/components/nodes/agentflow/CustomFunction/CustomFunction.ts b/packages/components/nodes/agentflow/CustomFunction/CustomFunction.ts index 1f6205e7..f5d4bb61 100644 --- a/packages/components/nodes/agentflow/CustomFunction/CustomFunction.ts +++ b/packages/components/nodes/agentflow/CustomFunction/CustomFunction.ts @@ -157,7 +157,8 @@ class CustomFunction_Agentflow implements INode { chatflowId: options.chatflowid, sessionId: options.sessionId, chatId: options.chatId, - input + input, + state: newState } let sandbox: any = { diff --git a/packages/components/nodes/agentflow/HTTP/HTTP.ts b/packages/components/nodes/agentflow/HTTP/HTTP.ts index 438c22ea..0027f1e2 100644 --- a/packages/components/nodes/agentflow/HTTP/HTTP.ts +++ b/packages/components/nodes/agentflow/HTTP/HTTP.ts @@ -21,7 +21,7 @@ class HTTP_Agentflow implements INode { constructor() { this.label = 'HTTP' this.name = 'httpAgentflow' - this.version = 1.0 + this.version = 1.1 this.type = 'HTTP' this.category = 'Agent Flows' this.description = 'Send a HTTP request' @@ -72,6 +72,7 @@ class HTTP_Agentflow implements INode { label: 'Headers', name: 'headers', type: 'array', + acceptVariable: true, array: [ { label: 'Key', @@ -83,7 +84,8 @@ class HTTP_Agentflow implements INode { label: 'Value', name: 'value', type: 'string', - default: '' + default: '', + acceptVariable: true } ], optional: true @@ -92,6 +94,7 @@ class HTTP_Agentflow implements INode { label: 'Query Params', name: 'queryParams', type: 'array', + acceptVariable: true, array: [ { label: 'Key', @@ -103,7 +106,8 @@ class HTTP_Agentflow implements INode { label: 'Value', name: 'value', type: 'string', - default: '' + default: '', + acceptVariable: true } ], optional: true @@ -147,6 +151,7 @@ class HTTP_Agentflow implements INode { label: 'Body', name: 'body', type: 'array', + acceptVariable: true, show: { bodyType: ['xWwwFormUrlencoded', 'formData'] }, @@ -161,7 +166,8 @@ class HTTP_Agentflow implements INode { label: 'Value', name: 'value', type: 'string', - default: '' + default: '', + acceptVariable: true } ], optional: true diff --git a/packages/components/nodes/agentflow/Start/Start.ts b/packages/components/nodes/agentflow/Start/Start.ts index 5f6bf844..833e3b7c 100644 --- a/packages/components/nodes/agentflow/Start/Start.ts +++ b/packages/components/nodes/agentflow/Start/Start.ts @@ -18,7 +18,7 @@ class Start_Agentflow implements INode { constructor() { this.label = 'Start' this.name = 'startAgentflow' - this.version = 1.0 + this.version = 1.1 this.type = 'Start' this.category = 'Agent Flows' this.description = 'Starting point of the agentflow' @@ -153,6 +153,13 @@ class Start_Agentflow implements INode { optional: true } ] + }, + { + label: 'Persist State', + name: 'startPersistState', + type: 'boolean', + description: 'Persist the state in the same session', + optional: true } ] } @@ -161,6 +168,7 @@ class Start_Agentflow implements INode { const _flowState = nodeData.inputs?.startState as string const startInputType = nodeData.inputs?.startInputType as string const startEphemeralMemory = nodeData.inputs?.startEphemeralMemory as boolean + const startPersistState = nodeData.inputs?.startPersistState as boolean let flowStateArray = [] if (_flowState) { @@ -176,6 +184,13 @@ class Start_Agentflow implements INode { flowState[state.key] = state.value } + const runtimeState = options.agentflowRuntime?.state as ICommonObject + if (startPersistState === true && runtimeState && Object.keys(runtimeState).length) { + for (const state in runtimeState) { + flowState[state] = runtimeState[state] + } + } + const inputData: ICommonObject = {} const outputData: ICommonObject = {} @@ -202,6 +217,10 @@ class Start_Agentflow implements INode { outputData.ephemeralMemory = true } + if (startPersistState) { + outputData.persistState = true + } + const returnOutput = { id: nodeData.id, name: this.name, diff --git a/packages/server/marketplaces/agentflowsv2/Agentic RAG V2.json b/packages/server/marketplaces/agentflowsv2/Agentic RAG V2.json index 343307f1..c37df8a9 100644 --- a/packages/server/marketplaces/agentflowsv2/Agentic RAG V2.json +++ b/packages/server/marketplaces/agentflowsv2/Agentic RAG V2.json @@ -12,7 +12,7 @@ "data": { "id": "startAgentflow_0", "label": "Start", - "version": 1, + "version": 1.1, "name": "startAgentflow", "type": "Start", "color": "#7EE787", @@ -157,6 +157,15 @@ ], "id": "startAgentflow_0-input-startState-array", "display": true + }, + { + "label": "Persist State", + "name": "startPersistState", + "type": "boolean", + "description": "Persist the state in the same session", + "optional": true, + "id": "startAgentflow_0-input-startPersistState-boolean", + "display": true } ], "inputAnchors": [], diff --git a/packages/server/marketplaces/agentflowsv2/Agents Handoff.json b/packages/server/marketplaces/agentflowsv2/Agents Handoff.json index 72b4da96..31d74d71 100644 --- a/packages/server/marketplaces/agentflowsv2/Agents Handoff.json +++ b/packages/server/marketplaces/agentflowsv2/Agents Handoff.json @@ -12,7 +12,7 @@ "data": { "id": "startAgentflow_0", "label": "Start", - "version": 1, + "version": 1.1, "name": "startAgentflow", "type": "Start", "color": "#7EE787", @@ -157,6 +157,15 @@ ], "id": "startAgentflow_0-input-startState-array", "display": true + }, + { + "label": "Persist State", + "name": "startPersistState", + "type": "boolean", + "description": "Persist the state in the same session", + "optional": true, + "id": "startAgentflow_0-input-startPersistState-boolean", + "display": true } ], "inputAnchors": [], diff --git a/packages/server/marketplaces/agentflowsv2/Deep Research V2.json b/packages/server/marketplaces/agentflowsv2/Deep Research V2.json index bd58656b..bad7e2ef 100644 --- a/packages/server/marketplaces/agentflowsv2/Deep Research V2.json +++ b/packages/server/marketplaces/agentflowsv2/Deep Research V2.json @@ -12,7 +12,7 @@ "data": { "id": "startAgentflow_0", "label": "Start", - "version": 1, + "version": 1.1, "name": "startAgentflow", "type": "Start", "color": "#7EE787", @@ -160,6 +160,15 @@ ], "id": "startAgentflow_0-input-startState-array", "display": true + }, + { + "label": "Persist State", + "name": "startPersistState", + "type": "boolean", + "description": "Persist the state in the same session", + "optional": true, + "id": "startAgentflow_0-input-startPersistState-boolean", + "display": true } ], "inputAnchors": [], diff --git a/packages/server/marketplaces/agentflowsv2/Email Reply HITL Agent.json b/packages/server/marketplaces/agentflowsv2/Email Reply HITL Agent.json index 0174b1a9..2ab7e4d9 100644 --- a/packages/server/marketplaces/agentflowsv2/Email Reply HITL Agent.json +++ b/packages/server/marketplaces/agentflowsv2/Email Reply HITL Agent.json @@ -12,7 +12,7 @@ "data": { "id": "startAgentflow_0", "label": "Start", - "version": 1, + "version": 1.1, "name": "startAgentflow", "type": "Start", "color": "#7EE787", @@ -157,6 +157,15 @@ ], "id": "startAgentflow_0-input-startState-array", "display": true + }, + { + "label": "Persist State", + "name": "startPersistState", + "type": "boolean", + "description": "Persist the state in the same session", + "optional": true, + "id": "startAgentflow_0-input-startPersistState-boolean", + "display": true } ], "inputAnchors": [], diff --git a/packages/server/marketplaces/agentflowsv2/Financial Research Agent.json b/packages/server/marketplaces/agentflowsv2/Financial Research Agent.json index f72312e7..ac4dab53 100644 --- a/packages/server/marketplaces/agentflowsv2/Financial Research Agent.json +++ b/packages/server/marketplaces/agentflowsv2/Financial Research Agent.json @@ -12,7 +12,7 @@ "data": { "id": "startAgentflow_0", "label": "Start", - "version": 1, + "version": 1.1, "name": "startAgentflow", "type": "Start", "color": "#7EE787", @@ -157,6 +157,15 @@ ], "id": "startAgentflow_0-input-startState-array", "display": true + }, + { + "label": "Persist State", + "name": "startPersistState", + "type": "boolean", + "description": "Persist the state in the same session", + "optional": true, + "id": "startAgentflow_0-input-startPersistState-boolean", + "display": true } ], "inputAnchors": [], diff --git a/packages/server/marketplaces/agentflowsv2/Iterations.json b/packages/server/marketplaces/agentflowsv2/Iterations.json index b33dd1a5..39eb72bd 100644 --- a/packages/server/marketplaces/agentflowsv2/Iterations.json +++ b/packages/server/marketplaces/agentflowsv2/Iterations.json @@ -12,7 +12,7 @@ "data": { "id": "startAgentflow_0", "label": "Start", - "version": 1, + "version": 1.1, "name": "startAgentflow", "type": "Start", "color": "#7EE787", @@ -157,6 +157,15 @@ ], "id": "startAgentflow_0-input-startState-array", "display": true + }, + { + "label": "Persist State", + "name": "startPersistState", + "type": "boolean", + "description": "Persist the state in the same session", + "optional": true, + "id": "startAgentflow_0-input-startPersistState-boolean", + "display": true } ], "inputAnchors": [], diff --git a/packages/server/marketplaces/agentflowsv2/Slack Agent.json b/packages/server/marketplaces/agentflowsv2/Slack Agent.json index cd30db64..ec95fb4b 100644 --- a/packages/server/marketplaces/agentflowsv2/Slack Agent.json +++ b/packages/server/marketplaces/agentflowsv2/Slack Agent.json @@ -12,7 +12,7 @@ "data": { "id": "startAgentflow_0", "label": "Start", - "version": 1, + "version": 1.1, "name": "startAgentflow", "type": "Start", "color": "#7EE787", @@ -157,6 +157,15 @@ ], "id": "startAgentflow_0-input-startState-array", "display": true + }, + { + "label": "Persist State", + "name": "startPersistState", + "type": "boolean", + "description": "Persist the state in the same session", + "optional": true, + "id": "startAgentflow_0-input-startPersistState-boolean", + "display": true } ], "inputAnchors": [], diff --git a/packages/server/marketplaces/agentflowsv2/Supervisor Worker.json b/packages/server/marketplaces/agentflowsv2/Supervisor Worker.json index dbf60b33..1773ce6a 100644 --- a/packages/server/marketplaces/agentflowsv2/Supervisor Worker.json +++ b/packages/server/marketplaces/agentflowsv2/Supervisor Worker.json @@ -12,7 +12,7 @@ "data": { "id": "startAgentflow_0", "label": "Start", - "version": 1, + "version": 1.1, "name": "startAgentflow", "type": "Start", "color": "#7EE787", @@ -157,6 +157,15 @@ ], "id": "startAgentflow_0-input-startState-array", "display": true + }, + { + "label": "Persist State", + "name": "startPersistState", + "type": "boolean", + "description": "Persist the state in the same session", + "optional": true, + "id": "startAgentflow_0-input-startPersistState-boolean", + "display": true } ], "inputAnchors": [], diff --git a/packages/server/src/utils/buildAgentflow.ts b/packages/server/src/utils/buildAgentflow.ts index 754f09b4..3e63d605 100644 --- a/packages/server/src/utils/buildAgentflow.ts +++ b/packages/server/src/utils/buildAgentflow.ts @@ -1324,6 +1324,24 @@ export const executeAgentFlow = async ({ } } + // If the state is persistent, get the state from the previous execution + const startPersistState = nodes.find((node) => node.data.name === 'startAgentflow')?.data.inputs?.startPersistState + if (startPersistState === true && previousExecution) { + const previousExecutionData = (JSON.parse(previousExecution.executionData) as IAgentflowExecutedData[]) ?? [] + + let previousState = {} + if (Array.isArray(previousExecutionData) && previousExecutionData.length) { + for (const execData of previousExecutionData.reverse()) { + if (execData.data.state) { + previousState = execData.data.state + break + } + } + } + + agentflowRuntime.state = previousState + } + // If the start input type is form input, get the form values from the previous execution (form values are persisted in the same session) if (startInputType === 'formInput' && previousExecution) { const previousExecutionData = (JSON.parse(previousExecution.executionData) as IAgentflowExecutedData[]) ?? [] diff --git a/packages/ui/src/ui-component/array/ArrayRenderer.jsx b/packages/ui/src/ui-component/array/ArrayRenderer.jsx index fa90a247..c3849954 100644 --- a/packages/ui/src/ui-component/array/ArrayRenderer.jsx +++ b/packages/ui/src/ui-component/array/ArrayRenderer.jsx @@ -54,7 +54,7 @@ export const ArrayRenderer = ({ inputParam, data, disabled }) => { // Initialize array items and parameters when component mounts or data changes useEffect(() => { - const initialArrayItems = cloneDeep(data.inputs[inputParam.name]) || [] + const initialArrayItems = data.inputs[inputParam.name] || [] setArrayItems(initialArrayItems) // Calculate initial display parameters for each array item diff --git a/packages/ui/src/views/agentflowsv2/AgentFlowNode.jsx b/packages/ui/src/views/agentflowsv2/AgentFlowNode.jsx index c5a71913..a028e828 100644 --- a/packages/ui/src/views/agentflowsv2/AgentFlowNode.jsx +++ b/packages/ui/src/views/agentflowsv2/AgentFlowNode.jsx @@ -20,7 +20,8 @@ import { IconCopy, IconTrash, IconInfoCircle, - IconLoader + IconLoader, + IconAlertCircleFilled } from '@tabler/icons-react' import StopCircleIcon from '@mui/icons-material/StopCircle' import CancelIcon from '@mui/icons-material/Cancel' @@ -51,11 +52,13 @@ const StyledNodeToolbar = styled(NodeToolbar)(({ theme }) => ({ const AgentFlowNode = ({ data }) => { const theme = useTheme() const customization = useSelector((state) => state.customization) + const canvas = useSelector((state) => state.canvas) const ref = useRef(null) const updateNodeInternals = useUpdateNodeInternals() // eslint-disable-next-line const [position, setPosition] = useState(0) const [isHovered, setIsHovered] = useState(false) + const [warningMessage, setWarningMessage] = useState('') const { deleteNode, duplicateNode } = useContext(flowContext) const [showInfoDialog, setShowInfoDialog] = useState(false) const [infoDialogProps, setInfoDialogProps] = useState({}) @@ -132,6 +135,28 @@ const AgentFlowNode = ({ data }) => { } }, [data, ref, updateNodeInternals]) + useEffect(() => { + const nodeOutdatedMessage = (oldVersion, newVersion) => + `Node version ${oldVersion} outdated\nUpdate to latest version ${newVersion}` + const nodeVersionEmptyMessage = (newVersion) => `Node outdated\nUpdate to latest version ${newVersion}` + + const componentNode = canvas.componentNodes.find((nd) => nd.name === data.name) + if (componentNode) { + if (!data.version) { + setWarningMessage(nodeVersionEmptyMessage(componentNode.version)) + } else if (data.version && componentNode.version > data.version) { + setWarningMessage(nodeOutdatedMessage(data.version, componentNode.version)) + } else if (componentNode.badge === 'DEPRECATING') { + setWarningMessage( + componentNode?.deprecateMessage ?? + 'This node will be deprecated in the next release. Change to a new node tagged with NEW' + ) + } else { + setWarningMessage('') + } + } + }, [canvas.componentNodes, data.name, data.version]) + return (
setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}> @@ -236,6 +261,24 @@ const AgentFlowNode = ({ data }) => { )} + {warningMessage && ( + {warningMessage}}> + + + + + )} + {!data.hideInput && ( { data.inputs[inputParam.name] = inputParam.codeExample + setReloadTimestamp(Date.now().toString()) }} > See Example @@ -1044,6 +1045,7 @@ const NodeInputHandler = ({ )}