fix(proxy): coalesce NULL rollup metrics in aggregated daily-activity (#30151)
This commit is contained in:
parent
a2c916fb45
commit
8e12d42ea7
@ -699,17 +699,23 @@ _GROUP_DATE_ENDPOINT_API_KEY = 30 # 0b0011110
|
||||
|
||||
|
||||
def _record_to_spend_metrics(record: Any) -> SpendMetrics:
|
||||
"""Build a SpendMetrics directly from one already-aggregated rollup row."""
|
||||
"""Build a SpendMetrics directly from one already-aggregated rollup row.
|
||||
|
||||
SUM() over zero rows is SQL NULL, so rollup rows (notably the grand-total
|
||||
row, which Postgres emits even on an empty match) can carry None values.
|
||||
"""
|
||||
prompt_tokens = record.prompt_tokens or 0
|
||||
completion_tokens = record.completion_tokens or 0
|
||||
return SpendMetrics(
|
||||
spend=record.spend,
|
||||
prompt_tokens=record.prompt_tokens,
|
||||
completion_tokens=record.completion_tokens,
|
||||
total_tokens=record.prompt_tokens + record.completion_tokens,
|
||||
cache_read_input_tokens=record.cache_read_input_tokens,
|
||||
cache_creation_input_tokens=record.cache_creation_input_tokens,
|
||||
api_requests=record.api_requests,
|
||||
successful_requests=record.successful_requests,
|
||||
failed_requests=record.failed_requests,
|
||||
spend=record.spend or 0.0,
|
||||
prompt_tokens=prompt_tokens,
|
||||
completion_tokens=completion_tokens,
|
||||
total_tokens=prompt_tokens + completion_tokens,
|
||||
cache_read_input_tokens=record.cache_read_input_tokens or 0,
|
||||
cache_creation_input_tokens=record.cache_creation_input_tokens or 0,
|
||||
api_requests=record.api_requests or 0,
|
||||
successful_requests=record.successful_requests or 0,
|
||||
failed_requests=record.failed_requests or 0,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -585,3 +585,61 @@ async def test_aggregated_activity_preserves_metadata_for_deleted_keys():
|
||||
assert key_data.metadata.key_alias == "toto-test-2"
|
||||
assert key_data.metadata.team_id == "69cd4b77-b095-4489-8c46-4f2f31d840a2"
|
||||
assert key_data.metrics.spend == 10.0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_daily_activity_aggregated_empty_result_set():
|
||||
"""Regression test for the empty-range 500.
|
||||
|
||||
When the date filter matches zero rows, Postgres still emits the
|
||||
grand-total () grouping-set row with every SUM column NULL. The
|
||||
endpoint must return an empty result set with zeroed totals, not
|
||||
crash on None + None.
|
||||
"""
|
||||
mock_prisma = MagicMock()
|
||||
mock_prisma.db = MagicMock()
|
||||
|
||||
mock_rows = [
|
||||
{
|
||||
"date": None,
|
||||
"api_key": None,
|
||||
"model": None,
|
||||
"model_group": None,
|
||||
"custom_llm_provider": None,
|
||||
"mcp_namespaced_tool_name": None,
|
||||
"endpoint": None,
|
||||
"group_level": 127,
|
||||
"spend": None,
|
||||
"prompt_tokens": None,
|
||||
"completion_tokens": None,
|
||||
"cache_read_input_tokens": None,
|
||||
"cache_creation_input_tokens": None,
|
||||
"api_requests": None,
|
||||
"successful_requests": None,
|
||||
"failed_requests": None,
|
||||
}
|
||||
]
|
||||
mock_prisma.db.query_raw = AsyncMock(return_value=mock_rows)
|
||||
|
||||
result = await get_daily_activity_aggregated(
|
||||
prisma_client=mock_prisma,
|
||||
table_name="litellm_dailyuserspend",
|
||||
entity_id_field="user_id",
|
||||
entity_id=None,
|
||||
entity_metadata_field=None,
|
||||
start_date="2026-06-16",
|
||||
end_date="2026-06-16",
|
||||
model=None,
|
||||
api_key=None,
|
||||
)
|
||||
|
||||
assert result.results == []
|
||||
assert result.metadata.total_spend == 0.0
|
||||
assert result.metadata.total_prompt_tokens == 0
|
||||
assert result.metadata.total_completion_tokens == 0
|
||||
assert result.metadata.total_tokens == 0
|
||||
assert result.metadata.total_api_requests == 0
|
||||
assert result.metadata.total_successful_requests == 0
|
||||
assert result.metadata.total_failed_requests == 0
|
||||
assert result.metadata.total_cache_read_input_tokens == 0
|
||||
assert result.metadata.total_cache_creation_input_tokens == 0
|
||||
|
||||
Loading…
Reference in New Issue
Block a user