use crate::capability::{resolve_profile, ArchPath, DecoderFamily};
use crate::decoder::Decoder;
use crate::engine::{Engine, Glm52Engine, KimiEngine, MlaEngine};
use crate::gemma4_engine::Gemma4Engine;
use crate::gemma4_gguf_loader;
use crate::glm52_gguf_loader;
use crate::loader::LoadError;
use crate::mla_gguf_loader;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum EngineSelectError {
#[error("architecture {0:?} is outside the text-generation scope ({1})")]
OutOfScope(String, &'static str),
#[error("architecture {0:?} requires a dedicated engine that is not wired for serve yet: {1}")]
DedicatedUnavailable(String, &'static str),
#[error("unknown or unsupported architecture {0:?}")]
Unknown(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SelectedEngineKind {
GenericDecoder,
DedicatedStack,
Mla,
Gemma4,
RecurrentHybrid,
EncoderDecoder,
}
pub fn select_engine_kind(arch: &str) -> Result<SelectedEngineKind, EngineSelectError> {
let profile =
resolve_profile(arch).ok_or_else(|| EngineSelectError::Unknown(arch.to_string()))?;
match profile.path {
ArchPath::GenericGqa { .. } | ArchPath::TestFixture { .. } => {
Ok(SelectedEngineKind::GenericDecoder)
}
ArchPath::Deferred { reason } => {
Err(EngineSelectError::OutOfScope(arch.to_string(), reason))
}
ArchPath::DedicatedOnly { reason } => match profile.family {
DecoderFamily::Dedicated => Ok(SelectedEngineKind::DedicatedStack),
DecoderFamily::Mla => Ok(SelectedEngineKind::Mla),
DecoderFamily::GemmaFamily if is_gemma4_arch(arch) => Ok(SelectedEngineKind::Gemma4),
DecoderFamily::Hybrid | DecoderFamily::Recurrent => {
Ok(SelectedEngineKind::RecurrentHybrid)
}
DecoderFamily::EncoderDecoder => Ok(SelectedEngineKind::EncoderDecoder),
_ => Err(EngineSelectError::DedicatedUnavailable(
arch.to_string(),
reason,
)),
},
}
}
pub fn ensure_generic_decoder(arch: &str) -> Result<(), EngineSelectError> {
match select_engine_kind(arch)? {
SelectedEngineKind::GenericDecoder => Ok(()),
SelectedEngineKind::DedicatedStack => Err(EngineSelectError::DedicatedUnavailable(
arch.to_string(),
if is_glm52_arch(arch) {
"use load_glm52_engine_from_path / ServedEngine::Glm52 — not generic Decoder"
} else {
"use the dedicated Kimi/GLM/DeepSeek loader, not generic Decoder"
},
)),
SelectedEngineKind::Mla => Err(EngineSelectError::DedicatedUnavailable(
arch.to_string(),
"use load_mla_engine_from_path / ServedEngine::Mla — not generic Decoder",
)),
SelectedEngineKind::Gemma4 => Err(EngineSelectError::DedicatedUnavailable(
arch.to_string(),
"use load_gemma4_engine_from_path / ServedEngine::Gemma4 — not generic Decoder",
)),
SelectedEngineKind::RecurrentHybrid => {
let _ = crate::recurrent_engine::RecurrentEngine::reject(arch);
let _ = crate::hybrid_engine::HybridEngine::reject(arch);
Err(EngineSelectError::DedicatedUnavailable(
arch.to_string(),
"recurrent/hybrid SSM engine stub present — not yet on the serve path",
))
}
SelectedEngineKind::EncoderDecoder => {
let _ = crate::t5_engine::T5Engine::reject(arch);
Err(EngineSelectError::DedicatedUnavailable(
arch.to_string(),
"T5 encoder-decoder engine stub present — not yet on the serve path",
))
}
}
}
pub enum ServedEngine {
Decoder(Box<Decoder>),
Kimi(KimiEngine),
Glm52(Glm52Engine),
Mla(MlaEngine),
Gemma4(Box<Gemma4Engine>),
}
impl ServedEngine {
pub fn vocab_size(&self) -> usize {
match self {
Self::Decoder(d) => Engine::vocab_size(d.as_ref()),
Self::Kimi(k) => Engine::vocab_size(k),
Self::Glm52(g) => Engine::vocab_size(g),
Self::Mla(m) => Engine::vocab_size(m),
Self::Gemma4(g) => Engine::vocab_size(g.as_ref()),
}
}
}
pub fn load_mla_engine_from_path(path: &std::path::Path) -> Result<ServedEngine, LoadError> {
let file = ferrox_gguf::ShardedGguf::open(path)?;
let arch = file
.metadata_str("general.architecture")
.unwrap_or("unknown");
match select_engine_kind(arch) {
Ok(SelectedEngineKind::Mla) => {}
Ok(other) => {
return Err(LoadError::DedicatedArchitectureRequired(
arch.to_string(),
match other {
SelectedEngineKind::GenericDecoder => "generic decoder arch, not MLA",
SelectedEngineKind::DedicatedStack => "dedicated non-MLA stack",
SelectedEngineKind::RecurrentHybrid => "hybrid/recurrent, not MLA",
SelectedEngineKind::EncoderDecoder => "encoder-decoder, not MLA",
SelectedEngineKind::Gemma4 => "gemma4 dedicated, not MLA",
SelectedEngineKind::Mla => unreachable!(),
},
));
}
Err(EngineSelectError::Unknown(a)) => {
return Err(LoadError::UnsupportedArchitecture(a));
}
Err(EngineSelectError::OutOfScope(a, r)) => {
return Err(LoadError::UnsupportedFeature(a, r.to_string()));
}
Err(EngineSelectError::DedicatedUnavailable(a, r)) => {
return Err(LoadError::DedicatedArchitectureRequired(a, r));
}
}
Ok(ServedEngine::Mla(mla_gguf_loader::load_mla_engine(&file)?))
}
fn is_glm52_arch(arch: &str) -> bool {
matches!(arch, "glm-dsa" | "glm4" | "glm4moe")
}
fn is_gemma4_arch(arch: &str) -> bool {
crate::gemma4_engine::GEMMA4_ARCHES.contains(&arch)
}
pub fn load_glm52_engine_from_path(path: &std::path::Path) -> Result<ServedEngine, LoadError> {
let file = ferrox_gguf::ShardedGguf::open(path)?;
let arch = file
.metadata_str("general.architecture")
.unwrap_or("unknown");
if !is_glm52_arch(arch) {
return Err(LoadError::DedicatedArchitectureRequired(
arch.to_string(),
"not a GLM-5.2 / GLM4-family architecture (expected glm-dsa/glm4/glm4moe)",
));
}
match select_engine_kind(arch) {
Ok(SelectedEngineKind::DedicatedStack) => {}
Ok(other) => {
return Err(LoadError::DedicatedArchitectureRequired(
arch.to_string(),
match other {
SelectedEngineKind::GenericDecoder => "generic decoder arch, not GLM DSA",
SelectedEngineKind::Mla => "MLA arch, not GLM DSA",
SelectedEngineKind::RecurrentHybrid => "hybrid/recurrent, not GLM DSA",
SelectedEngineKind::EncoderDecoder => "encoder-decoder, not GLM DSA",
SelectedEngineKind::Gemma4 => "gemma4 dedicated, not GLM DSA",
SelectedEngineKind::DedicatedStack => unreachable!(),
},
));
}
Err(EngineSelectError::Unknown(a)) => {
return Err(LoadError::UnsupportedArchitecture(a));
}
Err(EngineSelectError::OutOfScope(a, r)) => {
return Err(LoadError::UnsupportedFeature(a, r.to_string()));
}
Err(EngineSelectError::DedicatedUnavailable(a, r)) => {
return Err(LoadError::DedicatedArchitectureRequired(a, r));
}
}
Ok(ServedEngine::Glm52(glm52_gguf_loader::load_glm52_engine(
&file,
)?))
}
pub fn load_gemma4_engine_from_path(path: &std::path::Path) -> Result<ServedEngine, LoadError> {
let file = ferrox_gguf::ShardedGguf::open(path)?;
let arch = file
.metadata_str("general.architecture")
.unwrap_or("unknown");
if !is_gemma4_arch(arch) {
return Err(LoadError::DedicatedArchitectureRequired(
arch.to_string(),
"not a gemma4 / gemma4-assistant architecture",
));
}
match select_engine_kind(arch) {
Ok(SelectedEngineKind::Gemma4) => {}
Ok(other) => {
return Err(LoadError::DedicatedArchitectureRequired(
arch.to_string(),
match other {
SelectedEngineKind::GenericDecoder => "generic decoder arch, not Gemma4",
SelectedEngineKind::Mla => "MLA arch, not Gemma4",
SelectedEngineKind::DedicatedStack => "dedicated non-Gemma4 stack",
SelectedEngineKind::RecurrentHybrid => "hybrid/recurrent, not Gemma4",
SelectedEngineKind::EncoderDecoder => "encoder-decoder, not Gemma4",
SelectedEngineKind::Gemma4 => unreachable!(),
},
));
}
Err(EngineSelectError::Unknown(a)) => {
return Err(LoadError::UnsupportedArchitecture(a));
}
Err(EngineSelectError::OutOfScope(a, r)) => {
return Err(LoadError::UnsupportedFeature(a, r.to_string()));
}
Err(EngineSelectError::DedicatedUnavailable(a, r)) => {
return Err(LoadError::DedicatedArchitectureRequired(a, r));
}
}
Ok(ServedEngine::Gemma4(Box::new(
gemma4_gguf_loader::load_gemma4_engine(&file)?,
)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn llama_and_qwen3_select_generic_decoder() {
assert_eq!(
select_engine_kind("llama").unwrap(),
SelectedEngineKind::GenericDecoder
);
assert_eq!(
select_engine_kind("qwen3").unwrap(),
SelectedEngineKind::GenericDecoder
);
assert_eq!(
select_engine_kind("gemma3").unwrap(),
SelectedEngineKind::GenericDecoder
);
assert_eq!(
select_engine_kind("phi3").unwrap(),
SelectedEngineKind::GenericDecoder
);
assert_eq!(
select_engine_kind("mixtral").unwrap(),
SelectedEngineKind::GenericDecoder
);
}
#[test]
fn mamba_is_recurrent_fail_closed_for_generic() {
assert!(matches!(
select_engine_kind("mamba2").unwrap(),
SelectedEngineKind::RecurrentHybrid
));
assert!(ensure_generic_decoder("mamba2").is_err());
}
#[test]
fn deepseek2_is_mla_not_generic() {
assert_eq!(
select_engine_kind("deepseek2").unwrap(),
SelectedEngineKind::Mla
);
assert!(ensure_generic_decoder("deepseek2").is_err());
}
#[test]
fn glm4_is_dedicated_stack_not_generic() {
assert_eq!(
select_engine_kind("glm4").unwrap(),
SelectedEngineKind::DedicatedStack
);
assert!(ensure_generic_decoder("glm4").is_err());
}
#[test]
fn gemma4_selects_dedicated_engine() {
assert_eq!(
select_engine_kind("gemma4").unwrap(),
SelectedEngineKind::Gemma4
);
assert_eq!(
select_engine_kind("gemma4-assistant").unwrap(),
SelectedEngineKind::Gemma4
);
assert!(ensure_generic_decoder("gemma4").is_err());
}
}