From 7ec9830b025755007da2d94cd52b796ea4caf621 Mon Sep 17 00:00:00 2001
From: Abhimanyu Yadav <122007096+Abhi1992002@users.noreply.github.com>
Date: Wed, 8 Jan 2025 15:14:35 +0530
Subject: [PATCH 1/3] fix(platform): Add custom fonts and update layout styles
(#9195)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- resolve #9187
### Changes 🏗️
Add support for `Inter`, `Poppins`, `Geist-Mono`, `Geist-Neue`, and
`Neue` in `layout.tsx` and `tailwind.config.ts`.
---------
Co-authored-by: Aarushi <50577581+aarushik93@users.noreply.github.com>
---
autogpt_platform/frontend/src/app/layout.tsx | 17 ++++++++++++++---
.../frontend/src/components/TallyPopup.tsx | 2 +-
autogpt_platform/frontend/tailwind.config.ts | 2 ++
3 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/autogpt_platform/frontend/src/app/layout.tsx b/autogpt_platform/frontend/src/app/layout.tsx
index 944e2301d3..60efbde136 100644
--- a/autogpt_platform/frontend/src/app/layout.tsx
+++ b/autogpt_platform/frontend/src/app/layout.tsx
@@ -1,6 +1,6 @@
import React from "react";
import type { Metadata } from "next";
-import { Inter } from "next/font/google";
+import { Inter, Poppins } from "next/font/google";
import { Providers } from "@/app/providers";
import { cn } from "@/lib/utils";
import { Navbar } from "@/components/agptui/Navbar";
@@ -10,8 +10,16 @@ import TallyPopupSimple from "@/components/TallyPopup";
import { GoogleAnalytics } from "@next/third-parties/google";
import { Toaster } from "@/components/ui/toaster";
import { IconType } from "@/components/ui/icons";
+import { GeistSans } from "geist/font/sans";
+import { GeistMono } from "geist/font/mono";
-const inter = Inter({ subsets: ["latin"] });
+// Fonts
+const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
+const poppins = Poppins({
+ subsets: ["latin"],
+ weight: ["400", "500", "600", "700"],
+ variable: "--font-poppins",
+});
export const metadata: Metadata = {
title: "NextGen AutoGPT",
@@ -24,7 +32,10 @@ export default async function RootLayout({
children: React.ReactNode;
}>) {
return (
-
+
{
diff --git a/autogpt_platform/frontend/tailwind.config.ts b/autogpt_platform/frontend/tailwind.config.ts
index 3101cd7cc5..d2616f98a4 100644
--- a/autogpt_platform/frontend/tailwind.config.ts
+++ b/autogpt_platform/frontend/tailwind.config.ts
@@ -18,6 +18,8 @@ const config = {
mono: ["var(--font-geist-mono)"],
// Include the custom font family
neue: ['"PP Neue Montreal TT"', "sans-serif"],
+ poppin: ["var(--font-poppins)"],
+ inter: ["var(--font-inter)"],
},
colors: {
border: "hsl(var(--border))",
From 43a79d063fa23af49d8c6adda50d1353dc35967f Mon Sep 17 00:00:00 2001
From: Abhimanyu Yadav <122007096+Abhi1992002@users.noreply.github.com>
Date: Wed, 8 Jan 2025 16:43:08 +0530
Subject: [PATCH 2/3] feat(platform) : Add api key generation frontend (#9212)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Allow users to create API keys for the AutoGPT platform. The backend is
already set up, and here I’ve added the frontend for it.
### Changes
1. Fix the `response-model` of the API keys endpoints.
2. Add a new page `/store/api_keys`.
3. Add an `APIKeySection` component to create, delete, and view all your
API keys.
---
.../backend/backend/server/routers/v1.py | 10 +-
.../src/app/store/(user)/api_keys/page.tsx | 11 +
.../frontend/src/app/store/(user)/layout.tsx | 3 +-
.../src/components/agptui/Sidebar.tsx | 20 +-
.../agptui/composite/APIKeySection.tsx | 296 ++++++++++++++++++
.../src/lib/autogpt-server-api/client.ts | 33 ++
.../src/lib/autogpt-server-api/types.ts | 33 ++
7 files changed, 399 insertions(+), 7 deletions(-)
create mode 100644 autogpt_platform/frontend/src/app/store/(user)/api_keys/page.tsx
create mode 100644 autogpt_platform/frontend/src/components/agptui/composite/APIKeySection.tsx
diff --git a/autogpt_platform/backend/backend/server/routers/v1.py b/autogpt_platform/backend/backend/server/routers/v1.py
index aca22e5c5d..9e8bf50d6d 100644
--- a/autogpt_platform/backend/backend/server/routers/v1.py
+++ b/autogpt_platform/backend/backend/server/routers/v1.py
@@ -541,7 +541,7 @@ def get_execution_schedules(
@v1_router.post(
"/api-keys",
- response_model=list[CreateAPIKeyResponse] | dict[str, str],
+ response_model=CreateAPIKeyResponse,
tags=["api-keys"],
dependencies=[Depends(auth_middleware)],
)
@@ -583,7 +583,7 @@ async def get_api_keys(
@v1_router.get(
"/api-keys/{key_id}",
- response_model=list[APIKeyWithoutHash] | dict[str, str],
+ response_model=APIKeyWithoutHash,
tags=["api-keys"],
dependencies=[Depends(auth_middleware)],
)
@@ -604,7 +604,7 @@ async def get_api_key(
@v1_router.delete(
"/api-keys/{key_id}",
- response_model=list[APIKeyWithoutHash] | dict[str, str],
+ response_model=APIKeyWithoutHash,
tags=["api-keys"],
dependencies=[Depends(auth_middleware)],
)
@@ -626,7 +626,7 @@ async def delete_api_key(
@v1_router.post(
"/api-keys/{key_id}/suspend",
- response_model=list[APIKeyWithoutHash] | dict[str, str],
+ response_model=APIKeyWithoutHash,
tags=["api-keys"],
dependencies=[Depends(auth_middleware)],
)
@@ -648,7 +648,7 @@ async def suspend_key(
@v1_router.put(
"/api-keys/{key_id}/permissions",
- response_model=list[APIKeyWithoutHash] | dict[str, str],
+ response_model=APIKeyWithoutHash,
tags=["api-keys"],
dependencies=[Depends(auth_middleware)],
)
diff --git a/autogpt_platform/frontend/src/app/store/(user)/api_keys/page.tsx b/autogpt_platform/frontend/src/app/store/(user)/api_keys/page.tsx
new file mode 100644
index 0000000000..87f3d58b4e
--- /dev/null
+++ b/autogpt_platform/frontend/src/app/store/(user)/api_keys/page.tsx
@@ -0,0 +1,11 @@
+import { APIKeysSection } from "@/components/agptui/composite/APIKeySection";
+
+const ApiKeysPage = () => {
+ return (
+
+ );
+};
+
+export default ApiKeysPage;
diff --git a/autogpt_platform/frontend/src/app/store/(user)/layout.tsx b/autogpt_platform/frontend/src/app/store/(user)/layout.tsx
index 0f90e5bd3b..64900562af 100644
--- a/autogpt_platform/frontend/src/app/store/(user)/layout.tsx
+++ b/autogpt_platform/frontend/src/app/store/(user)/layout.tsx
@@ -8,6 +8,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
{ text: "Creator Dashboard", href: "/store/dashboard" },
{ text: "Agent dashboard", href: "/store/agent-dashboard" },
{ text: "Integrations", href: "/store/integrations" },
+ { text: "API Keys", href: "/store/api_keys" },
{ text: "Profile", href: "/store/profile" },
{ text: "Settings", href: "/store/settings" },
],
@@ -17,7 +18,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
return (
-
{children}
+
{children}
);
}
diff --git a/autogpt_platform/frontend/src/components/agptui/Sidebar.tsx b/autogpt_platform/frontend/src/components/agptui/Sidebar.tsx
index 5cad3fddcc..545d82b76f 100644
--- a/autogpt_platform/frontend/src/components/agptui/Sidebar.tsx
+++ b/autogpt_platform/frontend/src/components/agptui/Sidebar.tsx
@@ -2,7 +2,7 @@ import * as React from "react";
import Link from "next/link";
import { Button } from "./Button";
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
-import { Menu } from "lucide-react";
+import { KeyIcon, Menu } from "lucide-react";
import {
IconDashboardLayout,
IconIntegrations,
@@ -58,6 +58,15 @@ export const Sidebar: React.FC = ({ linkGroups }) => {
Integrations
+
+
+
+ API Keys
+
+
= ({ linkGroups }) => {
Integrations
+
+
+
+ API Keys
+
+
([]);
+ const [isLoading, setIsLoading] = useState(true);
+ const [isCreateOpen, setIsCreateOpen] = useState(false);
+ const [isKeyDialogOpen, setIsKeyDialogOpen] = useState(false);
+ const [newKeyName, setNewKeyName] = useState("");
+ const [newKeyDescription, setNewKeyDescription] = useState("");
+ const [newApiKey, setNewApiKey] = useState("");
+ const [selectedPermissions, setSelectedPermissions] = useState<
+ APIKeyPermission[]
+ >([]);
+ const { toast } = useToast();
+ const api = useBackendAPI();
+
+ useEffect(() => {
+ loadAPIKeys();
+ }, []);
+
+ const loadAPIKeys = async () => {
+ setIsLoading(true);
+ try {
+ const keys = await api.listAPIKeys();
+ setApiKeys(keys.filter((key) => key.status === "ACTIVE"));
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const handleCreateKey = async () => {
+ try {
+ const response = await api.createAPIKey(
+ newKeyName,
+ selectedPermissions,
+ newKeyDescription,
+ );
+
+ setNewApiKey(response.plain_text_key);
+ setIsCreateOpen(false);
+ setIsKeyDialogOpen(true);
+ loadAPIKeys();
+ } catch (error) {
+ toast({
+ title: "Error",
+ description: "Failed to create AutoGPT Platform API key",
+ variant: "destructive",
+ });
+ }
+ };
+
+ const handleCopyKey = () => {
+ navigator.clipboard.writeText(newApiKey);
+ toast({
+ title: "Copied",
+ description: "AutoGPT Platform API key copied to clipboard",
+ });
+ };
+
+ const handleRevokeKey = async (keyId: string) => {
+ try {
+ await api.revokeAPIKey(keyId);
+ toast({
+ title: "Success",
+ description: "AutoGPT Platform API key revoked successfully",
+ });
+ loadAPIKeys();
+ } catch (error) {
+ toast({
+ title: "Error",
+ description: "Failed to revoke AutoGPT Platform API key",
+ variant: "destructive",
+ });
+ }
+ };
+
+ return (
+
+
+ AutoGPT Platform API Keys
+
+ Manage your AutoGPT Platform API keys for programmatic access
+
+
+
+
+
+
+
+
+
+ {isLoading ? (
+
+
+
+ ) : (
+ apiKeys.length > 0 && (
+
+
+
+ Name
+ API Key
+ Status
+ Created
+ Last Used
+
+
+
+
+ {apiKeys.map((key) => (
+
+ {key.name}
+
+
+ {`${key.prefix}******************${key.postfix}`}
+
+
+
+
+ {key.status}
+
+
+
+ {new Date(key.created_at).toLocaleDateString()}
+
+
+ {key.last_used_at
+ ? new Date(key.last_used_at).toLocaleDateString()
+ : "Never"}
+
+
+
+
+
+
+
+ handleRevokeKey(key.id)}
+ >
+ Revoke
+
+
+
+
+
+ ))}
+
+
+ )
+ )}
+
+
+ );
+}
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 1f74e67f63..169c17ac86 100644
--- a/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts
+++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts
@@ -29,6 +29,9 @@ import {
StoreReview,
ScheduleCreatable,
Schedule,
+ APIKeyPermission,
+ CreateAPIKeyResponse,
+ APIKey,
} from "./types";
import { createBrowserClient } from "@supabase/ssr";
import getServerSupabase from "../supabase/getServerSupabase";
@@ -221,6 +224,36 @@ export default class BackendAPI {
);
}
+ // API Key related requests
+ async createAPIKey(
+ name: string,
+ permissions: APIKeyPermission[],
+ description?: string,
+ ): Promise {
+ return this._request("POST", "/api-keys", {
+ name,
+ permissions,
+ description,
+ });
+ }
+
+ async listAPIKeys(): Promise {
+ return this._get("/api-keys");
+ }
+
+ async revokeAPIKey(keyId: string): Promise {
+ return this._request("DELETE", `/api-keys/${keyId}`);
+ }
+
+ async updateAPIKeyPermissions(
+ keyId: string,
+ permissions: APIKeyPermission[],
+ ): Promise {
+ return this._request("PUT", `/api-keys/${keyId}/permissions`, {
+ permissions,
+ });
+ }
+
/**
* @returns `true` if a ping event was received, `false` if provider doesn't support pinging but the webhook exists.
* @throws `Error` if the webhook does not exist.
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 2f2b8fb965..d383206668 100644
--- a/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts
+++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts
@@ -513,3 +513,36 @@ export type StoreReviewCreate = {
score: number;
comments?: string;
};
+
+// API Key Types
+
+export enum APIKeyPermission {
+ EXECUTE_GRAPH = "EXECUTE_GRAPH",
+ READ_GRAPH = "READ_GRAPH",
+ EXECUTE_BLOCK = "EXECUTE_BLOCK",
+ READ_BLOCK = "READ_BLOCK",
+}
+
+export enum APIKeyStatus {
+ ACTIVE = "ACTIVE",
+ REVOKED = "REVOKED",
+ SUSPENDED = "SUSPENDED",
+}
+
+export interface APIKey {
+ id: string;
+ name: string;
+ prefix: string;
+ postfix: string;
+ status: APIKeyStatus;
+ permissions: APIKeyPermission[];
+ created_at: string;
+ last_used_at?: string;
+ revoked_at?: string;
+ description?: string;
+}
+
+export interface CreateAPIKeyResponse {
+ api_key: APIKey;
+ plain_text_key: string;
+}
From e4d8502729a0a084f5b56e2881953b329d0af1a0 Mon Sep 17 00:00:00 2001
From: Bently
Date: Wed, 8 Jan 2025 12:18:42 +0000
Subject: [PATCH 3/3] fix(blocks): improve handling of plain text in send web
request block (#9219)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
### Changes 🏗️
This is to improve how we deal with plain text in the send web request
block.
- Plain text stays as plain text (regardless of JSON toggle)
- Valid JSON with JSON toggle enabled sends as JSON
- JSON-like data with JSON toggle disabled sends as form data
---
autogpt_platform/backend/backend/blocks/http.py | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/autogpt_platform/backend/backend/blocks/http.py b/autogpt_platform/backend/backend/blocks/http.py
index 2adb058309..099e2c3c1e 100644
--- a/autogpt_platform/backend/backend/blocks/http.py
+++ b/autogpt_platform/backend/backend/blocks/http.py
@@ -56,15 +56,24 @@ class SendWebRequestBlock(Block):
)
def run(self, input_data: Input, **kwargs) -> BlockOutput:
- if isinstance(input_data.body, str):
- input_data.body = json.loads(input_data.body)
+ body = input_data.body
+
+ if input_data.json_format:
+ if isinstance(body, str):
+ try:
+ # Try to parse as JSON first
+ body = json.loads(body)
+ except json.JSONDecodeError:
+ # If it's not valid JSON and just plain text,
+ # we should send it as plain text instead
+ input_data.json_format = False
response = requests.request(
input_data.method.value,
input_data.url,
headers=input_data.headers,
- json=input_data.body if input_data.json_format else None,
- data=input_data.body if not input_data.json_format else None,
+ json=body if input_data.json_format else None,
+ data=body if not input_data.json_format else None,
)
result = response.json() if input_data.json_format else response.text