diff --git a/.dockerignore b/.dockerignore index bc751a07a0ec..f52cb8bc8d53 100644 --- a/.dockerignore +++ b/.dockerignore @@ -37,3 +37,4 @@ rnd/autogpt_builder/.env.local rnd/autogpt_server/.env rnd/autogpt_server/.venv/ +rnd/market/.env diff --git a/.github/workflows/autogpt-server-ci.yml b/.github/workflows/autogpt-server-ci.yml index f71f1a8aeef6..6c78301fda16 100644 --- a/.github/workflows/autogpt-server-ci.yml +++ b/.github/workflows/autogpt-server-ci.yml @@ -128,9 +128,14 @@ jobs: - name: Run pytest with coverage run: | - poetry run pytest -vv \ - test + if [[ "${{ runner.debug }}" == "1" ]]; then + poetry run pytest -vv -o log_cli=true -o log_cli_level=DEBUG test + else + poetry run pytest -vv test + fi if: success() || (failure() && steps.lint.outcome == 'failure') + env: + LOG_LEVEL: ${{ runner.debug && 'DEBUG' || 'INFO' }} env: CI: true PLAIN_OUTPUT: True diff --git a/docs/content/forge/components/built-in-components.md b/docs/content/forge/components/built-in-components.md index 39f607a21212..f174769b8092 100644 --- a/docs/content/forge/components/built-in-components.md +++ b/docs/content/forge/components/built-in-components.md @@ -69,6 +69,8 @@ Lets the agent execute non-interactive Shell commands and Python code. Python ex | `shell_denylist` | List of prohibited shell commands | `List[str]` | `[]` | | `docker_container_name` | Name of the Docker container used for code execution | `str` | `"agent_sandbox"` | +All shell command configurations are expected to be for convience only. This component is not secure and should not be used in production environments. It is recommended to use more appropriate sandboxing. + ### CommandProvider - `execute_shell` execute shell command diff --git a/docs/content/server/setup.md b/docs/content/server/setup.md index 8fe31bc13935..2e624c05f27c 100644 --- a/docs/content/server/setup.md +++ b/docs/content/server/setup.md @@ -73,6 +73,7 @@ Once you have installed Yarn and Poetry, you can run the following command to in ```bash cd rnd/autogpt_server +cp .env.example .env poetry install ``` @@ -90,7 +91,7 @@ Once you have installed the dependencies, you can proceed to the next step. In order to setup the database, you need to run the following commands, in the same terminal you ran the `poetry install` command: ```sh - docker compose up postgres -d + docker compose up postgres redis -d poetry run prisma migrate dev ``` After deploying the migration, to ensure that the database schema is correctly mapped to your codebase, allowing the application to interact with the database properly, you need to generate the Prisma database model: @@ -101,7 +102,15 @@ poetry run prisma generate Without running this command, the necessary Python modules (prisma.models) won't be available, leading to a `ModuleNotFoundError`. -### Running the server +### Running the server without Docker + +To run the server, you can run the following commands in the same terminal you ran the `poetry install` command: + +```bash +poetry run app +``` + +### Running the server within Docker To run the server, you can run the following commands in the same terminal you ran the `poetry install` command: @@ -110,7 +119,7 @@ docker compose build docker compose up ``` -In the other terminal, you can run the following command to start the frontend: +In the other terminal from autogpt_builder, you can run the following command to start the frontend: ```bash yarn dev @@ -119,3 +128,10 @@ yarn dev ### Checking if the server is running You can check if the server is running by visiting [http://localhost:3000](http://localhost:3000) in your browser. + +### Notes: +By default the daemons for different services run on the following ports: + +Execution Manager Daemon: 8002 +Execution Scheduler Daemon: 8003 +Rest Server Daemon: 8004 diff --git a/rnd/README.md b/rnd/README.md index fcc25ce60a72..607d1415c2e6 100644 --- a/rnd/README.md +++ b/rnd/README.md @@ -1,36 +1,114 @@ -This is a guide to setting up and running the AutoGPT Server and Builder. This tutorial will cover downloading the necessary files, setting up the server, and testing the system. - -https://github.com/user-attachments/assets/fd0d0f35-3155-4263-b575-ba3efb126cb4 - -1. Navigate to the AutoGPT GitHub repository. -2. Click the "Code" button, then select "Download ZIP". -3. Once downloaded, extract the ZIP file to a folder of your choice. - -4. Open the extracted folder and navigate to the "rnd" directory. -5. Enter the "AutoGPT server" folder. -6. Open a terminal window in this directory. -7. Locate and open the README file in the AutoGPT server folder: [doc](./autogpt_server/README.md#setup). -8. Copy and paste each command from the setup section in the README into your terminal. - - Important: Wait for each command to finish before running the next one. -9. If all commands run without errors, enter the final command: `poetry run app` -10. You should now see the server running in your terminal. - -11. Navigate back to the "rnd" folder. -12. Open the "AutoGPT builder" folder. -13. Open the README file in this folder: [doc](./autogpt_builder/README.md#getting-started). -14. In your terminal, run the following commands: - ``` - npm install - ``` - ``` - npm run dev - ``` - -15. Once the front-end is running, click the link to navigate to `localhost:3000`. -16. Click on the "Build" option. -17. Add a few blocks to test the functionality. -18. Connect the blocks together. -19. Click "Run". -20. Check your terminal window - you should see that the server has received the request, is processing it, and has executed it. - -And there you have it! You've successfully set up and tested AutoGPT. +# AutoGPT Platform + +Welcome to the AutoGPT Platform - a powerful system for creating and running AI agents to solve business problems. This platform enables you to harness the power of artificial intelligence to automate tasks, analyze data, and generate insights for your organization. + +## Getting Started + +### Prerequisites + +- Docker +- Docker Compose V2 (comes with Docker Desktop, or can be installed separately) + +### Running the System + +To run the AutoGPT Platform, follow these steps: + +1. Clone this repository to your local machine. +2. Navigate to the project directory. +3. Run the following command: + + ``` + docker compose up -d + ``` + + This command will start all the necessary services defined in the `docker-compose.yml` file in detached mode. + +### Docker Compose Commands + +Here are some useful Docker Compose commands for managing your AutoGPT Platform: + +- `docker compose up -d`: Start the services in detached mode. +- `docker compose stop`: Stop the running services without removing them. +- `docker compose rm`: Remove stopped service containers. +- `docker compose build`: Build or rebuild services. +- `docker compose down`: Stop and remove containers, networks, and volumes. +- `docker compose watch`: Watch for changes in your services and automatically update them. + + +### Sample Scenarios + +Here are some common scenarios where you might use multiple Docker Compose commands: + +1. Updating and restarting a specific service: + ``` + docker compose build api_srv + docker compose up -d --no-deps api_srv + ``` + This rebuilds the `api_srv` service and restarts it without affecting other services. + +2. Viewing logs for troubleshooting: + ``` + docker compose logs -f api_srv ws_srv + ``` + This shows and follows the logs for both `api_srv` and `ws_srv` services. + +3. Scaling a service for increased load: + ``` + docker compose up -d --scale executor=3 + ``` + This scales the `executor` service to 3 instances to handle increased load. + +4. Stopping the entire system for maintenance: + ``` + docker compose stop + docker compose rm -f + docker compose pull + docker compose up -d + ``` + This stops all services, removes containers, pulls the latest images, and restarts the system. + +5. Developing with live updates: + ``` + docker compose watch + ``` + This watches for changes in your code and automatically updates the relevant services. + +6. Checking the status of services: + ``` + docker compose ps + ``` + This shows the current status of all services defined in your docker-compose.yml file. + +These scenarios demonstrate how to use Docker Compose commands in combination to manage your AutoGPT Platform effectively. + + +### Persisting Data + +To persist data for PostgreSQL and Redis, you can modify the `docker-compose.yml` file to add volumes. Here's how: + +1. Open the `docker-compose.yml` file in a text editor. +2. Add volume configurations for PostgreSQL and Redis services: + + ```yaml + services: + postgres: + # ... other configurations ... + volumes: + - postgres_data:/var/lib/postgresql/data + + redis: + # ... other configurations ... + volumes: + - redis_data:/data + + volumes: + postgres_data: + redis_data: + ``` + +3. Save the file and run `docker compose up -d` to apply the changes. + +This configuration will create named volumes for PostgreSQL and Redis, ensuring that your data persists across container restarts. + + + diff --git a/rnd/autogpt_builder/.env.example b/rnd/autogpt_builder/.env.example index 9d36f0ce32d1..02aa5389e12d 100644 --- a/rnd/autogpt_builder/.env.example +++ b/rnd/autogpt_builder/.env.example @@ -1,6 +1,6 @@ NEXT_PUBLIC_AGPT_SERVER_URL=http://localhost:8000/api NEXT_PUBLIC_AGPT_WS_SERVER_URL=ws://localhost:8001/ws -NEXT_PUBLIC_AGPT_MARKETPLACE_URL=http://localhost:8001/api/v1/market +NEXT_PUBLIC_AGPT_MARKETPLACE_URL=http://localhost:8005/api/v1/market ## Supabase credentials ## YOU ONLY NEED THEM IF YOU WANT TO USE SUPABASE USER AUTHENTICATION diff --git a/rnd/autogpt_builder/Dockerfile b/rnd/autogpt_builder/Dockerfile index 08c675e3bbd5..faecc479a551 100644 --- a/rnd/autogpt_builder/Dockerfile +++ b/rnd/autogpt_builder/Dockerfile @@ -1,19 +1,19 @@ # Base stage for both dev and prod FROM node:21-alpine AS base WORKDIR /app -COPY autogpt_builder/package.json autogpt_builder/yarn.lock ./ +COPY rnd/autogpt_builder/package.json rnd/autogpt_builder/yarn.lock ./ RUN yarn install --frozen-lockfile # Dev stage FROM base AS dev ENV NODE_ENV=development -COPY autogpt_builder/ . +COPY rnd/autogpt_builder/ . EXPOSE 3000 -CMD ["npm", "run", "dev"] +CMD ["yarn", "run", "dev"] # Build stage for prod FROM base AS build -COPY autogpt_builder/ . +COPY rnd/autogpt_builder/ . RUN npm run build # Prod stage diff --git a/rnd/autogpt_builder/package.json b/rnd/autogpt_builder/package.json index f1086be167e8..77b24c096379 100644 --- a/rnd/autogpt_builder/package.json +++ b/rnd/autogpt_builder/package.json @@ -29,6 +29,7 @@ "@radix-ui/react-tooltip": "^1.1.2", "@supabase/ssr": "^0.4.0", "@supabase/supabase-js": "^2.45.0", + "@tanstack/react-table": "^8.20.5", "@xyflow/react": "^12.1.0", "ajv": "^8.17.1", "class-variance-authority": "^0.7.0", @@ -47,8 +48,8 @@ "react-icons": "^5.2.1", "react-markdown": "^9.0.1", "react-modal": "^3.16.1", - "recharts": "^2.12.7", "react-shepherd": "^6.1.1", + "recharts": "^2.12.7", "tailwind-merge": "^2.3.0", "tailwindcss-animate": "^1.0.7", "uuid": "^10.0.0", diff --git a/rnd/autogpt_builder/src/app/admin/marketplace/page.tsx b/rnd/autogpt_builder/src/app/admin/marketplace/page.tsx index e328dba505c2..7eb82d7d328d 100644 --- a/rnd/autogpt_builder/src/app/admin/marketplace/page.tsx +++ b/rnd/autogpt_builder/src/app/admin/marketplace/page.tsx @@ -2,15 +2,18 @@ import { withRoleAccess } from "@/lib/withRoleAccess"; import React from "react"; import { getReviewableAgents } from "@/components/admin/marketplace/actions"; -import AdminMarketplaceCard from "@/components/admin/marketplace/AdminMarketplaceCard"; import AdminMarketplaceAgentList from "@/components/admin/marketplace/AdminMarketplaceAgentList"; +import AdminFeaturedAgentsControl from "@/components/admin/marketplace/AdminFeaturedAgentsControl"; +import { Separator } from "@/components/ui/separator"; async function AdminMarketplace() { - const agents = await getReviewableAgents(); + const reviewableAgents = await getReviewableAgents(); + return ( -
-

Agents to review

- -
+ <> + + + + ); } diff --git a/rnd/autogpt_builder/src/app/error.tsx b/rnd/autogpt_builder/src/app/error.tsx new file mode 100644 index 000000000000..ce4db030c65e --- /dev/null +++ b/rnd/autogpt_builder/src/app/error.tsx @@ -0,0 +1,43 @@ +"use client"; + +import { useEffect } from "react"; +import { IconCircleAlert } from "@/components/ui/icons"; +import { Button } from "@/components/ui/button"; +import Link from "next/link"; + +export default function Error({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + useEffect(() => { + console.error(error); + }, [error]); + + return ( +
+
+
+ +
+

+ Oops, something went wrong! +

+

+ We're sorry, but an unexpected error has occurred. Please try + again later or contact support if the issue persists. +

+
+ + +
+
+
+ ); +} diff --git a/rnd/autogpt_builder/src/app/error/page.tsx b/rnd/autogpt_builder/src/app/error/page.tsx deleted file mode 100644 index f984f843129f..000000000000 --- a/rnd/autogpt_builder/src/app/error/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function ErrorPage() { - return

Sorry, something went wrong

; -} diff --git a/rnd/autogpt_builder/src/app/loading.tsx b/rnd/autogpt_builder/src/app/loading.tsx new file mode 100644 index 000000000000..b0be672e03c2 --- /dev/null +++ b/rnd/autogpt_builder/src/app/loading.tsx @@ -0,0 +1,21 @@ +import AgentFlowListSkeleton from "@/components/monitor/skeletons/AgentFlowListSkeleton"; +import React from "react"; +import FlowRunsListSkeleton from "@/components/monitor/skeletons/FlowRunsListSkeleton"; +import FlowRunsStatusSkeleton from "@/components/monitor/skeletons/FlowRunsStatusSkeleton"; + +export default function MonitorLoadingSkeleton() { + return ( +
+
+ {/* Agents Section */} + + + {/* Runs Section */} + + + {/* Stats Section */} + +
+
+ ); +} diff --git a/rnd/autogpt_builder/src/app/marketplace/[id]/page.tsx b/rnd/autogpt_builder/src/app/marketplace/[id]/page.tsx index 84e17d85cb9e..b9abb47f5477 100644 --- a/rnd/autogpt_builder/src/app/marketplace/[id]/page.tsx +++ b/rnd/autogpt_builder/src/app/marketplace/[id]/page.tsx @@ -2,7 +2,7 @@ import { Suspense } from "react"; import { notFound } from "next/navigation"; import MarketplaceAPI from "@/lib/marketplace-api"; import { AgentDetailResponse } from "@/lib/marketplace-api"; -import AgentDetailContent from "@/components/AgentDetailContent"; +import AgentDetailContent from "@/components/marketplace/AgentDetailContent"; async function getAgentDetails(id: string): Promise { const apiUrl = diff --git a/rnd/autogpt_builder/src/app/page.tsx b/rnd/autogpt_builder/src/app/page.tsx index 835d6e1424d6..e76636fc6bc9 100644 --- a/rnd/autogpt_builder/src/app/page.tsx +++ b/rnd/autogpt_builder/src/app/page.tsx @@ -1,5 +1,5 @@ "use client"; -import React, { useEffect, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import AutoGPTServerAPI, { GraphMeta, @@ -22,54 +22,57 @@ const Monitor = () => { const [selectedFlow, setSelectedFlow] = useState(null); const [selectedRun, setSelectedRun] = useState(null); - const api = new AutoGPTServerAPI(); + const api = useMemo(() => new AutoGPTServerAPI(), []); - useEffect(() => fetchFlowsAndRuns(), []); - useEffect(() => { - const intervalId = setInterval( - () => flows.map((f) => refreshFlowRuns(f.id)), - 5000, - ); - return () => clearInterval(intervalId); - }, []); + const refreshFlowRuns = useCallback( + (flowID: string) => { + // Fetch flow run IDs + api.listGraphRunIDs(flowID).then((runIDs) => + runIDs.map((runID) => { + let run; + if ( + (run = flowRuns.find((fr) => fr.id == runID)) && + !["waiting", "running"].includes(run.status) + ) { + return; + } + + // Fetch flow run + api.getGraphExecutionInfo(flowID, runID).then((execInfo) => + setFlowRuns((flowRuns) => { + if (execInfo.length == 0) return flowRuns; + + const flowRunIndex = flowRuns.findIndex((fr) => fr.id == runID); + const flowRun = flowRunFromNodeExecutionResults(execInfo); + if (flowRunIndex > -1) { + flowRuns.splice(flowRunIndex, 1, flowRun); + } else { + flowRuns.push(flowRun); + } + return [...flowRuns]; + }), + ); + }), + ); + }, + [api, flowRuns], + ); - function fetchFlowsAndRuns() { + const fetchFlowsAndRuns = useCallback(() => { api.listGraphs().then((flows) => { setFlows(flows); flows.map((flow) => refreshFlowRuns(flow.id)); }); - } - - function refreshFlowRuns(flowID: string) { - // Fetch flow run IDs - api.listGraphRunIDs(flowID).then((runIDs) => - runIDs.map((runID) => { - let run; - if ( - (run = flowRuns.find((fr) => fr.id == runID)) && - !["waiting", "running"].includes(run.status) - ) { - return; - } - - // Fetch flow run - api.getGraphExecutionInfo(flowID, runID).then((execInfo) => - setFlowRuns((flowRuns) => { - if (execInfo.length == 0) return flowRuns; + }, [api, refreshFlowRuns]); - const flowRunIndex = flowRuns.findIndex((fr) => fr.id == runID); - const flowRun = flowRunFromNodeExecutionResults(execInfo); - if (flowRunIndex > -1) { - flowRuns.splice(flowRunIndex, 1, flowRun); - } else { - flowRuns.push(flowRun); - } - return [...flowRuns]; - }), - ); - }), + useEffect(() => fetchFlowsAndRuns(), [fetchFlowsAndRuns]); + useEffect(() => { + const intervalId = setInterval( + () => flows.map((f) => refreshFlowRuns(f.id)), + 5000, ); - } + return () => clearInterval(intervalId); + }, [flows, refreshFlowRuns]); const column1 = "md:col-span-2 xl:col-span-3 xxl:col-span-2"; const column2 = "md:col-span-3 lg:col-span-2 xl:col-span-3 space-y-4"; @@ -90,10 +93,11 @@ const Monitor = () => { v.graphID == selectedFlow.id) - : flowRuns - ).toSorted((a, b) => Number(a.startTime) - Number(b.startTime))} + runs={[ + ...(selectedFlow + ? flowRuns.filter((v) => v.graphID == selectedFlow.id) + : flowRuns), + ].sort((a, b) => Number(a.startTime) - Number(b.startTime))} selectedRun={selectedRun} onSelectRun={(r) => setSelectedRun(r.id == selectedRun?.id ? null : r)} /> diff --git a/rnd/autogpt_builder/src/components/CustomEdge.tsx b/rnd/autogpt_builder/src/components/CustomEdge.tsx index e40d8d3b6d44..78b8bf60b137 100644 --- a/rnd/autogpt_builder/src/components/CustomEdge.tsx +++ b/rnd/autogpt_builder/src/components/CustomEdge.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useState } from "react"; +import React, { useCallback, useContext, useEffect, useState } from "react"; import { BaseEdge, EdgeLabelRenderer, @@ -65,24 +65,27 @@ export function CustomEdge({ const beadDiameter = 12; const deltaTime = 16; - function setTargetPositions(beads: Bead[]) { - const distanceBetween = Math.min( - (length - beadDiameter) / (beads.length + 1), - beadDiameter, - ); - - return beads.map((bead, index) => { - const distanceFromEnd = beadDiameter * 1.35; - const targetPosition = distanceBetween * index + distanceFromEnd; - const t = getTForDistance(-targetPosition); - - return { - ...bead, - t: visualizeBeads === "animate" ? bead.t : t, - targetT: t, - } as Bead; - }); - } + const setTargetPositions = useCallback( + (beads: Bead[]) => { + const distanceBetween = Math.min( + (length - beadDiameter) / (beads.length + 1), + beadDiameter, + ); + + return beads.map((bead, index) => { + const distanceFromEnd = beadDiameter * 1.35; + const targetPosition = distanceBetween * index + distanceFromEnd; + const t = getTForDistance(-targetPosition); + + return { + ...bead, + t: visualizeBeads === "animate" ? bead.t : t, + targetT: t, + } as Bead; + }); + }, + [getTForDistance, length, visualizeBeads], + ); useEffect(() => { if (data?.beadUp === 0 && data?.beadDown === 0) { @@ -170,7 +173,7 @@ export function CustomEdge({ }, deltaTime); return () => clearInterval(interval); - }, [data]); + }, [data, setTargetPositions, visualizeBeads]); const middle = getPointForT(0.5); diff --git a/rnd/autogpt_builder/src/components/CustomNode.tsx b/rnd/autogpt_builder/src/components/CustomNode.tsx index 45e31887aa30..e9f74709fd96 100644 --- a/rnd/autogpt_builder/src/components/CustomNode.tsx +++ b/rnd/autogpt_builder/src/components/CustomNode.tsx @@ -12,8 +12,10 @@ import InputModalComponent from "./InputModalComponent"; import OutputModalComponent from "./OutputModalComponent"; import { BlockIORootSchema, + BlockIOStringSubSchema, Category, NodeExecutionResult, + BlockUIType, } from "@/lib/autogpt-server-api/types"; import { beautifyString, cn, setNestedProperty } from "@/lib/utils"; import { Button } from "@/components/ui/button"; @@ -21,7 +23,10 @@ import { Switch } from "@/components/ui/switch"; import { Copy, Trash2 } from "lucide-react"; import { history } from "./history"; import NodeHandle from "./NodeHandle"; -import { NodeGenericInputField } from "./node-input-components"; +import { + NodeGenericInputField, + NodeTextBoxInput, +} from "./node-input-components"; import SchemaTooltip from "./SchemaTooltip"; import { getPrimaryCategoryColor } from "@/lib/utils"; import { FlowContext } from "./Flow"; @@ -59,6 +64,7 @@ export type CustomNodeData = { backend_id?: string; errors?: { [key: string]: string }; isOutputStatic?: boolean; + uiType: BlockUIType; }; export type CustomNode = Node; @@ -96,7 +102,7 @@ export function CustomNode({ data, id, width, height }: NodeProps) { useEffect(() => { setIsAnyModalOpen?.(isModalOpen || isOutputModalOpen); - }, [isModalOpen, isOutputModalOpen, data]); + }, [isModalOpen, isOutputModalOpen, data, setIsAnyModalOpen]); useEffect(() => { isInitialSetup.current = false; @@ -118,8 +124,16 @@ export function CustomNode({ data, id, width, height }: NodeProps) { setIsAdvancedOpen(checked); }; - const generateOutputHandles = (schema: BlockIORootSchema) => { - if (!schema?.properties) return null; + const generateOutputHandles = ( + schema: BlockIORootSchema, + nodeType: BlockUIType, + ) => { + if ( + !schema?.properties || + nodeType === BlockUIType.OUTPUT || + nodeType === BlockUIType.NOTE + ) + return null; const keys = Object.keys(schema.properties); return keys.map((key) => (
@@ -133,6 +147,137 @@ export function CustomNode({ data, id, width, height }: NodeProps) { )); }; + const generateInputHandles = ( + schema: BlockIORootSchema, + nodeType: BlockUIType, + ) => { + if (!schema?.properties) return null; + let keys = Object.entries(schema.properties); + switch (nodeType) { + case BlockUIType.INPUT: + // For INPUT blocks, dont include connection handles + return keys.map(([propKey, propSchema]) => { + const isRequired = data.inputSchema.required?.includes(propKey); + const isConnected = isHandleConnected(propKey); + const isAdvanced = propSchema.advanced; + return ( + (isRequired || isAdvancedOpen || !isAdvanced) && ( +
+ + {propSchema.title || beautifyString(propKey)} + +
{}}> + {!isConnected && ( + + )} +
+
+ ) + ); + }); + + case BlockUIType.NOTE: + // For NOTE blocks, don't render any input handles + const [noteKey, noteSchema] = keys[0]; + return ( +
+ +
+ ); + + case BlockUIType.OUTPUT: + // For OUTPUT blocks, only show the 'value' property + return keys.map(([propKey, propSchema]) => { + const isRequired = data.inputSchema.required?.includes(propKey); + const isConnected = isHandleConnected(propKey); + const isAdvanced = propSchema.advanced; + return ( + (isRequired || isAdvancedOpen || !isAdvanced) && ( +
{}}> + {propKey !== "value" ? ( + + {propSchema.title || beautifyString(propKey)} + + ) : ( + + )} + {!isConnected && ( + + )} +
+ ) + ); + }); + + default: + return keys.map(([propKey, propSchema]) => { + const isRequired = data.inputSchema.required?.includes(propKey); + const isConnected = isHandleConnected(propKey); + const isAdvanced = propSchema.advanced; + return ( + (isRequired || isAdvancedOpen || isConnected || !isAdvanced) && ( +
{}}> + + {!isConnected && ( + + )} +
+ ) + ); + }); + } + }; const handleInputChange = (path: string, value: any) => { const keys = parseKeys(path); const newValues = JSON.parse(JSON.stringify(data.hardcodedValues)); @@ -378,13 +523,13 @@ export function CustomNode({ data, id, width, height }: NodeProps) { return (
@@ -417,53 +562,24 @@ export function CustomNode({ data, id, width, height }: NodeProps) { )}
-
+ {data.uiType !== BlockUIType.NOTE ? ( +
+
+ {data.inputSchema && + generateInputHandles(data.inputSchema, data.uiType)} +
+
+ {data.outputSchema && + generateOutputHandles(data.outputSchema, data.uiType)} +
+
+ ) : (
{data.inputSchema && - Object.entries(data.inputSchema.properties).map( - ([propKey, propSchema]) => { - const isRequired = data.inputSchema.required?.includes(propKey); - const isConnected = isHandleConnected(propKey); - const isAdvanced = propSchema.advanced; - return ( - (isRequired || - isAdvancedOpen || - isConnected || - !isAdvanced) && ( -
{}}> - - {!isConnected && ( - - )} -
- ) - ); - }, - )} + generateInputHandles(data.inputSchema, data.uiType)}
-
- {data.outputSchema && generateOutputHandles(data.outputSchema)} -
-
- {isOutputOpen && ( + )} + {isOutputOpen && data.uiType !== BlockUIType.NOTE && (
) { )}
)} -
- - Output - {hasAdvancedFields && ( - <> - - Advanced - - )} - {data.status && ( - - {data.status} - - )} -
+ {data.uiType !== BlockUIType.NOTE && ( +
+ + Output + {hasAdvancedFields && ( + <> + + Advanced + + )} + {data.status && ( + + {data.status} + + )} +
+ )} ({}); @@ -97,7 +107,7 @@ const FlowEditor: React.FC<{ // If resetting tutorial if (params.get("resetTutorial") === "true") { localStorage.removeItem("shepherd-tour"); // Clear tutorial flag - window.location.href = window.location.pathname; // Redirect to clear URL parameters + router.push(pathname); } else { // Otherwise, start tutorial if conditions are met const shouldStartTutorial = !localStorage.getItem("shepherd-tour"); @@ -111,7 +121,7 @@ const FlowEditor: React.FC<{ localStorage.setItem("shepherd-tour", "yes"); } } - }, [availableNodes, tutorialStarted]); + }, [availableNodes, tutorialStarted, router, pathname]); useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { @@ -246,7 +256,7 @@ const FlowEditor: React.FC<{ } const edgeColor = getTypeColor( - getOutputType(connection.source!, connection.sourceHandle!), + getOutputType(nodes, connection.source!, connection.sourceHandle!), ); const sourceNode = getNode(connection.source!); const newEdge: CustomEdge = { @@ -285,6 +295,7 @@ const FlowEditor: React.FC<{ addEdges, deleteElements, clearNodesStatusAndOutput, + nodes, edges, formatEdgeID, getOutputType, @@ -367,7 +378,7 @@ const FlowEditor: React.FC<{ clearNodesStatusAndOutput(); } }, - [setNodes, clearNodesStatusAndOutput], + [setNodes, clearNodesStatusAndOutput, setEdges], ); const getNextNodeId = useCallback(() => { @@ -406,6 +417,7 @@ const FlowEditor: React.FC<{ isOutputOpen: false, block_id: blockId, isOutputStatic: nodeSchema.staticOutput, + uiType: nodeSchema.uiType, }, }; @@ -424,7 +436,6 @@ const FlowEditor: React.FC<{ nodeId, availableNodes, addNodes, - setNodes, deleteElements, clearNodesStatusAndOutput, x, @@ -539,9 +550,9 @@ const FlowEditor: React.FC<{ onClick: handleRedo, }, { - label: "Run", - icon: , - onClick: requestSaveRun, + label: !isRunning ? "Run" : "Stop", + icon: !isRunning ? : , + onClick: !isRunning ? requestSaveAndRun : requestStopRun, }, ]; @@ -577,7 +588,9 @@ const FlowEditor: React.FC<{ requestSave(isTemplate ?? false)} + agentDescription={agentDescription} onDescriptionChange={setAgentDescription} + agentName={agentName} onNameChange={setAgentName} /> diff --git a/rnd/autogpt_builder/src/components/admin/marketplace/AdminAddFeaturedAgentDialog.tsx b/rnd/autogpt_builder/src/components/admin/marketplace/AdminAddFeaturedAgentDialog.tsx new file mode 100644 index 000000000000..3fe3ef9e3452 --- /dev/null +++ b/rnd/autogpt_builder/src/components/admin/marketplace/AdminAddFeaturedAgentDialog.tsx @@ -0,0 +1,149 @@ +"use client"; + +import { + Dialog, + DialogContent, + DialogClose, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { + MultiSelector, + MultiSelectorContent, + MultiSelectorInput, + MultiSelectorItem, + MultiSelectorList, + MultiSelectorTrigger, +} from "@/components/ui/multiselect"; +import { Controller, useForm } from "react-hook-form"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { useState } from "react"; +import { addFeaturedAgent } from "./actions"; +import { Agent } from "@/lib/marketplace-api/types"; + +type FormData = { + agent: string; + categories: string[]; +}; + +export const AdminAddFeaturedAgentDialog = ({ + categories, + agents, +}: { + categories: string[]; + agents: Agent[]; +}) => { + const [selectedAgent, setSelectedAgent] = useState(""); + const [selectedCategories, setSelectedCategories] = useState([]); + + const { + control, + handleSubmit, + watch, + setValue, + formState: { errors }, + } = useForm({ + defaultValues: { + agent: "", + categories: [], + }, + }); + + return ( + + + + + + + Add Featured Agent + +
+ ( +
+ + +
+ )} + /> + ( + { + field.onChange(values); + setSelectedCategories(values); + }} + > + + + + + + {categories.map((category) => ( + + {category} + + ))} + + + + )} + /> +
+ + + + + + + + +
+
+ ); +}; diff --git a/rnd/autogpt_builder/src/components/admin/marketplace/AdminFeaturedAgentsControl.tsx b/rnd/autogpt_builder/src/components/admin/marketplace/AdminFeaturedAgentsControl.tsx new file mode 100644 index 000000000000..06331cd9fa52 --- /dev/null +++ b/rnd/autogpt_builder/src/components/admin/marketplace/AdminFeaturedAgentsControl.tsx @@ -0,0 +1,67 @@ +import { Button } from "@/components/ui/button"; +import { + getFeaturedAgents, + removeFeaturedAgent, + getCategories, + getNotFeaturedAgents, +} from "./actions"; + +import FeaturedAgentsTable from "./FeaturedAgentsTable"; +import { AdminAddFeaturedAgentDialog } from "./AdminAddFeaturedAgentDialog"; +import { revalidatePath } from "next/cache"; + +export default async function AdminFeaturedAgentsControl({ + className, +}: { + className?: string; +}) { + // add featured agent button + // modal to select agent? + // modal to select categories? + // table of featured agents + // in table + // remove featured agent button + // edit featured agent categories button + // table footer + // Next page button + // Previous page button + // Page number input + // Page size input + // Total pages input + // Go to page button + + const page = 1; + const pageSize = 10; + + const agents = await getFeaturedAgents(page, pageSize); + + const categories = await getCategories(); + + const notFeaturedAgents = await getNotFeaturedAgents(); + + return ( +
+
+

Featured Agent Controls

+ +
+ Remove, + action: async (rows) => { + "use server"; + const all = rows.map((row) => removeFeaturedAgent(row.id)); + await Promise.all(all); + revalidatePath("/marketplace"); + }, + }, + ]} + /> +
+ ); +} diff --git a/rnd/autogpt_builder/src/components/admin/marketplace/AdminMarketplaceAgentList.tsx b/rnd/autogpt_builder/src/components/admin/marketplace/AdminMarketplaceAgentList.tsx index d3fe147a9e40..cc6c751457c6 100644 --- a/rnd/autogpt_builder/src/components/admin/marketplace/AdminMarketplaceAgentList.tsx +++ b/rnd/autogpt_builder/src/components/admin/marketplace/AdminMarketplaceAgentList.tsx @@ -4,23 +4,33 @@ import { ClipboardX } from "lucide-react"; export default function AdminMarketplaceAgentList({ agents, + className, }: { agents: Agent[]; + className?: string; }) { if (agents.length === 0) { return ( -
- -

No agents to review

+
+

Agents to review

+
+ +

No agents to review

+
); } return ( -
- {agents.map((agent) => ( - - ))} +
+
+

Agents to review

+
+
+ {agents.map((agent) => ( + + ))} +
); } diff --git a/rnd/autogpt_builder/src/components/admin/marketplace/FeaturedAgentsTable.tsx b/rnd/autogpt_builder/src/components/admin/marketplace/FeaturedAgentsTable.tsx new file mode 100644 index 000000000000..6fd31301aae3 --- /dev/null +++ b/rnd/autogpt_builder/src/components/admin/marketplace/FeaturedAgentsTable.tsx @@ -0,0 +1,114 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; +import { DataTable } from "@/components/ui/data-table"; +import { Agent } from "@/lib/marketplace-api"; +import { ColumnDef } from "@tanstack/react-table"; +import { ArrowUpDown } from "lucide-react"; +import { removeFeaturedAgent } from "./actions"; +import { GlobalActions } from "@/components/ui/data-table"; + +export const columns: ColumnDef[] = [ + { + id: "select", + header: ({ table }) => ( + table.toggleAllPageRowsSelected(!!value)} + aria-label="Select all" + /> + ), + cell: ({ row }) => ( + row.toggleSelected(!!value)} + aria-label="Select row" + /> + ), + }, + { + header: ({ column }) => { + return ( + + ); + }, + accessorKey: "name", + }, + { + header: "Description", + accessorKey: "description", + }, + { + header: "Categories", + accessorKey: "categories", + }, + { + header: "Keywords", + accessorKey: "keywords", + }, + { + header: "Downloads", + accessorKey: "downloads", + }, + { + header: "Author", + accessorKey: "author", + }, + { + header: "Version", + accessorKey: "version", + }, + { + header: "actions", + cell: ({ row }) => { + const handleRemove = async () => { + await removeFeaturedAgentWithId(); + }; + // const handleEdit = async () => { + // console.log("edit"); + // }; + const removeFeaturedAgentWithId = removeFeaturedAgent.bind( + null, + row.original.id, + ); + return ( +
+ + {/* */} +
+ ); + }, + }, +]; + +export default function FeaturedAgentsTable({ + agents, + globalActions, +}: { + agents: Agent[]; + globalActions: GlobalActions[]; +}) { + return ( + + ); +} diff --git a/rnd/autogpt_builder/src/components/admin/marketplace/actions.ts b/rnd/autogpt_builder/src/components/admin/marketplace/actions.ts index c67313d36d36..88c3c2a88ad5 100644 --- a/rnd/autogpt_builder/src/components/admin/marketplace/actions.ts +++ b/rnd/autogpt_builder/src/components/admin/marketplace/actions.ts @@ -1,5 +1,7 @@ "use server"; +import AutoGPTServerAPI from "@/lib/autogpt-server-api"; import MarketplaceAPI from "@/lib/marketplace-api"; +import { revalidatePath } from "next/cache"; export async function approveAgent( agentId: string, @@ -9,6 +11,7 @@ export async function approveAgent( const api = new MarketplaceAPI(); await api.approveAgentSubmission(agentId, version, comment); console.debug(`Approving agent ${agentId}`); + revalidatePath("/marketplace"); } export async function rejectAgent( @@ -19,9 +22,64 @@ export async function rejectAgent( const api = new MarketplaceAPI(); await api.rejectAgentSubmission(agentId, version, comment); console.debug(`Rejecting agent ${agentId}`); + revalidatePath("/marketplace"); } export async function getReviewableAgents() { const api = new MarketplaceAPI(); return api.getAgentSubmissions(); } + +export async function getFeaturedAgents( + page: number = 1, + pageSize: number = 10, +) { + const api = new MarketplaceAPI(); + const featured = await api.getFeaturedAgents(page, pageSize); + console.debug(`Getting featured agents ${featured.agents.length}`); + return featured; +} + +export async function getFeaturedAgent(agentId: string) { + const api = new MarketplaceAPI(); + const featured = await api.getFeaturedAgent(agentId); + console.debug(`Getting featured agent ${featured.agentId}`); + return featured; +} + +export async function addFeaturedAgent( + agentId: string, + categories: string[] = ["featured"], +) { + const api = new MarketplaceAPI(); + await api.addFeaturedAgent(agentId, categories); + console.debug(`Adding featured agent ${agentId}`); + revalidatePath("/marketplace"); +} + +export async function removeFeaturedAgent( + agentId: string, + categories: string[] = ["featured"], +) { + const api = new MarketplaceAPI(); + await api.removeFeaturedAgent(agentId, categories); + console.debug(`Removing featured agent ${agentId}`); + revalidatePath("/marketplace"); +} + +export async function getCategories() { + const api = new MarketplaceAPI(); + const categories = await api.getCategories(); + console.debug(`Getting categories ${categories.unique_categories.length}`); + return categories; +} + +export async function getNotFeaturedAgents( + page: number = 1, + pageSize: number = 100, +) { + const api = new MarketplaceAPI(); + const agents = await api.getNotFeaturedAgents(page, pageSize); + console.debug(`Getting not featured agents ${agents.agents.length}`); + return agents; +} diff --git a/rnd/autogpt_builder/src/components/agent-import-form.tsx b/rnd/autogpt_builder/src/components/agent-import-form.tsx index d037fe6b899c..4261ed635001 100644 --- a/rnd/autogpt_builder/src/components/agent-import-form.tsx +++ b/rnd/autogpt_builder/src/components/agent-import-form.tsx @@ -21,8 +21,13 @@ import AutoGPTServerAPI, { import { cn } from "@/lib/utils"; import { EnterIcon } from "@radix-ui/react-icons"; +// Add this custom schema for File type +const fileSchema = z.custom((val) => val instanceof File, { + message: "Must be a File object", +}); + const formSchema = z.object({ - agentFile: z.instanceof(File), + agentFile: fileSchema, agentName: z.string().min(1, "Agent name is required"), agentDescription: z.string(), importAsTemplate: z.boolean(), diff --git a/rnd/autogpt_builder/src/components/customnode.css b/rnd/autogpt_builder/src/components/customnode.css index 90efecdd2e38..840fd31a94a9 100644 --- a/rnd/autogpt_builder/src/components/customnode.css +++ b/rnd/autogpt_builder/src/components/customnode.css @@ -1,6 +1,5 @@ .custom-node { color: #000000; - width: 500px; box-sizing: border-box; transition: border-color 0.3s ease-in-out; } diff --git a/rnd/autogpt_builder/src/components/edit/control/BlocksControl.tsx b/rnd/autogpt_builder/src/components/edit/control/BlocksControl.tsx index 2440fd42339c..4bf19c94d825 100644 --- a/rnd/autogpt_builder/src/components/edit/control/BlocksControl.tsx +++ b/rnd/autogpt_builder/src/components/edit/control/BlocksControl.tsx @@ -17,6 +17,11 @@ import { IconToyBrick } from "@/components/ui/icons"; import SchemaTooltip from "@/components/SchemaTooltip"; import { getPrimaryCategoryColor } from "@/lib/utils"; import { Badge } from "@/components/ui/badge"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; interface BlocksControlProps { blocks: Block[]; @@ -60,17 +65,20 @@ export const BlocksControl: React.FC = ({ return ( - {" "} - {/* Control popover open state */} - - - + + + + + + + Blocks + void; onNameChange: (name: string) => void; onDescriptionChange: (description: string) => void; @@ -30,7 +37,9 @@ interface SaveControlProps { export const SaveControl = ({ agentMeta, onSave, + agentName, onNameChange, + agentDescription, onDescriptionChange, }: SaveControlProps) => { /** @@ -51,11 +60,16 @@ export const SaveControl = ({ return ( - - - + + + + + + + Save + @@ -65,7 +79,7 @@ export const SaveControl = ({ id="name" placeholder="Enter your agent name" className="col-span-3" - defaultValue={agentMeta?.name || ""} + value={agentName} onChange={(e) => onNameChange(e.target.value)} /> @@ -73,9 +87,21 @@ export const SaveControl = ({ id="description" placeholder="Your agent description" className="col-span-3" - defaultValue={agentMeta?.description || ""} + value={agentDescription} onChange={(e) => onDescriptionChange(e.target.value)} /> + {agentMeta?.version && ( + <> + + + + )}
diff --git a/rnd/autogpt_builder/src/components/AgentDetailContent.tsx b/rnd/autogpt_builder/src/components/marketplace/AgentDetailContent.tsx similarity index 91% rename from rnd/autogpt_builder/src/components/AgentDetailContent.tsx rename to rnd/autogpt_builder/src/components/marketplace/AgentDetailContent.tsx index a57eaa58619d..aeaf9163e32c 100644 --- a/rnd/autogpt_builder/src/components/AgentDetailContent.tsx +++ b/rnd/autogpt_builder/src/components/marketplace/AgentDetailContent.tsx @@ -11,7 +11,10 @@ import { ChevronUp, } from "lucide-react"; import { Button } from "@/components/ui/button"; -import { AgentDetailResponse } from "@/lib/marketplace-api"; +import { + AgentDetailResponse, + InstallationLocation, +} from "@/lib/marketplace-api"; import dynamic from "next/dynamic"; import { Node, Edge } from "@xyflow/react"; import MarketplaceAPI from "@/lib/marketplace-api"; @@ -32,6 +35,7 @@ const Background = dynamic( import "@xyflow/react/dist/style.css"; import { beautifyString } from "@/lib/utils"; +import { makeAnalyticsEvent } from "./actions"; function convertGraphToReactFlow(graph: any): { nodes: Node[]; edges: Edge[] } { const nodes: Node[] = graph.nodes.map((node: any) => { @@ -96,8 +100,16 @@ async function installGraph(id: string): Promise { nodes: agent.graph.nodes, links: agent.graph.links, }; - await serverAPI.createTemplate(data); - console.log(`Agent installed successfully`); + const result = await serverAPI.createTemplate(data); + makeAnalyticsEvent({ + event_name: "agent_installed_from_marketplace", + event_data: { + marketplace_agent_id: id, + installed_agent_id: result.id, + installation_location: InstallationLocation.CLOUD, + }, + }); + console.log(`Agent installed successfully`, result); } catch (error) { console.error(`Error installing agent:`, error); throw error; diff --git a/rnd/autogpt_builder/src/components/marketplace/actions.ts b/rnd/autogpt_builder/src/components/marketplace/actions.ts new file mode 100644 index 000000000000..2fc158d4a6b4 --- /dev/null +++ b/rnd/autogpt_builder/src/components/marketplace/actions.ts @@ -0,0 +1,9 @@ +"use server"; + +import MarketplaceAPI, { AnalyticsEvent } from "@/lib/marketplace-api"; + +export async function makeAnalyticsEvent(event: AnalyticsEvent) { + const apiUrl = process.env.AGPT_SERVER_API_URL; + const api = new MarketplaceAPI(); + await api.makeAnalyticsEvent(event); +} diff --git a/rnd/autogpt_builder/src/components/monitor/AgentFlowList.tsx b/rnd/autogpt_builder/src/components/monitor/AgentFlowList.tsx index f6e467810519..9aef109a3657 100644 --- a/rnd/autogpt_builder/src/components/monitor/AgentFlowList.tsx +++ b/rnd/autogpt_builder/src/components/monitor/AgentFlowList.tsx @@ -1,5 +1,5 @@ import AutoGPTServerAPI, { GraphMeta } from "@/lib/autogpt-server-api"; -import React, { useEffect, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import Link from "next/link"; @@ -29,6 +29,7 @@ import { } from "@/components/ui/table"; import moment from "moment/moment"; import { FlowRun } from "@/lib/types"; +import { DialogTitle } from "@/components/ui/dialog"; export const AgentFlowList = ({ flows, @@ -44,10 +45,10 @@ export const AgentFlowList = ({ className?: string; }) => { const [templates, setTemplates] = useState([]); - const api = new AutoGPTServerAPI(); + const api = useMemo(() => new AutoGPTServerAPI(), []); useEffect(() => { api.listTemplates().then((templates) => setTemplates(templates)); - }, []); + }, [api]); return ( @@ -102,8 +103,11 @@ export const AgentFlowList = ({ - - Import an Agent (template) from a file + + Import Agent +

+ Import an Agent (template) from a file +

diff --git a/rnd/autogpt_builder/src/components/monitor/FlowInfo.tsx b/rnd/autogpt_builder/src/components/monitor/FlowInfo.tsx index 2a7b71ec99b2..0cf53d78e322 100644 --- a/rnd/autogpt_builder/src/components/monitor/FlowInfo.tsx +++ b/rnd/autogpt_builder/src/components/monitor/FlowInfo.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import AutoGPTServerAPI, { Graph, GraphMeta, @@ -28,7 +28,7 @@ export const FlowInfo: React.FC< flowVersion?: number | "all"; } > = ({ flow, flowRuns, flowVersion, ...props }) => { - const api = new AutoGPTServerAPI(); + const api = useMemo(() => new AutoGPTServerAPI(), []); const [flowVersions, setFlowVersions] = useState(null); const [selectedVersion, setSelectedFlowVersion] = useState( @@ -41,7 +41,7 @@ export const FlowInfo: React.FC< useEffect(() => { api.getGraphAllVersions(flow.id).then((result) => setFlowVersions(result)); - }, [flow.id]); + }, [flow.id, api]); return ( diff --git a/rnd/autogpt_builder/src/components/monitor/FlowRunInfo.tsx b/rnd/autogpt_builder/src/components/monitor/FlowRunInfo.tsx index 1247ba2d227b..10be74e29bdd 100644 --- a/rnd/autogpt_builder/src/components/monitor/FlowRunInfo.tsx +++ b/rnd/autogpt_builder/src/components/monitor/FlowRunInfo.tsx @@ -1,9 +1,10 @@ -import React from "react"; -import { GraphMeta } from "@/lib/autogpt-server-api"; +import React, { useCallback } from "react"; +import AutoGPTServerAPI, { GraphMeta } 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 { buttonVariants } from "@/components/ui/button"; +import { Button, buttonVariants } from "@/components/ui/button"; +import { IconSquare } from "@/components/ui/icons"; import { Pencil2Icon } from "@radix-ui/react-icons"; import moment from "moment/moment"; import { FlowRunStatusBadge } from "@/components/monitor/FlowRunStatusBadge"; @@ -20,6 +21,11 @@ export const FlowRunInfo: React.FC< ); } + const handleStopRun = useCallback(() => { + const api = new AutoGPTServerAPI(); + api.stopGraphExecution(flow.id, flowRun.id); + }, [flow.id, flowRun.id]); + return ( @@ -34,12 +40,19 @@ export const FlowRunInfo: React.FC< Run ID: {flowRun.id}

- - Edit Agent - +
+ {flowRun.status === "running" && ( + + )} + + Edit Agent + +

diff --git a/rnd/autogpt_builder/src/components/monitor/skeletons/AgentFlowListSkeleton.tsx b/rnd/autogpt_builder/src/components/monitor/skeletons/AgentFlowListSkeleton.tsx new file mode 100644 index 000000000000..58d8c6ca6fe1 --- /dev/null +++ b/rnd/autogpt_builder/src/components/monitor/skeletons/AgentFlowListSkeleton.tsx @@ -0,0 +1,24 @@ +export default function AgentsFlowListSkeleton() { + return ( +

+
+

Agents

+
+
+
+
+
Name
+
# of runs
+
Last run
+
+ {[...Array(3)].map((_, index) => ( +
+
+
+
+
+ ))} +
+
+ ); +} diff --git a/rnd/autogpt_builder/src/components/monitor/skeletons/FlowRunsListSkeleton.tsx b/rnd/autogpt_builder/src/components/monitor/skeletons/FlowRunsListSkeleton.tsx new file mode 100644 index 000000000000..6f884151d3a0 --- /dev/null +++ b/rnd/autogpt_builder/src/components/monitor/skeletons/FlowRunsListSkeleton.tsx @@ -0,0 +1,23 @@ +export default function FlowRunsListSkeleton() { + return ( +
+
+

Runs

+
+
Agent
+
Started
+
Status
+
Duration
+
+ {[...Array(4)].map((_, index) => ( +
+
+
+
+
+
+ ))} +
+
+ ); +} diff --git a/rnd/autogpt_builder/src/components/monitor/skeletons/FlowRunsStatusSkeleton.tsx b/rnd/autogpt_builder/src/components/monitor/skeletons/FlowRunsStatusSkeleton.tsx new file mode 100644 index 000000000000..40677c70cc7c --- /dev/null +++ b/rnd/autogpt_builder/src/components/monitor/skeletons/FlowRunsStatusSkeleton.tsx @@ -0,0 +1,28 @@ +export default function FlowRunsStatusSkeleton() { + return ( +
+
+
+

Stats

+
+ {["2h", "8h", "24h", "7d", "Custom", "All"].map((btn) => ( +
+ ))} +
+
+ + {/* Placeholder for the line chart */} +
+ + {/* Placeholders for total runs and total run time */} +
+
+
+
+
+
+ ); +} diff --git a/rnd/autogpt_builder/src/components/node-input-components.tsx b/rnd/autogpt_builder/src/components/node-input-components.tsx index 51a9ad39779b..f0a2bf85f48e 100644 --- a/rnd/autogpt_builder/src/components/node-input-components.tsx +++ b/rnd/autogpt_builder/src/components/node-input-components.tsx @@ -10,7 +10,7 @@ import { BlockIONumberSubSchema, BlockIOBooleanSubSchema, } from "@/lib/autogpt-server-api/types"; -import { FC, useEffect, useState } from "react"; +import React, { FC, useCallback, useEffect, useState } from "react"; import { Button } from "./ui/button"; import { Switch } from "./ui/switch"; import { @@ -296,7 +296,7 @@ const NodeKeyValueInput: FC<{ className, displayName, }) => { - const getPairValues = () => { + const getPairValues = useCallback(() => { let defaultEntries = new Map(); connections @@ -311,7 +311,7 @@ const NodeKeyValueInput: FC<{ }); return Array.from(defaultEntries, ([key, value]) => ({ key, value })); - }; + }, [connections, entries, schema.default, selfKey]); const [keyValuePairs, setKeyValuePairs] = useState< { key: string; value: string | number | null }[] @@ -319,7 +319,7 @@ const NodeKeyValueInput: FC<{ useEffect( () => setKeyValuePairs(getPairValues()), - [connections, entries, schema.default], + [connections, entries, schema.default, getPairValues], ); function updateKeyValuePairs(newPairs: typeof keyValuePairs) { @@ -587,6 +587,52 @@ const NodeStringInput: FC<{ ); }; +export const NodeTextBoxInput: FC<{ + selfKey: string; + schema: BlockIOStringSubSchema; + value?: string; + error?: string; + handleInputChange: NodeObjectInputTreeProps["handleInputChange"]; + handleInputClick: NodeObjectInputTreeProps["handleInputClick"]; + className?: string; + displayName: string; +}> = ({ + selfKey, + schema, + value = "", + error, + handleInputChange, + handleInputClick, + className, + displayName, +}) => { + return ( +
+
handleInputClick(selfKey) : undefined} + > +