# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: OpenMDW-1.1 """Self-contained regression test for the SFT smoke launch flow. Re-runs the same ``torchrun`` invocation that ``launch_sft_llava_ov.sh`` executes (limited to 10 iterations, ``--deterministic`` mode) and asserts that the rank-0 ``loss`` and global ``clip_grad_norm`` reproduce the inline goldens at the bottom of this file. The launch goes through ``cosmos_framework.scripts.train --sft-toml=examples/toml/sft_config/.toml`` — the only training entrypoint after the structured-TOML refactor. Per-GPU goldens --------------- Goldens are keyed by detected GPU architecture (``torch.cuda.get_device_name``): * ``gb200`` — original values captured 2026-05-18 against the legacy ``cosmos_framework.scripts.train`` pipeline. The inputs and VLM backbone used at the time are not part of the OSS layout. The entries stay inline as a documented historical reference; don't re-run the GB200 path locally. * ``h100`` — captured on 8× H100 (4-GPU subset). The VLM backbone is ``Qwen/Qwen3-VL-8B-Instruct``. Input paths come from env vars matching the names in ``docs/training.md``:: MODEL_PATH VLM backbone (Qwen/Qwen3-VL-8B-Instruct local snapshot) Use ``tests/_stage_h100_inputs.sh`` to download/convert this and emit an ``env.sh`` that ``source``s ``MODEL_PATH`` before invoking pytest. This file is intentionally the only deliverable — the goldens are embedded as a Python constant and the ``torchrun`` command line is reproduced here, so the upstream launch shell stays untouched and there is no separate JSON file to commit. Invocation (on a 4-GPU node, inside the training container, from the repo root):: pytest -s tests/launch_regression_test.py --num-gpus=4 --levels=2 -o addopts= * ``--num-gpus=4 --levels=2`` matches the markers on the test below and lets the conftest's per-test setup pin ``CUDA_VISIBLE_DEVICES=0,1,2,3`` for torchrun. (``4`` is in ``ALL_NUM_GPUS`` in ``cosmos_framework/inference/fixtures/args.py``.) * ``-o addopts=`` clears the ``addopts`` line in the repo's ``.pytest.toml`` which references ``--suppress-no-test-exit-code`` from the optional ``pytest-custom-exit-code`` plugin (not installed in the training image). Determinism notes: * ``llava_ov_datapacker`` runs **without** ``--deterministic`` on H100 AND overrides ``model.config.deterministic=false``: the Qwen3-VL text path uses an attention backend whose Hopper FMHA backward kernel has no deterministic mode (raises ``NotImplementedError`` under PyTorch's deterministic context). ``VLMModel.__init__`` honors the config-level flag via ``init_flash_attn_meta`` independently of the launcher arg, so both must be off. It streams ``lmms-lab/LLaVA-OneVision-Data`` from the HuggingFace Hub with ``dataloader_train.num_workers=0`` so the data order is fully deterministic (single process); the only run-to-run noise left is the FMHA backward kernel. iter-0 is bit-exact (forward only) but iters 1+ drift (the Hopper FMHA backward has no deterministic mode — confirmed: forcing ``deterministic=true`` raises ``NotImplementedError``, and a 2-run ``num_workers=0`` check still drifts ≤0.006 on iters 1-9). All 10 iters are asserted with a tiered tolerance (``loss_tol_bands``): iter-0 at 1e-3, iters 1-2 at 1e-2, iters 3-9 at 2e-2. Refreshing the goldens (after an intentional numerical change):: COSMOS_REGRESSION_UPDATE_GOLDENS=1 pytest -s launch_regression_test.py ... That prints the captured series for each spec; copy them into the matching ``_GOLDENS[]`` entry below. """ from __future__ import annotations import os import re import shutil import socket import subprocess import sys from dataclasses import dataclass, field from pathlib import Path import pytest from cosmos_framework.inference.fixtures.args import MAX_GPUS THIS_DIR = Path(__file__).resolve().parent # ``cosmos_framework.scripts.train`` and the ``--sft-toml=...`` paths are relative to # the repo root; we always invoke torchrun from there. REPO_ROOT = THIS_DIR.parent def _free_port() -> int: """Return a currently-free TCP port for torchrun's rendezvous, instead of a hardcoded ``master_port`` that ``EADDRINUSE``s when a prior run lingers.""" with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind(("", 0)) return s.getsockname()[1] # --- per-arch input paths ---------------------------------------------------- # # GB200: the original input snapshot lived on an internal read-only filesystem # that is not in the OSS layout, so the GB200 path is not runnable here. The # GB200 goldens dict is kept as a historical reference; ``_resolve_paths`` # below skips the GB200 arch instead of re-running it. def _hf_download(args: list[str]) -> str: """``uvx hf download --quiet`` -> the local path it prints (from the HF cache).""" result = subprocess.run( ["uvx", "hf@latest", "download", *args, "--quiet"], cwd=str(REPO_ROOT), capture_output=True, text=True, ) if result.returncode != 0: pytest.fail(f"hf download failed for {args} (exit {result.returncode}):\n{result.stdout}\n{result.stderr}") lines = [ln.strip() for ln in result.stdout.splitlines() if ln.strip()] if not lines: pytest.fail(f"hf download for {args} printed no path:\n{result.stdout}\n{result.stderr}") return lines[-1] def _convert_nano_dcp(dest: Path) -> None: """Convert the Cosmos3-Nano checkpoint to DCP at ``dest`` (Step 2 of docs/training.md).""" env = os.environ.copy() env["PYTHONPATH"] = f".:{env.get('PYTHONPATH', '')}" result = subprocess.run( [ sys.executable, "-m", "cosmos_framework.scripts.convert_model_to_dcp", "-o", str(dest), "--checkpoint-path", "Cosmos3-Nano", ], cwd=str(REPO_ROOT), env=env, ) if result.returncode != 0: pytest.fail(f"convert_model_to_dcp (Cosmos3-Nano) failed with exit code {result.returncode}") def _detect_arch() -> str: """Map ``torch.cuda.get_device_name(0)`` to a goldens key.""" import torch # local import keeps module import side-effects light if not torch.cuda.is_available(): return "unknown" name = torch.cuda.get_device_name(0).upper() if "GB200" in name: return "gb200" # H200 shares the Hopper kernels with H100 and is treated identically here: # both map to the ``h100`` goldens key (the GitHub GPU CI runs on 8×H200). if "H100" in name or "H200" in name: return "h100" return "unknown" # Pinned revisions mirror tests/_stage_h100_inputs.sh so prepared inputs match # the captured h100 goldens. _BRIDGE_REVISION = "46468e12ac0dd36901e9e3240d4fc7620942b5d7" _QWEN_VL_REVISION = "0c351dd01ed87e9c1b53cbc748cba10e6187ff3b" # Tolerances for ``pytest.approx``. The launch passes ``--deterministic`` and # ``PYTHONHASHSEED=42``; the tolerance only absorbs minor noise from # non-deterministic NCCL reductions. _DEFAULT_RTOL = 1e-3 _DEFAULT_ATOL = 1e-3 # --- log parsers ------------------------------------------------------------- # # VLM (``pre_exp012_llava_ov_datapacker``) logs the DP-reduced loss on rank 0:: # # train/loss_avg: 1.32225 (iteration 0) # # ``GradClip`` emits the global grad-norm via every rank, prefixed with # ``[RANK X]``. Key is ``clip_grad_norm/global`` for VLM. _VLM_LOSS_RE = re.compile(r"train/loss_avg:\s+([0-9.eE+-]+)\s+\(iteration\s+\d+\)") # VFM logs per-rank loss via the IterSpeed callback's on_training_step_end: # [RANK 0] Iteration 1: Hit counter: 1/50 | Loss: 0.2515 | Time: 120.42s _VFM_LOSS_RE = re.compile( r"\[RANK\s+0\]\s+Iteration\s+\d+:\s+Hit counter:[^|]+\|\s+Loss:\s+([0-9.eE+-]+)" ) _GRAD_NORM_RE = re.compile( r"\[RANK\s+0\][^\n]*clip_grad_norm/(?:[^/]+/)?global:\s+([0-9.eE+-]+)\s+\(iteration\s+\d+\)" ) @dataclass(frozen=True) class LaunchSpec: """A single launch flow under regression — mirrors the launcher shell.""" key: str # goldens key + pytest parametrize id source sft_toml: str # ``--sft-toml=...`` value, relative to REPO_ROOT extra_hydra_args: tuple[str, ...] loss_re: re.Pattern[str] deterministic_iters: int # how many leading iters are bit-exact deterministic extra_env: dict[str, str] = field(default_factory=dict) nproc_per_node: int = 4 # Some specs can't run under ``--deterministic`` on H100: the Qwen3-VL text # attention's Hopper FMHA backward kernel has no deterministic mode and # raises NotImplementedError. For those specs we drop the flag and accept # the tighter goldens tolerance only on the iters that still reproduce in # practice (see ``deterministic_iters``). deterministic: bool = True # Per-spec goldens tolerance for ``pytest.approx``. Deterministic specs use # the tight default uniformly across all asserted iters. loss_rtol: float = _DEFAULT_RTOL loss_atol: float = _DEFAULT_ATOL # Optional tiered tolerance: each ``(count, rtol, atol)`` applies to the next # ``count`` iters in order, and the counts must sum to ``deterministic_iters``. # Lets the reasoner tighten its bit-exact iter-0 while loosening the # non-deterministic tail. When empty, all iters use ``loss_rtol/loss_atol``. loss_tol_bands: tuple[tuple[int, float, float], ...] = () # 4-GPU specs run by ``test_launch_regression``; 8-GPU specs run by # ``test_launch_regression_8gpu`` (the ``gpus`` marker carries only one value, # so the test functions are split). _SPEC_KEYS = ( "llava_ov_datapacker", "vision_sft_nano", ) _SPEC_KEYS_8GPU = ("vision_sft_super",) def _build_specs(paths: dict[str, str]) -> dict[str, LaunchSpec]: """Build the per-arch ``LaunchSpec`` list using the resolved input paths.""" # vision_sft_super needs a Cosmos3-Super DCP; the default staging script # only produces Cosmos3-Nano. If BASE_CHECKPOINT_PATH_SUPER is set, # redirect BASE_CHECKPOINT_PATH for this spec via extra_env. super_extra_env: dict[str, str] = {} if super_ckpt := os.environ.get("BASE_CHECKPOINT_PATH_SUPER"): super_extra_env["BASE_CHECKPOINT_PATH"] = super_ckpt return { "llava_ov_datapacker": LaunchSpec( # Replicates launch_sft_llava_ov.sh, capped to 10 iters. key="llava_ov_datapacker", sft_toml="examples/toml/sft_config/llava_ov_datapacker.toml", extra_hydra_args=( # TAIL_OVERRIDES from launch_sft_llava_ov.sh — fields not modeled # by SFTExperimentConfig. f"model.config.policy.backbone.model_name={paths['vlm_model_path']}", "data_setting.max_tokens=16000", # 4-GPU subset for the test (TOML pins dp_shard=8 for the 8-GPU # launch shell). "model.config.parallelism.data_parallel_shard_degree=4", # The Hopper FMHA backward raises under PyTorch # deterministic mode, so both the config default and the # launcher's --deterministic flag must be off (see the # determinism notes in the module docstring). "model.config.deterministic=false", # num_workers=0: fully-ordered single-process streaming, so the # only run-to-run noise is the FMHA backward kernel, not data # order. prefetch_factor/persistent_workers must be unset for # num_workers=0 (torch DataLoader rejects them otherwise). "dataloader_train.num_workers=0", "dataloader_train.prefetch_factor=null", "dataloader_train.persistent_workers=false", # Regression-specific tweaks. "trainer.max_iter=10", "trainer.logging_iter=1", "job.wandb_mode=disabled", "ckpt_type=dummy", "checkpoint.load_from_object_store.enabled=false", "checkpoint.save_to_object_store.enabled=false", "upload_reproducible_setup=false", ), loss_re=_VLM_LOSS_RE, deterministic_iters=10, deterministic=False, # Tiered tolerance: iter-0 is bit-exact (forward only) → 1e-3; iters # 1+ carry the FMHA-backward noise (≤0.006 across two num_workers=0 # runs) → 1e-2 for the early iters 1-2, 2e-2 for the tail 3-9. loss_tol_bands=( (1, 1e-3, 1e-3), # iter 0 (2, 1e-2, 1e-2), # iters 1-2 (7, 2e-2, 2e-2), # iters 3-9 ), ), "vision_sft_nano": LaunchSpec( # Replicates launch_sft_vision_nano.sh, capped to 10 iters. # ``DATASET_PATH`` / ``WAN_VAE_PATH`` / ``BASE_CHECKPOINT_PATH`` flow # in via the TOML's ``${oc.env:...}`` interpolation; no Hydra plumbing # needed beyond the regression-cap overrides below. key="vision_sft_nano", sft_toml="examples/toml/sft_config/vision_sft_nano.toml", extra_hydra_args=( "model.config.parallelism.data_parallel_shard_degree=4", "model.config.compile.enabled=true", "trainer.max_iter=10", "trainer.logging_iter=1", "job.wandb_mode=disabled", "upload_reproducible_setup=false", "checkpoint.save_iter=999999", ), loss_re=_VFM_LOSS_RE, deterministic_iters=10, ), "vision_sft_super": LaunchSpec( # Replicates launch_sft_vision_super.sh on 8 GPUs (dp_shard=4 × cp=2), # capped to 10 iters. ``compile.enabled=false`` because the Super # backbone's compile path is not bit-exact across runs on H100. key="vision_sft_super", sft_toml="examples/toml/sft_config/vision_sft_super.toml", nproc_per_node=8, extra_hydra_args=( "model.config.parallelism.data_parallel_shard_degree=4", "model.config.parallelism.context_parallel_shard_degree=2", "model.config.compile.enabled=false", "trainer.max_iter=10", "trainer.logging_iter=1", "job.wandb_mode=disabled", "upload_reproducible_setup=false", "checkpoint.save_iter=999999", ), loss_re=_VFM_LOSS_RE, deterministic_iters=10, extra_env=super_extra_env, ), } # --- helpers ----------------------------------------------------------------- def _parse_series(log_text: str, loss_re: re.Pattern[str]) -> tuple[list[float], list[float]]: """Extract per-iteration rank-0 loss and global grad-norm series, in order.""" losses = [float(m.group(1)) for m in loss_re.finditer(log_text)] grad_norms = [float(m.group(1)) for m in _GRAD_NORM_RE.finditer(log_text)] assert losses and grad_norms, ( f"No loss/grad-norm pairs found in log (losses={len(losses)}, grads={len(grad_norms)})" ) assert len(losses) == len(grad_norms), ( f"loss vs grad-norm length mismatch ({len(losses)} vs {len(grad_norms)}): " "the log must contain one rank-0 entry of each per training step." ) return losses, grad_norms def _run_torchrun(spec: LaunchSpec, run_dir: Path) -> Path: """Invoke the same ``torchrun`` command that the launcher shell runs. Returns the path of the captured combined stdout+stderr log. """ run_dir.mkdir(parents=True, exist_ok=True) log_file = run_dir / "training.log" cmd = [ "torchrun", f"--nproc_per_node={spec.nproc_per_node}", f"--master_port={_free_port()}", "-m", "cosmos_framework.scripts.train", f"--sft-toml={spec.sft_toml}", ] if spec.deterministic: cmd.append("--deterministic") cmd += ["--", *spec.extra_hydra_args] env = os.environ.copy() # HF env mirrors what the launcher shell sets up; ``HF_TOKEN`` must already # be exported in the caller's environment if the experiment hits gated Hub # endpoints (e.g. the LLaVA-OneVision-Data streaming dataset). env.setdefault("HF_HOME", "/tmp/hf_cache") Path(env["HF_HOME"]).mkdir(parents=True, exist_ok=True) env.setdefault("HF_HUB_DISABLE_XET", "1") env["PYTHONHASHSEED"] = "42" # must be set before interpreter starts env["PYTHONPATH"] = f".:{env.get('PYTHONPATH', '')}" env["IMAGINAIRE_OUTPUT_ROOT"] = str(run_dir / "output") env.update(spec.extra_env) # Tee: stream the torchrun output live to stdout (so CI shows training # progress under ``pytest -s``) while capturing it into the log file. with log_file.open("w") as fp: proc = subprocess.Popen( cmd, env=env, cwd=str(REPO_ROOT), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, ) assert proc.stdout is not None for line in proc.stdout: sys.stdout.write(line) sys.stdout.flush() fp.write(line) returncode = proc.wait() if returncode != 0: # Tolerate harmless PyGIL teardown warnings if training did complete. text = log_file.read_text(errors="replace") if "Done with training" not in text: pytest.fail( f"{spec.key}: torchrun failed with exit code {returncode} " "and log does not contain 'Done with training'.\n" f"Log tail:\n{text[-2000:]}" ) return log_file # --- fixtures ---------------------------------------------------------------- @pytest.fixture(scope="module", autouse=True) def _require_4_gpus() -> None: """Skip the whole module unless we can launch 4-GPU training here.""" if shutil.which("torchrun") is None: pytest.skip("torchrun not on PATH — must run inside the training container") try: import torch except Exception as exc: # pragma: no cover — surfaces during dev only pytest.skip(f"torch unavailable ({exc!r})") if not torch.cuda.is_available() or torch.cuda.device_count() < 4: pytest.skip(f"requires 4 visible CUDA devices, found {torch.cuda.device_count()}") @pytest.fixture(scope="module") def h100_inputs(tmp_path_factory: pytest.TempPathFactory): """Provide the regression input paths, preparing any not already set in env. Mirrors the download/convert steps of ``tests/_stage_h100_inputs.sh`` (it does NOT set up the environment -- ``uv sync`` and the ``transformers`` pin still belong to that script / the caller). Honors pre-set env vars (so ``source env.sh`` still works); anything prepared here goes under a temp stage dir that is removed on teardown. The four vars are exported because the SFT TOMLs interpolate ``DATASET_PATH`` / ``WAN_VAE_PATH`` / ``BASE_CHECKPOINT_PATH`` at load time and the VLM spec passes ``MODEL_PATH`` as a Hydra backbone override. """ arch = _detect_arch() if arch not in ("h100", "gb200"): pytest.skip(f"no regression goldens for GPU arch {arch!r}; only h100/gb200 supported") if shutil.which("uvx") is None: pytest.skip("uvx not on PATH -- required to prepare regression inputs") stage = tmp_path_factory.mktemp("h100_stage") set_vars: list[str] = [] def _ensure(var: str, value_fn) -> None: if not os.environ.get(var): os.environ[var] = str(value_fn()) set_vars.append(var) _ensure( "DATASET_PATH", lambda: Path( _hf_download( ["--repo-type", "dataset", "nvidia/bridge-v2-subset-synthetic-captions", "--revision", _BRIDGE_REVISION] ) ) / "sft_dataset_bridge", ) _ensure("WAN_VAE_PATH", lambda: _hf_download(["Wan-AI/Wan2.2-TI2V-5B", "Wan2.2_VAE.pth"])) _ensure("MODEL_PATH", lambda: _hf_download(["Qwen/Qwen3-VL-8B-Instruct", "--revision", _QWEN_VL_REVISION])) def _make_dcp() -> Path: dest = stage / "Cosmos3-Nano-DCP" _convert_nano_dcp(dest) return dest _ensure("BASE_CHECKPOINT_PATH", _make_dcp) try: yield {"vlm_model_path": os.environ.get("MODEL_PATH", "")} finally: for var in set_vars: os.environ.pop(var, None) shutil.rmtree(stage, ignore_errors=True) # --- tests ------------------------------------------------------------------- def _assert_spec_matches_goldens(spec_key: str, tmp_path: Path, paths: dict[str, str]) -> None: """Re-run ``spec``'s torchrun command and check loss / grad-norm against goldens.""" arch = _detect_arch() spec = _build_specs(paths)[spec_key] log_path = _run_torchrun(spec, tmp_path) log_text = log_path.read_text(errors="replace") loss, grad_norm = _parse_series(log_text, spec.loss_re) # The run log also streamed live under ``pytest -s``; include its tail in any # failure message so the run detail is attached to the failure report too. run_detail = f"\n--- {spec.key} run log (last 4000 chars) ---\n{log_text[-4000:]}" assert len(loss) == 10, f"expected 10 iterations, parsed {len(loss)} (loss={loss}){run_detail}" # Refresh path: print captured values for manual copy into ``_GOLDENS``. if os.environ.get("COSMOS_REGRESSION_UPDATE_GOLDENS") == "1": print(f"\n# --- goldens for arch={arch!r} key={spec.key!r} ---") print(f'"{spec.key}": {{') print(f' "loss": {loss},') print(f' "grad_norm": {grad_norm},') print("},") pytest.skip( f"captured fresh series for arch={arch!r} key={spec.key!r}; copy the printed " f"dict into _GOLDENS[{arch!r}] at the bottom of launch_regression_test.py, " "then rerun without COSMOS_REGRESSION_UPDATE_GOLDENS to assert." ) arch_goldens = _GOLDENS.get(arch) assert arch_goldens is not None, ( f"no goldens table for arch {arch!r}; capture with COSMOS_REGRESSION_UPDATE_GOLDENS=1" ) expected = arch_goldens.get(spec.key) assert expected is not None, ( f"no goldens for arch={arch!r} key={spec.key!r}; capture with COSMOS_REGRESSION_UPDATE_GOLDENS=1" ) n = spec.deterministic_iters # Build the per-iter tolerance segments: either the spec's tiered bands or a # single uniform band spanning all asserted iters. if spec.loss_tol_bands: assert sum(c for c, _, _ in spec.loss_tol_bands) == n, ( f"{spec.key}: loss_tol_bands counts {[c for c, _, _ in spec.loss_tol_bands]} " f"must sum to deterministic_iters={n}" ) bands = spec.loss_tol_bands else: bands = ((n, spec.loss_rtol, spec.loss_atol),) start = 0 for count, rtol, atol in bands: end = start + count assert loss[start:end] == pytest.approx( expected["loss"][start:end], rel=rtol, abs=atol ), ( f"{spec.key} ({arch}): rank-0 loss[{start}:{end}] (rel/abs={rtol}) " f"does not match goldens\n" f" got : {loss[start:end]}\n" f" expected: {expected['loss'][start:end]}{run_detail}" ) start = end # ``grad_norm`` is optional: ``None`` skips the check when the FSDP # global-norm all-reduce isn't bit-exact on this arch. if expected["grad_norm"] is None: return assert grad_norm[:n] == pytest.approx( expected["grad_norm"][:n], rel=spec.loss_rtol, abs=spec.loss_atol ), ( f"{spec.key} ({arch}): global grad-norm[:{n}] does not match goldens\n" f" got : {grad_norm[:n]}\n" f" expected: {expected['grad_norm'][:n]}{run_detail}" ) # Define only the test function matching MAX_GPUS — the conftest rejects # ``gpus(N)`` markers outside the active ``ALL_NUM_GPUS = (0, 1, MAX_GPUS)``. if MAX_GPUS == 4: @pytest.mark.level(2) @pytest.mark.gpus(4) @pytest.mark.parametrize("spec_key", _SPEC_KEYS, ids=lambda k: k.removeprefix("launch_")) def test_launch_regression(spec_key: str, tmp_path: Path, h100_inputs: dict[str, str]) -> None: """Re-run ``spec``'s torchrun command and check loss / grad-norm against goldens.""" _assert_spec_matches_goldens(spec_key, tmp_path, h100_inputs) if MAX_GPUS == 8: @pytest.mark.skip(reason="vision_sft_super spec disabled") @pytest.mark.level(2) @pytest.mark.gpus(8) @pytest.mark.parametrize( "spec_key", _SPEC_KEYS_8GPU, ids=lambda k: k.removeprefix("launch_") ) def test_launch_regression_8gpu(spec_key: str, tmp_path: Path, h100_inputs: dict[str, str]) -> None: """8-GPU variant for ``vision_sft_super`` (dp_shard=4 × cp=2).""" _assert_spec_matches_goldens(spec_key, tmp_path, h100_inputs) # Goldens keyed by GPU arch then ``LaunchSpec.key``. Refresh with # ``COSMOS_REGRESSION_UPDATE_GOLDENS=1``. _GOLDENS: dict[str, dict[str, dict[str, list[float] | None]]] = { # Captured 2026-05-18 on a 4 × NVIDIA GB200 node with ``--deterministic`` # and seed 42 against the legacy training pipeline. VLM backbone is not # part of the OSS layout. "gb200": { "llava_ov_datapacker": { "loss": [1.32208, 1.20886, 1.39254, 1.40460, 1.16652, 1.24852, 1.38463, 1.22766, 0.96263, 1.14468], "grad_norm": [ 38.62454, 23.61477, 30.53218, 36.46255, 25.06240, 39.70305, 48.52226, 52.18334, 22.77521, 25.06970, ], }, # Captured 2026-06-09 on a 4 × NVIDIA GB200 node with seed 42 against the # current TOML-config pipeline (inputs prepared in-test by ``h100_inputs``, # which now also serves gb200). Runs under ``--deterministic`` so loss # reproduces bit-exact across all 10 iters; loss matches the h100 nano # series within ~1e-3. grad_norm is non-det because ``compile.enabled=true`` # makes the all-rank reduction not bit-exact, so None (same as h100). "vision_sft_nano": { "loss": [0.2269, 0.2181, 0.2026, 0.2309, 0.2178, 0.273, 0.2871, 0.2164, 0.2059, 0.264], "grad_norm": None, }, }, # Recaptured 2026-06-03 on a 4 × NVIDIA H100 80GB HBM3 node with seed 42 and # transformers==4.57.6. VLM model is ``Qwen/Qwen3-VL-8B-Instruct``; inputs are # prepared in-test by the ``h100_inputs`` fixture (or via # ``tests/_stage_h100_inputs.sh`` if its env vars are pre-set). "h100": { # num_workers=0, deterministic mode off (see the spec's hydra overrides # and the loss_tol_bands tiers). Centered on the midpoint of two H200 CI # runs (CI runs on H200) so the tiered bands keep maximum margin; iter-0 # is bit-exact across H100/H200 runs. grad-norm is non-det, so None. "llava_ov_datapacker": { "loss": [1.06924, 0.88399, 1.09293, 1.16314, 1.03592, 0.99041, 1.11041, 0.97001, 0.81246, 0.98548], "grad_norm": None, }, # Recaptured 2026-06-03 after the TOML-config rewrite shifted some # defaults. Runs under ``--deterministic`` so loss reproduces bit-exact # across all 10 iters, but grad_norm is non-det because # ``compile.enabled=true`` makes the all-rank reduction not bit-exact # on H100. "vision_sft_nano": { "loss": [0.2272, 0.2181, 0.2028, 0.2306, 0.218, 0.2734, 0.2865, 0.2162, 0.2055, 0.2643], "grad_norm": None, }, "vision_sft_super": { "loss": [0.2133, 0.2028, 0.1992, 0.2373, 0.2539, 0.2645, 0.2679, 0.2182, 0.1959, 0.2457], "grad_norm": [0.00403, 0.00255, 0.00412, 0.00485, 0.00305, 0.00331, 0.00375, 0.00371, 0.00313, 0.00276], }, }, } if __name__ == "__main__": # pragma: no cover — manual driver sys.exit(pytest.main([__file__, "-v", "-s", "-o", "addopts="]))