fix(litellm): treat a blank api_base as the default OpenAI endpoint in the bridge gate

A blank api_base (empty or whitespace) resolves to the default OpenAI
base downstream but is not None, so the constraint-enforcing-endpoint
check misclassified it as a custom backend and skipped the unset-effort
auto-bridge, leaving gpt-5.4+ function-tool requests to 400 at OpenAI.
The check now treats None, empty, and whitespace api_base alike; a real
custom base still opts out. Verified with get_llm_provider, which passes
a blank api_base through while resolving the provider to openai
This commit is contained in:
Tin Chi Lo 2026-07-22 15:46:55 -07:00
parent d516a72c05
commit cc00650fec
2 changed files with 27 additions and 1 deletions

View file

@ -1047,7 +1047,10 @@ def responses_api_bridge_check(
reasoning_active = reasoning_effort.get("effort") != "none" or reasoning_effort.get("summary") is not None
else:
reasoning_active = reasoning_effort != "none"
on_constraint_enforcing_endpoint = custom_llm_provider == "azure" or api_base is None
# A blank api_base (None, "", or whitespace) is not a custom endpoint: it resolves
# to the default OpenAI base downstream, which does enforce the reasoning+tools
# constraint. Azure always targets an OpenAI-constraint endpoint regardless.
on_constraint_enforcing_endpoint = custom_llm_provider == "azure" or not (api_base and api_base.strip())
if (
custom_llm_provider in ("openai", "azure")
and model_info.get("mode") != "responses"

View file

@ -998,6 +998,29 @@ def test_responses_api_bridge_check_dict_effort_none_with_summary_routes_to_resp
assert model_info.get("mode") == "responses"
@pytest.mark.parametrize("blank_api_base", [None, "", " ", "\t"])
def test_responses_api_bridge_check_blank_api_base_is_default_openai(blank_api_base):
"""
A blank api_base (None, empty, or whitespace) resolves to the default OpenAI
endpoint downstream, which enforces the reasoning+tools constraint, so gpt-5.4+
function-tool requests with unset reasoning_effort must still auto-bridge.
"""
from litellm.main import responses_api_bridge_check
with patch("litellm.main._get_model_info_helper") as mock_get_model_info:
mock_get_model_info.return_value = {"max_tokens": 128000}
model_info, model = responses_api_bridge_check(
model="gpt-5.6",
custom_llm_provider="openai",
tools=[{"type": "function", "function": {"name": "get_capital"}}],
reasoning_effort=None,
api_base=blank_api_base,
)
assert model == "gpt-5.6"
assert model_info.get("mode") == "responses"
def test_responses_api_bridge_check_custom_api_base_with_unset_effort_stays_chat():
"""
Chat-only OpenAI-compatible backends registered under the openai provider with a