Skip to content

Commit 2d94fe9

Browse files
author
Ronald Tse
committed
feat: server-side qwen-next orchestrator + durable eval markers
qwen_next_chain: workstation-independent state machine for E2/E3 — driven by volume markers only (best/config.json, final_eval.json, chain_log.jsonl), 20-min stall detection with respawn (distill_sequence resumes from the latest step checkpoint; redundant spawns are benign), idempotent across relaunches. Replaces the local shell chain, which died with the workstation and broke when the checked-out branch changed. evaluate_der now writes final_eval.json into the run dir — the durable verdict record r7-style _init_choice probes can read (run-006-morph lacked one, which is why r7 needed an explicit --init-run).
1 parent a08f76f commit 2d94fe9

2 files changed

Lines changed: 96 additions & 2 deletions

File tree

‎docs/EXPERIMENTS.md‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ All rows passed the CER parity gate at release. Readings:
6464
## E2 — PKM memory-layer student (ara-diac-small run-003-pkm)
6565

6666
- **Status:** in flight (launched 2026-08-27, A10G, labels reused from
67-
run-002; single-variable design vs run-002).
67+
run-002; single-variable design vs run-002). Survived one mid-run
68+
eviction: resumed from step-2000 via the checkpoint guard.
6869
- **Hypothesis:** the 5.68pp teacher→student gap (r6 2.5815 → ByT5-small
6970
8.259 full-set) is partly a *capacity* gap that lookup memory closes
7071
at near-zero compute — parameters and compute are separable (arXiv
@@ -85,7 +86,10 @@ All rows passed the CER parity gate at release. Readings:
8586

8687
## E3 — Muon optimizer A/B (run-004-pkm-muon)
8788

88-
- **Status:** queued behind E2 (same A10G slot, serialized).
89+
- **Status:** queued behind E2 (same A10G slot, serialized; the
90+
server-side `qwen_next_chain` orchestrator sequences both arms from
91+
durable volume markers — best/config.json, final_eval.json,
92+
chain_log.jsonl — with 20-min stall detection and respawn).
8993
- **Hypothesis:** orthogonalized-momentum updates (Newton–Schulz; the
9094
optimizer Qwen3.8-Flash-Next / LongCat report) help even in
9195
knowledge-limited distillation fine-tunes — unmeasured territory for

‎src/gpu/modal_distill.py‎

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,6 +1287,18 @@ def der_ce(model) -> dict:
12871287
result = {"teacher": der_ce(teacher), "student": der_ce(student)}
12881288
result["gate_delta"] = round(result["student"]["der_ce"] - result["teacher"]["der_ce"], 4)
12891289
result["gate_pass"] = result["gate_delta"] <= 0.5
1290+
# durable verdict marker: the run dir is the provenance record (also
1291+
# what r7-style _init_choice probes read)
1292+
import json
1293+
1294+
out_root = Path(
1295+
vol_map[spec.get("out_volume", spec.get("teacher_volume", "rababa"))]
1296+
) / spec["out"]
1297+
out_root.mkdir(parents=True, exist_ok=True)
1298+
(out_root / "final_eval.json").write_text(
1299+
json.dumps(result, indent=2), encoding="utf-8"
1300+
)
1301+
CHECKPOINTS.commit()
12901302
return result
12911303

12921304

@@ -1602,3 +1614,81 @@ def probe_pkm_gates(spec_id: str = "ara-diac-small-pkm") -> dict:
16021614
@app.local_entrypoint()
16031615
def pkm_gates(spec: str = "ara-diac-small-pkm") -> None:
16041616
print(probe_pkm_gates.remote(spec))
1617+
1618+
1619+
@app.function(
1620+
cpu=1,
1621+
memory=1024,
1622+
timeout=24 * 3600,
1623+
volumes={"/checkpoints": CHECKPOINTS, "/secryst-checkpoints": SECRYST_CHECKPOINTS},
1624+
)
1625+
def qwen_next_chain() -> dict:
1626+
"""Server-side orchestrator for the qwen-next experiments (E2/E3):
1627+
workstation-independent, self-healing, idempotent — the replacement
1628+
for local shell chains that die with the workstation or break when
1629+
the repo's checked-out branch changes.
1630+
1631+
State machine per arm, driven by durable volume markers only:
1632+
best/config.json absent -> watch step-* checkpoints; respawn
1633+
training if no progress for 20 min
1634+
(distill_sequence resumes from the
1635+
latest checkpoint; a redundant spawn
1636+
is benign — a finished run saves
1637+
best again and exits)
1638+
best present, final_eval.json absent -> evaluate_der (which now
1639+
writes final_eval.json itself)
1640+
final_eval.json present -> arm done
1641+
1642+
Audit trail: chain_log.jsonl in each run dir. If this function times
1643+
out (24h) or is evicted, relaunching continues from the markers:
1644+
1645+
modal run --detach src/gpu/modal_distill.py::qwen_chain
1646+
"""
1647+
import json
1648+
import time
1649+
from pathlib import Path
1650+
1651+
ARMS = [
1652+
("ara-diac-small-pkm", "rababa_arabic_distill_small/run-003-pkm"),
1653+
("ara-diac-small-pkm-muon", "rababa_arabic_distill_small/run-004-pkm-muon"),
1654+
]
1655+
ROOT = Path("/checkpoints")
1656+
1657+
def log(run: str, event: str) -> None:
1658+
with (ROOT / run / "chain_log.jsonl").open("a", encoding="utf-8") as fh:
1659+
fh.write(json.dumps({"t": round(time.time()), "event": event}) + "\n")
1660+
CHECKPOINTS.commit()
1661+
1662+
def latest_step(run: str) -> int:
1663+
steps = [int(p.name.split("-")[1]) for p in (ROOT / run).glob("step-*")]
1664+
return max(steps) if steps else -1
1665+
1666+
status = {}
1667+
for spec_id, run in ARMS:
1668+
while not (ROOT / run / "best" / "config.json").exists():
1669+
CHECKPOINTS.reload()
1670+
before = latest_step(run)
1671+
log(run, f"watch step={before}")
1672+
time.sleep(1200)
1673+
CHECKPOINTS.reload()
1674+
after = latest_step(run)
1675+
if after == before and not (ROOT / run / "best" / "config.json").exists():
1676+
log(run, f"stalled at step={after}; respawning {spec_id}")
1677+
distill_sequence.spawn(spec_id, epochs=3)
1678+
log(run, "training complete (best present)")
1679+
if not (ROOT / run / "final_eval.json").exists():
1680+
log(run, "evaluating")
1681+
evaluate_der.remote(spec_id=spec_id)
1682+
log(run, "eval done")
1683+
status[run] = "complete"
1684+
return status
1685+
1686+
1687+
@app.local_entrypoint()
1688+
def qwen_chain() -> None:
1689+
handle = qwen_next_chain.spawn()
1690+
print(
1691+
f"spawned {handle.object_id}; durable markers: chain_log.jsonl, "
1692+
f"final_eval.json in each run dir; relaunch is idempotent",
1693+
flush=True,
1694+
)

0 commit comments

Comments
 (0)