Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
198bf71
Add the SCHEDULED consistent-cut trigger for scheduler-ordered stops
chowhsu Oct 14, 2026
17b98c3
rl-identity-tiering L9.1/L9.2: semantic hash dual write, tolerant Plu…
chowhsu Oct 14, 2026
eeaa187
Add trainer lifecycle, buffer and scheduler ports over IslandDriver
chowhsu Oct 14, 2026
4a39de3
Record trainer start phases; setup reads the image manifest; fix mode…
chowhsu Oct 14, 2026
a51fb3a
Record the sample-buffer watermark in the cut; wait after publications
chowhsu Oct 14, 2026
81b49f8
Add the durable file sample buffer with arrival polling
chowhsu Oct 14, 2026
766c927
Add merge metrics, island sync timing and syncer tape clock (L5.5-L5.7)
chowhsu Oct 14, 2026
2838755
Drop Megatron-Bridge's eager modelopt import in the ports image layer
chowhsu Oct 14, 2026
b8eb808
Add neutral CorrectionSpec fields and keep the correction group out o…
chowhsu Oct 14, 2026
25d07c1
Map correction settings to Miles, refuse decoupled-PPO breakers, reco…
chowhsu Oct 14, 2026
5976f1b
Raise policy-age cap to 4, emit offpolicy_gate, add off-policy correc…
chowhsu Oct 14, 2026
dc96be6
Make the submitted-phase clock a seam; update tests for the new prefe…
chowhsu Oct 14, 2026
619016a
Load fake lifecycle cuts with weights_only=True
chowhsu Oct 14, 2026
2352deb
Add scheduler S1: backlog start, ski-rental stop, journaled idle time
chowhsu Oct 14, 2026
3ffea04
Keep plugin sources and default tapes unchanged: build offpolicy_gate…
chowhsu Oct 14, 2026
b5e5328
Add gated opt-in SGLang snapshot mode for sampler engines (L8.2)
chowhsu Oct 14, 2026
98d88d1
rl-identity-tiering L9.4: yeto/codepack.py run code pack (git-patch m…
chowhsu Oct 14, 2026
0ffd52a
Merge branch 's24-miles-import' into s24-image
chowhsu Oct 14, 2026
8c9ff33
Record trainer_start T_s for non-echo sky islands
chowhsu Oct 14, 2026
50cb213
rl-identity-tiering L9.4-L9.6: launcher uses the run code pack
chowhsu Oct 14, 2026
6b444d9
Run the trainer scheduler in the head loop behind --rl-trainer-scheduler
chowhsu Oct 14, 2026
69a2722
Regenerate decoupling golden samples for the phase-1 correction hash …
chowhsu Oct 14, 2026
c34b0da
Wire the Miles rollout through the durable sample buffer (L4.5)
chowhsu Oct 14, 2026
da796bb
Map off-policy correction to verl fully_async and add CPU parity checks
chowhsu Oct 14, 2026
05c359c
Keep the sampler snapshot gate on the island side only
chowhsu Oct 14, 2026
47e3052
Pin Miles 17aeb5cb5 and ports image 17aeb5c-2fa8801
chowhsu Oct 14, 2026
0a5241f
Record the L8.2 NCCL-path snapshot compatibility pass
chowhsu Oct 14, 2026
c8a437e
Add convertible nodes: node classes, conversion port, cut rule, sched…
chowhsu Oct 14, 2026
95ba2e8
Merge branch 's24-l4-wiring' into s24-l5-remote
chowhsu Oct 14, 2026
8e5884a
Label offpolicy_gate events with the batch's engine backend
chowhsu Oct 14, 2026
d3a423a
Record the correction group and prox_anchor on the verl island tape
chowhsu Oct 14, 2026
5ff3b06
Start and stop a remote trainer island from the head scheduler (L5.3c)
chowhsu Oct 14, 2026
d944da3
Wire the Miles conversion port callbacks: load_version, engine in-fli…
chowhsu Oct 14, 2026
2a21396
Stamp the version and warm the LoRA adapter in MilesPublisher.load_ve…
chowhsu Oct 14, 2026
5fd3f3b
Add sampler-only and trainer islands over the sample buffer (L4.6)
chowhsu Oct 14, 2026
adc2417
Keep the trainer island binding free of an import cycle
chowhsu Oct 14, 2026
faef230
Read the convertible node's weight checksum after the KV onload
chowhsu Oct 14, 2026
34102b2
Merge branch 's24-l5-measure' into s24-integ
chowhsu Oct 14, 2026
108e66c
Merge branch 's24-l9-identity' into s24-integ
chowhsu Oct 14, 2026
83bff47
Merge branch 's24-l8-snapshot' into s24-integ
chowhsu Oct 14, 2026
3643236
Merge branch 's24-l5-remote' into s24-integ
chowhsu Oct 14, 2026
9bdcef9
Merge branch 's24-sampler-mode' into s24-integ
chowhsu Oct 14, 2026
e89bcf7
Merge branch 's24-l7-convert' into s24-integ
chowhsu Oct 14, 2026
e12c6f7
Compose the L5.4 sampler/trainer pair in one launch
chowhsu Oct 14, 2026
06d617b
Create the Modal volume when the code pack is first stored
chowhsu Oct 14, 2026
f013058
Pin Miles 44d41efd0 and ports image 44d41ef-2fa8801 (lazy modelopt)
chowhsu Oct 14, 2026
4649cc4
Add check-run levers for the L5.4 start/stop GPU check
chowhsu Oct 14, 2026
0d473e7
Create the Modal Volume when the code pack is stored
chowhsu Oct 14, 2026
bd81194
Let a no-sync buffer island train every parameter in run_miles
chowhsu Oct 14, 2026
8dec16e
Keep the off-policy gate inputs on the Miles rollout handle
chowhsu Oct 14, 2026
7174b9f
Accept --debug-rollout-only on a fixed-partition sampler island
chowhsu Oct 14, 2026
3e6eca4
Give the sampler island its rollout metadata sink
chowhsu Oct 14, 2026
183b724
Set the sampler island's train parallel config for Miles post-processing
chowhsu Oct 14, 2026
555286f
Merge s24-image into s24-integ
chowhsu Oct 14, 2026
e50885d
Merge branch 's24-gpu-l610' into s24-integ
chowhsu Oct 14, 2026
79280be
Put RayMetadataSink.discard on the Ray sink, not the dir source
chowhsu Oct 14, 2026
439705e
Count Miles' own stale buffer drops in offpolicy_gate
chowhsu Oct 14, 2026
dce70b3
Pin the clock in the compaction byte-identity test
chowhsu Oct 14, 2026
db1315b
Publish a no-sync full-parameter island through a full-parameter digest
chowhsu Oct 14, 2026
387d05b
Stop the trainer scheduler after repeated failed incarnations
chowhsu Oct 14, 2026
212a9be
Run Miles' end-of-round hooks in the sample buffer reader
chowhsu Oct 14, 2026
4345e7b
Merge s24-integ (dce70b3c) into s24-gpu-l54
chowhsu Oct 14, 2026
6160480
Time a held trainer island's start from its spawn
chowhsu Oct 14, 2026
2748b9b
Cut a full-parameter trainer: carry every trainable parameter
chowhsu Oct 14, 2026
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
14 changes: 13 additions & 1 deletion docker/miles-ports/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
FROM docker.io/radixark/miles@sha256:4d69750720eb1a4b99976a3fed45ed018e1fc4fff3d240667fa17d258dfc6e0c

ARG MILES_REPOSITORY=https://github.com/michaellchung/miles
ARG MILES_COMMIT=386ae3b07138ccd45ce4a07fc0383587f7968a3a
ARG MILES_COMMIT=44d41efd0a0428cb12b4ee34422de6f5e1ccb9d9
ARG SGLANG_REPOSITORY=https://github.com/michaellchung/sglang
ARG SGLANG_COMMIT=2fa880182eefdb31e64d8ea70317be27f9f32121
# setuptools-scm version of SGLANG_COMMIT (describe v0.5.20-69-g4e4148f1b);
Expand Down Expand Up @@ -47,5 +47,17 @@ RUN cd /sgl-workspace/sglang \
&& SETUPTOOLS_SCM_PRETEND_VERSION="$SGLANG_VERSION" \
SGLANG_BUILD_RUST_EXTS=none pip install -e "python[all]" --no-deps

# peft at the pinned MILES_PEFT_VERSION, no deps (as the build script's
# wheel unpack); island setup then reads it from the manifest, not by import.
ARG PEFT_VERSION=0.20.0
RUN pip install --no-deps "peft==$PEFT_VERSION"

# Lazy modelopt (rl-trainer-cold-start-cost L2.2), as the crane layer: the
# Megatron-Bridge package __init__ and the Megatron-LM patch.
COPY megatron_bridge_init.py /opt/sglang/lib/python3.12/site-packages/megatron/bridge/__init__.py
COPY megatron_lm_lazy_modelopt.patch /tmp/
RUN cd /root/Megatron-LM && git apply /tmp/megatron_lm_lazy_modelopt.patch \
&& rm /tmp/megatron_lm_lazy_modelopt.patch

# Provenance (written by the build script: component SHAs + base digests).
COPY --from=manifest image-manifest.json /opt/yeto/image-manifest.json
69 changes: 69 additions & 0 deletions docker/miles-ports/make_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@
fork repository (opaque whiteout hides the base history),
* rewrites SGLang's editable-install metadata (dist-info, .pth, finder,
``sglang/_version.py``) for the fork's setuptools-scm version,
* unpacks extra pure-Python wheels (``--wheel``, e.g. peft) into the base's
site-packages, as ``pip install --no-deps`` would,
* replaces the base's ``megatron/bridge/__init__.py`` (Megatron-Bridge
8cd3466) with ``megatron_bridge_init.py``: the same file without its eager
``import modelopt.torch`` (rl-trainer-cold-start-cost L2.2),
* rewrites the Megatron-LM files (/root/Megatron-LM, base commit a84b105)
touched by ``megatron_lm_lazy_modelopt.patch`` -- the patch applied to the
commit's files from ``--megatron-lm-src`` -- so a trainer process does not
import modelopt unless a model uses it (L2.2),
* adds /opt/yeto/image-manifest.json with every component SHA.

Approximate docker equivalent (unverified): docker/miles-ports/Dockerfile.
Expand All @@ -30,6 +39,7 @@
import subprocess
import tarfile
import tempfile
import zipfile
from pathlib import Path

SITE = "opt/sglang/lib/python3.12/site-packages"
Expand All @@ -38,6 +48,14 @@
# pyproject.toml, setup.cfg, requirements*, sgl-kernel/**, rust/**, csrc,
# .cu/.cpp -- changes what a real install builds and needs a docker build.
EXEMPT_PREFIXES = ("test/", "tests/", "docs/")
# Megatron-Bridge as the base installs it (pip, not editable) and the file the
# layer writes over its package __init__ (see megatron_bridge_init.py).
MEGATRON_BRIDGE_COMMIT = "8cd3466d14d2337c8492827b3712482c2b3e4866"
MEGATRON_BRIDGE_INIT = Path(__file__).with_name("megatron_bridge_init.py")
# Megatron-LM as the base checks it out (/root/Megatron-LM, on sys.path) and
# the patch the layer applies to it (see the comments it adds).
MEGATRON_LM_COMMIT = "a84b105473eca9c9c6e49dbf1c8aa460c5cf796e"
MEGATRON_LM_PATCH = Path(__file__).with_name("megatron_lm_lazy_modelopt.patch")


def git(repo: Path, *argv: str) -> str:
Expand Down Expand Up @@ -93,6 +111,45 @@ def version_file(version: str) -> bytes:
).encode()


def wheel_files(wheel: Path) -> dict[str, bytes]:
"""``{site-packages relative path: bytes}`` of a pure-Python wheel.

Only ``*-none-any`` wheels without a ``.data`` directory are accepted:
those install by unpacking alone (no scripts, headers or compiled code).
An ``INSTALLER`` file is added, as pip writes one.
"""

if not wheel.name.endswith("-none-any.whl"):
raise SystemExit(f"{wheel.name}: only pure-Python (*-none-any) wheels are supported")
files: dict[str, bytes] = {}
with zipfile.ZipFile(wheel) as archive:
for name in archive.namelist():
if name.endswith("/"):
continue
if ".data/" in name.split(".dist-info/")[0]:
raise SystemExit(f"{wheel.name}: has a .data directory ({name}); not supported")
files[name] = archive.read(name)
dist_info = {n.split("/")[0] for n in files if n.split("/")[0].endswith(".dist-info")}
if len(dist_info) != 1:
raise SystemExit(f"{wheel.name}: expected one .dist-info directory, found {sorted(dist_info)}")
files[f"{dist_info.pop()}/INSTALLER"] = b"yeto-make-layer\n"
return files


def patched_files(src: Path, commit: str, patch: Path) -> dict[str, bytes]:
"""``{repo path: bytes}`` of the files ``patch`` touches, as ``commit`` plus the patch."""
paths = [line.split("\t")[-1] for line in subprocess.run(
["git", "apply", "--numstat", str(patch)], check=True, capture_output=True, text=True
).stdout.splitlines()]
with tempfile.TemporaryDirectory(prefix="img-patch-") as tmp:
archive = subprocess.run(["git", "-C", str(src), "archive", commit, *paths],
check=True, capture_output=True).stdout
with tarfile.open(fileobj=io.BytesIO(archive)) as tar:
tar.extractall(tmp, filter="data")
subprocess.run(["git", "apply", str(patch.resolve())], cwd=tmp, check=True, capture_output=True)
return {p: Path(tmp, p).read_bytes() for p in paths}


def main() -> None:
p = argparse.ArgumentParser()
p.add_argument("--manifest-json", required=True, help="image-manifest.json content file")
Expand All @@ -108,6 +165,10 @@ def main() -> None:
p.add_argument("--sglang-version", required=True)
p.add_argument("--sglang-base-metadata", type=Path, required=True,
help="dir with the base image's sglang site-packages metadata")
p.add_argument("--wheel", type=Path, action="append", default=[],
help="pure-Python wheel unpacked into site-packages (repeatable)")
p.add_argument("--megatron-lm-src", type=Path, required=True,
help=f"clone containing Megatron-LM {MEGATRON_LM_COMMIT[:7]} (the base's checkout)")
p.add_argument("--mtime", type=int, required=True)
p.add_argument("--out", type=Path, required=True)
a = p.parse_args()
Expand Down Expand Up @@ -184,6 +245,14 @@ def add_tree(root: Path, dest: str) -> None:
add_file(f"{SITE}/{name}", data)
add_file("sgl-workspace/sglang/python/sglang/_version.py", version_file(new))

for wheel in a.wheel:
for name, data in sorted(wheel_files(wheel).items()):
add_file(f"{SITE}/{name}", data)

add_file(f"{SITE}/megatron/bridge/__init__.py", MEGATRON_BRIDGE_INIT.read_bytes())
for name, data in patched_files(a.megatron_lm_src, MEGATRON_LM_COMMIT, MEGATRON_LM_PATCH).items():
add_file(f"root/Megatron-LM/{name}", data)

add_dir("opt/yeto")
add_file("opt/yeto/image-manifest.json", Path(a.manifest_json).read_bytes())

Expand Down
46 changes: 46 additions & 0 deletions docker/miles-ports/megatron_bridge_init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Megatron Bridge - A component of the Megatron ecosystem."""

# yeto miles-ports image: the eager ``import modelopt.torch`` that Megatron-Bridge
# 8cd3466 runs here is removed (rl-trainer-cold-start-cost L2.2). It cost
# 21-30 s per trainer process (coldstart-gpu-review §6.2). Every bridge module
# that uses modelopt (training.checkpointing, distillation, conversion.modelopt_utils)
# imports it itself, so its plugins still load when a modelopt feature is used.
# Written over the base image's file by docker/miles-ports/make_layer.py.

import megatron.bridge.diffusion.models # noqa: F401 — registers diffusion bridges
import megatron.bridge.models # noqa: F401 — triggers all bridge and HF Auto class registrations
from megatron.bridge.models.conversion.auto_bridge import AutoBridge
from megatron.bridge.package_info import (
__contact_emails__,
__contact_names__,
__download_url__,
__homepage__,
__package_name__,
__repository_url__,
__version__,
)


__all__ = [
"__version__",
"__package_name__",
"__contact_names__",
"__contact_emails__",
"__homepage__",
"__repository_url__",
"__download_url__",
"AutoBridge",
]
190 changes: 190 additions & 0 deletions docker/miles-ports/megatron_lm_lazy_modelopt.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
diff --git a/megatron/post_training/checkpointing.py b/megatron/post_training/checkpointing.py
index 19df61b..70106de 100644
--- a/megatron/post_training/checkpointing.py
+++ b/megatron/post_training/checkpointing.py
@@ -5,15 +5,7 @@ import os
from pathlib import Path
from typing import Optional, Tuple, Union

-import modelopt
-import modelopt.torch.opt as mto
import torch.nn as nn
-from modelopt.torch.opt.plugins import (
- restore_sharded_modelopt_state as restore_sharded_modelopt_state_legacy,
-)
-from modelopt.torch.opt.plugins.mcore_dist_checkpointing import (
- _load_extra_state_from_sharded_checkpoint,
-)

from megatron.core import dist_checkpointing
from megatron.core.dist_checkpointing.serialization import _legacy_common_state_exists
@@ -22,7 +14,11 @@ from megatron.training import get_args
from megatron.training.checkpointing import _load_base_checkpoint, load_checkpoint
from megatron.training.utils import print_rank_0

-from .utils import print_distributed_quant_summary
+# yeto miles-ports image (rl-trainer-cold-start-cost L2.2): Megatron-LM a84b105 imported
+# modelopt (and .utils, which imports it) at module top. megatron.training's
+# get_model imports this module for has_modelopt_state, a file check that needs
+# no modelopt, so every trainer paid for modelopt. The functions that use
+# modelopt import it themselves.

logger = logging.getLogger(__name__)

@@ -109,6 +105,11 @@ def load_modelopt_state(model: nn.Module, load_dir: Optional[str] = None) -> Non
model: the model to load the modelopt_state into
load_dir: optionally provide a different loading path
"""
+ import modelopt.torch.opt as mto
+ from modelopt.torch.opt.plugins import (
+ restore_sharded_modelopt_state as restore_sharded_modelopt_state_legacy,
+ )
+
args = get_args()
load_dir = load_dir or args.load

@@ -162,6 +163,8 @@ def load_modelopt_checkpoint(
When loading an .nemo sharded checkpoint, this is usually `model.`. Otherwise, this is
typically an empty string.
"""
+ from .utils import print_distributed_quant_summary
+

def _remove_prefix_state_dict_pre_hook(
state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs
@@ -213,6 +216,12 @@ def restore_sharded_modelopt_state(model: list[nn.Module], checkpoint_name: str
"""Temporary function. Copy of modelopt.torch.opt.plugins.restore_sharded_modelopt_state.
Will be removed once modelopt.torch.opt.plugins.restore_sharded_modelopt_state is up to date.
"""
+ import modelopt
+ import modelopt.torch.opt as mto
+ from modelopt.torch.opt.plugins.mcore_dist_checkpointing import (
+ _load_extra_state_from_sharded_checkpoint,
+ )
+
if len(model) > 1:
raise ValueError("sharded_modelopt_state does not support virtual pipeline parallel!")

diff --git a/megatron/training/checkpointing.py b/megatron/training/checkpointing.py
index 41865c5..8d4cc10 100644
--- a/megatron/training/checkpointing.py
+++ b/megatron/training/checkpointing.py
@@ -3,6 +3,7 @@
"""Input/output checkpointing."""

import contextlib
+import importlib.util
import inspect
import multiprocessing
import os
@@ -67,14 +68,31 @@ except ImportError:


# [ModelOpt]: Import
-try:
- from modelopt.torch.opt.plugins import save_modelopt_state, save_sharded_modelopt_state
-
- from megatron.post_training.utils import print_distributed_quant_summary
-
- has_nvidia_modelopt = True
-except Exception:
- has_nvidia_modelopt = False
+# yeto miles-ports image (rl-trainer-cold-start-cost L2.2): Megatron-LM a84b105 imported
+# modelopt at module top here, which every trainer process paid for (21-30 s).
+# Every use below is a no-op for a model ModelOpt has not converted
+# (save_modelopt_state / save_sharded_modelopt_state return early on
+# ``not is_converted``; the quant summary has nothing to report), and only
+# modelopt's own code converts a model. So while modelopt is not imported in
+# this process, skipping them is exact; once it is, they run as before.
+has_nvidia_modelopt = importlib.util.find_spec("modelopt") is not None
+
+
+def _modelopt_api():
+ """ModelOpt checkpoint helpers, or None when no model here can carry ModelOpt state."""
+ if not has_nvidia_modelopt or "modelopt" not in sys.modules:
+ return None
+ try:
+ from modelopt.torch.opt.plugins import save_modelopt_state, save_sharded_modelopt_state
+
+ from megatron.post_training.utils import print_distributed_quant_summary
+ except Exception:
+ return None
+ return types.SimpleNamespace(
+ save_modelopt_state=save_modelopt_state,
+ save_sharded_modelopt_state=save_sharded_modelopt_state,
+ print_distributed_quant_summary=print_distributed_quant_summary,
+ )


_CHECKPOINT_VERSION = None
@@ -882,8 +900,8 @@ def save_checkpoint(
verify_integrity=args.verify_integrity,
)
# [ModelOpt]: save sharded modelopt_state
- if has_nvidia_modelopt:
- save_sharded_modelopt_state(model, checkpoint_name, (args.ckpt_format, 1))
+ if (modelopt_api := _modelopt_api()) is not None:
+ modelopt_api.save_sharded_modelopt_state(model, checkpoint_name, (args.ckpt_format, 1))
elif ckpt_type == CheckpointType.GLOBAL and ckpt_format in ["torch_dcp", "fsdp_dtensor"]:
if not torch.distributed.is_initialized() or torch.distributed.get_rank() == 0:
# TODO Handle non-empty directories (e.g., after a crash during saving).
@@ -940,11 +958,11 @@ def save_checkpoint(
)
else:
# [ModelOpt]: Inject modelopt_state into state_dict
- if has_nvidia_modelopt:
+ if (modelopt_api := _modelopt_api()) is not None:
if ckpt_type == CheckpointType.LOCAL:
print_rank_0('WARNING: Local checkpointing does not support nvidia_modelopt.')
else:
- save_modelopt_state(model, state_dict)
+ modelopt_api.save_modelopt_state(model, state_dict)

end_ckpt = time()
logger.debug(
@@ -2570,7 +2588,8 @@ def load_checkpoint(
log_printed = True

if has_nvidia_modelopt:
- print_distributed_quant_summary(model, msg="After loading checkpoint")
+ if (modelopt_api := _modelopt_api()) is not None:
+ modelopt_api.print_distributed_quant_summary(model, msg="After loading checkpoint")

# Load teacher model in Distillation mode.
if getattr(args, "export_kd_teacher_load", None):
diff --git a/megatron/training/training.py b/megatron/training/training.py
index d64ae84..eb51d7d 100644
--- a/megatron/training/training.py
+++ b/megatron/training/training.py
@@ -13,6 +13,7 @@ import argparse
import copy
import dataclasses
import functools
+import importlib.util
import gc
import inspect
import logging
@@ -204,12 +205,18 @@ try:
except ImportError:
has_rl_utils = False

-try:
- from modelopt.torch.distill.plugins.megatron import get_tensor_shapes_adjust_fn_for_distillation
+# yeto miles-ports image (rl-trainer-cold-start-cost L2.2): Megatron-LM a84b105 imported
+# modelopt at module top here, which every trainer process paid for (21-30 s).
+# The distillation helper is now imported where a modelopt-enabled run uses it.
+has_nvidia_modelopt = importlib.util.find_spec("modelopt") is not None

- has_nvidia_modelopt = True
-except ImportError:
- has_nvidia_modelopt = False
+
+def get_tensor_shapes_adjust_fn_for_distillation(*args, **kwargs):
+ from modelopt.torch.distill.plugins.megatron import (
+ get_tensor_shapes_adjust_fn_for_distillation as adjust_fn,
+ )
+
+ return adjust_fn(*args, **kwargs)

try:
from megatron.core.distributed import TorchFullyShardedDataParallel as torch_FSDP
2 changes: 2 additions & 0 deletions docs/MILES_RL.md
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,8 @@ the run exit 3. A synced run still fetches its checkpoint first.
| 4 | a fixed-roster RL island could not be recovered |
| 5 | the Modal app was not confirmed stopped after teardown |
| 6 | the run stalled: no island event for `--rl-stall-timeout` seconds (default 900, 0 disables) and not every island finalized |
| 7 | Modal moved an island to a different container mid-run |
| 8 | the trainer scheduler gave up: `--rl-trainer-max-failures` (default 3) trainer incarnations failed in a row |

For exit 5, every row of `modal app list` with the run's app name must be
`stopped` with 0 tasks; an earlier run's row with the same name counts too.
Expand Down
Loading
Loading