From 88befe027b5c4efc834da5be8dff4de2eea25d72 Mon Sep 17 00:00:00 2001 From: HarshDubey23 <191850210+HarshDubey23@users.noreply.github.com> Date: Tue, 27 Oct 2026 20:48:03 +0530 Subject: [PATCH] fix(hosted_vllm): drop unneeded allowed_openai_params change, test extra_body merge The provider params reach the request through HostedVLLMResponsesAPIConfig alone, so the change to get_requested_response_api_optional_param is not needed for this fix. Also cover a request that sends its own extra_body: both reach vLLM and extra_body wins on a shared key. --- litellm/responses/utils.py | 14 ++-------- .../responses/test_hosted_vllm_responses.py | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/litellm/responses/utils.py b/litellm/responses/utils.py index 3fab03cc3b0..dc75e77d685 100644 --- a/litellm/responses/utils.py +++ b/litellm/responses/utils.py @@ -334,20 +334,10 @@ class ResponsesAPIRequestUtils: """ from litellm.utils import PreProcessNonDefaultParams - valid_keys: Final[frozenset[str]] = frozenset(get_type_hints(ResponsesAPIOptionalRequestParams).keys()) + valid_keys: Final = get_type_hints(ResponsesAPIOptionalRequestParams).keys() custom_llm_provider: Final = params.pop("custom_llm_provider", None) special_params: Final[dict[str, object]] = params.pop("kwargs", {}) - allowed_openai_params: Final[object] = params.get("allowed_openai_params") or special_params.get( - "allowed_openai_params" - ) - allowed_set: Final[frozenset[str]] = ( - frozenset(p for p in allowed_openai_params if isinstance(p, str)) # pyright: ignore[reportUnknownVariableType] # element type is dynamic - if isinstance(allowed_openai_params, (list, tuple, set, frozenset)) - else frozenset() - ) - default_keys: Final[frozenset[str]] = valid_keys | allowed_set - additional_drop_params: Final[list[str] | None] = params.pop("additional_drop_params", None) non_default_params: Final = _apply_nested_drop_params( PreProcessNonDefaultParams.base_pre_process_non_default_params( @@ -355,7 +345,7 @@ class ResponsesAPIRequestUtils: special_params=special_params, custom_llm_provider=custom_llm_provider, additional_drop_params=additional_drop_params, - default_param_values={k: None for k in default_keys}, + default_param_values={k: None for k in valid_keys}, additional_endpoint_specific_params=["input"], ), additional_drop_params, diff --git a/tests/unit/llms/hosted_vllm/responses/test_hosted_vllm_responses.py b/tests/unit/llms/hosted_vllm/responses/test_hosted_vllm_responses.py index 2f0302004de..a83ad492bdd 100644 --- a/tests/unit/llms/hosted_vllm/responses/test_hosted_vllm_responses.py +++ b/tests/unit/llms/hosted_vllm/responses/test_hosted_vllm_responses.py @@ -272,3 +272,30 @@ def test_hosted_vllm_responses_transform_request_from_optional_request_params(): assert request["chat_template_kwargs"] == {"enable_thinking": False} assert request["top_k"] == 30 + + +def test_hosted_vllm_responses_provider_params_merge_with_request_extra_body(): + """ + A request-level extra_body must not drop the forwarded provider params: + unrelated keys from both reach vLLM, and extra_body wins on a shared key. + """ + mock_client = _make_mock_http_client(_make_mock_responses_api_response("Test output")) + + with patch( + "litellm.llms.custom_httpx.llm_http_handler.get_httpx_client", + return_value=mock_client, + ): + litellm.responses( + model="hosted_vllm/Qwen/Qwen3-8B", + input="hi", + api_base="https://test-vllm.example.com/v1", + api_key="test-key", + chat_template_kwargs={"enable_thinking": False}, + top_k=20, + extra_body={"min_p": 0.1, "top_k": 5}, + ) + + call_json = mock_client.post.call_args[1]["json"] + assert call_json["chat_template_kwargs"] == {"enable_thinking": False} + assert call_json["min_p"] == 0.1 + assert call_json["top_k"] == 5