mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-08 22:21:35 +00:00
fix(router): keep serving when Claude Code session router cleanup fails
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
e3a61c82da
commit
2d4301589c
2 changed files with 22 additions and 8 deletions
|
|
@ -12578,15 +12578,11 @@ class Router:
|
|||
return None
|
||||
return f"claude_code_session_router:v1:{caller_scope}:{session_id}"
|
||||
|
||||
async def _delete_claude_code_session_router_binding(self, cache_key: str) -> None:
|
||||
async def _clear_claude_code_session_router(self, cache_key: str) -> None:
|
||||
try:
|
||||
await self.cache.async_delete_cache(key=cache_key)
|
||||
except Exception as e: # noqa: BLE001 # cache cleanup must not fail an otherwise routable request
|
||||
verbose_router_logger.warning(
|
||||
"Failed to delete Claude Code session router binding; "
|
||||
"the binding may remain until its TTL expires: %s",
|
||||
e,
|
||||
)
|
||||
verbose_router_logger.debug("Claude Code session router cleanup skipped for %s: %s", cache_key, e)
|
||||
|
||||
async def _resolve_claude_code_session_router(
|
||||
self,
|
||||
|
|
@ -12605,7 +12601,7 @@ class Router:
|
|||
return registered_model_name
|
||||
bound_registered_model: Final = self._get_model_from_alias(model=bound_model) or bound_model
|
||||
if self._select_pre_routing_strategy(bound_registered_model, request_kwargs) is None:
|
||||
await self._delete_claude_code_session_router_binding(cache_key)
|
||||
await self._clear_claude_code_session_router(cache_key)
|
||||
return registered_model_name
|
||||
await self.cache.async_set_cache(
|
||||
key=cache_key,
|
||||
|
|
@ -12620,7 +12616,7 @@ class Router:
|
|||
if request_kwargs.get("fallback_depth") not in (None, 0):
|
||||
return registered_model_name
|
||||
if self._select_pre_routing_strategy(registered_model_name, request_kwargs) is None:
|
||||
await self._delete_claude_code_session_router_binding(cache_key)
|
||||
await self._clear_claude_code_session_router(cache_key)
|
||||
return registered_model_name
|
||||
await self.cache.async_set_cache(
|
||||
key=cache_key,
|
||||
|
|
|
|||
|
|
@ -8428,6 +8428,24 @@ class TestClaudeCodeSubagentSessionRouterBinding:
|
|||
assert response is None
|
||||
redis_cache.async_delete_cache.assert_awaited_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_main_direct_model_still_served_when_cache_delete_fails(self):
|
||||
router = self._router()
|
||||
await router.acompletion(
|
||||
model="smart-router", messages=[{"role": "user", "content": "main turn"}], **self._request_kwargs()
|
||||
)
|
||||
|
||||
async def failing_delete(key: str) -> None:
|
||||
raise Exception("Redis circuit breaker is open — skipping async_delete_cache")
|
||||
|
||||
router.cache.async_delete_cache = failing_delete
|
||||
|
||||
response = await router.acompletion(
|
||||
model="expensive-model", messages=[{"role": "user", "content": "direct turn"}], **self._request_kwargs()
|
||||
)
|
||||
|
||||
assert response.choices[0].message.content == "expensive response"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_background_and_fallback_requests_do_not_clear_the_session_router(self):
|
||||
router = self._router()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue