# Purpose: the settings every script in this lab reads. Copy this file to `.env` # beside them and fill in the empty lines. Nothing here is a secret, and # nothing here should be committed: every value is a fact about your house. # Platform: mac (Track M). The fallback sections of the lab reuse MACHINE_NAME, # PEER_SSH and LABBOOK and ignore the rest. # Minimum memory: 32 GB per Mac for the primary path; 24 GB for the single-Mac path. # Assumes: `cp env-example.txt .env` and then an editor. The shell scripts read it # with `set -a; . ./.env; set +a`, so a value containing spaces needs quotes. # ----------------------------------------------------------------- who is who # A short name for THIS Mac, used as the record key in the lab notebook. Keep it # to something you will still recognise in six months, for example m1 or m2. MACHINE_NAME= # The name you can `ssh` to for the OTHER Mac, over your house network. RFC 8375 # reserves everything under `home.arpa` for names that mean something inside one # house and nothing outside it, so a value such as # PEER_SSH=mac-b.home.arpa # is exactly the intended use. Add it to /etc/hosts on both Macs, or as a static # entry on your router, as task 1 describes. Leave empty on the single-Mac path. PEER_SSH= # The name the OTHER Mac can ssh to for THIS one. Needed only by the JACCL # backend, whose configuration helper logs in to every node in turn. SELF_SSH= # The user account the scripts ssh as. Leave empty to use the account you are # logged in as, which is the usual case when the same username exists on both. SSH_USER= # --------------------------------------------------------------- the cluster # The host file mlx.launch reads, written by task 3. Keep it beside the scripts. HOSTFILE=hosts.json # The MLX distributed backend: ring or jaccl. Start with ring, which needs # nothing beyond a working Thunderbolt link, and move to jaccl once `ibv_devices` # lists devices on both machines. The single-Mac path uses ring. MLX_BACKEND=ring # How many ranks to launch. Two Macs means 2. On the single-Mac path this is also # 2, and both ranks run on this machine. RANKS=2 # The path to the python interpreter that has mlx-lm installed. It must be the # SAME path on both Macs. `mlx.launch --print-python` prints the one it would use. # Leave empty to let mlx.launch decide. CLUSTER_PYTHON= # The directory holding these scripts on BOTH Macs, at the same absolute path. # mlx.launch copies nothing: it runs the script it is given, wherever it is told # to look. Set this to that path, for example "$HOME/llm-course/part-21". CLUSTER_DIR= # ------------------------------------------------------------------ the model # The MLX repository to serve. Choose one whose implementation supports the split # you intend; the lesson lists which families support tensor parallelism and which # support pipelining. The primary path wants a model larger than one Mac's memory. MODEL_REPO=mlx-community/Qwen3-32B-8bit # The split: leave empty for tensor parallelism, or set to 1 for pipelining. # Pipelining downloads only each rank's own shard; tensor parallelism downloads # the whole repository on every machine. USE_PIPELINE= # A smaller model that fits on one Mac, used for the single-machine path and for # the one-machine baseline row of the results table. BASELINE_REPO=mlx-community/Qwen3-8B-4bit # ------------------------------------------------------------- the measurement # Where mlx_lm.server listens on rank 0, and where measure-pair.py points. SERVER_HOST=127.0.0.1 SERVER_PORT=8080 # exo's dashboard and API port. 52415 is its default; change it only if something # else already listens there. EXO_PORT=52415 # The directory you cloned exo into, used only to print the right start command. # Leave empty if you installed the macOS application instead of running from source. EXO_DIR= # The model id to ask exo to place. exo accepts a short id or a full Hugging Face # repository name; ask it what it knows with `curl http://localhost:52415/models`. EXO_MODEL=mlx-community/Qwen3-32B-8bit # Which placement to accept from exo's preview list, by index, counting from 0 # among the previews whose error field is null. Leave at 0 to take the first, # or run `bash run-exo.sh previews` and pick deliberately. EXO_PLACEMENT_INDEX=0 # The Thunderbolt interface to read byte counters from, as `networksetup # -listallhardwareports` names it for the Thunderbolt port your cable is in. # Leave empty and measure-pair.py records every interface it can see instead. LINK_IFACE= # How many requests per measurement, and how long each generation runs. Twenty # short requests give a steadier decode figure than one long one. REQUESTS=20 MAX_TOKENS=128 # The prompt length to use for the prefill measurement, in approximate tokens. # Prefill is compute-bound and only shows itself on a prompt long enough to # matter; 2048 is long enough on every Mac in this course. PREFILL_TOKENS=2048 # ------------------------------------------------- the reality-check comparison # The two sides compare-endpoints.sh drives, for the reality check that follows # this lab. Each URL is the /v1 base of one machine's OpenAI-compatible endpoint; # each name is what you want that side called in the notebook and the write-up. # The two sides usually serve different models, because each serves the largest # one its own memory can hold, and saying so in the name keeps that visible. SIDE_A_NAME= SIDE_A_URL= SIDE_A_MODEL= SIDE_B_NAME= SIDE_B_URL= SIDE_B_MODEL= # Requests in flight for the batch measurement. Eight is enough to separate a # machine that batches well from one that does not, without needing a load # generator on a third machine. BATCH_CONCURRENCY=8 # ------------------------------------------------------------------ the record # The lab notebook every script appends its JSON line to. LABBOOK=labbook.md