mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
Merge fba12c38d4 into eb0e3f8c18
This commit is contained in:
commit
dc245e66f1
2 changed files with 120 additions and 16 deletions
|
|
@ -111,21 +111,35 @@ class DualCache(BaseCache):
|
|||
without it, backfilled entries fall to ``InMemoryCache``'s own default
|
||||
TTL and can outlive the TTL this cache was configured with.
|
||||
"""
|
||||
if "ttl" not in kwargs and self.default_in_memory_ttl is not None:
|
||||
return {**kwargs, "ttl": self.default_in_memory_ttl}
|
||||
return kwargs
|
||||
return self._write_kwargs(kwargs, self.default_in_memory_ttl)
|
||||
|
||||
@property
|
||||
def _redis_write_ttl(self) -> float | None:
|
||||
"""
|
||||
Default TTL for a Redis write.
|
||||
|
||||
``default_redis_ttl`` when set, else ``default_in_memory_ttl`` so callers that
|
||||
configure only the in-memory tier keep the Redis TTL they have always had.
|
||||
"""
|
||||
if self.default_redis_ttl is not None:
|
||||
return self.default_redis_ttl
|
||||
return self.default_in_memory_ttl
|
||||
|
||||
@staticmethod
|
||||
def _write_kwargs(kwargs: "dict[str, object]", default_ttl: float | None) -> "dict[str, object]":
|
||||
"""Kwargs for one tier's write: apply ``default_ttl`` unless the caller passed an explicit ``ttl``."""
|
||||
if "ttl" in kwargs or default_ttl is None:
|
||||
return kwargs
|
||||
return {**kwargs, "ttl": default_ttl}
|
||||
|
||||
def set_cache(self, key, value, local_only: bool = False, **kwargs):
|
||||
# Update both Redis and in-memory cache
|
||||
try:
|
||||
if self.in_memory_cache is not None:
|
||||
if "ttl" not in kwargs and self.default_in_memory_ttl is not None:
|
||||
kwargs["ttl"] = self.default_in_memory_ttl
|
||||
|
||||
self.in_memory_cache.set_cache(key, value, **kwargs)
|
||||
self.in_memory_cache.set_cache(key, value, **self._write_kwargs(kwargs, self.default_in_memory_ttl))
|
||||
|
||||
if self.redis_cache is not None and local_only is False:
|
||||
self.redis_cache.set_cache(key, value, **kwargs)
|
||||
self.redis_cache.set_cache(key, value, **self._write_kwargs(kwargs, self._redis_write_ttl))
|
||||
except Exception as e:
|
||||
print_verbose(e)
|
||||
|
||||
|
|
@ -340,12 +354,12 @@ class DualCache(BaseCache):
|
|||
print_verbose(f"async set cache: cache key: {key}; local_only: {local_only}; value: {value}")
|
||||
try:
|
||||
if self.in_memory_cache is not None:
|
||||
if "ttl" not in kwargs and self.default_in_memory_ttl is not None:
|
||||
kwargs["ttl"] = self.default_in_memory_ttl
|
||||
await self.in_memory_cache.async_set_cache(key, value, **kwargs)
|
||||
await self.in_memory_cache.async_set_cache(
|
||||
key, value, **self._write_kwargs(kwargs, self.default_in_memory_ttl)
|
||||
)
|
||||
|
||||
if self.redis_cache is not None and local_only is False:
|
||||
await self.redis_cache.async_set_cache(key, value, **kwargs)
|
||||
await self.redis_cache.async_set_cache(key, value, **self._write_kwargs(kwargs, self._redis_write_ttl))
|
||||
except Exception as e:
|
||||
verbose_logger.exception("LiteLLM Cache: Excepton async add_cache: %s", e)
|
||||
|
||||
|
|
@ -357,13 +371,13 @@ class DualCache(BaseCache):
|
|||
print_verbose(f"async batch set cache: cache keys: {cache_list}; local_only: {local_only}")
|
||||
try:
|
||||
if self.in_memory_cache is not None:
|
||||
if "ttl" not in kwargs and self.default_in_memory_ttl is not None:
|
||||
kwargs["ttl"] = self.default_in_memory_ttl
|
||||
await self.in_memory_cache.async_set_cache_pipeline(cache_list=cache_list, **kwargs)
|
||||
await self.in_memory_cache.async_set_cache_pipeline(
|
||||
cache_list=cache_list, **self._write_kwargs(kwargs, self.default_in_memory_ttl)
|
||||
)
|
||||
|
||||
if self.redis_cache is not None and local_only is False:
|
||||
await self.redis_cache.async_set_cache_pipeline(
|
||||
cache_list=cache_list, ttl=kwargs.pop("ttl", None), **kwargs
|
||||
cache_list=cache_list, **self._write_kwargs(kwargs, self._redis_write_ttl)
|
||||
)
|
||||
except Exception as e:
|
||||
verbose_logger.exception("LiteLLM Cache: Excepton async add_cache: %s", e)
|
||||
|
|
|
|||
|
|
@ -445,3 +445,93 @@ async def test_dual_cache_late_attach_redis_wires_writes_and_ttl_async():
|
|||
assert mock_redis.async_set_cache.call_args[0][:2] == (key_after, val_after)
|
||||
|
||||
assert in_memory.get_cache(key_after) == val_after
|
||||
|
||||
|
||||
def _redis_ttl(mock_call):
|
||||
return mock_call.kwargs.get("ttl")
|
||||
|
||||
|
||||
def test_default_redis_ttl_applies_to_sync_redis_write():
|
||||
mock_redis = MagicMock(spec=RedisCache)
|
||||
dual_cache = DualCache(
|
||||
in_memory_cache=InMemoryCache(), redis_cache=mock_redis, default_redis_ttl=600.0
|
||||
)
|
||||
|
||||
dual_cache.set_cache("key", "value")
|
||||
|
||||
assert _redis_ttl(mock_redis.set_cache.call_args) == 600.0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_default_redis_ttl_applies_to_async_redis_writes():
|
||||
mock_redis = MagicMock(spec=RedisCache)
|
||||
mock_redis.async_set_cache = AsyncMock()
|
||||
mock_redis.async_set_cache_pipeline = AsyncMock()
|
||||
dual_cache = DualCache(
|
||||
in_memory_cache=InMemoryCache(), redis_cache=mock_redis, default_redis_ttl=600.0
|
||||
)
|
||||
|
||||
await dual_cache.async_set_cache("key", "value")
|
||||
await dual_cache.async_set_cache_pipeline([("key", "value")])
|
||||
|
||||
assert _redis_ttl(mock_redis.async_set_cache.call_args) == 600.0
|
||||
assert _redis_ttl(mock_redis.async_set_cache_pipeline.call_args) == 600.0
|
||||
|
||||
|
||||
def test_update_cache_ttl_redis_only_applies_to_redis_write():
|
||||
mock_redis = MagicMock(spec=RedisCache)
|
||||
dual_cache = DualCache(in_memory_cache=InMemoryCache(), redis_cache=mock_redis)
|
||||
|
||||
dual_cache.update_cache_ttl(default_in_memory_ttl=None, default_redis_ttl=600.0)
|
||||
dual_cache.set_cache("key", "value")
|
||||
|
||||
assert _redis_ttl(mock_redis.set_cache.call_args) == 600.0
|
||||
|
||||
|
||||
def test_attach_redis_cache_default_redis_ttl_applies_to_redis_write():
|
||||
mock_redis = MagicMock(spec=RedisCache)
|
||||
dual_cache = DualCache(in_memory_cache=InMemoryCache())
|
||||
|
||||
dual_cache.attach_redis_cache(mock_redis, default_redis_ttl=600.0)
|
||||
dual_cache.set_cache("key", "value")
|
||||
|
||||
assert _redis_ttl(mock_redis.set_cache.call_args) == 600.0
|
||||
|
||||
|
||||
def test_explicit_ttl_overrides_default_redis_ttl():
|
||||
mock_redis = MagicMock(spec=RedisCache)
|
||||
dual_cache = DualCache(
|
||||
in_memory_cache=InMemoryCache(),
|
||||
redis_cache=mock_redis,
|
||||
default_in_memory_ttl=300.0,
|
||||
default_redis_ttl=600.0,
|
||||
)
|
||||
|
||||
dual_cache.set_cache("key", "value", ttl=42)
|
||||
|
||||
assert _redis_ttl(mock_redis.set_cache.call_args) == 42
|
||||
|
||||
|
||||
def test_default_in_memory_ttl_still_propagates_to_redis_when_no_redis_ttl():
|
||||
mock_redis = MagicMock(spec=RedisCache)
|
||||
dual_cache = DualCache(
|
||||
in_memory_cache=InMemoryCache(), redis_cache=mock_redis, default_in_memory_ttl=5.0
|
||||
)
|
||||
|
||||
dual_cache.set_cache("key", "value")
|
||||
|
||||
assert _redis_ttl(mock_redis.set_cache.call_args) == 5.0
|
||||
|
||||
|
||||
def test_default_redis_ttl_does_not_leak_into_in_memory_write():
|
||||
mock_in_memory = MagicMock(spec=InMemoryCache)
|
||||
dual_cache = DualCache(
|
||||
in_memory_cache=mock_in_memory,
|
||||
redis_cache=MagicMock(spec=RedisCache),
|
||||
default_in_memory_ttl=300.0,
|
||||
default_redis_ttl=600.0,
|
||||
)
|
||||
|
||||
dual_cache.set_cache("key", "value")
|
||||
|
||||
assert _redis_ttl(mock_in_memory.set_cache.call_args) == 300.0
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue