mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-20 20:48:11 -05:00
complete upload functionality and fix filters
This commit is contained in:
@@ -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"}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-[158px] flex-col rounded-[14px] border border-[#E5E5E5] bg-white p-5 transition-all duration-300 ease-in-out hover:scale-[1.02]",
|
||||
!isCreatedByUser && "shadow-[0_-5px_0_0_rgb(196_181_253)]",
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-1">
|
||||
<h3 className="flex-1 font-inter text-[18px] font-semibold leading-4">
|
||||
{name}
|
||||
</h3>
|
||||
{/* <span
|
||||
className={cn(
|
||||
"h-[14px] w-[14px] rounded-full",
|
||||
status == "Nothing running" && "bg-[#64748B]",
|
||||
status == "healthy" && "bg-[#22C55E]",
|
||||
status == "something wrong" && "bg-[#EF4444]",
|
||||
status == "waiting for trigger" && "bg-[#FBBF24]",
|
||||
)}
|
||||
></span> */}
|
||||
<div className="inline-flex w-full max-w-[434px] cursor-pointer flex-col items-start justify-start gap-2.5 rounded-[26px] bg-white transition-all duration-300 hover:shadow-lg dark:bg-transparent dark:hover:shadow-gray-700">
|
||||
<div className="relative h-[200px] w-full overflow-hidden rounded-[20px]">
|
||||
{!imageUrl ? (
|
||||
<div
|
||||
className={`h-full w-full ${
|
||||
[
|
||||
"bg-gradient-to-r from-green-200 to-blue-200",
|
||||
"bg-gradient-to-r from-pink-200 to-purple-200",
|
||||
"bg-gradient-to-r from-yellow-200 to-orange-200",
|
||||
"bg-gradient-to-r from-blue-200 to-cyan-200",
|
||||
"bg-gradient-to-r from-indigo-200 to-purple-200",
|
||||
][Math.floor(Math.random() * 5)]
|
||||
}`}
|
||||
style={{
|
||||
backgroundSize: "200% 200%",
|
||||
animation: "gradient 15s ease infinite",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
src={imageUrl}
|
||||
alt={`${name} preview image`}
|
||||
fill
|
||||
className="object-cover"
|
||||
priority
|
||||
/>
|
||||
)}
|
||||
<div className="absolute bottom-4 left-4">
|
||||
<Avatar className="h-16 w-16 border-2 border-white dark:border-gray-800">
|
||||
<AvatarImage
|
||||
src="/avatar-placeholder.png"
|
||||
alt={`${name} creator avatar`}
|
||||
/>
|
||||
<AvatarFallback>{name.charAt(0)}</AvatarFallback>
|
||||
</Avatar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="mt-6 flex gap-3">
|
||||
<Link
|
||||
href={`/agents/${id}`}
|
||||
className="font-inter text-[14px] font-[700] leading-[24px] text-neutral-800 hover:cursor-pointer hover:underline"
|
||||
>
|
||||
See runs
|
||||
</Link>
|
||||
<div className="w-full px-4 py-4">
|
||||
<h3 className="font-poppins mb-2 text-2xl font-semibold leading-tight text-[#272727] dark:text-neutral-100">
|
||||
{name}
|
||||
</h3>
|
||||
|
||||
{isCreatedByUser && (
|
||||
<p className="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
||||
{descriptions}
|
||||
</p>
|
||||
|
||||
<div className="items-between mt-4 flex w-full justify-between">
|
||||
<div className="flex gap-3">
|
||||
<Link
|
||||
href={`/build?flowID=${id}`}
|
||||
className="font-inter text-[14px] font-[700] leading-[24px] text-neutral-800 hover:underline"
|
||||
href={`/agents/${id}`}
|
||||
className="font-geist text-lg font-semibold text-neutral-800 hover:underline dark:text-neutral-200"
|
||||
>
|
||||
Open in builder
|
||||
See runs
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
{/* {output && (
|
||||
<div className="h-[24px] w-fit rounded-[45px] bg-neutral-600 px-[9px] py-[2px] font-sans text-[12px] font-[700] text-neutral-50">
|
||||
New output
|
||||
|
||||
{!isCreatedByUser && (
|
||||
<Link
|
||||
href={`/build?flowID=${id}`}
|
||||
className="font-geist text-lg font-semibold text-neutral-800 hover:underline dark:text-neutral-200"
|
||||
>
|
||||
Open in builder
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
)} */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -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<File>((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<string, string> = {
|
||||
"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<File | null>(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<GraphCreatable | null>(null);
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
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<typeof formSchema>) => {
|
||||
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 = () => {
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="mb-8 text-center">Upload Agent </DialogTitle>
|
||||
<DialogTitle className="mb-8 text-center">Upload Agent</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="relative flex flex-col gap-4">
|
||||
<Input placeholder="Agent name" className="w-full rounded-[10px]" />
|
||||
<Input placeholder="Description" className="w-full rounded-[10px]" />
|
||||
|
||||
{file ? (
|
||||
<div className="flex rounded-[10px] border p-2 font-sans text-sm font-medium text-[#525252] outline-none">
|
||||
<span className="line-clamp-1">{file.name}</span>
|
||||
<Button
|
||||
onClick={() => setFile(null)}
|
||||
className="absolute left-[-10px] top-[-16px] mt-2 h-fit border-none bg-red-200 p-1"
|
||||
size="library"
|
||||
>
|
||||
<X
|
||||
className="m-0 h-[12px] w-[12px] text-red-600"
|
||||
strokeWidth={3}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<FileUploader
|
||||
handleChange={handleChange}
|
||||
name="file"
|
||||
types={fileTypes}
|
||||
label={"Upload your agent here..!!"}
|
||||
uploadedLabel={"Uploading Successful"}
|
||||
required={true}
|
||||
hoverTitle={"Drop your agent here...!!"}
|
||||
maxSize={10}
|
||||
classes={"drop-style"}
|
||||
onDrop={() => {
|
||||
setisDroped(true);
|
||||
}}
|
||||
onSelect={() => {
|
||||
setisDroped(true);
|
||||
}}
|
||||
children={
|
||||
<div
|
||||
style={{
|
||||
minHeight: "150px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
outline: "none",
|
||||
fontFamily: "var(--font-geist-sans)",
|
||||
color: "#525252",
|
||||
fontSize: "14px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
{isDroped ? (
|
||||
<div className="flex items-center justify-center py-4">
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-b-2 border-t-2 border-neutral-800"></div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<span>Drop your agent here</span>
|
||||
<span>or</span>
|
||||
<span>Click to upload</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="agentName"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Agent name</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} className="w-full rounded-[10px]" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button
|
||||
onClick={handleUpload}
|
||||
variant="library_primary"
|
||||
size="library"
|
||||
className="mt-2 self-end"
|
||||
disabled={!file}
|
||||
>
|
||||
Upload Agent
|
||||
</Button>
|
||||
</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="agentDescription"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Description</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea {...field} className="w-full rounded-[10px]" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="agentFile"
|
||||
render={({ field }) => (
|
||||
<FormItem className="rounded-xl border-2 border-dashed border-neutral-300 hover:border-neutral-600">
|
||||
<FormControl>
|
||||
{field.value ? (
|
||||
<div className="flex rounded-[10px] border p-2 font-sans text-sm font-medium text-[#525252] outline-none">
|
||||
<span className="line-clamp-1">{field.value.name}</span>
|
||||
<Button
|
||||
onClick={() =>
|
||||
form.setValue("agentFile", undefined as any)
|
||||
}
|
||||
className="absolute left-[-10px] top-[-16px] mt-2 h-fit border-none bg-red-200 p-1"
|
||||
size="library"
|
||||
>
|
||||
<X
|
||||
className="m-0 h-[12px] w-[12px] text-red-600"
|
||||
strokeWidth={3}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<FileUploader
|
||||
handleChange={handleChange}
|
||||
name="file"
|
||||
types={fileTypes}
|
||||
label={"Upload your agent here..!!"}
|
||||
uploadedLabel={"Uploading Successful"}
|
||||
required={true}
|
||||
hoverTitle={"Drop your agent here...!!"}
|
||||
maxSize={10}
|
||||
classes={"drop-style"}
|
||||
onDrop={() => {
|
||||
setisDroped(true);
|
||||
}}
|
||||
onSelect={() => setisDroped(true)}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
minHeight: "150px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
outline: "none",
|
||||
fontFamily: "var(--font-geist-sans)",
|
||||
color: "#525252",
|
||||
fontSize: "14px",
|
||||
fontWeight: "500",
|
||||
borderWidth: "0px",
|
||||
}}
|
||||
>
|
||||
{isDroped ? (
|
||||
<div className="flex items-center justify-center py-4">
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-b-2 border-t-2 border-neutral-800"></div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<span>Drop your agent here</span>
|
||||
<span>or</span>
|
||||
<span>Click to upload</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</FileUploader>
|
||||
)}
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
variant="library_primary"
|
||||
size="library"
|
||||
className="mt-2 self-end"
|
||||
disabled={!agentObject}
|
||||
>
|
||||
Upload Agent
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@@ -80,7 +80,7 @@ const LibraryAgentListContainer: React.FC<
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3">
|
||||
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-4">
|
||||
{agents?.map((agent) => (
|
||||
<LibraryAgentCard
|
||||
key={agent.id}
|
||||
|
||||
@@ -32,7 +32,7 @@ interface LibraryPageProviderProps {
|
||||
export function LibraryPageProvider({ children }: LibraryPageProviderProps) {
|
||||
const [agents, setAgents] = useState<GraphMeta[]>([]);
|
||||
const [agentLoading, setAgentLoading] = useState<boolean>(true);
|
||||
const [searchTerm, setSearchTerm] = useState<string | undefined>(undefined);
|
||||
const [searchTerm, setSearchTerm] = useState<string | undefined>("");
|
||||
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
|
||||
const [libraryFilter, setLibraryFilter] = useState<LibraryAgentFilterEnum>(
|
||||
LibraryAgentFilterEnum.UPDATED_AT,
|
||||
|
||||
Reference in New Issue
Block a user