-
Notifications
You must be signed in to change notification settings - Fork 53
fix(multimodal): fall back to config model type for aliased vision models #755
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -337,28 +337,33 @@ impl ImageProcessorRegistry { | |
| self.processors.insert(pattern.into(), processor); | ||
| } | ||
|
|
||
| /// Find a processor for the given model ID. | ||
| /// Find a processor for the given model ID, falling back to model_type. | ||
| /// | ||
| /// Matches by substring containment (case-insensitive). | ||
| pub fn find(&self, model_id: &str) -> Option<&dyn ImagePreProcessor> { | ||
| let model_lower = model_id.to_lowercase(); | ||
| for (pattern, processor) in &self.processors { | ||
| if model_lower.contains(&pattern.to_lowercase()) { | ||
| return Some(processor.as_ref()); | ||
| } | ||
| } | ||
| None | ||
| pub fn find(&self, model_id: &str, model_type: Option<&str>) -> Option<&dyn ImagePreProcessor> { | ||
| self.find_in_candidate(model_id) | ||
| .or_else(|| model_type.and_then(|model_type| self.find_in_candidate(model_type))) | ||
|
Comment on lines
+344
to
+345
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| /// Check if a model has a registered processor. | ||
| pub fn has_processor(&self, model_id: &str) -> bool { | ||
| self.find(model_id).is_some() | ||
| self.find(model_id, None).is_some() | ||
| } | ||
|
|
||
| /// Get list of supported model patterns. | ||
| pub fn supported_patterns(&self) -> Vec<&str> { | ||
| self.processors.keys().map(|s| s.as_str()).collect() | ||
| } | ||
|
|
||
| fn find_in_candidate(&self, candidate: &str) -> Option<&dyn ImagePreProcessor> { | ||
| let candidate = candidate.to_ascii_lowercase(); | ||
| for (pattern, processor) in &self.processors { | ||
| if candidate.contains(&pattern.to_ascii_lowercase()) { | ||
| return Some(processor.as_ref()); | ||
| } | ||
| } | ||
|
Comment on lines
+358
to
+364
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
FILE="$(fd -p 'image_processor.rs' | head -n1)"
echo "Inspecting: $FILE"
echo
echo "[1] Confirm HashMap + first-match iteration:"
rg -n -C2 'processors:\s*HashMap|for \(pattern, processor\) in &self\.processors|return Some\(processor\.as_ref\(\)\)' "$FILE"
echo
echo "[2] List registered patterns and detect substring overlaps:"
python - <<'PY'
import re
from pathlib import Path
p = next(Path('.').rglob('image_processor.rs'))
text = p.read_text()
patterns = re.findall(r'registry\.register\(\s*"([^"]+)"', text)
print("patterns:", patterns)
overlaps = []
for a in patterns:
for b in patterns:
if a != b and b in a:
overlaps.append((a, b))
print("overlaps:")
for a, b in sorted(set(overlaps)):
print(f" '{a}' contains '{b}'")
PYRepository: lightseekorg/smg Length of output: 1156 Deterministic processor selection is not guaranteed. Line 360 returns the first substring match from a 🔧 Proposed fix (choose most specific match deterministically) fn find_in_candidate(&self, candidate: &str) -> Option<&dyn ImagePreProcessor> {
let candidate = candidate.to_ascii_lowercase();
- for (pattern, processor) in &self.processors {
- if candidate.contains(&pattern.to_ascii_lowercase()) {
- return Some(processor.as_ref());
- }
- }
- None
+ self.processors
+ .iter()
+ .filter(|(pattern, _)| candidate.contains(&pattern.to_ascii_lowercase()))
+ .max_by(|(a, _), (b, _)| {
+ a.len()
+ .cmp(&b.len())
+ .then_with(|| a.as_str().cmp(b.as_str()))
+ })
+ .map(|(_, processor)| processor.as_ref())
}🤖 Prompt for AI Agents |
||
| None | ||
| } | ||
| } | ||
|
|
||
| impl Default for ImageProcessorRegistry { | ||
|
|
@@ -436,6 +441,10 @@ impl ImageProcessorRegistry { | |
| "phi3-vision", | ||
| Box::new(super::processors::Phi3VisionProcessor::new()), | ||
| ); | ||
| registry.register( | ||
| "phi3_v", | ||
| Box::new(super::processors::Phi3VisionProcessor::new()), | ||
| ); | ||
|
|
||
| // Register LLaMA 4 Vision | ||
| registry.register( | ||
|
|
@@ -551,7 +560,7 @@ mod tests { | |
| assert!(registry.has_processor("lmms-lab/llava-next-interleave-qwen-7b")); | ||
|
|
||
| // Get the processor and check model name | ||
| let processor = registry.find("llava-hf/llava-1.5-7b-hf").unwrap(); | ||
| let processor = registry.find("llava-hf/llava-1.5-7b-hf", None).unwrap(); | ||
| assert_eq!(processor.model_name(), "llava"); | ||
| } | ||
|
|
||
|
|
@@ -566,4 +575,36 @@ mod tests { | |
| assert!(registry.has_processor("TEST-MODEL")); | ||
| assert!(!registry.has_processor("other-model")); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_registry_find_falls_back_to_model_type() { | ||
| let registry = ImageProcessorRegistry::with_defaults(); | ||
|
|
||
| assert!(registry.find("custom-model", None).is_none()); | ||
|
|
||
| let processor = registry | ||
| .find("custom-model", Some("qwen3_vl")) | ||
| .expect("qwen3 processor by model_type"); | ||
| assert_eq!(processor.model_name(), "qwen3-vl"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_registry_find_preserves_fast_path() { | ||
| let registry = ImageProcessorRegistry::with_defaults(); | ||
|
|
||
| let processor = registry | ||
| .find("Qwen3-VL-30B-A3B-Instruct", Some("qwen2_vl")) | ||
| .expect("qwen3 processor by model_id"); | ||
| assert_eq!(processor.model_name(), "qwen3-vl"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_registry_find_phi3_model_type_fallback() { | ||
| let registry = ImageProcessorRegistry::with_defaults(); | ||
|
|
||
| let processor = registry | ||
| .find("custom-model", Some("phi3_v")) | ||
| .expect("phi3 processor by model_type"); | ||
| assert_eq!(processor.model_name(), "phi3-vision"); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.