mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-23 08:24:15 +00:00
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:
co-authored by
Claude Sonnet 4.6
parent
62757ff48f
commit
f6b03a469e
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user