11#### SPEND MANAGEMENT #####
22import collections
3+ import json
34import os
45from datetime import datetime , timedelta , timezone
56from functools import lru_cache
67from typing import TYPE_CHECKING , Any , Dict , List , Literal , Optional
78
89import fastapi
9- from fastapi import APIRouter , Depends , HTTPException , status
10+ from fastapi import APIRouter , Depends , HTTPException , Request , status
1011
1112import litellm
1213from litellm ._logging import verbose_proxy_logger
@@ -1609,6 +1610,14 @@ async def calculate_spend(request: SpendCalculateRequest):
16091610 )
16101611
16111612
1613+ @router .get (
1614+ "/spend/logs/v2" ,
1615+ tags = ["Budget & Spend Tracking" ],
1616+ dependencies = [Depends (user_api_key_auth )],
1617+ responses = {
1618+ 200 : {"model" : Dict [str , Any ]},
1619+ },
1620+ )
16121621@router .get (
16131622 "/spend/logs/ui" ,
16141623 tags = ["Budget & Spend Tracking" ],
@@ -1619,6 +1628,7 @@ async def calculate_spend(request: SpendCalculateRequest):
16191628 },
16201629)
16211630async def ui_view_spend_logs ( # noqa: PLR0915
1631+ request : Request ,
16221632 api_key : Optional [str ] = fastapi .Query (
16231633 default = None ,
16241634 description = "Get spend logs based on api key" ,
@@ -1672,16 +1682,16 @@ async def ui_view_spend_logs( # noqa: PLR0915
16721682 ),
16731683):
16741684 """
1675- View spend logs for UI with pagination support
1685+ View spend logs with pagination support.
1686+ Available at both `/spend/logs/v2` (public API) and `/spend/logs/ui` (internal UI).
16761687
1677- Returns:
1678- {
1679- "data": List[LiteLLM_SpendLogs], # Paginated spend logs
1680- "total": int, # Total number of records
1681- "page": int, # Current page number
1682- "page_size": int, # Number of items per page
1683- "total_pages": int # Total number of pages
1684- }
1688+ Returns paginated response with data, total, page, page_size, and total_pages.
1689+
1690+ Example:
1691+ ```
1692+ 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" \
1693+ -H "Authorization: Bearer sk-1234"
1694+ ```
16851695 """
16861696 from litellm .proxy .proxy_server import prisma_client
16871697
@@ -1702,13 +1712,24 @@ async def ui_view_spend_logs( # noqa: PLR0915
17021712 )
17031713
17041714 try :
1705- # Convert the date strings to datetime objects
1706- start_date_obj = datetime .strptime (start_date , "%Y-%m-%d %H:%M:%S" ).replace (
1707- tzinfo = timezone .utc
1708- )
1709- end_date_obj = datetime .strptime (end_date , "%Y-%m-%d %H:%M:%S" ).replace (
1710- tzinfo = timezone .utc
1711- )
1715+ is_v2 = "/spend/logs/v2" in request .url .path
1716+ formats = ["%Y-%m-%d %H:%M:%S" , "%Y-%m-%d" ] if is_v2 else ["%Y-%m-%d %H:%M:%S" ]
1717+
1718+ def parse_date (date_str : str ) -> datetime :
1719+ date_str = date_str .strip ()
1720+ for fmt in formats :
1721+ try :
1722+ return datetime .strptime (date_str , fmt ).replace (tzinfo = timezone .utc )
1723+ except ValueError :
1724+ continue
1725+ expected = "'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'" if is_v2 else "'YYYY-MM-DD HH:MM:SS'"
1726+ raise HTTPException (
1727+ status_code = status .HTTP_400_BAD_REQUEST ,
1728+ detail = f"Invalid date format: { date_str } . Expected: { expected } " ,
1729+ )
1730+
1731+ start_date_obj = parse_date (start_date )
1732+ end_date_obj = parse_date (end_date )
17121733
17131734 # Convert to ISO format strings for Prisma
17141735 start_date_iso = start_date_obj .isoformat () # Already in UTC, no need to add Z
@@ -1896,6 +1917,9 @@ async def view_spend_logs( # noqa: PLR0915
18961917 user_api_key_dict : UserAPIKeyAuth = Depends (user_api_key_auth ),
18971918):
18981919 """
1920+ [DEPRECATED] This endpoint is not paginated and can cause performance issues.
1921+ Please use `/spend/logs/v2` instead for paginated access to spend logs.
1922+
18991923 View all spend logs, if request_id is provided, only logs for that request_id will be returned
19001924
19011925 When start_date and end_date are provided:
0 commit comments