pub use crate::sam_runner::{SamArch, SamPredictionAny, SamRunner, SamRunnerBuilder};
pub use rlx_cli::{
AssembledTurn, ChatMessage, ChatTemplate, ChatTemplateSource, CompatSource,
CompatibilityReport, CompatibilityStatus, GgufRequiredFields, LmRunner, MediaSource,
ModelRunner, MtmdContext, MtmdTurn, SniffedFrom, SniffedRunner, UnimplementedArch,
WeightFormat, arch_runner_name, auto_chat_template, auto_dispatch, auto_runner_name,
auto_sniff, check_hf_repo, check_path, debug_resolve_name, dispatch, dispatch_help,
known_unimplemented_arch, known_unimplemented_keys, list_mtp_keys, looks_like_hf_repo,
model_type_runner_name, open_gguf_loader, open_loader, open_loader_resolved,
open_loader_with_format, register_cli, register_runner, registered_runners, run_auto,
run_check, run_inspect, run_registered,
};
pub use rlx_dinov2::{DinoV2Output, DinoV2Runner, DinoV2RunnerBuilder, DinoV2Variant};
pub use rlx_flux2::{Flux2Output, Flux2Runner, Flux2RunnerBuilder};
pub use rlx_gemma::{GemmaConfigSource, GemmaRunner, GemmaRunnerBuilder};
pub use rlx_llama32::{Llama32ConfigSource, Llama32Runner, Llama32RunnerBuilder};
pub use rlx_qwen3::{Precision, Qwen3ConfigSource, Qwen3Runner, Qwen3RunnerBuilder};
pub use rlx_qwen35::{Qwen35ConfigSource, Qwen35Runner, Qwen35RunnerBuilder};
pub use rlx_vjepa2::{
Vjepa2Output, Vjepa2PoolOutput, Vjepa2PredictOutput, Vjepa2Runner, Vjepa2RunnerBuilder,
};
pub use rlx_wav2vec2_bert::{Wav2Vec2BertRunner, Wav2Vec2BertRunnerBuilder};
pub type ConfigSource = Qwen3ConfigSource;
use anyhow::{Result, bail};
use std::path::Path;
pub fn auto_runner(path: &Path) -> Result<Box<dyn LmRunner>> {
auto_runner_with_mmproj(path, None)
}
pub fn auto_runner_with_mmproj(path: &Path, mmproj: Option<&Path>) -> Result<Box<dyn LmRunner>> {
let sniff = auto_sniff(path)?;
let weights = sniff.path.as_path();
let runner: Box<dyn LmRunner> = match sniff.runner_name {
"qwen3" => Box::new(Qwen3Runner::builder().weights(weights).build()?),
"qwen35" => {
if gguf_has_mtp_heads(weights).unwrap_or(false) {
Box::new(
rlx_qwen35::Qwen35SpecRunner::builder()
.weights(weights)
.build()?,
)
} else {
let mut b = Qwen35Runner::builder().weights(weights);
if let Some(mp) = mmproj {
b = b.mmproj(mp);
}
Box::new(b.build()?)
}
}
"gemma" => Box::new(GemmaRunner::builder().weights(weights).build()?),
"llama32" => Box::new(Llama32Runner::builder().weights(weights).build()?),
"lfm" => Box::new(rlx_lfm::LfmRunner::builder().weights(weights).build()?),
other => bail!(
"auto_runner: runner `{other}` (sniffed from {:?}) has no `LmRunner` impl yet — \
use its per-family builder directly",
sniff.from
),
};
Ok(runner)
}
fn gguf_has_mtp_heads(path: &Path) -> Result<bool> {
use rlx_gguf::{GgufFile, MetaValue};
let is_gguf = path
.extension()
.and_then(|s| s.to_str())
.map(|s| s.eq_ignore_ascii_case("gguf"))
.unwrap_or(false);
if !is_gguf {
return Ok(false);
}
let raw = GgufFile::from_path(path)?;
let arch = raw
.metadata
.get("general.architecture")
.and_then(MetaValue::as_str)
.unwrap_or("");
for k in [
format!("{arch}.nextn_predict_layers"),
"qwen35.nextn_predict_layers".to_string(),
"qwen36.nextn_predict_layers".to_string(),
] {
if let Some(MetaValue::U32(n)) = raw.metadata.get(&k) {
return Ok(*n > 0);
}
}
Ok(false)
}
pub fn auto_tokenize(
weights_path: &Path,
text: &str,
explicit_tokenizer: Option<&Path>,
) -> Result<Vec<u32>> {
use anyhow::Context;
match rlx_qwen35::encode_prompt_auto(weights_path, explicit_tokenizer, text) {
Ok(ids) => Ok(ids),
Err(e) => {
let is_gguf = weights_path
.extension()
.and_then(|s| s.to_str())
.map(|s| s.eq_ignore_ascii_case("gguf"))
.unwrap_or(false);
if !is_gguf {
return Err(e);
}
Err(e).with_context(|| {
format!(
"auto_tokenize: no `tokenizer.json` resolved for {weights_path:?}. \
The GGUF ships a vocab at `tokenizer.ggml.tokens` but \
reconstructing a BPE encoder from GGUF-only metadata is \
per-family work (PLAN.md M8 follow-up). Options: \
(1) place `tokenizer.json` next to the GGUF; \
(2) pass an explicit path via the `explicit_tokenizer` arg; \
(3) download the matching `tokenizer.json` from the model's \
HF repo and point at it"
)
})
}
}
}
pub fn auto_detokenize(
weights_path: &Path,
ids: &[u32],
explicit_tokenizer: Option<&Path>,
skip_special_tokens: bool,
) -> Result<String> {
rlx_qwen35::decode_ids_auto(weights_path, explicit_tokenizer, ids, skip_special_tokens)
}