feat(responses): add use_responses_api_bridge flag for openai/ models with custom api_base

Allows openai/-prefixed models with a custom api_base pointing to a
third-party OpenAI-compatible provider to opt-in to the
/responses → /chat/completions bridge, rather than forwarding requests
natively to /v1/responses (which may not be supported by the provider).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Sameer Kankute
2026-04-08 19:47:03 +05:30
co-authored by Claude Sonnet 4.6
parent 62757ff48f
commit f6b03a469e
3 changed files with 113 additions and 1 deletions
+3 -1
View File
@@ -754,6 +754,7 @@ def responses(
litellm_logging_obj: LiteLLMLoggingObj = kwargs.get("litellm_logging_obj") # type: ignore
litellm_call_id: Optional[str] = kwargs.get("litellm_call_id", None)
_is_async = kwargs.pop("aresponses", False) is True
use_responses_api_bridge = kwargs.pop("use_responses_api_bridge", None)
# Convert text_format to text parameter if provided
text = ResponsesAPIRequestUtils.convert_text_format_to_text_param(
@@ -871,6 +872,7 @@ def responses(
if _has_file_search_tool(tools) and (
responses_api_provider_config is None
or use_responses_api_bridge is True
or not responses_api_provider_config.supports_native_file_search()
):
from litellm.responses.file_search.emulated_handler import (
@@ -919,7 +921,7 @@ def responses(
**emulated_kwargs,
)
if responses_api_provider_config is None:
if responses_api_provider_config is None or use_responses_api_bridge is True:
return litellm_completion_transformation_handler.response_api_handler(
model=model,
input=input,
+3
View File
@@ -199,6 +199,7 @@ class GenericLiteLLMParams(CredentialLiteLLMParams, CustomPricingLiteLLMParams):
budget_duration: Optional[str] = None
use_in_pass_through: Optional[bool] = False
use_litellm_proxy: Optional[bool] = False
use_responses_api_bridge: Optional[bool] = None
model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
merge_reasoning_content_in_choices: Optional[bool] = False
model_info: Optional[Dict] = None
@@ -318,6 +319,8 @@ class LiteLLMParamsTypedDict(TypedDict, total=False):
configurable_clientside_auth_params: CONFIGURABLE_CLIENTSIDE_AUTH_PARAMS # for allowing api base switching on finetuned models
## DROP PARAMS ##
drop_params: Optional[bool]
## RESPONSES API BRIDGE ##
use_responses_api_bridge: Optional[bool]
## UNIFIED PROJECT/REGION ##
region_name: Optional[str]
## VERTEX AI ##
@@ -0,0 +1,107 @@
"""
Tests for the `use_responses_api_bridge` flag that allows openai/ models
with custom api_base to opt-in to the /responses /chat/completions bridge.
"""
import os
import sys
from unittest.mock import MagicMock, patch
sys.path.insert(
0, os.path.abspath("../../..")
) # Adds the parent directory to the system path
import litellm
class TestUseResponsesApiBridgeFlag:
"""Test that use_responses_api_bridge forces the chat completions bridge."""
@patch(
"litellm.responses.main.litellm_completion_transformation_handler.response_api_handler"
)
@patch(
"litellm.responses.main.ProviderConfigManager.get_provider_responses_api_config"
)
def test_bridge_used_when_flag_is_true(self, mock_get_config, mock_bridge_handler):
"""When use_responses_api_bridge=True, the bridge handler should be called
even though the provider (openai) has native responses API support."""
# Setup: provider config returns a non-None config (native support exists)
mock_get_config.return_value = litellm.OpenAIResponsesAPIConfig()
mock_bridge_handler.return_value = MagicMock()
litellm.responses(
model="openai/my-custom-model",
input="Hello",
use_responses_api_bridge=True,
litellm_logging_obj=MagicMock(),
)
mock_bridge_handler.assert_called_once()
@patch("litellm.responses.main.base_llm_http_handler.response_api_handler")
@patch(
"litellm.responses.main.ProviderConfigManager.get_provider_responses_api_config"
)
def test_native_forwarding_when_flag_absent(
self, mock_get_config, mock_native_handler
):
"""When use_responses_api_bridge is not set, openai/ models should use
native responses API forwarding (existing behavior)."""
mock_get_config.return_value = litellm.OpenAIResponsesAPIConfig()
mock_native_handler.return_value = MagicMock()
litellm.responses(
model="openai/gpt-4o",
input="Hello",
litellm_logging_obj=MagicMock(),
)
mock_native_handler.assert_called_once()
@patch(
"litellm.responses.main.litellm_completion_transformation_handler.response_api_handler"
)
@patch(
"litellm.responses.main.ProviderConfigManager.get_provider_responses_api_config"
)
def test_flag_does_not_leak_into_kwargs(self, mock_get_config, mock_bridge_handler):
"""The use_responses_api_bridge flag should be popped from kwargs and not
passed through to the bridge handler."""
mock_get_config.return_value = litellm.OpenAIResponsesAPIConfig()
mock_bridge_handler.return_value = MagicMock()
litellm.responses(
model="openai/my-custom-model",
input="Hello",
use_responses_api_bridge=True,
litellm_logging_obj=MagicMock(),
)
call_kwargs = mock_bridge_handler.call_args
# The flag should not appear in the kwargs passed to the bridge handler
all_kwargs = call_kwargs.kwargs if call_kwargs.kwargs else {}
assert "use_responses_api_bridge" not in all_kwargs
@patch(
"litellm.responses.main.litellm_completion_transformation_handler.response_api_handler"
)
@patch(
"litellm.responses.main.ProviderConfigManager.get_provider_responses_api_config"
)
def test_bridge_used_when_provider_config_none(
self, mock_get_config, mock_bridge_handler
):
"""When the provider has no native responses API config (returns None),
the bridge should be used regardless of the flag (existing behavior)."""
mock_get_config.return_value = None
mock_bridge_handler.return_value = MagicMock()
litellm.responses(
model="anthropic/claude-3-haiku",
input="Hello",
litellm_logging_obj=MagicMock(),
)
mock_bridge_handler.assert_called_once()