refactor(responses): move responses_api_bridge_check out of litellm.main (#45772)

Part of LIT-9423
This commit is contained in:
nate-berri 2026-10-10 12:07:00 -07:00 • committed by GitHub
parent 8d510a6b1b
commit d02dc43154
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 186 additions and 172 deletions

View file

@ -412,7 +412,7 @@ class LiteLLMMessagesToCompletionTransformationHandler:
return None
if resolved_provider == "litellm_proxy":
return None
from litellm.main import responses_api_bridge_check
from litellm.responses.bridge_check import responses_api_bridge_check
web_search_options: Final = completion_kwargs.get("web_search_options")
tools: Final = completion_kwargs.get("tools")

View file

@ -45,7 +45,7 @@ _RESPONSES_API_PROVIDERS: Final = frozenset({"openai"})
def _bridges_to_responses_api(model: str, custom_llm_provider: str) -> bool:
from litellm.main import responses_api_bridge_check
from litellm.responses.bridge_check import responses_api_bridge_check
model_info, _ = responses_api_bridge_check(model=model, custom_llm_provider=custom_llm_provider)
return model_info.get("mode") == "responses"

View file

@ -26,7 +26,6 @@ from copy import deepcopy
from functools import partial
from types import MappingProxyType
from typing import TYPE_CHECKING, Any, Final, Literal, Optional, Protocol, Union, cast, get_args
from urllib.parse import urlsplit
from litellm._logging import redact_string
from litellm._uuid import uuid
@ -109,10 +108,6 @@ from litellm.litellm_core_utils.request_timeout_resolver import (
get_configured_request_timeout,
)
from litellm.litellm_core_utils.tokenizer import Encoding as Tokenizer
from litellm.llms.azure_ai.common_utils import (
azure_ai_supports_native_responses,
foundry_chat_rejects_function_tools_while_reasoning,
)
from litellm.llms.base_llm import BaseConfig, BaseImageGenerationConfig
from litellm.llms.base_llm.base_model_iterator import (
convert_model_response_to_streaming,
@ -167,7 +162,6 @@ from litellm.utils import (
create_tokenizer,
get_llm_provider,
get_model_info,
get_model_info_helper,
get_non_default_completion_params,
get_non_default_transcription_params,
get_optional_params_embeddings,
@ -1073,142 +1067,12 @@ def mock_completion(
raise Exception(f"Mock completion response failed - {e}")
_OPENAI_DEFAULT_API_BASE: Final = "https://api.openai.com/v1"
_OPENAI_API_HOST: Final = "api.openai.com"
def _is_openai_backed_api_base(api_base: str) -> bool:
hostname: Final = urlsplit(api_base).hostname
return hostname is not None and (hostname == _OPENAI_API_HOST or hostname.endswith(f".{_OPENAI_API_HOST}"))
def _resolve_openai_api_base(api_base: str | None) -> str:
"""Effective OpenAI base a chat request will hit: arg > global > env > default. The bridge gate
and the ``_complete_custom_openai`` chat handler MUST resolve this identically, or a custom base
set via ``litellm.api_base`` or ``OPENAI_BASE_URL``/``OPENAI_API_BASE`` is invisible to the gate,
which then misreads it as the default OpenAI endpoint and bridges a request the backend can't serve."""
return (
api_base
or litellm.api_base
or get_secret_str("OPENAI_BASE_URL")
or get_secret_str("OPENAI_API_BASE")
or _OPENAI_DEFAULT_API_BASE
)
def responses_api_bridge_check(
model: str,
custom_llm_provider: str,
web_search_options: OpenAIWebSearchOptions | None = None,
tools: Sequence[Mapping[str, object]] | None = None,
reasoning_effort: str | Mapping[str, object] | None = None,
reasoning_summary: object | None = None,
api_base: str | None = None,
) -> tuple[dict, str]:
model_info: dict[str, object] = {}
# Global flag: route ALL OpenAI chat completions through Responses API.
# Returns early with minimal model_info; callers only inspect the "mode" key.
if litellm.route_all_chat_openai_to_responses and custom_llm_provider == "openai":
model = model.replace("responses/", "")
model_info["mode"] = "responses"
return model_info, model
try:
model_info = cast(
dict,
get_model_info_helper(model=model, custom_llm_provider=custom_llm_provider),
)
if model_info.get("mode") is None and model.startswith("responses/"):
model = model.replace("responses/", "")
mode = "responses"
model_info["mode"] = mode
except Exception as e:
verbose_logger.debug("Error getting model info: %s", e)
if model.startswith("responses/"): # handle azure models - `azure/responses/<deployment-name>`
model = model.replace("responses/", "")
mode = "responses"
model_info["mode"] = mode
if web_search_options is not None and custom_llm_provider == "xai":
model_info["mode"] = "responses"
model = model.replace("responses/", "")
# OpenAI/Azure GPT-5 chat-completions that need Responses-only fields (e.g.
# ``reasoningSummary`` in ``extra_body``) must be bridged; Chat Completions rejects
# those keys.
#
# - gpt-5.4+: FUNCTION tools with reasoning active must be bridged. OpenAI enables
# reasoning by default for these models (unset reasoning_effort means medium
# server-side), and Chat Completions rejects function tools whenever reasoning is
# on ("Function tools with reasoning_effort are not supported ... use
# /v1/responses or set reasoning_effort to 'none'"), so only an explicit
# ``"none"`` keeps the request chat-servable. Custom (grammar) tools are served
# natively by Chat Completions with reasoning on, so custom-only requests stay on
# chat and keep their native custom tool_call response shape.
# - The UNSET-effort arm only fires against endpoints known to enforce that
# constraint (any api.openai.com host, or Azure OpenAI where api_base is
# always set): chat-only OpenAI-compatible backends registered under the openai
# provider with a custom api_base and gpt-5.4+ model names serve tools without
# reasoning fine and have no /responses route, so they keep pre-existing
# behavior (bridge only on an explicit reasoning_effort).
# - Azure AI Foundry's OpenAI v1 hosts (azure_ai provider) enforce it later in the series:
# an explicit effort with function tools is rejected from gpt-5.6 on, and the unset
# effort only from gpt-6 on (gpt-5.6 serves tools with reasoning silently off), so the
# azure_ai gate keys on those measured boundaries instead of gpt-5.4+.
# - Older GPT-5 names (e.g. ``gpt-5``, ``gpt-5.1``): bridge only when a reasoning
# summary alias is present with ``reasoning_effort`` (tools alone stay on chat).
has_function_tool: Final = any(
(
tool.get("type") == "function" and (isinstance(tool.get("function"), dict) or "name" in tool)
if isinstance(tool, dict)
else getattr(tool, "type", None) == "function"
)
for tool in (tools or ())
)
if isinstance(reasoning_effort, dict):
reasoning_active = reasoning_effort.get("effort") != "none" or reasoning_effort.get("summary") is not None
else:
reasoning_active = reasoning_effort != "none"
# The reasoning+tools constraint is enforced by the real OpenAI backend behind any api.openai.com
# host (the default URL or a PrivateLink hostname such as <region>.privatelink.api.openai.com) and
# by Azure OpenAI through the azure provider. Resolve the effective OpenAI base arg>global>env>default
# exactly as the chat handler does, so a custom base set via litellm.api_base or
# OPENAI_BASE_URL/OPENAI_API_BASE isn't misread as the default and bridged to a /responses route it
# lacks. A whitespace-only base collapses to the default too.
resolved_api_base: Final = _resolve_openai_api_base(api_base).strip()
on_foundry_openai_endpoint: Final = custom_llm_provider == "azure_ai" and azure_ai_supports_native_responses(
model, api_base
)
on_constraint_enforcing_endpoint: Final = (
custom_llm_provider == "azure" or resolved_api_base == "" or _is_openai_backed_api_base(resolved_api_base)
)
chat_rejects_function_tools: Final = (
has_function_tool
and reasoning_active
and (
foundry_chat_rejects_function_tools_while_reasoning(model, reasoning_effort)
if on_foundry_openai_endpoint
else (
OpenAIGPT5Config.is_model_gpt_5_4_plus_model(model)
and (reasoning_effort is not None or on_constraint_enforcing_endpoint)
)
)
)
if (
(custom_llm_provider in ("openai", "azure") or on_foundry_openai_endpoint)
and model_info.get("mode") != "responses"
and OpenAIGPT5Config.is_model_gpt_5_model(model)
and not OpenAIGPT5Config.is_model_gpt_5_search_model(model)
and ((reasoning_effort is not None and reasoning_summary is not None) or chat_rejects_function_tools)
):
model_info["mode"] = "responses"
model = model.replace("responses/", "")
return model_info, model
from litellm.responses.bridge_check import ( # noqa: E402 # re-export at the old definition site
_resolve_openai_api_base as _resolve_openai_api_base, # pyright: ignore[reportPrivateUsage] # noqa: PLC0414 # re-export of moved helper
)
from litellm.responses.bridge_check import ( # noqa: E402 # re-export at the old definition site
responses_api_bridge_check as responses_api_bridge_check, # pyright: ignore[reportUnknownVariableType] # noqa: PLC0414 # re-export of moved function
)
_ANTHROPIC_ONLY_TOOL_KEYS: Final = frozenset({"input_examples", "eager_input_streaming"})

View file

@ -0,0 +1,150 @@
from collections.abc import Mapping, Sequence
from typing import Final, cast # noqa: TID251 # moved unchanged from litellm/main.py
from urllib.parse import urlsplit
import litellm
from litellm._logging import verbose_logger
from litellm.llms.azure_ai.common_utils import (
azure_ai_supports_native_responses,
foundry_chat_rejects_function_tools_while_reasoning,
)
from litellm.llms.openai.chat.gpt_5_transformation import OpenAIGPT5Config
from litellm.secret_managers.main import get_secret_str
from litellm.types.llms.openai import OpenAIWebSearchOptions
from litellm.utils import get_model_info_helper
_OPENAI_DEFAULT_API_BASE: Final = "https://api.openai.com/v1"
_OPENAI_API_HOST: Final = "api.openai.com"
def _is_openai_backed_api_base(api_base: str) -> bool:
hostname: Final = urlsplit(api_base).hostname
return hostname is not None and (hostname == _OPENAI_API_HOST or hostname.endswith(f".{_OPENAI_API_HOST}"))
def _resolve_openai_api_base(api_base: str | None) -> str:
"""Effective OpenAI base a chat request will hit: arg > global > env > default. The bridge gate
and the ``_complete_custom_openai`` chat handler MUST resolve this identically, or a custom base
set via ``litellm.api_base`` or ``OPENAI_BASE_URL``/``OPENAI_API_BASE`` is invisible to the gate,
which then misreads it as the default OpenAI endpoint and bridges a request the backend can't serve."""
return (
api_base
or litellm.api_base
or get_secret_str("OPENAI_BASE_URL")
or get_secret_str("OPENAI_API_BASE")
or _OPENAI_DEFAULT_API_BASE
)
def responses_api_bridge_check(
model: str,
custom_llm_provider: str,
web_search_options: OpenAIWebSearchOptions | None = None,
tools: Sequence[Mapping[str, object]] | None = None,
reasoning_effort: str | Mapping[str, object] | None = None,
reasoning_summary: object | None = None,
api_base: str | None = None,
) -> tuple[dict, str]:
model_info: dict[str, object] = {}
# Global flag: route ALL OpenAI chat completions through Responses API.
# Returns early with minimal model_info; callers only inspect the "mode" key.
if litellm.route_all_chat_openai_to_responses and custom_llm_provider == "openai":
model = model.replace("responses/", "")
model_info["mode"] = "responses"
return model_info, model
try:
model_info = cast(
dict,
get_model_info_helper(model=model, custom_llm_provider=custom_llm_provider),
)
if model_info.get("mode") is None and model.startswith("responses/"):
model = model.replace("responses/", "")
mode = "responses"
model_info["mode"] = mode
except Exception as e:
verbose_logger.debug("Error getting model info: %s", e)
if model.startswith("responses/"): # handle azure models - `azure/responses/<deployment-name>`
model = model.replace("responses/", "")
mode = "responses"
model_info["mode"] = mode
if web_search_options is not None and custom_llm_provider == "xai":
model_info["mode"] = "responses"
model = model.replace("responses/", "")
# OpenAI/Azure GPT-5 chat-completions that need Responses-only fields (e.g.
# ``reasoningSummary`` in ``extra_body``) must be bridged; Chat Completions rejects
# those keys.
#
# - gpt-5.4+: FUNCTION tools with reasoning active must be bridged. OpenAI enables
# reasoning by default for these models (unset reasoning_effort means medium
# server-side), and Chat Completions rejects function tools whenever reasoning is
# on ("Function tools with reasoning_effort are not supported ... use
# /v1/responses or set reasoning_effort to 'none'"), so only an explicit
# ``"none"`` keeps the request chat-servable. Custom (grammar) tools are served
# natively by Chat Completions with reasoning on, so custom-only requests stay on
# chat and keep their native custom tool_call response shape.
# - The UNSET-effort arm only fires against endpoints known to enforce that
# constraint (any api.openai.com host, or Azure OpenAI where api_base is
# always set): chat-only OpenAI-compatible backends registered under the openai
# provider with a custom api_base and gpt-5.4+ model names serve tools without
# reasoning fine and have no /responses route, so they keep pre-existing
# behavior (bridge only on an explicit reasoning_effort).
# - Azure AI Foundry's OpenAI v1 hosts (azure_ai provider) enforce it later in the series:
# an explicit effort with function tools is rejected from gpt-5.6 on, and the unset
# effort only from gpt-6 on (gpt-5.6 serves tools with reasoning silently off), so the
# azure_ai gate keys on those measured boundaries instead of gpt-5.4+.
# - Older GPT-5 names (e.g. ``gpt-5``, ``gpt-5.1``): bridge only when a reasoning
# summary alias is present with ``reasoning_effort`` (tools alone stay on chat).
has_function_tool: Final = any(
(
tool.get("type") == "function" and (isinstance(tool.get("function"), dict) or "name" in tool)
if isinstance(tool, dict)
else getattr(tool, "type", None) == "function"
)
for tool in (tools or ())
)
if isinstance(reasoning_effort, dict):
reasoning_active = reasoning_effort.get("effort") != "none" or reasoning_effort.get("summary") is not None
else:
reasoning_active = reasoning_effort != "none"
# The reasoning+tools constraint is enforced by the real OpenAI backend behind any api.openai.com
# host (the default URL or a PrivateLink hostname such as <region>.privatelink.api.openai.com) and
# by Azure OpenAI through the azure provider. Resolve the effective OpenAI base arg>global>env>default
# exactly as the chat handler does, so a custom base set via litellm.api_base or
# OPENAI_BASE_URL/OPENAI_API_BASE isn't misread as the default and bridged to a /responses route it
# lacks. A whitespace-only base collapses to the default too.
resolved_api_base: Final = _resolve_openai_api_base(api_base).strip()
on_foundry_openai_endpoint: Final = custom_llm_provider == "azure_ai" and azure_ai_supports_native_responses(
model, api_base
)
on_constraint_enforcing_endpoint: Final = (
custom_llm_provider == "azure" or resolved_api_base == "" or _is_openai_backed_api_base(resolved_api_base)
)
chat_rejects_function_tools: Final = (
has_function_tool
and reasoning_active
and (
foundry_chat_rejects_function_tools_while_reasoning(model, reasoning_effort)
if on_foundry_openai_endpoint
else (
OpenAIGPT5Config.is_model_gpt_5_4_plus_model(model)
and (reasoning_effort is not None or on_constraint_enforcing_endpoint)
)
)
)
if (
(custom_llm_provider in ("openai", "azure") or on_foundry_openai_endpoint)
and model_info.get("mode") != "responses"
and OpenAIGPT5Config.is_model_gpt_5_model(model)
and not OpenAIGPT5Config.is_model_gpt_5_search_model(model)
and ((reasoning_effort is not None and reasoning_summary is not None) or chat_rejects_function_tools)
):
model_info["mode"] = "responses"
model = model.replace("responses/", "")
return model_info, model

View file

@ -837,7 +837,7 @@ def test_responses_api_bridge_check_strips_responses_prefix():
"""Test that responses_api_bridge_check strips 'responses/' prefix and sets mode."""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.get_model_info_helper") as mock_get_model_info:
mock_get_model_info.return_value = {"max_tokens": 4096}
model_info, model = responses_api_bridge_check(
@ -871,7 +871,7 @@ def test_responses_api_bridge_check_gpt_5_4_tools_plus_reasoning_routes_to_respo
"""gpt-5.4 with both tools and reasoning_effort should route to Responses API."""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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.4",
@ -901,7 +901,7 @@ def test_responses_api_bridge_check_gpt_5_5_tools_plus_reasoning_routes_to_respo
"""gpt-5.5+ with both tools and reasoning_effort should route to Responses API."""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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.5-pro",
@ -918,7 +918,7 @@ def test_responses_api_bridge_check_azure_gpt_5_4_tools_plus_reasoning_routes_to
"""Azure gpt-5.4 with both tools and reasoning_effort should route to Responses API."""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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.4",
@ -939,7 +939,7 @@ def test_responses_api_bridge_check_azure_gpt_5_4_tools_with_default_reasoning_r
"""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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.4",
@ -960,7 +960,7 @@ def test_responses_api_bridge_check_gpt_5_4_tools_with_default_reasoning_routes_
"""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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.4",
@ -1031,7 +1031,7 @@ def test_responses_api_bridge_check_gpt_5_6_tools_with_default_reasoning_routes_
monkeypatch.delenv("OPENAI_API_BASE", raising=False)
monkeypatch.setattr(litellm, "api_base", None)
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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=model_name,
@ -1051,7 +1051,7 @@ def test_responses_api_bridge_check_gpt_5_4_tools_with_reasoning_none_stays_chat
"""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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.4",
@ -1068,7 +1068,7 @@ def test_responses_api_bridge_check_reasoning_none_with_summary_still_routes_to_
"""A reasoning summary is Responses-only regardless of effort value."""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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.4",
@ -1090,7 +1090,7 @@ def test_responses_api_bridge_check_gpt_5_4_custom_tools_only_stays_chat():
"""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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",
@ -1107,7 +1107,7 @@ def test_responses_api_bridge_check_gpt_5_4_mixed_function_and_custom_tools_rout
"""One function tool in the mix is enough to make chat unservable with reasoning on."""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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",
@ -1127,7 +1127,7 @@ def test_responses_api_bridge_check_gpt_5_4_flat_function_tool_routes_to_respons
"""Responses-style flat function tool defs still count as function tools."""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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",
@ -1173,7 +1173,7 @@ def test_responses_api_bridge_check_dict_effort_none_stays_chat():
"""The escape hatch must honor litellm's dict form: {"effort": "none"} means reasoning off."""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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",
@ -1189,7 +1189,7 @@ def test_responses_api_bridge_check_dict_effort_none_stays_chat():
def test_responses_api_bridge_check_dict_effort_active_routes_to_responses():
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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",
@ -1206,7 +1206,7 @@ def test_responses_api_bridge_check_dict_effort_none_with_summary_routes_to_resp
"""A summary inside the dict form is Responses-only even when effort is none."""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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",
@ -1228,7 +1228,7 @@ def test_responses_api_bridge_check_blank_api_base_is_default_openai(blank_api_b
"""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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",
@ -1250,7 +1250,7 @@ def test_responses_api_bridge_check_custom_api_base_with_unset_effort_stays_chat
"""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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",
@ -1275,7 +1275,7 @@ def test_responses_api_bridge_check_custom_api_base_via_global_with_unset_effort
from litellm.main import responses_api_bridge_check
monkeypatch.setattr(litellm, "api_base", "http://vllm.internal:8000/v1")
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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",
@ -1302,7 +1302,7 @@ def test_responses_api_bridge_check_custom_api_base_via_env_with_unset_effort_st
monkeypatch.delenv("OPENAI_BASE_URL", raising=False)
monkeypatch.delenv("OPENAI_API_BASE", raising=False)
monkeypatch.setenv(env_var, "http://vllm.internal:8000/v1")
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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",
@ -1396,7 +1396,7 @@ def test_responses_api_bridge_check_custom_api_base_with_explicit_effort_still_r
"""Explicit reasoning_effort keeps its pre-existing bridging behavior on any api_base."""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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",
@ -1414,7 +1414,7 @@ def test_responses_api_bridge_check_azure_with_api_base_and_unset_effort_routes(
"""Azure OpenAI always sets api_base and does enforce the constraint; keep bridging."""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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.4",
@ -1494,7 +1494,7 @@ def test_responses_api_bridge_check_older_gpt_5_tools_without_reasoning_stays_ch
"""Pre-5.4 GPT-5 names keep the old boundary: tools alone never bridge."""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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.1",
@ -1511,7 +1511,7 @@ def test_responses_api_bridge_check_gpt_5_4_reasoning_summary_without_tools_rout
"""gpt-5.4+ with reasoning_effort + reasoningSummary but no tools should bridge (AI SDK)."""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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.4",
@ -1529,7 +1529,7 @@ def test_responses_api_bridge_check_gpt_5_reasoning_summary_routes_to_responses(
"""Bare ``gpt-5`` with reasoning_effort + reasoningSummary should bridge (not 5.4+)."""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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",
@ -1547,7 +1547,7 @@ def test_responses_api_bridge_check_gpt_5_tools_without_summary_stays_chat():
"""gpt-5 with tools + reasoning_effort but no summary should stay on chat."""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.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",
@ -1881,7 +1881,7 @@ def test_responses_api_bridge_check_handles_exception():
"""Test that responses_api_bridge_check handles exceptions and still processes responses/ models."""
from litellm.main import responses_api_bridge_check
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.get_model_info_helper") as mock_get_model_info:
mock_get_model_info.side_effect = Exception("Model not found")
model_info, model = responses_api_bridge_check(model="responses/custom-model", custom_llm_provider="custom")
@ -1909,7 +1909,7 @@ def test_responses_api_bridge_check_global_flag_does_not_affect_azure():
from litellm.main import responses_api_bridge_check
with patch.object(litellm, "route_all_chat_openai_to_responses", True):
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.get_model_info_helper") as mock_get_model_info:
mock_get_model_info.return_value = {"max_tokens": 4096}
model_info, model = responses_api_bridge_check(
model="gpt-4o",
@ -1924,7 +1924,7 @@ def test_responses_api_bridge_check_global_flag_default_false():
from litellm.main import responses_api_bridge_check
with patch.object(litellm, "route_all_chat_openai_to_responses", False):
with patch("litellm.main.get_model_info_helper") as mock_get_model_info:
with patch("litellm.responses.bridge_check.get_model_info_helper") as mock_get_model_info:
mock_get_model_info.return_value = {"max_tokens": 4096}
model_info, model = responses_api_bridge_check(
model="gpt-4o",