"""tools/make_profile.py - the expert-cache profile (`data/expert-profile.bin`): every (layer, expert) pair, ranked. `--expert-cache auto` fills the VRAM slots it can afford with the profile's pairs in order, so the ranking decides which experts start resident (the adaptive tier then swaps in what a conversation routes most). A profile that ranks fewer pairs than a card can hold caps the cache (issue #46: a 32 GB card stopped at 8,000 slots); this tool writes one that ranks all 24,576. The order: the base profile's ranking (default: the shipped data/expert-profile.bin), then the pairs your routing traces used, most frequent first, then every pair still missing, interleaved across the layers. python tools/make_profile.py [TRACE ...] [--base data/expert-profile.bin | --no-base] [--out PATH] [--n-expert 256] (a pruned model: GSQ-RCO Coder keeps 256 of 512) A routing trace comes from a one-shot engine run with `--dump-routing FILE` (a prompt typical of your use; the routed experts of every layer and position are written). Point the model config's `--expert-profile` at the result. """ import argparse import struct from collections import defaultdict from pathlib import Path ROOT = Path(__file__).resolve().parent.parent N_LAYER, N_EXPERT = 48, 512 MAGIC, VERSION = b"STRP", 1 def read_profile(path, n_expert=N_EXPERT): blob = Path(path).read_bytes() if blob[:4] != MAGIC: raise SystemExit(f"{path}: not a Strata profile") ver, nl, ne, slots, n = struct.unpack_from("<5I", blob, 4) if (nl, ne) != (N_LAYER, n_expert): raise SystemExit(f"{path}: {nl}x{ne}, not {N_LAYER}x{n_expert}") return [struct.unpack_from("