diff --git a/components/src/dynamo/vllm/tests/conftest.py b/components/src/dynamo/vllm/tests/conftest.py index fc0228fd844c..02e05106734d 100644 --- a/components/src/dynamo/vllm/tests/conftest.py +++ b/components/src/dynamo/vllm/tests/conftest.py @@ -16,6 +16,7 @@ # `None` = not yet attempted, `True` = succeeded, `False` = raised. _omni_importable: bool | None = None _vllm_importable: bool | None = None +_gpu_tiering_importable: bool | None = None def _can_import_vllm() -> bool: @@ -54,14 +55,49 @@ def _can_import_omni() -> bool: return _omni_importable +def _can_import_gpu_tiering() -> bool: + """Omit only absent optional KVCR or upstream adapter modules. + + Broken transitive dependencies or other API/import failures remain errors. + Supported-image qualification separately requires all GPU tests to collect. + """ + global _gpu_tiering_importable + if _gpu_tiering_importable is None: + if importlib.util.find_spec("kvcr") is None: + _gpu_tiering_importable = False + return False + try: + importlib.import_module("vllm.v1.kv_offload.tiering.kvcr.manager") + _gpu_tiering_importable = True + except ModuleNotFoundError as error: + if error.name not in { + "vllm", + "vllm.v1", + "vllm.v1.kv_offload", + "vllm.v1.kv_offload.tiering", + "vllm.v1.kv_offload.tiering.kvcr", + "vllm.v1.kv_offload.tiering.kvcr.manager", + }: + raise + _gpu_tiering_importable = False + return _gpu_tiering_importable + + def pytest_ignore_collect(collection_path, config): """Skip collecting vllm test files if vllm module isn't installed. - Checks test file naming pattern: test_vllm_*.py + Checks test_vllm_*.py and the retained-GPU framework-dependent modules. """ filename = collection_path.name - if filename.startswith("test_vllm_"): + gpu_tiering_test = filename in {"test_gpu_offloading.py", "test_gpu_memory.py"} + # FABRIC driver tests load only the stdlib helper, without vLLM or CUDA. + requires_vllm = ( + filename.startswith("test_vllm_") and filename != "test_vllm_mnnvl_memory.py" + ) + if requires_vllm or gpu_tiering_test: if not _can_import_vllm(): return True # vllm not available, skip this file + if gpu_tiering_test and not _can_import_gpu_tiering(): + return True # Omni tests import dynamo.vllm.omni.* which transitively imports # vllm_omni at module load. On CPU-only sample-runtime runners the # import chain reaches vllm._C and raises (NotImplementedError when diff --git a/components/src/dynamo/vllm/tests/test_optional_gpu_collection.py b/components/src/dynamo/vllm/tests/test_optional_gpu_collection.py new file mode 100644 index 000000000000..c1aab0eec8dc --- /dev/null +++ b/components/src/dynamo/vllm/tests/test_optional_gpu_collection.py @@ -0,0 +1,178 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Pure optional-framework collection boundary; never import serving libraries.""" + +import importlib.util +from pathlib import Path + +import pytest + +pytestmark = [ + pytest.mark.pre_merge, + pytest.mark.unit, + pytest.mark.vllm, + pytest.mark.gpu_0, + pytest.mark.core, +] + + +@pytest.mark.parametrize( + ("filename", "available", "expected", "checks"), + [ + ("test_gpu_offloading.py", False, True, 1), + ("test_gpu_memory.py", False, True, 1), + ("test_vllm_args.py", False, True, 1), + ("test_vllm_mnnvl_memory.py", False, None, 0), + ("test_kv_hints.py", False, None, 0), + ("test_gpu_offloading.py", True, None, 1), + ("test_gpu_memory.py", True, None, 1), + ], +) +def test_optional_framework_collection_boundary( + monkeypatch, filename, available, expected, checks +): + path = Path(__file__).with_name("conftest.py") + spec = importlib.util.spec_from_file_location("pure_vllm_collection_contract", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + calls = [] + + def can_import(): + calls.append("canonical_vllm_check") + return available + + monkeypatch.setattr(module, "_can_import_vllm", can_import) + monkeypatch.setattr(module, "_can_import_gpu_tiering", lambda: True) + result = module.pytest_ignore_collect(path.with_name(filename), None) + assert result is expected + assert len(calls) == checks + + +@pytest.mark.parametrize( + "missing", + [ + "vllm", + "vllm.v1", + "vllm.v1.kv_offload", + "vllm.v1.kv_offload.tiering", + "vllm.v1.kv_offload.tiering.kvcr", + "vllm.v1.kv_offload.tiering.kvcr.manager", + ], +) +def test_exact_optional_upstream_tiering_absence(monkeypatch, missing): + path = Path(__file__).with_name("conftest.py") + spec = importlib.util.spec_from_file_location("pure_gpu_tiering_absence", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + def absent(name): + assert name == "vllm.v1.kv_offload.tiering.kvcr.manager" + raise ModuleNotFoundError(name=missing) + + monkeypatch.setattr(module.importlib.util, "find_spec", lambda _: object()) + monkeypatch.setattr(module.importlib, "import_module", absent) + assert module._can_import_gpu_tiering() is False + + +def test_available_optional_upstream_tiering_is_collected(monkeypatch): + path = Path(__file__).with_name("conftest.py") + spec = importlib.util.spec_from_file_location("pure_gpu_tiering_available", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + calls = [] + + def available(name): + calls.append(name) + return object() + + monkeypatch.setattr(module.importlib.util, "find_spec", lambda _: object()) + monkeypatch.setattr(module.importlib, "import_module", available) + assert module._can_import_gpu_tiering() is True + assert calls == ["vllm.v1.kv_offload.tiering.kvcr.manager"] + + +def test_absent_optional_kvcr_is_cached_without_importing_upstream(monkeypatch): + path = Path(__file__).with_name("conftest.py") + spec = importlib.util.spec_from_file_location("pure_optional_kvcr_absence", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + calls = [] + + def absent(name): + assert name == "kvcr" + calls.append(name) + return None + + monkeypatch.setattr(module.importlib.util, "find_spec", absent) + monkeypatch.setattr( + module.importlib, + "import_module", + lambda _: pytest.fail("An absent KVCR must not import the GPU adapter"), + ) + assert module._can_import_gpu_tiering() is False + assert module._can_import_gpu_tiering() is False + assert calls == ["kvcr"] + + +@pytest.mark.parametrize( + ("filename", "expected"), + [ + ("test_gpu_offloading.py", True), + ("test_gpu_memory.py", True), + ("test_vllm_args.py", None), + ("test_kv_hints.py", None), + ("test_optional_gpu_collection.py", None), + ], +) +def test_missing_kvcr_omits_only_the_gpu_framework_file( + monkeypatch, filename, expected +): + path = Path(__file__).with_name("conftest.py") + spec = importlib.util.spec_from_file_location("pure_optional_kvcr_file_scope", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + monkeypatch.setattr(module, "_can_import_vllm", lambda: True) + monkeypatch.setattr(module.importlib.util, "find_spec", lambda _: None) + monkeypatch.setattr( + module.importlib, + "import_module", + lambda _: pytest.fail("The absent optional package must not be imported"), + ) + assert module.pytest_ignore_collect(path.with_name(filename), None) is expected + + +@pytest.mark.parametrize("filename", ["test_gpu_offloading.py", "test_gpu_memory.py"]) +def test_gpu_file_with_absent_optional_upstream_is_ignored(monkeypatch, filename): + path = Path(__file__).with_name("conftest.py") + spec = importlib.util.spec_from_file_location("pure_gpu_optional_file", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + monkeypatch.setattr(module, "_can_import_vllm", lambda: True) + monkeypatch.setattr(module, "_can_import_gpu_tiering", lambda: False) + assert module.pytest_ignore_collect(path.with_name(filename), None) is True + + +@pytest.mark.parametrize( + "error", + [ + ModuleNotFoundError(name="kvcr"), + ModuleNotFoundError(name="kvcr.config"), + ModuleNotFoundError(name="kvcr._bindings"), + ModuleNotFoundError(name="unrelated_dependency"), + ImportError("broken adapter ABI"), + RuntimeError("bad API"), + ], +) +def test_gpu_tiering_errors_are_not_optional_absence(monkeypatch, error): + path = Path(__file__).with_name("conftest.py") + spec = importlib.util.spec_from_file_location("pure_gpu_tiering_errors", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + def broken(_): + raise error + + monkeypatch.setattr(module.importlib.util, "find_spec", lambda _: object()) + monkeypatch.setattr(module.importlib, "import_module", broken) + with pytest.raises(type(error)): + module._can_import_gpu_tiering() diff --git a/components/src/dynamo/vllm/tests/test_vllm_mnnvl_memory.py b/components/src/dynamo/vllm/tests/test_vllm_mnnvl_memory.py new file mode 100644 index 000000000000..ac19f299dc9e --- /dev/null +++ b/components/src/dynamo/vllm/tests/test_vllm_mnnvl_memory.py @@ -0,0 +1,80 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +"""No-GPU VMM ownership, admission and rollback tests with a fake CUDA driver.""" + +import importlib.util +import sys +from pathlib import Path + +import pytest + +_helper_relative = Path("lib/bindings/kvbm/python/kvbm/vllm_integration/gpu_mnnvl.py") +_helper_file = next( + ( + parent / _helper_relative + for parent in Path(__file__).resolve().parents + if (parent / _helper_relative).is_file() + ), + None, +) +if _helper_file is None: + _package = importlib.util.find_spec("kvbm") + if _package is None or _package.submodule_search_locations is None: + raise ModuleNotFoundError( + "KVBM fabric helper source is unavailable", name="kvbm" + ) + _locations = list(_package.submodule_search_locations) + if len(_locations) != 1: + raise RuntimeError("One unambiguous KVBM helper source is required") + _helper_file = Path(_locations[0]) / "vllm_integration/gpu_mnnvl.py" +_helper_spec = importlib.util.spec_from_file_location( + __name__ + "._gpu_mnnvl", _helper_file +) +if _helper_spec is None or _helper_spec.loader is None: + raise RuntimeError("KVBM fabric helper source cannot be loaded") +m = importlib.util.module_from_spec(_helper_spec) +sys.modules[_helper_spec.name] = m +_helper_spec.loader.exec_module(m) +pytestmark = [ + pytest.mark.pre_merge, + pytest.mark.post_merge, + pytest.mark.unit, + pytest.mark.vllm, + pytest.mark.kvbm, + pytest.mark.gpu_0, +] + + +def test_effective_domain_matches_same_clique_not_gpu_uuid_and_separates_other_profiles(): + first = m.FabricIdentity("GPU-00000000-0000-0000-0000-000000000001", "1" * 32, 0) + second = m.FabricIdentity("GPU-00000000-0000-0000-0000-000000000002", "1" * 32, 0) + domain = m.effective_domain("layout-v1", first.domain) + assert domain == m.effective_domain("layout-v1", second.domain) + assert domain != m.effective_domain("other-layout", first.domain) + assert domain != m.effective_domain("layout-v1", "mnnvl:" + "2" * 32 + ":0") + assert domain != m.effective_domain("layout-v1", "mnnvl:" + "1" * 32 + ":1") + assert domain != "layout-v1" + + +@pytest.mark.parametrize( + "domain", + [ + "mnnvl:" + "0" * 32 + ":0", + "mnnvl:" + "1" * 32 + ":00", + "mnnvl:" + "1" * 32 + ":-1", + "mnnvl:" + "1" * 32 + ":4294967296", + "tcp", + None, + ], +) +def test_uncanonical_or_missing_fabric_domain_is_not_a_capability(domain): + with pytest.raises(ValueError): + m.effective_domain("layout-v1", domain) + + +@pytest.mark.parametrize("layout_domain", ["bad\x00layout", "", " padded ", None]) +def test_invalid_raw_layout_domain_cannot_be_hidden_by_compatibility_hash( + layout_domain, +): + with pytest.raises(ValueError): + m.effective_domain(layout_domain, "mnnvl:" + "1" * 32 + ":0") diff --git a/lib/bindings/kvbm/python/kvbm/vllm_integration/gpu_mnnvl.py b/lib/bindings/kvbm/python/kvbm/vllm_integration/gpu_mnnvl.py new file mode 100644 index 000000000000..0c0c24174d98 --- /dev/null +++ b/lib/bindings/kvbm/python/kvbm/vllm_integration/gpu_mnnvl.py @@ -0,0 +1,80 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Opt-in FABRIC VMM storage; importing this module does not load CUDA or NVML. + +Only allocation handles/addresses use host memory. KV bytes are mapped DEVICE +memory. Each PyTorch CUDA-array-interface storage retains its allocation owner; +the owner must outlive every local view and all native transfer leases. +""" + +import hashlib +import json +import uuid +from dataclasses import dataclass + + +def _gpu_uuid(value): + if not isinstance(value, str): + raise ValueError("A physical GPU UUID is required") + return "GPU-" + str(uuid.UUID(value.removeprefix("GPU-"))) + + +@dataclass(frozen=True) +class FabricIdentity: + gpu_uuid: str + cluster_uuid: str + clique_id: int + health_mask: int = 0 + + def __post_init__(self): + if self.gpu_uuid != _gpu_uuid(self.gpu_uuid): + raise ValueError("Canonical physical GPU UUID required") + if ( + not isinstance(self.cluster_uuid, str) + or len(self.cluster_uuid) != 32 + or any(c not in "0123456789abcdef" for c in self.cluster_uuid) + or int(self.cluster_uuid, 16) == 0 + or type(self.clique_id) is not int + or not 0 <= self.clique_id <= 0xFFFFFFFF + ): + raise ValueError("Completed nonzero MNNVL cluster and u32 clique required") + + @property + def domain(self): + return f"mnnvl:{self.cluster_uuid}:{self.clique_id}" + + +def effective_domain(layout_domain, fabric_domain): + """Bind the existing exact router domain to layout, transport and fabric.""" + if ( + not isinstance(layout_domain, str) + or not layout_domain + or layout_domain != layout_domain.strip() + or "\x00" in layout_domain + ): + raise ValueError("Explicit unpadded certified layout domain required") + parts = fabric_domain.split(":") if isinstance(fabric_domain, str) else [] + if ( + len(parts) != 3 + or parts[0] != "mnnvl" + or not parts[2].isascii() + or not parts[2].isdecimal() + ): + raise ValueError("Canonical observed MNNVL fabric domain required") + identity = FabricIdentity( + "GPU-00000000-0000-0000-0000-000000000001", parts[1], int(parts[2]) + ) + if identity.domain != fabric_domain: + raise ValueError("Canonical observed MNNVL fabric domain required") + body = { + "schema": 1, + "layout_domain": layout_domain, + "gpu_transport": "mnnvl", + "fabric_domain": fabric_domain, + } + return ( + "mnnvl-sha256:" + + hashlib.sha256( + json.dumps(body, sort_keys=True, separators=(",", ":")).encode() + ).hexdigest() + )