Spaces:
Running on L40S
Running on L40S
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: OpenMDW-1.1 | |
| # Cosmos3-Nano GPU test suite on a self-hosted 8×H200 runner. | |
| # | |
| # A single ``pre-commit`` (lint) job runs first; the four GPU jobs all | |
| # ``needs:`` it, so they wait on ONE pre-commit run and are skipped if lint | |
| # fails — the single self-hosted runner is never spent on a lint-failing commit. | |
| # The four GPU jobs then run (one at a time on the single runner): | |
| # * training-smoke — Nano SFT pipeline (convert -> train 5 -> export -> t2i) | |
| # * generator-regression — vision_sft_nano loss vs goldens (4-GPU subset) | |
| # * inference-smoke — Nano multi-modality inference (t2vs + policy + forward_dynamics) | |
| # * reasoner-regression — llava_ov_datapacker loss vs goldens (4-GPU subset) | |
| # | |
| # Requires: | |
| # * a self-hosted runner labelled [self-hosted, gpu, h200] with 8 GPUs, | |
| # NVIDIA drivers, and `uv` on PATH; | |
| # * an `HF_TOKEN` repository secret (gated dataset/model downloads, incl. the | |
| # streamed LLaVA-OneVision-Data dataset). | |
| # | |
| # Inputs/checkpoints download to examples/ + the HF cache and are reused across | |
| # runs (the h100 goldens are reused on H200 — see _detect_arch). | |
| name: GPU Tests | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| concurrency: | |
| group: gpu-tests-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # Single lint gate: runs once on ubuntu-latest; every GPU job below waits on it | |
| # and is skipped if it fails. | |
| pre-commit: | |
| uses: ./.github/workflows/pre-commit.yml | |
| training-smoke: | |
| needs: pre-commit | |
| runs-on: [self-hosted, gpu, h200] | |
| timeout-minutes: 90 | |
| env: | |
| HF_TOKEN: ${{ secrets.HF_TOKEN }} | |
| HF_HUB_DISABLE_XET: "1" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: astral-sh/setup-uv@v7 | |
| - name: Sync environment (cu128-train) | |
| run: uv sync --all-extras --group=cu128-train | |
| # Full SFT pipeline: download + convert Nano->DCP, train 5 steps (loss | |
| # trend), export to HF safetensors, then a t2i generation from the export. | |
| # MAX_GPUS defaults to 8. -s streams the live process log. | |
| - name: Nano SFT pipeline smoke (convert -> train 5 -> export -> t2i, 8 GPU) | |
| run: | | |
| export LD_LIBRARY_PATH= | |
| uv run --all-extras --group=cu128-train python -m pytest -v -s \ | |
| tests/nano_training_smoke_test.py --num-gpus=8 --levels=2 -o addopts= | |
| # Clear the heavy artifacts (even on failure): examples/checkpoints (the | |
| # Cosmos3-Nano DCP + Wan VAE, ~30 GB) and the pytest tmp dirs (the SFT | |
| # checkpoint + logs). The small examples/data dataset and the HF cache are | |
| # intentionally kept so subsequent runs reuse them. | |
| - name: Clean up run outputs | |
| if: always() | |
| run: | | |
| rm -rf examples/checkpoints || true | |
| rm -rf "${TMPDIR:-/tmp}"/pytest-of-* /tmp/pytest-of-* || true | |
| generator-regression: | |
| needs: pre-commit | |
| runs-on: [self-hosted, gpu, h200] | |
| timeout-minutes: 60 | |
| env: | |
| HF_TOKEN: ${{ secrets.HF_TOKEN }} | |
| HF_HUB_DISABLE_XET: "1" | |
| # Select the 4-GPU regression test variant (uses 4 of the 8 GPUs). | |
| TEST_MAX_GPUS: "4" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: astral-sh/setup-uv@v7 | |
| - name: Sync environment (cu128-train) | |
| run: uv sync --all-extras --group=cu128-train | |
| # Generator (vision_sft_nano) loss vs the h100 goldens. -s streams the live log. | |
| - name: Generator regression (vision_sft_nano, 4-GPU subset) | |
| run: | | |
| export LD_LIBRARY_PATH= | |
| uv run --all-extras --group=cu128-train python -m pytest -v -s \ | |
| tests/launch_regression_test.py -k vision_sft_nano \ | |
| --num-gpus=4 --levels=2 -o addopts= | |
| # The h100_inputs fixture removes its DCP stage on teardown; clear the | |
| # pytest tmp dirs too (logs + any run output). The HF cache is kept. | |
| - name: Clean up run outputs | |
| if: always() | |
| run: | | |
| rm -rf "${TMPDIR:-/tmp}"/pytest-of-* /tmp/pytest-of-* || true | |
| inference-smoke: | |
| needs: pre-commit | |
| runs-on: [self-hosted, gpu, h200] | |
| timeout-minutes: 60 | |
| env: | |
| HF_TOKEN: ${{ secrets.HF_TOKEN }} | |
| HF_HUB_DISABLE_XET: "1" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: astral-sh/setup-uv@v7 | |
| - name: Sync environment (cu128-train) | |
| run: uv sync --all-extras --group=cu128-train | |
| # One inference call over t2vs (+sound), action policy, and forward_dynamics; checks each output. | |
| # MAX_GPUS defaults to 8. -s streams the live process log. | |
| # Reuse the same input-asset cache dir as the unittest job. | |
| - name: Nano inference smoke (t2vs + action policy + forward_dynamics, 8 GPU) | |
| run: | | |
| export LD_LIBRARY_PATH= | |
| export COSMOS_DOWNLOAD_CACHE_DIR="$RUNNER_WORKSPACE/cosmos_input_cache" | |
| uv run --all-extras --group=cu128-train python -m pytest -v -s \ | |
| tests/nano_inference_smoke_test.py --num-gpus=8 --levels=2 -o addopts= | |
| # Inference writes only the pytest tmp dir (the t2vs video + logs); the | |
| # checkpoint download stays in the HF cache (kept). No examples/ artifacts. | |
| - name: Clean up run outputs | |
| if: always() | |
| run: | | |
| rm -rf "${TMPDIR:-/tmp}"/pytest-of-* /tmp/pytest-of-* || true | |
| reasoner-regression: | |
| needs: pre-commit | |
| runs-on: [self-hosted, gpu, h200] | |
| timeout-minutes: 60 | |
| env: | |
| HF_TOKEN: ${{ secrets.HF_TOKEN }} | |
| HF_HUB_DISABLE_XET: "1" | |
| # Select the 4-GPU regression test variant (uses 4 of the 8 GPUs). | |
| TEST_MAX_GPUS: "4" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: astral-sh/setup-uv@v7 | |
| - name: Sync environment (cu128-train) | |
| run: uv sync --all-extras --group=cu128-train | |
| # Reasoner (llava_ov_datapacker) loss vs the h100 goldens. -s streams the live log. | |
| - name: Reasoner regression (llava_ov_datapacker, 4-GPU subset) | |
| run: | | |
| export LD_LIBRARY_PATH= | |
| uv run --all-extras --group=cu128-train python -m pytest -v -s \ | |
| tests/launch_regression_test.py -k llava_ov_datapacker \ | |
| --num-gpus=4 --levels=2 -o addopts= | |
| # The h100_inputs fixture removes its DCP stage on teardown; clear the | |
| # pytest tmp dirs too (logs + any run output). The HF cache is kept. | |
| - name: Clean up run outputs | |
| if: always() | |
| run: | | |
| rm -rf "${TMPDIR:-/tmp}"/pytest-of-* /tmp/pytest-of-* || true | |
| # Co-located unit tests: every *_test.py under cosmos_framework/ (CPU and GPU | |
| # together) in one pytest invocation, plus two torchrun steps for the | |
| # distributed tests that hardcode their world size. Runs parallel to the four | |
| # jobs above (all gated on the single pre-commit lint). | |
| unittest: | |
| needs: pre-commit | |
| runs-on: [self-hosted, gpu, h200] | |
| # 60 (not 30) so the first cold-cache run can download the Cosmos3-Nano | |
| # checkpoint for scripts/_test's convert->export->inference pipeline; steady | |
| # state (warm HF cache) is a few minutes. | |
| timeout-minutes: 60 | |
| env: | |
| HF_TOKEN: ${{ secrets.HF_TOKEN }} | |
| HF_HUB_DISABLE_XET: "1" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: astral-sh/setup-uv@v7 | |
| - name: Sync environment (cu128-train) | |
| run: uv sync --all-extras --group=cu128-train | |
| # Run the whole co-located suite (CPU + GPU). Tests that load internal | |
| # pretrained weights from S3 skip themselves when credentials/pretrained.secret | |
| # is absent (via RunIf / pytest.skip guards), so this is green without | |
| # internal credentials; provide the credential file on the runner to | |
| # exercise them. New tests are picked up automatically (no markers/lists). | |
| # Cache downloaded input assets in a persistent dir (outside the repo tree, | |
| # so the cleanup step keeps it) and reuse it across runs. | |
| - name: Unit tests | |
| run: | | |
| export LD_LIBRARY_PATH= | |
| export COSMOS_DOWNLOAD_CACHE_DIR="$RUNNER_WORKSPACE/cosmos_input_cache" | |
| uv run --all-extras --group=cu128-train python -m pytest -v -s \ | |
| cosmos_framework/ -o addopts= | |
| # The cfgp_ar / context_parallel tests call dist.init_process_group and | |
| # skip under plain pytest (world_size 1); they must be launched with | |
| # torchrun. They hardcode their world size via ParallelDims, so each file | |
| # needs the matching --nproc_per_node and they cannot share one launch: | |
| # * cfgp_ar -> cfgp=2, dp_shard=1 => world_size must be 2 | |
| # * context_parallel -> cp=4 (and cp=world_size) => world_size must be 4 | |
| # Over-provisioning (e.g. 8) makes ParallelDims' product != world_size and | |
| # fails, so these sizes are fixed, not "the more GPUs the better". | |
| - name: Distributed unit tests - cfgp_ar (torchrun, 2 ranks) | |
| run: | | |
| export LD_LIBRARY_PATH= | |
| uv run --all-extras --group=cu128-train torchrun --nproc_per_node=2 -m pytest -v \ | |
| cosmos_framework/model/vfm/mot/cfgp_ar_test.py -o addopts= | |
| - name: Distributed unit tests - context_parallel (torchrun, 4 ranks) | |
| run: | | |
| export LD_LIBRARY_PATH= | |
| uv run --all-extras --group=cu128-train torchrun --nproc_per_node=4 -m pytest -v \ | |
| cosmos_framework/model/vfm/mot/context_parallel_test.py -o addopts= | |
| # Clear everything the suite writes into the working tree (all gitignored | |
| # scratch): pytest tmp dirs (DCP checkpoint, logs), the script-test | |
| # `outputs/` dir, any `examples/checkpoints`, and the `schemas/` dir from | |
| # export_schemas_test. The HF cache lives outside the tree and is kept for | |
| # reuse across runs. Runs on success or failure. | |
| - name: Clean up run outputs | |
| if: always() | |
| run: | | |
| rm -rf "${TMPDIR:-/tmp}"/pytest-of-* /tmp/pytest-of-* \ | |
| outputs examples/checkpoints schemas || true | |