Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 41 additions & 17 deletions litellm/proxy/spend_tracking/spend_management_endpoints.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#### SPEND MANAGEMENT #####
import collections
import json
import os
from datetime import datetime, timedelta, timezone
from functools import lru_cache
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional

import fastapi
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi import APIRouter, Depends, HTTPException, Request, status

import litellm
from litellm._logging import verbose_proxy_logger
Expand Down Expand Up @@ -1609,6 +1610,14 @@ async def calculate_spend(request: SpendCalculateRequest):
)


@router.get(
"/spend/logs/v2",
tags=["Budget & Spend Tracking"],
dependencies=[Depends(user_api_key_auth)],
responses={
200: {"model": Dict[str, Any]},
},
)
@router.get(
"/spend/logs/ui",
tags=["Budget & Spend Tracking"],
Expand All @@ -1619,6 +1628,7 @@ async def calculate_spend(request: SpendCalculateRequest):
},
)
async def ui_view_spend_logs( # noqa: PLR0915
request: Request,
api_key: Optional[str] = fastapi.Query(
default=None,
description="Get spend logs based on api key",
Expand Down Expand Up @@ -1672,16 +1682,16 @@ async def ui_view_spend_logs( # noqa: PLR0915
),
):
"""
View spend logs for UI with pagination support
View spend logs with pagination support.
Available at both `/spend/logs/v2` (public API) and `/spend/logs/ui` (internal UI).

Returns:
{
"data": List[LiteLLM_SpendLogs], # Paginated spend logs
"total": int, # Total number of records
"page": int, # Current page number
"page_size": int, # Number of items per page
"total_pages": int # Total number of pages
}
Returns paginated response with data, total, page, page_size, and total_pages.

Example:
```
curl -X GET "http://0.0.0.0:8000/spend/logs/v2?start_date=2025-11-25%2000:00:00&end_date=2025-11-26%2023:59:59&page=1&page_size=50" \
-H "Authorization: Bearer sk-1234"
```
"""
from litellm.proxy.proxy_server import prisma_client

Expand All @@ -1702,13 +1712,24 @@ async def ui_view_spend_logs( # noqa: PLR0915
)

try:
# Convert the date strings to datetime objects
start_date_obj = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S").replace(
tzinfo=timezone.utc
)
end_date_obj = datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S").replace(
tzinfo=timezone.utc
)
is_v2 = "/spend/logs/v2" in request.url.path
formats = ["%Y-%m-%d %H:%M:%S", "%Y-%m-%d"] if is_v2 else ["%Y-%m-%d %H:%M:%S"]

def parse_date(date_str: str) -> datetime:
date_str = date_str.strip()
for fmt in formats:
try:
return datetime.strptime(date_str, fmt).replace(tzinfo=timezone.utc)
except ValueError:
continue
expected = "'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'" if is_v2 else "'YYYY-MM-DD HH:MM:SS'"
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Invalid date format: {date_str}. Expected: {expected}",
)

start_date_obj = parse_date(start_date)
end_date_obj = parse_date(end_date)

# Convert to ISO format strings for Prisma
start_date_iso = start_date_obj.isoformat() # Already in UTC, no need to add Z
Expand Down Expand Up @@ -1896,6 +1917,9 @@ async def view_spend_logs( # noqa: PLR0915
user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
):
"""
[DEPRECATED] This endpoint is not paginated and can cause performance issues.
Please use `/spend/logs/v2` instead for paginated access to spend logs.

View all spend logs, if request_id is provided, only logs for that request_id will be returned

When start_date and end_date are provided:
Expand Down
Loading