mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-02 10:55:14 -05:00
Compare commits
9 Commits
dev
...
ntindle/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7968a560d | ||
|
|
56788a0226 | ||
|
|
6119900f44 | ||
|
|
9688a507a3 | ||
|
|
3a38bb2338 | ||
|
|
683fff4fab | ||
|
|
ccb537696f | ||
|
|
7838855da9 | ||
|
|
3ec8fd912f |
@@ -104,18 +104,60 @@ async def list_library_agents(
|
|||||||
order_by = {"updatedAt": "desc"}
|
order_by = {"updatedAt": "desc"}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
library_agents = await prisma.models.LibraryAgent.prisma().find_many(
|
# For LAST_EXECUTED sorting, we need to fetch execution data and sort in Python
|
||||||
where=where_clause,
|
# since Prisma doesn't support sorting by nested relations
|
||||||
include=library_agent_include(
|
if sort_by == library_model.LibraryAgentSort.LAST_EXECUTED:
|
||||||
user_id, include_nodes=False, include_executions=include_executions
|
# TODO: This fetches all agents into memory for sorting, which may cause
|
||||||
),
|
# performance issues for users with many agents. Prisma doesn't support
|
||||||
order=order_by,
|
# sorting by nested relations, so a dedicated lastExecutedAt column or
|
||||||
skip=(page - 1) * page_size,
|
# raw SQL query would be needed for database-level pagination.
|
||||||
take=page_size,
|
library_agents = await prisma.models.LibraryAgent.prisma().find_many(
|
||||||
)
|
where=where_clause,
|
||||||
agent_count = await prisma.models.LibraryAgent.prisma().count(
|
include=library_agent_include(
|
||||||
where=where_clause
|
user_id,
|
||||||
)
|
include_nodes=False,
|
||||||
|
include_executions=True,
|
||||||
|
execution_limit=1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_sort_key(
|
||||||
|
agent: prisma.models.LibraryAgent,
|
||||||
|
) -> tuple[int, float]:
|
||||||
|
"""
|
||||||
|
Returns a tuple for sorting: (has_no_executions, -timestamp).
|
||||||
|
|
||||||
|
Agents WITH executions come first (sorted by most recent execution),
|
||||||
|
agents WITHOUT executions come last (sorted by creation date).
|
||||||
|
"""
|
||||||
|
graph = agent.AgentGraph
|
||||||
|
if graph and graph.Executions and len(graph.Executions) > 0:
|
||||||
|
execution = graph.Executions[0]
|
||||||
|
timestamp = execution.updatedAt or execution.createdAt
|
||||||
|
return (0, -timestamp.timestamp())
|
||||||
|
return (1, -agent.createdAt.timestamp())
|
||||||
|
|
||||||
|
library_agents.sort(key=get_sort_key)
|
||||||
|
|
||||||
|
# Apply pagination after sorting
|
||||||
|
agent_count = len(library_agents)
|
||||||
|
start_idx = (page - 1) * page_size
|
||||||
|
end_idx = start_idx + page_size
|
||||||
|
library_agents = library_agents[start_idx:end_idx]
|
||||||
|
else:
|
||||||
|
# Standard sorting via database
|
||||||
|
library_agents = await prisma.models.LibraryAgent.prisma().find_many(
|
||||||
|
where=where_clause,
|
||||||
|
include=library_agent_include(
|
||||||
|
user_id, include_nodes=False, include_executions=False
|
||||||
|
),
|
||||||
|
order=order_by,
|
||||||
|
skip=(page - 1) * page_size,
|
||||||
|
take=page_size,
|
||||||
|
)
|
||||||
|
agent_count = await prisma.models.LibraryAgent.prisma().count(
|
||||||
|
where=where_clause
|
||||||
|
)
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Retrieved {len(library_agents)} library agents for user #{user_id}"
|
f"Retrieved {len(library_agents)} library agents for user #{user_id}"
|
||||||
@@ -345,6 +387,20 @@ async def get_library_agent_by_graph_id(
|
|||||||
graph_id: str,
|
graph_id: str,
|
||||||
graph_version: Optional[int] = None,
|
graph_version: Optional[int] = None,
|
||||||
) -> library_model.LibraryAgent | None:
|
) -> library_model.LibraryAgent | None:
|
||||||
|
"""
|
||||||
|
Retrieves a library agent by its graph ID for a given user.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: The ID of the user who owns the library agent.
|
||||||
|
graph_id: The ID of the agent graph to look up.
|
||||||
|
graph_version: Optional specific version of the graph to retrieve.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The LibraryAgent if found, otherwise None.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
DatabaseError: If there's an error during retrieval.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
filter: prisma.types.LibraryAgentWhereInput = {
|
filter: prisma.types.LibraryAgentWhereInput = {
|
||||||
"agentGraphId": graph_id,
|
"agentGraphId": graph_id,
|
||||||
@@ -628,6 +684,17 @@ async def update_library_agent(
|
|||||||
async def delete_library_agent(
|
async def delete_library_agent(
|
||||||
library_agent_id: str, user_id: str, soft_delete: bool = True
|
library_agent_id: str, user_id: str, soft_delete: bool = True
|
||||||
) -> None:
|
) -> None:
|
||||||
|
"""
|
||||||
|
Deletes a library agent and cleans up associated schedules and webhooks.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
library_agent_id: The ID of the library agent to delete.
|
||||||
|
user_id: The ID of the user who owns the library agent.
|
||||||
|
soft_delete: If True, marks the agent as deleted; if False, permanently removes it.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotFoundError: If the library agent is not found or doesn't belong to the user.
|
||||||
|
"""
|
||||||
# First get the agent to find the graph_id for cleanup
|
# First get the agent to find the graph_id for cleanup
|
||||||
library_agent = await prisma.models.LibraryAgent.prisma().find_unique(
|
library_agent = await prisma.models.LibraryAgent.prisma().find_unique(
|
||||||
where={"id": library_agent_id}, include={"AgentGraph": True}
|
where={"id": library_agent_id}, include={"AgentGraph": True}
|
||||||
@@ -1121,6 +1188,20 @@ async def update_preset(
|
|||||||
async def set_preset_webhook(
|
async def set_preset_webhook(
|
||||||
user_id: str, preset_id: str, webhook_id: str | None
|
user_id: str, preset_id: str, webhook_id: str | None
|
||||||
) -> library_model.LibraryAgentPreset:
|
) -> library_model.LibraryAgentPreset:
|
||||||
|
"""
|
||||||
|
Sets or removes a webhook connection for a preset.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: The ID of the user who owns the preset.
|
||||||
|
preset_id: The ID of the preset to update.
|
||||||
|
webhook_id: The ID of the webhook to connect, or None to disconnect.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The updated LibraryAgentPreset.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotFoundError: If the preset is not found or doesn't belong to the user.
|
||||||
|
"""
|
||||||
current = await prisma.models.AgentPreset.prisma().find_unique(
|
current = await prisma.models.AgentPreset.prisma().find_unique(
|
||||||
where={"id": preset_id},
|
where={"id": preset_id},
|
||||||
include=AGENT_PRESET_INCLUDE,
|
include=AGENT_PRESET_INCLUDE,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
import prisma.enums
|
import prisma.enums
|
||||||
import prisma.models
|
import prisma.models
|
||||||
@@ -9,6 +9,7 @@ from backend.data.db import connect
|
|||||||
from backend.data.includes import library_agent_include
|
from backend.data.includes import library_agent_include
|
||||||
|
|
||||||
from . import db
|
from . import db
|
||||||
|
from . import model as library_model
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -225,3 +226,183 @@ async def test_add_agent_to_library_not_found(mocker):
|
|||||||
mock_store_listing_version.return_value.find_unique.assert_called_once_with(
|
mock_store_listing_version.return_value.find_unique.assert_called_once_with(
|
||||||
where={"id": "version123"}, include={"AgentGraph": True}
|
where={"id": "version123"}, include={"AgentGraph": True}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_list_library_agents_sort_by_last_executed(mocker):
|
||||||
|
"""
|
||||||
|
Test LAST_EXECUTED sorting behavior:
|
||||||
|
- Agents WITH executions come first, sorted by most recent execution (updatedAt)
|
||||||
|
- Agents WITHOUT executions come last, sorted by creation date
|
||||||
|
"""
|
||||||
|
now = datetime.now(timezone.utc)
|
||||||
|
|
||||||
|
# Agent 1: Has execution that finished 1 hour ago
|
||||||
|
agent1_execution = prisma.models.AgentGraphExecution(
|
||||||
|
id="exec1",
|
||||||
|
agentGraphId="agent1",
|
||||||
|
agentGraphVersion=1,
|
||||||
|
userId="test-user",
|
||||||
|
createdAt=now - timedelta(hours=2),
|
||||||
|
updatedAt=now - timedelta(hours=1), # Finished 1 hour ago
|
||||||
|
executionStatus=prisma.enums.AgentExecutionStatus.COMPLETED,
|
||||||
|
isDeleted=False,
|
||||||
|
isShared=False,
|
||||||
|
)
|
||||||
|
agent1_graph = prisma.models.AgentGraph(
|
||||||
|
id="agent1",
|
||||||
|
version=1,
|
||||||
|
name="Agent With Recent Execution",
|
||||||
|
description="Has execution finished 1 hour ago",
|
||||||
|
userId="test-user",
|
||||||
|
isActive=True,
|
||||||
|
createdAt=now - timedelta(days=5),
|
||||||
|
Executions=[agent1_execution],
|
||||||
|
)
|
||||||
|
library_agent1 = prisma.models.LibraryAgent(
|
||||||
|
id="lib1",
|
||||||
|
userId="test-user",
|
||||||
|
agentGraphId="agent1",
|
||||||
|
agentGraphVersion=1,
|
||||||
|
settings="{}", # type: ignore
|
||||||
|
isCreatedByUser=True,
|
||||||
|
isDeleted=False,
|
||||||
|
isArchived=False,
|
||||||
|
createdAt=now - timedelta(days=5),
|
||||||
|
updatedAt=now - timedelta(days=5),
|
||||||
|
isFavorite=False,
|
||||||
|
useGraphIsActiveVersion=True,
|
||||||
|
AgentGraph=agent1_graph,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Agent 2: Has execution that finished 3 hours ago
|
||||||
|
agent2_execution = prisma.models.AgentGraphExecution(
|
||||||
|
id="exec2",
|
||||||
|
agentGraphId="agent2",
|
||||||
|
agentGraphVersion=1,
|
||||||
|
userId="test-user",
|
||||||
|
createdAt=now - timedelta(hours=5),
|
||||||
|
updatedAt=now - timedelta(hours=3), # Finished 3 hours ago
|
||||||
|
executionStatus=prisma.enums.AgentExecutionStatus.COMPLETED,
|
||||||
|
isDeleted=False,
|
||||||
|
isShared=False,
|
||||||
|
)
|
||||||
|
agent2_graph = prisma.models.AgentGraph(
|
||||||
|
id="agent2",
|
||||||
|
version=1,
|
||||||
|
name="Agent With Older Execution",
|
||||||
|
description="Has execution finished 3 hours ago",
|
||||||
|
userId="test-user",
|
||||||
|
isActive=True,
|
||||||
|
createdAt=now - timedelta(days=3),
|
||||||
|
Executions=[agent2_execution],
|
||||||
|
)
|
||||||
|
library_agent2 = prisma.models.LibraryAgent(
|
||||||
|
id="lib2",
|
||||||
|
userId="test-user",
|
||||||
|
agentGraphId="agent2",
|
||||||
|
agentGraphVersion=1,
|
||||||
|
settings="{}", # type: ignore
|
||||||
|
isCreatedByUser=True,
|
||||||
|
isDeleted=False,
|
||||||
|
isArchived=False,
|
||||||
|
createdAt=now - timedelta(days=3),
|
||||||
|
updatedAt=now - timedelta(days=3),
|
||||||
|
isFavorite=False,
|
||||||
|
useGraphIsActiveVersion=True,
|
||||||
|
AgentGraph=agent2_graph,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Agent 3: No executions, created 1 day ago (should come after agents with executions)
|
||||||
|
agent3_graph = prisma.models.AgentGraph(
|
||||||
|
id="agent3",
|
||||||
|
version=1,
|
||||||
|
name="Agent Without Executions (Newer)",
|
||||||
|
description="No executions, created 1 day ago",
|
||||||
|
userId="test-user",
|
||||||
|
isActive=True,
|
||||||
|
createdAt=now - timedelta(days=1),
|
||||||
|
Executions=[],
|
||||||
|
)
|
||||||
|
library_agent3 = prisma.models.LibraryAgent(
|
||||||
|
id="lib3",
|
||||||
|
userId="test-user",
|
||||||
|
agentGraphId="agent3",
|
||||||
|
agentGraphVersion=1,
|
||||||
|
settings="{}", # type: ignore
|
||||||
|
isCreatedByUser=True,
|
||||||
|
isDeleted=False,
|
||||||
|
isArchived=False,
|
||||||
|
createdAt=now - timedelta(days=1),
|
||||||
|
updatedAt=now - timedelta(days=1),
|
||||||
|
isFavorite=False,
|
||||||
|
useGraphIsActiveVersion=True,
|
||||||
|
AgentGraph=agent3_graph,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Agent 4: No executions, created 2 days ago
|
||||||
|
agent4_graph = prisma.models.AgentGraph(
|
||||||
|
id="agent4",
|
||||||
|
version=1,
|
||||||
|
name="Agent Without Executions (Older)",
|
||||||
|
description="No executions, created 2 days ago",
|
||||||
|
userId="test-user",
|
||||||
|
isActive=True,
|
||||||
|
createdAt=now - timedelta(days=2),
|
||||||
|
Executions=[],
|
||||||
|
)
|
||||||
|
library_agent4 = prisma.models.LibraryAgent(
|
||||||
|
id="lib4",
|
||||||
|
userId="test-user",
|
||||||
|
agentGraphId="agent4",
|
||||||
|
agentGraphVersion=1,
|
||||||
|
settings="{}", # type: ignore
|
||||||
|
isCreatedByUser=True,
|
||||||
|
isDeleted=False,
|
||||||
|
isArchived=False,
|
||||||
|
createdAt=now - timedelta(days=2),
|
||||||
|
updatedAt=now - timedelta(days=2),
|
||||||
|
isFavorite=False,
|
||||||
|
useGraphIsActiveVersion=True,
|
||||||
|
AgentGraph=agent4_graph,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Return agents in random order to verify sorting works
|
||||||
|
mock_library_agents = [
|
||||||
|
library_agent3,
|
||||||
|
library_agent1,
|
||||||
|
library_agent4,
|
||||||
|
library_agent2,
|
||||||
|
]
|
||||||
|
|
||||||
|
# Mock prisma calls
|
||||||
|
mock_agent_graph = mocker.patch("prisma.models.AgentGraph.prisma")
|
||||||
|
mock_agent_graph.return_value.find_many = mocker.AsyncMock(return_value=[])
|
||||||
|
|
||||||
|
mock_library_agent = mocker.patch("prisma.models.LibraryAgent.prisma")
|
||||||
|
mock_library_agent.return_value.find_many = mocker.AsyncMock(
|
||||||
|
return_value=mock_library_agents
|
||||||
|
)
|
||||||
|
|
||||||
|
# Call function with LAST_EXECUTED sort
|
||||||
|
result = await db.list_library_agents(
|
||||||
|
"test-user",
|
||||||
|
sort_by=library_model.LibraryAgentSort.LAST_EXECUTED,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify sorting order:
|
||||||
|
# 1. Agent 1 (execution finished 1 hour ago) - most recent execution
|
||||||
|
# 2. Agent 2 (execution finished 3 hours ago) - older execution
|
||||||
|
# 3. Agent 3 (no executions, created 1 day ago) - newer creation
|
||||||
|
# 4. Agent 4 (no executions, created 2 days ago) - older creation
|
||||||
|
assert len(result.agents) == 4
|
||||||
|
assert (
|
||||||
|
result.agents[0].id == "lib1"
|
||||||
|
), "Agent with most recent execution should be first"
|
||||||
|
assert result.agents[1].id == "lib2", "Agent with older execution should be second"
|
||||||
|
assert (
|
||||||
|
result.agents[2].id == "lib3"
|
||||||
|
), "Agent without executions (newer) should be third"
|
||||||
|
assert (
|
||||||
|
result.agents[3].id == "lib4"
|
||||||
|
), "Agent without executions (older) should be last"
|
||||||
|
|||||||
@@ -442,6 +442,7 @@ class LibraryAgentSort(str, Enum):
|
|||||||
|
|
||||||
CREATED_AT = "createdAt"
|
CREATED_AT = "createdAt"
|
||||||
UPDATED_AT = "updatedAt"
|
UPDATED_AT = "updatedAt"
|
||||||
|
LAST_EXECUTED = "lastExecuted"
|
||||||
|
|
||||||
|
|
||||||
class LibraryAgentUpdateRequest(pydantic.BaseModel):
|
class LibraryAgentUpdateRequest(pydantic.BaseModel):
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ async def list_library_agents(
|
|||||||
None, description="Search term to filter agents"
|
None, description="Search term to filter agents"
|
||||||
),
|
),
|
||||||
sort_by: library_model.LibraryAgentSort = Query(
|
sort_by: library_model.LibraryAgentSort = Query(
|
||||||
library_model.LibraryAgentSort.UPDATED_AT,
|
library_model.LibraryAgentSort.LAST_EXECUTED,
|
||||||
description="Criteria to sort results by",
|
description="Criteria to sort results by",
|
||||||
),
|
),
|
||||||
page: int = Query(
|
page: int = Query(
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ async def test_get_library_agents_success(
|
|||||||
mock_db_call.assert_called_once_with(
|
mock_db_call.assert_called_once_with(
|
||||||
user_id=test_user_id,
|
user_id=test_user_id,
|
||||||
search_term="test",
|
search_term="test",
|
||||||
sort_by=library_model.LibraryAgentSort.UPDATED_AT,
|
sort_by=library_model.LibraryAgentSort.LAST_EXECUTED,
|
||||||
page=1,
|
page=1,
|
||||||
page_size=15,
|
page_size=15,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -119,7 +119,9 @@ def library_agent_include(
|
|||||||
if include_executions:
|
if include_executions:
|
||||||
agent_graph_include["Executions"] = {
|
agent_graph_include["Executions"] = {
|
||||||
"where": {"userId": user_id},
|
"where": {"userId": user_id},
|
||||||
"order_by": {"createdAt": "desc"},
|
"order_by": {
|
||||||
|
"updatedAt": "desc"
|
||||||
|
}, # Uses updatedAt because it reflects when the executioncompleted or last progressed
|
||||||
"take": execution_limit,
|
"take": execution_limit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
import fastapi
|
|
||||||
from fastapi.routing import APIRoute
|
|
||||||
|
|
||||||
from backend.api.features.integrations.router import router as integrations_router
|
|
||||||
from backend.integrations.providers import ProviderName
|
|
||||||
from backend.integrations.webhooks import utils as webhooks_utils
|
|
||||||
|
|
||||||
|
|
||||||
def test_webhook_ingress_url_matches_route(monkeypatch) -> None:
|
|
||||||
app = fastapi.FastAPI()
|
|
||||||
app.include_router(integrations_router, prefix="/api/integrations")
|
|
||||||
|
|
||||||
provider = ProviderName.GITHUB
|
|
||||||
webhook_id = "webhook_123"
|
|
||||||
base_url = "https://example.com"
|
|
||||||
|
|
||||||
monkeypatch.setattr(webhooks_utils.app_config, "platform_base_url", base_url)
|
|
||||||
|
|
||||||
route = next(
|
|
||||||
route
|
|
||||||
for route in integrations_router.routes
|
|
||||||
if isinstance(route, APIRoute)
|
|
||||||
and route.path == "/{provider}/webhooks/{webhook_id}/ingress"
|
|
||||||
and "POST" in route.methods
|
|
||||||
)
|
|
||||||
expected_path = f"/api/integrations{route.path}".format(
|
|
||||||
provider=provider.value,
|
|
||||||
webhook_id=webhook_id,
|
|
||||||
)
|
|
||||||
actual_url = urlparse(webhooks_utils.webhook_ingress_url(provider, webhook_id))
|
|
||||||
expected_base = urlparse(base_url)
|
|
||||||
|
|
||||||
assert (actual_url.scheme, actual_url.netloc) == (
|
|
||||||
expected_base.scheme,
|
|
||||||
expected_base.netloc,
|
|
||||||
)
|
|
||||||
assert actual_url.path == expected_path
|
|
||||||
@@ -23,10 +23,13 @@ export function LibrarySortMenu({ setLibrarySort }: Props) {
|
|||||||
<Select onValueChange={handleSortChange}>
|
<Select onValueChange={handleSortChange}>
|
||||||
<SelectTrigger className="ml-1 w-fit space-x-1 border-none px-0 text-base underline underline-offset-4 shadow-none">
|
<SelectTrigger className="ml-1 w-fit space-x-1 border-none px-0 text-base underline underline-offset-4 shadow-none">
|
||||||
<ArrowDownNarrowWideIcon className="h-4 w-4 sm:hidden" />
|
<ArrowDownNarrowWideIcon className="h-4 w-4 sm:hidden" />
|
||||||
<SelectValue placeholder="Last Modified" />
|
<SelectValue placeholder="Last Executed" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
|
<SelectItem value={LibraryAgentSort.lastExecuted}>
|
||||||
|
Last Executed
|
||||||
|
</SelectItem>
|
||||||
<SelectItem value={LibraryAgentSort.createdAt}>
|
<SelectItem value={LibraryAgentSort.createdAt}>
|
||||||
Creation Date
|
Creation Date
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
|
|||||||
@@ -11,12 +11,14 @@ export function useLibrarySortMenu({ setLibrarySort }: Props) {
|
|||||||
|
|
||||||
const getSortLabel = (sort: LibraryAgentSort) => {
|
const getSortLabel = (sort: LibraryAgentSort) => {
|
||||||
switch (sort) {
|
switch (sort) {
|
||||||
|
case LibraryAgentSort.lastExecuted:
|
||||||
|
return "Last Executed";
|
||||||
case LibraryAgentSort.createdAt:
|
case LibraryAgentSort.createdAt:
|
||||||
return "Creation Date";
|
return "Creation Date";
|
||||||
case LibraryAgentSort.updatedAt:
|
case LibraryAgentSort.updatedAt:
|
||||||
return "Last Modified";
|
return "Last Modified";
|
||||||
default:
|
default:
|
||||||
return "Last Modified";
|
return "Last Executed";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { LibraryAgentSort } from "@/app/api/__generated__/models/libraryAgentSort";
|
import { LibraryAgentSort } from "@/app/api/__generated__/models/libraryAgentSort";
|
||||||
import { parseAsStringEnum, useQueryState } from "nuqs";
|
import { parseAsStringEnum, useQueryState } from "nuqs";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useMemo, useState } from "react";
|
||||||
|
|
||||||
const sortParser = parseAsStringEnum(Object.values(LibraryAgentSort));
|
const sortParser = parseAsStringEnum(Object.values(LibraryAgentSort));
|
||||||
|
|
||||||
@@ -11,14 +11,7 @@ export function useLibraryListPage() {
|
|||||||
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
|
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
|
||||||
const [librarySortRaw, setLibrarySortRaw] = useQueryState("sort", sortParser);
|
const [librarySortRaw, setLibrarySortRaw] = useQueryState("sort", sortParser);
|
||||||
|
|
||||||
// Ensure sort param is always present in URL (even if default)
|
const librarySort = librarySortRaw || LibraryAgentSort.lastExecuted;
|
||||||
useEffect(() => {
|
|
||||||
if (!librarySortRaw) {
|
|
||||||
setLibrarySortRaw(LibraryAgentSort.updatedAt, { shallow: false });
|
|
||||||
}
|
|
||||||
}, [librarySortRaw, setLibrarySortRaw]);
|
|
||||||
|
|
||||||
const librarySort = librarySortRaw || LibraryAgentSort.updatedAt;
|
|
||||||
|
|
||||||
const setLibrarySort = useCallback(
|
const setLibrarySort = useCallback(
|
||||||
(value: LibraryAgentSort) => {
|
(value: LibraryAgentSort) => {
|
||||||
|
|||||||
@@ -3361,7 +3361,7 @@
|
|||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/components/schemas/LibraryAgentSort",
|
"$ref": "#/components/schemas/LibraryAgentSort",
|
||||||
"description": "Criteria to sort results by",
|
"description": "Criteria to sort results by",
|
||||||
"default": "updatedAt"
|
"default": "lastExecuted"
|
||||||
},
|
},
|
||||||
"description": "Criteria to sort results by"
|
"description": "Criteria to sort results by"
|
||||||
},
|
},
|
||||||
@@ -8239,7 +8239,7 @@
|
|||||||
},
|
},
|
||||||
"LibraryAgentSort": {
|
"LibraryAgentSort": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["createdAt", "updatedAt"],
|
"enum": ["createdAt", "updatedAt", "lastExecuted"],
|
||||||
"title": "LibraryAgentSort",
|
"title": "LibraryAgentSort",
|
||||||
"description": "Possible sort options for sorting library agents."
|
"description": "Possible sort options for sorting library agents."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -612,6 +612,7 @@ export type LibraryAgentPresetUpdatable = Partial<
|
|||||||
export enum LibraryAgentSortEnum {
|
export enum LibraryAgentSortEnum {
|
||||||
CREATED_AT = "createdAt",
|
CREATED_AT = "createdAt",
|
||||||
UPDATED_AT = "updatedAt",
|
UPDATED_AT = "updatedAt",
|
||||||
|
LAST_EXECUTED = "lastExecuted",
|
||||||
}
|
}
|
||||||
|
|
||||||
/* *** CREDENTIALS *** */
|
/* *** CREDENTIALS *** */
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export class LibraryPage extends BasePage {
|
|||||||
|
|
||||||
async selectSortOption(
|
async selectSortOption(
|
||||||
page: Page,
|
page: Page,
|
||||||
sortOption: "Creation Date" | "Last Modified",
|
sortOption: "Last Executed" | "Creation Date" | "Last Modified",
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { getRole } = getSelectors(page);
|
const { getRole } = getSelectors(page);
|
||||||
await getRole("combobox").click();
|
await getRole("combobox").click();
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ test("logged in user is redirected from /login to /library", async ({
|
|||||||
await hasUrl(page, "/marketplace");
|
await hasUrl(page, "/marketplace");
|
||||||
|
|
||||||
await page.goto("/login");
|
await page.goto("/login");
|
||||||
await hasUrl(page, "/library?sort=updatedAt");
|
await hasUrl(page, "/library");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("logged in user is redirected from /signup to /library", async ({
|
test("logged in user is redirected from /signup to /library", async ({
|
||||||
@@ -195,5 +195,5 @@ test("logged in user is redirected from /signup to /library", async ({
|
|||||||
await hasUrl(page, "/marketplace");
|
await hasUrl(page, "/marketplace");
|
||||||
|
|
||||||
await page.goto("/signup");
|
await page.goto("/signup");
|
||||||
await hasUrl(page, "/library?sort=updatedAt");
|
await hasUrl(page, "/library");
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user