"""Derive a block-FP8-visual-tower checkpoint from GLM-5.3-Flash-NVFP4-FP8ATTN-recal. Worker VISION (glm-max push). The r2-era "yields NaN image features" warning in model.py (~line 1050) is about inheriting the global fp8 quant_config while the checkpoint ships BF16 visual weights WITHOUT weight_scale_inv — i.e. a loader/format mismatch, not FP8 numerics of the tower. This conversion ships the scales, so with the manifest-gated model.py overlay (vision-test image) the tower's Linear layers load through vLLM's proven Fp8LinearMethod block path (same serialization as the r2 MLP/attention conversion: weight F8_E4M3 [N,K] + weight_scale_inv F32 [N/128,K/128], dequant multiplier amax/448). Converted (124 tensors, 1.0156 GiB BF16 -> ~0.512 GiB, all in shard 120): model.visual.blocks.{0..23}.attn.qkv.weight [3072,1024] model.visual.blocks.{0..23}.attn.proj.weight [1024,1024] model.visual.blocks.{0..23}.mlp.{gate,up,down}_proj.weight model.visual.merger.{proj,gate_proj,up_proj,down_proj}.weight NOT converted: patch_embed (Conv3d), downsample (Conv2d), all biases/norms (34.2 MiB total). Every shape and TP2 shard offset divides by 128 (verified: qkv q/k/v sections 1024 each -> 512/rank; merger 10240 -> 5120/rank). Config: prune the three visual ignore patterns; add FP8_BLOCK128 quantized_layers entries under the RUNTIME lookup prefixes ("visual.blocks.N.attn.qkv_proj" — note qkv_proj, multimodal.py renames the prefix when quant_config is passed) plus "model.visual." spellings and per-shard aliases (strategy-4 fused lookups), mirroring FIT's r4 alias precedent. Unlisted visual linears resolve to UnquantizedLinearMethod, so pruning the ignore patterns is safe for the conv/norm remainder. Usage: python3 convert_fp8visual.py # recal -> -recal-visfp8 SRC=
0.05: print(f" WARN {k} round-trip rel err {rel:.4%}") for nm, arr, dt in ( (k, qw, "F8_E4M3"), (k[: -len(".weight")] + ".weight_scale_inv", sc, "F32"), ): b = arr.tobytes() new_hdr[nm] = { "dtype": dt, "shape": list(arr.shape), "data_offsets": [off, off + len(b)], } blobs.append(b) off += len(b) size_delta += len(b) size_delta -= len(raw) else: new_hdr[k] = { "dtype": m["dtype"], "shape": m["shape"], "data_offsets": [off, off + len(raw)], } blobs.append(raw) off += len(raw) if meta is not None: new_hdr["__metadata__"] = meta hb = json.dumps(new_hdr, separators=(",", ":")).encode() pad = (-(8 + len(hb))) % 8 hb += b" " * pad tmp = dst + ".tmp" with open(tmp, "wb") as f: f.write(struct.pack("