From 6d7b0a5ae952205075e778d2e21c896fdb048535 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Wed, 6 May 2026 16:17:58 -0700 Subject: [PATCH] feat(agent_endpoints): cursor pagination on GET /v2/agents Switches list_agents from offset-based to cursor-based pagination returning {items, next_cursor}. Matches Cursor SDK shape. --- .../agent_endpoints.py | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/litellm/proxy/agent_session_endpoints/agent_endpoints.py b/litellm/proxy/agent_session_endpoints/agent_endpoints.py index 51113b3e628..fc69e95824a 100644 --- a/litellm/proxy/agent_session_endpoints/agent_endpoints.py +++ b/litellm/proxy/agent_session_endpoints/agent_endpoints.py @@ -14,6 +14,7 @@ row is removed. import asyncio from datetime import datetime, timezone +from typing import Optional from fastapi import APIRouter, Depends, HTTPException, Query from fastapi.responses import ORJSONResponse @@ -28,6 +29,13 @@ from litellm.proxy.agent_session_endpoints.ownership import ( caller_api_key_hash, owner_filter_for_caller, ) +from litellm.proxy.agent_session_endpoints.pagination import ( + DEFAULT_PAGE_LIMIT, + MAX_PAGE_LIMIT, + build_page_response, + cursor_where_clause, + normalize_limit, +) from litellm.proxy.agent_session_endpoints.schemas import ( AgentCreate, AgentResponse, @@ -168,18 +176,26 @@ async def update_agent( ) async def list_agents( user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), - limit: int = Query(default=100, ge=1, le=500), - offset: int = Query(default=0, ge=0), + cursor: Optional[str] = Query(default=None), + limit: int = Query(default=DEFAULT_PAGE_LIMIT, ge=1, le=MAX_PAGE_LIMIT), ): + """List agents owned by the caller (or all, for proxy admins). + + Cursor-paginated. Pass ``?cursor=`` from the previous + response to fetch the next page. + """ prisma_client = await _get_prisma_client_or_503() - where_filter = owner_filter_for_caller(user_api_key_dict) + page_limit = normalize_limit(limit) rows = await prisma_client.db.litellm_agent.find_many( - where=where_filter, - order={"created_at": "desc"}, - take=limit, - skip=offset, + where=cursor_where_clause(owner_filter_for_caller(user_api_key_dict), cursor), + order=[{"created_at": "desc"}, {"id": "desc"}], + take=page_limit + 1, + ) + return build_page_response( + rows, + page_limit, + lambda r: agent_row_to_response(r).model_dump(), ) - return {"data": [agent_row_to_response(r).model_dump() for r in rows]} @router.delete(