From 6d74a2fc49608489a6c15ab241f0fb8b09e6a6c5 Mon Sep 17 00:00:00 2001 From: felipebridge Date: Wed, 27 May 2026 16:52:17 -0300 Subject: [PATCH] fix(proxy): handle NULL token fields in daily activity aggregation update_metrics and _record_to_spend_metrics perform arithmetic on token fields that PostgreSQL GROUPING SETS returns as NULL when a date only has failed requests, causing: TypeError: unsupported operand type(s) for +: 'NoneType' and 'NoneType' Guard every nullable numeric field with / in both functions. Fixes #28858 --- .../common_daily_activity.py | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/litellm/proxy/management_endpoints/common_daily_activity.py b/litellm/proxy/management_endpoints/common_daily_activity.py index d173cd745ba..191f187aab3 100644 --- a/litellm/proxy/management_endpoints/common_daily_activity.py +++ b/litellm/proxy/management_endpoints/common_daily_activity.py @@ -32,15 +32,17 @@ _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 + 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 @@ -696,16 +698,18 @@ _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.""" + 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, )