mirror of
https://github.com/tiennm99/litellm.git
synced 2026-06-18 07:33:58 +00:00
3c2c61e1e4
- Updated instances of DualCache to UserApiKeyCache across multiple files to enhance cache handling for user API keys. - Adjusted cache retrieval and storage methods to ensure proper serialization and deserialization of cached objects. - Introduced a new UserApiKeyCache class to streamline caching logic and improve type safety. - Updated relevant tests to reflect changes in caching behavior and ensure compatibility with the new cache implementation.
146 lines
5.8 KiB
Python
146 lines
5.8 KiB
Python
"""
|
|
Tests for the enable_redis_auth_cache litellm_settings flag.
|
|
|
|
Verifies that _init_cache attaches Redis to user_api_key_cache only when
|
|
the flag is explicitly set to True, and leaves it in-memory-only otherwise.
|
|
"""
|
|
|
|
from contextlib import contextmanager
|
|
import json
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
import litellm
|
|
import litellm.proxy.proxy_server as ps
|
|
from litellm.caching.caching import RedisCache
|
|
from litellm.caching.dual_cache import DualCache
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class _FakeRedisCache(RedisCache):
|
|
"""
|
|
Minimal RedisCache subclass that passes isinstance checks without
|
|
requiring a real Redis connection. __init__ is bypassed so no
|
|
network calls are made.
|
|
"""
|
|
|
|
def __init__(self): # noqa: super().__init__ skipped intentionally
|
|
self._store = {}
|
|
|
|
def set_cache(self, key, value, **kwargs): # type: ignore[override]
|
|
# Enforce Redis JSON-serializable payload contract.
|
|
self._store[key] = json.dumps(value)
|
|
return True
|
|
|
|
def get_cache(self, key, **kwargs): # type: ignore[override]
|
|
raw = self._store.get(key)
|
|
if raw is None:
|
|
return None
|
|
return json.loads(raw)
|
|
|
|
|
|
@contextmanager
|
|
def _patched_init_cache(litellm_settings: dict, cache_params: dict):
|
|
"""
|
|
Context manager that:
|
|
1. Replaces the module-level globals with fresh DualCache instances.
|
|
2. Patches ``litellm.Cache`` (locally imported inside _init_cache) so
|
|
it returns a fake cache whose ``.cache`` attribute is a
|
|
_FakeRedisCache (passes the isinstance guard in _init_cache).
|
|
3. Extracts enable_redis_auth_cache from litellm_settings and passes it
|
|
as the second argument to _init_cache (matching production behaviour).
|
|
4. Yields (user_api_key_cache, spend_counter_cache) after calling
|
|
_init_cache, then restores everything.
|
|
"""
|
|
fake_redis = _FakeRedisCache()
|
|
|
|
mock_litellm_cache = MagicMock()
|
|
mock_litellm_cache.cache = fake_redis
|
|
|
|
fresh_user_cache = DualCache()
|
|
fresh_spend_cache = DualCache()
|
|
|
|
enable_redis_auth_cache = litellm_settings.get("enable_redis_auth_cache", False)
|
|
|
|
with (
|
|
patch.object(ps, "user_api_key_cache", fresh_user_cache),
|
|
patch.object(ps, "spend_counter_cache", fresh_spend_cache),
|
|
patch.object(ps, "llm_router", None),
|
|
# Cache is locally imported inside _init_cache: patch it at source.
|
|
patch("litellm.Cache", return_value=mock_litellm_cache),
|
|
):
|
|
litellm.cache = None
|
|
ps.ProxyConfig()._init_cache(cache_params, enable_redis_auth_cache)
|
|
yield fresh_user_cache, fresh_spend_cache
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestRedisAuthCacheFlag:
|
|
def test_flag_true_attaches_redis_to_user_api_key_cache(self):
|
|
"""When enable_redis_auth_cache=True, user_api_key_cache.redis_cache must be set."""
|
|
with _patched_init_cache(
|
|
litellm_settings={"enable_redis_auth_cache": True},
|
|
cache_params={"type": "redis", "host": "localhost", "port": 6379},
|
|
) as (user_cache, _):
|
|
assert user_cache.redis_cache is not None, (
|
|
"Redis should be attached to user_api_key_cache when "
|
|
"enable_redis_auth_cache=True"
|
|
)
|
|
|
|
def test_flag_false_leaves_user_api_key_cache_in_memory_only(self):
|
|
"""When enable_redis_auth_cache=False, user_api_key_cache must stay in-memory."""
|
|
with _patched_init_cache(
|
|
litellm_settings={"enable_redis_auth_cache": False},
|
|
cache_params={"type": "redis", "host": "localhost", "port": 6379},
|
|
) as (user_cache, _):
|
|
assert user_cache.redis_cache is None, (
|
|
"user_api_key_cache must remain in-memory-only when "
|
|
"enable_redis_auth_cache=False"
|
|
)
|
|
|
|
def test_flag_absent_leaves_user_api_key_cache_in_memory_only(self):
|
|
"""When enable_redis_auth_cache is not set at all, default is in-memory-only."""
|
|
with _patched_init_cache(
|
|
litellm_settings={},
|
|
cache_params={"type": "redis", "host": "localhost", "port": 6379},
|
|
) as (user_cache, _):
|
|
assert user_cache.redis_cache is None, (
|
|
"user_api_key_cache must remain in-memory-only when "
|
|
"enable_redis_auth_cache is absent from litellm_settings"
|
|
)
|
|
|
|
def test_spend_counter_cache_always_gets_redis_regardless_of_flag(self):
|
|
"""spend_counter_cache must receive Redis regardless of the auth-cache flag."""
|
|
for flag_value in (True, False, None):
|
|
ls = (
|
|
{"enable_redis_auth_cache": flag_value}
|
|
if flag_value is not None
|
|
else {}
|
|
)
|
|
with _patched_init_cache(
|
|
litellm_settings=ls,
|
|
cache_params={"type": "redis", "host": "localhost", "port": 6379},
|
|
) as (_, spend_cache):
|
|
assert spend_cache.redis_cache is not None, (
|
|
f"spend_counter_cache must always get Redis "
|
|
f"(enable_redis_auth_cache={flag_value!r})"
|
|
)
|
|
|
|
def test_flag_false_spend_gets_redis_but_user_cache_does_not(self):
|
|
"""Explicit False: spend cache wired, auth cache left in-memory."""
|
|
with _patched_init_cache(
|
|
litellm_settings={"enable_redis_auth_cache": False},
|
|
cache_params={"type": "redis", "host": "localhost", "port": 6379},
|
|
) as (user_cache, spend_cache):
|
|
assert spend_cache.redis_cache is not None
|
|
assert user_cache.redis_cache is None
|