posttrainllm — nightly training queue
Project shape: every night the Mac produces a training artifact. Daytime work fills the queue and polishes infrastructure; nighttime runs through it.
How it works
- Each job is a shell script under
scripts/nightly/N*.sh, numbered lex-sortably. - The runner
scripts/pipelines/nightly.shpicks the lowest-numbered job that doesn’t have a matching.donefile in~/.cache/posttrainllm/nightly/done/, runs it undercaffeinate -di, logs to~/.cache/posttrainllm/nightly/logs/<ts>-<name>.log, and posts a completion notification. - Before bed:
./scripts/pipelines/nightly.sh &. Wake up to a done job + a log- a
/training-dashboardJSONL ready to drop.
- a
- To add a job: copy an existing
scripts/nightly/N*.shto the next number, edit the commands, commit. To re-run a job: delete its.donemarker. - To pause the queue: just don’t run
./scripts/pipelines/nightly.shthat night.
Tonight (next job the runner will pick)
The runner picks the topmost line in the Queue below whose .done
file is absent. Move lines to Done by hand when you want a visible
audit trail (the runner doesn’t edit this file — it touches .done
markers).
Queue
The path to A1 (first specialist · tool-caller) end-to-end. Honest state as of 2026-06-05 — parquet decoder + HF_TOKEN unblocking is v2 work (see “Known blockers” at bottom):
-
N01 · pull-datasets · DONE — Gutenberg corpus (32 MB clean text) + hermes-function-calling-v1 + SmolLM2-135M tokenizer all verified in place. FineWeb-Edu, xlam, UltraFeedback, BFCL still blocked (see “Known blockers”). N01 now a verifier, not a downloader.
-
N02 · huge-base-v1 · 9 hr ·
scripts/nightly/N02-huge-base-v1.shPretrain Huge preset (12L · d=256 · ctx=256, SmolLM2 BPE) on Gutenberg combined (32 MB · ~8M tokens · ~6 epochs at 200K steps). WSD schedule (warmup 1000, decay 20000). save-every 2000 + save-history (100 checkpoints, ~11 GB). –seed 42, spike detector on, JSONL log on. Output:/tmp/huge-base-v1.tinygpt+/tmp/huge-base-v1.step-*.tinygpt(history) +/tmp/huge-base-v1.jsonl. Smoke-tested at 100 steps: 11.4→6.5 loss, 6.2 step/s steady state. Known blockers (limit our recipe for now): -
posttrainllm download-datasetsaves parquet files as-is — no parquet→txt decoder yet. Blocks FineWeb-Edu (richer pretrain, 2.4 GB on disk), UltraFeedback (DPO data), and any other parquet-only HF dataset. Daytime task: add parquet decoding toposttrainllm download-dataset(or write a smallposttrainllm parquet-to-txtsubcommand). -
Salesforce/xlam-function-calling-60kis gated — needsHF_TOKEN. Eitherexport HF_TOKEN=hf_…orhuggingface-cli login. -
BFCL flat-emit hit a bug —
bfcl.jsonlis 0 bytes. The cached HF dataset is fine; needs a re-emit pass once we know what fixed it.
Planned (scripts written after N02 results land):
- N03 · sft-toolcaller-v1 · 8 hr · LoRA SFT on huge-base-v1 against
xlam+hermes concat (70K examples · 3 epochs · rank 8 · chatml).
Output:
/tmp/sft-toolcaller-v1.lora. Recipe finalized after we see N02 base PPL + sample quality — bad base → adjust SFT steps/lr. - N04 · dpo-toolcaller-v1 · ~6 hr · DPO LoRA on huge-base-v1 against
UltraFeedback (β=0.1, 1 epoch, rank 4). NOTE: posttrainllm dpo’s
reference model is the BASE, not the SFT-tuned base, so this is a
separate adapter from N03 rather than chained on top. v2 = adapter
composition at inference + a
dpo --init-from-sftflag. - N05 · eval-toolcaller-v1 · ~2 hr · generate on BFCL sample with base + each adapter; emit JSON with accuracy + per-category breakdown. Built after N03+N04 produce real adapters to score.
After N05 we have the first evidence the thesis holds (or doesn’t). Next queue depends on the numbers:
- If A1 beats some baseline on BFCL → N06+ trains a second specialist (code, math, or debugger) on the same base.
- If A1 doesn’t beat the baseline → N06 is a debug-and-retrain cycle with adjusted recipe (more SFT steps, different LoRA rank, etc.).
Done
(Touch a ~/.cache/posttrainllm/nightly/done/<name>.done marker to “complete”
a job manually. Or run the job via the runner and the marker is created
automatically on exit-0.)
Disk budget
Each Huge checkpoint is 115 MB. --save-history with save-every 1000
on a 50K-step run = 50 history checkpoints × 115 MB = **5.8 GB per
base pretrain**. Plan to keep:
- Latest base + its dashboard JSONL
- Last 5 history checkpoints from the latest base (for B13 interp work); archive the rest off-disk OR delete after the run finishes if not doing interp on it
- Every shipped specialist’s final adapter (~10 MB) + DPO adapter + eval JSON. These are the deliverables, keep forever.
Tonight’s projected disk use: ~7 GB (datasets + base + ckpts). 473 GB free → 60+ nightly runs of headroom before cleanup is a hard constraint.
Caveats
- Mega is OOM-blocked at ctx=1024 batch=16 on 48 GB. All queue entries use Huge. Until gradient checkpointing ships, Huge is the ceiling for our base pretrain.
- Spike rollback is v2. If a long run spikes hard at step 25K, you lose ~5 hr of compute. Spike detector logs the warning but doesn’t auto-rollback. Worth shipping spike rollback if a real run loses time to this.
- The Mac is committed for the full night.
caffeinate -diwrapping prevents sleep + idle; you can still use the laptop, but GPU contention with other workloads (Xcode builds, browser workers, etc.) will slow training. - First run is the riskiest. A flag-name typo, a numerics gotcha in WSD on a long horizon, a memory creep — all undetected until the first overnight. Watch the first hour of N02 before going to bed; if step rate looks healthy and spike detector is silent, walk away.