Compare commits

...

43 Commits

Author SHA1 Message Date
Reinier van der Leer
e9cf61aa2d clean up 2025-02-13 21:44:39 +01:00
Reinier van der Leer
0e4d0ce108 Merge branch 'dev' into swiftyos/open-2278-implement-agent-preset-functionality 2025-02-13 21:30:07 +01:00
Reinier van der Leer
2f11e9601e Merge branch 'dev' into swiftyos/open-2278-implement-agent-preset-functionality 2025-02-12 14:30:22 +01:00
Reinier van der Leer
539d3e0791 Merge branch 'dev' into swiftyos/open-2278-implement-agent-preset-functionality 2025-02-12 00:21:08 +01:00
SwiftyOS
7a14e5dd66 Merge remote-tracking branch 'origin/dev' into swiftyos/open-2278-implement-agent-preset-functionality 2025-02-03 15:49:23 +01:00
SwiftyOS
e711c1db4e added frontend client routes 2025-02-03 15:49:10 +01:00
SwiftyOS
c282d2d912 added image generation on create from agent 2025-02-03 15:45:31 +01:00
SwiftyOS
582097221f updating get library agents functions 2025-02-03 15:37:27 +01:00
Reinier van der Leer
8dacdd16f2 fix library.db.update_agent_version_in_library(..)
- Resolves #9250
2025-01-31 10:11:39 +01:00
Reinier van der Leer
04d7feea28 fix other DB calls in v2.library.db 2025-01-31 10:10:56 +01:00
SwiftyOS
3bcd6df193 Merge branch 'swiftyos/open-2278-implement-agent-preset-functionality' of github.com:Significant-Gravitas/AutoGPT into swiftyos/open-2278-implement-agent-preset-functionality 2025-01-31 09:57:42 +01:00
SwiftyOS
f3c524a74a code quality fixes 2025-01-31 09:57:33 +01:00
Swifty
1a08922ccf Merge branch 'dev' into swiftyos/open-2278-implement-agent-preset-functionality 2025-01-30 16:20:42 +01:00
Swifty
195261835d Merge branch 'dev' into swiftyos/open-2278-implement-agent-preset-functionality 2025-01-30 15:07:18 +01:00
SwiftyOS
2c473146dd code quality improvements 2025-01-30 10:52:16 +01:00
SwiftyOS
311100d26f trigger frontend tests 2025-01-30 10:39:32 +01:00
SwiftyOS
f3319f23ba fix backend tests 2025-01-30 10:35:41 +01:00
SwiftyOS
644cff8155 Merge remote-tracking branch 'origin/dev' into swiftyos/open-2278-implement-agent-preset-functionality 2025-01-29 08:33:07 +01:00
SwiftyOS
d0c4a1f14a Merge remote-tracking branch 'origin/dev' into swiftyos/open-2278-implement-agent-preset-functionality 2025-01-10 12:51:50 +01:00
SwiftyOS
4d20e419e1 testing preset functionality 2025-01-10 12:23:49 +01:00
SwiftyOS
6007def168 Merge branch 'swiftyos/open-2277-implement-library-add-update-remove-archive-functionality' into swiftyos/open-2278-implement-agent-preset-functionality 2025-01-10 10:35:01 +01:00
Swifty
1f82cebb05 Merge branch 'dev' into swiftyos/open-2278-implement-agent-preset-functionality 2025-01-10 10:09:30 +01:00
SwiftyOS
bc8043b862 fix tests 2025-01-08 11:00:37 +01:00
SwiftyOS
2a86f22eb4 updated router 2025-01-08 10:53:30 +01:00
SwiftyOS
fa5f24eb12 add db functions 2025-01-08 10:42:33 +01:00
SwiftyOS
37c59990f5 add library management functions 2025-01-08 10:41:57 +01:00
SwiftyOS
783ca12927 Merge branch 'swiftyos/open-2276-add-ability-to-execute-store-agents-without-agent-ownership' into swiftyos/open-2277-implement-library-add-update-remove-archive-functionality 2025-01-08 09:16:26 +01:00
SwiftyOS
52598759df add preset execution 2025-01-08 09:10:12 +01:00
SwiftyOS
37e8b51821 add isActive filter back in 2025-01-08 08:53:18 +01:00
Swifty
f051266a33 Merge branch 'dev' into swiftyos/open-2276-add-ability-to-execute-store-agents-without-agent-ownership 2025-01-07 16:59:06 +01:00
SwiftyOS
0c4888f15f add from_db test 2025-01-07 16:47:38 +01:00
Swifty
bb8e5622b3 Update autogpt_platform/backend/backend/data/graph.py
Co-authored-by: Reinier van der Leer <pwuts@agpt.co>
2025-01-07 16:43:05 +01:00
SwiftyOS
46d573c472 added error message to logs 2025-01-07 16:33:22 +01:00
Swifty
c027080607 Update autogpt_platform/backend/backend/server/model.py
Co-authored-by: Reinier van der Leer <pwuts@agpt.co>
2025-01-07 16:30:04 +01:00
Swifty
9750b79ced Merge branch 'dev' into swiftyos/open-2278-implement-agent-preset-functionality 2025-01-07 11:52:43 +01:00
SwiftyOS
797f9eda5c fmt 2025-01-07 11:51:34 +01:00
SwiftyOS
4f861e3823 fixed merged error 2025-01-07 11:30:38 +01:00
SwiftyOS
69747cc891 added preset functionality 2025-01-07 10:58:41 +01:00
Swifty
c6daeefa06 Merge branch 'dev' into swiftyos/open-2276-add-ability-to-execute-store-agents-without-agent-ownership 2025-01-07 08:50:49 +01:00
SwiftyOS
a7e0af0551 Merge branch 'swiftyos/open-2276-add-ability-to-execute-store-agents-without-agent-ownership' of github.com:Significant-Gravitas/AutoGPT into swiftyos/open-2276-add-ability-to-execute-store-agents-without-agent-ownership 2025-01-03 13:40:28 +01:00
SwiftyOS
6df94aac44 updated comment to make the meaning of userId clearer 2025-01-03 13:40:19 +01:00
Swifty
6b9580b666 Merge branch 'dev' into swiftyos/open-2276-add-ability-to-execute-store-agents-without-agent-ownership 2025-01-03 13:35:38 +01:00
SwiftyOS
44659948e5 Allow execution of store listings 2025-01-03 11:26:28 +01:00
13 changed files with 290 additions and 186 deletions

View File

@@ -1,5 +1,6 @@
import logging import logging
import fastapi
import prisma.errors import prisma.errors
import prisma.fields import prisma.fields
import prisma.models import prisma.models
@@ -8,6 +9,8 @@ import prisma.types
import backend.server.model import backend.server.model
import backend.server.v2.library.model as library_model import backend.server.v2.library.model as library_model
import backend.server.v2.store.exceptions as store_exceptions import backend.server.v2.store.exceptions as store_exceptions
import backend.server.v2.store.image_gen as store_image_gen
import backend.server.v2.store.media as store_media
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -51,9 +54,11 @@ async def get_library_agents(
include={ include={
"Agent": { "Agent": {
"include": { "include": {
"AgentNodes": {"include": {"Input": True, "Output": True}} "AgentNodes": {"include": {"Input": True, "Output": True}},
"AgentGraphExecution": {"where": {"userId": user_id}},
} }
} },
"Creator": True,
}, },
order=[{"updatedAt": "desc"}], order=[{"updatedAt": "desc"}],
) )
@@ -61,7 +66,7 @@ async def get_library_agents(
return [library_model.LibraryAgent.from_db(agent) for agent in library_agents] return [library_model.LibraryAgent.from_db(agent) for agent in library_agents]
except prisma.errors.PrismaError as e: except prisma.errors.PrismaError as e:
logger.error(f"Database error fetching library agents: {e}") logger.error(f"Database error fetching library agents: {e}")
raise store_exceptions.DatabaseError("Unable to fetch library agents.") raise store_exceptions.DatabaseError("Failed to fetch library agents") from e
async def create_library_agent( async def create_library_agent(
@@ -72,6 +77,39 @@ async def create_library_agent(
""" """
try: try:
agent = await prisma.models.AgentGraph.prisma().find_unique(
where={"id": agent_id, "version": agent_version}
)
if not agent:
raise store_exceptions.AgentNotFoundError(
f"Agent {agent_id} version {agent_version} not found"
)
try:
# Use .jpeg here since we are generating JPEG images
filename = f"agent_{agent_id}.jpeg"
image_url = await store_media.check_media_exists(user_id, filename)
if not image_url:
# Generate agent image as JPEG
image = await store_image_gen.generate_agent_image(agent=agent)
# Create UploadFile with the correct filename and content_type
image_file = fastapi.UploadFile(
file=image,
filename=filename,
)
image_url = await store_media.upload_media(
user_id=user_id, file=image_file, use_file_name=True
)
except Exception as e:
logger.error(f"Error generating agent image: {e}")
raise store_exceptions.DatabaseError(
"Failed to generate agent image"
) from e
return await prisma.models.LibraryAgent.prisma().create( return await prisma.models.LibraryAgent.prisma().create(
data={ data={
"userId": user_id, "userId": user_id,
@@ -79,11 +117,12 @@ async def create_library_agent(
"agentVersion": agent_version, "agentVersion": agent_version,
"isCreatedByUser": False, "isCreatedByUser": False,
"useGraphIsActiveVersion": True, "useGraphIsActiveVersion": True,
"Creator": {"connect": {"id": agent.userId}},
} }
) )
except prisma.errors.PrismaError as e: except prisma.errors.PrismaError as e:
logger.error(f"Database error creating agent to library: {str(e)}") logger.error(f"Database error creating agent in library: {str(e)}")
raise store_exceptions.DatabaseError("Failed to create agent to library") from e raise store_exceptions.DatabaseError("Failed to create agent in library") from e
async def update_agent_version_in_library( async def update_agent_version_in_library(
@@ -164,7 +203,8 @@ async def add_store_agent_to_library(
if they don't already have it if they don't already have it
""" """
logger.debug( logger.debug(
f"Adding agent from store listing version {store_listing_version_id} to library for user {user_id}" f"Adding agent from store listing version #{store_listing_version_id} "
f"to library for user #{user_id}"
) )
try: try:
@@ -183,11 +223,13 @@ async def add_store_agent_to_library(
f"Store listing version {store_listing_version_id} not found" f"Store listing version {store_listing_version_id} not found"
) )
# We need the agent object to be able to check if
# the user_id is the same as the agent's user_id
agent = store_listing_version.Agent agent = store_listing_version.Agent
if agent.userId == user_id: if agent.userId == user_id:
logger.warning( logger.warning(
f"User {user_id} cannot add their own agent to their library" f"User #{user_id} cannot add their own agent to their library"
) )
raise store_exceptions.DatabaseError("Cannot add own agent to library") raise store_exceptions.DatabaseError("Cannot add own agent to library")
@@ -202,7 +244,7 @@ async def add_store_agent_to_library(
if existing_user_agent: if existing_user_agent:
logger.debug( logger.debug(
f"User {user_id} already has agent {agent.id} in their library" f"User #{user_id} already has agent #{agent.id} in their library"
) )
return return
@@ -215,7 +257,7 @@ async def add_store_agent_to_library(
"isCreatedByUser": False, "isCreatedByUser": False,
} }
) )
logger.debug(f"Added agent {agent.id} to library for user {user_id}") logger.debug(f"Added agent #{agent.id} to library for user #{user_id}")
except store_exceptions.AgentNotFoundError: except store_exceptions.AgentNotFoundError:
raise raise

View File

@@ -5,7 +5,6 @@ import prisma.models
import pytest import pytest
from prisma import Prisma from prisma import Prisma
import backend.data.includes
import backend.server.v2.library.db as db import backend.server.v2.library.db as db
import backend.server.v2.store.exceptions import backend.server.v2.store.exceptions
@@ -81,12 +80,10 @@ async def test_get_library_agents(mocker):
assert result[0].id == "ua1" assert result[0].id == "ua1"
assert result[0].name == "Test Agent 2" assert result[0].name == "Test Agent 2"
assert result[0].description == "Test Description 2" assert result[0].description == "Test Description 2"
assert result[0].is_created_by_user is False
assert result[0].is_latest_version is True
assert result[0].is_favorite is False
assert result[0].agent_id == "agent2" assert result[0].agent_id == "agent2"
assert result[0].agent_version == 1 assert result[0].agent_version == 1
assert result[0].preset_id is None assert result[0].can_access_graph is False
assert result[0].is_latest_version is True
@pytest.mark.asyncio @pytest.mark.asyncio

View File

@@ -1,6 +1,8 @@
import datetime import datetime
from enum import Enum
from typing import Any from typing import Any
import prisma.enums
import prisma.models import prisma.models
import pydantic import pydantic
@@ -9,25 +11,40 @@ import backend.data.graph as graph_model
import backend.server.model as server_model import backend.server.model as server_model
class AgentStatus(str, Enum):
# The agent has completed all runs
COMPLETED = "COMPLETED"
# An agent is running, but not all runs have completed
HEALTHY = "HEALTHY"
# An agent is waiting to start or waiting for another reason
WAITING = "WAITING"
# An agent is in an error state
ERROR = "ERROR"
class LibraryAgent(pydantic.BaseModel): class LibraryAgent(pydantic.BaseModel):
id: str # Changed from agent_id to match GraphMeta id: str # Changed from agent_id to match GraphMeta
agent_id: str agent_id: str
agent_version: int # Changed from agent_version to match GraphMeta agent_version: int # Changed from agent_version to match GraphMeta
preset_id: str | None image_url: str
creator_name: str # from profile
creator_image_url: str # from profile
status: AgentStatus
updated_at: datetime.datetime updated_at: datetime.datetime
name: str name: str # from graph
description: str description: str # from graph
# Made input_schema and output_schema match GraphMeta's type # Made input_schema and output_schema match GraphMeta's type
input_schema: dict[str, Any] # Should be BlockIOObjectSubSchema in frontend input_schema: dict[str, Any] # Should be BlockIOObjectSubSchema in frontend
output_schema: dict[str, Any] # Should be BlockIOObjectSubSchema in frontend
is_favorite: bool new_output: bool
is_created_by_user: bool can_access_graph: bool
is_latest_version: bool is_latest_version: bool
@@ -41,6 +58,18 @@ class LibraryAgent(pydantic.BaseModel):
agent_updated_at = agent.Agent.updatedAt agent_updated_at = agent.Agent.updatedAt
lib_agent_updated_at = agent.updatedAt lib_agent_updated_at = agent.updatedAt
name = graph.name
description = graph.description
image_url = agent.image_url if agent.image_url else ""
if agent.Creator:
creator_name = agent.Creator.name
creator_image_url = (
agent.Creator.avatarUrl if agent.Creator.avatarUrl else ""
)
else:
creator_name = "Unknown"
creator_image_url = ""
# Take the latest updated_at timestamp either when the graph was updated or the library agent was updated # Take the latest updated_at timestamp either when the graph was updated or the library agent was updated
updated_at = ( updated_at = (
max(agent_updated_at, lib_agent_updated_at) max(agent_updated_at, lib_agent_updated_at)
@@ -48,22 +77,56 @@ class LibraryAgent(pydantic.BaseModel):
else lib_agent_updated_at else lib_agent_updated_at
) )
# Getting counts as expecting more refined logic for determining status
status_counts = {status: 0 for status in prisma.enums.AgentExecutionStatus}
new_output = False
runs_since = datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=7)
if not agent.Agent.AgentGraphExecution:
status = AgentStatus.COMPLETED
else:
for execution in agent.Agent.AgentGraphExecution:
if runs_since > execution.createdAt:
if (
execution.executionStatus
== prisma.enums.AgentExecutionStatus.COMPLETED
):
new_output = True
status_counts[execution.executionStatus] += 1
if status_counts[prisma.enums.AgentExecutionStatus.FAILED] > 0:
status = AgentStatus.ERROR
elif status_counts[prisma.enums.AgentExecutionStatus.QUEUED] > 0:
status = AgentStatus.WAITING
elif status_counts[prisma.enums.AgentExecutionStatus.RUNNING] > 0:
status = AgentStatus.HEALTHY
else:
status = AgentStatus.COMPLETED
return LibraryAgent( return LibraryAgent(
id=agent.id, id=agent.id,
agent_id=agent.agentId, agent_id=agent.agentId,
agent_version=agent.agentVersion, agent_version=agent.agentVersion,
image_url=image_url,
creator_name=creator_name,
creator_image_url=creator_image_url,
name=name,
description=description,
status=status,
updated_at=updated_at, updated_at=updated_at,
name=graph.name,
description=graph.description,
input_schema=graph.input_schema, input_schema=graph.input_schema,
output_schema=graph.output_schema, new_output=new_output,
is_favorite=agent.isFavorite, can_access_graph=agent.Agent.userId == agent.userId,
is_created_by_user=agent.isCreatedByUser, # TODO: work out how to calculate this efficiently
is_latest_version=graph.is_active, is_latest_version=True,
preset_id=agent.AgentPreset.id if agent.AgentPreset else None,
) )
class LibraryAgentResponse:
agents: list[LibraryAgent]
pagination: server_model.Pagination
class LibraryAgentPreset(pydantic.BaseModel): class LibraryAgentPreset(pydantic.BaseModel):
id: str id: str
updated_at: datetime.datetime updated_at: datetime.datetime

View File

@@ -3,147 +3,10 @@ import datetime
import prisma.fields import prisma.fields
import prisma.models import prisma.models
import backend.data.block import backend.server.v2.library.model as library_model
import backend.server.model
import backend.server.v2.library.model
def test_library_agent(): def test_agent_preset_from_db():
agent = backend.server.v2.library.model.LibraryAgent(
id="test-agent-123",
agent_id="agent-123",
agent_version=1,
preset_id=None,
updated_at=datetime.datetime.now(),
name="Test Agent",
description="Test description",
input_schema={"type": "object", "properties": {}},
output_schema={"type": "object", "properties": {}},
is_favorite=False,
is_created_by_user=False,
is_latest_version=True,
)
assert agent.id == "test-agent-123"
assert agent.agent_id == "agent-123"
assert agent.agent_version == 1
assert agent.name == "Test Agent"
assert agent.description == "Test description"
assert agent.is_favorite is False
assert agent.is_created_by_user is False
assert agent.is_latest_version is True
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",
agent_id="agent-456",
agent_version=2,
preset_id=None,
updated_at=datetime.datetime.now(),
name="User Created Agent",
description="An agent created by the user",
input_schema={"type": "object", "properties": {}},
output_schema={"type": "object", "properties": {}},
is_favorite=False,
is_created_by_user=True,
is_latest_version=True,
)
assert agent.id == "user-agent-456"
assert agent.agent_id == "agent-456"
assert agent.agent_version == 2
assert agent.name == "User Created Agent"
assert agent.description == "An agent created by the user"
assert agent.is_favorite is False
assert agent.is_created_by_user is True
assert agent.is_latest_version is True
assert agent.input_schema == {"type": "object", "properties": {}}
assert agent.output_schema == {"type": "object", "properties": {}}
def test_library_agent_preset():
preset = backend.server.v2.library.model.LibraryAgentPreset(
id="preset-123",
name="Test Preset",
description="Test preset description",
agent_id="test-agent-123",
agent_version=1,
is_active=True,
inputs={
"dictionary": {"key1": "Hello", "key2": "World"},
"selected_value": "key2",
},
updated_at=datetime.datetime.now(),
)
assert preset.id == "preset-123"
assert preset.name == "Test Preset"
assert preset.description == "Test preset description"
assert preset.agent_id == "test-agent-123"
assert preset.agent_version == 1
assert preset.is_active is True
assert preset.inputs == {
"dictionary": {"key1": "Hello", "key2": "World"},
"selected_value": "key2",
}
def test_library_agent_preset_response():
preset = backend.server.v2.library.model.LibraryAgentPreset(
id="preset-123",
name="Test Preset",
description="Test preset description",
agent_id="test-agent-123",
agent_version=1,
is_active=True,
inputs={
"dictionary": {"key1": "Hello", "key2": "World"},
"selected_value": "key2",
},
updated_at=datetime.datetime.now(),
)
pagination = backend.server.model.Pagination(
total_items=1, total_pages=1, current_page=1, page_size=10
)
response = backend.server.v2.library.model.LibraryAgentPresetResponse(
presets=[preset], pagination=pagination
)
assert len(response.presets) == 1
assert response.presets[0].id == "preset-123"
assert response.pagination.total_items == 1
assert response.pagination.total_pages == 1
assert response.pagination.current_page == 1
assert response.pagination.page_size == 10
def test_create_library_agent_preset_request():
request = backend.server.v2.library.model.CreateLibraryAgentPresetRequest(
name="New Preset",
description="New preset description",
agent_id="agent-123",
agent_version=1,
is_active=True,
inputs={
"dictionary": {"key1": "Hello", "key2": "World"},
"selected_value": "key2",
},
)
assert request.name == "New Preset"
assert request.description == "New preset description"
assert request.agent_id == "agent-123"
assert request.agent_version == 1
assert request.is_active is True
assert request.inputs == {
"dictionary": {"key1": "Hello", "key2": "World"},
"selected_value": "key2",
}
def test_library_agent_from_db():
# Create mock DB agent # Create mock DB agent
db_agent = prisma.models.AgentPreset( db_agent = prisma.models.AgentPreset(
id="test-agent-123", id="test-agent-123",
@@ -167,7 +30,7 @@ def test_library_agent_from_db():
) )
# Convert to LibraryAgentPreset # Convert to LibraryAgentPreset
agent = backend.server.v2.library.model.LibraryAgentPreset.from_db(db_agent) agent = library_model.LibraryAgentPreset.from_db(db_agent)
assert agent.id == "test-agent-123" assert agent.id == "test-agent-123"
assert agent.agent_version == 1 assert agent.agent_version == 1

View File

@@ -109,8 +109,6 @@ async def execute_preset(
if not preset: if not preset:
raise fastapi.HTTPException(status_code=404, detail="Preset not found") raise fastapi.HTTPException(status_code=404, detail="Preset not found")
logger.debug(f"Preset inputs: {preset.inputs}")
# Merge input overrides with preset inputs # Merge input overrides with preset inputs
merged_node_input = preset.inputs | node_input merged_node_input = preset.inputs | node_input

View File

@@ -35,29 +35,33 @@ def test_get_library_agents_success(mocker: pytest_mock.MockFixture):
id="test-agent-1", id="test-agent-1",
agent_id="test-agent-1", agent_id="test-agent-1",
agent_version=1, agent_version=1,
preset_id="preset-1",
updated_at=datetime.datetime(2023, 1, 1, 0, 0, 0),
is_favorite=False,
is_created_by_user=True,
is_latest_version=True,
name="Test Agent 1", name="Test Agent 1",
description="Test Description 1", description="Test Description 1",
image_url="",
creator_name="Test Creator",
creator_image_url="",
input_schema={"type": "object", "properties": {}}, input_schema={"type": "object", "properties": {}},
output_schema={"type": "object", "properties": {}}, status=library_model.AgentStatus.COMPLETED,
new_output=False,
can_access_graph=True,
is_latest_version=True,
updated_at=datetime.datetime(2023, 1, 1, 0, 0, 0),
), ),
library_model.LibraryAgent( library_model.LibraryAgent(
id="test-agent-2", id="test-agent-2",
agent_id="test-agent-2", agent_id="test-agent-2",
agent_version=1, agent_version=1,
preset_id="preset-2",
updated_at=datetime.datetime(2023, 1, 1, 0, 0, 0),
is_favorite=False,
is_created_by_user=False,
is_latest_version=True,
name="Test Agent 2", name="Test Agent 2",
description="Test Description 2", description="Test Description 2",
image_url="",
creator_name="Test Creator",
creator_image_url="",
input_schema={"type": "object", "properties": {}}, input_schema={"type": "object", "properties": {}},
output_schema={"type": "object", "properties": {}}, status=library_model.AgentStatus.COMPLETED,
new_output=False,
can_access_graph=False,
is_latest_version=True,
updated_at=datetime.datetime(2023, 1, 1, 0, 0, 0),
), ),
] ]
mock_db_call = mocker.patch("backend.server.v2.library.db.get_library_agents") mock_db_call = mocker.patch("backend.server.v2.library.db.get_library_agents")
@@ -71,9 +75,9 @@ def test_get_library_agents_success(mocker: pytest_mock.MockFixture):
] ]
assert len(data) == 2 assert len(data) == 2
assert data[0].agent_id == "test-agent-1" assert data[0].agent_id == "test-agent-1"
assert data[0].is_created_by_user is True assert data[0].can_access_graph is True
assert data[1].agent_id == "test-agent-2" assert data[1].agent_id == "test-agent-2"
assert data[1].is_created_by_user is False assert data[1].can_access_graph is False
mock_db_call.assert_called_once_with("test-user-id") mock_db_call.assert_called_once_with("test-user-id")

View File

@@ -5,6 +5,7 @@ from enum import Enum
import replicate import replicate
import replicate.exceptions import replicate.exceptions
import requests import requests
from prisma.models import AgentGraph
from replicate.helpers import FileOutput from replicate.helpers import FileOutput
from backend.data.graph import Graph from backend.data.graph import Graph
@@ -21,7 +22,7 @@ class ImageStyle(str, Enum):
DIGITAL_ART = "digital art" DIGITAL_ART = "digital art"
async def generate_agent_image(agent: Graph) -> io.BytesIO: async def generate_agent_image(agent: Graph | AgentGraph) -> io.BytesIO:
""" """
Generate an image for an agent using Flux model via Replicate API. Generate an image for an agent using Flux model via Replicate API.

View File

@@ -0,0 +1,6 @@
-- AlterTable
ALTER TABLE "LibraryAgent" ADD COLUMN "creatorId" TEXT,
ADD COLUMN "image_url" TEXT;
-- AddForeignKey
ALTER TABLE "LibraryAgent" ADD CONSTRAINT "LibraryAgent_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "Profile"("id") ON DELETE SET NULL ON UPDATE CASCADE;

View File

@@ -65,6 +65,7 @@ model AgentGraph {
name String? name String?
description String? description String?
isActive Boolean @default(true) isActive Boolean @default(true)
isTemplate Boolean @default(false) isTemplate Boolean @default(false)
@@ -173,6 +174,8 @@ model LibraryAgent {
userId String userId String
User User @relation(fields: [userId], references: [id], onDelete: Cascade) User User @relation(fields: [userId], references: [id], onDelete: Cascade)
image_url String?
agentId String agentId String
agentVersion Int agentVersion Int
Agent AgentGraph @relation(fields: [agentId, agentVersion], references: [id, version]) Agent AgentGraph @relation(fields: [agentId, agentVersion], references: [id, version])
@@ -180,6 +183,9 @@ model LibraryAgent {
agentPresetId String? agentPresetId String?
AgentPreset AgentPreset? @relation(fields: [agentPresetId], references: [id]) AgentPreset AgentPreset? @relation(fields: [agentPresetId], references: [id])
creatorId String?
Creator Profile? @relation(fields: [creatorId], references: [id])
useGraphIsActiveVersion Boolean @default(false) useGraphIsActiveVersion Boolean @default(false)
isFavorite Boolean @default(false) isFavorite Boolean @default(false)
@@ -477,6 +483,8 @@ model Profile {
isFeatured Boolean @default(false) isFeatured Boolean @default(false)
LibraryAgent LibraryAgent[]
@@index([username]) @@index([username])
@@index([userId]) @@index([userId])
} }

View File

@@ -469,7 +469,7 @@ async def test_execute_preset_with_clash(server: SpinTestServer):
) )
# Verify execution # Verify execution
assert result is not None assert result is not None, "Result must not be None"
graph_exec_id = result["id"] graph_exec_id = result["id"]
# Wait for execution to complete # Wait for execution to complete

View File

@@ -3,5 +3,6 @@
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
export default function Page() { export default function Page() {
// Redirects to marketplace
redirect("/marketplace"); redirect("/marketplace");
} }

View File

@@ -33,6 +33,12 @@ import {
TransactionHistory, TransactionHistory,
User, User,
UserPasswordCredentials, UserPasswordCredentials,
LibraryAgentResponse,
LibraryAgentPresetResponse,
CreateLibraryAgentPresetRequest,
LibraryAgent,
LibraryAgentPreset,
AgentStatus,
} from "./types"; } from "./types";
import { createBrowserClient } from "@supabase/ssr"; import { createBrowserClient } from "@supabase/ssr";
import getServerSupabase from "../supabase/getServerSupabase"; import getServerSupabase from "../supabase/getServerSupabase";
@@ -443,7 +449,7 @@ export default class BackendAPI {
/////////// V2 LIBRARY API ////////////// /////////// V2 LIBRARY API //////////////
///////////////////////////////////////// /////////////////////////////////////////
async listLibraryAgents(): Promise<GraphMeta[]> { async listLibraryAgents(): Promise<LibraryAgentResponse> {
return this._get("/library/agents"); return this._get("/library/agents");
} }
@@ -451,6 +457,59 @@ export default class BackendAPI {
await this._request("POST", `/library/agents/${storeListingVersionId}`); await this._request("POST", `/library/agents/${storeListingVersionId}`);
} }
async updateLibraryAgent(
libraryAgentId: string,
params: {
auto_update_version?: boolean;
is_favorite?: boolean;
is_archived?: boolean;
is_deleted?: boolean;
},
): Promise<void> {
await this._request("PUT", `/library/agents/${libraryAgentId}`, params);
}
async listLibraryAgentPresets(params?: {
page?: number;
page_size?: number;
}): Promise<LibraryAgentPresetResponse> {
return this._get("/library/presets", params);
}
async getLibraryAgentPreset(presetId: string): Promise<LibraryAgentPreset> {
return this._get(`/library/presets/${presetId}`);
}
async createLibraryAgentPreset(
preset: CreateLibraryAgentPresetRequest,
): Promise<LibraryAgentPreset> {
return this._request("POST", "/library/presets", preset);
}
async updateLibraryAgentPreset(
presetId: string,
preset: CreateLibraryAgentPresetRequest,
): Promise<LibraryAgentPreset> {
return this._request("PUT", `/library/presets/${presetId}`, preset);
}
async deleteLibraryAgentPreset(presetId: string): Promise<void> {
await this._request("DELETE", `/library/presets/${presetId}`);
}
async executeLibraryAgentPreset(
presetId: string,
graphId: string,
graphVersion: number,
nodeInput: { [key: string]: any },
): Promise<{ id: string }> {
return this._request("POST", `/library/presets/${presetId}/execute`, {
graph_id: graphId,
graph_version: graphVersion,
node_input: nodeInput,
});
}
/////////////////////////////////////////// ///////////////////////////////////////////
/////////// INTERNAL FUNCTIONS //////////// /////////// INTERNAL FUNCTIONS ////////////
//////////////////////////////??/////////// //////////////////////////////??///////////

View File

@@ -589,3 +589,65 @@ export interface TransactionHistory {
transactions: CreditTransaction[]; transactions: CreditTransaction[];
next_transaction_time: Date | null; next_transaction_time: Date | null;
} }
export enum AgentStatus {
COMPLETED = "COMPLETED",
HEALTHY = "HEALTHY",
WAITING = "WAITING",
ERROR = "ERROR",
}
export interface LibraryAgent {
id: string;
agent_id: string;
agent_version: number;
image_url: string;
creator_name: string;
creator_image_url: string;
status: AgentStatus;
updated_at: Date;
name: string;
description: string;
input_schema: { [key: string]: any };
new_output: boolean;
can_access_graph: boolean;
is_latest_version: boolean;
}
export interface LibraryAgentResponse {
agents: LibraryAgent[];
pagination: {
total: number;
page: number;
size: number;
};
}
export interface LibraryAgentPreset {
id: string;
updated_at: Date;
agent_id: string;
agent_version: number;
name: string;
description: string;
is_active: boolean;
inputs: { [key: string]: any };
}
export interface LibraryAgentPresetResponse {
presets: LibraryAgentPreset[];
pagination: {
total: number;
page: number;
size: number;
};
}
export interface CreateLibraryAgentPresetRequest {
name: string;
description: string;
inputs: { [key: string]: any };
agent_id: string;
agent_version: number;
is_active: boolean;
}