Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion src/registry/qwen3_vl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ impl ModelProcessorSpec for Qwen3VLVisionSpec {
let is_qwen3_5 = id.contains("qwen3.5")
|| id.contains("qwen3.6")
|| model_type.is_some_and(|mt| mt == "qwen3_5" || mt == "qwen3_5_moe");
is_qwen3_vl || is_qwen3_5
let is_qwen4_exp = model_type.is_some_and(|mt| mt == "qwen4_exp");
is_qwen3_vl || is_qwen3_5 || is_qwen4_exp
}

fn placeholder_token(&self, metadata: &ModelMetadata) -> RegistryResult<String> {
Expand Down Expand Up @@ -503,4 +504,23 @@ mod tests {
.expect("should match qwen3.5 alias");
assert_eq!(spec.name(), "qwen3_vl");
}

#[test]
fn qwen4_exp_matches_alias_via_model_type() {
let tokenizer = TestTokenizer::new(&[("<|image_pad|>", 151655)]);
let config = json!({
"model_type": "qwen4_exp",
"image_token_id": 151655,
});
let metadata = ModelMetadata {
model_id: "custom-model",
tokenizer: &tokenizer,
config: &config,
};
let registry = ModelRegistry::new();
let spec = registry
.lookup(&metadata)
.expect("should match qwen4_exp alias");
assert_eq!(spec.name(), "qwen3_vl");
}
}
21 changes: 21 additions & 0 deletions src/vision/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ impl VisionProcessorRegistry {
/// - `qwen2.5-vl` -> Qwen2VLProcessor (same preprocessing as Qwen2-VL)
/// - `qwen3-vl` -> Qwen3VLProcessor (patch_size=16, [0.5,0.5,0.5] normalization)
/// - `qwen3.5` / `qwen3_5` -> Qwen3VLProcessor (Qwen3.5 reuses Qwen3-VL preprocessing)
/// - `qwen4_exp` / `qwen4-exp` -> Qwen3VLProcessor (same vision tower as Qwen3.5)
/// - `phi-3-vision` -> Phi3VisionProcessor (HD transform with 336x336 tiles)
pub fn with_defaults() -> Self {
let mut registry = Self::new();
Expand Down Expand Up @@ -272,6 +273,16 @@ impl VisionProcessorRegistry {
Box::new(super::processors::Qwen3VLProcessor::new()),
);

// Qwen4-Exp: same vision tower as Qwen3.5
registry.register(
"qwen4_exp",
Box::new(super::processors::Qwen3VLProcessor::new()),
);
registry.register(
"qwen4-exp",
Box::new(super::processors::Qwen3VLProcessor::new()),
);

// Register Qwen2-VL (matches Qwen/Qwen2-VL-*, etc.)
registry.register(
"qwen2-vl",
Expand Down Expand Up @@ -443,4 +454,14 @@ mod tests {
.expect("phi3 processor by model_type");
assert_eq!(processor.model_name(), "phi3-vision");
}

#[test]
fn test_registry_find_qwen4_exp_model_type_fallback() {
let registry = VisionProcessorRegistry::with_defaults();

let processor = registry
.find("custom-model", Some("qwen4_exp"))
.expect("qwen3 processor by qwen4_exp model_type");
assert_eq!(processor.model_name(), "qwen3-vl");
}
}
Loading