Compare commits

...

1 Commits

Author SHA1 Message Date
itsababseh
12d67477fa feat(platform/library): sort by last run 2025-05-24 18:08:01 +01:00
7 changed files with 46 additions and 17 deletions

View File

@@ -1,3 +1,4 @@
import datetime
import logging import logging
from typing import Optional from typing import Optional
@@ -29,7 +30,7 @@ integration_creds_manager = IntegrationCredentialsManager()
async def list_library_agents( async def list_library_agents(
user_id: str, user_id: str,
search_term: Optional[str] = None, search_term: Optional[str] = None,
sort_by: library_model.LibraryAgentSort = library_model.LibraryAgentSort.UPDATED_AT, sort_by: library_model.LibraryAgentSort = library_model.LibraryAgentSort.LAST_EXECUTED_AT,
page: int = 1, page: int = 1,
page_size: int = 50, page_size: int = 50,
) -> library_model.LibraryAgentResponse: ) -> library_model.LibraryAgentResponse:
@@ -95,16 +96,23 @@ async def list_library_agents(
order_by = {"updatedAt": "desc"} order_by = {"updatedAt": "desc"}
try: try:
library_agents = await prisma.models.LibraryAgent.prisma().find_many( if sort_by == library_model.LibraryAgentSort.LAST_EXECUTED_AT:
where=where_clause, library_agents = await prisma.models.LibraryAgent.prisma().find_many(
include=library_agent_include(user_id), where=where_clause,
order=order_by, include=library_agent_include(user_id),
skip=(page - 1) * page_size, )
take=page_size, agent_count = len(library_agents)
) else:
agent_count = await prisma.models.LibraryAgent.prisma().count( library_agents = await prisma.models.LibraryAgent.prisma().find_many(
where=where_clause where=where_clause,
) include=library_agent_include(user_id),
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}"
@@ -124,7 +132,15 @@ async def list_library_agents(
) )
continue continue
# Return the response with only valid agents if sort_by == library_model.LibraryAgentSort.LAST_EXECUTED_AT:
valid_library_agents.sort(
key=lambda a: a.last_executed_at
or datetime.datetime.min.replace(tzinfo=datetime.timezone.utc),
reverse=True,
)
start = (page - 1) * page_size
valid_library_agents = valid_library_agents[start : start + page_size]
return library_model.LibraryAgentResponse( return library_model.LibraryAgentResponse(
agents=valid_library_agents, agents=valid_library_agents,
pagination=backend.server.model.Pagination( pagination=backend.server.model.Pagination(

View File

@@ -36,6 +36,8 @@ class LibraryAgent(pydantic.BaseModel):
status: LibraryAgentStatus status: LibraryAgentStatus
updated_at: datetime.datetime updated_at: datetime.datetime
# Most recent time this agent was executed
last_executed_at: Optional[datetime.datetime] = None
name: str name: str
description: str description: str
@@ -88,6 +90,10 @@ class LibraryAgent(pydantic.BaseModel):
status = status_result.status status = status_result.status
new_output = status_result.new_output new_output = status_result.new_output
last_executed_at = None
if executions:
last_executed_at = max(exec.createdAt for exec in executions)
# Check if user can access the graph # Check if user can access the graph
can_access_graph = agent.AgentGraph.userId == agent.userId can_access_graph = agent.AgentGraph.userId == agent.userId
@@ -103,6 +109,7 @@ class LibraryAgent(pydantic.BaseModel):
creator_image_url=creator_image_url, creator_image_url=creator_image_url,
status=status, status=status,
updated_at=updated_at, updated_at=updated_at,
last_executed_at=last_executed_at,
name=graph.name, name=graph.name,
description=graph.description, description=graph.description,
input_schema=graph.input_schema, input_schema=graph.input_schema,
@@ -235,6 +242,7 @@ class LibraryAgentSort(str, Enum):
CREATED_AT = "createdAt" CREATED_AT = "createdAt"
UPDATED_AT = "updatedAt" UPDATED_AT = "updatedAt"
LAST_EXECUTED_AT = "lastExecutedAt"
class LibraryAgentUpdateRequest(pydantic.BaseModel): class LibraryAgentUpdateRequest(pydantic.BaseModel):

View File

@@ -30,7 +30,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_AT,
description="Criteria to sort results by", description="Criteria to sort results by",
), ),
page: int = Query( page: int = Query(

View File

@@ -85,7 +85,7 @@ async def test_get_library_agents_success(mocker: pytest_mock.MockFixture):
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_AT,
page=1, page=1,
page_size=15, page_size=15,
) )
@@ -100,7 +100,7 @@ def test_get_library_agents_error(mocker: pytest_mock.MockFixture):
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_AT,
page=1, page=1,
page_size=15, page_size=15,
) )

View File

@@ -37,7 +37,7 @@ export function LibraryPageStateProvider({
const [searchTerm, setSearchTerm] = useState<string | undefined>(""); const [searchTerm, setSearchTerm] = useState<string | undefined>("");
const [uploadedFile, setUploadedFile] = useState<File | null>(null); const [uploadedFile, setUploadedFile] = useState<File | null>(null);
const [librarySort, setLibrarySort] = useState<LibraryAgentSortEnum>( const [librarySort, setLibrarySort] = useState<LibraryAgentSortEnum>(
LibraryAgentSortEnum.UPDATED_AT, LibraryAgentSortEnum.LAST_EXECUTED_AT,
); );
return ( return (

View File

@@ -34,7 +34,7 @@ export default function LibrarySortMenu(): React.ReactNode {
<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 Ran" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectGroup> <SelectGroup>
@@ -44,6 +44,9 @@ export default function LibrarySortMenu(): React.ReactNode {
<SelectItem value={LibraryAgentSortEnum.UPDATED_AT}> <SelectItem value={LibraryAgentSortEnum.UPDATED_AT}>
Last Modified Last Modified
</SelectItem> </SelectItem>
<SelectItem value={LibraryAgentSortEnum.LAST_EXECUTED_AT}>
Last Ran
</SelectItem>
</SelectGroup> </SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>

View File

@@ -397,6 +397,7 @@ export type LibraryAgent = {
creator_image_url: string; creator_image_url: string;
status: AgentStatus; status: AgentStatus;
updated_at: Date; updated_at: Date;
last_executed_at?: Date;
name: string; name: string;
description: string; description: string;
input_schema: BlockIOObjectSubSchema; input_schema: BlockIOObjectSubSchema;
@@ -456,6 +457,7 @@ export interface CreateLibraryAgentPresetRequest {
export enum LibraryAgentSortEnum { export enum LibraryAgentSortEnum {
CREATED_AT = "createdAt", CREATED_AT = "createdAt",
UPDATED_AT = "updatedAt", UPDATED_AT = "updatedAt",
LAST_EXECUTED_AT = "lastExecutedAt",
} }
/* *** CREDENTIALS *** */ /* *** CREDENTIALS *** */