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:
Moe Khalil 2026-09-02 01:10:31 +00:00
parent e3a61c82da
commit 2d4301589c
2 changed files with 22 additions and 8 deletions

View file

@ -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,

View file

@ -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()