From 88e92a36a641898125e28f87efb0e48ed4b1b609 Mon Sep 17 00:00:00 2001 From: Abhimanyu Yadav <122007096+Abhi1992002@users.noreply.github.com> Date: Fri, 16 Jan 2026 18:50:00 +0530 Subject: [PATCH] =?UTF-8?q?Revert=20"fix(frontend):=20improve=20history=20?= =?UTF-8?q?tracking=20and=20error=20handling=20in=20flow=20ed=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 11d5ef2f4372acf04050b1497778cb2d777a2c76. --- .../RunInputDialog/useRunInputDialog.ts | 36 +++------ .../build/components/FlowEditor/Flow/Flow.tsx | 6 +- .../FlowEditor/edges/useCustomEdge.ts | 14 ---- .../CustomNode/components/NodeHeader.tsx | 2 +- .../app/(platform)/build/stores/edgeStore.ts | 39 ++++------ .../(platform)/build/stores/historyStore.ts | 48 ++---------- .../app/(platform)/build/stores/nodeStore.ts | 77 +++++++------------ .../renderers/InputRenderer/FormRenderer.tsx | 2 + 8 files changed, 63 insertions(+), 161 deletions(-) diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunInputDialog/useRunInputDialog.ts b/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunInputDialog/useRunInputDialog.ts index 358fd3ae7e..f19cb96205 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunInputDialog/useRunInputDialog.ts +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunInputDialog/useRunInputDialog.ts @@ -48,29 +48,17 @@ export const useRunInputDialog = ({ }, onError: (error) => { if (error instanceof ApiError && error.isGraphValidationError()) { - const errorData = error.response?.detail || { - node_errors: {}, - message: undefined, - }; - const nodeErrors = errorData.node_errors || {}; - - if (Object.keys(nodeErrors).length > 0) { - Object.entries(nodeErrors).forEach( - ([nodeId, nodeErrorsForNode]) => { - useNodeStore - .getState() - .updateNodeErrors( - nodeId, - nodeErrorsForNode as { [key: string]: string }, - ); - }, - ); - } else { - useNodeStore.getState().nodes.forEach((node) => { - useNodeStore.getState().updateNodeErrors(node.id, {}); - }); - } - + const errorData = error.response?.detail; + Object.entries(errorData.node_errors).forEach( + ([nodeId, nodeErrors]) => { + useNodeStore + .getState() + .updateNodeErrors( + nodeId, + nodeErrors as { [key: string]: string }, + ); + }, + ); toast({ title: errorData?.message || "Graph validation failed", description: @@ -79,7 +67,7 @@ export const useRunInputDialog = ({ }); setIsOpen(false); - const firstBackendId = Object.keys(nodeErrors)[0]; + const firstBackendId = Object.keys(errorData.node_errors)[0]; if (firstBackendId) { const firstErrorNode = useNodeStore diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/Flow.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/Flow.tsx index 87ae4300b8..29fd984b1d 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/Flow.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/Flow.tsx @@ -55,16 +55,14 @@ export const Flow = () => { const edgeTypes = useMemo(() => ({ custom: CustomEdge }), []); const onNodeDragStop = useCallback(() => { - const currentNodes = useNodeStore.getState().nodes; setNodes( - resolveCollisions(currentNodes, { + resolveCollisions(nodes, { maxIterations: Infinity, overlapThreshold: 0.5, margin: 15, }), ); - }, [setNodes]); - + }, [setNodes, nodes]); const { edges, onConnect, onEdgesChange } = useCustomEdge(); // for loading purpose diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/edges/useCustomEdge.ts b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/edges/useCustomEdge.ts index d8571749d3..bf4ba3a418 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/edges/useCustomEdge.ts +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/edges/useCustomEdge.ts @@ -6,7 +6,6 @@ import { import { useEdgeStore } from "@/app/(platform)/build/stores/edgeStore"; import { useCallback } from "react"; import { useNodeStore } from "../../../stores/nodeStore"; -import { useHistoryStore } from "../../../stores/historyStore"; import { CustomEdge } from "./CustomEdge"; export const useCustomEdge = () => { @@ -52,20 +51,7 @@ export const useCustomEdge = () => { const onEdgesChange = useCallback( (changes: EdgeChange[]) => { - const hasRemoval = changes.some((change) => change.type === "remove"); - - const prevState = hasRemoval - ? { - nodes: useNodeStore.getState().nodes, - edges: edges, - } - : null; - setEdges(applyEdgeChanges(changes, edges)); - - if (prevState) { - useHistoryStore.getState().pushState(prevState); - } }, [edges, setEdges], ); diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeHeader.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeHeader.tsx index c4659b8dcf..d9f3d108f4 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeHeader.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeHeader.tsx @@ -22,7 +22,7 @@ export const NodeHeader = ({ data, nodeId }: Props) => { const updateNodeData = useNodeStore((state) => state.updateNodeData); const title = (data.metadata?.customized_name as string) || - data.hardcodedValues?.agent_name || + data.hardcodedValues.agent_name || data.title; const [isEditingTitle, setIsEditingTitle] = useState(false); diff --git a/autogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts b/autogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts index 6a45b9e1e2..7b17eecfb3 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts +++ b/autogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts @@ -5,8 +5,6 @@ import { customEdgeToLink, linkToCustomEdge } from "../components/helper"; import { MarkerType } from "@xyflow/react"; import { NodeExecutionResult } from "@/app/api/__generated__/models/nodeExecutionResult"; import { cleanUpHandleId } from "@/components/renderers/InputRenderer/helpers"; -import { useHistoryStore } from "./historyStore"; -import { useNodeStore } from "./nodeStore"; type EdgeStore = { edges: CustomEdge[]; @@ -55,36 +53,25 @@ export const useEdgeStore = create((set, get) => ({ id, }; - const exists = get().edges.some( - (e) => - e.source === newEdge.source && - e.target === newEdge.target && - e.sourceHandle === newEdge.sourceHandle && - e.targetHandle === newEdge.targetHandle, - ); - if (exists) return newEdge; - const prevState = { - nodes: useNodeStore.getState().nodes, - edges: get().edges, - }; - - set((state) => ({ edges: [...state.edges, newEdge] })); - useHistoryStore.getState().pushState(prevState); + set((state) => { + const exists = state.edges.some( + (e) => + e.source === newEdge.source && + e.target === newEdge.target && + e.sourceHandle === newEdge.sourceHandle && + e.targetHandle === newEdge.targetHandle, + ); + if (exists) return state; + return { edges: [...state.edges, newEdge] }; + }); return newEdge; }, - removeEdge: (edgeId) => { - const prevState = { - nodes: useNodeStore.getState().nodes, - edges: get().edges, - }; - + removeEdge: (edgeId) => set((state) => ({ edges: state.edges.filter((e) => e.id !== edgeId), - })); - useHistoryStore.getState().pushState(prevState); - }, + })), upsertMany: (edges) => set((state) => { diff --git a/autogpt_platform/frontend/src/app/(platform)/build/stores/historyStore.ts b/autogpt_platform/frontend/src/app/(platform)/build/stores/historyStore.ts index 3a67bb8dcd..4eea5741a4 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/stores/historyStore.ts +++ b/autogpt_platform/frontend/src/app/(platform)/build/stores/historyStore.ts @@ -37,15 +37,6 @@ export const useHistoryStore = create((set, get) => ({ return; } - const actualCurrentState = { - nodes: useNodeStore.getState().nodes, - edges: useEdgeStore.getState().edges, - }; - - if (isEqual(state, actualCurrentState)) { - return; - } - set((prev) => ({ past: [...prev.past.slice(-MAX_HISTORY + 1), state], future: [], @@ -64,25 +55,18 @@ export const useHistoryStore = create((set, get) => ({ undo: () => { const { past, future } = get(); - if (past.length === 0) return; + if (past.length <= 1) return; - const actualCurrentState = { - nodes: useNodeStore.getState().nodes, - edges: useEdgeStore.getState().edges, - }; + const currentState = past[past.length - 1]; - const previousState = past[past.length - 1]; - - if (isEqual(actualCurrentState, previousState)) { - return; - } + const previousState = past[past.length - 2]; useNodeStore.getState().setNodes(previousState.nodes); useEdgeStore.getState().setEdges(previousState.edges); set({ - past: past.length > 1 ? past.slice(0, -1) : past, - future: [actualCurrentState, ...future], + past: past.slice(0, -1), + future: [currentState, ...future], }); }, @@ -90,36 +74,18 @@ export const useHistoryStore = create((set, get) => ({ const { past, future } = get(); if (future.length === 0) return; - const actualCurrentState = { - nodes: useNodeStore.getState().nodes, - edges: useEdgeStore.getState().edges, - }; - const nextState = future[0]; useNodeStore.getState().setNodes(nextState.nodes); useEdgeStore.getState().setEdges(nextState.edges); - const lastPast = past[past.length - 1]; - const shouldPushToPast = - !lastPast || !isEqual(actualCurrentState, lastPast); - set({ - past: shouldPushToPast ? [...past, actualCurrentState] : past, + past: [...past, nextState], future: future.slice(1), }); }, - canUndo: () => { - const { past } = get(); - if (past.length === 0) return false; - - const actualCurrentState = { - nodes: useNodeStore.getState().nodes, - edges: useEdgeStore.getState().edges, - }; - return !isEqual(actualCurrentState, past[past.length - 1]); - }, + canUndo: () => get().past.length > 1, canRedo: () => get().future.length > 0, clear: () => set({ past: [{ nodes: [], edges: [] }], future: [] }), diff --git a/autogpt_platform/frontend/src/app/(platform)/build/stores/nodeStore.ts b/autogpt_platform/frontend/src/app/(platform)/build/stores/nodeStore.ts index 5502a8780d..cb41da9463 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/stores/nodeStore.ts +++ b/autogpt_platform/frontend/src/app/(platform)/build/stores/nodeStore.ts @@ -1,7 +1,6 @@ import { create } from "zustand"; import { NodeChange, XYPosition, applyNodeChanges } from "@xyflow/react"; import { CustomNode } from "../components/FlowEditor/nodes/CustomNode/CustomNode"; -import { CustomEdge } from "../components/FlowEditor/edges/CustomEdge"; import { BlockInfo } from "@/app/api/__generated__/models/blockInfo"; import { convertBlockInfoIntoCustomNodeData, @@ -45,8 +44,6 @@ const MINIMUM_MOVE_BEFORE_LOG = 50; // Track initial positions when drag starts (outside store to avoid re-renders) const dragStartPositions: Record = {}; -let dragStartState: { nodes: CustomNode[]; edges: CustomEdge[] } | null = null; - type NodeStore = { nodes: CustomNode[]; nodeCounter: number; @@ -127,20 +124,14 @@ export const useNodeStore = create((set, get) => ({ nodeCounter: state.nodeCounter + 1, })), onNodesChange: (changes) => { + const prevState = { + nodes: get().nodes, + edges: useEdgeStore.getState().edges, + }; + + // Track initial positions when drag starts changes.forEach((change) => { if (change.type === "position" && change.dragging === true) { - if (!dragStartState) { - const currentNodes = get().nodes; - const currentEdges = useEdgeStore.getState().edges; - dragStartState = { - nodes: currentNodes.map((n) => ({ - ...n, - position: { ...n.position }, - data: { ...n.data }, - })), - edges: currentEdges.map((e) => ({ ...e })), - }; - } if (!dragStartPositions[change.id]) { const node = get().nodes.find((n) => n.id === change.id); if (node) { @@ -150,17 +141,12 @@ export const useNodeStore = create((set, get) => ({ } }); - let shouldTrack = changes.some((change) => change.type === "remove"); - let stateToTrack: { nodes: CustomNode[]; edges: CustomEdge[] } | null = - null; - - if (shouldTrack) { - stateToTrack = { - nodes: get().nodes, - edges: useEdgeStore.getState().edges, - }; - } + // Check if we should track this change in history + let shouldTrack = changes.some( + (change) => change.type === "remove" || change.type === "add", + ); + // For position changes, only track if movement exceeds threshold if (!shouldTrack) { changes.forEach((change) => { if (change.type === "position" && change.dragging === false) { @@ -172,23 +158,20 @@ export const useNodeStore = create((set, get) => ({ ); if (distanceMoved > MINIMUM_MOVE_BEFORE_LOG) { shouldTrack = true; - stateToTrack = dragStartState; } } + // Clean up tracked position after drag ends delete dragStartPositions[change.id]; } }); - if (Object.keys(dragStartPositions).length === 0) { - dragStartState = null; - } } set((state) => ({ nodes: applyNodeChanges(changes, state.nodes), })); - if (shouldTrack && stateToTrack) { - useHistoryStore.getState().pushState(stateToTrack); + if (shouldTrack) { + useHistoryStore.getState().pushState(prevState); } }, @@ -202,11 +185,6 @@ export const useNodeStore = create((set, get) => ({ hardcodedValues?: Record, position?: XYPosition, ) => { - const prevState = { - nodes: get().nodes, - edges: useEdgeStore.getState().edges, - }; - const customNodeData = convertBlockInfoIntoCustomNodeData( block, hardcodedValues, @@ -240,24 +218,21 @@ export const useNodeStore = create((set, get) => ({ set((state) => ({ nodes: [...state.nodes, customNode], })); - - useHistoryStore.getState().pushState(prevState); - return customNode; }, updateNodeData: (nodeId, data) => { - const prevState = { - nodes: get().nodes, - edges: useEdgeStore.getState().edges, - }; - set((state) => ({ nodes: state.nodes.map((n) => n.id === nodeId ? { ...n, data: { ...n.data, ...data } } : n, ), })); - useHistoryStore.getState().pushState(prevState); + const newState = { + nodes: get().nodes, + edges: useEdgeStore.getState().edges, + }; + + useHistoryStore.getState().pushState(newState); }, toggleAdvanced: (nodeId: string) => set((state) => ({ @@ -416,11 +391,6 @@ export const useNodeStore = create((set, get) => ({ }, setCredentialsOptional: (nodeId: string, optional: boolean) => { - const prevState = { - nodes: get().nodes, - edges: useEdgeStore.getState().edges, - }; - set((state) => ({ nodes: state.nodes.map((n) => n.id === nodeId @@ -438,7 +408,12 @@ export const useNodeStore = create((set, get) => ({ ), })); - useHistoryStore.getState().pushState(prevState); + const newState = { + nodes: get().nodes, + edges: useEdgeStore.getState().edges, + }; + + useHistoryStore.getState().pushState(newState); }, // Sub-agent resolution mode state diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/FormRenderer.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/FormRenderer.tsx index c3a20d8cd2..fc388cc343 100644 --- a/autogpt_platform/frontend/src/components/renderers/InputRenderer/FormRenderer.tsx +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/FormRenderer.tsx @@ -30,6 +30,8 @@ export const FormRenderer = ({ return generateUiSchemaForCustomFields(preprocessedSchema, uiSchema); }, [preprocessedSchema, uiSchema]); + console.log("preprocessedSchema", preprocessedSchema); + return (