diff --git a/fern/openapi.json b/fern/openapi.json index 04627669..922aa8f6 100644 --- a/fern/openapi.json +++ b/fern/openapi.json @@ -4197,6 +4197,87 @@ "type": "string", "title": "Agent Id" } + }, + { + "name": "before", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Tool ID cursor for pagination. Returns tools that come before this tool ID in the specified sort order", + "title": "Before" + }, + "description": "Tool ID cursor for pagination. Returns tools that come before this tool ID in the specified sort order" + }, + { + "name": "after", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Tool ID cursor for pagination. Returns tools that come after this tool ID in the specified sort order", + "title": "After" + }, + "description": "Tool ID cursor for pagination. Returns tools that come after this tool ID in the specified sort order" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Maximum number of tools to return", + "default": 10, + "title": "Limit" + }, + "description": "Maximum number of tools to return" + }, + { + "name": "order", + "in": "query", + "required": false, + "schema": { + "enum": ["asc", "desc"], + "type": "string", + "description": "Sort order for tools by creation time. 'asc' for oldest first, 'desc' for newest first", + "default": "desc", + "title": "Order" + }, + "description": "Sort order for tools 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" } ], "responses": { diff --git a/letta/server/rest_api/routers/v1/agents.py b/letta/server/rest_api/routers/v1/agents.py index 93b7907f..fe6ec325 100644 --- a/letta/server/rest_api/routers/v1/agents.py +++ b/letta/server/rest_api/routers/v1/agents.py @@ -460,10 +460,28 @@ async def list_agent_tools( agent_id: str, server: "SyncServer" = Depends(get_letta_server), headers: HeaderParams = Depends(get_headers), + before: Optional[str] = Query( + None, description="Tool ID cursor for pagination. Returns tools that come before this tool ID in the specified sort order" + ), + after: Optional[str] = Query( + None, description="Tool ID cursor for pagination. Returns tools that come after this tool ID in the specified sort order" + ), + limit: Optional[int] = Query(10, description="Maximum number of tools to return"), + order: Literal["asc", "desc"] = Query( + "desc", description="Sort order for tools by creation time. 'asc' for oldest first, 'desc' for newest first" + ), + order_by: Literal["created_at"] = Query("created_at", description="Field to sort by"), ): """Get tools from an existing agent""" actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id) - return await server.agent_manager.list_attached_tools_async(agent_id=agent_id, actor=actor) + return await server.agent_manager.list_attached_tools_async( + agent_id=agent_id, + actor=actor, + before=before, + after=after, + limit=limit, + ascending=(order == "asc"), + ) @router.patch("/{agent_id}/tools/attach/{tool_id}", response_model=AgentState, operation_id="attach_tool") diff --git a/letta/services/agent_manager.py b/letta/services/agent_manager.py index d6b62a15..1d275ef4 100644 --- a/letta/services/agent_manager.py +++ b/letta/services/agent_manager.py @@ -2579,7 +2579,15 @@ class AgentManager: @enforce_types @trace_method - async def list_attached_tools_async(self, agent_id: str, actor: PydanticUser) -> List[PydanticTool]: + async def list_attached_tools_async( + self, + agent_id: str, + actor: PydanticUser, + before: Optional[str] = None, + after: Optional[str] = None, + limit: Optional[int] = None, + ascending: bool = False, + ) -> List[PydanticTool]: """ List all tools attached to an agent (async version with optimized performance). Uses direct SQL queries to avoid SqlAlchemyBase overhead. @@ -2587,6 +2595,10 @@ class AgentManager: Args: agent_id: ID of the agent to list tools for. actor: User performing the action. + before: Tool ID cursor for pagination. Returns tools that come before this tool ID. + after: Tool ID cursor for pagination. Returns tools that come after this tool ID. + limit: Maximum number of tools to return. + ascending: Sort order by creation time. Returns: List[PydanticTool]: List of tools attached to the agent. @@ -2602,6 +2614,22 @@ class AgentManager: .where(ToolsAgents.agent_id == agent_id, ToolModel.organization_id == actor.organization_id) ) + # Apply cursor-based pagination + if before: + query = query.where(ToolModel.id < before) + if after: + query = query.where(ToolModel.id > after) + + # Apply sorting + if ascending: + query = query.order_by(ToolModel.created_at.asc()) + else: + query = query.order_by(ToolModel.created_at.desc()) + + # Apply limit + if limit: + query = query.limit(limit) + result = await session.execute(query) tools = result.scalars().all() return [tool.to_pydantic() for tool in tools]