litellm/tests/unit/rust_bridge/test_model_capabilities.py
yujonglee ee295aea68
refactor(rust): give Messages configs typed litellm params (#45611)
* refactor(rust): give Messages configs typed litellm params

* feat(rust): port _normalize_system_role_messages for Messages hosts

mid_conversation_system mirrors Python's module function for function: a
leading run of system turns is hoisted into the top-level system, a later
turn stays in place when the model map flags supports_mid_conversation_system
and otherwise becomes a user turn where it was, never between a tool_use and
its tool_result, and billing blocks are stripped from the hoisted system. The
capability joins MessagesModelCapabilities and the bridge projection. Azure AI
uses it in place of fold_system_role_messages, which hoisted every turn.

* fix(rust): keep the Bedrock runtime endpoint behind a blank api_base

* refactor(rust): import LitellmParams from its crate instead of a re-export

* refactor(rust): template the Messages request decode errors

Both decode failures go through ErrorDetail::invalid with the subject and the
serde error as source instead of a preformatted sentence.

* refactor(rust_bridge): read the module globals the param specs name

The Messages host no longer keeps its own list of litellm globals; a spec
whose spellings the call leaves out names the global to read, so the next
family that has one needs no host change.

* refactor(rust): give each route lookup Python's implicit None

ProviderConfigManager's per-route methods list only the providers the route
serves and fall through to None; the Rust lookups now do the same, so a new
LlmProviders variant touches only the route that serves it.
2026-10-09 16:29:45 -07:00

76 lines
2.7 KiB
Python

from typing import Final
import pytest
import litellm
from litellm.rust_bridge.model_capabilities import anthropic_model_capabilities
pytestmark = pytest.mark.usefixtures("local_model_cost_map")
def _flag_model(monkeypatch: pytest.MonkeyPatch, name: str, **flags: bool) -> None:
monkeypatch.setitem(
litellm.model_cost,
name,
{
"litellm_provider": "anthropic",
"mode": "chat",
"input_cost_per_token": 0,
"output_cost_per_token": 0,
**flags,
},
)
def test_capabilities_come_from_the_model_map_under_the_callers_provider(monkeypatch: pytest.MonkeyPatch) -> None:
_flag_model(
monkeypatch,
"claude-test-adaptive",
supports_reasoning=True,
supports_adaptive_thinking=True,
supports_output_config=True,
supports_xhigh_reasoning_effort=True,
supports_sampling_params=False,
supports_mid_conversation_system=True,
)
capabilities: Final = anthropic_model_capabilities("anthropic/claude-test-adaptive", None)
assert capabilities["supports_adaptive_thinking"]
assert capabilities["supports_output_config"]
assert capabilities["supports_mid_conversation_system"]
assert not capabilities["supports_legacy_thinking"]
assert not capabilities["supports_sampling_params"]
assert capabilities["effort_tiers"] == {
"minimal": False,
"low": False,
"medium": False,
"high": False,
"xhigh": True,
"max": False,
}
def test_unmapped_model_keeps_sampling_params_and_no_reasoning_features() -> None:
capabilities: Final = anthropic_model_capabilities("anthropic/not-a-real-model", None)
assert capabilities["supports_sampling_params"]
assert not capabilities["supports_reasoning"]
assert not capabilities["supports_adaptive_thinking"]
assert not capabilities["supports_mid_conversation_system"]
assert capabilities["effort_tiers"] == dict.fromkeys(("minimal", "low", "medium", "high", "xhigh", "max"), False)
def test_capability_source_observes_runtime_registration_changes(monkeypatch: pytest.MonkeyPatch) -> None:
model: Final = "claude-test-runtime-registration"
_flag_model(monkeypatch, model, supports_reasoning=True, supports_output_config=True)
before: Final = anthropic_model_capabilities(model, "anthropic")
_flag_model(monkeypatch, model, supports_reasoning=False, supports_output_config=False)
after: Final = anthropic_model_capabilities(model, "anthropic")
assert before["supports_reasoning"] is True
assert before["supports_output_config"] is True
assert after["supports_reasoning"] is False
assert after["supports_output_config"] is False