Pagination for /spend/logs/session/ui endpoint (#16603)

This commit is contained in:
yuneng-jiang 2025-11-13 22:03:00 -08:00 • committed by GitHub
parent 01065a1284
commit 379aa7b79a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 99 additions and 5 deletions

View file

@ -2968,10 +2968,30 @@ async def ui_view_session_spend_logs(
session_id: str = fastapi.Query(
description="Get all spend logs for a particular session",
),
page: int = fastapi.Query(
default=1,
ge=1,
description="Page number for pagination",
),
page_size: int = fastapi.Query(
default=50,
ge=1,
le=100,
description="Number of items per page",
),
user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
):
"""
Get all spend logs for a particular session
Get paginated spend logs for a particular session.
Returns:
{
"data": List[LiteLLM_SpendLogs],
"total": int,
"page": int,
"page_size": int,
"total_pages": int,
}
"""
from litellm.proxy.proxy_server import prisma_client
@ -2984,11 +3004,32 @@ async def ui_view_session_spend_logs(
# Build query conditions
where_conditions = {"session_id": session_id}
# Query the database
result = await prisma_client.db.litellm_spendlogs.find_many(
where=where_conditions, order={"startTime": "asc"}
# Calculate pagination offsets
skip = (page - 1) * page_size
# Get total count for pagination metadata
total_records = await prisma_client.db.litellm_spendlogs.count(
where=where_conditions
)
return result
# Query the database with pagination
result = await prisma_client.db.litellm_spendlogs.find_many(
where=where_conditions,
order={"startTime": "asc"},
skip=skip,
take=page_size,
)
total_pages = (total_records + page_size - 1) // page_size
return {
"data": result,
"total": total_records,
"page": page,
"page_size": page_size,
"total_pages": total_pages,
}
except Exception as e:
if isinstance(e, HTTPException):
raise e

View file

@ -628,6 +628,59 @@ async def test_ui_view_spend_logs_pagination(client, monkeypatch):
assert data["page"] == 2
@pytest.mark.asyncio
async def test_ui_view_session_spend_logs_pagination(client, monkeypatch):
mock_spend_logs = [
{
"id": "log1",
"request_id": "req1",
"session_id": "session-123",
"startTime": "2024-01-01T00:00:00Z",
},
{
"id": "log2",
"request_id": "req2",
"session_id": "session-123",
"startTime": "2024-01-02T00:00:00Z",
},
]
class MockDB:
async def count(self, *args, **kwargs):
assert kwargs.get("where") == {"session_id": "session-123"}
return len(mock_spend_logs)
async def find_many(self, *args, **kwargs):
assert kwargs.get("where") == {"session_id": "session-123"}
assert kwargs.get("order") == {"startTime": "asc"}
assert kwargs.get("skip") == 1 # page=2, page_size=1
assert kwargs.get("take") == 1
return [mock_spend_logs[1]]
class MockPrismaClient:
def __init__(self):
self.db = MockDB()
self.db.litellm_spendlogs = self.db
mock_prisma_client = MockPrismaClient()
monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client)
response = client.get(
"/spend/logs/session/ui",
params={"session_id": "session-123", "page": 2, "page_size": 1},
headers={"Authorization": "Bearer sk-test"},
)
assert response.status_code == 200
data = response.json()
assert data["total"] == 2
assert data["page"] == 2
assert data["page_size"] == 1
assert data["total_pages"] == 2
assert len(data["data"]) == 1
assert data["data"][0]["request_id"] == "req2"
@pytest.mark.asyncio
async def test_ui_view_spend_logs_date_range_filter(client, monkeypatch):
# Create mock data with different dates