From d93d5a466bc8c0f60f93be2c86732ccd117ed192 Mon Sep 17 00:00:00 2001 From: mateo Date: Sat, 31 Oct 2026 22:16:24 +0000 Subject: [PATCH] fix(openai): keep web_search_options when a chat completions request bridges to the Responses API Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/main.py | 12 ++++++-- .../chat_completions/params/__init__.py | 0 ...ions_params_openai_responses_web_search.py | 28 +++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 tests/integration/translation/chat_completions/params/__init__.py create mode 100644 tests/integration/translation/chat_completions/params/test_chat_completions_params_openai_responses_web_search.py diff --git a/litellm/main.py b/litellm/main.py index 78767b03eb7..14a1e912ce8 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -6048,15 +6048,21 @@ def completion( bridges_to_responses_api: Final = ( responses_api_model_info.get("mode") == "responses" and not skip_responses_api_bridge ) - allowed_openai_params: Final[list[str] | None] = ( - [*(kwargs.get("allowed_openai_params") or []), "reasoning_effort"] + bridge_allowed_params: Final[tuple[str, ...]] = ( + ("reasoning_effort", "web_search_options") if bridges_to_responses_api - or reasoning_effort_needs_allowing( + else ("reasoning_effort",) + if reasoning_effort_needs_allowing( chat_reasoning.effort, model=model, custom_llm_provider=custom_llm_provider, base_model=base_model if isinstance(base_model, str) else None, ) + else () + ) + allowed_openai_params: Final[list[str] | None] = ( + [*(kwargs.get("allowed_openai_params") or []), *bridge_allowed_params] + if bridge_allowed_params else kwargs.get("allowed_openai_params") ) optional_param_args: Final = { diff --git a/tests/integration/translation/chat_completions/params/__init__.py b/tests/integration/translation/chat_completions/params/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/integration/translation/chat_completions/params/test_chat_completions_params_openai_responses_web_search.py b/tests/integration/translation/chat_completions/params/test_chat_completions_params_openai_responses_web_search.py new file mode 100644 index 00000000000..799b2a13d12 --- /dev/null +++ b/tests/integration/translation/chat_completions/params/test_chat_completions_params_openai_responses_web_search.py @@ -0,0 +1,28 @@ +from dataclasses import replace +from typing import Final + +import pytest +from integration._support.client import Gateway +from integration._support.provider import SharedProvider +from integration.translation.case import TranslationTestCase +from integration.translation.chat_completions.bases.openai_responses import GPT_5_6_SOL_TEST_CASE +from integration.translation.runner import assert_translation + +GPT_5_6_SOL_WEB_SEARCH_TEST_CASE: Final = replace( + GPT_5_6_SOL_TEST_CASE, + scenario="web_search_options", + litellm_request={**GPT_5_6_SOL_TEST_CASE.litellm_request, "web_search_options": {"search_context_size": "low"}}, + expected_provider_request={ + **GPT_5_6_SOL_TEST_CASE.expected_provider_request, + "tools": [{"type": "web_search", "search_context_size": "low"}], + }, +) + +CASES: Final = (GPT_5_6_SOL_WEB_SEARCH_TEST_CASE,) + + +@pytest.mark.parametrize("case", CASES, ids=lambda case: case.id) +def test_chat_completions_params_openai_responses_web_search( + case: TranslationTestCase, gateway: Gateway, provider: SharedProvider +) -> None: + assert_translation(case, gateway, provider)