Feature chore bug 005 #19
@@ -12,6 +12,12 @@ struct Catalog {
|
|||||||
id: &'static str,
|
id: &'static str,
|
||||||
label: &'static str,
|
label: &'static str,
|
||||||
size_mb: u32,
|
size_mb: u32,
|
||||||
|
/// `false` for the `.en` (English-only) ggml variants; `true` for the
|
||||||
|
/// multilingual variants, which whisper.cpp ships as the same filename
|
||||||
|
/// minus the `.en` infix (e.g. `ggml-base.bin` vs `ggml-base.en.bin`) —
|
||||||
|
/// same download/install machinery, just a different id/URL (T8.7,
|
||||||
|
/// FR-TRX-4, M4.2).
|
||||||
|
multilingual: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CATALOG: &[Catalog] = &[
|
const CATALOG: &[Catalog] = &[
|
||||||
@@ -19,21 +25,49 @@ const CATALOG: &[Catalog] = &[
|
|||||||
id: "tiny.en-q5_1",
|
id: "tiny.en-q5_1",
|
||||||
label: "Tiny (English, quantized) — fastest, least accurate",
|
label: "Tiny (English, quantized) — fastest, least accurate",
|
||||||
size_mb: 32,
|
size_mb: 32,
|
||||||
|
multilingual: false,
|
||||||
},
|
},
|
||||||
Catalog {
|
Catalog {
|
||||||
id: "base.en-q5_1",
|
id: "base.en-q5_1",
|
||||||
label: "Base (English, quantized) — balanced default",
|
label: "Base (English, quantized) — balanced default",
|
||||||
size_mb: 60,
|
size_mb: 60,
|
||||||
|
multilingual: false,
|
||||||
},
|
},
|
||||||
Catalog {
|
Catalog {
|
||||||
id: "small.en-q5_1",
|
id: "small.en-q5_1",
|
||||||
label: "Small (English, quantized) — more accurate, slower",
|
label: "Small (English, quantized) — more accurate, slower",
|
||||||
size_mb: 190,
|
size_mb: 190,
|
||||||
|
multilingual: false,
|
||||||
},
|
},
|
||||||
Catalog {
|
Catalog {
|
||||||
id: "medium.en-q5_1",
|
id: "medium.en-q5_1",
|
||||||
label: "Medium (English, quantized) — best accuracy, slowest",
|
label: "Medium (English, quantized) — best accuracy, slowest",
|
||||||
size_mb: 540,
|
size_mb: 540,
|
||||||
|
multilingual: false,
|
||||||
|
},
|
||||||
|
Catalog {
|
||||||
|
id: "tiny-q5_1",
|
||||||
|
label: "Tiny (multilingual, quantized) — fastest, least accurate",
|
||||||
|
size_mb: 32,
|
||||||
|
multilingual: true,
|
||||||
|
},
|
||||||
|
Catalog {
|
||||||
|
id: "base-q5_1",
|
||||||
|
label: "Base (multilingual, quantized) — balanced default",
|
||||||
|
size_mb: 60,
|
||||||
|
multilingual: true,
|
||||||
|
},
|
||||||
|
Catalog {
|
||||||
|
id: "small-q5_1",
|
||||||
|
label: "Small (multilingual, quantized) — more accurate, slower",
|
||||||
|
size_mb: 190,
|
||||||
|
multilingual: true,
|
||||||
|
},
|
||||||
|
Catalog {
|
||||||
|
id: "medium-q5_1",
|
||||||
|
label: "Medium (multilingual, quantized) — best accuracy, slowest",
|
||||||
|
size_mb: 540,
|
||||||
|
multilingual: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -50,10 +84,19 @@ pub fn list(active_id: &str) -> Vec<ModelInfo> {
|
|||||||
size_mb: m.size_mb,
|
size_mb: m.size_mb,
|
||||||
installed: whisper_model_file(m.id).exists(),
|
installed: whisper_model_file(m.id).exists(),
|
||||||
active: m.id == active_id,
|
active: m.id == active_id,
|
||||||
|
multilingual: m.multilingual,
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether `id` names a multilingual (non-`.en`) catalog model — an unknown
|
||||||
|
/// id (shouldn't happen; callers validate against the catalog first) is
|
||||||
|
/// conservatively treated as English-only rather than granting language
|
||||||
|
/// selection it can't honor.
|
||||||
|
pub fn is_multilingual(id: &str) -> bool {
|
||||||
|
CATALOG.iter().any(|m| m.id == id && m.multilingual)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum ModelError {
|
pub enum ModelError {
|
||||||
#[error("unknown model id: {0}")]
|
#[error("unknown model id: {0}")]
|
||||||
@@ -147,4 +190,38 @@ mod tests {
|
|||||||
let err = remove(active, active).unwrap_err();
|
let err = remove(active, active).unwrap_err();
|
||||||
assert!(matches!(err, ModelError::Invalid(_)));
|
assert!(matches!(err, ModelError::Invalid(_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn catalog_has_a_multilingual_counterpart_for_every_english_only_size() {
|
||||||
|
// T8.7/M4.2: every `.en` model must have a same-size multilingual
|
||||||
|
// sibling so the Settings picker always has a language-capable
|
||||||
|
// option at whatever accuracy/speed tier the user already chose.
|
||||||
|
let en_only: Vec<_> = CATALOG.iter().filter(|m| !m.multilingual).collect();
|
||||||
|
let multilingual: Vec<_> = CATALOG.iter().filter(|m| m.multilingual).collect();
|
||||||
|
assert_eq!(en_only.len(), multilingual.len());
|
||||||
|
for en in &en_only {
|
||||||
|
assert!(
|
||||||
|
multilingual.iter().any(|m| m.size_mb == en.size_mb),
|
||||||
|
"no multilingual sibling for {}",
|
||||||
|
en.id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_multilingual_matches_the_catalog_flag() {
|
||||||
|
assert!(!is_multilingual("base.en-q5_1"));
|
||||||
|
assert!(is_multilingual("base-q5_1"));
|
||||||
|
// Unknown ids are conservatively English-only (no model to check).
|
||||||
|
assert!(!is_multilingual("nonexistent-id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_reports_multilingual_flag_per_model() {
|
||||||
|
let models = list("base.en-q5_1");
|
||||||
|
let base_en = models.iter().find(|m| m.id == "base.en-q5_1").unwrap();
|
||||||
|
let base_multi = models.iter().find(|m| m.id == "base-q5_1").unwrap();
|
||||||
|
assert!(!base_en.multilingual);
|
||||||
|
assert!(base_multi.multilingual);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user