diff --git a/autogpt_platform/frontend/src/components/RunnerUIWrapper.tsx b/autogpt_platform/frontend/src/components/RunnerUIWrapper.tsx index 6c3a117a53be..8a2b7f798f1c 100644 --- a/autogpt_platform/frontend/src/components/RunnerUIWrapper.tsx +++ b/autogpt_platform/frontend/src/components/RunnerUIWrapper.tsx @@ -89,7 +89,6 @@ const RunnerUIWrapper = forwardRef( const outputs = outputBlocks.map((node) => ({ id: node.id, type: "output" as const, - outputSchema: node.data.outputSchema as BlockIORootSchema, hardcodedValues: { name: (node.data.hardcodedValues as any).name || "Output", description: diff --git a/autogpt_platform/frontend/src/components/edit/control/BlocksControl.tsx b/autogpt_platform/frontend/src/components/edit/control/BlocksControl.tsx index fcd21c316a11..6a19771aed53 100644 --- a/autogpt_platform/frontend/src/components/edit/control/BlocksControl.tsx +++ b/autogpt_platform/frontend/src/components/edit/control/BlocksControl.tsx @@ -10,7 +10,7 @@ import { PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; -import { Block, BlockUIType } from "@/lib/autogpt-server-api"; +import { Block, BlockUIType, SpecialBlockID } from "@/lib/autogpt-server-api"; import { MagnifyingGlassIcon, PlusIcon } from "@radix-ui/react-icons"; import { IconToyBrick } from "@/components/ui/icons"; import { getPrimaryCategoryColor } from "@/lib/utils"; @@ -57,7 +57,7 @@ export const BlocksControl: React.FC = ({ const agentList = flows.map( (flow) => ({ - id: "e189baac-8c20-45a1-94a7-55177ea42565", // TODO: fetch this programmatically. + id: SpecialBlockID.AGENT, name: flow.name, description: `Ver.${flow.version}` + diff --git a/autogpt_platform/frontend/src/components/monitor/FlowRunInfo.tsx b/autogpt_platform/frontend/src/components/monitor/FlowRunInfo.tsx index f866feda5f77..b98c2c68b7df 100644 --- a/autogpt_platform/frontend/src/components/monitor/FlowRunInfo.tsx +++ b/autogpt_platform/frontend/src/components/monitor/FlowRunInfo.tsx @@ -1,13 +1,20 @@ -import React, { useCallback } from "react"; -import AutoGPTServerAPI, { GraphMeta } from "@/lib/autogpt-server-api"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; +import AutoGPTServerAPI, { + BlockIORootSchema, + Graph, + GraphMeta, + NodeExecutionResult, + SpecialBlockID, +} from "@/lib/autogpt-server-api"; import { FlowRun } from "@/lib/types"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import Link from "next/link"; import { Button, buttonVariants } from "@/components/ui/button"; import { IconSquare } from "@/components/ui/icons"; -import { Pencil2Icon } from "@radix-ui/react-icons"; +import { ExitIcon, Pencil2Icon } from "@radix-ui/react-icons"; import moment from "moment/moment"; import { FlowRunStatusBadge } from "@/components/monitor/FlowRunStatusBadge"; +import RunnerOutputUI, { BlockOutput } from "../runner-ui/RunnerOutputUI"; export const FlowRunInfo: React.FC< React.HTMLAttributes & { @@ -15,6 +22,66 @@ export const FlowRunInfo: React.FC< flowRun: FlowRun; } > = ({ flow, flowRun, ...props }) => { + const [isOutputOpen, setIsOutputOpen] = useState(false); + const [blockOutputs, setBlockOutputs] = useState([]); + const api = useMemo(() => new AutoGPTServerAPI(), []); + + const fetchBlockResults = useCallback(async () => { + const executionResults = await api.getGraphExecutionInfo( + flow.id, + flowRun.id, + ); + + // Create a map of the latest COMPLETED execution results of output nodes by node_id + const latestCompletedResults = executionResults + .filter( + (result) => + result.status === "COMPLETED" && + result.block_id === SpecialBlockID.OUTPUT, + ) + .reduce((acc, result) => { + const existing = acc.get(result.node_id); + + // Compare dates if there's an existing result + if (existing) { + const existingDate = existing.end_time || existing.add_time; + const currentDate = result.end_time || result.add_time; + + if (currentDate > existingDate) { + acc.set(result.node_id, result); + } + } else { + acc.set(result.node_id, result); + } + + return acc; + }, new Map()); + + // Transform results to BlockOutput format + setBlockOutputs( + Array.from(latestCompletedResults.values()).map((result) => ({ + id: result.node_id, + type: "output" as const, + hardcodedValues: { + name: result.input_data.name || "Output", + description: result.input_data.description || "Output from the agent", + value: result.input_data.value, + }, + // Change this line to extract the array directly + result: result.output_data?.output || undefined, + })), + ); + }, [api, flow.id, flow.version, flowRun.id]); + + // Fetch graph and execution data + useEffect(() => { + if (!isOutputOpen || blockOutputs.length > 0) { + return; + } + + fetchBlockResults(); + }, [isOutputOpen, blockOutputs]); + if (flowRun.graphID != flow.id) { throw new Error( `FlowRunInfo can't be used with non-matching flowRun.flowID and flow.id`, @@ -22,58 +89,67 @@ export const FlowRunInfo: React.FC< } const handleStopRun = useCallback(() => { - const api = new AutoGPTServerAPI(); api.stopGraphExecution(flow.id, flowRun.id); }, [flow.id, flowRun.id]); return ( - - -
- - {flow.name} v{flow.version} - -

- Agent ID: {flow.id} + <> + + +

+ + {flow.name} v{flow.version} + +

+ Agent ID: {flow.id} +

+

+ Run ID: {flowRun.id} +

+
+
+ {flowRun.status === "running" && ( + + )} + + + Open in Builder + +
+ + +
+ Status:{" "} + +
+

+ Started:{" "} + {moment(flowRun.startTime).format("YYYY-MM-DD HH:mm:ss")}

-

- Run ID: {flowRun.id} +

+ Finished:{" "} + {moment(flowRun.endTime).format("YYYY-MM-DD HH:mm:ss")}

-
-
- {flowRun.status === "running" && ( - - )} - - Open in Builder - -
-
- -
- Status:{" "} - -
-

- Started:{" "} - {moment(flowRun.startTime).format("YYYY-MM-DD HH:mm:ss")} -

-

- Finished:{" "} - {moment(flowRun.endTime).format("YYYY-MM-DD HH:mm:ss")} -

-

- Duration (run time): {flowRun.duration} ( - {flowRun.totalRunTime}) seconds -

- {/*

Total cost: €1,23

*/} -
-
+

+ Duration (run time): {flowRun.duration} ( + {flowRun.totalRunTime}) seconds +

+ + + setIsOutputOpen(false)} + blockOutputs={blockOutputs} + /> + ); }; + export default FlowRunInfo; diff --git a/autogpt_platform/frontend/src/components/runner-ui/RunnerOutputUI.tsx b/autogpt_platform/frontend/src/components/runner-ui/RunnerOutputUI.tsx index 8e39eb3d4863..4751e151ea86 100644 --- a/autogpt_platform/frontend/src/components/runner-ui/RunnerOutputUI.tsx +++ b/autogpt_platform/frontend/src/components/runner-ui/RunnerOutputUI.tsx @@ -11,9 +11,8 @@ import { BlockIORootSchema } from "@/lib/autogpt-server-api/types"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; -interface BlockOutput { +export interface BlockOutput { id: string; - outputSchema: BlockIORootSchema; hardcodedValues: { name: string; description: string; diff --git a/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts b/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts index 47ea96a293df..0794cc8611c6 100644 --- a/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts +++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts @@ -241,11 +241,12 @@ export type GraphExecuteResponse = { /* Mirror of backend/data/execution.py:ExecutionResult */ export type NodeExecutionResult = { - graph_exec_id: string; - node_exec_id: string; graph_id: string; graph_version: number; + graph_exec_id: string; + node_exec_id: string; node_id: string; + block_id: string; status: "INCOMPLETE" | "QUEUED" | "RUNNING" | "COMPLETED" | "FAILED"; input_data: { [key: string]: any }; output_data: { [key: string]: Array }; @@ -319,6 +320,12 @@ export enum BlockUIType { AGENT = "Agent", } +export enum SpecialBlockID { + AGENT = "e189baac-8c20-45a1-94a7-55177ea42565", + INPUT = "c0a8e994-ebf1-4a9c-a4d8-89d09c86741b", + OUTPUT = "363ae599-353e-4804-937e-b2ee3cef3da4", +} + export type AnalyticsMetrics = { metric_name: string; metric_value: number;