mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-08 08:08:37 +00:00
* feat(redis): route the router, rate limiters and auth through the native store families * style(redis): satisfy the python lint gates Format the rate limit store, sort its __all__ and give the routing read batch and two tests concrete list and dict types. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(router): give the registry's default usage store a memory-only handle RouterUsageStore now takes a handle, so the default passed None and any usage read before RustCore swapped in its store raised AttributeError. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(redis): build stores from native handles and wait for router tasks Tests still used the pre-refactor store constructors (Store(None), handle= factories, NativeIncrement, tuple scopes). The router outcome tests counted 20 loop ticks, too few now that the allowed-fails increment resolves on the native runtime, so settle() waits for the scheduled tasks instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
44 lines
2 KiB
Python
44 lines
2 KiB
Python
import time
|
|
from typing import Final
|
|
|
|
import pytest
|
|
|
|
from litellm.redis.stores.connection import connect
|
|
from litellm.redis.stores.router_cooldowns import RouterCooldownStore
|
|
from litellm.router_utils.cooldown_cache import CooldownCache
|
|
|
|
@pytest.mark.parametrize("message", ("Short error", "Rate limit " + "secret" * 100))
|
|
@pytest.mark.parametrize("ttl", (None, 5.5, 30.0))
|
|
def test_cooldown_masks_exception_and_preserves_status_timestamp_and_ttl(message: str, ttl: float | None) -> None:
|
|
now: Final = time.time()
|
|
store: Final = RouterCooldownStore(connect(None).router_cooldowns())
|
|
cache: Final = CooldownCache(store, default_cooldown_time=60.0, clock=lambda: now)
|
|
cache.add_deployment_to_cooldown("dep", Exception(message), 429, ttl)
|
|
expected_ttl: Final = 60.0 if ttl is None else ttl
|
|
expected_message: Final = message if len(message) <= 50 else message[:50] + "*" * (len(message) - 50)
|
|
assert cache.get_active_cooldowns(["dep"], None) == [
|
|
(
|
|
"dep",
|
|
{
|
|
"exception_received": expected_message,
|
|
"status_code": "429",
|
|
"timestamp": now,
|
|
"cooldown_time": expected_ttl,
|
|
},
|
|
)
|
|
]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sync_and_async_router_cooldown_surfaces_share_active_state() -> None:
|
|
now: Final = time.time()
|
|
store: Final = RouterCooldownStore(connect(None).router_cooldowns())
|
|
cache: Final = CooldownCache(store, default_cooldown_time=60.0, clock=lambda: now)
|
|
cache.add_deployment_to_cooldown("dep", Exception("unavailable"), 503, 30.0)
|
|
expected: Final = [
|
|
("dep", {"exception_received": "unavailable", "status_code": "503", "timestamp": now, "cooldown_time": 30.0})
|
|
]
|
|
assert cache.get_active_cooldowns(["absent", "dep"], None) == expected
|
|
assert await cache.async_get_active_cooldowns(["absent", "dep"], None) == expected
|
|
assert cache.get_min_cooldown(["dep"], None) == 30.0
|
|
assert cache.get_min_cooldown(["absent"], None) == 60.0
|