mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-28 06:23:07 +00:00
Merge c71d51e72e into 8d6b8dab3d
This commit is contained in:
commit
f56ee5f2aa
3 changed files with 87 additions and 6 deletions
|
|
@ -324,7 +324,7 @@ SELECT
|
|||
model,
|
||||
SUM(spend) AS spend
|
||||
FROM "LiteLLM_SpendLogs"
|
||||
WHERE "startTime" >= ($1::timestamptz AT TIME ZONE 'UTC') AND "startTime" <= ($2::timestamptz AT TIME ZONE 'UTC')
|
||||
WHERE "startTime" >= ($1::timestamptz AT TIME ZONE 'UTC') AND "startTime" < ($2::timestamptz AT TIME ZONE 'UTC')
|
||||
{filter_sql}
|
||||
GROUP BY 1, 2, 3, 4
|
||||
ORDER BY 1
|
||||
|
|
@ -3584,14 +3584,14 @@ async def view_spend_logs(
|
|||
start_date_obj: Final = datetime.strptime(start_date, "%Y-%m-%d").replace(tzinfo=timezone.utc)
|
||||
end_date_obj: Final = datetime.strptime(end_date, "%Y-%m-%d").replace(tzinfo=timezone.utc)
|
||||
|
||||
# Convert to ISO format strings for Prisma
|
||||
# end_date is a calendar day, so the window ends at the next midnight (exclusive)
|
||||
start_date_iso: Final = start_date_obj.isoformat()
|
||||
end_date_iso: Final = end_date_obj.isoformat()
|
||||
end_date_iso: Final = (end_date_obj + timedelta(days=1)).isoformat()
|
||||
|
||||
filter_query: Final[dict[str, object]] = {
|
||||
"startTime": {
|
||||
"gte": start_date_iso, # Greater than or equal to Start Date
|
||||
"lte": end_date_iso, # Less than or equal to End Date
|
||||
"lt": end_date_iso, # Before the day after End Date
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
"""/spend/logs treats end_date as a whole calendar day (#40216)."""
|
||||
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
import pytest
|
||||
from fastapi import Response
|
||||
|
||||
import litellm.proxy.spend_tracking.spend_management_endpoints as spend_endpoints
|
||||
from litellm.proxy import proxy_server
|
||||
from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth
|
||||
from litellm.proxy.spend_tracking.spend_management_endpoints import (
|
||||
_spend_logs_daily_summary_sql,
|
||||
view_spend_logs,
|
||||
)
|
||||
|
||||
|
||||
def _admin() -> UserAPIKeyAuth:
|
||||
return UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN, api_key="sk-admin")
|
||||
|
||||
|
||||
def test_daily_summary_sql_uses_exclusive_upper_bound():
|
||||
sql, _ = _spend_logs_daily_summary_sql(
|
||||
start_date_iso="2026-09-07T00:00:00+00:00",
|
||||
end_date_iso="2026-09-08T00:00:00+00:00",
|
||||
api_key=None,
|
||||
request_id=None,
|
||||
user_id=None,
|
||||
)
|
||||
assert "\"startTime\" < ($2::timestamptz AT TIME ZONE 'UTC')" in sql
|
||||
assert "<= ($2" not in sql
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_summary_queries_through_end_of_end_date(monkeypatch):
|
||||
prisma = MagicMock()
|
||||
prisma.db.query_raw = AsyncMock(
|
||||
return_value=[{"day": "2026-09-08", "api_key": "k", "user": "u", "model": "m", "spend": 1.5}]
|
||||
)
|
||||
monkeypatch.setattr(proxy_server, "prisma_client", prisma)
|
||||
|
||||
result = await view_spend_logs(
|
||||
fastapi_response=Response(),
|
||||
api_key=None,
|
||||
user_id=None,
|
||||
request_id=None,
|
||||
start_date="2026-09-08",
|
||||
end_date="2026-09-08",
|
||||
summarize=True,
|
||||
user_api_key_dict=_admin(),
|
||||
)
|
||||
|
||||
params = prisma.db.query_raw.call_args[0][1:]
|
||||
assert params[0] == "2026-09-08T00:00:00+00:00"
|
||||
assert params[1] == "2026-09-09T00:00:00+00:00"
|
||||
# The real end-day row is returned and no synthetic zero row is appended.
|
||||
assert len(result) == 1
|
||||
assert result[0]["spend"] == 1.5
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_unsummarized_filter_includes_end_date(monkeypatch):
|
||||
find_spend_logs = AsyncMock(return_value=[])
|
||||
monkeypatch.setattr(proxy_server, "prisma_client", MagicMock())
|
||||
monkeypatch.setattr(spend_endpoints, "_find_spend_logs", find_spend_logs)
|
||||
|
||||
await view_spend_logs(
|
||||
fastapi_response=Response(),
|
||||
api_key=None,
|
||||
user_id=None,
|
||||
request_id=None,
|
||||
start_date="2026-09-07",
|
||||
end_date="2026-09-07",
|
||||
summarize=False,
|
||||
user_api_key_dict=_admin(),
|
||||
)
|
||||
|
||||
where = find_spend_logs.call_args.kwargs["where"]
|
||||
assert where["startTime"] == {
|
||||
"gte": "2026-09-07T00:00:00+00:00",
|
||||
"lt": "2026-09-08T00:00:00+00:00",
|
||||
}
|
||||
|
|
@ -4497,7 +4497,7 @@ async def test_view_spend_logs_summarize_groups_by_day_in_sql(client, monkeypatc
|
|||
assert not hasattr(mock_prisma_client.db, "group_by")
|
||||
assert mock_prisma_client.db.captured_params == (
|
||||
"2024-01-01T00:00:00+00:00",
|
||||
"2024-01-03T00:00:00+00:00",
|
||||
"2024-01-04T00:00:00+00:00",
|
||||
"hashed::sk-abc",
|
||||
"req-123",
|
||||
"u1",
|
||||
|
|
@ -4591,7 +4591,7 @@ async def test_view_spend_logs_summarize_unhashed_api_key_without_padding(client
|
|||
data = response.json()
|
||||
assert mock_prisma_client.db.captured_params == (
|
||||
"2024-01-01T00:00:00+00:00",
|
||||
"2024-01-01T00:00:00+00:00",
|
||||
"2024-01-02T00:00:00+00:00",
|
||||
"plain-key",
|
||||
)
|
||||
assert data == [
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue