From e982fe99aca034f00beb3d500fa190bbb09ddabc Mon Sep 17 00:00:00 2001 From: Reinier van der Leer Date: Wed, 26 Feb 2025 16:41:50 +0100 Subject: [PATCH] implement agent run "Delete" option --- .../backend/backend/data/execution.py | 31 +++++------------- .../backend/backend/data/graph.py | 1 + .../backend/backend/server/routers/v1.py | 15 +++++++++ .../src/app/library/agents/[id]/page.tsx | 30 +++++++++++------ .../agents/agent-run-summary-card.tsx | 32 +++++++------------ .../agents/agent-runs-selector-list.tsx | 17 +++++----- .../src/lib/autogpt-server-api/client.ts | 4 +++ 7 files changed, 70 insertions(+), 60 deletions(-) diff --git a/autogpt_platform/backend/backend/data/execution.py b/autogpt_platform/backend/backend/data/execution.py index bd6d79d47e..ddab2b185b 100644 --- a/autogpt_platform/backend/backend/data/execution.py +++ b/autogpt_platform/backend/backend/data/execution.py @@ -1,11 +1,10 @@ from collections import defaultdict from datetime import datetime, timezone from multiprocessing import Manager -from typing import Any, AsyncGenerator, Generator, Generic, Optional, Type, TypeVar +from typing import Any, AsyncGenerator, Generator, Generic, Type, TypeVar from prisma import Json from prisma.enums import AgentExecutionStatus -from prisma.errors import PrismaError from prisma.models import ( AgentGraphExecution, AgentNodeExecution, @@ -331,28 +330,14 @@ async def update_execution_status( return ExecutionResult.from_db(res) -async def get_execution( - execution_id: str, user_id: str -) -> Optional[AgentNodeExecution]: - """ - Get an execution by ID. Returns None if not found. - - Args: - execution_id: The ID of the execution to retrieve - - Returns: - The execution if found, None otherwise - """ - try: - execution = await AgentNodeExecution.prisma().find_unique( - where={ - "id": execution_id, - "userId": user_id, - } +async def delete_execution(graph_exec_id: str, user_id: str) -> None: + delete_count = await AgentGraphExecution.prisma().delete_many( + where={"id": graph_exec_id, "userId": user_id} + ) + if delete_count < 1: + raise DatabaseError( + f"Could not delete graph execution #{graph_exec_id}: not found" ) - return execution - except PrismaError: - return None async def get_execution_results(graph_exec_id: str) -> list[ExecutionResult]: diff --git a/autogpt_platform/backend/backend/data/graph.py b/autogpt_platform/backend/backend/data/graph.py index 25d574204b..7d3bcddf83 100644 --- a/autogpt_platform/backend/backend/data/graph.py +++ b/autogpt_platform/backend/backend/data/graph.py @@ -591,6 +591,7 @@ async def get_graphs( return graph_models +# TODO: move execution stuff to .execution async def get_graphs_executions(user_id: str) -> list[GraphExecutionMeta]: executions = await AgentGraphExecution.prisma().find_many( where={"userId": user_id}, diff --git a/autogpt_platform/backend/backend/server/routers/v1.py b/autogpt_platform/backend/backend/server/routers/v1.py index 5e0eeea0ec..c649d9f6c2 100644 --- a/autogpt_platform/backend/backend/server/routers/v1.py +++ b/autogpt_platform/backend/backend/server/routers/v1.py @@ -10,12 +10,14 @@ from autogpt_libs.auth.middleware import auth_middleware from autogpt_libs.feature_flag.client import feature_flag from autogpt_libs.utils.cache import thread_cached from fastapi import APIRouter, Body, Depends, HTTPException, Request, Response +from starlette.status import HTTP_204_NO_CONTENT from typing_extensions import Optional, TypedDict import backend.data.block import backend.server.integrations.router import backend.server.routers.analytics import backend.server.v2.library.db as library_db +from backend.data import execution as execution_db from backend.data import graph as graph_db from backend.data.api_key import ( APIKeyError, @@ -630,6 +632,19 @@ async def get_graph_execution( return result +@v1_router.delete( + path="/executions/{graph_exec_id}", + tags=["graphs"], + dependencies=[Depends(auth_middleware)], + status_code=HTTP_204_NO_CONTENT, +) +async def delete_graph_execution( + graph_exec_id: str, + user_id: Annotated[str, Depends(get_user_id)], +) -> None: + await execution_db.delete_execution(graph_exec_id=graph_exec_id, user_id=user_id) + + ######################################################## ##################### Schedules ######################## ######################################################## diff --git a/autogpt_platform/frontend/src/app/library/agents/[id]/page.tsx b/autogpt_platform/frontend/src/app/library/agents/[id]/page.tsx index 5435b88a5c..018fb97087 100644 --- a/autogpt_platform/frontend/src/app/library/agents/[id]/page.tsx +++ b/autogpt_platform/frontend/src/app/library/agents/[id]/page.tsx @@ -98,20 +98,30 @@ export default function AgentRunsPage(): React.ReactElement { fetchSchedules(); }, [fetchSchedules]); - const removeSchedule = useCallback( - async (scheduleId: string) => { - const removedSchedule = await api.deleteSchedule(scheduleId); - setSchedules(schedules.filter((s) => s.id !== removedSchedule.id)); - }, - [schedules, api], - ); - /* TODO: use websockets instead of polling - https://github.com/Significant-Gravitas/AutoGPT/issues/8782 */ useEffect(() => { const intervalId = setInterval(() => fetchAgents(), 5000); return () => clearInterval(intervalId); }, [fetchAgents, agent]); + // =========================== ACTIONS ============================ + + const deleteRun = useCallback( + async (graphExecID: string) => { + await api.deleteGraphExecution(graphExecID); + setAgentRuns(agentRuns.filter((r) => r.execution_id !== graphExecID)); + }, + [agentRuns, api], + ); + + const deleteSchedule = useCallback( + async (scheduleID: string) => { + const removedSchedule = await api.deleteSchedule(scheduleID); + setSchedules(schedules.filter((s) => s.id !== removedSchedule.id)); + }, + [schedules, api], + ); + const agentActions: { label: string; callback: () => void }[] = useMemo( () => [ { @@ -139,7 +149,9 @@ export default function AgentRunsPage(): React.ReactElement { selectedView={selectedView} onSelectRun={selectRun} onSelectSchedule={selectSchedule} - onDraftNewRun={openRunDraftView} + onSelectDraftNewRun={openRunDraftView} + onDeleteRun={(id) => deleteRun(id)} + onDeleteSchedule={(id) => deleteSchedule(id)} />
diff --git a/autogpt_platform/frontend/src/components/agents/agent-run-summary-card.tsx b/autogpt_platform/frontend/src/components/agents/agent-run-summary-card.tsx index 7336835438..912569a8cf 100644 --- a/autogpt_platform/frontend/src/components/agents/agent-run-summary-card.tsx +++ b/autogpt_platform/frontend/src/components/agents/agent-run-summary-card.tsx @@ -18,24 +18,24 @@ import AgentRunStatusChip, { } from "@/components/agents/agent-run-status-chip"; export type AgentRunSummaryProps = { - agentID: string; - agentRunID: string; status: AgentRunStatus; title: string; timestamp: number | Date; selected?: boolean; onClick?: () => void; + // onRename: () => void; + onDelete: () => void; className?: string; }; export default function AgentRunSummaryCard({ - agentID, - agentRunID, status, title, timestamp, selected = false, onClick, + // onRename, + onDelete, className, }: AgentRunSummaryProps): React.ReactElement { return ( @@ -55,32 +55,24 @@ export default function AgentRunSummaryCard({ {title} - {/* + - - Pin into a template + {/* {onPinAsPreset && ( + + Pin as a preset + )} */} - - Rename - + {/* Rename */} - - Delete - + Delete - */} +

void; onSelectSchedule: (schedule: Schedule) => void; - onDraftNewRun: () => void; + onSelectDraftNewRun: () => void; + onDeleteSchedule: (id: string) => void; className?: string; } @@ -34,7 +35,9 @@ export default function AgentRunsSelectorList({ selectedView, onSelectRun, onSelectSchedule, - onDraftNewRun, + onSelectDraftNewRun, + onDeleteRun, + onDeleteSchedule, className, }: AgentRunsSelectorListProps): React.ReactElement { const [activeListTab, setActiveListTab] = useState<"runs" | "scheduled">( @@ -51,7 +54,7 @@ export default function AgentRunsSelectorList({ ? "agpt-card-selected text-accent" : "") } - onClick={onDraftNewRun} + onClick={onSelectDraftNewRun} > New run @@ -91,7 +94,7 @@ export default function AgentRunsSelectorList({ ? "agpt-card-selected text-accent" : "") } - onClick={onDraftNewRun} + onClick={onSelectDraftNewRun} > New run @@ -102,13 +105,12 @@ export default function AgentRunsSelectorList({ onSelectRun(run.execution_id)} + onDelete={() => onDeleteRun(run.execution_id)} /> )) : schedules @@ -117,13 +119,12 @@ export default function AgentRunsSelectorList({ onSelectSchedule(schedule)} + onDelete={() => onDeleteSchedule(schedule.id)} /> ))} diff --git a/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts b/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts index 37b1a2975e..b81f1141f0 100644 --- a/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts +++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts @@ -246,6 +246,10 @@ export default class BackendAPI { return result; } + async deleteGraphExecution(runID: string): Promise { + await this._request("DELETE", `/executions/${runID}`); + } + oAuthLogin( provider: string, scopes?: string[],