diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index b7f11ec270..db0bb735ba 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -871,6 +871,12 @@ async def proxy_startup_event(app: FastAPI): # noqa: PLR0915 redis_usage_cache=redis_usage_cache, ) + ## Validate use_redis_transaction_buffer requires Redis cache ## + ProxyStartupEvent._validate_redis_transaction_buffer_config( + general_settings=general_settings, + redis_usage_cache=redis_usage_cache, + ) + ## SEMANTIC TOOL FILTER ## # Read litellm_settings from config for semantic filter initialization try: @@ -5618,6 +5624,35 @@ class ProxyStartupEvent: llm_router=llm_router, redis_usage_cache=redis_usage_cache ) + @staticmethod + def _validate_redis_transaction_buffer_config( + general_settings: dict, + redis_usage_cache: Optional[RedisCache], + ): + """ + Validates that when use_redis_transaction_buffer is enabled, + a Redis cache is properly configured in litellm_settings. + """ + from litellm.secret_managers.main import str_to_bool + + _use_redis_transaction_buffer: Optional[Union[bool, str]] = ( + general_settings.get("use_redis_transaction_buffer", False) + ) + if isinstance(_use_redis_transaction_buffer, str): + _use_redis_transaction_buffer = str_to_bool(_use_redis_transaction_buffer) + + if _use_redis_transaction_buffer and redis_usage_cache is None: + raise ValueError( + "`use_redis_transaction_buffer` is enabled in general_settings " + "but no Redis cache is configured. This will cause spend updates " + "to not be tracked. Add a Redis cache in litellm_settings:\n\n" + "litellm_settings:\n" + " cache: true\n" + " cache_params:\n" + " type: redis\n" + " url: os.environ/REDIS_URL\n" + ) + @classmethod async def _initialize_semantic_tool_filter( cls, diff --git a/tests/test_litellm/proxy/db/db_transaction_queue/test_redis_update_buffer.py b/tests/test_litellm/proxy/db/db_transaction_queue/test_redis_update_buffer.py index 2a380370c3..78e07c2967 100644 --- a/tests/test_litellm/proxy/db/db_transaction_queue/test_redis_update_buffer.py +++ b/tests/test_litellm/proxy/db/db_transaction_queue/test_redis_update_buffer.py @@ -1,7 +1,7 @@ import json import os import sys -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import AsyncMock, MagicMock import pytest @@ -10,6 +10,7 @@ sys.path.insert( ) # Adds the parent directory to the system path from litellm.proxy.db.db_transaction_queue.redis_update_buffer import RedisUpdateBuffer +from litellm.proxy.proxy_server import ProxyStartupEvent from litellm.types.caching import RedisPipelineRpushOperation @@ -192,3 +193,39 @@ async def test_get_all_transactions_from_redis_buffer_pipeline_no_redis(): buffer = RedisUpdateBuffer(redis_cache=None) result = await buffer.get_all_transactions_from_redis_buffer_pipeline() assert result == (None, None, None, None, None, None, None) + + +def test_validate_redis_transaction_buffer_raises_without_redis(): + """ + When use_redis_transaction_buffer=true but no Redis cache is configured, + the proxy should refuse to start with a clear error message. + """ + with pytest.raises(ValueError, match="use_redis_transaction_buffer"): + ProxyStartupEvent._validate_redis_transaction_buffer_config( + general_settings={"use_redis_transaction_buffer": True}, + redis_usage_cache=None, + ) + + +def test_validate_redis_transaction_buffer_passes_with_redis(): + """ + When use_redis_transaction_buffer=true and Redis cache is configured, + validation should pass without error. + """ + # Should not raise + ProxyStartupEvent._validate_redis_transaction_buffer_config( + general_settings={"use_redis_transaction_buffer": True}, + redis_usage_cache=MagicMock(), + ) + + +def test_validate_redis_transaction_buffer_passes_when_disabled(): + """ + When use_redis_transaction_buffer is not set or false, + validation should pass regardless of Redis configuration. + """ + # Should not raise even without Redis + ProxyStartupEvent._validate_redis_transaction_buffer_config( + general_settings={}, + redis_usage_cache=None, + )