diff --git a/litellm/proxy/caching_routes.py b/litellm/proxy/caching_routes.py index 470e4cb9ba3..247c31d207f 100644 --- a/litellm/proxy/caching_routes.py +++ b/litellm/proxy/caching_routes.py @@ -17,7 +17,7 @@ from litellm.types.caching import CachePingResponse, HealthCheckCacheParams @with_config(ConfigDict(extra="allow", strict=True)) class _CacheDeleteRequest(TypedDict): - keys: ReadOnly[NotRequired[list[str] | None]] + keys: ReadOnly[NotRequired[list[str | int | float] | None]] _CACHE_DELETE_REQUEST: Final = TypeAdapter(_CacheDeleteRequest) diff --git a/tests/unit/proxy/test_caching_routes.py b/tests/unit/proxy/test_caching_routes.py index 8a59793495a..ac3cfdaf8fa 100644 --- a/tests/unit/proxy/test_caching_routes.py +++ b/tests/unit/proxy/test_caching_routes.py @@ -52,6 +52,31 @@ def mock_redis_failure(mocker): return mock_cache +@pytest.fixture +def mock_redis_delete(mocker): + mock_cache = mocker.MagicMock() + mock_cache.type = "redis" + mock_cache.delete_cache_keys = mocker.AsyncMock() + mocker.patch.object(litellm, "cache", mock_cache) + return mock_cache + + +@pytest.mark.parametrize("keys", [["a-key", "b-key"], [12345, 1.5]]) +def test_cache_delete_passes_string_and_numeric_keys_to_the_cache(mock_redis_delete, keys): + response = client.post("/cache/delete", headers={"Authorization": "Bearer sk-9876"}, json={"keys": keys}) + + assert response.status_code == 200 + assert response.json() == {"status": "success"} + mock_redis_delete.delete_cache_keys.assert_awaited_once_with(keys=keys) + + +def test_cache_delete_rejects_keys_that_are_not_a_list(mock_redis_delete): + response = client.post("/cache/delete", headers={"Authorization": "Bearer sk-9876"}, json={"keys": "abc"}) + + assert response.status_code == 500 + mock_redis_delete.delete_cache_keys.assert_not_awaited() + + def test_cache_ping_success(mock_redis_success): """Test successful cache ping with regular response""" response = client.get("/cache/ping", headers={"Authorization": "Bearer sk-9876"})