From 63fa72643d730e0cacb2bfccf768a8823e8fdf9d Mon Sep 17 00:00:00 2001 From: Chenglun Hu Date: Tue, 25 Aug 2026 16:01:13 +0800 Subject: [PATCH] fix(databricks): accept Reasoning(effort=..., summary=...) dict for reasoning_effort MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenAI Responses callers send reasoning_effort as a dict ({'effort': 'low', 'summary': 'concise'}) — the Responses->Chat parser keeps the full dict when summary is set (#25359 / #28196). The Databricks Claude adapter only handled the bare string and silently dropped the dict shape. Coerce dict -> effort before mapping, matching the direct Anthropic and Bedrock Converse adapters. Adds regression tests for both shapes. --- .../llms/databricks/chat/transformation.py | 10 +++- .../test_databricks_chat_transformation.py | 51 +++++++++++++++++-- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/litellm/llms/databricks/chat/transformation.py b/litellm/llms/databricks/chat/transformation.py index 8a625569cfa..6c8474d1170 100644 --- a/litellm/llms/databricks/chat/transformation.py +++ b/litellm/llms/databricks/chat/transformation.py @@ -358,7 +358,15 @@ class DatabricksConfig(DatabricksBase, OpenAILikeChatConfig, AnthropicConfig): ) # unsupported for claude models - if json_schema -> convert to tool call if "reasoning_effort" in non_default_params and "claude" in model: - reasoning_effort_value: Final = non_default_params.get("reasoning_effort") + reasoning_effort_value = non_default_params.get("reasoning_effort") + # Accept both string ("low") and dict ({"effort": "low", + # "summary": "concise"}). The Responses->Chat parser keeps the + # full dict when `summary` is set (see #25359 / #28196), so a + # dict here is the standard shape Otto/OpenAI-Responses-Bridge + # callers send. Same coercion the direct Anthropic adapter and + # the Bedrock Converse adapter already do. + if isinstance(reasoning_effort_value, dict): + reasoning_effort_value = reasoning_effort_value.get("effort") mapped_thinking: Final = AnthropicConfig._map_reasoning_effort( reasoning_effort=reasoning_effort_value, model=model, diff --git a/tests/test_litellm/llms/databricks/chat/test_databricks_chat_transformation.py b/tests/test_litellm/llms/databricks/chat/test_databricks_chat_transformation.py index 41fb2589655..307ad58e950 100644 --- a/tests/test_litellm/llms/databricks/chat/test_databricks_chat_transformation.py +++ b/tests/test_litellm/llms/databricks/chat/test_databricks_chat_transformation.py @@ -1,9 +1,4 @@ -import json - import pytest -from fastapi.testclient import TestClient - -from unittest.mock import MagicMock, patch from litellm.llms.databricks.chat.transformation import ( DatabricksChatResponseIterator, @@ -493,3 +488,49 @@ def test_chunk_parser_without_usage_still_parses_content(): assert result.id == "chatcmpl-test" assert result.model == "databricks-claude-sonnet-5" assert result.choices[0]["delta"]["content"] == "hi" + + +@pytest.mark.parametrize( + "model", + [ + "databricks/databricks-claude-3-7-sonnet", + "databricks/databricks-claude-opus-4-7", + ], +) +def test_reasoning_effort_accepts_dict_shape(model): + """Regression for #28196 (companion to bedrock + direct Anthropic fixes). + + OpenAI Responses callers send + ``reasoning_effort={'effort': 'low', 'summary': 'concise'}``; + the Databricks Claude adapter must coerce the dict to ``low`` and + forward thinking + (for adaptive Claude 4.6 / 4.7) output_config, + instead of silently dropping it. + """ + config = DatabricksConfig() + + optional_params = config.map_openai_params( + non_default_params={ + "reasoning_effort": {"effort": "low", "summary": "concise"}, + }, + optional_params={}, + model=model, + drop_params=False, + replace_max_completion_tokens_with_max_tokens=False, + ) + + assert "thinking" in optional_params, f"reasoning_effort dict was dropped on {model}: {optional_params!r}" + + +def test_reasoning_effort_bare_string_still_works(): + """Regression guard for the pre-existing string shape.""" + config = DatabricksConfig() + + optional_params = config.map_openai_params( + non_default_params={"reasoning_effort": "low"}, + optional_params={}, + model="databricks/databricks-claude-3-7-sonnet", + drop_params=False, + replace_max_completion_tokens_with_max_tokens=False, + ) + + assert "thinking" in optional_params