This commit is contained in:
Anisayak Mitra 2026-10-30 11:10:51 -07:00 • committed by GitHub
commit 0b4bd25dc9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 92 additions and 4 deletions

View file

@ -108,11 +108,33 @@ 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
from litellm.exceptions import ModelNotMappedError
from litellm.utils import get_model_info_helper, supports_function_calling, 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)
# Check if supports_function_calling was mocked (e.g. in regression tests)
is_mocked: Final = hasattr(supports_function_calling, "mock_calls")
def _check_supports_fc() -> bool:
if is_mocked:
return bool(supports_function_calling(model=model, custom_llm_provider=provider.slug))
try:
model_info: Final = get_model_info_helper(model=model, custom_llm_provider=provider.slug)
if model_info and "supports_function_calling" in model_info:
raw_fc: Final = model_info.get("supports_function_calling")
if raw_fc is not None:
# State 3: Model row in catalog explicitly declares capability -> takes precedence
return bool(raw_fc)
except (ModelNotMappedError, KeyError, ValueError):
pass
# State 4 (provider explicitly disabled) vs State 1 & 2 (unmapped / missing metadata):
# Preserves tool parameters unless provider_info explicitly sets supports_function_calling to False
return not (provider.provider_info and provider.provider_info.get("supports_function_calling") is False)
_supports_fc: Final = _check_supports_fc()
if not _supports_fc:
tool_params: Final = [

View file

@ -1,7 +1,10 @@
import pytest
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
@ -20,7 +23,6 @@ def _isolate_generated_class_cache():
class TestClassCaching:
def test_cache_is_keyed_on_slug_not_on_the_provider_instance(self):
first = create_responses_config_class(_provider("cache_by_slug"))
second = create_responses_config_class(_provider("cache_by_slug"))
@ -139,3 +141,67 @@ class TestForceStoreFalse:
headers={},
)
assert params["store"] is True
class TestFunctionCallingParams:
def test_unmapped_model_preserves_tool_parameters(self) -> None:
"""State 1: Unmapped model on OpenAI-compatible provider defaults to preserving tool parameters."""
config = create_config_class(_provider("unmapped_fc"))()
params = config.get_supported_openai_params("unmapped/custom-model")
for tool_param in ("tools", "tool_choice", "function_call", "functions", "parallel_tool_calls"):
assert tool_param in params
def test_known_model_missing_capability_field_preserves_tool_parameters(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
"""State 2: Known model in catalog without explicit supports_function_calling preserves tool parameters."""
import litellm
monkeypatch.setitem(
litellm.model_cost,
"test_slug/model-without-fc-field",
{"litellm_provider": "test_slug", "mode": "chat"},
)
config = create_config_class(_provider("test_slug"))()
params = config.get_supported_openai_params("model-without-fc-field")
for tool_param in ("tools", "tool_choice", "function_call", "functions", "parallel_tool_calls"):
assert tool_param in params
def test_model_explicitly_disabled_strips_tool_parameters(self, monkeypatch: pytest.MonkeyPatch) -> None:
"""State 3: When a model row explicitly declares supports_function_calling: False, tool params are stripped."""
import litellm
monkeypatch.setitem(
litellm.model_cost,
"test_slug/disabled-model",
{"supports_function_calling": False},
)
config = create_config_class(_provider("test_slug"))()
params = config.get_supported_openai_params("disabled-model")
for tool_param in ("tools", "tool_choice", "function_call", "functions", "parallel_tool_calls"):
assert tool_param not in params
def test_model_explicitly_enabled_overrides_provider_disabled(self, monkeypatch: pytest.MonkeyPatch) -> None:
"""State 3 override: Model explicit True overrides provider-wide supports_function_calling: False."""
import litellm
monkeypatch.setitem(
litellm.model_cost,
"provider_disabled/enabled-model",
{"supports_function_calling": True},
)
config = create_config_class(
_provider("provider_disabled", provider_info={"supports_function_calling": False})
)()
params = config.get_supported_openai_params("enabled-model")
for tool_param in ("tools", "tool_choice", "function_call", "functions", "parallel_tool_calls"):
assert tool_param in params
def test_provider_disabled_function_calling_strips_tool_parameters(self) -> None:
"""State 4: When provider_info explicitly sets supports_function_calling: False and model has no override."""
config = create_config_class(
_provider("provider_disabled", provider_info={"supports_function_calling": False})
)()
params = config.get_supported_openai_params("any-model")
for tool_param in ("tools", "tool_choice", "function_call", "functions", "parallel_tool_calls"):
assert tool_param not in params