mirror of
https://github.com/usestrix/strix.git
synced 2026-11-15 09:11:26 +00:00
Merge 3f79c1098d into cb581bf93d
This commit is contained in:
commit
b76847c50d
3 changed files with 166 additions and 8 deletions
|
|
@ -1016,22 +1016,30 @@ def model_supports_images(model_name: str) -> bool:
|
|||
|
||||
|
||||
def _bare_openai_name(model_name: str) -> str:
|
||||
name = model_name.strip().lower()
|
||||
name = model_name.strip()
|
||||
normalized = name.lower()
|
||||
for prefix in ("litellm/", "any-llm/", "openai/"):
|
||||
if name.startswith(prefix):
|
||||
if normalized.startswith(prefix):
|
||||
return name[len(prefix) :]
|
||||
return name
|
||||
|
||||
|
||||
def _catalog_entry(model_name: str) -> dict[str, Any] | None:
|
||||
"""LiteLLM's cost-map entry for the model, looked up as it would route it."""
|
||||
"""LiteLLM's case-sensitive cost-map entry for the model and its aliases."""
|
||||
import litellm
|
||||
|
||||
name = _bare_openai_name(model_name)
|
||||
entry = litellm.model_cost.get(name)
|
||||
if entry is None and "/" in name:
|
||||
entry = litellm.model_cost.get(name.rsplit("/", 1)[1])
|
||||
return entry if isinstance(entry, dict) else None
|
||||
supplied_name = model_name.strip()
|
||||
routed_name = _bare_openai_name(supplied_name)
|
||||
candidates = [supplied_name, routed_name, routed_name.lower()]
|
||||
if "/" in routed_name:
|
||||
bare_name = routed_name.rsplit("/", 1)[1]
|
||||
candidates.extend((bare_name, bare_name.lower()))
|
||||
|
||||
for candidate in dict.fromkeys(candidates):
|
||||
entry = litellm.model_cost.get(candidate)
|
||||
if entry is not None:
|
||||
return entry if isinstance(entry, dict) else None
|
||||
return None
|
||||
|
||||
|
||||
def is_known_openai_bare_model(model_name: str) -> bool:
|
||||
|
|
|
|||
|
|
@ -156,6 +156,22 @@ def test_max_reasoning_effort_sent_as_raw_body_field() -> None:
|
|||
assert settings.extra_body == {"reasoning_effort": "max"}
|
||||
|
||||
|
||||
def test_mixed_case_reasoning_capability_reaches_model_settings(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
model = "deepinfra/deepseek-ai/DeepSeek-V4-Flash-0731"
|
||||
monkeypatch.setattr(
|
||||
litellm,
|
||||
"model_cost",
|
||||
{model: {"supports_reasoning": True}},
|
||||
)
|
||||
|
||||
settings = make_model_settings("high", model_name=model)
|
||||
|
||||
assert settings.reasoning is not None
|
||||
assert settings.reasoning.effort == "high"
|
||||
|
||||
|
||||
def test_conversation_tail_breakpoint_moves_with_appended_transcript() -> None:
|
||||
# LiteLLM must place the index=-1 cache_control on the last message however
|
||||
# long the transcript grows.
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ from strix.config.models import (
|
|||
_TurnGuardModel,
|
||||
configure_sdk_model_defaults,
|
||||
model_supports_images,
|
||||
model_supports_reasoning,
|
||||
request_timeout_extra_args,
|
||||
resolve_api_type,
|
||||
routes_through_litellm,
|
||||
|
|
@ -211,3 +212,136 @@ def test_image_support_follows_the_catalog(monkeypatch: pytest.MonkeyPatch) -> N
|
|||
assert model_supports_images("acme-text") is False
|
||||
assert model_supports_images("litellm/openai/acme-vision") is True
|
||||
assert model_supports_images("acme-unknown-model") is True
|
||||
|
||||
|
||||
_MIXED_CASE_MODEL = "deepinfra/deepseek-ai/DeepSeek-V4-Flash-0731"
|
||||
|
||||
|
||||
def test_mixed_case_model_capabilities_follow_the_pinned_litellm_catalog() -> None:
|
||||
"""Integration check against the real catalog pinned by uv.lock."""
|
||||
entry = litellm.model_cost[_MIXED_CASE_MODEL]
|
||||
|
||||
assert entry["supports_reasoning"] is True
|
||||
assert entry["supports_vision"] is False
|
||||
assert model_supports_reasoning(_MIXED_CASE_MODEL) is True
|
||||
assert model_supports_images(_MIXED_CASE_MODEL) is False
|
||||
|
||||
|
||||
@pytest.mark.parametrize("route_prefix", ["litellm/", "any-llm/", "openai/"])
|
||||
def test_catalog_lookup_strips_supported_route_prefix_without_losing_case(
|
||||
monkeypatch: pytest.MonkeyPatch, route_prefix: str
|
||||
) -> None:
|
||||
monkeypatch.setattr(
|
||||
litellm,
|
||||
"model_cost",
|
||||
{"vendor/MixedCase": {"supports_reasoning": True, "supports_vision": False}},
|
||||
)
|
||||
|
||||
model = f"{route_prefix}vendor/MixedCase"
|
||||
assert model_supports_reasoning(model) is True
|
||||
assert model_supports_images(model) is False
|
||||
|
||||
|
||||
def test_catalog_lookup_prefers_exact_key_over_normalized_and_bare_fallbacks(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
model = "openai/vendor/MixedCase"
|
||||
monkeypatch.setattr(
|
||||
litellm,
|
||||
"model_cost",
|
||||
{
|
||||
model: {
|
||||
"supports_reasoning": False,
|
||||
"supports_vision": False,
|
||||
"supported_endpoints": ["/v1/chat/completions"],
|
||||
},
|
||||
model.lower(): {
|
||||
"supports_reasoning": True,
|
||||
"supports_vision": True,
|
||||
"supported_endpoints": ["/v1/responses"],
|
||||
},
|
||||
"MixedCase": {
|
||||
"supports_reasoning": True,
|
||||
"supports_vision": True,
|
||||
"supported_endpoints": ["/v1/responses"],
|
||||
},
|
||||
},
|
||||
)
|
||||
settings = _settings(monkeypatch, model, None)
|
||||
|
||||
assert model_supports_reasoning(model) is False
|
||||
assert model_supports_images(model) is False
|
||||
assert resolve_api_type(model, settings) == "chat_completions"
|
||||
assert uses_chat_completions_tool_schema(model, settings) is True
|
||||
|
||||
|
||||
def test_catalog_lookup_prefers_case_preserving_stripped_key_over_normalized_fallback(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.setattr(
|
||||
litellm,
|
||||
"model_cost",
|
||||
{
|
||||
"vendor/MixedCase": {"supports_reasoning": False, "supports_vision": False},
|
||||
"vendor/mixedcase": {"supports_reasoning": True, "supports_vision": True},
|
||||
},
|
||||
)
|
||||
|
||||
model = "litellm/vendor/MixedCase"
|
||||
assert model_supports_reasoning(model) is False
|
||||
assert model_supports_images(model) is False
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("model", "catalog_key"),
|
||||
[
|
||||
("vendor/lowercase", "vendor/lowercase"),
|
||||
("vendor/MixedCase", "vendor/mixedcase"),
|
||||
("vendor/MixedCase", "MixedCase"),
|
||||
],
|
||||
ids=["lowercase-exact", "normalized-fallback", "bare-alias"],
|
||||
)
|
||||
def test_catalog_lookup_compatibility_fallbacks(
|
||||
monkeypatch: pytest.MonkeyPatch, model: str, catalog_key: str
|
||||
) -> None:
|
||||
monkeypatch.setattr(
|
||||
litellm,
|
||||
"model_cost",
|
||||
{catalog_key: {"supports_reasoning": True, "supports_vision": False}},
|
||||
)
|
||||
|
||||
assert model_supports_reasoning(model) is True
|
||||
assert model_supports_images(model) is False
|
||||
|
||||
|
||||
def test_unknown_model_capability_defaults_are_unchanged(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(litellm, "model_cost", {})
|
||||
settings = _settings(monkeypatch, "openai/UnknownMixedCase", None)
|
||||
|
||||
assert model_supports_reasoning("openai/UnknownMixedCase") is False
|
||||
assert model_supports_images("openai/UnknownMixedCase") is True
|
||||
assert resolve_api_type("openai/UnknownMixedCase", settings) == "chat_completions"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("model", "supported_endpoints", "expected"),
|
||||
[
|
||||
("openai/AcmeResponses", ["/v1/responses"], "responses"),
|
||||
("openai/AcmeChat", ["/v1/chat/completions"], "chat_completions"),
|
||||
],
|
||||
)
|
||||
def test_api_type_follows_exact_mixed_case_catalog_capability(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
model: str,
|
||||
supported_endpoints: list[str],
|
||||
expected: str,
|
||||
) -> None:
|
||||
monkeypatch.setattr(
|
||||
litellm,
|
||||
"model_cost",
|
||||
{model: {"supported_endpoints": supported_endpoints}},
|
||||
)
|
||||
settings = _settings(monkeypatch, model, None)
|
||||
|
||||
assert resolve_api_type(model, settings) == expected
|
||||
assert uses_chat_completions_tool_schema(model, settings) is (expected == "chat_completions")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue