Download scripts/prep_resources_v2.py from workfunction/McBopomofoLM-models: direct link, hf CLI and curl.
- Browser
- Download file 6.03 kB
-
https://huggingface.co/workfunction/McBopomofoLM-models/resolve/main/scripts/prep_resources_v2.py
- Command line
-
hf download hf://workfunction/McBopomofoLM-models/scripts/prep_resources_v2.py
-
curl -L -o prep_resources_v2.py https://huggingface.co/workfunction/McBopomofoLM-models/resolve/main/scripts/prep_resources_v2.py
6.03 kB
| """Stage the McBopomofoLM v2 runtime files (Core ML only, ANE) into SlothE/Bundle/SlothE/ | |
| (folder reference -> Contents/Resources/SlothE/). | |
| Inputs (read-only): | |
| * $W/ane/enc/models/enc25m_multi_pal2_emb.mlpackage SlothE-T 25M encoder, 2-bit ternary, functions L8/L16/L32/L64/L256, | |
| caller-side embedding (ids = fp16 rows [1, L, 352], mask [1, L]) | |
| * $W/ane/enc/work/embed_f16_25m.bin the fp16 embedding table [1539, 352] (== w25m.npz["embed"]) | |
| * $W/ane/dec/mf/dec_mf_fp16.mlpackage SlothE decoder pred_q35_60m, fp16, functions t16/t32/t64/t96, B=3 | |
| * HF cache Luigi/sloth-ime-models @ e7d13c9 pred_q35_60m/tokenizer.json (the decoder's byte-level BPE) | |
| * the v1 staging's vocabulary files (syl_vocab.tsv, char2id.tsv, syl2legal.bin, variants.tsv; same vocab for 12M/25M) | |
| Outputs (SlothE/Bundle/SlothE/): | |
| enc25m.mlmodelc/ xcrun coremlcompiler compile of the encoder package | |
| dec60m.mlmodelc/ xcrun coremlcompiler compile of the decoder package | |
| enc25m_embed_f16.bin embedding table | |
| dec_tokenizer.json tokenizer.json, byte copy | |
| syl_vocab.tsv char2id.tsv syl2legal.bin variants.tsv | |
| runtime-manifest.txt "<sha256> <bytes> <path>" for every runtime file, including every file inside the | |
| two .mlmodelc directories; the app verifies it before loading (SlothEEngine.cpp) | |
| MANIFEST.txt NOTICE.txt OpenCC-LICENSE.txt | |
| usage: python3 prep_resources_v2.py (needs only the standard library + xcrun) | |
| """ | |
| import hashlib | |
| import shutil | |
| import subprocess | |
| import tempfile | |
| from pathlib import Path | |
| HERE = Path(__file__).resolve().parent | |
| W = HERE.parent.parent | |
| OUT = HERE / "Bundle" / "SlothE" | |
| ENC_PKG = W / "ane" / "enc" / "models" / "enc25m_multi_pal2_emb.mlpackage" | |
| EMBED = W / "ane" / "enc" / "work" / "embed_f16_25m.bin" | |
| DEC_PKG = W / "ane" / "dec" / "mf" / "dec_mf_fp16.mlpackage" | |
| HF = Path.home() / ".cache/huggingface/hub/models--Luigi--sloth-ime-models/snapshots/e7d13c9c451dc01ab9546be53c574f4dbe0fdd54" | |
| TOKENIZER = HF / "pred_q35_60m" / "tokenizer.json" | |
| KEEP = ["syl_vocab.tsv", "char2id.tsv", "syl2legal.bin", "variants.tsv", "OpenCC-LICENSE.txt"] | |
| def sha256(p): | |
| h = hashlib.sha256() | |
| with open(p, "rb") as f: | |
| for b in iter(lambda: f.read(1 << 20), b""): | |
| h.update(b) | |
| return h.hexdigest() | |
| def tree_sha(d): | |
| h = hashlib.sha256() | |
| for p in sorted(x for x in Path(d).rglob("*") if x.is_file()): | |
| h.update(str(p.relative_to(d)).encode() + b"\0" + sha256(p).encode() + b"\n") | |
| return h.hexdigest() | |
| def compile_pkg(pkg, name, tmp): | |
| src = Path(tmp) / f"{name}.mlpackage" | |
| shutil.copytree(pkg, src) | |
| subprocess.run(["xcrun", "coremlcompiler", "compile", str(src), str(tmp)], check=True, capture_output=True) | |
| dst = OUT / f"{name}.mlmodelc" | |
| shutil.rmtree(dst, ignore_errors=True) | |
| shutil.move(str(Path(tmp) / f"{name}.mlmodelc"), dst) | |
| return dst | |
| def main(): | |
| OUT.mkdir(parents=True, exist_ok=True) | |
| keep = {n: (OUT / n).read_bytes() for n in KEEP} | |
| for p in OUT.iterdir(): # v2 ships no GGUF, no ggml/llama.cpp licence files | |
| shutil.rmtree(p) if p.is_dir() else p.unlink() | |
| for n, b in keep.items(): | |
| (OUT / n).write_bytes(b) | |
| with tempfile.TemporaryDirectory() as tmp: | |
| compile_pkg(ENC_PKG, "enc25m", tmp) | |
| compile_pkg(DEC_PKG, "dec60m", tmp) | |
| shutil.copyfile(EMBED, OUT / "enc25m_embed_f16.bin") | |
| shutil.copyfile(TOKENIZER, OUT / "dec_tokenizer.json") | |
| assert (OUT / "enc25m_embed_f16.bin").stat().st_size == 1539 * 352 * 2 | |
| (OUT / "NOTICE.txt").write_text( | |
| "McBopomofoLM v2 bundles third-party parts (side-by-side build next to stock McBopomofo):\n" | |
| "- SlothE-T 25M encoder (enc25m.mlmodelc, enc25m_embed_f16.bin; converted to Core ML from the published " | |
| "weights) and its syl_vocab / syl2legal tables: huggingface.co/Luigi/sloth-ime-models, Apache-2.0.\n" | |
| "- SlothE decoder pred_q35_60m (dec60m.mlmodelc, dec_tokenizer.json; converted to Core ML from the published " | |
| "weights): huggingface.co/Luigi/sloth-ime-models, Apache-2.0.\n" | |
| "- char2id table (char2id.tsv, converted from enc/char2id.json): huggingface.co/spaces/Luigi/slothing-web, Apache-2.0.\n" | |
| "- Orthographic variant classes derived from OpenCC TWVariants.txt / HKVariants.txt, Apache-2.0 " | |
| "(OpenCC-LICENSE.txt).\n" | |
| "v2 runs the models with Core ML on the Apple Neural Engine only. It contains no ggml, llama.cpp or " | |
| "libslothe code.\n", encoding="utf-8") | |
| files = sorted(str(p.relative_to(OUT)) for p in OUT.rglob("*") | |
| if p.is_file() and p.name not in ("runtime-manifest.txt", "MANIFEST.txt", "NOTICE.txt", "OpenCC-LICENSE.txt")) | |
| (OUT / "runtime-manifest.txt").write_text( | |
| "# McBopomofoLM v2 checks the byte size and sha256 of every file below before loading a model.\n" | |
| "# Any mismatch or missing file = that model is not loaded (encoder -> stock McBopomofo,\n" | |
| "# decoder -> encoder-only in-walk). Format: <sha256> <bytes> <path>\n" | |
| + "".join(f"{sha256(OUT / n)} {(OUT / n).stat().st_size} {n}\n" for n in files), encoding="utf-8") | |
| lines = [ | |
| "SlothE runtime files for McBopomofoLM v2 (Core ML, ANE only)", | |
| f"encoder source: {ENC_PKG.relative_to(W)} tree sha256 {tree_sha(ENC_PKG)}", | |
| f"decoder source: {DEC_PKG.relative_to(W)} tree sha256 {tree_sha(DEC_PKG)}", | |
| f"embedding: {EMBED.relative_to(W)} sha256 {sha256(EMBED)}", | |
| f"tokenizer: HF pred_q35_60m/tokenizer.json sha256 {sha256(TOKENIZER)}", | |
| "compiled with: xcrun coremlcompiler compile", | |
| "config A' (walk2/frozen_final2.json): 25M, beta 0.3, gamma 0, pen -15, variant guard; decoder lambda 2, tau 0.5, top-3", | |
| "", | |
| ] | |
| (OUT / "MANIFEST.txt").write_text("\n".join(lines) + "\n", encoding="utf-8") | |
| print("\n".join(lines)) | |
| print(len(files), "files in runtime-manifest.txt") | |
| if __name__ == "__main__": | |
| main() | |