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
15 changes: 13 additions & 2 deletions npu/pack_import.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -407,10 +407,16 @@ OnnxModel parse_onnx(const std::string& path) {
continue;
}
if (!w) { kept.push_back(mm); continue; } // names nothing this graph declares: not this genre's business
// Two spellings of the placeholder: 1.9 names it `<w>.empty` beside `<w>.packed`; the 1.8 hybrid line names the
// node's own `<w>` as the `[0]` placeholder beside `<w>.packed` (Llama-3.2-1B-Instruct_rai_1.8.0_hybrid: all 162
// `.MatMulNBits.*` initializers dims [0] and no data, the weights in model.bin). Missing the second spelling kept
// the node as a real weight, so `info` reported every projection importable and `import` died on
// `...MatMulNBits.bias: no data` (engine#1503).
std::string packed = w->name;
if (packed.size() >= 6 && packed.compare(packed.size() - 6, 6, ".empty") == 0)
packed.replace(packed.size() - 6, 6, ".packed");
else { kept.push_back(mm); continue; }
else
packed += ".packed";
const OnnxTensor* pw = find_init(packed);
// No sibling at all: not this genre's pack. Keep the node exactly as it was -- it still names its `*.empty`
// initializer and the reader below decides what that means. Dropping it here would be a decision made by an
Expand Down Expand Up @@ -3596,7 +3602,12 @@ Plan plan(const std::string& pack_dir) {
pack_dir + ": " + onnx.weightless_example +
", and the pack carries no Token .fconst either (no dd_metastate_Llm_Token_*.fconst): these weights are "
"genuinely absent from the pack's external data, so this pack cannot be imported -- not a tile-layout or "
"block-size problem");
"block-size problem" +
// The 1.8 hybrid line keeps them in a `model.bin` beside the graph (1.3 GB on Llama-3.2-1B-Instruct), a
// container this importer does not read; saying where they are is what tells a reader which work is missing.
std::string(fs::exists(pack_dir + "/model.bin")
? ". This pack's weights are in its model.bin, which this importer does not read (engine#1503)"
: ""));
const bool no_fconst = tok_path.empty();
// The 1.9 external-data genre (phi-4: 201 `MatMulNBitsBf` ops whose bytes are `*.qweight.packed` siblings in
// `eager-mm.onnx.data`) must NOT ride the graph-weights path below, which was written for the 1.3/1.6/1.7.1 line's
Expand Down
34 changes: 34 additions & 0 deletions tests/npu_pack_import_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1350,6 +1350,40 @@ void test_crafted_refusals(const std::string& root) {
}
check(!fs::exists(dir / "out3" / "model.qnx") && !fs::exists(dir / "out3" / "model.q4nx"),
"crafted: nothing written for the empty .packed sibling");

// 4. engine#1503: the 1.8 hybrid spelling of the same thing. The node names `<w>` ITSELF as the [0] placeholder (no
// `.empty` suffix) beside an equally empty `<w>.packed`, and the weights are in a model.bin next to the graph
// (Llama-3.2-1B-Instruct_rai_1.8.0_hybrid: all 162 `.MatMulNBits.*` initializers are this shape). Before the fix the
// walker only knew the `.empty` spelling, kept the node as a real weight, `info` called every projection importable
// and `import` died much later on `...MatMulNBits.bias: no data`.
put_file((dir / "model.onnx").string(),
pb::model({pb::tensor("model.embed_tokens.weight", {8, 8}, "model.onnx.data", 0, 256),
pb::tensor("rai18_qweight", {0}, "", 0, 0),
pb::tensor("rai18_qweight.packed", {0}, "", 0, 0),
pb::tensor("rai18_scales", {0}, "", 0, 0),
pb::tensor("rai18_zeros", {0}, "", 0, 0)},
{pb::matmulnbits("mm", {"x", "rai18_qweight", "rai18_scales", "rai18_zeros"}, 64, 512, 4, 128)}));
put_file((dir / "model.bin").string(), std::string(64, '\0'));
try {
(void)pi::plan(dir.string());
check(false, "crafted 1.8: a [0] qweight with an empty .packed sibling was refused");
} catch (const std::exception& e) {
const std::string what = e.what();
std::printf(" crafted 1.8 placeholder refusal: %s\n", what.c_str());
check(what.find("rai18_qweight (its rai18_qweight.packed sibling carries no data either)") != std::string::npos &&
what.find("genuinely absent") != std::string::npos,
"crafted 1.8: plan() refuses by name, naming the placeholder and its sibling (" + what + ")");
check(what.find("weights are in its model.bin") != std::string::npos,
"crafted 1.8: and says where the weights are when a model.bin is there (" + what + ")");
}
fs::remove(dir / "model.bin");
try {
(void)pi::plan(dir.string());
check(false, "crafted 1.8 without model.bin: refused");
} catch (const std::exception& e) {
check(std::string(e.what()).find("model.bin") == std::string::npos,
"crafted 1.8: no model.bin, so the refusal does not claim one (" + std::string(e.what()) + ")");
}
fs::remove_all(dir);
}

Expand Down
Loading