Merge branch 'master' of github.com:Significant-Gravitas/AutoGPT into swiftyos/secrt-900-update-block-ui

This commit is contained in:
Zamil Majdy
2024-10-03 04:04:30 +02:00
41 changed files with 787 additions and 276 deletions

View File

@@ -88,7 +88,7 @@ class FindInDictionaryBlock(Block):
def __init__(self):
super().__init__(
id="b2g2c3d4-5e6f-7g8h-9i0j-k1l2m3n4o5p6",
id="0e50422c-6dee-4145-83d6-3a5a392f65de",
description="Lookup the given key in the input dictionary/object/list and return the value.",
input_schema=FindInDictionaryBlock.Input,
output_schema=FindInDictionaryBlock.Output,
@@ -429,7 +429,7 @@ class NoteBlock(Block):
def __init__(self):
super().__init__(
id="31d1064e-7446-4693-o7d4-65e5ca9110d1",
id="cc10ff7b-7753-4ff2-9af6-9399b1a7eddc",
description="This block is used to display a sticky note with the given text.",
categories={BlockCategory.BASIC},
input_schema=NoteBlock.Input,

View File

@@ -0,0 +1,42 @@
import codecs
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
class TextDecoderBlock(Block):
class Input(BlockSchema):
text: str = SchemaField(
description="A string containing escaped characters to be decoded",
placeholder='Your entire text block with \\n and \\" escaped characters',
)
class Output(BlockSchema):
decoded_text: str = SchemaField(
description="The decoded text with escape sequences processed"
)
def __init__(self):
super().__init__(
id="2570e8fe-8447-43ed-84c7-70d657923231",
description="Decodes a string containing escape sequences into actual text",
categories={BlockCategory.TEXT},
input_schema=TextDecoderBlock.Input,
output_schema=TextDecoderBlock.Output,
test_input={"text": """Hello\nWorld!\nThis is a \"quoted\" string."""},
test_output=[
(
"decoded_text",
"""Hello
World!
This is a "quoted" string.""",
)
],
)
def run(self, input_data: Input, **kwargs) -> BlockOutput:
try:
decoded_text = codecs.decode(input_data.text, "unicode_escape")
yield "decoded_text", decoded_text
except Exception as e:
yield "error", f"Error decoding text: {str(e)}"

View File

@@ -28,7 +28,7 @@ class ReadDiscordMessagesBlock(Block):
def __init__(self):
super().__init__(
id="d3f4g5h6-1i2j-3k4l-5m6n-7o8p9q0r1s2t", # Unique ID for the node
id="df06086a-d5ac-4abb-9996-2ad0acb2eff7",
input_schema=ReadDiscordMessagesBlock.Input, # Assign input schema
output_schema=ReadDiscordMessagesBlock.Output, # Assign output schema
description="Reads messages from a Discord channel using a bot token.",
@@ -146,7 +146,7 @@ class SendDiscordMessageBlock(Block):
def __init__(self):
super().__init__(
id="h1i2j3k4-5l6m-7n8o-9p0q-r1s2t3u4v5w6", # Unique ID for the node
id="d0822ab5-9f8a-44a3-8971-531dd0178b6b",
input_schema=SendDiscordMessageBlock.Input, # Assign input schema
output_schema=SendDiscordMessageBlock.Output, # Assign output schema
description="Sends a message to a Discord channel using a bot token.",

View File

@@ -43,7 +43,7 @@ class SendEmailBlock(Block):
def __init__(self):
super().__init__(
id="a1234567-89ab-cdef-0123-456789abcdef",
id="4335878a-394e-4e67-adf2-919877ff49ae",
description="This block sends an email using the provided SMTP credentials.",
categories={BlockCategory.OUTPUT},
input_schema=SendEmailBlock.Input,

View File

@@ -19,7 +19,7 @@ class StepThroughItemsBlock(Block):
def __init__(self):
super().__init__(
id="f8e7d6c5-b4a3-2c1d-0e9f-8g7h6i5j4k3l",
id="f66a3543-28d3-4ab5-8945-9b336371e2ce",
input_schema=StepThroughItemsBlock.Input,
output_schema=StepThroughItemsBlock.Output,
categories={BlockCategory.LOGIC},

View File

@@ -392,7 +392,7 @@ class AITextSummarizerBlock(Block):
def __init__(self):
super().__init__(
id="c3d4e5f6-7g8h-9i0j-1k2l-m3n4o5p6q7r8",
id="a0a69be1-4528-491c-a85a-a4ab6873e3f0",
description="Utilize a Large Language Model (LLM) to summarize a long text.",
categories={BlockCategory.AI, BlockCategory.TEXT},
input_schema=AITextSummarizerBlock.Input,
@@ -535,7 +535,7 @@ class AIConversationBlock(Block):
def __init__(self):
super().__init__(
id="c3d4e5f6-g7h8-i9j0-k1l2-m3n4o5p6q7r8",
id="32a87eab-381e-4dd4-bdb8-4c47151be35a",
description="Advanced LLM call that takes a list of messages and sends them to the language model.",
categories={BlockCategory.AI},
input_schema=AIConversationBlock.Input,

View File

@@ -43,7 +43,7 @@ class ReadRSSFeedBlock(Block):
def __init__(self):
super().__init__(
id="c6731acb-4105-4zp1-bc9b-03d0036h370g",
id="5ebe6768-8e5d-41e3-9134-1c7bd89a8d52",
input_schema=ReadRSSFeedBlock.Input,
output_schema=ReadRSSFeedBlock.Output,
description="Reads RSS feed entries from a given URL.",

View File

@@ -25,7 +25,7 @@ class GetWikipediaSummaryBlock(Block, GetRequest):
def __init__(self):
super().__init__(
id="h5e7f8g9-1b2c-3d4e-5f6g-7h8i9j0k1l2m",
id="f5b0f5d0-1862-4d61-94be-3ad0fa772760",
description="This block fetches the summary of a given topic from Wikipedia.",
categories={BlockCategory.SEARCH},
input_schema=GetWikipediaSummaryBlock.Input,
@@ -62,7 +62,7 @@ class SearchTheWebBlock(Block, GetRequest):
def __init__(self):
super().__init__(
id="b2c3d4e5-6f7g-8h9i-0j1k-l2m3n4o5p6q7",
id="87840993-2053-44b7-8da4-187ad4ee518c",
description="This block searches the internet for the given search query.",
categories={BlockCategory.SEARCH},
input_schema=SearchTheWebBlock.Input,
@@ -109,7 +109,7 @@ class ExtractWebsiteContentBlock(Block, GetRequest):
def __init__(self):
super().__init__(
id="a1b2c3d4-5e6f-7g8h-9i0j-k1l2m3n4o5p6", # Unique ID for the block
id="436c3984-57fd-4b85-8e9a-459b356883bd",
description="This block scrapes the content from the given web URL.",
categories={BlockCategory.SEARCH},
input_schema=ExtractWebsiteContentBlock.Input,

View File

@@ -77,7 +77,7 @@ class GetCurrentDateAndTimeBlock(Block):
def __init__(self):
super().__init__(
id="b29c1b50-5d0e-4d9f-8f9d-1b0e6fcbf0h2",
id="716a67b3-6760-42e7-86dc-18645c6e00fc",
description="This block outputs the current date and time.",
categories={BlockCategory.TEXT},
input_schema=GetCurrentDateAndTimeBlock.Input,

View File

@@ -1,5 +1,6 @@
import asyncio
import logging
from contextlib import asynccontextmanager
import uvicorn
from autogpt_libs.auth import parse_jwt_token
@@ -16,7 +17,17 @@ from backend.util.settings import Config, Settings
logger = logging.getLogger(__name__)
settings = Settings()
app = FastAPI()
@asynccontextmanager
async def lifespan(app: FastAPI):
await event_queue.connect()
manager = get_connection_manager()
asyncio.create_task(event_broadcaster(manager))
yield
await event_queue.close()
app = FastAPI(lifespan=lifespan)
event_queue = AsyncRedisEventQueue()
_connection_manager = None
@@ -37,18 +48,6 @@ def get_connection_manager():
return _connection_manager
@app.on_event("startup")
async def startup_event():
await event_queue.connect()
manager = get_connection_manager()
asyncio.create_task(event_broadcaster(manager))
@app.on_event("shutdown")
async def shutdown_event():
await event_queue.close()
async def event_broadcaster(manager: ConnectionManager):
while True:
event = await event_queue.get()

View File

@@ -1,5 +1,5 @@
import json
from typing import Any, Type, TypeVar, get_origin
from typing import Any, Type, TypeVar, get_args, get_origin
class ConversionError(Exception):
@@ -103,26 +103,75 @@ def __convert_bool(value: Any) -> bool:
def convert(value: Any, target_type: Type):
target_type = get_origin(target_type) or target_type
if target_type not in [list, dict, tuple, str, set, int, float, bool]:
origin = get_origin(target_type)
args = get_args(target_type)
if origin is None:
origin = target_type
if origin not in [list, dict, tuple, str, set, int, float, bool]:
return value
if isinstance(value, target_type):
return value
if target_type is list:
return __convert_list(value)
elif target_type is dict:
return __convert_dict(value)
elif target_type is tuple:
return __convert_tuple(value)
elif target_type is str:
return __convert_str(value)
elif target_type is set:
return __convert_set(value)
elif target_type is int:
return __convert_num(value, int)
elif target_type is float:
return __convert_num(value, float)
elif target_type is bool:
return __convert_bool(value)
# Handle the case when value is already of the target type
if isinstance(value, origin):
if not args:
return value
else:
# Need to convert elements
if origin is list:
return [convert(v, args[0]) for v in value]
elif origin is tuple:
# Tuples can have multiple types
if len(args) == 1:
return tuple(convert(v, args[0]) for v in value)
else:
return tuple(convert(v, t) for v, t in zip(value, args))
elif origin is dict:
key_type, val_type = args
return {
convert(k, key_type): convert(v, val_type) for k, v in value.items()
}
elif origin is set:
return {convert(v, args[0]) for v in value}
else:
return value
else:
return value
# Need to convert value to the origin type
if origin is list:
value = __convert_list(value)
if args:
return [convert(v, args[0]) for v in value]
else:
return value
elif origin is dict:
value = __convert_dict(value)
if args:
key_type, val_type = args
return {
convert(k, key_type): convert(v, val_type) for k, v in value.items()
}
else:
return value
elif origin is tuple:
value = __convert_tuple(value)
if args:
if len(args) == 1:
return tuple(convert(v, args[0]) for v in value)
else:
return tuple(convert(v, t) for v, t in zip(value, args))
else:
return value
elif origin is str:
return __convert_str(value)
elif origin is set:
value = __convert_set(value)
if args:
return {convert(v, args[0]) for v in value}
else:
return value
elif origin is int:
return __convert_num(value, int)
elif origin is float:
return __convert_num(value, float)
elif origin is bool:
return __convert_bool(value)
else:
return value

View File

@@ -8,7 +8,7 @@
"nodes": [
{
"id": "b8138bca-7892-42c2-9594-a845d3483413",
"block_id": "d3f4g5h6-1i2j-3k4l-5m6n-7o8p9q0r1s2t",
"block_id": "df06086a-d5ac-4abb-9996-2ad0acb2eff7",
"input_default": {},
"metadata": {
"position": {
@@ -59,7 +59,7 @@
},
{
"id": "dda2d061-2ef9-4dc5-9433-918c8395a4ac",
"block_id": "h1i2j3k4-5l6m-7n8o-9p0q-r1s2t3u4v5w6",
"block_id": "d0822ab5-9f8a-44a3-8971-531dd0178b6b",
"input_default": {},
"metadata": {
"position": {

View File

@@ -110,7 +110,7 @@
},
{
"id": "b45cfa51-5ead-4621-9f1c-f847dfea3e4c",
"block_id": "d3f4g5h6-1i2j-3k4l-5m6n-7o8p9q0r1s2t",
"block_id": "df06086a-d5ac-4abb-9996-2ad0acb2eff7",
"input_default": {},
"metadata": {
"position": {
@@ -146,7 +146,7 @@
},
{
"id": "8eedcf71-1146-4f54-b522-bf9b6e2d26b2",
"block_id": "h1i2j3k4-5l6m-7n8o-9p0q-r1s2t3u4v5w6",
"block_id": "d0822ab5-9f8a-44a3-8971-531dd0178b6b",
"input_default": {},
"metadata": {
"position": {
@@ -197,7 +197,7 @@
},
{
"id": "a568daee-45d2-4429-bf33-cbe9e1261f7b",
"block_id": "c3d4e5f6-g7h8-i9j0-k1l2-m3n4o5p6q7r8",
"block_id": "32a87eab-381e-4dd4-bdb8-4c47151be35a",
"input_default": {
"model": "llama-3.1-70b-versatile",
"max_tokens": 2000

View File

@@ -8,7 +8,7 @@
"nodes": [
{
"id": "60ba4aac-1751-4be7-8745-1bd32191d4a2",
"block_id": "d3f4g5h6-1i2j-3k4l-5m6n-7o8p9q0r1s2t",
"block_id": "df06086a-d5ac-4abb-9996-2ad0acb2eff7",
"input_default": {},
"metadata": {
"position": {
@@ -45,7 +45,7 @@
},
{
"id": "5658c4f7-8e67-4d30-93f2-157bdbd3ef87",
"block_id": "b2c3d4e5-6f7g-8h9i-0j1k-l2m3n4o5p6q7",
"block_id": "87840993-2053-44b7-8da4-187ad4ee518c",
"input_default": {},
"metadata": {
"position": {
@@ -118,7 +118,7 @@
},
{
"id": "f3d62f22-d193-4f04-85d2-164200fca4c0",
"block_id": "h1i2j3k4-5l6m-7n8o-9p0q-r1s2t3u4v5w6",
"block_id": "d0822ab5-9f8a-44a3-8971-531dd0178b6b",
"input_default": {},
"metadata": {
"position": {

View File

@@ -8,7 +8,7 @@
"nodes": [
{
"id": "382efac9-3def-4baf-b16a-d6d2512a5c8b",
"block_id": "b2c3d4e5-6f7g-8h9i-0j1k-l2m3n4o5p6q7",
"block_id": "87840993-2053-44b7-8da4-187ad4ee518c",
"input_default": {
"query": "19th July 2024 Microsoft Blackout"
},
@@ -44,7 +44,7 @@
},
{
"id": "0cd8f670-8956-4942-ba28-aee732ec783f",
"block_id": "b2g2c3d4-5e6f-7g8h-9i0j-k1l2m3n4o5p6",
"block_id": "0e50422c-6dee-4145-83d6-3a5a392f65de",
"input_default": {
"key": "TITLE"
},
@@ -57,7 +57,7 @@
},
{
"id": "4a15b6b9-036d-43d3-915a-7e931fbc6522",
"block_id": "b2g2c3d4-5e6f-7g8h-9i0j-k1l2m3n4o5p6",
"block_id": "0e50422c-6dee-4145-83d6-3a5a392f65de",
"input_default": {
"key": "CONTENT"
},

View File

@@ -0,0 +1,18 @@
-- Update AgentBlock IDs: this should cascade to the AgentNode and UserBlockCredit tables
UPDATE "AgentBlock"
SET "id" = CASE
WHEN "id" = 'a1b2c3d4-5e6f-7g8h-9i0j-k1l2m3n4o5p6' THEN '436c3984-57fd-4b85-8e9a-459b356883bd'
WHEN "id" = 'b2g2c3d4-5e6f-7g8h-9i0j-k1l2m3n4o5p6' THEN '0e50422c-6dee-4145-83d6-3a5a392f65de'
WHEN "id" = 'c3d4e5f6-7g8h-9i0j-1k2l-m3n4o5p6q7r8' THEN 'a0a69be1-4528-491c-a85a-a4ab6873e3f0'
WHEN "id" = 'c3d4e5f6-g7h8-i9j0-k1l2-m3n4o5p6q7r8' THEN '32a87eab-381e-4dd4-bdb8-4c47151be35a'
WHEN "id" = 'b2c3d4e5-6f7g-8h9i-0j1k-l2m3n4o5p6q7' THEN '87840993-2053-44b7-8da4-187ad4ee518c'
WHEN "id" = 'h1i2j3k4-5l6m-7n8o-9p0q-r1s2t3u4v5w6' THEN 'd0822ab5-9f8a-44a3-8971-531dd0178b6b'
WHEN "id" = 'd3f4g5h6-1i2j-3k4l-5m6n-7o8p9q0r1s2t' THEN 'df06086a-d5ac-4abb-9996-2ad0acb2eff7'
WHEN "id" = 'h5e7f8g9-1b2c-3d4e-5f6g-7h8i9j0k1l2m' THEN 'f5b0f5d0-1862-4d61-94be-3ad0fa772760'
WHEN "id" = 'a1234567-89ab-cdef-0123-456789abcdef' THEN '4335878a-394e-4e67-adf2-919877ff49ae'
WHEN "id" = 'f8e7d6c5-b4a3-2c1d-0e9f-8g7h6i5j4k3l' THEN 'f66a3543-28d3-4ab5-8945-9b336371e2ce'
WHEN "id" = 'b29c1b50-5d0e-4d9f-8f9d-1b0e6fcbf0h2' THEN '716a67b3-6760-42e7-86dc-18645c6e00fc'
WHEN "id" = '31d1064e-7446-4693-o7d4-65e5ca9110d1' THEN 'cc10ff7b-7753-4ff2-9af6-9399b1a7eddc'
WHEN "id" = 'c6731acb-4105-4zp1-bc9b-03d0036h370g' THEN '5ebe6768-8e5d-41e3-9134-1c7bd89a8d52'
ELSE "id"
END;

View File

@@ -27,5 +27,6 @@ def test_type_conversion():
from typing import List
# assert convert("5", List[int]) == [5]
assert convert("5", List[int]) == [5]
assert convert("[5,4,2]", List[int]) == [5, 4, 2]
assert convert([5, 4, 2], List[str]) == ["5", "4", "2"]

View File

@@ -207,6 +207,7 @@ services:
# - NEXT_PUBLIC_AGPT_SERVER_URL=http://localhost:8006/api
# - NEXT_PUBLIC_AGPT_WS_SERVER_URL=ws://localhost:8001/ws
# - NEXT_PUBLIC_AGPT_MARKETPLACE_URL=http://localhost:8015/api/v1/market
# - NEXT_PUBLIC_BEHAVE_AS=LOCAL
# ports:
# - "3000:3000"
# networks:

View File

@@ -13,3 +13,6 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAic
## Only used if you're using Supabase and OAuth
AUTH_CALLBACK_URL=http://localhost:3000/auth/callback
GA_MEASUREMENT_ID=G-FH2XK2W4GN
# When running locally, set NEXT_PUBLIC_BEHAVE_AS=CLOUD to use the a locally hosted marketplace (as is typical in development, and the cloud deployment), otherwise set it to LOCAL to have the marketplace open in a new tab
NEXT_PUBLIC_BEHAVE_AS=LOCAL

View File

@@ -67,7 +67,7 @@
},
"devDependencies": {
"@playwright/test": "^1.47.1",
"@types/node": "^20",
"@types/node": "^22.7.3",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react-modal": "^3.16.3",

View File

@@ -6,8 +6,9 @@ import Image from "next/image";
import getServerUser from "@/hooks/getServerUser";
import ProfileDropdown from "./ProfileDropdown";
import { IconCircleUser, IconMenu } from "@/components/ui/icons";
import CreditButton from "@/components/CreditButton";
import { NavBarButtons } from "./NavBarButtons";
import CreditButton from "@/components/nav/CreditButton";
import { NavBarButtons } from "./nav/NavBarButtons";
export async function NavBar() {
const isAvailable = Boolean(

View File

@@ -1,47 +0,0 @@
"use client";
import Link from "next/link";
import { BsBoxes } from "react-icons/bs";
import { LuLaptop } from "react-icons/lu";
import { LuShoppingCart } from "react-icons/lu";
import { cn } from "@/lib/utils";
import { usePathname } from "next/navigation";
export function NavBarButtons({ className }: { className?: string }) {
"use client";
const pathname = usePathname();
const buttons = [
{
href: "/marketplace",
text: "Marketplace",
icon: <LuShoppingCart />,
},
{
href: "/",
text: "Monitor",
icon: <LuLaptop />,
},
{
href: "/build",
text: "Build",
icon: <BsBoxes />,
},
];
const activeButton = buttons.find((button) => button.href === pathname);
return buttons.map((button) => (
<Link
key={button.href}
href={button.href}
className={cn(
className,
"rounded-xl p-3",
activeButton === button ? "button bg-gray-950 text-white" : "",
)}
>
{button.icon} {button.text}
</Link>
));
}

View File

@@ -33,6 +33,44 @@ const formSchema = z.object({
importAsTemplate: z.boolean(),
});
function updateBlockIDs(graph: Graph) {
// https://github.com/Significant-Gravitas/AutoGPT/issues/8223
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 AgentImportForm: React.FC<
React.FormHTMLAttributes<HTMLFormElement>
> = ({ className, ...props }) => {
@@ -116,6 +154,7 @@ export const AgentImportForm: React.FC<
);
}
const agent = obj as Graph;
updateBlockIDs(agent);
setAgentObject(agent);
form.setValue("agentName", agent.name);
form.setValue("agentDescription", agent.description);

View File

@@ -1,81 +1,44 @@
"use client";
import { useState } from "react";
import Link from "next/link";
import {
ArrowLeft,
Download,
Calendar,
Tag,
ChevronDown,
ChevronUp,
} from "lucide-react";
import { ArrowLeft, Download, Calendar, Tag } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
AgentDetailResponse,
InstallationLocation,
} from "@/lib/marketplace-api";
import dynamic from "next/dynamic";
import { Node, Edge } from "@xyflow/react";
import MarketplaceAPI from "@/lib/marketplace-api";
import AutoGPTServerAPI, { GraphCreatable } from "@/lib/autogpt-server-api";
const ReactFlow = dynamic(
() => import("@xyflow/react").then((mod) => mod.ReactFlow),
{ ssr: false },
);
const Controls = dynamic(
() => import("@xyflow/react").then((mod) => mod.Controls),
{ ssr: false },
);
const Background = dynamic(
() => import("@xyflow/react").then((mod) => mod.Background),
{ ssr: false },
);
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) => {
let label = node.block_id || "Unknown";
try {
label = beautifyString(label);
} catch (error) {
console.error("Error beautifying node label:", error);
}
async function downloadAgent(id: string): Promise<void> {
const api = new MarketplaceAPI();
try {
const file = await api.downloadAgentFile(id);
console.debug(`Agent file downloaded:`, file);
return {
id: node.id,
position: node.metadata.position || { x: 0, y: 0 },
data: {
label,
blockId: node.block_id,
inputDefault: node.input_default || {},
...node, // Include all other node data
},
type: "custom",
};
});
// Create a Blob from the file content
const blob = new Blob([file], { type: "application/json" });
const edges: Edge[] = graph.links.map((link: any) => ({
id: `${link.source_id}-${link.sink_id}`,
source: link.source_id,
target: link.sink_id,
sourceHandle: link.source_name,
targetHandle: link.sink_name,
type: "custom",
data: {
sourceId: link.source_id,
targetId: link.sink_id,
sourceName: link.source_name,
targetName: link.sink_name,
isStatic: link.is_static,
},
}));
// Create a temporary URL for the Blob
const url = window.URL.createObjectURL(blob);
return { nodes, edges };
// Create a temporary anchor element
const a = document.createElement("a");
a.href = url;
a.download = `agent_${id}.json`; // Set the filename
// Append the anchor to the body, click it, and remove it
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
// Revoke the temporary URL
window.URL.revokeObjectURL(url);
} catch (error) {
console.error(`Error downloading agent:`, error);
throw error;
}
}
async function installGraph(id: string): Promise<void> {
@@ -84,12 +47,12 @@ async function installGraph(id: string): Promise<void> {
"http://localhost:8015/api/v1/market";
const api = new MarketplaceAPI(apiUrl);
const serverAPIUrl = process.env.AGPT_SERVER_API_URL;
const serverAPIUrl = process.env.NEXT_PUBLIC_AGPT_SERVER_API_URL;
const serverAPI = new AutoGPTServerAPI(serverAPIUrl);
try {
console.log(`Installing agent with id: ${id}`);
console.debug(`Installing agent with id: ${id}`);
let agent = await api.downloadAgent(id);
console.log(`Agent downloaded:`, agent);
console.debug(`Agent downloaded:`, agent);
const data: GraphCreatable = {
id: agent.id,
version: agent.version,
@@ -109,7 +72,7 @@ async function installGraph(id: string): Promise<void> {
installation_location: InstallationLocation.CLOUD,
},
});
console.log(`Agent installed successfully`, result);
console.debug(`Agent installed successfully`, result);
} catch (error) {
console.error(`Error installing agent:`, error);
throw error;
@@ -117,9 +80,6 @@ async function installGraph(id: string): Promise<void> {
}
function AgentDetailContent({ agent }: { agent: AgentDetailResponse }) {
const [isGraphExpanded, setIsGraphExpanded] = useState(false);
const { nodes, edges } = convertGraphToReactFlow(agent.graph);
return (
<div className="mx-auto max-w-7xl px-4 py-4 sm:px-6 lg:px-8">
<div className="mb-4 flex items-center justify-between">
@@ -130,13 +90,22 @@ function AgentDetailContent({ agent }: { agent: AgentDetailResponse }) {
<ArrowLeft className="mr-2" size={20} />
Back to Marketplace
</Link>
<Button
onClick={() => installGraph(agent.id)}
className="inline-flex items-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
<Download className="mr-2" size={16} />
Download Agent
</Button>
<div className="flex space-x-4">
<Button
onClick={() => installGraph(agent.id)}
className="inline-flex items-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
<Download className="mr-2" size={16} />
Save to Templates
</Button>
<Button
onClick={() => downloadAgent(agent.id)}
className="inline-flex items-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
<Download className="mr-2" size={16} />
Download Agent
</Button>
</div>
</div>
<div className="overflow-hidden bg-white shadow sm:rounded-lg">
<div className="px-4 py-5 sm:px-6">

View File

@@ -0,0 +1,27 @@
import { ButtonHTMLAttributes } from "react";
import React from "react";
interface MarketPopupProps extends ButtonHTMLAttributes<HTMLButtonElement> {
marketplaceUrl?: string;
}
export default function MarketPopup({
className = "",
marketplaceUrl = "http://platform.agpt.co/marketplace",
children,
...props
}: MarketPopupProps) {
const openMarketplacePopup = () => {
window.open(
marketplaceUrl,
"popupWindow",
"width=600,height=400,toolbar=no,menubar=no,scrollbars=no",
);
};
return (
<button onClick={openMarketplacePopup} className={className} {...props}>
{children}
</button>
);
}

View File

@@ -0,0 +1,74 @@
"use client";
import React from "react";
import Link from "next/link";
import { BsBoxes } from "react-icons/bs";
import { LuLaptop, LuShoppingCart } from "react-icons/lu";
import { BehaveAs, cn } from "@/lib/utils";
import { usePathname } from "next/navigation";
import { getBehaveAs } from "@/lib/utils";
import MarketPopup from "./MarketPopup";
export function NavBarButtons({ className }: { className?: string }) {
const pathname = usePathname();
const buttons = [
{
href: "/",
text: "Monitor",
icon: <LuLaptop />,
},
{
href: "/build",
text: "Build",
icon: <BsBoxes />,
},
];
const isCloud = getBehaveAs() === BehaveAs.CLOUD;
return (
<>
{buttons.map((button) => {
const isActive = button.href === pathname;
return (
<Link
key={button.href}
href={button.href}
className={cn(
className,
"flex items-center gap-2 rounded-xl p-3",
isActive
? "bg-gray-950 text-white"
: "text-muted-foreground hover:text-foreground",
)}
>
{button.icon} {button.text}
</Link>
);
})}
{isCloud ? (
<Link
href="/marketplace"
className={cn(
className,
"flex items-center gap-2 rounded-xl p-3",
pathname === "/marketplace"
? "bg-gray-950 text-white"
: "text-muted-foreground hover:text-foreground",
)}
>
<LuShoppingCart /> Marketplace
</Link>
) : (
<MarketPopup
className={cn(
className,
"flex items-center gap-2 rounded-xl p-3 text-muted-foreground hover:text-foreground",
)}
>
<LuShoppingCart /> Marketplace
</MarketPopup>
)}
</>
);
}

View File

@@ -201,3 +201,14 @@ export function filterBlocksByType<T>(
): T[] {
return blocks.filter(predicate);
}
export enum BehaveAs {
CLOUD = "CLOUD",
LOCAL = "LOCAL",
}
export function getBehaveAs(): BehaveAs {
return process.env.NEXT_PUBLIC_BEHAVE_AS === "CLOUD"
? BehaveAs.CLOUD
: BehaveAs.LOCAL;
}

View File

@@ -740,13 +740,13 @@
integrity sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==
"@radix-ui/react-context-menu@^2.2.1":
version "2.2.1"
resolved "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.2.1.tgz"
integrity sha512-wvMKKIeb3eOrkJ96s722vcidZ+2ZNfcYZWBPRHIB1VWrF+fiF851Io6LX0kmK5wTDQFKdulCCKJk2c3SBaQHvA==
version "2.2.2"
resolved "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.2.2.tgz"
integrity sha512-99EatSTpW+hRYHt7m8wdDlLtkmTovEe8Z/hnxUPV+SKuuNL5HWNhQI4QSdjZqNSgXHay2z4M3Dym73j9p2Gx5Q==
dependencies:
"@radix-ui/primitive" "1.1.0"
"@radix-ui/react-context" "1.1.0"
"@radix-ui/react-menu" "2.1.1"
"@radix-ui/react-context" "1.1.1"
"@radix-ui/react-menu" "2.1.2"
"@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-use-callback-ref" "1.1.0"
"@radix-ui/react-use-controllable-state" "1.1.0"
@@ -763,6 +763,11 @@
resolved "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz"
integrity sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==
"@radix-ui/react-context@1.1.1":
version "1.1.1"
resolved "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz"
integrity sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==
"@radix-ui/react-dialog@^1.1.1":
version "1.1.1"
resolved "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.1.tgz"
@@ -832,6 +837,17 @@
"@radix-ui/react-use-callback-ref" "1.1.0"
"@radix-ui/react-use-escape-keydown" "1.1.0"
"@radix-ui/react-dismissable-layer@1.1.1":
version "1.1.1"
resolved "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz"
integrity sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==
dependencies:
"@radix-ui/primitive" "1.1.0"
"@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-use-callback-ref" "1.1.0"
"@radix-ui/react-use-escape-keydown" "1.1.0"
"@radix-ui/react-dropdown-menu@^2.1.1":
version "2.1.1"
resolved "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.1.tgz"
@@ -857,6 +873,11 @@
resolved "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz"
integrity sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==
"@radix-ui/react-focus-guards@1.1.1":
version "1.1.1"
resolved "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz"
integrity sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==
"@radix-ui/react-focus-scope@1.0.4":
version "1.0.4"
resolved "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz"
@@ -927,6 +948,30 @@
aria-hidden "^1.1.1"
react-remove-scroll "2.5.7"
"@radix-ui/react-menu@2.1.2":
version "2.1.2"
resolved "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.2.tgz"
integrity sha512-lZ0R4qR2Al6fZ4yCCZzu/ReTFrylHFxIqy7OezIpWF4bL0o9biKo0pFIvkaew3TyZ9Fy5gYVrR5zCGZBVbO1zg==
dependencies:
"@radix-ui/primitive" "1.1.0"
"@radix-ui/react-collection" "1.1.0"
"@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-context" "1.1.1"
"@radix-ui/react-direction" "1.1.0"
"@radix-ui/react-dismissable-layer" "1.1.1"
"@radix-ui/react-focus-guards" "1.1.1"
"@radix-ui/react-focus-scope" "1.1.0"
"@radix-ui/react-id" "1.1.0"
"@radix-ui/react-popper" "1.2.0"
"@radix-ui/react-portal" "1.1.2"
"@radix-ui/react-presence" "1.1.1"
"@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-roving-focus" "1.1.0"
"@radix-ui/react-slot" "1.1.0"
"@radix-ui/react-use-callback-ref" "1.1.0"
aria-hidden "^1.1.1"
react-remove-scroll "2.6.0"
"@radix-ui/react-popover@^1.1.1":
version "1.1.1"
resolved "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.1.tgz"
@@ -980,6 +1025,14 @@
"@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-use-layout-effect" "1.1.0"
"@radix-ui/react-portal@1.1.2":
version "1.1.2"
resolved "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz"
integrity sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==
dependencies:
"@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-use-layout-effect" "1.1.0"
"@radix-ui/react-presence@1.0.1":
version "1.0.1"
resolved "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz"
@@ -997,6 +1050,14 @@
"@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-use-layout-effect" "1.1.0"
"@radix-ui/react-presence@1.1.1":
version "1.1.1"
resolved "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz"
integrity sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==
dependencies:
"@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-use-layout-effect" "1.1.0"
"@radix-ui/react-primitive@1.0.3":
version "1.0.3"
resolved "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz"
@@ -1701,10 +1762,10 @@
dependencies:
"@types/node" "*"
"@types/node@*", "@types/node@^20":
version "20.16.1"
resolved "https://registry.npmjs.org/@types/node/-/node-20.16.1.tgz"
integrity sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==
"@types/node@*", "@types/node@^22.7.3":
version "22.7.4"
resolved "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz"
integrity sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==
dependencies:
undici-types "~6.19.2"
@@ -4998,7 +5059,7 @@ react-modal@^3.16.1:
react-lifecycles-compat "^3.0.0"
warning "^4.0.3"
react-remove-scroll-bar@^2.3.3, react-remove-scroll-bar@^2.3.4:
react-remove-scroll-bar@^2.3.3, react-remove-scroll-bar@^2.3.4, react-remove-scroll-bar@^2.3.6:
version "2.3.6"
resolved "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz"
integrity sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==
@@ -5028,6 +5089,17 @@ react-remove-scroll@2.5.7:
use-callback-ref "^1.3.0"
use-sidecar "^1.1.2"
react-remove-scroll@2.6.0:
version "2.6.0"
resolved "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz"
integrity sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==
dependencies:
react-remove-scroll-bar "^2.3.6"
react-style-singleton "^2.2.1"
tslib "^2.1.0"
use-callback-ref "^1.3.0"
use-sidecar "^1.1.2"
react-shepherd@^6.1.1:
version "6.1.1"
resolved "https://registry.npmjs.org/react-shepherd/-/react-shepherd-6.1.1.tgz"

View File

@@ -61,4 +61,5 @@ env:
GOOGLE_CLIENT_ID: ""
GOOGLE_CLIENT_SECRET: ""
NEXT_PUBLIC_SUPABASE_URL: ""
NEXT_PUBLIC_SUPABASE_ANON_KEY: ""
NEXT_PUBLIC_SUPABASE_ANON_KEY: ""
NEXT_PUBLIC_BEHAVE_AS: "CLOUD"

View File

@@ -0,0 +1,26 @@
apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
name: {{ include "autogpt-server.fullname" . }}-backend-config
spec:
customRequestHeaders:
headers:
- "Access-Control-Allow-Origin:{{ .Values.cors.allowOrigin }}"
- "Access-Control-Allow-Methods:{{ .Values.cors.allowMethods | join "," }}"
- "Access-Control-Allow-Headers:{{ .Values.cors.allowHeaders | join "," }}"
- "Access-Control-Max-Age:{{ .Values.cors.maxAge }}"
{{- if .Values.cors.allowCredentials }}
- "Access-Control-Allow-Credentials:true"
{{- end }}
customResponseHeaders:
headers:
- "Access-Control-Allow-Origin:{{ .Values.cors.allowOrigin }}"
- "Access-Control-Allow-Methods:{{ .Values.cors.allowMethods | join "," }}"
- "Access-Control-Allow-Headers:{{ .Values.cors.allowHeaders | join "," }}"
- "Access-Control-Max-Age:{{ .Values.cors.maxAge }}"
{{- if .Values.cors.allowCredentials }}
- "Access-Control-Allow-Credentials:true"
{{- end }}
timeoutSec: 1800
connectionDraining:
drainingTimeoutSec: 1800

View File

@@ -0,0 +1,68 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "autogpt-server.fullname" . }}-executor
labels:
app.kubernetes.io/component: executor
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
app.kubernetes.io/component: executor
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
app.kubernetes.io/component: executor
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "autogpt-server.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
envFrom:
- configMapRef:
name: {{ include "autogpt-server.fullname" . }}-config
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["poetry", "run", "executor"]
ports:
- name: http
containerPort: {{ .Values.serviceExecutor.port }}
protocol: TCP
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.volumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.volumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -44,6 +44,9 @@ spec:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
- name: pyro
containerPort: 8004
protocol: TCP
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }}
readinessProbe:
@@ -54,15 +57,6 @@ spec:
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
- name: cloud-sql-proxy
image: "{{ .Values.cloudSqlProxy.image.repository }}:{{ .Values.cloudSqlProxy.image.tag }}"
args:
- "--structured-logs"
{{- if .Values.cloudSqlProxy.usePrivateIp }}
- "--private-ip"
{{- end }}
- "--port={{ .Values.cloudSqlProxy.port }}"
- "{{ .Values.cloudSqlProxy.instanceConnectionName }}"
{{- with .Values.volumes }}
volumes:
{{- toYaml . | nindent 8 }}

View File

@@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "autogpt-server.fullname" . }}-executor
labels:
app.kubernetes.io/component: executor
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.serviceExecutor.type }}
ports:
- port: {{ .Values.serviceExecutor.port }}
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/component: executor

View File

@@ -15,5 +15,11 @@ spec:
targetPort: http
protocol: TCP
name: http
{{- if .Values.service.pyroDaemonPort }}
- port: {{ .Values.service.pyroDaemonPort }}
targetPort: pyro
protocol: TCP
name: pyro
{{- end }}
selector:
{{- include "autogpt-server.selectorLabels" . | nindent 4 }}
{{- include "autogpt-server.selectorLabels" . | nindent 4 }}

View File

@@ -12,10 +12,18 @@ serviceAccount:
service:
type: ClusterIP
port: 8000
targetPort: 8000
port: 8006
pyroDaemonPort: 8004
annotations:
cloud.google.com/neg: '{"ingress": true}'
beta.cloud.google.com/backend-config: '{"default": "autogpt-server-backend-config"}'
serviceExecutor:
type: ClusterIP
port: 8002
targetPort: 8002
annotations:
beta.cloud.google.com/backend-config: '{"default": "autogpt-server-backend-config"}'
ingress:
enabled: true
@@ -33,12 +41,12 @@ ingress:
backend:
service:
name: autogpt-server
port: 8000
port: 8006
defaultBackend:
service:
name: autogpt-server
port:
number: 8000
number: 8006
resources:
requests:
@@ -46,12 +54,12 @@ resources:
memory: 256Mi
limits:
cpu: 2
memory: 2Gi
memory: 10Gi
livenessProbe:
httpGet:
path: /docs
port: 8000
port: 8006
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
@@ -59,7 +67,7 @@ livenessProbe:
readinessProbe:
httpGet:
path: /docs
port: 8000
port: 8006
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
@@ -67,28 +75,39 @@ readinessProbe:
domain: "dev-server.agpt.co"
cloudSqlProxy:
image:
repository: gcr.io/cloud-sql-connectors/cloud-sql-proxy
tag: 2.11.4
instanceConnectionName: "agpt-dev:us-central1:agpt-server-dev"
port: 5432
resources:
requests:
memory: "2Gi"
cpu: "1"
cors:
allowOrigin: "https://dev-builder.agpt.co"
allowMethods:
- "GET"
- "POST"
- "PUT"
- "DELETE"
- "OPTIONS"
allowHeaders:
- "Content-Type"
- "Authorization"
maxAge: 3600
allowCredentials: true
env:
APP_ENV: "dev"
PYRO_HOST: "0.0.0.0"
NUM_GRAPH_WORKERS: 100
NUM_NODE_WORKERS: 5
ENABLE_AUTH: "true"
REDIS_HOST: "redis-dev-master.redis-dev.svc.cluster.local"
REDIS_PORT: "6379"
OPENAI_API_KEY: ""
SUPABASE_JWT_SECRET: ""
REDIS_PASSWORD: "password"
NUM_GRAPH_WORKERS: 10
NUM_NODE_WORKERS: 5
DATABASE_URL: ""
SENTRY_DSN: ""
ENABLE_CREDIT: "true"
BACKEND_CORS_ALLOW_ORIGINS: '["https://dev-builder.agpt.co"]'
SUPABASE_SERVICE_ROLE_KEY: ""
GITHUB_CLIENT_ID: ""
GITHUB_CLIENT_SECRET: ""
FRONTEND_BASE_URL: ""
SUPABASE_URL: ""
SUPABASE_JWT_SECRET: ""
FRONTEND_BASE_URL: "https://dev-builder.agpt.co/"
SUPABASE_URL: "https://adfjtextkuilwuhzdjpf.supabase.co"
AGENTSERVER_HOST: "autogpt-server.dev-agpt.svc.cluster.local"
EXECUTIONMANAGER_HOST: "autogpt-server-executor.dev-agpt.svc.cluster.local"

View File

@@ -0,0 +1,113 @@
# prod values, overwrite base values as needed.
image:
repository: us-east1-docker.pkg.dev/agpt-prod/agpt-backend-prod/agpt-backend-prod
pullPolicy: Always
tag: "latest"
serviceAccount:
annotations:
iam.gke.io/gcp-service-account: "prod-agpt-server-sa@agpt-prod.iam.gserviceaccount.com"
name: "prod-agpt-server-sa"
service:
type: ClusterIP
port: 8006
pyroDaemonPort: 8004
annotations:
cloud.google.com/neg: '{"ingress": true}'
beta.cloud.google.com/backend-config: '{"default": "autogpt-server-backend-config"}'
serviceExecutor:
type: ClusterIP
port: 8002
targetPort: 8002
annotations:
beta.cloud.google.com/backend-config: '{"default": "autogpt-server-backend-config"}'
ingress:
enabled: true
className: "gce"
annotations:
kubernetes.io/ingress.class: gce
kubernetes.io/ingress.global-static-ip-name: "agpt-prod-agpt-backend-ip"
networking.gke.io/managed-certificates: "autogpt-server-cert"
networking.gke.io/v1beta1.FrontendConfig: "autogpt-server-frontend-config"
hosts:
- host: backend.agpt.co
paths:
- path: /
pathType: Prefix
backend:
service:
name: autogpt-server
port: 8006
defaultBackend:
service:
name: autogpt-server
port:
number: 8006
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 2
memory: 10Gi
cors:
allowOrigin: "https://platform.agpt.co"
allowMethods:
- "GET"
- "POST"
- "PUT"
- "DELETE"
- "OPTIONS"
allowHeaders:
- "Content-Type"
- "Authorization"
maxAge: 3600
allowCredentials: true
livenessProbe:
httpGet:
path: /docs
port: 8006
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
readinessProbe:
httpGet:
path: /docs
port: 8006
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
domain: "backend.agpt.co"
env:
APP_ENV: "prod"
PYRO_HOST: "0.0.0.0"
ENABLE_AUTH: "true"
REDIS_HOST: "redis-prod-master.redis-prod.svc.cluster.local"
REDIS_PORT: "6379"
OPENAI_API_KEY: ""
REDIS_PASSWORD: ""
NUM_GRAPH_WORKERS: 10
NUM_NODE_WORKERS: 5
ENABLE_CREDIT: "true"
BACKEND_CORS_ALLOW_ORIGINS: '["https://platform.agpt.co"]'
SUPABASE_JWT_SECRET: ""
DATABASE_URL: ""
SENTRY_DSN: ""
SUPABASE_SERVICE_ROLE_KEY: ""
FRONTEND_BASE_URL: "https://platform.agpt.co/"
SUPABASE_URL: "https://bgwpwdsxblryihinutbx.supabase.co"
AGENTSERVER_HOST: "autogpt-server.prod-agpt.svc.cluster.local"
EXECUTIONMANAGER_HOST: "autogpt-server-executor.prod-agpt.svc.cluster.local"
GITHUB_CLIENT_ID: ""
GITHUB_CLIENT_SECRET: ""

View File

@@ -45,17 +45,9 @@ resources:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
cpu: 1
memory: 1Gi
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
autoscaling:
enabled: false
@@ -75,6 +67,21 @@ affinity: {}
domain: ""
cors:
allowOrigins:
- "https://dev-builder.agpt.co"
allowMethods:
- "GET"
- "POST"
- "PUT"
- "DELETE"
- "OPTIONS"
allowHeaders:
- "Content-Type"
- "Authorization"
maxAge: 3600
allowCredentials: true
cloudSqlProxy:
image:
repository: gcr.io/cloud-sql-connectors/cloud-sql-proxy

View File

@@ -6,4 +6,5 @@ DATABASE_URL="postgresql://${DB_USER}:${DB_PASS}@localhost:${DB_PORT}/${DB_NAME}
SENTRY_DSN=https://11d0640fef35640e0eb9f022eb7d7626@o4505260022104064.ingest.us.sentry.io/4507890252447744
ENABLE_AUTH=true
SUPABASE_JWT_SECRET=our-super-secret-jwt-token-with-at-least-32-characters-long
SUPABASE_JWT_SECRET=our-super-secret-jwt-token-with-at-least-32-characters-long
BACKEND_CORS_ALLOW_ORIGINS="http://localhost:3000,http://127.0.0.1:3000"

View File

@@ -593,24 +593,24 @@ async def get_not_featured_agents(
agents = await prisma.client.get_client().query_raw(
query=f"""
SELECT
"Agents".id,
"Agents"."createdAt",
"Agents"."updatedAt",
"Agents".version,
"Agents".name,
LEFT("Agents".description, 500) AS description,
"Agents".author,
"Agents".keywords,
"Agents".categories,
"Agents".graph,
"Agents"."submissionStatus",
"Agents"."submissionDate",
"Agents".search::text AS search
FROM "Agents"
LEFT JOIN "FeaturedAgent" ON "Agents"."id" = "FeaturedAgent"."agentId"
WHERE ("FeaturedAgent"."agentId" IS NULL OR "FeaturedAgent"."featuredCategories" = '{{}}')
AND "Agents"."submissionStatus" = 'APPROVED'
ORDER BY "Agents"."createdAt" DESC
"market"."Agents".id,
"market"."Agents"."createdAt",
"market"."Agents"."updatedAt",
"market"."Agents".version,
"market"."Agents".name,
LEFT("market"."Agents".description, 500) AS description,
"market"."Agents".author,
"market"."Agents".keywords,
"market"."Agents".categories,
"market"."Agents".graph,
"market"."Agents"."submissionStatus",
"market"."Agents"."submissionDate",
"market"."Agents".search::text AS search
FROM "market"."Agents"
LEFT JOIN "market"."FeaturedAgent" ON "market"."Agents"."id" = "market"."FeaturedAgent"."agentId"
WHERE ("market"."FeaturedAgent"."agentId" IS NULL OR "market"."FeaturedAgent"."featuredCategories" = '{{}}')
AND "market"."Agents"."submissionStatus" = 'APPROVED'
ORDER BY "market"."Agents"."createdAt" DESC
LIMIT {page_size} OFFSET {page_size * (page - 1)}
""",
model=prisma.models.Agents,
@@ -630,24 +630,20 @@ async def get_all_categories() -> market.model.CategoriesResponse:
CategoriesResponse: A list of unique categories.
"""
try:
categories = await prisma.client.get_client().query_first(
query="""
SELECT ARRAY_AGG(DISTINCT category ORDER BY category) AS unique_categories
FROM (
SELECT UNNEST(categories) AS category
FROM "Agents"
) subquery;
""",
model=market.model.CategoriesResponse,
)
if not categories:
return market.model.CategoriesResponse(unique_categories=[])
agents = await prisma.models.Agents.prisma().find_many(distinct=["categories"])
return categories
# Aggregate categories on the Python side
all_categories = set()
for agent in agents:
all_categories.update(agent.categories)
unique_categories = sorted(list(all_categories))
return market.model.CategoriesResponse(unique_categories=unique_categories)
except prisma.errors.PrismaError as e:
raise AgentQueryError(f"Database query failed: {str(e)}")
except Exception as e:
# raise AgentQueryError(f"Unexpected error occurred: {str(e)}")
# Return an empty list of categories in case of unexpected errors
return market.model.CategoriesResponse(unique_categories=[])

View File

@@ -49,13 +49,15 @@ Follow these steps to create and test a new block:
4. **Implement the `__init__` method, including test data and mocks:**
!!! important
Use UUID generator (e.g. https://www.uuidgenerator.net/) for every new block `id` and *do not* make up your own. Alternatively, you can run this python code to generate an uuid: `print(__import__('uuid').uuid4())`
```python
def __init__(self):
super().__init__(
# Unique ID for the block, used across users for templates
# you can generate this with this python one liner
# print(__import__('uuid').uuid4())
id="h5e7f8g9-1b2c-3d4e-5f6g-7h8i9j0k1l2m",
# If you are an AI leave it as is or change to "generate-proper-uuid"
id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
input_schema=WikipediaSummaryBlock.Input, # Assign input schema
output_schema=WikipediaSummaryBlock.Output, # Assign output schema