mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
fix: coalesce null aggregates in update_metrics for no-spend keys (#29945)
This commit is contained in:
parent
8fbe3dc537
commit
a40009eaaf
2 changed files with 65 additions and 10 deletions
|
|
@ -35,16 +35,25 @@ _PRISMA_TO_PG_TABLE: Dict[str, str] = {
|
|||
|
||||
|
||||
def update_metrics(existing_metrics: SpendMetrics, record: Any) -> SpendMetrics:
|
||||
"""Update metrics with new record data."""
|
||||
existing_metrics.spend += record.spend
|
||||
existing_metrics.prompt_tokens += record.prompt_tokens
|
||||
existing_metrics.completion_tokens += record.completion_tokens
|
||||
existing_metrics.total_tokens += record.prompt_tokens + record.completion_tokens
|
||||
existing_metrics.cache_read_input_tokens += record.cache_read_input_tokens
|
||||
existing_metrics.cache_creation_input_tokens += record.cache_creation_input_tokens
|
||||
existing_metrics.api_requests += record.api_requests
|
||||
existing_metrics.successful_requests += record.successful_requests
|
||||
existing_metrics.failed_requests += record.failed_requests
|
||||
"""Update metrics with new record data.
|
||||
|
||||
Rollup rows can carry None for numeric fields when SUM() spans zero rows
|
||||
(e.g. a key with no spend), so coalesce to 0 before accumulating to avoid
|
||||
a TypeError. Mirrors the handling in ``_record_to_spend_metrics``.
|
||||
"""
|
||||
prompt_tokens = record.prompt_tokens or 0
|
||||
completion_tokens = record.completion_tokens or 0
|
||||
existing_metrics.spend += record.spend or 0.0
|
||||
existing_metrics.prompt_tokens += prompt_tokens
|
||||
existing_metrics.completion_tokens += completion_tokens
|
||||
existing_metrics.total_tokens += prompt_tokens + completion_tokens
|
||||
existing_metrics.cache_read_input_tokens += record.cache_read_input_tokens or 0
|
||||
existing_metrics.cache_creation_input_tokens += (
|
||||
record.cache_creation_input_tokens or 0
|
||||
)
|
||||
existing_metrics.api_requests += record.api_requests or 0
|
||||
existing_metrics.successful_requests += record.successful_requests or 0
|
||||
existing_metrics.failed_requests += record.failed_requests or 0
|
||||
return existing_metrics
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import os
|
||||
import sys
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
import pytest
|
||||
|
|
@ -12,10 +13,13 @@ from litellm.proxy.management_endpoints.common_daily_activity import (
|
|||
_adjust_dates_for_timezone,
|
||||
_build_aggregated_sql_query,
|
||||
_is_user_agent_tag,
|
||||
_record_to_spend_metrics,
|
||||
get_api_key_metadata,
|
||||
get_daily_activity,
|
||||
get_daily_activity_aggregated,
|
||||
update_metrics,
|
||||
)
|
||||
from litellm.types.proxy.management_endpoints.common_daily_activity import SpendMetrics
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -810,3 +814,45 @@ async def test_get_daily_activity_aggregated_empty_result_set():
|
|||
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
|
||||
|
||||
|
||||
def _no_spend_record():
|
||||
"""A rollup row for a key with no spend, where SUM() returns NULL (None)."""
|
||||
return SimpleNamespace(
|
||||
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,
|
||||
)
|
||||
|
||||
|
||||
def test_record_to_spend_metrics_handles_none_values():
|
||||
"""Keys with no spend produce NULL aggregates; treat them as zero, not a crash."""
|
||||
metrics = _record_to_spend_metrics(_no_spend_record())
|
||||
assert metrics.spend == 0
|
||||
assert metrics.prompt_tokens == 0
|
||||
assert metrics.completion_tokens == 0
|
||||
assert metrics.total_tokens == 0
|
||||
assert metrics.api_requests == 0
|
||||
assert metrics.successful_requests == 0
|
||||
assert metrics.failed_requests == 0
|
||||
assert metrics.cache_read_input_tokens == 0
|
||||
assert metrics.cache_creation_input_tokens == 0
|
||||
|
||||
|
||||
def test_update_metrics_handles_none_values():
|
||||
"""update_metrics should coalesce NULL aggregates instead of raising TypeError."""
|
||||
metrics = update_metrics(SpendMetrics(), _no_spend_record())
|
||||
assert metrics.spend == 0
|
||||
assert metrics.prompt_tokens == 0
|
||||
assert metrics.completion_tokens == 0
|
||||
assert metrics.total_tokens == 0
|
||||
assert metrics.api_requests == 0
|
||||
assert metrics.successful_requests == 0
|
||||
assert metrics.failed_requests == 0
|
||||
assert metrics.cache_read_input_tokens == 0
|
||||
assert metrics.cache_creation_input_tokens == 0
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue