Skip to content

Commit c72ae0e

Browse files
authored
feat(multimodal): route Qwen3.5 family to Qwen3-VL processor (#1563)
1 parent b63d6dd commit c72ae0e

2 files changed

Lines changed: 45 additions & 4 deletions

File tree

crates/multimodal/src/registry/qwen3_vl.rs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,13 @@ impl ModelProcessorSpec for Qwen3VLVisionSpec {
2828

2929
fn matches(&self, metadata: &ModelMetadata) -> bool {
3030
let id = metadata.model_id.to_ascii_lowercase();
31-
id.contains("qwen3") && id.contains("vl")
32-
|| metadata
33-
.config_model_type()
34-
.is_some_and(|mt| mt == "qwen3_vl")
31+
let model_type = metadata.config_model_type();
32+
let is_qwen3_vl = id.contains("qwen3") && id.contains("vl")
33+
|| model_type.is_some_and(|mt| mt == "qwen3_vl");
34+
let is_qwen3_5 = id.contains("qwen3.5")
35+
|| id.contains("qwen3.6")
36+
|| model_type.is_some_and(|mt| mt == "qwen3_5" || mt == "qwen3_5_moe");
37+
is_qwen3_vl || is_qwen3_5
3538
}
3639

3740
fn placeholder_token(&self, metadata: &ModelMetadata) -> RegistryResult<String> {
@@ -178,4 +181,23 @@ mod tests {
178181
.expect("should match qwen3 alias");
179182
assert_eq!(spec.name(), "qwen3_vl");
180183
}
184+
185+
#[test]
186+
fn qwen3_5_matches_alias_via_model_type() {
187+
let tokenizer = TestTokenizer::new(&[("<|image_pad|>", 151655)]);
188+
let config = json!({
189+
"model_type": "qwen3_5_moe",
190+
"image_token_id": 151655,
191+
});
192+
let metadata = ModelMetadata {
193+
model_id: "custom-model",
194+
tokenizer: &tokenizer,
195+
config: &config,
196+
};
197+
let registry = ModelRegistry::new();
198+
let spec = registry
199+
.lookup(&metadata)
200+
.expect("should match qwen3.5 alias");
201+
assert_eq!(spec.name(), "qwen3_vl");
202+
}
181203
}

crates/multimodal/src/vision/image_processor.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ impl ImageProcessorRegistry {
376376
/// - `qwen2-vl` -> Qwen2VLProcessor
377377
/// - `qwen2.5-vl` -> Qwen2VLProcessor (same preprocessing as Qwen2-VL)
378378
/// - `qwen3-vl` -> Qwen3VLProcessor (patch_size=16, [0.5,0.5,0.5] normalization)
379+
/// - `qwen3.5` / `qwen3_5` -> Qwen3VLProcessor (Qwen3.5 reuses Qwen3-VL preprocessing)
379380
/// - `phi-3-vision` -> Phi3VisionProcessor (HD transform with 336x336 tiles)
380381
pub fn with_defaults() -> Self {
381382
let mut registry = Self::new();
@@ -416,6 +417,24 @@ impl ImageProcessorRegistry {
416417
Box::new(super::processors::Qwen3VLProcessor::new()),
417418
);
418419

420+
// Qwen3.5 family (and Qwen3.6: same arch) reuses Qwen3-VL preprocessing.
421+
registry.register(
422+
"qwen3.5",
423+
Box::new(super::processors::Qwen3VLProcessor::new()),
424+
);
425+
registry.register(
426+
"qwen3_5",
427+
Box::new(super::processors::Qwen3VLProcessor::new()),
428+
);
429+
registry.register(
430+
"qwen3.6",
431+
Box::new(super::processors::Qwen3VLProcessor::new()),
432+
);
433+
registry.register(
434+
"qwen3_6",
435+
Box::new(super::processors::Qwen3VLProcessor::new()),
436+
);
437+
419438
// Register Qwen2-VL (matches Qwen/Qwen2-VL-*, etc.)
420439
registry.register(
421440
"qwen2-vl",

0 commit comments

Comments
 (0)