From 0af4f7f0e2b60313393e13a58c62219877d2b17a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 07:09:45 +0000 Subject: [PATCH] fix(router): strip silent_model from Responses and Anthropic Messages primary requests --- litellm/router.py | 1 + .../test_router_silent_experiment.py | 69 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/litellm/router.py b/litellm/router.py index 487d6a31226..40882688972 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -4499,6 +4499,7 @@ class Router: **kwargs, "model": model_name, } + response_kwargs.pop("silent_model", None) # Only set custom_llm_provider if it's not None if custom_llm_provider is not None: response_kwargs["custom_llm_provider"] = custom_llm_provider diff --git a/tests/test_litellm/test_router_silent_experiment.py b/tests/test_litellm/test_router_silent_experiment.py index bfdf39bad71..96053959be2 100644 --- a/tests/test_litellm/test_router_silent_experiment.py +++ b/tests/test_litellm/test_router_silent_experiment.py @@ -281,3 +281,72 @@ def test_router_silent_experiment_completion(): assert silent_call[1]["model"] == "openai/gpt-4" # Verify model_group is set to the silent model name for correct metric attribution assert silent_call[1]["metadata"]["model_group"] == "silent-model" + + +def _silent_model_router() -> Router: + return Router( + model_list=[ + { + "model_name": "primary-model", + "litellm_params": { + "model": "openai/primary-model", + "custom_llm_provider": "openai", + "api_key": "fake-key", + "silent_model": "shadow-model", + }, + }, + { + "model_name": "shadow-model", + "litellm_params": { + "model": "openai/shadow-model", + "custom_llm_provider": "openai", + "api_key": "fake-key", + }, + }, + ] + ) + + +@pytest.mark.asyncio +async def test_router_silent_model_stripped_from_responses_call(): + """ + Regression for #34890: a deployment configured with silent_model must not + leak that param into the Responses API primary request. Without the fix the + underlying handler receives silent_model and raises + "got an unexpected keyword argument 'silent_model'". + """ + router = _silent_model_router() + mock_aresponses = AsyncMock(return_value=MagicMock()) + router.aresponses = router.factory_function(mock_aresponses, call_type="aresponses") + + await router.aresponses(model="primary-model", input="Reply only OK") + + assert mock_aresponses.call_count == 1 + _, call_kwargs = mock_aresponses.call_args + assert "silent_model" not in call_kwargs + assert call_kwargs["model"] == "openai/primary-model" + + +@pytest.mark.asyncio +async def test_router_silent_model_stripped_from_anthropic_messages_call(): + """ + Regression for #34890: silent_model must not leak into the Anthropic + Messages primary request. PR #31901's regression test only covered + Responses, so this pins the /v1/messages path too. + """ + router = _silent_model_router() + mock_anthropic_messages = AsyncMock(return_value=MagicMock()) + router.aanthropic_messages = router.factory_function( + mock_anthropic_messages, call_type="anthropic_messages" + ) + + await router.aanthropic_messages( + model="primary-model", + messages=[{"role": "user", "content": "Reply only OK"}], + max_tokens=16, + ) + + assert mock_anthropic_messages.call_count == 1 + _, call_kwargs = mock_anthropic_messages.call_args + assert "silent_model" not in call_kwargs + assert call_kwargs["model"] == "openai/primary-model"