fix(litellm): keep a function tool without a body on the chat route

A tools entry of only {"type": "function"} has nothing for the Responses
bridge to convert, and the bridge raised a 500 for it where the chat
route returns the provider's own 400. The gate now counts a tool as a
function tool only when it carries a function body or a top-level name,
on every provider the gate serves
This commit is contained in:
mateo-berri 2026-09-19 18:58:08 -07:00
parent e833bdccde
commit 325d17aca9
2 changed files with 34 additions and 1 deletions

View file

@ -1117,7 +1117,11 @@ def responses_api_bridge_check(
# - 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" if isinstance(tool, dict) else getattr(tool, "type", None) == "function")
(
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):

View file

@ -1049,6 +1049,35 @@ def test_responses_api_bridge_check_gpt_5_4_flat_function_tool_routes_to_respons
assert model_info.get("mode") == "responses"
@pytest.mark.parametrize(
"custom_llm_provider, model_name, api_base",
[
pytest.param("openai", "gpt-5.6", None, id="openai"),
pytest.param("azure_ai", "gpt-6-astra", "https://myproject.services.ai.azure.com", id="azure-ai-foundry"),
],
)
def test_responses_api_bridge_check_function_tool_without_body_stays_chat(
monkeypatch, custom_llm_provider, model_name, api_base
):
import litellm
from litellm.main import responses_api_bridge_check
monkeypatch.delenv("OPENAI_BASE_URL", raising=False)
monkeypatch.delenv("OPENAI_API_BASE", raising=False)
monkeypatch.setattr(litellm, "api_base", None)
model_info, model = responses_api_bridge_check(
model=model_name,
custom_llm_provider=custom_llm_provider,
tools=[{"type": "function"}],
reasoning_effort=None,
api_base=api_base,
)
assert model == model_name
assert model_info.get("mode") != "responses"
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