fn output_json(path: &Path, file_size: u64, header: &HeaderData, metadata: MetadataInfo) {
let (v_maj, v_min) = header.version;
let architecture = metadata.architecture.clone();
let num_layers = metadata.num_layers;
let num_heads = metadata.num_heads;
let hidden_size = metadata.hidden_size;
let vocab_size = metadata.vocab_size;
let result = InspectResult {
file: path.display().to_string(),
valid: true,
format: "APR v2".to_string(),
version: format!("{v_maj}.{v_min}"),
tensor_count: header.tensor_count,
size_bytes: file_size,
checksum_valid: header.checksum_valid,
architecture,
num_layers,
num_heads,
hidden_size,
vocab_size,
flags: flags_from_header(header),
metadata,
};
if let Ok(json) = serde_json::to_string_pretty(&result) {
println!("{json}");
}
}
fn output_text(
path: &Path,
file_size: u64,
header: &HeaderData,
metadata: &MetadataInfo,
show_vocab: bool,
show_filters: bool,
show_weights: bool,
) {
output::header(&path.display().to_string());
let (v_maj, v_min) = header.version;
let checksum_str = if header.checksum_valid {
output::badge_pass("VALID")
} else {
output::badge_fail("INVALID")
};
let header_pairs = vec![
("Format", "APR v2".to_string()),
("Version", format!("{v_maj}.{v_min}")),
("Size", output::format_size(file_size)),
("Tensors", output::count_fmt(header.tensor_count as usize)),
("Checksum", checksum_str),
(
"Data Offset",
format!(
"0x{:X} ({})",
header.data_offset,
output::format_size(header.data_offset)
),
),
];
println!("{}", output::kv_table(&header_pairs));
output_flags(header);
output_architecture(metadata);
output_metadata_text(metadata);
if show_vocab {
println!("\n Vocabulary: (use `apr tensors` for detailed view)");
}
if show_filters {
println!("\n Filters: (not applicable for v2 format)");
}
if show_weights {
println!("\n Weights: (use `apr tensors` for detailed view)");
}
}
fn flags_from_header(header: &HeaderData) -> FlagsInfo {
FlagsInfo {
lz4_compressed: header.flags.is_lz4_compressed(),
zstd_compressed: header.flags.is_zstd_compressed(),
encrypted: header.flags.is_encrypted(),
signed: header.flags.contains(AprV2Flags::SIGNED),
sharded: header.flags.is_sharded(),
quantized: header.flags.is_quantized(),
has_vocab: header.flags.contains(AprV2Flags::HAS_VOCAB),
}
}
fn output_flags(header: &HeaderData) {
let flag_list = collect_flag_labels(header);
if flag_list.is_empty() {
output::kv("Flags", "(none)");
} else {
output::kv("Flags", flag_list.join(" | "));
}
}
fn collect_flag_labels(header: &HeaderData) -> Vec<&'static str> {
type Pred = fn(&HeaderData) -> bool;
const TABLE: &[(Pred, &str)] = &[
(|h| h.flags.is_lz4_compressed(), "LZ4"),
(|h| h.flags.is_zstd_compressed(), "ZSTD"),
(|h| h.flags.is_encrypted(), "ENCRYPTED"),
(|h| h.flags.contains(AprV2Flags::SIGNED), "SIGNED"),
(|h| h.flags.is_sharded(), "SHARDED"),
(|h| h.flags.is_quantized(), "QUANTIZED"),
(|h| h.flags.contains(AprV2Flags::HAS_VOCAB), "HAS_VOCAB"),
(|h| h.flags.contains(AprV2Flags::HAS_FILTERBANK), "HAS_FILTERBANK"),
(|h| h.flags.contains(AprV2Flags::HAS_MODEL_CARD), "HAS_MODEL_CARD"),
(|h| h.flags.contains(AprV2Flags::STREAMING), "STREAMING"),
];
TABLE
.iter()
.filter_map(|(pred, label)| pred(header).then_some(*label))
.collect()
}
fn output_architecture(metadata: &MetadataInfo) {
let has_arch_info = metadata.architecture.is_some()
|| metadata.hidden_size.is_some()
|| metadata.num_layers.is_some();
if !has_arch_info {
return;
}
println!("\n Architecture:");
if let Some(arch) = &metadata.architecture {
println!(" Family: {arch}");
}
if let Some(p) = metadata.param_count {
println!(" Parameters: {}", format_param_count(p));
}
print_arch_numeric_fields(metadata);
}
fn print_arch_numeric_fields(metadata: &MetadataInfo) {
for (label, value) in [
("Hidden Size", metadata.hidden_size),
("Layers", metadata.num_layers),
("Attention Heads", metadata.num_heads),
("KV Heads", metadata.num_kv_heads),
("Intermediate Size", metadata.intermediate_size),
("Vocab Size", metadata.vocab_size),
("Max Position", metadata.max_position_embeddings),
] {
if let Some(v) = value {
println!(" {label}: {v}");
}
}
if let Some(r) = metadata.rope_theta {
println!(" RoPE Theta: {r}");
}
}
fn output_chat_template_info(metadata: &MetadataInfo) {
if metadata.chat_template.is_none() && metadata.chat_format.is_none() {
return;
}
println!("\n Chat Template:");
if let Some(format) = &metadata.chat_format {
println!(" Format: {format}");
}
if let Some(template) = &metadata.chat_template {
let display_template = if template.len() > 100 {
format!("{}... ({} chars)", &template[..100], template.len())
} else {
template.clone()
};
println!(" Template: {display_template}");
}
if let Some(tokens) = &metadata.special_tokens {
print_json_object(" Special Tokens:", tokens, " ");
}
}
fn print_json_object(header: &str, value: &serde_json::Value, indent: &str) {
println!("{header}");
let Some(obj) = value.as_object() else { return };
for (k, v) in obj {
if !v.is_null() {
if let Some(s) = v.as_str() {
println!("{indent}{k}: {s}");
} else {
println!("{indent}{k}: {v}");
}
}
}
}
fn output_metadata_text(metadata: &MetadataInfo) {
let fields: &[(&str, &Option<String>)] = &[
("Name", &metadata.name),
("Model Type", &metadata.model_type),
("Description", &metadata.description),
("Author", &metadata.author),
("Source", &metadata.source),
("Original Format", &metadata.original_format),
("Created", &metadata.created_at),
];
for (label, value) in fields {
if let Some(v) = value {
output::kv(label, v);
}
}
print!("{}", format_provenance_block(metadata));
output_chat_template_info(metadata);
if let Some(source_meta) = &metadata.source_metadata {
print_json_object("\n Source Metadata (PMAT-223):", source_meta, " ");
}
}
fn format_provenance_block(metadata: &MetadataInfo) -> String {
let mut out = String::new();
out.push('\n');
out.push_str(" Provenance:\n");
for (label, value) in [
("license", &metadata.license),
("data_source", &metadata.data_source),
("data_license", &metadata.data_license),
] {
let display = value.as_deref().unwrap_or("(missing)");
out.push_str(&format!(" {label}: {display}\n"));
}
out
}
fn format_param_count(count: u64) -> String {
if count >= 1_000_000_000 {
format!("{:.1}B ({count})", count as f64 / 1_000_000_000.0)
} else if count >= 1_000_000 {
format!("{:.1}M ({count})", count as f64 / 1_000_000.0)
} else if count >= 1_000 {
format!("{:.1}K ({count})", count as f64 / 1_000.0)
} else {
count.to_string()
}
}