mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
fix(ollama): accept dict reasoning_effort from Responses API bridge
Responses API clients such as Codex CLI send reasoning: {"summary": "auto"}
on every request. When the Responses to chat bridge forwarded that object as
reasoning_effort, OllamaChatConfig and OllamaConfig hashed it in a set
membership check and raised TypeError: unhashable type: 'dict', which came
back as an APIConnectionError and put Codex into a reconnect loop
Unwrap the effort key when the value is a dict and skip the think mapping
when no effort is present, so {"summary": ...} alone is a no-op and
{"effort": "low", "summary": ...} behaves like "low"
Fixes #37452
This commit is contained in:
parent
90e4962c81
commit
d15aa0c89b
4 changed files with 93 additions and 9 deletions
|
|
@ -171,10 +171,12 @@ class OllamaChatConfig(BaseConfig):
|
|||
if value.get("json_schema") and value["json_schema"].get("schema"):
|
||||
optional_params["format"] = value["json_schema"]["schema"]
|
||||
if param == "reasoning_effort" and value is not None:
|
||||
if model.startswith("gpt-oss"):
|
||||
optional_params["think"] = value
|
||||
else:
|
||||
optional_params["think"] = value in {"low", "medium", "high"}
|
||||
effort: Final = value.get("effort") if isinstance(value, dict) else value
|
||||
if effort is not None:
|
||||
if model.startswith("gpt-oss"):
|
||||
optional_params["think"] = effort
|
||||
else:
|
||||
optional_params["think"] = effort in {"low", "medium", "high"}
|
||||
### FUNCTION CALLING LOGIC ###
|
||||
# Ollama 0.4+ supports native tool calling - pass tools directly
|
||||
# and let Ollama handle model capability detection
|
||||
|
|
|
|||
|
|
@ -215,10 +215,12 @@ class OllamaConfig(BaseConfig):
|
|||
elif param == "stop":
|
||||
optional_params["stop"] = value
|
||||
elif param == "reasoning_effort" and value is not None:
|
||||
if model.startswith("gpt-oss"):
|
||||
optional_params["think"] = value
|
||||
else:
|
||||
optional_params["think"] = value in {"low", "medium", "high"}
|
||||
effort: Final = value.get("effort") if isinstance(value, dict) else value
|
||||
if effort is not None:
|
||||
if model.startswith("gpt-oss"):
|
||||
optional_params["think"] = effort
|
||||
else:
|
||||
optional_params["think"] = effort in {"low", "medium", "high"}
|
||||
elif param == "response_format" and isinstance(value, dict):
|
||||
if value["type"] == "json_object":
|
||||
optional_params["format"] = "json"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import inspect
|
||||
import os
|
||||
import sys
|
||||
from typing import cast
|
||||
from typing import Final, cast
|
||||
|
||||
import pytest
|
||||
from pydantic import BaseModel
|
||||
|
|
@ -989,3 +989,43 @@ class TestOllamaStreamingUsage:
|
|||
)
|
||||
|
||||
assert result.usage is None
|
||||
|
||||
|
||||
class TestOllamaChatReasoningEffort:
|
||||
@pytest.mark.parametrize(
|
||||
"reasoning_effort, expected_think",
|
||||
[
|
||||
("low", True),
|
||||
("high", True),
|
||||
("none", False),
|
||||
({"effort": "medium"}, True),
|
||||
({"effort": "medium", "summary": "auto"}, True),
|
||||
({"effort": "none", "summary": "detailed"}, False),
|
||||
],
|
||||
)
|
||||
def test_reasoning_effort_string_or_dict_maps_to_think(
|
||||
self, reasoning_effort: str | dict[str, str], expected_think: bool
|
||||
) -> None:
|
||||
optional_params: Final = get_optional_params(
|
||||
model="ollama_chat/qwen3:8b",
|
||||
custom_llm_provider="ollama_chat",
|
||||
reasoning_effort=reasoning_effort,
|
||||
)
|
||||
assert optional_params["think"] is expected_think
|
||||
|
||||
def test_reasoning_dict_without_effort_sets_nothing(self) -> None:
|
||||
optional_params: Final = get_optional_params(
|
||||
model="ollama_chat/qwen3:8b",
|
||||
custom_llm_provider="ollama_chat",
|
||||
reasoning_effort={"summary": "auto"},
|
||||
)
|
||||
assert "think" not in optional_params
|
||||
|
||||
def test_reasoning_dict_gpt_oss_forwards_effort_string(self) -> None:
|
||||
optional_params: Final = OllamaChatConfig().map_openai_params(
|
||||
non_default_params={"reasoning_effort": {"effort": "high", "summary": "auto"}},
|
||||
optional_params={},
|
||||
model="gpt-oss:20b",
|
||||
drop_params=False,
|
||||
)
|
||||
assert optional_params["think"] == "high"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import base64
|
|||
import io
|
||||
import json
|
||||
import sys
|
||||
from typing import Final
|
||||
from litellm._uuid import uuid
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
|
|
@ -775,3 +776,42 @@ def test_transform_request_leaves_unreadable_images_untouched(payload: str) -> N
|
|||
data = _transform_image_request(payload, "png")
|
||||
|
||||
assert data["images"] == [payload]
|
||||
|
||||
|
||||
class TestOllamaConfigReasoningEffort:
|
||||
@pytest.mark.parametrize(
|
||||
"reasoning_effort, expected_think",
|
||||
[
|
||||
("medium", True),
|
||||
({"effort": "medium", "summary": "auto"}, True),
|
||||
({"effort": "none"}, False),
|
||||
],
|
||||
)
|
||||
def test_reasoning_effort_string_or_dict_maps_to_think(
|
||||
self, reasoning_effort: str | dict[str, str], expected_think: bool
|
||||
) -> None:
|
||||
optional_params: Final = OllamaConfig().map_openai_params(
|
||||
non_default_params={"reasoning_effort": reasoning_effort},
|
||||
optional_params={},
|
||||
model="qwen3:8b",
|
||||
drop_params=False,
|
||||
)
|
||||
assert optional_params["think"] is expected_think
|
||||
|
||||
def test_reasoning_dict_gpt_oss_forwards_effort_string(self) -> None:
|
||||
optional_params: Final = OllamaConfig().map_openai_params(
|
||||
non_default_params={"reasoning_effort": {"effort": "high", "summary": "auto"}},
|
||||
optional_params={},
|
||||
model="gpt-oss:20b",
|
||||
drop_params=False,
|
||||
)
|
||||
assert optional_params["think"] == "high"
|
||||
|
||||
def test_reasoning_dict_without_effort_sets_nothing(self) -> None:
|
||||
optional_params: Final = OllamaConfig().map_openai_params(
|
||||
non_default_params={"reasoning_effort": {"summary": "auto"}},
|
||||
optional_params={},
|
||||
model="qwen3:8b",
|
||||
drop_params=False,
|
||||
)
|
||||
assert "think" not in optional_params
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue