use alice_llm::gguf::{GgufFile, GgufTokenizer};
use alice_llm::llama3::Llama3Model;
use std::env;
use std::fs;
fn main() {
let args: Vec<String> = env::args().collect();
let model_path = args.get(1).expect("Usage: forward_debug <model.gguf>");
let data = fs::read(model_path).expect("Failed to read");
let gguf = GgufFile::parse(&data).expect("Failed to parse");
println!("=== Tensor dimensions ===");
let check = [
"token_embd.weight",
"output.weight",
"output_norm.weight",
"blk.0.attn_norm.weight",
"blk.0.attn_q.weight",
"blk.0.attn_k.weight",
"blk.0.attn_v.weight",
"blk.0.attn_output.weight",
"blk.0.ffn_gate.weight",
"blk.0.ffn_up.weight",
"blk.0.ffn_down.weight",
];
for name in check {
if let Some(info) = gguf.tensor_info(name) {
println!(
" {}: dims={:?}, qtype={:?}, n_elements={}",
name,
info.dims,
info.qtype,
info.n_elements()
);
} else {
println!(" {}: NOT FOUND", name);
}
}
println!("\n=== Embedding check ===");
let emb = gguf.tensor_to_f32("token_embd.weight").expect("embedding");
let emb_info = gguf.tensor_info("token_embd.weight").unwrap();
println!(
" Shape: {:?}, total elements: {}",
emb_info.dims,
emb.len()
);
let dim0 = emb_info.dims[0] as usize;
let dim1 = if emb_info.dims.len() > 1 {
emb_info.dims[1] as usize
} else {
1
};
println!(" dim0={dim0}, dim1={dim1}");
println!(
" token 0 (if row-major, stride=dim0): {:?}",
&emb[0..8.min(emb.len())]
);
println!(
" token 1 (if row-major, stride=dim0): {:?}",
&emb[dim0..dim0 + 8.min(emb.len())]
);
let sum: f32 = emb[0..dim0.min(emb.len())].iter().sum();
let max = emb[0..dim0.min(emb.len())]
.iter()
.cloned()
.fold(f32::NEG_INFINITY, f32::max);
let min = emb[0..dim0.min(emb.len())]
.iter()
.cloned()
.fold(f32::INFINITY, f32::min);
println!(" token 0 stats: sum={sum:.4}, min={min:.4}, max={max:.4}");
println!("\n=== Norm weights ===");
let norm = gguf
.tensor_to_f32("output_norm.weight")
.expect("output_norm");
println!(
" output_norm: len={}, first_8={:?}",
norm.len(),
&norm[0..8]
);
let norm0 = gguf
.tensor_to_f32("blk.0.attn_norm.weight")
.expect("attn_norm");
println!(
" blk.0.attn_norm: len={}, first_8={:?}",
norm0.len(),
&norm0[0..8]
);
println!("\n=== Forward pass ===");
let tokenizer = GgufTokenizer::from_gguf(&gguf).expect("tokenizer");
let mut model = Llama3Model::from_gguf(&gguf).expect("model");
let logits = model.forward(tokenizer.bos_id);
println!(" logits len: {}", logits.len());
let logits_sum: f32 = logits.iter().sum();
let logits_max = logits.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
let logits_min = logits.iter().cloned().fold(f32::INFINITY, f32::min);
let argmax = logits
.iter()
.enumerate()
.max_by(|a, b| a.1.partial_cmp(b.1).unwrap())
.unwrap();
println!(" logits stats: sum={logits_sum:.2}, min={logits_min:.4}, max={logits_max:.4}");
println!(" argmax: token_id={}, logit={:.4}", argmax.0, argmax.1);
println!(
" argmax decode: '{}'",
tokenizer.decode(&[argmax.0 as u32])
);
let mut indexed: Vec<(usize, f32)> = logits.iter().copied().enumerate().collect();
indexed.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
println!("\n Top-10 after BOS:");
for (idx, logit) in indexed.iter().take(10) {
let decoded = tokenizer.decode(&[*idx as u32]);
println!(" [{idx}] {logit:.4} '{decoded}'");
}
}