mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
Merge ee86931b0d into e768ad55ce
This commit is contained in:
commit
ed8cffe694
5 changed files with 122 additions and 14 deletions
|
|
@ -28,7 +28,7 @@ from litellm.types.llms.openai import (
|
|||
)
|
||||
from litellm.types.utils import ModelResponse, ModelResponseStream
|
||||
|
||||
from ..common_utils import OllamaError
|
||||
from ..common_utils import OllamaError, think_from_reasoning_effort
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
|
||||
|
|
@ -170,11 +170,17 @@ class OllamaChatConfig(BaseConfig):
|
|||
if param == "response_format" and isinstance(value, dict) and value.get("type") == "json_schema":
|
||||
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"}
|
||||
if (
|
||||
param == "reasoning_effort"
|
||||
and (
|
||||
think := think_from_reasoning_effort(
|
||||
model,
|
||||
cast(object, value), # cast-ok: [LIT006] untyped dict value, widened to object for the helper
|
||||
)
|
||||
)
|
||||
is not None
|
||||
):
|
||||
optional_params["think"] = think
|
||||
### FUNCTION CALLING LOGIC ###
|
||||
# Ollama 0.4+ supports native tool calling - pass tools directly
|
||||
# and let Ollama handle model capability detection
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import base64
|
||||
import io
|
||||
from collections.abc import Mapping
|
||||
from typing import Any, Final
|
||||
|
||||
import httpx
|
||||
|
|
@ -8,6 +9,15 @@ from litellm import verbose_logger
|
|||
from litellm.llms.base_llm.chat.transformation import BaseLLMException
|
||||
|
||||
|
||||
def think_from_reasoning_effort(model: str, reasoning_effort: object) -> str | bool | None:
|
||||
effort: Final = reasoning_effort.get("effort") if isinstance(reasoning_effort, Mapping) else reasoning_effort
|
||||
if not isinstance(effort, str):
|
||||
return None
|
||||
if model.startswith("gpt-oss"):
|
||||
return effort
|
||||
return effort in {"low", "medium", "high"}
|
||||
|
||||
|
||||
class OllamaError(BaseLLMException):
|
||||
def __init__(self, status_code: int, message: str, headers: dict | httpx.Headers):
|
||||
super().__init__(status_code=status_code, message=message, headers=headers)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import json
|
||||
import time
|
||||
from collections.abc import AsyncIterator, Iterator
|
||||
from typing import TYPE_CHECKING, Any, Final
|
||||
from typing import TYPE_CHECKING, Any, Final, cast
|
||||
|
||||
from httpx._models import Headers, Response
|
||||
from pydantic import BaseModel, ConfigDict, ValidationError
|
||||
|
|
@ -33,7 +33,7 @@ from litellm.types.utils import (
|
|||
StreamingChoices,
|
||||
)
|
||||
|
||||
from ..common_utils import OllamaError, OllamaModelInfo, _convert_image
|
||||
from ..common_utils import OllamaError, OllamaModelInfo, _convert_image, think_from_reasoning_effort
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
|
||||
|
|
@ -214,11 +214,17 @@ class OllamaConfig(BaseConfig):
|
|||
optional_params["frequency_penalty"] = value
|
||||
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"}
|
||||
elif (
|
||||
param == "reasoning_effort"
|
||||
and (
|
||||
think := think_from_reasoning_effort(
|
||||
model,
|
||||
cast(object, value), # cast-ok: [LIT006] untyped dict value, widened to object for the helper
|
||||
)
|
||||
)
|
||||
is not None
|
||||
):
|
||||
optional_params["think"] = think
|
||||
elif param == "response_format" and isinstance(value, dict):
|
||||
if value["type"] == "json_object":
|
||||
optional_params["format"] = "json"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import inspect
|
||||
import os
|
||||
import sys
|
||||
from typing import cast
|
||||
from collections.abc import Mapping
|
||||
from types import MappingProxyType
|
||||
from typing import Final, cast
|
||||
|
||||
import pytest
|
||||
from pydantic import BaseModel
|
||||
|
|
@ -989,3 +991,44 @@ 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),
|
||||
(MappingProxyType({"effort": "medium", "summary": "auto"}), True),
|
||||
({"effort": "none", "summary": "detailed"}, False),
|
||||
],
|
||||
)
|
||||
def test_reasoning_effort_string_or_dict_maps_to_think(
|
||||
self, reasoning_effort: str | Mapping[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,9 @@ import base64
|
|||
import io
|
||||
import json
|
||||
import sys
|
||||
from collections.abc import Mapping
|
||||
from types import MappingProxyType
|
||||
from typing import Final
|
||||
from litellm._uuid import uuid
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
|
|
@ -775,3 +778,43 @@ 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),
|
||||
(MappingProxyType({"effort": "medium", "summary": "auto"}), True),
|
||||
({"effort": "none"}, False),
|
||||
],
|
||||
)
|
||||
def test_reasoning_effort_string_or_dict_maps_to_think(
|
||||
self, reasoning_effort: str | Mapping[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