From a57d576805ed30ac9d6f4800ea46161697ee850f Mon Sep 17 00:00:00 2001 From: happy5318 Date: Sat, 25 Jul 2026 16:27:50 +0800 Subject: [PATCH] fix: make prefixed Qwen detection positive-only to avoid shadowing hybrid IDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gate review found that the broadened token-prefix check caused deepseek-r1-distill-qwen2.5-bakeneko-32b to enter the Qwen branch (qwen2 token), return False, and prevent the DeepSeek detector from running — a silent regression. Fix: split into two phases: 1. Positive-only scan: any token matching qwen(\d+) with major >= 3 immediately returns True. No False path — Qwen 2.x in hybrid IDs falls through to subsequent detectors. 2. Terminal guard (original behavior): standalone "qwen" token or normalized.startswith("qwen") still applies the 3+ gate with return False for 2.x, since those ARE pure Qwen models. Add regression test for deepseek-r1-distill-qwen2.5-bakeneko-32b. --- api/config.py | 20 +++++++++++++------ ...est_reasoning_effort_model_capabilities.py | 10 ++++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/api/config.py b/api/config.py index 447ebf62..cd4ee1d6 100644 --- a/api/config.py +++ b/api/config.py @@ -3316,12 +3316,20 @@ def _candidate_supports_reasoning(candidate: str) -> bool: if major >= 4 or (major == 3 and minor >= 7): return True return False - if any(token.startswith("qwen") for token in tokens): - # Restrict to Qwen 3+ (exclude Qwen 2/2.5) - # Token-prefix matching (position-independent) so prefixed ids like - # "al-qwen3-8-max-preview" (tokens: ["al","qwen3","8",...]) still match, - # even though "qwen" is not a standalone token. The bare - # normalized.startswith("qwen") case is subsumed by this check. + # Positive-only prefixed Qwen 3+ detection (e.g. "al-qwen3-8-max-preview" + # → tokens ["al","qwen3","8",...]). Scan for any token starting with + # "qwen" followed by version >= 3 and immediately allow. Do NOT return + # False here — Qwen 2.x embedded in hybrid IDs like + # "deepseek-r1-distill-qwen2.5-bakeneko-32b" must fall through to the + # DeepSeek detector below. + for token in tokens: + m = re.match(r"qwen(\d+)", token) + if m and int(m.group(1)) >= 3: + return True + # Terminal guard for standalone/bare Qwen IDs (original behavior): + # "qwen" as a standalone token or normalized starting with "qwen" means + # this IS a Qwen model — apply the 3+ gate and block 2.x. + if "qwen" in token_set or normalized.startswith("qwen"): match = re.search(r"qwen.*?(\d+)(?:\D+(\d+))?", normalized) if match: major = int(match.group(1)) diff --git a/tests/test_reasoning_effort_model_capabilities.py b/tests/test_reasoning_effort_model_capabilities.py index 798d7114..2a4e7edb 100644 --- a/tests/test_reasoning_effort_model_capabilities.py +++ b/tests/test_reasoning_effort_model_capabilities.py @@ -550,4 +550,14 @@ def test_qwen_prefixed_alias_reasoning_detection(): assert cfg._candidate_supports_reasoning(model) is False, ( f"{model} must NOT be reasoning-capable (Qwen 2.x excluded)" ) + # Hybrid IDs with embedded Qwen 2.x must NOT be shadowed by the Qwen + # branch — they must fall through to the DeepSeek detector. + for model in ( + "deepseek-r1-distill-qwen2.5-bakeneko-32b", + "rinna/deepseek-r1-distill-qwen2.5-bakeneko-32b", + ): + assert cfg._candidate_supports_reasoning(model) is True, ( + f"{model} must remain reasoning-capable (DeepSeek-R1 hybrid, " + f"Qwen 2.x must not shadow the DeepSeek detector)" + )