Skip to content

Commit 29de134

Browse files
majdyzclaude
andcommitted
refactor(frontend): implement reviewer feedback - improve HITL architecture
Address reviewer suggestions for cleaner architecture: **GraphStore Improvements:** - Move graph execution status to centralized graphStore - Derive isGraphRunning automatically from executionStatus - Use consistent useQueryStates pattern instead of useSearchParams - Remove prop drilling by letting components read from store directly **Loading State Consistency:** - Add missing isPending state to stopGraph operation in new builder - New builder now has consistent loading states for both start/stop actions - Matches behavior with new library page (both have proper loading feedback) **Architecture Clean-up:** - New builder: Uses graphStore for execution state ✓ - Legacy builder: Keeps existing local state approach ✓ - No mixed/duplicate state tracking between builders - FloatingReviewsPanel reads execution status from store **Benefits:** - Zero prop drilling for execution status - Consistent query state patterns across codebase - Better UX with proper stop operation loading feedback - Cleaner separation between new and legacy architectures - Reusable execution state for future components 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 0422173 commit 29de134

File tree

9 files changed

+50
-80
lines changed

9 files changed

+50
-80
lines changed

autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunGraph/RunGraph.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const RunGraph = ({ flowID }: { flowID: string | null }) => {
1818
openRunInputDialog,
1919
setOpenRunInputDialog,
2020
isExecutingGraph,
21+
isTerminatingGraph,
2122
isSaving,
2223
} = useRunGraph();
2324
const isGraphRunning = useGraphStore(
@@ -34,8 +35,8 @@ export const RunGraph = ({ flowID }: { flowID: string | null }) => {
3435
"border-red-500 bg-gradient-to-br from-red-400 to-red-500 shadow-[inset_0_2px_0_0_rgba(255,255,255,0.5),0_2px_4px_0_rgba(0,0,0,0.2)]",
3536
)}
3637
onClick={isGraphRunning ? handleStopGraph : handleRunGraph}
37-
disabled={!flowID || isExecutingGraph}
38-
isLoading={isExecutingGraph || isSaving}
38+
disabled={!flowID || isExecutingGraph || isTerminatingGraph}
39+
isLoading={isExecutingGraph || isTerminatingGraph || isSaving}
3940
>
4041
{!isGraphRunning ? (
4142
<PlayIcon className="size-6 drop-shadow-sm" />

autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunGraph/useRunGraph.ts

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ export const useRunGraph = () => {
1515
showToast: false,
1616
});
1717
const { toast } = useToast();
18-
const setIsGraphRunning = useGraphStore(
19-
useShallow((state) => state.setIsGraphRunning),
20-
);
2118
const hasInputs = useGraphStore(useShallow((state) => state.hasInputs));
2219
const hasCredentials = useGraphStore(
2320
useShallow((state) => state.hasCredentials),
@@ -34,15 +31,13 @@ export const useRunGraph = () => {
3431
const { mutateAsync: executeGraph, isPending: isExecutingGraph } =
3532
usePostV1ExecuteGraphAgent({
3633
mutation: {
37-
onSuccess: (response) => {
34+
onSuccess: (response: any) => {
3835
const { id } = response.data as GraphExecutionMeta;
3936
setQueryStates({
4037
flowExecutionID: id,
4138
});
4239
},
43-
onError: (error) => {
44-
setIsGraphRunning(false);
45-
40+
onError: (error: any) => {
4641
toast({
4742
title: (error.detail as string) ?? "An unexpected error occurred.",
4843
description: "An unexpected error occurred.",
@@ -52,20 +47,19 @@ export const useRunGraph = () => {
5247
},
5348
});
5449

55-
const { mutateAsync: stopGraph } = usePostV1StopGraphExecution({
56-
mutation: {
57-
onSuccess: () => {
58-
setIsGraphRunning(false);
59-
},
60-
onError: (error) => {
61-
toast({
62-
title: (error.detail as string) ?? "An unexpected error occurred.",
63-
description: "An unexpected error occurred.",
64-
variant: "destructive",
65-
});
50+
const { mutateAsync: stopGraph, isPending: isTerminatingGraph } =
51+
usePostV1StopGraphExecution({
52+
mutation: {
53+
onSuccess: () => {},
54+
onError: (error: any) => {
55+
toast({
56+
title: (error.detail as string) ?? "An unexpected error occurred.",
57+
description: "An unexpected error occurred.",
58+
variant: "destructive",
59+
});
60+
},
6661
},
67-
},
68-
});
62+
});
6963

7064
const handleRunGraph = async () => {
7165
await saveGraph(undefined);
@@ -96,6 +90,7 @@ export const useRunGraph = () => {
9690
handleStopGraph,
9791
isSaving,
9892
isExecutingGraph,
93+
isTerminatingGraph,
9994
openRunInputDialog,
10095
setOpenRunInputDialog,
10196
};

autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunInputDialog/useRunInputDialog.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,6 @@ export const useRunInputDialog = ({
3030
flowID: parseAsString,
3131
flowVersion: parseAsInteger,
3232
});
33-
const setIsGraphRunning = useGraphStore(
34-
useShallow((state) => state.setIsGraphRunning),
35-
);
3633
const { toast } = useToast();
3734

3835
const { mutateAsync: executeGraph, isPending: isExecutingGraph } =
@@ -45,8 +42,6 @@ export const useRunInputDialog = ({
4542
});
4643
},
4744
onError: (error) => {
48-
setIsGraphRunning(false);
49-
5045
toast({
5146
title: (error.detail as string) ?? "An unexpected error occurred.",
5247
description: "An unexpected error occurred.",

autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/Flow.tsx

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ import { RunningBackground } from "./components/RunningBackground";
1414
import { useGraphStore } from "../../../stores/graphStore";
1515
import { useCopyPaste } from "./useCopyPaste";
1616
import { FloatingReviewsPanel } from "@/components/organisms/FloatingReviewsPanel/FloatingReviewsPanel";
17-
import { useSearchParams } from "next/navigation";
17+
import { parseAsString, useQueryStates } from "nuqs";
1818
import { CustomControls } from "./components/CustomControl";
1919

2020
export const Flow = () => {
21-
const searchParams = useSearchParams();
22-
const flowExecutionID = searchParams.get("flowExecutionID") || undefined;
23-
const graphId = searchParams.get("flowID") || undefined;
21+
const [{ flowID, flowExecutionID }] = useQueryStates({
22+
flowID: parseAsString,
23+
flowExecutionID: parseAsString,
24+
});
2425

2526
const nodes = useNodeStore(useShallow((state) => state.nodes));
2627
const onNodesChange = useNodeStore(
@@ -31,17 +32,11 @@ export const Flow = () => {
3132
const { edges, onConnect, onEdgesChange } = useCustomEdge();
3233

3334
// We use this hook to load the graph and convert them into custom nodes and edges.
34-
const {
35-
onDragOver,
36-
onDrop,
37-
isFlowContentLoading,
38-
isLocked,
39-
setIsLocked,
40-
executionStatus: initialExecutionStatus,
41-
} = useFlow();
35+
const { onDragOver, onDrop, isFlowContentLoading, isLocked, setIsLocked } =
36+
useFlow();
4237

4338
// This hook is used for websocket realtime updates.
44-
const { executionStatus: realtimeExecutionStatus } = useFlowRealtime();
39+
useFlowRealtime();
4540

4641
// Copy/paste functionality
4742
const handleCopyPaste = useCopyPaste();
@@ -56,7 +51,9 @@ export const Flow = () => {
5651
window.removeEventListener("keydown", handleKeyDown);
5752
};
5853
}, [handleCopyPaste]);
59-
const { isGraphRunning } = useGraphStore();
54+
const isGraphRunning = useGraphStore(
55+
useShallow((state) => state.isGraphRunning),
56+
);
6057
return (
6158
<div className="flex h-full w-full dark:bg-slate-900">
6259
<div className="relative flex-1">
@@ -84,11 +81,7 @@ export const Flow = () => {
8481
{isGraphRunning && <RunningBackground />}
8582
</ReactFlow>
8683
</div>
87-
<FloatingReviewsPanel
88-
executionId={flowExecutionID}
89-
graphId={graphId}
90-
executionStatus={realtimeExecutionStatus ?? initialExecutionStatus}
91-
/>
84+
<FloatingReviewsPanel executionId={flowExecutionID || undefined} />
9285
</div>
9386
);
9487
};

autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ export const useFlow = () => {
2828
const updateNodeExecutionResult = useNodeStore(
2929
useShallow((state) => state.updateNodeExecutionResult),
3030
);
31-
const setIsGraphRunning = useGraphStore(
32-
useShallow((state) => state.setIsGraphRunning),
33-
);
3431
const setGraphSchemas = useGraphStore(
3532
useShallow((state) => state.setGraphSchemas),
3633
);
@@ -126,15 +123,6 @@ export const useFlow = () => {
126123
}
127124
}, [graph?.links, addLinks]);
128125

129-
// update graph running status
130-
useEffect(() => {
131-
const isRunning =
132-
executionDetails?.status === AgentExecutionStatus.RUNNING ||
133-
executionDetails?.status === AgentExecutionStatus.QUEUED;
134-
135-
setIsGraphRunning(isRunning);
136-
}, [executionDetails?.status, customNodes]);
137-
138126
// update node execution status in nodes
139127
useEffect(() => {
140128
if (
@@ -182,7 +170,6 @@ export const useFlow = () => {
182170
useEdgeStore.getState().setEdges([]);
183171
useGraphStore.getState().reset();
184172
useEdgeStore.getState().resetEdgeBeads();
185-
setIsGraphRunning(false);
186173
};
187174
}, []);
188175

@@ -228,6 +215,5 @@ export const useFlow = () => {
228215
onDrop,
229216
isLocked,
230217
setIsLocked,
231-
executionStatus: executionDetails?.status,
232218
};
233219
};

autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlowRealtime.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// In this hook, I am only keeping websocket related code.
22

3-
import { useState } from "react";
43
import { GraphExecutionID } from "@/lib/autogpt-server-api";
54
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
65
import { parseAsString, useQueryStates } from "nuqs";
@@ -14,17 +13,14 @@ import { useEdgeStore } from "../../../stores/edgeStore";
1413

1514
export const useFlowRealtime = () => {
1615
const api = useBackendAPI();
17-
const [executionStatus, setExecutionStatus] = useState<
18-
AgentExecutionStatus | undefined
19-
>(undefined);
2016
const updateNodeExecutionResult = useNodeStore(
2117
useShallow((state) => state.updateNodeExecutionResult),
2218
);
2319
const updateStatus = useNodeStore(
2420
useShallow((state) => state.updateNodeStatus),
2521
);
26-
const setIsGraphRunning = useGraphStore(
27-
useShallow((state) => state.setIsGraphRunning),
22+
const setGraphExecutionStatus = useGraphStore(
23+
useShallow((state) => state.setGraphExecutionStatus),
2824
);
2925
const updateEdgeBeads = useEdgeStore(
3026
useShallow((state) => state.updateEdgeBeads),
@@ -61,12 +57,7 @@ export const useFlowRealtime = () => {
6157
return;
6258
}
6359

64-
const isRunning =
65-
graphExecution.status === AgentExecutionStatus.RUNNING ||
66-
graphExecution.status === AgentExecutionStatus.QUEUED;
67-
68-
setIsGraphRunning(isRunning);
69-
setExecutionStatus(graphExecution.status as AgentExecutionStatus);
60+
setGraphExecutionStatus(graphExecution.status as AgentExecutionStatus);
7061
},
7162
);
7263

@@ -98,5 +89,5 @@ export const useFlowRealtime = () => {
9889
};
9990
}, [api, flowExecutionID, resetEdgeBeads]);
10091

101-
return { executionStatus };
92+
return {};
10293
};

autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/Flow/Flow.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,8 +1029,6 @@ const FlowEditor: React.FC<{
10291029
)}
10301030
<FloatingReviewsPanel
10311031
executionId={flowExecutionID || undefined}
1032-
graphId={flowID || undefined}
1033-
executionStatus={graphExecutionStatus as AgentExecutionStatus}
10341032
className="fixed bottom-24 right-4"
10351033
/>
10361034
<Suspense fallback={null}>

autogpt_platform/frontend/src/app/(platform)/build/stores/graphStore.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { create } from "zustand";
2+
import { AgentExecutionStatus } from "@/app/api/__generated__/models/agentExecutionStatus";
23

34
interface GraphStore {
5+
graphExecutionStatus: AgentExecutionStatus | undefined;
46
isGraphRunning: boolean;
5-
setIsGraphRunning: (isGraphRunning: boolean) => void;
7+
setGraphExecutionStatus: (status: AgentExecutionStatus | undefined) => void;
68

79
inputSchema: Record<string, any> | null;
810
credentialsInputSchema: Record<string, any> | null;
@@ -21,12 +23,20 @@ interface GraphStore {
2123
}
2224

2325
export const useGraphStore = create<GraphStore>((set, get) => ({
26+
graphExecutionStatus: undefined,
2427
isGraphRunning: false,
2528
inputSchema: null,
2629
credentialsInputSchema: null,
2730
outputSchema: null,
2831

29-
setIsGraphRunning: (isGraphRunning: boolean) => set({ isGraphRunning }),
32+
setGraphExecutionStatus: (status: AgentExecutionStatus | undefined) => {
33+
set({
34+
graphExecutionStatus: status,
35+
isGraphRunning:
36+
status === AgentExecutionStatus.RUNNING ||
37+
status === AgentExecutionStatus.QUEUED,
38+
});
39+
},
3040

3141
setGraphSchemas: (inputSchema, credentialsInputSchema, outputSchema) =>
3242
set({ inputSchema, credentialsInputSchema, outputSchema }),
@@ -48,6 +58,7 @@ export const useGraphStore = create<GraphStore>((set, get) => ({
4858

4959
reset: () =>
5060
set({
61+
graphExecutionStatus: undefined,
5162
isGraphRunning: false,
5263
inputSchema: null,
5364
credentialsInputSchema: null,

autogpt_platform/frontend/src/components/organisms/FloatingReviewsPanel/FloatingReviewsPanel.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@ import { ClockIcon, XIcon } from "@phosphor-icons/react";
66
import { cn } from "@/lib/utils";
77
import { Text } from "@/components/atoms/Text/Text";
88
import { AgentExecutionStatus } from "@/app/api/__generated__/models/agentExecutionStatus";
9+
import { useGraphStore } from "@/app/(platform)/build/stores/graphStore";
910

1011
interface FloatingReviewsPanelProps {
1112
executionId?: string;
12-
graphId?: string;
13-
executionStatus?: AgentExecutionStatus;
1413
className?: string;
1514
}
1615

1716
export function FloatingReviewsPanel({
1817
executionId,
19-
executionStatus,
2018
className,
2119
}: FloatingReviewsPanelProps) {
2220
const [isOpen, setIsOpen] = useState(false);
2321

22+
const executionStatus = useGraphStore((state) => state.graphExecutionStatus);
23+
2424
const { pendingReviews, isLoading, refetch } = usePendingReviewsForExecution(
2525
executionId || "",
2626
);

0 commit comments

Comments
 (0)