diff --git a/autogpt_platform/backend/backend/server/v2/library/db.py b/autogpt_platform/backend/backend/server/v2/library/db.py index ccb5d4a682..dc56f1f91c 100644 --- a/autogpt_platform/backend/backend/server/v2/library/db.py +++ b/autogpt_platform/backend/backend/server/v2/library/db.py @@ -148,17 +148,17 @@ async def search_library_agents( ) - if sort_by == backend.server.v2.library.model.LibraryAgentFilter.UPDATED_AT: + if sort_by == backend.server.v2.library.model.LibraryAgentFilter.UPDATED_AT.value: order_by = {"updatedAt": "desc"} - elif sort_by == backend.server.v2.library.model.LibraryAgentFilter.CREATED_AT: + elif sort_by == backend.server.v2.library.model.LibraryAgentFilter.CREATED_AT.value: order_by = {"createdAt": "desc"} - elif sort_by == backend.server.v2.library.model.LibraryAgentFilter.IS_FAVOURITE: + elif sort_by == backend.server.v2.library.model.LibraryAgentFilter.IS_FAVOURITE.value: where["isFavorite"] = True order_by = {"updatedAt": "desc"} - elif sort_by == backend.server.v2.library.model.LibraryAgentFilter.IS_CREATED_BY_USER: + elif sort_by == backend.server.v2.library.model.LibraryAgentFilter.IS_CREATED_BY_USER.value: where["isCreatedByUser"] = True order_by = {"updatedAt": "desc"} diff --git a/autogpt_platform/frontend/src/app/globals.css b/autogpt_platform/frontend/src/app/globals.css index eaa5fe00f1..28de6d1b4e 100644 --- a/autogpt_platform/frontend/src/app/globals.css +++ b/autogpt_platform/frontend/src/app/globals.css @@ -129,12 +129,3 @@ body { overflow-x: hidden; } - -.drop-style { - border: dashed 2px #a3a3a3 !important; - border-radius: 20px; -} - -.drop-style:hover { - border: dashed 2px #525252 !important; -} diff --git a/autogpt_platform/frontend/src/components/agptui/LibraryAgentCard.tsx b/autogpt_platform/frontend/src/components/agptui/LibraryAgentCard.tsx index ca781bb3e3..3d1ba3862e 100644 --- a/autogpt_platform/frontend/src/components/agptui/LibraryAgentCard.tsx +++ b/autogpt_platform/frontend/src/components/agptui/LibraryAgentCard.tsx @@ -1,53 +1,82 @@ import { cn } from "@/lib/utils"; import Link from "next/link"; +import Image from "next/image"; import { GraphMeta } from "@/lib/autogpt-server-api"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; export const LibraryAgentCard = ({ id, name, isCreatedByUser }: GraphMeta) => { + const descriptions = `An intelligent agent that helps automate your workflow, + saving valuable time and improving productivity with smart automations, + and enabling you to focus on what matters most.`; + + const imageUrl = null; return ( -
-
-

- {name} -

- {/* */} +
+
+ {!imageUrl ? ( +
+ ) : ( + {`${name} + )} +
+ + + {name.charAt(0)} + +
-
-
- - See runs - +
+

+ {name} +

- {isCreatedByUser && ( +

+ {descriptions} +

+ +
+
- Open in builder + See runs - )} -
- {/* {output && ( -
- New output + + {!isCreatedByUser && ( + + Open in builder + + )}
- )} */} +
); diff --git a/autogpt_platform/frontend/src/components/agptui/LibraryUploadAgent.tsx b/autogpt_platform/frontend/src/components/agptui/LibraryUploadAgent.tsx index cc913ce2ae..d3bf14abcd 100644 --- a/autogpt_platform/frontend/src/components/agptui/LibraryUploadAgent.tsx +++ b/autogpt_platform/frontend/src/components/agptui/LibraryUploadAgent.tsx @@ -3,7 +3,7 @@ import { Upload, X } from "lucide-react"; import { Button } from "./Button"; import { useEffect, useState } from "react"; import { motion, useAnimation } from "framer-motion"; -import { cn } from "@/lib/utils"; +import { cn, removeCredentials } from "@/lib/utils"; import { Dialog, DialogContent, @@ -11,23 +11,86 @@ import { DialogTitle, DialogTrigger, } from "../ui/dialog"; +import { z } from "zod"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; import { Input } from "../ui/input"; import { FileUploader } from "react-drag-drop-files"; +import { Graph, GraphCreatable } from "@/lib/autogpt-server-api"; +import { useBackendAPI } from "@/lib/autogpt-server-api/context"; +import { Textarea } from "@/components/ui/textarea"; const fileTypes = ["JSON"]; +const fileSchema = z.custom((val) => val instanceof File, { + message: "Must be a File object", +}); + +const formSchema = z.object({ + agentFile: fileSchema, + agentName: z.string().min(1, "Agent name is required"), + agentDescription: z.string(), +}); + +function updateBlockIDs(graph: Graph) { + const updatedBlockIDMap: Record = { + "a1b2c3d4-5e6f-7g8h-9i0j-k1l2m3n4o5p6": + "436c3984-57fd-4b85-8e9a-459b356883bd", + "b2g2c3d4-5e6f-7g8h-9i0j-k1l2m3n4o5p6": + "0e50422c-6dee-4145-83d6-3a5a392f65de", + "c3d4e5f6-7g8h-9i0j-1k2l-m3n4o5p6q7r8": + "a0a69be1-4528-491c-a85a-a4ab6873e3f0", + "c3d4e5f6-g7h8-i9j0-k1l2-m3n4o5p6q7r8": + "32a87eab-381e-4dd4-bdb8-4c47151be35a", + "b2c3d4e5-6f7g-8h9i-0j1k-l2m3n4o5p6q7": + "87840993-2053-44b7-8da4-187ad4ee518c", + "h1i2j3k4-5l6m-7n8o-9p0q-r1s2t3u4v5w6": + "d0822ab5-9f8a-44a3-8971-531dd0178b6b", + "d3f4g5h6-1i2j-3k4l-5m6n-7o8p9q0r1s2t": + "df06086a-d5ac-4abb-9996-2ad0acb2eff7", + "h5e7f8g9-1b2c-3d4e-5f6g-7h8i9j0k1l2m": + "f5b0f5d0-1862-4d61-94be-3ad0fa772760", + "a1234567-89ab-cdef-0123-456789abcdef": + "4335878a-394e-4e67-adf2-919877ff49ae", + "f8e7d6c5-b4a3-2c1d-0e9f-8g7h6i5j4k3l": + "f66a3543-28d3-4ab5-8945-9b336371e2ce", + "b29c1b50-5d0e-4d9f-8f9d-1b0e6fcbf0h2": + "716a67b3-6760-42e7-86dc-18645c6e00fc", + "31d1064e-7446-4693-o7d4-65e5ca9110d1": + "cc10ff7b-7753-4ff2-9af6-9399b1a7eddc", + "c6731acb-4105-4zp1-bc9b-03d0036h370g": + "5ebe6768-8e5d-41e3-9134-1c7bd89a8d52", + }; + graph.nodes + .filter((node) => node.block_id in updatedBlockIDMap) + .forEach((node) => { + node.block_id = updatedBlockIDMap[node.block_id]; + }); + return graph; +} + export const LibraryUploadAgent = () => { const [scrolled, setScrolled] = useState(false); - const [file, setFile] = useState(null); const [isDroped, setisDroped] = useState(false); const controls = useAnimation(); - const handleChange = (file: File) => { - setTimeout(() => { - setisDroped(false); - }, 2000); - setFile(file); - setisDroped(false); - }; + const api = useBackendAPI(); + const [agentObject, setAgentObject] = useState(null); + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + agentName: "", + agentDescription: "", + }, + }); useEffect(() => { const handleScroll = () => { @@ -42,11 +105,66 @@ export const LibraryUploadAgent = () => { return () => window.removeEventListener("scroll", handleScroll); }, []); - const handleUpload = () => { - // Add upload logic here - if (file) { - console.log("Uploading file:", file); + const onSubmit = (values: z.infer) => { + if (!agentObject) { + form.setError("root", { message: "No Agent object to save" }); + return; } + const payload: GraphCreatable = { + ...agentObject, + name: values.agentName, + description: values.agentDescription, + is_active: true, + }; + + api + .createGraph(payload) + .then((response) => { + const qID = "flowID"; + window.location.href = `/build?${qID}=${response.id}`; + }) + .catch((error) => { + form.setError("root", { + message: `Could not create agent: ${error}`, + }); + }); + }; + + const handleChange = (file: File) => { + setTimeout(() => { + setisDroped(false); + }, 2000); + + form.setValue("agentFile", file); + const reader = new FileReader(); + reader.onload = (event) => { + try { + const obj = JSON.parse(event.target?.result as string); + if ( + !["name", "description", "nodes", "links"].every( + (key) => key in obj && obj[key] != null, + ) + ) { + throw new Error( + "Invalid agent object in file: " + JSON.stringify(obj, null, 2), + ); + } + const agent = obj as Graph; + removeCredentials(agent); + updateBlockIDs(agent); + setAgentObject(agent); + if (!form.getValues("agentName")) { + form.setValue("agentName", agent.name); + } + if (!form.getValues("agentDescription")) { + form.setValue("agentDescription", agent.description); + } + } catch (error) { + console.error("Error loading agent file:", error); + } + }; + reader.readAsText(file); + setisDroped(false); }; return ( @@ -82,85 +200,123 @@ export const LibraryUploadAgent = () => { - Upload Agent + Upload Agent -
- - - - {file ? ( -
- {file.name} - -
- ) : ( - { - setisDroped(true); - }} - onSelect={() => { - setisDroped(true); - }} - children={ -
- {isDroped ? ( -
-
-
- ) : ( - <> - Drop your agent here - or - Click to upload - - )} -
- } +
+ + ( + + Agent name + + + + + + )} /> - )} - -
+ ( + + Description + +