Skip to content

Commit dcf61ba

Browse files
author
Ronald Tse
committed
fix(export): parity/margins functions accept list or string precisions
The parity/margins entrypoints pass precisions.split(",") — a list — into the remote functions, which then called .split again and crashed with AttributeError. Every entrypoint invocation has been broken since the loops learned to split; only direct ::parity_model CLI calls worked. normalize_precisions accepts both forms.
1 parent 4edce57 commit dcf61ba

2 files changed

Lines changed: 29 additions & 6 deletions

File tree

‎src/gpu/modal_export.py‎

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,15 +191,26 @@
191191
app = modal.App("interscript-ml-export", image=IMAGE)
192192

193193

194+
def normalize_precisions(precisions: "str | list[str]") -> list[str]:
195+
"""Accept both invocation forms: the parity/margins entrypoints pass
196+
a pre-split list, direct ::parity_model-style CLI calls pass a
197+
comma string."""
198+
if isinstance(precisions, str):
199+
precisions = precisions.split(",")
200+
return [q.strip() for q in precisions if q.strip()]
201+
202+
194203
def pending_precisions(
195-
out_dir: Path, mid: str, precisions: list[str]
204+
out_dir: Path, mid: str, precisions: "str | list[str]"
196205
) -> list[str]:
197206
"""Precision stages still to run: the margin report is the last
198207
artifact a stage writes, so its presence means the stage (parity
199208
block in the zip included) completed durably. Preemption restarts
200209
resume at the next stage instead of redoing hours of decode."""
201210
return [
202-
p for p in precisions if not (out_dir / f"{mid}-margins-{p}.json").exists()
211+
p
212+
for p in normalize_precisions(precisions)
213+
if not (out_dir / f"{mid}-margins-{p}.json").exists()
203214
]
204215

205216

@@ -336,9 +347,7 @@ def stage(event: str) -> None:
336347
meta_path = Path("/root/interscript-ml", spec["metadata"])
337348
mid = re.search(r"^id:\s*(\S+)", meta_path.read_text(encoding="utf-8"), re.M).group(1)
338349
reports: dict[str, str] = {}
339-
todo = pending_precisions(
340-
out_dir, mid, [q.strip() for q in precisions.split(",") if q.strip()]
341-
)
350+
todo = pending_precisions(out_dir, mid, precisions)
342351
for precision in todo:
343352
zip_path = out_dir / f"{mid}-{precision}.zip"
344353
stage(f"onnx decode {precision}")
@@ -406,7 +415,7 @@ def margin_model(
406415
meta_path = Path("/root/interscript-ml", spec["metadata"])
407416
mid = re.search(r"^id:\s*(\S+)", meta_path.read_text(encoding="utf-8"), re.M).group(1)
408417
reports: dict[str, str] = {}
409-
for precision in [p.strip() for p in precisions.split(",") if p.strip()]:
418+
for precision in normalize_precisions(precisions):
410419
zip_path = out_dir / f"{mid}-{precision}.zip"
411420
if not zip_path.exists():
412421
reports[precision] = "zip not exported (skipped)"

‎tests/test_parity_stage_skip.py‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,17 @@ def test_zip_without_margin_report_still_runs(tmp_path: Path) -> None:
3333
(tmp_path / "ara-diac-small-2.1-int8.zip").write_text("partial")
3434
got = pending_precisions(tmp_path, "ara-diac-small-2.1", ["int8"])
3535
assert got == ["int8"]
36+
37+
38+
def test_list_input_from_entrypoint(tmp_path: Path) -> None:
39+
# the parity/margins entrypoints pass precisions.split(",") — a
40+
# list — into the remote functions; the functions must accept both
41+
# forms (direct ::parity_model CLI invocation passes a string)
42+
(tmp_path / "ara-diac-small-2.1-margins-fp32.json").write_text("{}")
43+
got = pending_precisions(tmp_path, "ara-diac-small-2.1", ["fp32", "int8"])
44+
assert got == ["int8"]
45+
46+
47+
def test_string_input_strips_whitespace(tmp_path: Path) -> None:
48+
got = pending_precisions(tmp_path, "m", "fp32, int8")
49+
assert got == ["fp32", "int8"]

0 commit comments

Comments
 (0)