From bea22df6c27b30d060e6f7e239e94396c9f38ad7 Mon Sep 17 00:00:00 2001 From: yassin Date: Mon, 14 Sep 2026 19:59:10 +0000 Subject: [PATCH] fix(caching): keep generic add_cache failures at ERROR unless the backend is Redis Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/caching/caching.py | 13 ++++++++--- tests/test_litellm/caching/test_caching.py | 26 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/litellm/caching/caching.py b/litellm/caching/caching.py index d6dd2a073af..be82f5def1f 100644 --- a/litellm/caching/caching.py +++ b/litellm/caching/caching.py @@ -679,7 +679,14 @@ class Cache: cache_key, cached_data, kwargs = self._add_cache_logic(result=result, **kwargs) self.cache.set_cache(cache_key, cached_data, **kwargs) except Exception as e: - log_redis_failure(verbose_logger, logging.ERROR, "LiteLLM Cache: exception in add_cache", e) + self._log_add_cache_failure(e) + + def _log_add_cache_failure(self, exc: Exception) -> None: + message: Final = "LiteLLM Cache: exception in add_cache" + if isinstance(self.cache, RedisCache): + log_redis_failure(verbose_logger, logging.ERROR, message, exc) + return + verbose_logger.error("%s: %s", message, exc) async def async_add_cache(self, result, dynamic_cache_object: BaseCache | None = None, **kwargs): """ @@ -698,7 +705,7 @@ class Cache: else: await self.cache.async_set_cache(cache_key, cached_data, **kwargs) except Exception as e: - log_redis_failure(verbose_logger, logging.ERROR, "LiteLLM Cache: exception in add_cache", e) + self._log_add_cache_failure(e) def _convert_to_cached_embedding( self, @@ -877,7 +884,7 @@ class Cache: else: await self.cache.async_set_cache_pipeline(cache_list=cache_list, **kwargs) except Exception as e: - log_redis_failure(verbose_logger, logging.ERROR, "LiteLLM Cache: exception in add_cache", e) + self._log_add_cache_failure(e) def should_use_cache(self, **kwargs): """ diff --git a/tests/test_litellm/caching/test_caching.py b/tests/test_litellm/caching/test_caching.py index 4d0ec0fb677..c7ec8abc31e 100644 --- a/tests/test_litellm/caching/test_caching.py +++ b/tests/test_litellm/caching/test_caching.py @@ -1,9 +1,12 @@ import logging import re +from unittest.mock import MagicMock import pytest +import litellm.caching.redis_cache as redis_cache_module from litellm.caching.caching import Cache +from litellm.caching.redis_cache import RedisCache, _RedisTimeoutLogThrottle from litellm.types.caching import LiteLLMCacheType, SemanticCacheScope from litellm.types.utils import Embedding, EmbeddingResponse, Usage @@ -53,6 +56,29 @@ def test_cache_key_debug_log_does_not_include_prompt_material(caplog): assert any(cache_key in message for message in created_cache_key_logs) +@pytest.mark.parametrize( + ("backend", "expected_level"), + [ + pytest.param(MagicMock(spec=RedisCache), logging.DEBUG, id="redis_backend_is_throttled"), + pytest.param(MagicMock(), logging.ERROR, id="other_backend_logs_every_timeout"), + ], +) +def test_add_cache_timeout_only_joins_redis_throttle_for_redis_backends(backend, expected_level, caplog, monkeypatch): + throttle = _RedisTimeoutLogThrottle(interval=5.0, clock=MagicMock(return_value=1_000.0)) + assert throttle.admit() == 0 + monkeypatch.setattr(redis_cache_module, "_redis_timeout_log_throttle", throttle) + + cache = Cache(type=LiteLLMCacheType.LOCAL) + backend.set_cache.side_effect = TimeoutError("lit7520 backend timed out") + cache.cache = backend + + with caplog.at_level(logging.DEBUG, logger="LiteLLM"): + cache.add_cache("result", model="gpt-4.1-mini", messages=[{"role": "user", "content": "hi"}]) + + records = [r for r in caplog.records if "lit7520 backend timed out" in r.getMessage()] + assert [r.levelno for r in records] == [expected_level] + + def _embedding_response(prompt_tokens, num_items): return EmbeddingResponse( model="amazon.titan-embed-image-v1",