diff --git a/litellm/llms/deepseek/chat/transformation.py b/litellm/llms/deepseek/chat/transformation.py index 06b86b2fa44..7ed3e484535 100644 --- a/litellm/llms/deepseek/chat/transformation.py +++ b/litellm/llms/deepseek/chat/transformation.py @@ -90,12 +90,15 @@ class DeepSeekChatConfig(OpenAIGPTConfig): cleaned.pop("reasoning_content", None) patched["provider_specific_fields"] = cleaned else: - litellm.verbose_logger.debug( + litellm.verbose_logger.warning( "DeepSeek thinking mode: assistant message is missing " - "`reasoning_content`. Injecting a placeholder to satisfy " - "API validation. For best results, preserve " - "`reasoning_content` from the original assistant response " - "when building multi-turn conversation history." + "`reasoning_content` and none was saved in " + "`provider_specific_fields`. A single-space placeholder " + "is being injected to satisfy API validation, but the " + "model will receive a blank reasoning chain for this turn, " + "which may silently degrade multi-turn response quality. " + "Preserve `reasoning_content` from the original assistant " + "response when building multi-turn conversation history." ) patched["reasoning_content"] = " " result.append(cast(AllMessageValues, patched)) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 059bd9f8125..5d89d3fa9c5 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -8090,6 +8090,11 @@ async def model_list( proxy_logging_obj=proxy_logging_obj, ) + # Compute once — used in both branches below to hide paused models from the listing. + blocked_names = ( + llm_router.get_fully_blocked_model_names() if llm_router is not None else set() + ) + # If scope=expand and user has admin privileges, return all proxy models if should_expand_scope: # Get all proxy models as if user is a proxy admin @@ -8123,10 +8128,8 @@ async def model_list( ) # Hide paused models from the public listing (admins manage them via /model/info) - if llm_router is not None: - blocked_names = llm_router.get_fully_blocked_model_names() - if blocked_names: - all_models = [m for m in all_models if m not in blocked_names] + if blocked_names: + all_models = [m for m in all_models if m not in blocked_names] # Build response data with all proxy models model_data = [] @@ -8162,10 +8165,8 @@ async def model_list( ) # Hide paused models from the public listing (admins manage them via /model/info) - if llm_router is not None: - blocked_names = llm_router.get_fully_blocked_model_names() - if blocked_names: - all_models = [m for m in all_models if m not in blocked_names] + if blocked_names: + all_models = [m for m in all_models if m not in blocked_names] # Build response data model_data = [] diff --git a/litellm/router.py b/litellm/router.py index 61534f32ba2..52e4e9bab85 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -10163,7 +10163,11 @@ class Router: if isinstance(healthy_deployments, dict): if (healthy_deployments.get("model_info") or {}).get("blocked") is True: - raise RouterRateLimitErrorBasic(model=model) + raise litellm.ServiceUnavailableError( + message=f"Model '{model}' is administratively paused. Contact your proxy admin to unblock it.", + model=model, + llm_provider="", + ) return healthy_deployments # Health-check-based filtering (before cooldown) @@ -10591,7 +10595,11 @@ class Router: if isinstance(healthy_deployments, dict): if (healthy_deployments.get("model_info") or {}).get("blocked") is True: - raise RouterRateLimitErrorBasic(model=model) + raise litellm.ServiceUnavailableError( + message=f"Model '{model}' is administratively paused. Contact your proxy admin to unblock it.", + model=model, + llm_provider="", + ) return healthy_deployments parent_otel_span: Optional[Span] = _get_parent_otel_span_from_kwargs( @@ -10744,7 +10752,11 @@ class Router: # 2. If the returned is a specific deployment (Dict), verify and return directly if isinstance(healthy_deployments, dict): if (healthy_deployments.get("model_info") or {}).get("blocked") is True: - raise RouterRateLimitErrorBasic(model=model) + raise litellm.ServiceUnavailableError( + message=f"Model '{model}' is administratively paused. Contact your proxy admin to unblock it.", + model=model, + llm_provider="", + ) litellm_params = healthy_deployments.get("litellm_params", {}) if litellm_params.get("use_in_pass_through"): return healthy_deployments diff --git a/tests/test_litellm/test_router.py b/tests/test_litellm/test_router.py index 02d24aded02..1757fae69a0 100644 --- a/tests/test_litellm/test_router.py +++ b/tests/test_litellm/test_router.py @@ -3788,10 +3788,10 @@ def test_public_get_available_deployment_skips_blocked_on_primary_path(): def test_get_available_deployment_raises_when_addressed_dict_is_blocked(): - from litellm.types.router import RouterRateLimitErrorBasic + import litellm router = _router_with_two_deployments([True, True]) - with pytest.raises(RouterRateLimitErrorBasic): + with pytest.raises(litellm.ServiceUnavailableError): router.get_available_deployment(model="dep-0", request_kwargs={}) @@ -3823,10 +3823,10 @@ def test_get_available_deployment_for_pass_through_skips_blocked(): def test_get_available_deployment_for_pass_through_raises_when_dict_blocked(): - from litellm.types.router import RouterRateLimitErrorBasic + import litellm router = _router_with_two_pass_through_deployments([True, True]) - with pytest.raises(RouterRateLimitErrorBasic): + with pytest.raises(litellm.ServiceUnavailableError): router.get_available_deployment_for_pass_through( model="pt-0", request_kwargs={} )