From 5094645a82b39536fa2ec50f32275b90a56ed20a Mon Sep 17 00:00:00 2001 From: unknown <> Date: Wed, 1 Jul 2026 03:33:06 +0000 Subject: [PATCH 1/2] fix: clear all strategy routers in clear_cache to prevent complexity_router disappearing after model update clear_cache() only cleared auto_routers but not complexity_routers, quality_routers, or adaptive_routers. After a model update, the complexity_router entry was still present so init_complexity_router_deployment raised ValueError("already exists") on re-add; with ignore_invalid_deployments=True the error was silently swallowed and the model vanished from /v2/model/info. Fixes #31694 Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../model_management_endpoints.py | 5 +- .../test_model_management_endpoints.py | 62 ++++++++++++++++++- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/management_endpoints/model_management_endpoints.py b/litellm/proxy/management_endpoints/model_management_endpoints.py index afe71084a45..ab5e2697bb0 100644 --- a/litellm/proxy/management_endpoints/model_management_endpoints.py +++ b/litellm/proxy/management_endpoints/model_management_endpoints.py @@ -1715,8 +1715,11 @@ async def clear_cache(): for model_id in db_model_ids: llm_router.delete_deployment(id=model_id) - # Clear auto routers + # Clear all strategy routers so they are re-initialized from DB state llm_router.auto_routers.clear() + llm_router.complexity_routers.clear() + llm_router.quality_routers.clear() + llm_router.adaptive_routers.clear() # Reload only DB models await proxy_config.add_deployment(prisma_client=prisma_client, proxy_logging_obj=proxy_logging_obj) diff --git a/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py index 6a81b1b613b..a7a7f58ffd1 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py @@ -7,6 +7,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest from fastapi.testclient import TestClient +import litellm from litellm._uuid import uuid sys.path.insert( @@ -458,6 +459,12 @@ class TestClearCache: mock_router.delete_deployment = MagicMock(return_value=True) mock_router.auto_routers = MagicMock() mock_router.auto_routers.clear = MagicMock() + mock_router.complexity_routers = MagicMock() + mock_router.complexity_routers.clear = MagicMock() + mock_router.quality_routers = MagicMock() + mock_router.quality_routers.clear = MagicMock() + mock_router.adaptive_routers = MagicMock() + mock_router.adaptive_routers.clear = MagicMock() mock_config = MagicMock() mock_config.add_deployment = AsyncMock(return_value=True) @@ -479,8 +486,11 @@ class TestClearCache: mock_router.delete_deployment.assert_any_call(id="db-model-1") mock_router.delete_deployment.assert_any_call(id="db-model-2") - # Should have cleared auto routers + # Should have cleared all strategy routers mock_router.auto_routers.clear.assert_called_once() + mock_router.complexity_routers.clear.assert_called_once() + mock_router.quality_routers.clear.assert_called_once() + mock_router.adaptive_routers.clear.assert_called_once() # Should have called add_deployment to reload DB models mock_config.add_deployment.assert_called_once_with( @@ -488,6 +498,56 @@ class TestClearCache: ) + @pytest.mark.asyncio + async def test_clear_cache_complexity_router_survives_update(self): + """ + Regression test for #31694: after PATCH /model/{id}/update on an + auto_router/complexity_router model, the deployment must reappear + in the router. Before the fix, clear_cache only cleared auto_routers + but not complexity_routers, so init_complexity_router_deployment raised + ValueError("already exists") on re-add and the model silently vanished. + """ + router = litellm.Router( + model_list=[ + { + "model_name": "gpt-4o", + "litellm_params": {"model": "gpt-4o", "api_key": "fake"}, + "model_info": {"id": "dep-gpt4o"}, + }, + { + "model_name": "gpt-4o-mini", + "litellm_params": {"model": "gpt-4o-mini", "api_key": "fake"}, + "model_info": {"id": "dep-gpt4o-mini"}, + }, + ], + ignore_invalid_deployments=True, + ) + + complexity_dep = Deployment( + model_name="my-complexity-model", + litellm_params=LiteLLM_Params( + model="auto_router/complexity_router", + complexity_router_config={ + "tiers": {"SIMPLE": "gpt-4o-mini", "MODERATE": "gpt-4o", "COMPLEX": "gpt-4o"}, + }, + complexity_router_default_model="gpt-4o-mini", + ), + model_info={"id": "dep-complexity", "db_model": True}, + ) + router.add_deployment(deployment=complexity_dep) + + assert "my-complexity-model" in router.complexity_routers + assert router.get_deployment(model_id="dep-complexity") is not None + + router.delete_deployment(id="dep-complexity") + router.complexity_routers.clear() + + re_added = router.add_deployment(deployment=complexity_dep) + assert re_added is not None, "complexity_router deployment must be re-added after clearing" + assert "my-complexity-model" in router.complexity_routers + assert router.get_deployment(model_id="dep-complexity") is not None + + class TestUpdateModel: """ Tests for the update_model (POST /model/update) handler. From 75dd9ee4d22cc68896287d1b324abf334224f406 Mon Sep 17 00:00:00 2001 From: unknown <> Date: Wed, 1 Jul 2026 04:21:50 +0000 Subject: [PATCH 2/2] fix: drop adaptive_routers.clear() per Greptile review adaptive_routers uses `continue` (not raise ValueError) for duplicates, and its init is deferred to _finalize_adaptive_router_if_configured which is only called from set_model_list, not the upsert_deployment path used by clear_cache. Clearing it would lose routing preferences with no way to restore them. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../proxy/management_endpoints/model_management_endpoints.py | 2 -- .../management_endpoints/test_model_management_endpoints.py | 4 ---- 2 files changed, 6 deletions(-) diff --git a/litellm/proxy/management_endpoints/model_management_endpoints.py b/litellm/proxy/management_endpoints/model_management_endpoints.py index ab5e2697bb0..d6d7d9ea03a 100644 --- a/litellm/proxy/management_endpoints/model_management_endpoints.py +++ b/litellm/proxy/management_endpoints/model_management_endpoints.py @@ -1715,11 +1715,9 @@ async def clear_cache(): for model_id in db_model_ids: llm_router.delete_deployment(id=model_id) - # Clear all strategy routers so they are re-initialized from DB state llm_router.auto_routers.clear() llm_router.complexity_routers.clear() llm_router.quality_routers.clear() - llm_router.adaptive_routers.clear() # Reload only DB models await proxy_config.add_deployment(prisma_client=prisma_client, proxy_logging_obj=proxy_logging_obj) diff --git a/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py index a7a7f58ffd1..f915024503c 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py @@ -463,8 +463,6 @@ class TestClearCache: mock_router.complexity_routers.clear = MagicMock() mock_router.quality_routers = MagicMock() mock_router.quality_routers.clear = MagicMock() - mock_router.adaptive_routers = MagicMock() - mock_router.adaptive_routers.clear = MagicMock() mock_config = MagicMock() mock_config.add_deployment = AsyncMock(return_value=True) @@ -486,11 +484,9 @@ class TestClearCache: mock_router.delete_deployment.assert_any_call(id="db-model-1") mock_router.delete_deployment.assert_any_call(id="db-model-2") - # Should have cleared all strategy routers mock_router.auto_routers.clear.assert_called_once() mock_router.complexity_routers.clear.assert_called_once() mock_router.quality_routers.clear.assert_called_once() - mock_router.adaptive_routers.clear.assert_called_once() # Should have called add_deployment to reload DB models mock_config.add_deployment.assert_called_once_with(