From df8e815d8c4e164a80f9e3a6191da712fb2b374c Mon Sep 17 00:00:00 2001 From: willcai1984 Date: Thu, 30 Jul 2026 15:01:55 +0800 Subject: [PATCH] fix(health): use provider default mode for checks --- .../health_check_helpers.py | 4 +- litellm/llms/base_llm/chat/transformation.py | 3 + litellm/llms/chatgpt/chat/transformation.py | 3 + litellm/main.py | 14 ++-- .../test_health_check_helpers.py | 64 ++++++++++++++++++- 5 files changed, 81 insertions(+), 7 deletions(-) diff --git a/litellm/litellm_core_utils/health_check_helpers.py b/litellm/litellm_core_utils/health_check_helpers.py index 69b12fea21b..2c2980aac09 100644 --- a/litellm/litellm_core_utils/health_check_helpers.py +++ b/litellm/litellm_core_utils/health_check_helpers.py @@ -283,7 +283,9 @@ class HealthCheckHelpers: ), "responses": lambda: litellm.aresponses( **_filter_model_params(model_params=model_params), - input=[{"role": "user", "content": prompt or "test"}], + input=[ + {"role": "user", "content": prompt or "test"}, + ], ), "anthropic_messages": lambda: litellm.anthropic_messages( **{ diff --git a/litellm/llms/base_llm/chat/transformation.py b/litellm/llms/base_llm/chat/transformation.py index 8275829765b..a9bf1402bdf 100644 --- a/litellm/llms/base_llm/chat/transformation.py +++ b/litellm/llms/base_llm/chat/transformation.py @@ -89,6 +89,9 @@ class BaseConfig(ABC): def __init__(self): pass + def get_health_check_mode(self) -> str | None: + return None + @classmethod def get_config(cls): return { diff --git a/litellm/llms/chatgpt/chat/transformation.py b/litellm/llms/chatgpt/chat/transformation.py index 7c985b7dd5e..17f9a1519f4 100644 --- a/litellm/llms/chatgpt/chat/transformation.py +++ b/litellm/llms/chatgpt/chat/transformation.py @@ -26,6 +26,9 @@ class ChatGPTConfig(OpenAIConfig): def api_base_without_login(self) -> str: return self.authenticator.get_api_base() + def get_health_check_mode(self) -> str: + return "responses" + def _get_openai_compatible_provider_info( self, model: str, diff --git a/litellm/main.py b/litellm/main.py index 8ec1eb9403a..a2952f69638 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -8810,12 +8810,16 @@ async def ahealth_check( api_base=api_base_from_params, api_key=api_key_from_params, ) - model_params["cache"] = {"no-cache": True} # don't used cached responses for making health check calls - # chatgpt provider uses the Responses API exclusively; default to "responses" so the - # health check doesn't send a Chat Completions payload that the backend rejects. - if mode is None and custom_llm_provider == "chatgpt": - mode = "responses" + if mode is None and custom_llm_provider in {provider.value for provider in LlmProviders}: + provider_config: Final = ProviderConfigManager.get_provider_chat_config( + model=model, + provider=LlmProviders(custom_llm_provider), + ) + if provider_config is not None: + mode = ( # rebind-ok: resolve provider-config default + provider_config.get_health_check_mode() + ) mode = mode or default_health_check_mode( requested_model=requested_model, model=model, custom_llm_provider=custom_llm_provider ) diff --git a/tests/unit/litellm_core_utils/test_health_check_helpers.py b/tests/unit/litellm_core_utils/test_health_check_helpers.py index ffe50c137d2..8fbbf0b2601 100644 --- a/tests/unit/litellm_core_utils/test_health_check_helpers.py +++ b/tests/unit/litellm_core_utils/test_health_check_helpers.py @@ -151,6 +151,68 @@ async def test_ahealth_check_supports_image_edit_mode(): assert "error" not in result assert "Mode image_edit not supported" not in str(result) +@pytest.mark.asyncio +async def test_ahealth_check_uses_provider_mode_and_responses_message_input(): + mock_response = MagicMock() + mock_response._hidden_params = {} + mock_aresponses = AsyncMock(return_value=mock_response) + mock_acompletion = AsyncMock() + + with ( + patch( # test-quality-ok: isolate provider and handler calls for focused health-check routing + "litellm.main.get_llm_provider", + return_value=("gpt-5.6-sol", "chatgpt", None, None), + ), + patch.object( # test-quality-ok: isolate health-check metadata plumbing for routing assertion + HealthCheckHelpers, + "_update_model_params_with_health_check_tracking_information", + side_effect=lambda model_params: model_params, + ), + patch("litellm.aresponses", new=mock_aresponses), # test-quality-ok: verify provider-mode dispatch + patch("litellm.acompletion", new=mock_acompletion), # test-quality-ok: verify chat fallback dispatch + ): + result = await ahealth_check( + model_params={"model": "chatgpt/gpt-5.6-sol"}, + mode=None, + prompt="health check", + ) + + assert "error" not in result + mock_aresponses.assert_awaited_once() + assert mock_aresponses.await_args.kwargs["input"] == [ + {"role": "user", "content": "health check"} + ] + mock_acompletion.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_ahealth_check_provider_without_default_mode_keeps_chat(): + mock_response = MagicMock() + mock_response._hidden_params = {} + mock_aresponses = AsyncMock() + mock_acompletion = AsyncMock(return_value=mock_response) + + with ( + patch( # test-quality-ok: isolate provider and handler calls for focused health-check routing + "litellm.main.get_llm_provider", + return_value=("custom-model", "custom", None, None), + ), + patch.object( # test-quality-ok: isolate health-check metadata plumbing for routing assertion + HealthCheckHelpers, + "_update_model_params_with_health_check_tracking_information", + side_effect=lambda model_params: model_params, + ), + patch("litellm.aresponses", new=mock_aresponses), # test-quality-ok: verify provider-mode dispatch + patch("litellm.acompletion", new=mock_acompletion), # test-quality-ok: verify chat fallback dispatch + ): + result = await ahealth_check( + model_params={"model": "custom/custom-model"}, + mode=None, + ) + + assert "error" not in result + mock_acompletion.assert_awaited_once() + mock_aresponses.assert_not_awaited() @pytest.mark.asyncio @@ -472,7 +534,7 @@ async def test_realtime_health_check_uses_model_level_vertex_params(): "websockets.connect", lambda url, **kwargs: _FakeWebsocketConnect(connect_calls, url, **kwargs), ), - patch.object( + patch.object( # test-quality-ok: isolate health-check metadata plumbing for routing assertion HealthCheckHelpers, "update_model_params_with_health_check_tracking_information", staticmethod(lambda model_params: model_params),