mirror of
https://github.com/usestrix/strix.git
synced 2026-09-14 23:21:15 +00:00
Add Claude Fable 5.1, Gemini 3.7 Flash, and Z.ai GLM-5.3 / GLM-5.3-Flash to RECOMMENDED_MODEL_NAMES, add a Z.ai GLM frontier family so GLM-5.x is accepted through OpenRouter and Novita routes, and drop the superseded GPT-5.4, GPT-5.3-codex, Opus 4.8, Sonnet 4.6, Gemini 3.6 Flash, and Qwen3.7 entries. Update the README, docs provider pages, quickstart, and CLI hint strings to the same current models, including DeepSeek V4, Kimi K3, and GLM-5.3.
164 lines
5.4 KiB
Python
164 lines
5.4 KiB
Python
"""Tests for LLM model recommendation helpers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
from agents.extensions.models.litellm_model import LitellmModel
|
|
from agents.model_settings import ModelSettings
|
|
|
|
from strix.config.models import (
|
|
RECOMMENDED_MODEL_NAMES,
|
|
StrixProvider,
|
|
_NonStreamingModel,
|
|
_TurnGuardModel,
|
|
is_recommended_or_frontier_model,
|
|
request_timeout_extra_args,
|
|
routes_through_litellm,
|
|
supports_strict_tool_schemas,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("model_name", RECOMMENDED_MODEL_NAMES)
|
|
def test_recommended_models_are_accepted(model_name: str) -> None:
|
|
assert is_recommended_or_frontier_model(model_name)
|
|
|
|
|
|
def test_request_timeout_extra_args_positive() -> None:
|
|
assert request_timeout_extra_args(300) == {"timeout": 300}
|
|
assert request_timeout_extra_args(10) == {"timeout": 10}
|
|
|
|
|
|
def test_request_timeout_extra_args_survives_model_settings_json_dump() -> None:
|
|
"""The Chat Completions and LiteLLM paths pydantic-serialize ModelSettings for
|
|
their tracing span; a non-JSON-serializable timeout fails every turn there."""
|
|
settings = ModelSettings(extra_args=request_timeout_extra_args(300))
|
|
assert settings.to_json_dict()["extra_args"] == {"timeout": 300}
|
|
|
|
|
|
@pytest.mark.parametrize("value", [None, 0, -1])
|
|
def test_request_timeout_extra_args_disabled(value: float | None) -> None:
|
|
assert request_timeout_extra_args(value) is None
|
|
|
|
|
|
def test_recommended_models_are_matched_case_insensitively() -> None:
|
|
assert is_recommended_or_frontier_model("Vertex_AI/Gemini-3-Pro-Preview")
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"model_name",
|
|
[
|
|
"gpt-5.5",
|
|
"chatgpt/gpt-5.4",
|
|
"litellm/openai/gpt-5.4-pro",
|
|
"azure_ai/gpt-5.5-pro",
|
|
"bedrock_mantle/openai.gpt-5.5",
|
|
"anthropic/claude-opus-5",
|
|
"anthropic/claude-opus-4-8",
|
|
"anthropic.claude-opus-4-8",
|
|
"anthropic/claude-opus-4-7",
|
|
"anthropic/claude-fable-5",
|
|
"anthropic/claude-sonnet-5",
|
|
"vertex_ai/claude-sonnet-5@default",
|
|
"vertex_ai/claude-sonnet-4-6@default",
|
|
"any-llm/anthropic/claude-sonnet-4-6",
|
|
"vertex_ai/gemini-3.1-pro-preview",
|
|
"openrouter/google/gemini-3.1-pro-preview",
|
|
"deepseek/deepseek-v4-pro",
|
|
"deepseek/deepseek-r1-0528",
|
|
"deepseek/deepseek-reasoner",
|
|
"dashscope/qwen3-max-2026-01-23",
|
|
"qwen3.7-max",
|
|
"dashscope/qwen3.8-max",
|
|
"moonshot/kimi-k2.6",
|
|
"kimi-k2.7-code",
|
|
"moonshot/kimi-k3",
|
|
"anthropic/claude-fable-5-1",
|
|
"vertex_ai/claude-fable-5-1@default",
|
|
"gemini/gemini-3.7-flash",
|
|
"glm-5.3",
|
|
"zai/glm-5.3-flash",
|
|
"openrouter/z-ai/glm-5.3",
|
|
"novita/zai-org/glm-5.2",
|
|
],
|
|
)
|
|
def test_frontier_model_families_are_accepted(model_name: str) -> None:
|
|
assert is_recommended_or_frontier_model(model_name)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"model_name",
|
|
[
|
|
"",
|
|
"openai/gpt-4.1",
|
|
"anthropic/claude-3-5-sonnet-latest",
|
|
"ollama/llama3.1",
|
|
"deepseek/deepseek-chat",
|
|
"custom-ollama/gpt-5-mini-local",
|
|
"custom-provider/claude-opus-4-local",
|
|
"xai/grok-4.5",
|
|
"openrouter/x-ai/grok-4",
|
|
"mistral/mistral-medium-3-5",
|
|
"mistral/magistral-medium-latest",
|
|
"zai/glm-4.7",
|
|
"openrouter/z-ai/glm-5",
|
|
"custom-provider/glm-5.3-local",
|
|
],
|
|
)
|
|
def test_non_frontier_models_are_rejected(model_name: str) -> None:
|
|
assert not is_recommended_or_frontier_model(model_name)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"model_name",
|
|
[
|
|
"anthropic/claude-sonnet-4-6",
|
|
"bedrock/anthropic.claude-opus-4-8-v1:0",
|
|
"vertex_ai/claude-sonnet-5",
|
|
"Sonnet-5",
|
|
],
|
|
)
|
|
def test_claude_routes_reject_strict_tool_schemas(model_name: str) -> None:
|
|
assert not supports_strict_tool_schemas(model_name)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"model_name",
|
|
["openai/gpt-5.4", "gpt-5.4", "gemini/gemini-3.1-pro-preview", "deepseek/deepseek-v4"],
|
|
)
|
|
def test_other_routes_keep_strict_tool_schemas(model_name: str) -> None:
|
|
assert supports_strict_tool_schemas(model_name)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("model_name", "litellm"),
|
|
[
|
|
("claude-sonnet-4-5", False),
|
|
("openai/claude-sonnet-4-5", False),
|
|
("any-llm/anthropic/claude-sonnet-4-5", False),
|
|
("anthropic/claude-sonnet-4-5", True),
|
|
("litellm/anthropic/claude-sonnet-4-5", True),
|
|
("bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0", True),
|
|
("ollama/llama3", True),
|
|
],
|
|
)
|
|
def test_routes_through_litellm_matches_the_provider(
|
|
monkeypatch: pytest.MonkeyPatch, model_name: str, litellm: bool
|
|
) -> None:
|
|
"""The helper must agree with what StrixProvider actually builds.
|
|
|
|
Callers use it to decide whether a LiteLLM-only request field is safe to
|
|
attach; on the SDK's own clients such a field raises TypeError mid-turn, so
|
|
drift here breaks every request on that route.
|
|
"""
|
|
monkeypatch.setenv("OPENAI_API_KEY", "test-key")
|
|
assert routes_through_litellm(model_name) is litellm
|
|
try:
|
|
model = StrixProvider().get_model(model_name)
|
|
except ImportError:
|
|
# any-llm's client is an optional dependency; reaching it at all already
|
|
# proves the route is not LiteLLM's.
|
|
assert not litellm
|
|
return
|
|
while isinstance(model, _NonStreamingModel | _TurnGuardModel):
|
|
model = model._inner
|
|
assert isinstance(model, LitellmModel) is litellm
|