fix(caching): honor s-maxage 0 on the sync cache read so TTS on this line skips cached audio

The sync Cache.get_cache read s-maxage with `or`, so 0 became infinity and
a request asking for no stale audio got the cached result anyway. The async
read already used nested defaults. On lts/1.108.x the TTS bridge's inner chat
call stays on sync completion, so speech hit this path. Main's open #34403
makes the same change for main's sync callers
This commit is contained in:
mateo-berri 2026-11-10 09:11:39 -08:00
parent e5410e3358
commit c2ba33609a
3 changed files with 22 additions and 1 deletions

View file

@ -662,7 +662,9 @@ class Cache:
cache_key = self.get_cache_key(**kwargs) cache_key = self.get_cache_key(**kwargs)
if cache_key is not None: if cache_key is not None:
cache_control_args: Final[DynamicCacheControl] = kwargs.get("cache", {}) cache_control_args: Final[DynamicCacheControl] = kwargs.get("cache", {})
max_age = cache_control_args.get("s-maxage") or cache_control_args.get("s-max-age") or float("inf") max_age: Final = cache_control_args.get(
"s-maxage", cache_control_args.get("s-max-age", float("inf"))
)
cache_lookup_kwargs: Final = self._get_safe_cache_lookup_kwargs(kwargs) cache_lookup_kwargs: Final = self._get_safe_cache_lookup_kwargs(kwargs)
if dynamic_cache_object is not None: if dynamic_cache_object is not None:
cached_result = dynamic_cache_object.get_cache(cache_key, **cache_lookup_kwargs) cached_result = dynamic_cache_object.get_cache(cache_key, **cache_lookup_kwargs)

View file

@ -1437,6 +1437,24 @@ async def test_caching_with_cache_controls(monkeypatch: pytest.MonkeyPatch) -> N
assert async_ttl_first.id == async_ttl_second.id assert async_ttl_first.id == async_ttl_second.id
@pytest.mark.parametrize("max_age_key", ["s-maxage", "s-max-age"])
def test_sync_completion_with_zero_s_maxage_skips_the_cached_response(
monkeypatch: pytest.MonkeyPatch, max_age_key: str
) -> None:
monkeypatch.setattr(litellm, "cache", Cache(type=LiteLLMCacheType.LOCAL))
messages: Final = [{"role": "user", "content": f"zero s-maxage {uuid.uuid4().hex}"}]
first: Final = completion(model="gpt-4o-mini", messages=messages, mock_response="first")
zero_max_age: Final = completion(
model="gpt-4o-mini", messages=messages, cache={max_age_key: 0}, mock_response="second"
)
open_max_age: Final = completion(
model="gpt-4o-mini", messages=messages, cache={max_age_key: 60}, mock_response="third"
)
assert zero_max_age.id != first.id
assert open_max_age.id == zero_max_age.id
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_embedding_caching_redis_ttl(monkeypatch: pytest.MonkeyPatch, redis_client_without_network: None) -> None: async def test_embedding_caching_redis_ttl(monkeypatch: pytest.MonkeyPatch, redis_client_without_network: None) -> None:
mock_pipeline: Final = AsyncMock() mock_pipeline: Final = AsyncMock()

View file

@ -4874,6 +4874,7 @@ async def test_speech_gemini_tts_behind_the_router_waits_as_long_as_the_same_cha
[ [
({}, {}, 1), ({}, {}, 1),
({}, {"cache": {"no-cache": True}}, 2), ({}, {"cache": {"no-cache": True}}, 2),
({}, {"cache": {"s-maxage": 0}}, 2),
({"cache": {"no-store": True}}, {}, 2), ({"cache": {"no-store": True}}, {}, 2),
({}, {"caching": False}, 2), ({}, {"caching": False}, 2),
({}, {"caching": "yes"}, 2), ({}, {"caching": "yes"}, 2),