Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions src/gpu/modal_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,26 @@
app = modal.App("interscript-ml-export", image=IMAGE)


def normalize_precisions(precisions: "str | list[str]") -> list[str]:
"""Accept both invocation forms: the parity/margins entrypoints pass
a pre-split list, direct ::parity_model-style CLI calls pass a
comma string."""
if isinstance(precisions, str):
precisions = precisions.split(",")
return [q.strip() for q in precisions if q.strip()]


def pending_precisions(
out_dir: Path, mid: str, precisions: list[str]
out_dir: Path, mid: str, precisions: "str | list[str]"
) -> list[str]:
"""Precision stages still to run: the margin report is the last
artifact a stage writes, so its presence means the stage (parity
block in the zip included) completed durably. Preemption restarts
resume at the next stage instead of redoing hours of decode."""
return [
p for p in precisions if not (out_dir / f"{mid}-margins-{p}.json").exists()
p
for p in normalize_precisions(precisions)
if not (out_dir / f"{mid}-margins-{p}.json").exists()
]


Expand Down Expand Up @@ -336,9 +347,7 @@ def stage(event: str) -> None:
meta_path = Path("/root/interscript-ml", spec["metadata"])
mid = re.search(r"^id:\s*(\S+)", meta_path.read_text(encoding="utf-8"), re.M).group(1)
reports: dict[str, str] = {}
todo = pending_precisions(
out_dir, mid, [q.strip() for q in precisions.split(",") if q.strip()]
)
todo = pending_precisions(out_dir, mid, precisions)
for precision in todo:
zip_path = out_dir / f"{mid}-{precision}.zip"
stage(f"onnx decode {precision}")
Expand Down Expand Up @@ -406,7 +415,7 @@ def margin_model(
meta_path = Path("/root/interscript-ml", spec["metadata"])
mid = re.search(r"^id:\s*(\S+)", meta_path.read_text(encoding="utf-8"), re.M).group(1)
reports: dict[str, str] = {}
for precision in [p.strip() for p in precisions.split(",") if p.strip()]:
for precision in normalize_precisions(precisions):
zip_path = out_dir / f"{mid}-{precision}.zip"
if not zip_path.exists():
reports[precision] = "zip not exported (skipped)"
Expand Down
14 changes: 14 additions & 0 deletions tests/test_parity_stage_skip.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,17 @@ def test_zip_without_margin_report_still_runs(tmp_path: Path) -> None:
(tmp_path / "ara-diac-small-2.1-int8.zip").write_text("partial")
got = pending_precisions(tmp_path, "ara-diac-small-2.1", ["int8"])
assert got == ["int8"]


def test_list_input_from_entrypoint(tmp_path: Path) -> None:
# the parity/margins entrypoints pass precisions.split(",") — a
# list — into the remote functions; the functions must accept both
# forms (direct ::parity_model CLI invocation passes a string)
(tmp_path / "ara-diac-small-2.1-margins-fp32.json").write_text("{}")
got = pending_precisions(tmp_path, "ara-diac-small-2.1", ["fp32", "int8"])
assert got == ["int8"]


def test_string_input_strips_whitespace(tmp_path: Path) -> None:
got = pending_precisions(tmp_path, "m", "fp32, int8")
assert got == ["fp32", "int8"]
Loading