mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-09 14:25:25 -05:00
Compare commits
43 Commits
fix/execut
...
swiftyos/o
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9cf61aa2d | ||
|
|
0e4d0ce108 | ||
|
|
2f11e9601e | ||
|
|
539d3e0791 | ||
|
|
7a14e5dd66 | ||
|
|
e711c1db4e | ||
|
|
c282d2d912 | ||
|
|
582097221f | ||
|
|
8dacdd16f2 | ||
|
|
04d7feea28 | ||
|
|
3bcd6df193 | ||
|
|
f3c524a74a | ||
|
|
1a08922ccf | ||
|
|
195261835d | ||
|
|
2c473146dd | ||
|
|
311100d26f | ||
|
|
f3319f23ba | ||
|
|
644cff8155 | ||
|
|
d0c4a1f14a | ||
|
|
4d20e419e1 | ||
|
|
6007def168 | ||
|
|
1f82cebb05 | ||
|
|
bc8043b862 | ||
|
|
2a86f22eb4 | ||
|
|
fa5f24eb12 | ||
|
|
37c59990f5 | ||
|
|
783ca12927 | ||
|
|
52598759df | ||
|
|
37e8b51821 | ||
|
|
f051266a33 | ||
|
|
0c4888f15f | ||
|
|
bb8e5622b3 | ||
|
|
46d573c472 | ||
|
|
c027080607 | ||
|
|
9750b79ced | ||
|
|
797f9eda5c | ||
|
|
4f861e3823 | ||
|
|
69747cc891 | ||
|
|
c6daeefa06 | ||
|
|
a7e0af0551 | ||
|
|
6df94aac44 | ||
|
|
6b9580b666 | ||
|
|
44659948e5 |
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 ////////////
|
||||||
//////////////////////////////??///////////
|
//////////////////////////////??///////////
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user