use std::io::{self, Write};
const DIM: &str = "\x1b[2m";
const RESET: &str = "\x1b[0m";
pub fn short_chip_label(name: &str) -> String {
name.strip_prefix("Apple ").unwrap_or(name).to_string()
}
pub struct HeaderInfoPrefill {
pub prefill_n: usize,
pub prefill_ms: f64,
pub prefill_tok_s: f64,
}
pub fn print_header_prefill<W: Write>(
w: &mut W,
info: &HeaderInfoPrefill,
tty: bool,
) -> io::Result<()> {
let (d, r) = if tty { (DIM, RESET) } else { ("", "") };
writeln!(
w,
"{d}prefill: {} tok in {:.0}ms ({:.0} tok/s){r}",
info.prefill_n, info.prefill_ms, info.prefill_tok_s,
)?;
writeln!(w)?;
w.flush()
}
pub struct LoadProgress {
enabled: bool,
n_layers: usize,
last_width: usize,
}
impl LoadProgress {
pub fn new(stderr_is_tty: bool, verbosity: u8, n_layers: usize) -> Self {
Self {
enabled: stderr_is_tty && verbosity == 0,
n_layers,
last_width: 0,
}
}
pub fn on_layer(&mut self, i: usize) {
if !self.enabled {
return;
}
let line = format!("loading {}/{} layers", i, self.n_layers);
self.last_width = line.len().max(self.last_width);
eprint!("\r{line}");
let _ = io::stderr().flush();
}
pub fn finish(&mut self) {
if !self.enabled {
return;
}
eprint!("\r{}\r", " ".repeat(self.last_width));
let _ = io::stderr().flush();
self.enabled = false;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn short_chip_label_strips_apple_prefix() {
assert_eq!(short_chip_label("Apple M5 Max"), "M5 Max");
assert_eq!(short_chip_label("Apple M3 Pro"), "M3 Pro");
}
#[test]
fn short_chip_label_passes_through_unknown_prefix() {
assert_eq!(short_chip_label("AMD Radeon Pro"), "AMD Radeon Pro");
assert_eq!(short_chip_label(""), "");
}
#[test]
fn print_banner_default_arm_smoke() {
use crate::core::provenance::Provenance;
use crate::serve::load_info::{
print_banner, ArchFamily, ChatTemplateSource, LoadInfo, MoeShape, TokenizerSource,
};
use std::path::PathBuf;
use std::time::Duration;
let info = LoadInfo {
model_id: "gemma-4-27b-it-Q4_K_M".to_string(),
arch_str: "gemma4".to_string(),
arch_family: ArchFamily::Gemma4,
model_path: PathBuf::from("/cache/gemma-4-27b-it-Q4_K_M.gguf"),
on_disk_bytes: (16.91_f64 * 1024.0 * 1024.0 * 1024.0).round() as u64,
backend_chip: "Apple M5 Max".to_string(),
backend: "mlx-native",
n_layers: 62,
hidden_size: 5376,
vocab_size: 262_144,
n_attention_heads: 32,
n_key_value_heads: 16,
head_dim: 128,
sliding_window: Some(4096),
full_attention_interval: None,
max_context_length: Some(131_072),
moe: Some(MoeShape {
n_experts: 0,
n_experts_per_tok: 0,
}),
quant_label: Some("Q4_K".to_string()),
quant_bpw: Some(4.83),
tokenizer_source: TokenizerSource::HfTokenizerJson {
path: PathBuf::from("/cache/tokenizer.json"),
},
eos_token_ids: vec![1, 106],
bos_token_id: Some(2),
chat_template_source: ChatTemplateSource::GgufEmbedded,
provenance: Provenance::External,
vision_projector: None,
load_wall_clock: Duration::from_secs_f64(2.41),
resident_weight_bytes: Some((16.42_f64 * 1024.0 * 1024.0 * 1024.0).round() as u64),
kv_cache_budget_bytes: None,
kv_spill_active: false,
tq_kv_active: false,
kv_bytes_per_token_override: None,
};
let mut buf = Vec::new();
print_banner(&info, &mut buf, false).expect("print banner");
let s = String::from_utf8(buf).expect("utf8");
assert_eq!(
s.lines().count(),
14,
"expected 14-line banner (post-iter-17 tq_kv addition), got\n{s}"
);
for line in s.lines() {
assert!(
line.starts_with("hf2q load: "),
"line shape diverged: {line:?}"
);
}
assert!(s.contains("backend = mlx-native (M5 Max)"));
assert!(s.contains("arch = gemma4, family = gemma4"));
assert!(s.contains("quant = Q4_K dominant"));
assert!(s.contains("ready in 2.41 s"));
}
#[test]
fn header_prefill_adds_blank_line() {
let info = HeaderInfoPrefill {
prefill_n: 15,
prefill_ms: 260.0,
prefill_tok_s: 57.6,
};
let mut buf = Vec::new();
print_header_prefill(&mut buf, &info, false).unwrap();
let s = String::from_utf8(buf).unwrap();
assert_eq!(s, "prefill: 15 tok in 260ms (58 tok/s)\n\n");
}
#[test]
fn load_progress_disabled_when_not_tty() {
let mut p = LoadProgress::new(false, 0, 30);
p.on_layer(1);
p.finish();
}
#[test]
fn load_progress_disabled_when_verbose() {
let mut p = LoadProgress::new(true, 1, 30);
p.on_layer(1);
p.finish();
}
}