use std::path::PathBuf;
use crate::quantize::ggml_quants::apex::{ApexTier, SUPPORTED_APEX_TIERS};
use crate::quantize::ggml_quants::{LlamaFtype, DEEPSEEK4_AGENTIC_Q2_NAME};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum QuantSelector {
Standard(LlamaFtype),
Deepseek4AgenticQ2,
Apex(ApexTier),
ApexCustom(PathBuf),
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum QuantSelectorError {
#[error("unknown --quant value `{name}` (no LlamaFtype / Apex tier mapping)")]
UnknownQuant { name: String },
#[error("unknown Apex tier `apex-{tier}`; v1 supports {supported:?}")]
UnknownApexTier {
tier: String,
supported: &'static [&'static str],
},
#[error(
"Apex tier `apex-{tier}` is out of v1 scope (mudler's experimental tiers); \
use `--quant apex-custom --tensor-type-file <vendored-config>` instead"
)]
ApexTierOutOfScope { tier: String },
#[error("--quant apex-custom requires --tensor-type-file <path>")]
ApexCustomRequiresTensorTypeFile,
#[error(
"--quant dwq is reserved for the future DWQ-train pipeline; \
not implemented in convert-v2"
)]
DwqReserved,
#[error("--quant apex is unqualified; use one of {supported:?} or `apex-custom`")]
ApexUnqualified { supported: &'static [&'static str] },
#[error(
"--quant {name} is a recognized ftype but out of v1 convert-v2 scope \
(no Quantizer impl)"
)]
TqOutOfV1Scope { name: String },
}
impl QuantSelector {
pub fn receipt_name(&self) -> String {
match self {
QuantSelector::Standard(ftype) => ftype.name().to_string(),
QuantSelector::Deepseek4AgenticQ2 => DEEPSEEK4_AGENTIC_Q2_NAME.to_string(),
QuantSelector::Apex(tier) => format!("apex-{}", tier.cli_name()),
QuantSelector::ApexCustom(path) => format!("apex-custom:{}", path.display()),
}
}
pub fn from_name(s: &str) -> Result<Self, QuantSelectorError> {
if s == DEEPSEEK4_AGENTIC_Q2_NAME {
return Ok(QuantSelector::Deepseek4AgenticQ2);
}
match s {
"tq1_0" | "tq2_0" => {
return Err(QuantSelectorError::TqOutOfV1Scope {
name: s.to_string(),
});
}
_ => {}
}
if let Some(ftype) = LlamaFtype::from_name(s) {
return Ok(QuantSelector::Standard(ftype));
}
if let Some(rest) = s.strip_prefix("apex-") {
let tier = match rest {
"quality" => ApexTier::Quality,
"i-quality" => ApexTier::IQuality,
"balanced" => ApexTier::Balanced,
"i-balanced" => ApexTier::IBalanced,
"compact" => ApexTier::Compact,
"i-compact" => ApexTier::ICompact,
"mini" => ApexTier::Mini,
"custom" => {
return Err(QuantSelectorError::ApexCustomRequiresTensorTypeFile);
}
"nano" | "i-nano" | "micro" | "i-micro" => {
return Err(QuantSelectorError::ApexTierOutOfScope {
tier: rest.to_string(),
});
}
_ => {
return Err(QuantSelectorError::UnknownApexTier {
tier: rest.to_string(),
supported: SUPPORTED_APEX_TIERS,
});
}
};
return Ok(QuantSelector::Apex(tier));
}
match s {
"dwq" => Err(QuantSelectorError::DwqReserved),
"apex" => Err(QuantSelectorError::ApexUnqualified {
supported: SUPPORTED_APEX_TIERS,
}),
_ => Err(QuantSelectorError::UnknownQuant {
name: s.to_string(),
}),
}
}
}
pub const fn approximate_for_apex(tier: ApexTier) -> LlamaFtype {
match tier {
ApexTier::Quality | ApexTier::IQuality => LlamaFtype::MostlyQ6_K,
ApexTier::Balanced | ApexTier::IBalanced => LlamaFtype::MostlyQ5_K_M,
ApexTier::Compact | ApexTier::ICompact => LlamaFtype::MostlyQ4_K_M,
ApexTier::Mini => LlamaFtype::MostlyQ3_K_S,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_quant_selector_standard_round_trip() {
assert_eq!(
QuantSelector::from_name("q5_k_m").unwrap(),
QuantSelector::Standard(LlamaFtype::MostlyQ5_K_M)
);
assert_eq!(
QuantSelector::from_name("q8_0").unwrap(),
QuantSelector::Standard(LlamaFtype::MostlyQ8_0)
);
assert_eq!(
QuantSelector::from_name("f16").unwrap(),
QuantSelector::Standard(LlamaFtype::MostlyF16)
);
assert_eq!(
QuantSelector::from_name("iq4_nl").unwrap(),
QuantSelector::Standard(LlamaFtype::MostlyIQ4_NL)
);
}
#[test]
fn parse_deepseek4_agentic_q2_round_trip() {
let selector = QuantSelector::from_name(DEEPSEEK4_AGENTIC_Q2_NAME).unwrap();
assert_eq!(selector, QuantSelector::Deepseek4AgenticQ2);
assert_eq!(selector.receipt_name(), DEEPSEEK4_AGENTIC_Q2_NAME);
}
#[test]
fn parse_quant_selector_apex_round_trip() {
assert_eq!(
QuantSelector::from_name("apex-balanced").unwrap(),
QuantSelector::Apex(ApexTier::Balanced)
);
assert_eq!(
QuantSelector::from_name("apex-quality").unwrap(),
QuantSelector::Apex(ApexTier::Quality)
);
assert_eq!(
QuantSelector::from_name("apex-compact").unwrap(),
QuantSelector::Apex(ApexTier::Compact)
);
assert_eq!(
QuantSelector::from_name("apex-mini").unwrap(),
QuantSelector::Apex(ApexTier::Mini)
);
}
#[test]
fn parse_quant_selector_apex_i_variant() {
assert_eq!(
QuantSelector::from_name("apex-i-quality").unwrap(),
QuantSelector::Apex(ApexTier::IQuality)
);
assert_eq!(
QuantSelector::from_name("apex-i-balanced").unwrap(),
QuantSelector::Apex(ApexTier::IBalanced)
);
assert_eq!(
QuantSelector::from_name("apex-i-compact").unwrap(),
QuantSelector::Apex(ApexTier::ICompact)
);
}
#[test]
fn parse_quant_selector_apex_custom_errors() {
let err = QuantSelector::from_name("apex-custom").unwrap_err();
assert!(matches!(
err,
QuantSelectorError::ApexCustomRequiresTensorTypeFile
));
}
#[test]
fn parse_quant_selector_dwq_reserved() {
let err = QuantSelector::from_name("dwq").unwrap_err();
assert!(matches!(err, QuantSelectorError::DwqReserved));
}
#[test]
fn parse_quant_selector_apex_nano_out_of_scope() {
let err = QuantSelector::from_name("apex-nano").unwrap_err();
match err {
QuantSelectorError::ApexTierOutOfScope { tier } => {
assert_eq!(tier, "nano");
}
other => panic!("expected ApexTierOutOfScope, got {other:?}"),
}
for v in ["apex-i-nano", "apex-micro", "apex-i-micro"] {
let err = QuantSelector::from_name(v).unwrap_err();
assert!(
matches!(err, QuantSelectorError::ApexTierOutOfScope { .. }),
"{v} should be ApexTierOutOfScope, got {err:?}"
);
}
}
#[test]
fn parse_quant_selector_bare_apex_unqualified() {
let err = QuantSelector::from_name("apex").unwrap_err();
match err {
QuantSelectorError::ApexUnqualified { supported } => {
assert!(supported.contains(&"balanced"));
assert!(supported.contains(&"quality"));
assert!(supported.contains(&"mini"));
}
other => panic!("expected ApexUnqualified, got {other:?}"),
}
}
#[test]
fn parse_quant_selector_unknown_apex_tier_errors() {
let err = QuantSelector::from_name("apex-bogus").unwrap_err();
match err {
QuantSelectorError::UnknownApexTier { tier, supported } => {
assert_eq!(tier, "bogus");
assert!(supported.contains(&"balanced"));
}
other => panic!("expected UnknownApexTier, got {other:?}"),
}
}
#[test]
fn parse_quant_selector_tq_out_of_v1_scope() {
let err = QuantSelector::from_name("tq1_0").unwrap_err();
assert!(matches!(err, QuantSelectorError::TqOutOfV1Scope { .. }));
let err = QuantSelector::from_name("tq2_0").unwrap_err();
assert!(matches!(err, QuantSelectorError::TqOutOfV1Scope { .. }));
}
#[test]
fn parse_quant_selector_unknown_quant_errors() {
let err = QuantSelector::from_name("garbage").unwrap_err();
match err {
QuantSelectorError::UnknownQuant { name } => assert_eq!(name, "garbage"),
other => panic!("expected UnknownQuant, got {other:?}"),
}
}
#[test]
fn p7_ac3_hint_dwq_reserved() {
let msg = QuantSelectorError::DwqReserved.to_string();
assert!(
msg.contains("dwq"),
"msg should name the rejected flag: {msg}"
);
assert!(
msg.contains("reserved") || msg.contains("future"),
"msg should hint at the reserved/future-pipeline status: {msg}"
);
}
#[test]
fn p7_ac3_hint_apex_unqualified() {
let err = QuantSelectorError::ApexUnqualified {
supported: SUPPORTED_APEX_TIERS,
};
let msg = err.to_string();
assert!(msg.contains("apex"), "msg should name the flag: {msg}");
assert!(
msg.contains("balanced"),
"msg should list `balanced`: {msg}"
);
assert!(msg.contains("mini"), "msg should list `mini`: {msg}");
assert!(
msg.contains("apex-custom"),
"msg should mention the apex-custom escape hatch: {msg}"
);
}
#[test]
fn p7_ac3_hint_tq_out_of_v1_scope() {
let msg = QuantSelectorError::TqOutOfV1Scope {
name: "tq1_0".to_string(),
}
.to_string();
assert!(
msg.contains("tq1_0"),
"msg should echo the rejected name: {msg}"
);
assert!(
msg.contains("out of v1") || msg.contains("scope"),
"msg should hint at the scope reason: {msg}"
);
assert!(
msg.contains("Quantizer"),
"msg should reference the missing Quantizer impl: {msg}"
);
}
#[test]
fn p7_ac3_hint_unknown_apex_tier_lists_supported() {
let msg = QuantSelectorError::UnknownApexTier {
tier: "bogus".to_string(),
supported: SUPPORTED_APEX_TIERS,
}
.to_string();
assert!(msg.contains("bogus"), "msg should echo the bad tier: {msg}");
assert!(
msg.contains("balanced"),
"msg should list the supported tiers (e.g. `balanced`): {msg}"
);
}
#[test]
fn p7_ac3_hint_apex_custom_requires_tensor_type_file() {
let msg = QuantSelectorError::ApexCustomRequiresTensorTypeFile.to_string();
assert!(
msg.contains("apex-custom") || msg.contains("--tensor-type-file"),
"msg should name the missing flag the operator must supply: {msg}"
);
}
#[test]
fn p7_ac3_hint_apex_tier_out_of_scope() {
let msg = QuantSelectorError::ApexTierOutOfScope {
tier: "nano".to_string(),
}
.to_string();
assert!(
msg.contains("nano"),
"msg should echo the rejected tier: {msg}"
);
assert!(
msg.contains("apex-custom") || msg.contains("scope"),
"msg should hint at the escape hatch or scope reason: {msg}"
);
}
#[test]
fn p7_ac3_hint_unknown_quant() {
let msg = QuantSelectorError::UnknownQuant {
name: "garbage".to_string(),
}
.to_string();
assert!(
msg.contains("garbage"),
"msg should echo the bad name: {msg}"
);
}
#[test]
fn approximate_for_apex_table() {
assert_eq!(
approximate_for_apex(ApexTier::Quality),
LlamaFtype::MostlyQ6_K
);
assert_eq!(
approximate_for_apex(ApexTier::IQuality),
LlamaFtype::MostlyQ6_K
);
assert_eq!(
approximate_for_apex(ApexTier::Balanced),
LlamaFtype::MostlyQ5_K_M
);
assert_eq!(
approximate_for_apex(ApexTier::IBalanced),
LlamaFtype::MostlyQ5_K_M
);
assert_eq!(
approximate_for_apex(ApexTier::Compact),
LlamaFtype::MostlyQ4_K_M
);
assert_eq!(
approximate_for_apex(ApexTier::ICompact),
LlamaFtype::MostlyQ4_K_M
);
assert_eq!(
approximate_for_apex(ApexTier::Mini),
LlamaFtype::MostlyQ3_K_S
);
}
}