Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/refset/src/arch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub mod draft;
pub mod glm5next;
pub mod mimo2;
pub mod minicpmv4_7;
pub mod nemotron_h;
pub mod qwen35;
pub mod qwen35moe;
pub mod qwen4exp;
Expand All @@ -30,6 +31,7 @@ static BY_ARCH: &[(&str, &[&Family])] = &[
(mimo2::ARCH, mimo2::FAMILIES),
(minicpmv4_7::ARCH, minicpmv4_7::FAMILIES),
(qwen4exp::ARCH, qwen4exp::FAMILIES),
(nemotron_h::ARCH, nemotron_h::FAMILIES),
(tokenizer::ARCH, tokenizer::FAMILIES),
(dequant::ARCH, dequant::FAMILIES),
];
Expand Down
261 changes: 261 additions & 0 deletions crates/refset/src/arch/nemotron_h/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
//! The Nemotron 3 Nano family. The logits set is dumped from the tokenizer
//! family's model ([`super::tokenizer::nemotron_model`]) by mainline llama.cpp
//! ([`super::tokenizer::NEMOTRON_BUILD`]), the build that family already pins.
//! ik has no `nemotron_h`.

use crate::family::{Build, Family, Identity};

/// The architecture every nemotron manifest names in its `# arch` line.
pub const ARCH: &str = "nemotron_h";

/// Last-position logits of four prompts, mainline on the CPU.
pub const LOGITS_SET: &str = "ref-nemotron-logits";

/// Mainline's last-position logits (`just dump-ref-nemotron`).
pub static LOGITS: Family = Family {
name: "ref-nemotron-logits",
sets: &[LOGITS_SET],
resolve: None,
recipe: "just dump-ref-nemotron",
identity: Identity::NemotronLogits,
arch: Some(ARCH),
build: Some(Build::Is(super::tokenizer::NEMOTRON_BUILD)),
runs: Some(super::tokenizer::nemotron_model),
draft_runs: None,
consumers: &[],
};

/// The architecture's families, in the order `refset-check` lists them.
pub static FAMILIES: &[&Family] = &[&LOGITS];

#[cfg(test)]
mod tests {
use super::LOGITS;
use crate::RefError;
use crate::family::Build;
use crate::nemotron::LogitsSet;
use std::path::Path;

fn pin() -> &'static str {
match LOGITS.build {
Some(Build::Is(p)) => p,
_ => panic!("the logits family pins no build"),
}
}

fn runs() -> String {
LOGITS.runs().unwrap_or_else(|e| panic!("{e}"))
}

/// Write one prompt's ids and logits and the manifest. `complete` adds the trailer.
fn write(dir: &Path, model: &str, build: &str, complete: bool) {
std::fs::create_dir_all(dir).unwrap_or_else(|e| panic!("{e}"));
std::fs::write(dir.join("p.ids"), "0\n1\n2\n3\n0\n").unwrap_or_else(|e| panic!("{e}"));
let logits: Vec<u8> = [0.0f32, 1.5, -0.5, 0.25]
.into_iter()
.flat_map(f32::to_le_bytes)
.collect();
std::fs::write(dir.join("p.logits.f32"), logits).unwrap_or_else(|e| panic!("{e}"));
let mut text = format!(
"# model\t{model}\n# build\t{build}\n# arch\tnemotron_h\n\
# prompt columns\tname\tn_vocab\tn_ids\tlogits\tids\n\
prompt\tp\t4\t5\tp.logits.f32\tp.ids\n"
);
if complete {
text.push_str("# complete\t1\t0\n");
}
std::fs::write(dir.join("MANIFEST.tsv"), text).unwrap_or_else(|e| panic!("{e}"));
}

fn dir(tag: &str) -> std::path::PathBuf {
let d =
std::env::temp_dir().join(format!("bloomery-nemotron-{tag}-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&d);
d
}

/// The build pin is the tokenizer family's constant.
#[test]
fn the_build_pin_is_the_tokenizer_familys() {
let Some(Build::Is(p)) = LOGITS.build else {
panic!("no pin");
};
assert_eq!(p, crate::arch::tokenizer::NEMOTRON_BUILD);
assert_eq!(LOGITS.sets, &["ref-nemotron-logits"]);
assert_eq!(
runs(),
crate::arch::tokenizer::NEMOTRON
.runs()
.unwrap_or_else(|e| panic!("{e}"))
);
}

/// A finished set opens, and `logits` is that column's file.
#[test]
fn a_finished_set_reads_logits_by_column_name() {
let d = dir("ok");
write(&d, &runs(), pin(), true);
let set = LogitsSet::open(&d, &LOGITS).unwrap_or_else(|e| panic!("{e}"));
assert_eq!(set.prompts.len(), 1);
let row = set
.logits(&set.prompts[0])
.unwrap_or_else(|e| panic!("{e}"));
assert_eq!(row[1], 1.5);
std::fs::remove_dir_all(&d).unwrap_or_else(|e| panic!("{e}"));
}

/// No `# complete`: unfinished, even when the other lines are well formed.
#[test]
fn a_set_without_its_trailer_is_unfinished() {
let d = dir("trailer");
write(&d, &runs(), pin(), false);
match LogitsSet::open(&d, &LOGITS) {
Err(RefError::Unfinished { set }) => assert_eq!(set, d.display().to_string()),
other => panic!("a set without its trailer: {other:?}"),
}
std::fs::remove_dir_all(&d).unwrap_or_else(|e| panic!("{e}"));
}

/// Another model path is stale.
#[test]
fn a_set_of_another_model_is_stale() {
let d = dir("model");
write(&d, "/models/other/file.gguf", pin(), true);
match LogitsSet::open(&d, &LOGITS) {
Err(RefError::Stale { dumped_from, .. }) => {
assert_eq!(dumped_from, "/models/other/file.gguf");
}
other => panic!("another file: {other:?}"),
}
std::fs::remove_dir_all(&d).unwrap_or_else(|e| panic!("{e}"));
}

/// Another mainline build is foreign.
#[test]
fn a_set_of_another_build_is_foreign() {
let d = dir("build");
write(
&d,
&runs(),
"llama-tokenize 00000000000000000000000000000000 libllama.so 00000000000000000000000000000000",
true,
);
match LogitsSet::open(&d, &LOGITS) {
Err(RefError::Foreign { field, .. }) => assert_eq!(field, "build"),
other => panic!("another build: {other:?}"),
}
std::fs::remove_dir_all(&d).unwrap_or_else(|e| panic!("{e}"));
}

/// A line that is not a token id is malformed, naming the field and the text.
#[test]
fn a_token_line_that_is_not_an_id_is_malformed() {
let d = dir("bad-id");
write(&d, &runs(), pin(), true);
std::fs::write(d.join("p.ids"), "0\n1\n2\ngarbage\n0\n").unwrap_or_else(|e| panic!("{e}"));
match LogitsSet::open(&d, &LOGITS) {
Err(RefError::Malformed { what, .. }) => {
assert!(
what.contains("token id") && what.contains("garbage"),
"{what}"
);
}
other => panic!("a token line that is not an id: {other:?}"),
}
std::fs::remove_dir_all(&d).unwrap_or_else(|e| panic!("{e}"));
}

/// A NaN or infinite logit is malformed, naming its index.
#[test]
fn a_non_finite_logit_is_malformed() {
let d = dir("nan-logit");
write(&d, &runs(), pin(), true);
let logits: Vec<u8> = [0.0f32, f32::NAN, -0.5, 0.25]
.into_iter()
.flat_map(f32::to_le_bytes)
.collect();
std::fs::write(d.join("p.logits.f32"), logits).unwrap_or_else(|e| panic!("{e}"));
match LogitsSet::open(&d, &LOGITS) {
Err(RefError::Malformed { what, .. }) => {
assert!(what.contains("non-finite logit at index 1"), "{what}");
}
other => panic!("a non-finite logit: {other:?}"),
}
std::fs::remove_dir_all(&d).unwrap_or_else(|e| panic!("{e}"));
}

/// An empty line among the ids is malformed, as the C++ reader that writes the set refuses it.
#[test]
fn an_empty_token_line_is_malformed() {
let d = dir("empty-id");
write(&d, &runs(), pin(), true);
std::fs::write(d.join("p.ids"), "0\n\n1\n2\n3\n0\n").unwrap_or_else(|e| panic!("{e}"));
match LogitsSet::open(&d, &LOGITS) {
Err(RefError::Malformed { what, .. }) => {
assert!(what.contains("empty token id"), "{what}")
}
other => panic!("an empty token line: {other:?}"),
}
std::fs::remove_dir_all(&d).unwrap_or_else(|e| panic!("{e}"));
}

/// An id at or past `n_vocab` is malformed, naming the id and the vocabulary.
#[test]
fn a_token_id_outside_the_vocabulary_is_malformed() {
let d = dir("oob-id");
write(&d, &runs(), pin(), true);
std::fs::write(d.join("p.ids"), "0\n1\n2\n3\n4\n").unwrap_or_else(|e| panic!("{e}"));
match LogitsSet::open(&d, &LOGITS) {
Err(RefError::Malformed { what, .. }) => {
assert!(what.contains("outside vocabulary"), "{what}");
}
other => panic!("a token id outside the vocabulary: {other:?}"),
}
std::fs::remove_dir_all(&d).unwrap_or_else(|e| panic!("{e}"));
}

/// The trailer's count is the prompt rows. One row and `# complete 2` is malformed.
#[test]
fn a_completion_count_that_is_not_the_prompt_rows_is_malformed() {
let d = dir("complete-n");
write(&d, &runs(), pin(), true);
let text =
std::fs::read_to_string(d.join("MANIFEST.tsv")).unwrap_or_else(|e| panic!("{e}"));
std::fs::write(
d.join("MANIFEST.tsv"),
text.replace("# complete\t1\t0\n", "# complete\t2\t0\n"),
)
.unwrap_or_else(|e| panic!("{e}"));
match LogitsSet::open(&d, &LOGITS) {
Err(RefError::Malformed { what, .. }) => {
assert!(what.contains("completion count"), "{what}");
}
other => panic!("a completion count that is not the prompt rows: {other:?}"),
}
std::fs::remove_dir_all(&d).unwrap_or_else(|e| panic!("{e}"));
}

/// A completion count that does not parse is malformed, naming the field.
#[test]
fn a_completion_count_that_does_not_parse_is_malformed() {
let d = dir("complete-bad");
write(&d, &runs(), pin(), true);
let text =
std::fs::read_to_string(d.join("MANIFEST.tsv")).unwrap_or_else(|e| panic!("{e}"));
std::fs::write(
d.join("MANIFEST.tsv"),
text.replace("# complete\t1\t0\n", "# complete\tgarbage\t0\n"),
)
.unwrap_or_else(|e| panic!("{e}"));
match LogitsSet::open(&d, &LOGITS) {
Err(RefError::Malformed { what, .. }) => {
assert!(
what.contains("complete count") && what.contains("garbage"),
"{what}"
);
}
other => panic!("a completion count that does not parse: {other:?}"),
}
std::fs::remove_dir_all(&d).unwrap_or_else(|e| panic!("{e}"));
}
}
4 changes: 2 additions & 2 deletions crates/refset/src/arch/tokenizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub const TOKREF_BUILD: &str = "llama-tokenize 0128e83c5e71e5f2b72f3d5d2c2de6b2
/// `nemotron_h` architecture. The soname is `libllama.so.0`; the pin's word
/// is the one [`crate::tokenizer::build_id`] spells.
// PIN(2026-10-14): /home/user/llama.cpp-mainline's llama-tokenize and libllama.so.0.
const NEMOTRON_BUILD: &str = "llama-tokenize 5df95d3a942effc971ff170432d840fd \
pub(crate) const NEMOTRON_BUILD: &str = "llama-tokenize 5df95d3a942effc971ff170432d840fd \
libllama.so 3575b1f39e7773379fec011d39f32a77";

/// V4.1's set (`deepseek-v3` pre-tokenizer), from the file the tree runs.
Expand Down Expand Up @@ -141,7 +141,7 @@ fn qwen38_model() -> Result<String, RefError> {
Ok(super::qwen4exp::MODEL.to_string())
}

fn nemotron_model() -> Result<String, RefError> {
pub(crate) fn nemotron_model() -> Result<String, RefError> {
Ok(NEMOTRON_MODEL.to_string())
}

Expand Down
12 changes: 12 additions & 0 deletions crates/refset/src/columns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,15 @@ where
v.parse()
.map_err(|e| RefError::malformed(at, format!("{name} {v:?}: {e}")))
}

/// `name` is one file of the set's own directory. Empty, `.`, `..`, or a
/// slash is `Malformed`: the manifest must not name a path outside it.
pub(crate) fn file_name(name: &str, at: &dyn Display) -> Result<(), RefError> {
if name.is_empty() || name.contains('/') || matches!(name, "." | "..") {
return Err(RefError::malformed(
at,
format!("{name:?} does not name a file of the set's directory"),
));
}
Ok(())
}
7 changes: 1 addition & 6 deletions crates/refset/src/dequant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,7 @@ impl DequantSet {

fn file_row(r: &Row<'_>, at: &dyn fmt::Display) -> Result<SetFile, RefError> {
let name = r.text("file")?;
if name.is_empty() || name.contains('/') || matches!(name, "." | "..") {
return Err(RefError::malformed(
at,
format!("{name:?} does not name a file of the set's directory"),
));
}
crate::columns::file_name(name, at)?;
let md5 = r.text("md5")?;
check_hex("md5", md5, at)?;
Ok(SetFile {
Expand Down
12 changes: 12 additions & 0 deletions crates/refset/src/family.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ pub enum Identity {
/// naming the harness and ggml's library by md5: the family's `build`
/// pins both ([`crate::dequant::build_id`]).
DequantManifest,
/// `# model`, `# build` and `# arch` in a last-position logits set's
/// `MANIFEST.tsv` (`tools/ref/nemotron_ref.cpp`). A prompt row is read by
/// the `# prompt columns` line; `logits` is that prompt's last position.
NemotronLogits,
}

/// The ik build a family's sets must name.
Expand Down Expand Up @@ -390,6 +394,14 @@ impl Family {
build: set.build(),
})
}
Identity::NemotronLogits => {
let set = crate::nemotron::LogitsSet::open(path, self)?;
Ok(Provenance {
dumped_from: stated(set.model.as_deref()),
draft: None,
build: set.build,
})
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions crates/refset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
//! - [`tokenizer`]: `llama-tokenize`'s ids per vocabulary
//! (`crates/tokenizer/tools/oracle.sh`).
//! - [`dequant`]: ggml's dequantized rows (`tools/ref/dump-dequant.sh`).
//! - [`nemotron`]: mainline's last-position logits of Nemotron 3 Nano
//! (`tools/ref/nemotron_ref.cpp`).
//! - [`census`]: the census stubs — header-only copies of many users' model
//! files, pinned by `tools/census/stubs.tsv` (the writer
//! `tools/type-census.py --stubs`) and checked against it.
Expand All @@ -48,6 +50,7 @@ pub mod ik;
pub mod kld;
mod md5;
pub mod mtpref;
pub mod nemotron;
pub mod tokenizer;
pub mod vision;
pub mod visref;
Expand Down
Loading
Loading