Repository navigation
serve: the built-in template pins cover the two chat models the stick itself ships - #1158
Conversation
…easured templates
`config/template-pins.json` pins 7 names, and none of them is a row the appliance itself serves: the
stick's staged marker is `Qwen3-4B-Instruct-2507-1bit` (unsloth/Qwen3-4B-Instruct-2507-GGUF,
`Qwen3-4B-Instruct-2507-Q4_K_M.gguf`) and `Qwen3-0.6B-NPU-1bit` (the FastFlowLM/Qwen3-0.6B-NPU2
container) — the last row of ci.yml's appliance table and the one a stick image boots with. So on the
appliance's own default chat row, `--template-pin-strict` checked nothing: an unpinned model is logged,
not refused (docs/serve.md), and the owner's open decision "flip the serve rows to strict" would have
bought the appliance's model nothing.
Four rows added, keyed on both names a row can be served under (the catalog id that becomes `--alias`
and the file/dir name), with digests measured rather than guessed:
qwen3-4b-instruct-2507-1bit c979e0e71a3e21b8… (the staged
qwen3-4b-instruct-2507-q4_k_m.gguf c979e0e71a3e21b8… Qwen3-4B-Instruct-2507-Q4_K_M.gguf
on the c26 staging tree; equal to the
digest models/pins.json records for it)
qwen3-0.6b-npu-1bit a136047ba140347b… (the container's tokenizer_config.json
qwen3-0.6b-npu2 a136047ba140347b… `chat_template` string — the digest
#1095's serve line printed on a real
container, not the JSON file's own)
`tests/template_pin.sh` gains the case that makes the claim checkable: a fixture named
`qwen3-4b-instruct-2507-q4_k_m.gguf` whose template is not the pinned one must warn with that digest.
Verified in both directions — against the current main build (wt/onebp-zyphra/build-main-merged/1bit,
built 11:56 today from the merged #1089/#1095) every existing case passes and **the new case fails**, so
it detects the missing row; `cmake -S . -B /tmp/tp-cfg` then generates
`generated/template_pins_default.h` containing the new names, which is what the binary compiles in.
CI runs the suite (`add_test(NAME template_pin ...)`, ci.yml `ctest`), so the pass is proven there.
…rry an expectation The router's four classes are the one place a model is pulled and then served by name (docs/laya-first.md). Three of them were pinned by file name; `short` — Qwen3-0.6B-Q8_0.gguf, digest 57f1fd00f0013a2b… in models/pins.json — was not, so the smallest model in the routing table was the one a strict pin could not refuse. All four now agree with models/pins.json's measured digests. Doc: the coverage sentence says so, instead of listing the stick plus demo/scoreboard.
|
Second commit
All four routed rows now have an expectation, and each digest in Where the table now stands: 12 rows — the stick's two chat models (2 names each), the four router rows, and the demo/scoreboard names that were already there. That is every model this engine can serve by name today, which is what §9's |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY
All four new digests check out against the files they name, and the one that first looked wrong is the interesting one.
config/template-pins.json is at 12 rows at this head, and the four laya-first routed rows all carry an expectation now: short qwen3-0.6b-q8_0.gguf 57f1fd00…, prose zaya1-8b-q4_k_m.gguf b55ecfbb…, code qwen3-coder-30b-a3b-instruct-q4_k_m.gguf 87710339…, long_doc qwen3.8-27b-ud-q4_k_xl.gguf 12827f24….
Re-derived by fetching each source and hashing the template the same way the tool does (GGUF metadata / container chat_template string):
| row | source the pin names | digest I compute |
|---|---|---|
qwen3-4b-instruct-2507-1bit, qwen3-4b-instruct-2507-q4_k_m.gguf |
unsloth/Qwen3-4B-Instruct-2507-GGUF |
c979e0e71a3e21b8… = the pinned c979e0e71a3e |
qwen3-0.6b-npu-1bit, qwen3-0.6b-npu2 |
FastFlowLM/Qwen3-0.6B-NPU2 |
a136047ba140347b… = the pinned a136047ba140 |
qwen3-0.6b-q8_0.gguf |
1bit-MONSTER/Qwen3-0.6B-GGUF |
57f1fd00f0013a2b… = the pinned 57f1fd00f001 |
That last row is worth a sentence because my first cross-check got a different digest for the same file name: unsloth/Qwen3-0.6B-GGUF:Qwen3-0.6B-Q8_0.gguf hashes to 5da44855ab7e0641…, while 1bit-MONSTER and Qwen's own repo both give 57f1fd00…. Same file name, different upload, different template — so the "source" field these rows carry is load-bearing, not decoration, and the row is right because it names the repo it took the file from.
The claim about what the stick ships also holds: ~/wt/payload-c26c/lemonade-onebit-rc-2026-11-05-c26c/bin/resources/server_models.json contains both Qwen3-4B-Instruct-2507 and Qwen3-0.6B-NPU.
Test: built 1bit at this head (ONEBIT_NPU=ON, the private npu_rt) and ran the PR's own gate, bash tests/template_pin.sh <1bit> -> all 12 ok, including ok the appliance's staged chat model is a built-in pin (exit 0) and the directory-route cases (names its tokenizer_config.json template, names its chat_template.jinja template, mismatch warns / strict refuses, "none" with a template warns). So the new row is exercised through the same path a booted appliance uses, not only by reading the JSON.
Merge note, not a blocker: this is the same coupling #1157 has - both PRs describe or rely on models/pins.json, which is #1148 (open, now READY at 5c2a3c3c). #1158 does not cite that file, so it stands alone; #1157 does, and still needs its citation fixed or #1148 landed first.
|
CI green at and it bites: against the 11:56 build of merged main, the same script reports |
|
End-to-end evidence for the claim, on the real staged files, with the already-merged binary — not just the fixture. Binary:
So both keys a row can be served under are exercised on real bytes (the catalog alias, and the file/dir name the stick actually uses), the digests are the ones this PR records, and strict mode neither false-refuses the shipped media nor tolerates a different template under the same name. The two negatives (2, 3, 6) are the part worth checking: they show the rows are load-bearing rather than inert data. |
…pen PR would add The reviewer's blocker, and it is the same class as the text being fixed: the paragraph said the digests for the four rows "are already measured — models/pins.json records the template of every file we serve and publish", and `models/pins.json` does not exist in this tree (`ls` fails, `git cat-file -e origin/main:models/pins.json` fails). It is engine #1148, open, so a reader on `main` was sent to a path that is not there, and the "already measured" claim rested on an unmerged working tree — including the one digest that exists nowhere else yet (`short`'s 57f1fd00f0…). Now the paragraph says what a reader can check today: - three of the four rows are in `config/template-pins.json` **now** — zaya1-8b-q4_k_m.gguf b55ecfbbbc…, qwen3-coder-30b-a3b-instruct-q4_k_m.gguf 87710339d2…, qwen3.8-27b-ud-q4_k_xl.gguf 12827f24b7… — because they are the same files the load-time check already pins (verified against the tree at this head); - the fourth, `short`'s Qwen3-0.6B-Q8_0.gguf 57f1fd00f0…, is **not in any table in this tree yet**; it is measured, it is added by #1158, and it is recorded with every other served/published file in #1148's models/pins.json — said as such rather than implied to be here. The Q4NX sentence now links docs/npu-chat-template.md, which is in this tree (#1115). Nothing else in the paragraph changed: what runs today, what it catches, and what step 3 adds all verified in the review.
…one that does not (#1157) * docs/laya-first: say which template check runs today, and that the per-row digest is step 3 The section ended with a present-tense claim the tree does not meet: that `~/.local/share/1bit/laya-first.json` "ships with the digests measured on 2026-11-05" and that "the table's validator (§8 step 3) fails" a row that skips one. Measured on main: `config/laya-first.json` — the file compiled in as `laya_first_default.h` — has no `template_sha256` in any of its four rows, nothing in `app/`, `src/`, `tests/`, `include/` or `os/` reads one, and no installed `<laya home>/laya-first.json` exists on the c25/c26 media, the staging trees or the box (§8 step 3 is where that field and the pull-time check are scheduled). A design note that asserts a check which does not run is the exact failure this row is supposed to prevent, so the paragraph now states what is true: - the one template check on a routed pull today is the load-time one, against the engine's `config/template-pins.json` — 7 names, three of them these rows; - that check warns unless the row ships `--template-pin-strict` (§9), and it catches one thing: the same name carrying a different template; - the field, the per-row requirement and the pull-time refusal are §8 step 3, and the digests they will need are already measured — `models/pins.json` records 57f1fd00f0… (short), b55ecfbbbc… (prose), 87710339d2… (code), 12827f24b7… (long_doc). Doc only; no code, no table and no operand changed. * docs/laya-first: cite the digests that are in this tree, not one an open PR would add The reviewer's blocker, and it is the same class as the text being fixed: the paragraph said the digests for the four rows "are already measured — models/pins.json records the template of every file we serve and publish", and `models/pins.json` does not exist in this tree (`ls` fails, `git cat-file -e origin/main:models/pins.json` fails). It is engine #1148, open, so a reader on `main` was sent to a path that is not there, and the "already measured" claim rested on an unmerged working tree — including the one digest that exists nowhere else yet (`short`'s 57f1fd00f0…). Now the paragraph says what a reader can check today: - three of the four rows are in `config/template-pins.json` **now** — zaya1-8b-q4_k_m.gguf b55ecfbbbc…, qwen3-coder-30b-a3b-instruct-q4_k_m.gguf 87710339d2…, qwen3.8-27b-ud-q4_k_xl.gguf 12827f24b7… — because they are the same files the load-time check already pins (verified against the tree at this head); - the fourth, `short`'s Qwen3-0.6B-Q8_0.gguf 57f1fd00f0…, is **not in any table in this tree yet**; it is measured, it is added by #1158, and it is recorded with every other served/published file in #1148's models/pins.json — said as such rather than implied to be here. The Q4NX sentence now links docs/npu-chat-template.md, which is in this tree (#1115). Nothing else in the paragraph changed: what runs today, what it catches, and what step 3 adds all verified in the review. --------- Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg>
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
Second commit
1796a81b:short(Qwen3-0.6B-Q8_0.gguf,57f1fd00…) was the onelaya-firstrouted row with no expectation — three of the four were pinned, the smallest was not. It is in now, so all
four router rows agree with
models/pins.json, and the table stands at 12 rows: every model this enginecan serve by name today.
Second commit
1796a81b:short(Qwen3-0.6B-Q8_0.gguf,57f1fd00…) was the onelaya-firstrouted row with no expectation — three of the four were pinned, the smallest was not. It is in now, so all
four router rows agree with
models/pins.json, and the table stands at 12 rows: every model this enginecan serve by name today.
Root cause.
config/template-pins.jsonpins 7 names, and not one of them is a row the applianceships. The c26 stick's staged marker is
Qwen3-4B-Instruct-2507-1bit(
unsloth/Qwen3-4B-Instruct-2507-GGUF:Qwen3-4B-Instruct-2507-Q4_K_M.gguf) andQwen3-0.6B-NPU-1bit(the
FastFlowLM/Qwen3-0.6B-NPU2container) — read from the shipping payload'sbin/resources/server_models.json(~/wt/payload-c26c,2026.47.0~8.f44ff953). An unpinned model islogged, not checked (docs/serve.md), so on the appliance's own default chat row
--template-pin-strictrefuses nothing: the owner's open decision "flip the appliance's serve rows tostrict" would have bought that model nothing at all.
Measured, not guessed. Both digests come from the bytes we ship, and the container one matches an
independent instrument:
qwen3-4b-instruct-2507-1bit,qwen3-4b-instruct-2507-q4_k_m.ggufc979e0e71a3e21b8f208e6ab120d5cb29327885f29d2a8b18fda67a723798e18Qwen3-4B-Instruct-2507-Q4_K_M.ggufon~/stage-c26(identical file to the c25 tree, 16/16 files compared), equal tomodels/pins.json'stemplate_sha256for the same pathqwen3-0.6b-npu-1bit,qwen3-0.6b-npu2a136047ba140347b359d17b6ff8fa1bf8cb29d23f0c07bef7e0bf479b8680793tokenizer_config.jsonchat_templatestring — the digest #1095's serve line printed on a real container; not the JSON file's own digest (51015f78…), which would be two numbers for one templateBoth names per row are real paths: the catalog id is what Lemonade passes as
--alias, the file/dir nameis what a hand
-mload uses — serve matches id first, then the file name, case-insensitively.The test makes the claim checkable, and bites.
tests/template_pin.shgains one case: a fixture namedqwen3-4b-instruct-2507-q4_k_m.ggufwhose template is not the pinned one must warn with that digest.Verified both ways — against the current main build (
~/wt/onebp-zyphra/build-main-merged/1bit, built11:56 today from the merged #1089/#1095) every existing case passes and the new case fails (row
absent), so the assertion detects the gap it was written for; with this change,
cmake -S . -B /tmp/tp-cfggeneratesgenerated/template_pins_default.hcontaining the new names, whichis exactly what the binary compiles in. CI runs the suite (
add_test(NAME template_pin …)+ctest), sothe green case is proven there rather than on my word.
Doc: one paragraph in docs/serve.md naming what the table covers and why a row is added from a measured
digest. No engine code changed; nothing about the appliance's launch line changes here (that is the
packaging decision, §9).