mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-11 19:48:29 +00:00
Reserve reasoning for responses via chat completion
This commit is contained in:
@@ -188,11 +188,9 @@ class OpenAIGPT5Config(OpenAIGPTConfig):
|
||||
) or optional_params.get("reasoning_effort")
|
||||
effective_effort = _get_effort_level(raw_reasoning_effort)
|
||||
|
||||
# Normalize to string for Chat Completions API when dict has only "effort".
|
||||
# Preserve full dict (e.g. {"effort": "high", "summary": "detailed"}) for Responses API.
|
||||
if isinstance(raw_reasoning_effort, dict) and set(
|
||||
raw_reasoning_effort.keys()
|
||||
) <= {"effort"}:
|
||||
# Normalize dict reasoning_effort to string for Chat Completions API.
|
||||
# Example: {"effort": "high", "summary": "detailed"} -> "high"
|
||||
if isinstance(raw_reasoning_effort, dict) and "effort" in raw_reasoning_effort:
|
||||
normalized = _normalize_reasoning_effort_for_chat_completion(
|
||||
raw_reasoning_effort
|
||||
)
|
||||
|
||||
+5
-1
@@ -955,7 +955,7 @@ def responses_api_bridge_check(
|
||||
model_info["mode"] = "responses"
|
||||
model = model.replace("responses/", "")
|
||||
|
||||
# OpenAI gpt-5.4 chat-completions calls with both tools + reasoning_effort
|
||||
# OpenAI gpt-5.4+ chat-completions calls with both tools + reasoning_effort
|
||||
# must be bridged to Responses API.
|
||||
if (
|
||||
custom_llm_provider == "openai"
|
||||
@@ -1617,6 +1617,10 @@ def completion( # type: ignore # noqa: PLR0915
|
||||
if model_info.get("mode") == "responses":
|
||||
from litellm.completion_extras import responses_api_bridge
|
||||
|
||||
if isinstance(reasoning_effort, dict) and "summary" in reasoning_effort:
|
||||
optional_params = dict(optional_params)
|
||||
optional_params["reasoning_effort"] = reasoning_effort
|
||||
|
||||
return responses_api_bridge.completion(
|
||||
model=model,
|
||||
messages=messages,
|
||||
|
||||
@@ -324,19 +324,15 @@ def test_gpt5_4_pro_allows_reasoning_effort_xhigh(config: OpenAIConfig):
|
||||
assert params["reasoning_effort"] == "xhigh"
|
||||
|
||||
|
||||
def test_gpt5_preserves_reasoning_effort_dict_with_summary(config: OpenAIConfig):
|
||||
"""Dict with summary/generate_summary is preserved for Responses API.
|
||||
|
||||
Config/deployments may pass Responses API format: {'effort': 'high', 'summary': 'detailed'}.
|
||||
We preserve the full dict so it reaches the Responses API transformation.
|
||||
"""
|
||||
def test_gpt5_normalizes_reasoning_effort_dict_with_summary(config: OpenAIConfig):
|
||||
"""Dict with summary/generate_summary is normalized for chat completions."""
|
||||
params = config.map_openai_params(
|
||||
non_default_params={"reasoning_effort": {"effort": "high", "summary": "detailed"}},
|
||||
optional_params={},
|
||||
model="gpt-5.4",
|
||||
drop_params=False,
|
||||
)
|
||||
assert params["reasoning_effort"] == {"effort": "high", "summary": "detailed"}
|
||||
assert params["reasoning_effort"] == "high"
|
||||
|
||||
|
||||
def test_gpt5_xhigh_dict_triggers_validation(config: OpenAIConfig):
|
||||
@@ -362,7 +358,7 @@ def test_gpt5_xhigh_dict_accepted_for_supported_model(config: OpenAIConfig):
|
||||
model="gpt-5.4",
|
||||
drop_params=False,
|
||||
)
|
||||
assert params["reasoning_effort"] == {"effort": "xhigh", "summary": "detailed"}
|
||||
assert params["reasoning_effort"] == "xhigh"
|
||||
|
||||
|
||||
def test_gpt5_none_dict_with_tools_no_tool_drop(config: OpenAIConfig):
|
||||
@@ -378,7 +374,7 @@ def test_gpt5_none_dict_with_tools_no_tool_drop(config: OpenAIConfig):
|
||||
model="gpt-5.4",
|
||||
drop_params=False,
|
||||
)
|
||||
assert params["reasoning_effort"] == {"effort": "none", "summary": "detailed"}
|
||||
assert params["reasoning_effort"] == "none"
|
||||
assert params["tools"] == tools
|
||||
|
||||
|
||||
@@ -398,20 +394,20 @@ def test_gpt5_none_dict_with_sampling_params_allowed(config: OpenAIConfig):
|
||||
model="gpt-5.1",
|
||||
drop_params=False,
|
||||
)
|
||||
assert params["reasoning_effort"] == {"effort": "none", "summary": "detailed"}
|
||||
assert params["reasoning_effort"] == "none"
|
||||
assert params["logprobs"] is True
|
||||
assert params["top_p"] == 0.9
|
||||
|
||||
|
||||
def test_gpt5_preserves_reasoning_effort_dict_with_summary_from_optional_params(config: OpenAIConfig):
|
||||
"""reasoning_effort dict with summary in optional_params is preserved."""
|
||||
def test_gpt5_normalizes_reasoning_effort_dict_with_summary_from_optional_params(config: OpenAIConfig):
|
||||
"""reasoning_effort dict with summary in optional_params is normalized."""
|
||||
params = config.map_openai_params(
|
||||
non_default_params={},
|
||||
optional_params={"reasoning_effort": {"effort": "medium", "summary": "detailed"}},
|
||||
model="gpt-5.4",
|
||||
drop_params=False,
|
||||
)
|
||||
assert params["reasoning_effort"] == {"effort": "medium", "summary": "detailed"}
|
||||
assert params["reasoning_effort"] == "medium"
|
||||
|
||||
|
||||
def test_gpt5_4_pro_rejects_non_default_temperature(config: OpenAIConfig):
|
||||
|
||||
@@ -678,6 +678,43 @@ def test_responses_api_bridge_check_gpt_5_4_tools_without_reasoning_stays_chat()
|
||||
assert model_info.get("mode") != "responses"
|
||||
|
||||
|
||||
@patch("litellm.completion_extras.responses_api_bridge.completion")
|
||||
def test_gpt_5_4_responses_bridge_preserves_reasoning_summary_dict(
|
||||
mock_responses_completion,
|
||||
):
|
||||
"""When routed to Responses, preserve reasoning_effort summary dict."""
|
||||
mock_responses_completion.return_value = MagicMock()
|
||||
|
||||
import litellm
|
||||
|
||||
litellm.completion(
|
||||
model="gpt-5.4",
|
||||
messages=[{"role": "user", "content": "What is the capital of France?"}],
|
||||
tools=[
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_capital",
|
||||
"description": "Get the capital of a country",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {"country": {"type": "string"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
],
|
||||
reasoning_effort={"effort": "xhigh", "summary": "detailed"},
|
||||
api_key="fake-key",
|
||||
)
|
||||
|
||||
assert mock_responses_completion.called is True
|
||||
optional_params = mock_responses_completion.call_args.kwargs["optional_params"]
|
||||
assert optional_params["reasoning_effort"] == {
|
||||
"effort": "xhigh",
|
||||
"summary": "detailed",
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user