diff --git a/autogpt_platform/backend/backend/server/rest_api.py b/autogpt_platform/backend/backend/server/rest_api.py index 2a7f84ca4b02..c5be1c179260 100644 --- a/autogpt_platform/backend/backend/server/rest_api.py +++ b/autogpt_platform/backend/backend/server/rest_api.py @@ -16,6 +16,7 @@ import backend.data.graph import backend.data.user import backend.server.routers.v1 +import backend.server.v2.library.routes import backend.server.v2.store.routes import backend.util.service import backend.util.settings @@ -89,6 +90,9 @@ def handler(request: fastapi.Request, exc: Exception): app.include_router( backend.server.v2.store.routes.router, tags=["v2"], prefix="/api/store" ) +app.include_router( + backend.server.v2.library.routes.router, tags=["v2"], prefix="/api/library" +) @app.get(path="/health", tags=["health"], dependencies=[]) diff --git a/autogpt_platform/backend/backend/server/v2/library/__init__.py b/autogpt_platform/backend/backend/server/v2/library/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/autogpt_platform/backend/backend/server/v2/library/db.py b/autogpt_platform/backend/backend/server/v2/library/db.py new file mode 100644 index 000000000000..8d142ef40c76 --- /dev/null +++ b/autogpt_platform/backend/backend/server/v2/library/db.py @@ -0,0 +1,165 @@ +import logging +from typing import List + +import prisma.errors +import prisma.models +import prisma.types + +import backend.data.graph +import backend.data.includes +import backend.server.v2.library.model +import backend.server.v2.store.exceptions + +logger = logging.getLogger(__name__) + + +async def get_library_agents( + user_id: str, +) -> List[backend.server.v2.library.model.LibraryAgent]: + """ + Returns all agents (AgentGraph) that belong to the user and all agents in their library (UserAgent table) + """ + logger.debug(f"Getting library agents for user {user_id}") + + try: + # Get agents created by user with nodes and links + user_created = await prisma.models.AgentGraph.prisma().find_many( + where=prisma.types.AgentGraphWhereInput(userId=user_id, isActive=True), + include=backend.data.includes.AGENT_GRAPH_INCLUDE, + ) + + # Get agents in user's library with nodes and links + library_agents = await prisma.models.UserAgent.prisma().find_many( + where=prisma.types.UserAgentWhereInput( + userId=user_id, isDeleted=False, isArchived=False + ), + include={ + "Agent": { + "include": { + "AgentNodes": { + "include": { + "Input": True, + "Output": True, + "Webhook": True, + "AgentBlock": True, + } + } + } + } + }, + ) + + # Convert to Graph models first + graphs = [] + + # Add user created agents + for agent in user_created: + try: + graphs.append(backend.data.graph.GraphModel.from_db(agent)) + except Exception as e: + logger.error(f"Error processing user created agent {agent.id}: {e}") + continue + + # Add library agents + for agent in library_agents: + if agent.Agent: + try: + graphs.append(backend.data.graph.GraphModel.from_db(agent.Agent)) + except Exception as e: + logger.error(f"Error processing library agent {agent.agentId}: {e}") + continue + + # Convert Graph models to LibraryAgent models + result = [] + for graph in graphs: + result.append( + backend.server.v2.library.model.LibraryAgent( + id=graph.id, + version=graph.version, + is_active=graph.is_active, + name=graph.name, + description=graph.description, + isCreatedByUser=any(a.id == graph.id for a in user_created), + input_schema=graph.input_schema, + output_schema=graph.output_schema, + ) + ) + + logger.debug(f"Found {len(result)} library agents") + return result + + except prisma.errors.PrismaError as e: + logger.error(f"Database error getting library agents: {str(e)}") + raise backend.server.v2.store.exceptions.DatabaseError( + "Failed to fetch library agents" + ) from e + + +async def add_agent_to_library(store_listing_version_id: str, user_id: str) -> None: + """ + Finds the agent from the store listing version and adds it to the user's library (UserAgent table) + if they don't already have it + """ + logger.debug( + f"Adding agent from store listing version {store_listing_version_id} to library for user {user_id}" + ) + + try: + # Get store listing version to find agent + store_listing_version = ( + await prisma.models.StoreListingVersion.prisma().find_unique( + where={"id": store_listing_version_id}, include={"Agent": True} + ) + ) + + if not store_listing_version or not store_listing_version.Agent: + logger.warning( + f"Store listing version not found: {store_listing_version_id}" + ) + raise backend.server.v2.store.exceptions.AgentNotFoundError( + f"Store listing version {store_listing_version_id} not found" + ) + + agent = store_listing_version.Agent + + if agent.userId == user_id: + logger.warning( + f"User {user_id} cannot add their own agent to their library" + ) + raise backend.server.v2.store.exceptions.DatabaseError( + "Cannot add own agent to library" + ) + + # Check if user already has this agent + existing_user_agent = await prisma.models.UserAgent.prisma().find_first( + where={ + "userId": user_id, + "agentId": agent.id, + "agentVersion": agent.version, + } + ) + + if existing_user_agent: + logger.debug( + f"User {user_id} already has agent {agent.id} in their library" + ) + return + + # Create UserAgent entry + await prisma.models.UserAgent.prisma().create( + data=prisma.types.UserAgentCreateInput( + userId=user_id, + agentId=agent.id, + agentVersion=agent.version, + isCreatedByUser=False, + ) + ) + logger.debug(f"Added agent {agent.id} to library for user {user_id}") + + except backend.server.v2.store.exceptions.AgentNotFoundError: + raise + except prisma.errors.PrismaError as e: + logger.error(f"Database error adding agent to library: {str(e)}") + raise backend.server.v2.store.exceptions.DatabaseError( + "Failed to add agent to library" + ) from e diff --git a/autogpt_platform/backend/backend/server/v2/library/db_test.py b/autogpt_platform/backend/backend/server/v2/library/db_test.py new file mode 100644 index 000000000000..309bd20286a0 --- /dev/null +++ b/autogpt_platform/backend/backend/server/v2/library/db_test.py @@ -0,0 +1,189 @@ +from datetime import datetime + +import prisma.errors +import prisma.models +import pytest +from prisma import Prisma + +import backend.data.includes +import backend.server.v2.library.db as db +import backend.server.v2.store.exceptions + + +@pytest.fixture(autouse=True) +async def setup_prisma(): + # Don't register client if already registered + try: + Prisma() + except prisma.errors.ClientAlreadyRegisteredError: + pass + yield + + +@pytest.mark.asyncio +async def test_get_library_agents(mocker): + # Mock data + mock_user_created = [ + prisma.models.AgentGraph( + id="agent1", + version=1, + name="Test Agent 1", + description="Test Description 1", + userId="test-user", + isActive=True, + createdAt=datetime.now(), + isTemplate=False, + ) + ] + + mock_library_agents = [ + prisma.models.UserAgent( + id="ua1", + userId="test-user", + agentId="agent2", + agentVersion=1, + isCreatedByUser=False, + isDeleted=False, + isArchived=False, + createdAt=datetime.now(), + updatedAt=datetime.now(), + isFavorite=False, + Agent=prisma.models.AgentGraph( + id="agent2", + version=1, + name="Test Agent 2", + description="Test Description 2", + userId="other-user", + isActive=True, + createdAt=datetime.now(), + isTemplate=False, + ), + ) + ] + + # Mock prisma calls + mock_agent_graph = mocker.patch("prisma.models.AgentGraph.prisma") + mock_agent_graph.return_value.find_many = mocker.AsyncMock( + return_value=mock_user_created + ) + + mock_user_agent = mocker.patch("prisma.models.UserAgent.prisma") + mock_user_agent.return_value.find_many = mocker.AsyncMock( + return_value=mock_library_agents + ) + + # Call function + result = await db.get_library_agents("test-user") + + # Verify results + assert len(result) == 2 + assert result[0].id == "agent1" + assert result[0].name == "Test Agent 1" + assert result[0].description == "Test Description 1" + assert result[0].isCreatedByUser is True + assert result[1].id == "agent2" + assert result[1].name == "Test Agent 2" + assert result[1].description == "Test Description 2" + assert result[1].isCreatedByUser is False + + # Verify mocks called correctly + mock_agent_graph.return_value.find_many.assert_called_once_with( + where=prisma.types.AgentGraphWhereInput(userId="test-user", isActive=True), + include=backend.data.includes.AGENT_GRAPH_INCLUDE, + ) + mock_user_agent.return_value.find_many.assert_called_once_with( + where=prisma.types.UserAgentWhereInput( + userId="test-user", isDeleted=False, isArchived=False + ), + include={ + "Agent": { + "include": { + "AgentNodes": { + "include": { + "Input": True, + "Output": True, + "Webhook": True, + "AgentBlock": True, + } + } + } + } + }, + ) + + +@pytest.mark.asyncio +async def test_add_agent_to_library(mocker): + # Mock data + mock_store_listing = prisma.models.StoreListingVersion( + id="version123", + version=1, + createdAt=datetime.now(), + updatedAt=datetime.now(), + agentId="agent1", + agentVersion=1, + slug="test-agent", + name="Test Agent", + subHeading="Test Agent Subheading", + imageUrls=["https://example.com/image.jpg"], + description="Test Description", + categories=["test"], + isFeatured=False, + isDeleted=False, + isAvailable=True, + isApproved=True, + Agent=prisma.models.AgentGraph( + id="agent1", + version=1, + name="Test Agent", + description="Test Description", + userId="creator", + isActive=True, + createdAt=datetime.now(), + isTemplate=False, + ), + ) + + # Mock prisma calls + mock_store_listing_version = mocker.patch( + "prisma.models.StoreListingVersion.prisma" + ) + mock_store_listing_version.return_value.find_unique = mocker.AsyncMock( + return_value=mock_store_listing + ) + + mock_user_agent = mocker.patch("prisma.models.UserAgent.prisma") + mock_user_agent.return_value.create = mocker.AsyncMock() + + # Call function + await db.add_agent_to_library("version123", "test-user") + + # Verify mocks called correctly + mock_store_listing_version.return_value.find_unique.assert_called_once_with( + where={"id": "version123"}, include={"Agent": True} + ) + mock_user_agent.return_value.create.assert_called_once_with( + data=prisma.types.UserAgentCreateInput( + userId="test-user", agentId="agent1", agentVersion=1, isCreatedByUser=False + ) + ) + + +@pytest.mark.asyncio +async def test_add_agent_to_library_not_found(mocker): + # Mock prisma calls + mock_store_listing_version = mocker.patch( + "prisma.models.StoreListingVersion.prisma" + ) + mock_store_listing_version.return_value.find_unique = mocker.AsyncMock( + return_value=None + ) + + # Call function and verify exception + with pytest.raises(backend.server.v2.store.exceptions.AgentNotFoundError): + await db.add_agent_to_library("version123", "test-user") + + # Verify mock called correctly + mock_store_listing_version.return_value.find_unique.assert_called_once_with( + where={"id": "version123"}, include={"Agent": True} + ) diff --git a/autogpt_platform/backend/backend/server/v2/library/model.py b/autogpt_platform/backend/backend/server/v2/library/model.py new file mode 100644 index 000000000000..88a81f6d77a0 --- /dev/null +++ b/autogpt_platform/backend/backend/server/v2/library/model.py @@ -0,0 +1,16 @@ +import typing + +import pydantic + + +class LibraryAgent(pydantic.BaseModel): + id: str # Changed from agent_id to match GraphMeta + version: int # Changed from agent_version to match GraphMeta + is_active: bool # Added to match GraphMeta + name: str + description: str + + isCreatedByUser: bool + # Made input_schema and output_schema match GraphMeta's type + input_schema: dict[str, typing.Any] # Should be BlockIOObjectSubSchema in frontend + output_schema: dict[str, typing.Any] # Should be BlockIOObjectSubSchema in frontend diff --git a/autogpt_platform/backend/backend/server/v2/library/model_test.py b/autogpt_platform/backend/backend/server/v2/library/model_test.py new file mode 100644 index 000000000000..81aa8fe07b23 --- /dev/null +++ b/autogpt_platform/backend/backend/server/v2/library/model_test.py @@ -0,0 +1,43 @@ +import backend.server.v2.library.model + + +def test_library_agent(): + agent = backend.server.v2.library.model.LibraryAgent( + id="test-agent-123", + version=1, + is_active=True, + name="Test Agent", + description="Test description", + isCreatedByUser=False, + input_schema={"type": "object", "properties": {}}, + output_schema={"type": "object", "properties": {}}, + ) + assert agent.id == "test-agent-123" + assert agent.version == 1 + assert agent.is_active is True + assert agent.name == "Test Agent" + assert agent.description == "Test description" + assert agent.isCreatedByUser is False + assert agent.input_schema == {"type": "object", "properties": {}} + assert agent.output_schema == {"type": "object", "properties": {}} + + +def test_library_agent_with_user_created(): + agent = backend.server.v2.library.model.LibraryAgent( + id="user-agent-456", + version=2, + is_active=True, + name="User Created Agent", + description="An agent created by the user", + isCreatedByUser=True, + input_schema={"type": "object", "properties": {}}, + output_schema={"type": "object", "properties": {}}, + ) + assert agent.id == "user-agent-456" + assert agent.version == 2 + assert agent.is_active is True + assert agent.name == "User Created Agent" + assert agent.description == "An agent created by the user" + assert agent.isCreatedByUser is True + assert agent.input_schema == {"type": "object", "properties": {}} + assert agent.output_schema == {"type": "object", "properties": {}} diff --git a/autogpt_platform/backend/backend/server/v2/library/routes.py b/autogpt_platform/backend/backend/server/v2/library/routes.py new file mode 100644 index 000000000000..7f3e89fe903a --- /dev/null +++ b/autogpt_platform/backend/backend/server/v2/library/routes.py @@ -0,0 +1,74 @@ +import logging +import typing + +import autogpt_libs.auth.depends +import autogpt_libs.auth.middleware +import fastapi + +import backend.data.graph +import backend.server.v2.library.db +import backend.server.v2.library.model + +logger = logging.getLogger(__name__) + +router = fastapi.APIRouter() + + +@router.get( + "/agents", + tags=["library", "private"], + dependencies=[fastapi.Depends(autogpt_libs.auth.middleware.auth_middleware)], +) +async def get_library_agents( + user_id: typing.Annotated[ + str, fastapi.Depends(autogpt_libs.auth.depends.get_user_id) + ] +) -> typing.Sequence[backend.server.v2.library.model.LibraryAgent]: + """ + Get all agents in the user's library, including both created and saved agents. + """ + try: + agents = await backend.server.v2.library.db.get_library_agents(user_id) + return agents + except Exception: + logger.exception("Exception occurred whilst getting library agents") + raise fastapi.HTTPException( + status_code=500, detail="Failed to get library agents" + ) + + +@router.post( + "/agents/{store_listing_version_id}", + tags=["library", "private"], + dependencies=[fastapi.Depends(autogpt_libs.auth.middleware.auth_middleware)], + status_code=201, +) +async def add_agent_to_library( + store_listing_version_id: str, + user_id: typing.Annotated[ + str, fastapi.Depends(autogpt_libs.auth.depends.get_user_id) + ], +) -> fastapi.Response: + """ + Add an agent from the store to the user's library. + + Args: + store_listing_version_id (str): ID of the store listing version to add + user_id (str): ID of the authenticated user + + Returns: + fastapi.Response: 201 status code on success + + Raises: + HTTPException: If there is an error adding the agent to the library + """ + try: + await backend.server.v2.library.db.add_agent_to_library( + store_listing_version_id=store_listing_version_id, user_id=user_id + ) + return fastapi.Response(status_code=201) + except Exception: + logger.exception("Exception occurred whilst adding agent to library") + raise fastapi.HTTPException( + status_code=500, detail="Failed to add agent to library" + ) diff --git a/autogpt_platform/backend/backend/server/v2/library/routes_test.py b/autogpt_platform/backend/backend/server/v2/library/routes_test.py new file mode 100644 index 000000000000..a48b416de437 --- /dev/null +++ b/autogpt_platform/backend/backend/server/v2/library/routes_test.py @@ -0,0 +1,103 @@ +import autogpt_libs.auth.depends +import autogpt_libs.auth.middleware +import fastapi +import fastapi.testclient +import pytest_mock + +import backend.server.v2.library.db +import backend.server.v2.library.model +import backend.server.v2.library.routes + +app = fastapi.FastAPI() +app.include_router(backend.server.v2.library.routes.router) + +client = fastapi.testclient.TestClient(app) + + +def override_auth_middleware(): + """Override auth middleware for testing""" + return {"sub": "test-user-id"} + + +def override_get_user_id(): + """Override get_user_id for testing""" + return "test-user-id" + + +app.dependency_overrides[autogpt_libs.auth.middleware.auth_middleware] = ( + override_auth_middleware +) +app.dependency_overrides[autogpt_libs.auth.depends.get_user_id] = override_get_user_id + + +def test_get_library_agents_success(mocker: pytest_mock.MockFixture): + mocked_value = [ + backend.server.v2.library.model.LibraryAgent( + id="test-agent-1", + version=1, + is_active=True, + name="Test Agent 1", + description="Test Description 1", + isCreatedByUser=True, + input_schema={"type": "object", "properties": {}}, + output_schema={"type": "object", "properties": {}}, + ), + backend.server.v2.library.model.LibraryAgent( + id="test-agent-2", + version=1, + is_active=True, + name="Test Agent 2", + description="Test Description 2", + isCreatedByUser=False, + input_schema={"type": "object", "properties": {}}, + output_schema={"type": "object", "properties": {}}, + ), + ] + mock_db_call = mocker.patch("backend.server.v2.library.db.get_library_agents") + mock_db_call.return_value = mocked_value + + response = client.get("/agents") + assert response.status_code == 200 + + data = [ + backend.server.v2.library.model.LibraryAgent.model_validate(agent) + for agent in response.json() + ] + assert len(data) == 2 + assert data[0].id == "test-agent-1" + assert data[0].isCreatedByUser is True + assert data[1].id == "test-agent-2" + assert data[1].isCreatedByUser is False + mock_db_call.assert_called_once_with("test-user-id") + + +def test_get_library_agents_error(mocker: pytest_mock.MockFixture): + mock_db_call = mocker.patch("backend.server.v2.library.db.get_library_agents") + mock_db_call.side_effect = Exception("Test error") + + response = client.get("/agents") + assert response.status_code == 500 + mock_db_call.assert_called_once_with("test-user-id") + + +def test_add_agent_to_library_success(mocker: pytest_mock.MockFixture): + mock_db_call = mocker.patch("backend.server.v2.library.db.add_agent_to_library") + mock_db_call.return_value = None + + response = client.post("/agents/test-version-id") + assert response.status_code == 201 + mock_db_call.assert_called_once_with( + store_listing_version_id="test-version-id", user_id="test-user-id" + ) + + +def test_add_agent_to_library_error(mocker: pytest_mock.MockFixture): + mock_db_call = mocker.patch("backend.server.v2.library.db.add_agent_to_library") + mock_db_call.side_effect = Exception("Test error") + + response = client.post("/agents/test-version-id") + assert response.status_code == 500 + assert response.json()["detail"] == "Failed to add agent to library" + mock_db_call.assert_called_once_with( + store_listing_version_id="test-version-id", user_id="test-user-id" + ) diff --git a/autogpt_platform/backend/backend/server/v2/store/README.md b/autogpt_platform/backend/backend/server/v2/store/README.md new file mode 100644 index 000000000000..90d41e8d7164 --- /dev/null +++ b/autogpt_platform/backend/backend/server/v2/store/README.md @@ -0,0 +1,53 @@ +# Store Module + +This module implements the backend API for the AutoGPT Store, handling agents, creators, profiles, submissions and media uploads. + +## Files + +### routes.py +Contains the FastAPI route handlers for the store API endpoints: + +- Profile endpoints for managing user profiles +- Agent endpoints for browsing and retrieving store agents +- Creator endpoints for browsing and retrieving creator details +- Store submission endpoints for submitting agents to the store +- Media upload endpoints for submission images/videos + +### model.py +Contains Pydantic models for request/response validation and serialization: + +- Pagination model for paginated responses +- Models for agents, creators, profiles, submissions +- Request/response models for all API endpoints + +### db.py +Contains database access functions using Prisma ORM: + +- Functions to query and manipulate store data +- Handles database operations for all API endpoints +- Implements business logic and data validation + +### media.py +Handles media file uploads to Google Cloud Storage: + +- Validates file types and sizes +- Processes image and video uploads +- Stores files in GCS buckets +- Returns public URLs for uploaded media + +## Key Features + +- Paginated listings of store agents and creators +- Search and filtering of agents and creators +- Agent submission workflow +- Media file upload handling +- Profile management +- Reviews and ratings + +## Authentication + +Most endpoints require authentication via the AutoGPT auth middleware. Public endpoints are marked with the "public" tag. + +## Error Handling + +All database and storage operations include proper error handling and logging. Errors are mapped to appropriate HTTP status codes. diff --git a/autogpt_platform/backend/backend/server/v2/store/media.py b/autogpt_platform/backend/backend/server/v2/store/media.py index a737a667b551..7ced3a842b9a 100644 --- a/autogpt_platform/backend/backend/server/v2/store/media.py +++ b/autogpt_platform/backend/backend/server/v2/store/media.py @@ -75,13 +75,10 @@ async def upload_media(user_id: str, file: fastapi.UploadFile) -> str: settings = Settings() # Check required settings first before doing any file processing - if ( - not settings.config.media_gcs_bucket_name - or not settings.config.google_application_credentials - ): - logger.error("Missing required GCS settings") + if not settings.config.media_gcs_bucket_name: + logger.error("Missing GCS bucket name setting") raise backend.server.v2.store.exceptions.StorageConfigError( - "Missing storage configuration" + "Missing storage bucket configuration" ) try: diff --git a/autogpt_platform/backend/backend/server/v2/store/routes.py b/autogpt_platform/backend/backend/server/v2/store/routes.py index 57ed6225f758..3ecfce29c9ca 100644 --- a/autogpt_platform/backend/backend/server/v2/store/routes.py +++ b/autogpt_platform/backend/backend/server/v2/store/routes.py @@ -434,6 +434,8 @@ async def upload_submission_media( user_id=user_id, file=file ) return media_url - except Exception: + except Exception as e: logger.exception("Exception occurred whilst uploading submission media") - raise + raise fastapi.HTTPException( + status_code=500, detail=f"Failed to upload media file: {str(e)}" + ) diff --git a/autogpt_platform/backend/backend/util/settings.py b/autogpt_platform/backend/backend/util/settings.py index 33442b41b999..69504f528f3c 100644 --- a/autogpt_platform/backend/backend/util/settings.py +++ b/autogpt_platform/backend/backend/util/settings.py @@ -153,11 +153,6 @@ class Config(UpdateTrackingModel["Config"], BaseSettings): description="The name of the Google Cloud Storage bucket for media files", ) - google_application_credentials: str = Field( - default="", - description="The path to the Google Cloud credentials JSON file", - ) - @field_validator("platform_base_url", "frontend_base_url") @classmethod def validate_platform_base_url(cls, v: str, info: ValidationInfo) -> str: diff --git a/autogpt_platform/frontend/src/app/monitoring/page.tsx b/autogpt_platform/frontend/src/app/monitoring/page.tsx index bb04629ae323..66a37c96cfc4 100644 --- a/autogpt_platform/frontend/src/app/monitoring/page.tsx +++ b/autogpt_platform/frontend/src/app/monitoring/page.tsx @@ -37,7 +37,7 @@ const Monitor = () => { ); const fetchAgents = useCallback(() => { - api.listGraphs().then((agent) => { + api.listLibraryAgents().then((agent) => { setFlows(agent); }); api.getExecutions().then((executions) => { diff --git a/autogpt_platform/frontend/src/app/store/agent/[creator]/[slug]/page.tsx b/autogpt_platform/frontend/src/app/store/agent/[creator]/[slug]/page.tsx index 4c4f1c303151..f68b0517746a 100644 --- a/autogpt_platform/frontend/src/app/store/agent/[creator]/[slug]/page.tsx +++ b/autogpt_platform/frontend/src/app/store/agent/[creator]/[slug]/page.tsx @@ -65,6 +65,7 @@ export default async function Page({ categories={agent.categories} lastUpdated={agent.updated_at} version={agent.versions[agent.versions.length - 1]} + storeListingVersionId={agent.store_listing_version_id} /> diff --git a/autogpt_platform/frontend/src/components/agptui/AgentInfo.tsx b/autogpt_platform/frontend/src/components/agptui/AgentInfo.tsx index 942790837f44..72f6272bf597 100644 --- a/autogpt_platform/frontend/src/components/agptui/AgentInfo.tsx +++ b/autogpt_platform/frontend/src/components/agptui/AgentInfo.tsx @@ -1,10 +1,10 @@ "use client"; import * as React from "react"; -import { IconPlay, IconStar, StarRatingIcons } from "@/components/ui/icons"; -import Link from "next/link"; +import { IconPlay, StarRatingIcons } from "@/components/ui/icons"; import { Separator } from "@/components/ui/separator"; - +import BackendAPI from "@/lib/autogpt-server-api"; +import { useRouter } from "next/navigation"; interface AgentInfoProps { name: string; creator: string; @@ -15,6 +15,7 @@ interface AgentInfoProps { categories: string[]; lastUpdated: string; version: string; + storeListingVersionId: string; } export const AgentInfo: React.FC = ({ @@ -27,7 +28,22 @@ export const AgentInfo: React.FC = ({ categories, lastUpdated, version, + storeListingVersionId, }) => { + const router = useRouter(); + + const api = React.useMemo(() => new BackendAPI(), []); + + const handleAddToLibrary = async () => { + try { + await api.addAgentToLibrary(storeListingVersionId); + console.log("Agent added to library successfully"); + router.push("/monitoring"); + } catch (error) { + console.error("Failed to add agent to library:", error); + } + }; + return (
{/* Title */} @@ -52,10 +68,13 @@ export const AgentInfo: React.FC = ({ {/* Run Agent Button */}
-
diff --git a/autogpt_platform/frontend/src/components/agptui/PublishAgentSelectInfo.tsx b/autogpt_platform/frontend/src/components/agptui/PublishAgentSelectInfo.tsx index 7353a532fe25..864528a47f13 100644 --- a/autogpt_platform/frontend/src/components/agptui/PublishAgentSelectInfo.tsx +++ b/autogpt_platform/frontend/src/components/agptui/PublishAgentSelectInfo.tsx @@ -338,7 +338,16 @@ export const PublishAgentInfo: React.FC = ({ className="w-full appearance-none rounded-[55px] border border-slate-200 py-2.5 pl-4 pr-5 font-['Geist'] text-base font-normal leading-normal text-slate-500 dark:border-slate-700 dark:bg-gray-700 dark:text-slate-300" > - + + + + + + + + + + {/* Add more options here */}
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 13d26505a748..cc3e638b59d6 100644 --- a/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts +++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts @@ -333,6 +333,18 @@ export default class BackendAPI { return this._get("/store/myagents", params); } + ///////////////////////////////////////// + /////////// V2 LIBRARY API ////////////// + ///////////////////////////////////////// + + async listLibraryAgents(): Promise { + return this._get("/library/agents"); + } + + async addAgentToLibrary(storeListingVersionId: string): Promise { + await this._request("POST", `/library/agents/${storeListingVersionId}`); + } + /////////////////////////////////////////// /////////// INTERNAL FUNCTIONS //////////// //////////////////////////////??///////////