From 250a69486423af42eeeb1bb6183886d233cd2750 Mon Sep 17 00:00:00 2001 From: 72004 Date: Sun, 11 Oct 2026 19:26:21 +0500 Subject: [PATCH] fix(databricks): bridge GPT-5.4+ function tools to the Responses API Databricks forwards its OpenAI GPT endpoints (databricks-gpt-5-4, -5-6-sol, ...) to OpenAI, whose Chat Completions rejects function tools while reasoning is on. The version check never matched the hyphenated Databricks names, so these requests were sent to /chat/completions and failed. --- litellm/responses/bridge_check.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/litellm/responses/bridge_check.py b/litellm/responses/bridge_check.py index 3b1e958ff86..e4b5dc02828 100644 --- a/litellm/responses/bridge_check.py +++ b/litellm/responses/bridge_check.py @@ -1,3 +1,4 @@ +import re from collections.abc import Mapping, Sequence from typing import Final, cast # noqa: TID251 # moved unchanged from litellm/main.py from urllib.parse import urlsplit @@ -15,6 +16,15 @@ 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" +# Databricks serves OpenAI GPT models as e.g. ``databricks-gpt-5-6-sol``: the minor version is +# hyphenated, so the OpenAI version parser does not recognise it. +_DATABRICKS_GPT_NAME: Final = re.compile(r"^databricks-gpt-(\d+)-(\d+)(?=-|$)") + + +def _openai_name_for_databricks_model(model: str) -> str: + """``databricks-gpt-5-6-sol`` -> ``gpt-5.6-sol``; other names are returned unchanged.""" + model_name: Final = model.split("/")[-1] + return _DATABRICKS_GPT_NAME.sub(r"gpt-\1.\2", model_name) def _is_openai_backed_api_base(api_base: str) -> bool: @@ -122,8 +132,15 @@ def responses_api_bridge_check( on_foundry_openai_endpoint: Final = custom_llm_provider == "azure_ai" and azure_ai_supports_native_responses( model, api_base ) + # Databricks proxies OpenAI GPT models to the real OpenAI backend, which enforces the + # reasoning+tools constraint, and serves them on its own /responses route as well. + on_databricks_gpt_endpoint: Final = custom_llm_provider == "databricks" and OpenAIGPT5Config.is_model_gpt_5_model( + model + ) on_constraint_enforcing_endpoint: Final = ( - custom_llm_provider == "azure" or resolved_api_base == "" or _is_openai_backed_api_base(resolved_api_base) + custom_llm_provider in ("azure", "databricks") + or resolved_api_base == "" + or _is_openai_backed_api_base(resolved_api_base) ) chat_rejects_function_tools: Final = ( has_function_tool @@ -132,7 +149,9 @@ def responses_api_bridge_check( 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) + OpenAIGPT5Config.is_model_gpt_5_4_plus_model( + _openai_name_for_databricks_model(model) if on_databricks_gpt_endpoint else model + ) and (reasoning_effort is not None or on_constraint_enforcing_endpoint) ) ) @@ -143,7 +162,7 @@ def responses_api_bridge_check( 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) - ): + ) or (on_databricks_gpt_endpoint and model_info.get("mode") != "responses" and chat_rejects_function_tools): model_info["mode"] = "responses" model = model.replace("responses/", "")