diff --git a/CHANGELOG.md b/CHANGELOG.md index 147e0cbb483..4a04856a7df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ ## [Unreleased] +### Fixed +- Reasoning-effort detection now recognises GLM models (`zai.glm-5`, `zai.glm-4.7`, `glm-5`, etc.) and Claude family-before-version names (`claude-opus-4-8`, `claude-haiku-4-5`, `anthropic.claude-opus-4-8`) as thinking-capable when accessed through named custom providers. Both were missed by PR #3327's family list even though the dot-namespace stripping it introduced correctly surfaces the bare model name. + ## [v0.51.210] — 2026-06-02 — Release GD (stage-batch1 — model-picker multi-slash fix + extensionless preview highlighting) ### Fixed diff --git a/api/config.py b/api/config.py index b474a82251b..83132acae14 100644 --- a/api/config.py +++ b/api/config.py @@ -2140,12 +2140,18 @@ def _candidate_supports_reasoning(candidate: str) -> bool: return True if normalized.startswith(("kimi-k2", "kimi-thinking", "claude-3", "claude-4")): return True + # claude-opus-4-8 / claude-haiku-4-5 style (family-before-version) + if normalized.startswith("claude-") and token_set & {"3", "4"}: + return True if normalized.startswith("qwen3") or "qwen3" in token_set: return True if normalized.startswith(("deepseek-v3", "deepseek-v4", "deepseek-r1", "deepseek-r2")): return True if len(tokens) >= 2 and tokens[0] == "deepseek" and tokens[1] in {"v3", "v4", "r1", "r2"}: return True + # GLM models (ZAI / Zhipu AI) — all modern generations (4.5+) support reasoning + if normalized.startswith("glm-"): + return True return False diff --git a/tests/test_custom_provider_bare_model_reasoning.py b/tests/test_custom_provider_bare_model_reasoning.py index 46fbf3f1d44..6d087b2b17d 100644 --- a/tests/test_custom_provider_bare_model_reasoning.py +++ b/tests/test_custom_provider_bare_model_reasoning.py @@ -127,6 +127,51 @@ def test_vendor_prefix_keyword_does_not_trigger_reasoning(model_id): ) == [] +# ── GLM models (ZAI / Zhipu AI) ────────────────────────────────────────────── + +@pytest.mark.parametrize( + "model_id", + [ + "zai.glm-5", + "zai.glm-4.7", + "zai.glm-4.6", + "zai.glm-4.7-flash", + "glm-5", + "glm-4.5", + ], +) +def test_glm_dot_separated_custom_provider(model_id): + efforts = cfg.resolve_model_reasoning_efforts( + model_id, + provider_id="custom:newapi", + ) + assert set(efforts) >= {"low", "medium", "high"}, ( + f"{model_id} via custom provider should expose reasoning efforts" + ) + + +# ── Claude family-before-version naming (claude-opus-4-8, claude-haiku-4-5) ── + +@pytest.mark.parametrize( + "model_id", + [ + "anthropic.claude-opus-4-8", + "anthropic.claude-haiku-4-5", + "anthropic.claude-opus-4-7", + "claude-opus-4-8", + "claude-haiku-4-5", + ], +) +def test_claude_family_version_naming_custom_provider(model_id): + efforts = cfg.resolve_model_reasoning_efforts( + model_id, + provider_id="custom:newapi", + ) + assert set(efforts) >= {"low", "medium", "high"}, ( + f"{model_id} via custom provider should expose reasoning efforts" + ) + + # ── slash-prefixed names must still work (no regression) ───────────────────── def test_deepseek_slash_prefix_still_works():