Skip to content

Commit 7983b66

Browse files
build: update PyTorch base image to 26.09 (#6258)
Signed-off-by: svcnemo-autobot <svcnemo-autobot@nvidia.com> Signed-off-by: Ajay Balasa <abalasa@nvidia.com> Signed-off-by: Ajay <abalasa@nvidia.com> Co-authored-by: svcnemo-autobot <svcnemo-autobot@nvidia.com>
1 parent 4f0e30e commit 7983b66

6 files changed

Lines changed: 79 additions & 5 deletions

File tree

‎.github/actions/test-template/action.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ runs:
177177
set -e
178178
export GH_TOKEN=${{ inputs.github-token }}
179179
180+
# Keep the FlashAttention generation policy scoped to CI tests.
181+
export NVTE_FLASH_ATTN_V2=1 NVTE_FLASH_ATTN_V3=0 NVTE_FLASH_ATTN_V4=0
182+
180183
if [[ "${{ contains(inputs.runner, 'gcp') }}" == "true" ]]; then
181184
export NCCL_ENV_PLUGIN=none
182185
export NCCL_NET_PLUGIN=none

‎docker/Dockerfile.ci‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
ARG BASE_IMAGE=nvcr.io/nvidia/pytorch:26.08-py3
15+
ARG BASE_IMAGE=nvcr.io/nvidia/pytorch:26.09-py3
1616
FROM ${BASE_IMAGE} AS base
1717

1818
ENV PATH="/opt/venv/bin:/usr/local/bin:$PATH" \

‎docker/common/fw_pyproject.toml‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ override-dependencies = [
6161
"pyzmq==25.1.2", # @URL: https://github.com/NVIDIA/NeMo/issues/13625
6262
"tiktoken>=0.9.0", # let megatron-hub win over nemo-toolkit
6363
"datasets==4.8.5",
64-
"pyarrow>=21.0.0", # datasets 4.8.5 needs >=21; override export-deploy's <21 cap (not exercised by training)
64+
# datasets 4.8.5 needs >=21, overriding export-deploy's <21 cap.
65+
# PyArrow 26 requires NumPy 2; preserve MBridge's NumPy 1 runtime.
66+
"pyarrow>=21.0.0,<26",
6567
"fsspec[http]>=2023.1.0,<=2024.9.0",
6668
"transformers; sys_platform == 'never'", # Defer to transformers in MBridge install
6769
"packaging>=25.0",

‎tests/unit_tests/ci/test_test_template.py‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ def test_container_startup_cannot_override_gcp_test_environment(
5454
"python3 - <<'PROBE'\n"
5555
"import json, os\n"
5656
"from pathlib import Path\n"
57-
"names = ('NCCL_ENV_PLUGIN', 'NCCL_NET_PLUGIN', 'NCCL_PROFILER_PLUGIN', 'NCCL_NET', 'STARTUP_WAS_RUN')\n"
57+
"names = ('NCCL_ENV_PLUGIN', 'NCCL_NET_PLUGIN', 'NCCL_PROFILER_PLUGIN', 'NCCL_NET', 'STARTUP_WAS_RUN', "
58+
"'NVTE_FLASH_ATTN_V2', 'NVTE_FLASH_ATTN_V3', 'NVTE_FLASH_ATTN_V4')\n"
5859
"Path('observed.json').write_text(json.dumps({key: os.environ.get(key) for key in names}))\n"
5960
"PROBE\n"
6061
f"exit {test_exit_code}\n"
@@ -65,6 +66,9 @@ def test_container_startup_cannot_override_gcp_test_environment(
6566
"BASH_ENV": str(startup),
6667
"GITHUB_OUTPUT": str(tmp_path / "github-output"),
6768
"NCCL_NET": "inherited-network",
69+
"NVTE_FLASH_ATTN_V2": "0",
70+
"NVTE_FLASH_ATTN_V3": "1",
71+
"NVTE_FLASH_ATTN_V4": "1",
6872
}
6973
subprocess.run(["bash", "-c", create], cwd=tmp_path, env=env, check=True, capture_output=True, text=True)
7074
result = subprocess.run(["bash", "job.sh"], cwd=tmp_path, env=env, capture_output=True, text=True)
@@ -77,4 +81,7 @@ def test_container_startup_cannot_override_gcp_test_environment(
7781
"NCCL_PROFILER_PLUGIN": plugin,
7882
"NCCL_NET": "Socket" if "gcp" in runner else "inherited-network",
7983
"STARTUP_WAS_RUN": "yes",
84+
"NVTE_FLASH_ATTN_V2": "1",
85+
"NVTE_FLASH_ATTN_V3": "0",
86+
"NVTE_FLASH_ATTN_V4": "0",
8087
}

‎tests/unit_tests/models/nemotron_omni/test_nemotron_omni_model.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,8 @@ def test_real_radio_image_forward_with_collator_owned_cp1_packing(
12611261
assert torch.isfinite(output).all()
12621262

12631263

1264+
# Dump all Python thread stacks before the CI wrapper terminates a stalled backward pass.
1265+
@pytest.mark.timeout(300, method="thread")
12641266
@pytest.mark.run_only_on("GPU")
12651267
def test_real_packed_multimodal_optimizer_step(single_rank_model_parallel):
12661268
del single_rank_model_parallel

‎tests/unit_tests/models/test_attention_backend_config.py‎

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import pytest
2323
from megatron.core.models.common.language_module.language_module import LanguageModule
24+
from megatron.core.transformer import attention as attention_module
2425
from megatron.core.transformer import utils as transformer_utils
2526
from megatron.core.transformer.enums import AttnBackend
2627

@@ -33,19 +34,21 @@
3334

3435
CONFIG_TYPES = (TransformerConfig, MLATransformerConfig, HeterogeneousTransformerConfig)
3536
BACKEND_ENV = ("NVTE_FLASH_ATTN", "NVTE_FUSED_ATTN", "NVTE_UNFUSED_ATTN")
37+
FLASH_VERSION_ENV = ("NVTE_FLASH_ATTN_V2", "NVTE_FLASH_ATTN_V3", "NVTE_FLASH_ATTN_V4")
3638
pytestmark = pytest.mark.unit
3739

3840

3941
@pytest.fixture(autouse=True)
4042
def isolated_attention_environment():
4143
with patch.dict(os.environ):
42-
for name in BACKEND_ENV:
44+
for name in (*BACKEND_ENV, *FLASH_VERSION_ENV):
4345
os.environ.pop(name, None)
4446
yield
4547

4648

47-
def make_config(config_type, backend):
49+
def make_config(config_type, backend, **overrides):
4850
kwargs = dict(num_layers=2, hidden_size=64, num_attention_heads=4, attention_backend=backend)
51+
kwargs.update(overrides)
4952
if config_type is HeterogeneousTransformerConfig:
5053
block = {
5154
"attention": {"no_op": False, "replace_with_linear": False, "num_query_groups": 4},
@@ -63,6 +66,63 @@ def configure_mcore_attention(config):
6366
LanguageModule._set_attention_backend(SimpleNamespace(config=config))
6467

6568

69+
@pytest.mark.parametrize("config_type", CONFIG_TYPES)
70+
@pytest.mark.parametrize("backend", list(AttnBackend))
71+
@pytest.mark.skipif(
72+
not hasattr(attention_module.Attention, "_resolve_flash_version"),
73+
reason="The frozen MCore dev pin predates FlashAttention version controls",
74+
)
75+
def test_explicit_fa2_disables_fa4_without_changing_backend(config_type, backend, monkeypatch):
76+
monkeypatch.setenv("NVTE_FLASH_ATTN_V4", "0")
77+
config = make_config(config_type, backend, flash_attention_version=2)
78+
79+
config.finalize()
80+
configure_mcore_attention(config)
81+
82+
assert config.flash_attention_version == 2
83+
assert config.attention_backend is backend
84+
assert tuple(os.environ[name] for name in FLASH_VERSION_ENV) == ("1", "0", "0")
85+
86+
87+
@pytest.mark.parametrize("config_type", CONFIG_TYPES)
88+
@pytest.mark.skipif(
89+
not hasattr(attention_module.Attention, "_resolve_flash_version"),
90+
reason="The frozen MCore dev pin predates FlashAttention version controls",
91+
)
92+
def test_direct_attention_uses_explicit_fa2_even_when_fa4_is_installed(config_type, monkeypatch):
93+
monkeypatch.setattr(attention_module, "HAVE_FA4", True)
94+
monkeypatch.setattr(attention_module, "HAVE_FA3", True)
95+
config = make_config(config_type, AttnBackend.auto, flash_attention_version=2)
96+
config.finalize()
97+
98+
attention = SimpleNamespace(flash_attention_version=config.flash_attention_version)
99+
assert attention_module.Attention._resolve_flash_version(attention) == (False, False)
100+
101+
102+
@pytest.mark.parametrize("config_type", CONFIG_TYPES)
103+
@pytest.mark.parametrize("version", [None, 2, 3, 4])
104+
@pytest.mark.skipif(
105+
not hasattr(attention_module.Attention, "_resolve_flash_version"),
106+
reason="The frozen MCore dev pin predates FlashAttention version controls",
107+
)
108+
def test_explicit_flash_version_is_preserved(config_type, version):
109+
config = make_config(config_type, AttnBackend.auto, flash_attention_version=version)
110+
111+
config.finalize()
112+
113+
assert config.flash_attention_version == version
114+
115+
116+
@pytest.mark.parametrize("config_type", CONFIG_TYPES)
117+
def test_default_flash_version_is_inherited_from_mcore(config_type):
118+
config = make_config(config_type, AttnBackend.auto)
119+
120+
config.finalize()
121+
122+
assert getattr(config, "flash_attention_version", None) is None
123+
assert all(name not in os.environ for name in FLASH_VERSION_ENV)
124+
125+
66126
@pytest.mark.parametrize("config_type", CONFIG_TYPES)
67127
def test_unset_backend_finalizes_to_mcore_auto(config_type):
68128
config = make_config(config_type, None)

0 commit comments

Comments
 (0)