[Rust Frontend] Fix Qwen parser auto-detection (#51169)
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Bugen Zhao <i@bugenzhao.com> Signed-off-by: Sage Ahrac <sagiahrak@gmail.com>
Sage committed
Aug 20, 2026 at 12:39 UTC
727274a75ba7b2668cd2dc04ad61bbb7c5292bf4
4 files changed
+57
-15
rust/src/chat/src/parser/reasoning/mod.rs
+2
-1
@@ -85,7 +85,8 @@ impl ReasoningParserFactory {
85
.register_pattern("deepseek-v3", names::DEEPSEEK_V3)
86
.register_pattern("gemma-4", names::GEMMA4)
87
.register_pattern("gemma4", names::GEMMA4)
88
- .register_pattern("qwen", names::QWEN3)
88
+ .register_pattern("qwq", names::DEEPSEEK_R1)
89
+ .register_pattern("qwen3", names::QWEN3)
90
.register_pattern("glm-5", names::GLM45)
91
.register_pattern("glm-4.7", names::GLM45)
92
.register_pattern("glm-4.6", names::GLM45)
rust/src/chat/src/parser/reasoning/tests.rs
+17
@@ -37,6 +37,23 @@ fn factory_resolves_deepseek_v4_to_qwen3_alias() {
37
);
38
}
39
40
+#[test]
41
+fn factory_distinguishes_qwen_model_families() {
42
+ let factory = ReasoningParserFactory::new();
43
+ assert_eq!(
44
+ factory.resolve_name_for_model("Qwen/QwQ-32B"),
45
+ Some(names::DEEPSEEK_R1)
46
+ );
47
+ assert_eq!(
48
+ factory.resolve_name_for_model("Qwen/Qwen3-8B"),
49
+ Some(names::QWEN3)
50
+ );
51
+ assert_eq!(
52
+ factory.resolve_name_for_model("Qwen/Qwen2.5-0.5B-Instruct"),
53
+ None
54
+ );
55
+}
56
+
57
#[test]
58
fn factory_routes_step3p5_models_to_dedicated_parser() {
59
let factory = ReasoningParserFactory::new();
rust/src/chat/src/parser/tool/mod.rs
+3
-2
@@ -94,9 +94,10 @@ impl ToolParserFactory {
94
.register_pattern("mistral-", names::MISTRAL)
95
.register_pattern("mixtral-", names::MISTRAL)
96
.register_pattern("qwen3-coder", names::QWEN3_CODER)
97
- .register_pattern("qwen2.5-coder", names::QWEN3_CODER)
97
.register_pattern("qwen3.5", names::QWEN3_CODER)
99
- .register_pattern("qwen", names::QWEN3_XML)
98
+ .register_pattern("qwq", names::HERMES)
99
+ .register_pattern("qwen2.5", names::HERMES)
100
+ .register_pattern("qwen3", names::QWEN3_XML)
101
.register_pattern("hermes", names::HERMES)
102
.register_pattern("hy3", names::HY_V3)
103
.register_pattern("hy_v3", names::HY_V3)
rust/src/chat/src/parser/tool/tests.rs
+35
-12
@@ -80,22 +80,45 @@ fn factory_creates_registered_parser_for_model() {
80
factory.create_for_model("my-fake-model-v1", &[]).unwrap();
81
}
82
83
+#[test]
84
+fn factory_distinguishes_qwen_tool_formats() {
85
+ let factory = ToolParserFactory::new();
86
+
87
+ for (model, expected) in [
88
+ ("Qwen/Qwen3.5-0.8B", Some(names::QWEN3_CODER)),
89
+ ("Qwen/Qwen3-0.6B", Some(names::QWEN3_XML)),
90
+ ("Qwen/Qwen3-Coder-30B", Some(names::QWEN3_CODER)),
91
+ ("Qwen/QwQ-32B", Some(names::HERMES)),
92
+ ("Qwen/Qwen2.5-0.5B-Instruct", Some(names::HERMES)),
93
+ ("Qwen/Qwen2-1.5B-Instruct", None),
94
+ ] {
95
+ assert_eq!(factory.resolve_name_for_model(model), expected, "{model}");
96
+ }
97
+}
98
+
99
+#[test]
100
+fn factory_parses_qwen2_5_coder_template_with_hermes() {
101
+ let factory = ToolParserFactory::new();
102
+ let tool_call = r#"<tool_call>
103
+{"name":"get_weather","arguments":{"location":"Tokyo"}}
104
+</tool_call>"#;
105
+
106
+ let mut parser = factory.create_for_model("Qwen/Qwen2.5-Coder-7B-Instruct", &[]).unwrap();
107
+ let mut output = ToolParserOutput::default();
108
+ parser.parse_into(tool_call, &mut output).unwrap();
109
+ output.append(parser.finish().unwrap());
110
+ let output = output.coalesce();
111
+
112
+ assert!(output.normal_text().is_empty());
113
+ assert_eq!(output.calls().len(), 1);
114
+ assert_eq!(output.calls()[0].name.as_deref(), Some("get_weather"));
115
+ assert_eq!(output.calls()[0].arguments, r#"{"location":"Tokyo"}"#);
116
+}
117
+
118
#[test]
119
fn factory_new_resolves_default_patterns() {
120
let factory = ToolParserFactory::new();
121
87
- assert_eq!(
88
- factory.resolve_name_for_model("Qwen/Qwen3.5-0.8B"),
89
- Some(names::QWEN3_CODER)
90
- );
91
- assert_eq!(
92
- factory.resolve_name_for_model("Qwen/Qwen3-0.6B"),
93
- Some(names::QWEN3_XML)
94
- );
95
- assert_eq!(
96
- factory.resolve_name_for_model("Qwen/Qwen3-Coder-30B"),
97
- Some(names::QWEN3_CODER)
98
- );
122
assert_eq!(
123
factory.resolve_name_for_model("meta-llama-4-maverick"),
124
Some(names::LLAMA4_JSON)