From e96fd83adff2d0f2304c33bc560fa9f87ad82a52 Mon Sep 17 00:00:00 2001 From: cthomas Date: Wed, 1 Oct 2025 17:31:34 -0700 Subject: [PATCH] feat: add pagination to list jobs endpoint (#5078) * feat: add pagination to list jobs endpoint * chore: regenerate API client with pagination parameters --- fern/openapi.json | 46 +++++++++++++++++++----- letta/server/rest_api/routers/v1/jobs.py | 29 +++++++++++---- 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/fern/openapi.json b/fern/openapi.json index b323b18c..af2f31dc 100644 --- a/fern/openapi.json +++ b/fern/openapi.json @@ -9009,10 +9009,10 @@ "type": "null" } ], - "description": "Cursor for pagination", + "description": "Job ID cursor for pagination. Returns jobs that come before this job ID in the specified sort order", "title": "Before" }, - "description": "Cursor for pagination" + "description": "Job ID cursor for pagination. Returns jobs that come before this job ID in the specified sort order" }, { "name": "after", @@ -9027,10 +9027,10 @@ "type": "null" } ], - "description": "Cursor for pagination", + "description": "Job ID cursor for pagination. Returns jobs that come after this job ID in the specified sort order", "title": "After" }, - "description": "Cursor for pagination" + "description": "Job ID cursor for pagination. Returns jobs that come after this job ID in the specified sort order" }, { "name": "limit", @@ -9045,11 +9045,37 @@ "type": "null" } ], - "description": "Limit for pagination", - "default": 50, + "description": "Maximum number of jobs to return", + "default": 100, "title": "Limit" }, - "description": "Limit for pagination" + "description": "Maximum number of jobs to return" + }, + { + "name": "order", + "in": "query", + "required": false, + "schema": { + "enum": ["asc", "desc"], + "type": "string", + "description": "Sort order for jobs by creation time. 'asc' for oldest first, 'desc' for newest first", + "default": "desc", + "title": "Order" + }, + "description": "Sort order for jobs by creation time. 'asc' for oldest first, 'desc' for newest first" + }, + { + "name": "order_by", + "in": "query", + "required": false, + "schema": { + "const": "created_at", + "type": "string", + "description": "Field to sort by", + "default": "created_at", + "title": "Order By" + }, + "description": "Field to sort by" }, { "name": "active", @@ -9069,11 +9095,13 @@ "required": false, "schema": { "type": "boolean", - "description": "Whether to sort jobs oldest to newest (True, default) or newest to oldest (False)", + "description": "Whether to sort jobs oldest to newest (True, default) or newest to oldest (False). Deprecated in favor of order field.", + "deprecated": true, "default": true, "title": "Ascending" }, - "description": "Whether to sort jobs oldest to newest (True, default) or newest to oldest (False)" + "description": "Whether to sort jobs oldest to newest (True, default) or newest to oldest (False). Deprecated in favor of order field.", + "deprecated": true } ], "responses": { diff --git a/letta/server/rest_api/routers/v1/jobs.py b/letta/server/rest_api/routers/v1/jobs.py index 2a39c7ed..7aeccfaa 100644 --- a/letta/server/rest_api/routers/v1/jobs.py +++ b/letta/server/rest_api/routers/v1/jobs.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import List, Literal, Optional from fastapi import APIRouter, Depends, HTTPException, Query @@ -16,11 +16,23 @@ router = APIRouter(prefix="/jobs", tags=["jobs"]) async def list_jobs( server: "SyncServer" = Depends(get_letta_server), source_id: Optional[str] = Query(None, description="Only list jobs associated with the source."), - before: Optional[str] = Query(None, description="Cursor for pagination"), - after: Optional[str] = Query(None, description="Cursor for pagination"), - limit: Optional[int] = Query(50, description="Limit for pagination"), + before: Optional[str] = Query( + None, description="Job ID cursor for pagination. Returns jobs that come before this job ID in the specified sort order" + ), + after: Optional[str] = Query( + None, description="Job ID cursor for pagination. Returns jobs that come after this job ID in the specified sort order" + ), + limit: Optional[int] = Query(100, description="Maximum number of jobs to return"), + order: Literal["asc", "desc"] = Query( + "desc", description="Sort order for jobs by creation time. 'asc' for oldest first, 'desc' for newest first" + ), + order_by: Literal["created_at"] = Query("created_at", description="Field to sort by"), active: bool = Query(False, description="Filter for active jobs."), - ascending: bool = Query(True, description="Whether to sort jobs oldest to newest (True, default) or newest to oldest (False)"), + ascending: bool = Query( + True, + description="Whether to sort jobs oldest to newest (True, default) or newest to oldest (False). Deprecated in favor of order field.", + deprecated=True, + ), headers: HeaderParams = Depends(get_headers), ): """ @@ -32,6 +44,11 @@ async def list_jobs( if active: statuses = [JobStatus.created, JobStatus.running] + if ascending is not True: + sort_ascending = ascending + else: + sort_ascending = order == "asc" + # TODO: add filtering by status return await server.job_manager.list_jobs_async( actor=actor, @@ -40,7 +57,7 @@ async def list_jobs( before=before, after=after, limit=limit, - ascending=ascending, + ascending=sort_ascending, )