mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-14 09:03:16 +00:00
Merge 61d3407923 into f5e740e69a
This commit is contained in:
commit
b1ebaf131a
3 changed files with 99 additions and 17 deletions
|
|
@ -21,6 +21,25 @@ if TYPE_CHECKING:
|
|||
from litellm.llms.openai_like.responses.transformation import OpenAILikeResponsesConfig
|
||||
|
||||
|
||||
def _model_map_function_calling(model: str, custom_llm_provider: str) -> bool | None:
|
||||
from litellm.utils import get_model_info_helper
|
||||
|
||||
try:
|
||||
value: Final = get_model_info_helper(model=model, custom_llm_provider=custom_llm_provider).get(
|
||||
"supports_function_calling"
|
||||
)
|
||||
except Exception: # noqa: BLE001 # get_model_info_helper raises bare Exception for unmapped models
|
||||
return None
|
||||
return value if isinstance(value, bool) else None
|
||||
|
||||
|
||||
def _function_calling_disabled(model: str, provider: SimpleProviderConfig) -> bool:
|
||||
from_model_map: Final = _model_map_function_calling(model=model, custom_llm_provider=provider.slug)
|
||||
if from_model_map is not None:
|
||||
return not from_model_map
|
||||
return provider.provider_info.get("supports_function_calling") is False
|
||||
|
||||
|
||||
def create_config_class(provider: SimpleProviderConfig):
|
||||
"""Generate config class dynamically from JSON configuration"""
|
||||
|
||||
|
|
@ -107,14 +126,12 @@ def create_config_class(provider: SimpleProviderConfig):
|
|||
|
||||
def get_supported_openai_params(self, model: str) -> list:
|
||||
"""Get supported OpenAI params, excluding tool-related params for models
|
||||
that don't support function calling."""
|
||||
from litellm.utils import supports_function_calling, supports_reasoning
|
||||
the model map, or failing that the provider, marks as not supporting function calling."""
|
||||
from litellm.utils import supports_reasoning
|
||||
|
||||
supported_params: Final = super().get_supported_openai_params(model=model)
|
||||
|
||||
_supports_fc: Final = supports_function_calling(model=model, custom_llm_provider=provider.slug)
|
||||
|
||||
if not _supports_fc:
|
||||
if _function_calling_disabled(model=model, provider=provider):
|
||||
tool_params: Final = [
|
||||
"tools",
|
||||
"tool_choice",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
from typing import Final
|
||||
|
||||
import pytest
|
||||
|
||||
import litellm
|
||||
from litellm.llms.openai_like import dynamic_config
|
||||
from litellm.llms.openai_like.dynamic_config import create_responses_config_class
|
||||
from litellm.llms.openai_like.dynamic_config import create_config_class, create_responses_config_class
|
||||
from litellm.llms.openai_like.json_loader import SimpleProviderConfig
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
|
||||
|
|
@ -139,3 +142,58 @@ class TestForceStoreFalse:
|
|||
headers={},
|
||||
)
|
||||
assert params["store"] is True
|
||||
|
||||
|
||||
class TestToolParamsFollowTheModelMap:
|
||||
_TOOLS: Final = [{"type": "function", "function": {"name": "get_weather", "parameters": {"type": "object"}}}]
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("model_supports_fc", "provider_supports_fc", "tools_forwarded"),
|
||||
[
|
||||
(None, None, True),
|
||||
(None, False, False),
|
||||
(None, True, True),
|
||||
(True, None, True),
|
||||
(False, None, False),
|
||||
(True, False, True),
|
||||
(False, True, False),
|
||||
],
|
||||
ids=[
|
||||
"unmapped",
|
||||
"unmapped_provider_unsupported",
|
||||
"unmapped_provider_supported",
|
||||
"mapped_as_supported",
|
||||
"mapped_as_unsupported",
|
||||
"model_supported_overrides_provider",
|
||||
"model_unsupported_overrides_provider",
|
||||
],
|
||||
)
|
||||
def test_tools_are_dropped_only_when_the_model_or_provider_says_unsupported(
|
||||
self,
|
||||
model_supports_fc: bool | None,
|
||||
provider_supports_fc: bool | None,
|
||||
tools_forwarded: bool,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
if model_supports_fc is not None:
|
||||
monkeypatch.setitem(
|
||||
litellm.model_cost,
|
||||
"tools_map/some-model",
|
||||
{"litellm_provider": "tools_map", "mode": "chat", "supports_function_calling": model_supports_fc},
|
||||
)
|
||||
provider_overrides: Final = (
|
||||
{}
|
||||
if provider_supports_fc is None
|
||||
else {"provider_info": {"supports_function_calling": provider_supports_fc}}
|
||||
)
|
||||
config: Final = create_config_class(_provider("tools_map", **provider_overrides))()
|
||||
|
||||
optional_params: Final = config.map_openai_params(
|
||||
non_default_params={"tools": self._TOOLS, "tool_choice": "auto"},
|
||||
optional_params={},
|
||||
model="some-model",
|
||||
drop_params=True,
|
||||
)
|
||||
|
||||
assert ("tools" in optional_params) is tools_forwarded
|
||||
assert ("tool_choice" in optional_params) is tools_forwarded
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ Tests for JSON-based provider configuration system.
|
|||
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import patch
|
||||
|
||||
try:
|
||||
import pytest
|
||||
|
|
@ -94,20 +93,24 @@ class TestJSONProviderLoader:
|
|||
assert isinstance(supported, list)
|
||||
assert len(supported) > 0
|
||||
|
||||
def test_tool_params_excluded_when_function_calling_not_supported(self):
|
||||
"""Test that tool-related params are excluded for models that don't support
|
||||
function calling. Regression test for https://github.com/BerriAI/litellm/issues/21125
|
||||
def test_tool_params_excluded_when_function_calling_not_supported(self, monkeypatch):
|
||||
"""Test that tool-related params are excluded for models the model map marks as not
|
||||
supporting function calling. Regression test for https://github.com/BerriAI/litellm/issues/21125
|
||||
"""
|
||||
import litellm
|
||||
from litellm.llms.openai_like.dynamic_config import create_config_class
|
||||
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
||||
|
||||
monkeypatch.setitem(
|
||||
litellm.model_cost,
|
||||
"publicai/some-model-without-fc",
|
||||
{"litellm_provider": "publicai", "mode": "chat", "supports_function_calling": False},
|
||||
)
|
||||
provider = JSONProviderRegistry.get("publicai")
|
||||
config_class = create_config_class(provider)
|
||||
config = config_class()
|
||||
|
||||
# Mock supports_function_calling to return False
|
||||
with patch("litellm.utils.supports_function_calling", return_value=False):
|
||||
supported = config.get_supported_openai_params("some-model-without-fc")
|
||||
supported = config.get_supported_openai_params("some-model-without-fc")
|
||||
|
||||
tool_params = [
|
||||
"tools",
|
||||
|
|
@ -126,18 +129,22 @@ class TestJSONProviderLoader:
|
|||
assert "max_tokens" in supported
|
||||
assert "stop" in supported
|
||||
|
||||
def test_tool_params_included_when_function_calling_supported(self):
|
||||
def test_tool_params_included_when_function_calling_supported(self, monkeypatch):
|
||||
"""Test that tool-related params are included for models that support function calling."""
|
||||
import litellm
|
||||
from litellm.llms.openai_like.dynamic_config import create_config_class
|
||||
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
||||
|
||||
monkeypatch.setitem(
|
||||
litellm.model_cost,
|
||||
"publicai/some-model-with-fc",
|
||||
{"litellm_provider": "publicai", "mode": "chat", "supports_function_calling": True},
|
||||
)
|
||||
provider = JSONProviderRegistry.get("publicai")
|
||||
config_class = create_config_class(provider)
|
||||
config = config_class()
|
||||
|
||||
# Mock supports_function_calling to return True
|
||||
with patch("litellm.utils.supports_function_calling", return_value=True):
|
||||
supported = config.get_supported_openai_params("some-model-with-fc")
|
||||
supported = config.get_supported_openai_params("some-model-with-fc")
|
||||
|
||||
assert "tools" in supported
|
||||
assert "tool_choice" in supported
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue