#!/usr/bin/env python3 """Build, inside the container, the two model folders the b12x vLLM needs -- symlinks + a few small JSON files, nothing copied, nothing written outside the container: /model the served model: every snapshot file linked, the 33 PLE-table files (shipped in an unindexed `ple-table/` subfolder) linked at top level, and * model.safetensors.index.json = the shipped index + the 128 `ngram_embedding.shard_N.weight` keys and the table's `ngram_embedding.weight_scale`. ONLY those keys: two of the table files also carry 1,057 tensors of the original checkpoint (layer 1's original experts, shared expert and router gate). A loader that reads by index key never touches them; one that reads whole files would silently overwrite V8's healed layer 1 -- see the loader filter in patch_b12x.py. * config.json = shipped config + `text_config.ple_embedding_dtype` (b12x's PLE storage needs it; absent -> bf16 assumed) + the `Qwen3_8FlashNextForConditionalGeneration` architecture name this vLLM registers (it does not know `Qwen4ExpForConditionalGeneration`). /draft-k10 the MTP draft: only the files the speculative head needs (its own tensors, the int4 lm_head, the embedding shard) + an index of just those keys + a config routing top-k 10 with the head's own shared-expert width (640; the model's is 1280). Slim on purpose: a draft folder that links every shard makes the draft load walk the whole model again (2026-09-11 and 2026-09-21 out-of-memory). usage: build_views.py prints one summary line; exit != 0 on any inconsistency """ import glob, json, os, shutil, struct, sys snap, root = (os.path.abspath(a) for a in sys.argv[1:3]) view, draft = os.path.join(root, "model"), os.path.join(root, "draft-k10") B12X_ARCH = "Qwen3_8FlashNextForConditionalGeneration" def fail(msg): sys.exit(f"build_views: {msg}") def header(path): with open(path, "rb") as fh: n = struct.unpack("