diff --git a/test/registered/unit/server_args/test_resolution_is_reproducible.py b/test/registered/unit/server_args/test_resolution_is_reproducible.py index 6f140bd942de..dbdd8db6bbe7 100644 --- a/test/registered/unit/server_args/test_resolution_is_reproducible.py +++ b/test/registered/unit/server_args/test_resolution_is_reproducible.py @@ -151,6 +151,26 @@ if torch.cuda.is_available(): _SHAPES = _SHAPES + (("deepseek_dsa", _DEEPSEEK_MINI_CONFIG, {}),) +# Since #34662 made cuda_ipc opt-in, no auto-resolution reaches the handler's +# cuda_ipc arm any more -- an explicit request is the only way in, so without +# this shape that arm (two raises and the pool-budget logging) is resolved by +# nothing in this file. It is also the only shape whose env write differs from +# what the *next* resolution would pick on its own, which is what keeps the +# sticky-carry assertion in `test_a_resolution_does_not_leak_into_the_next` +# from being vacuous. Gated on `is_cuda()` rather than +# `torch.cuda.is_available()`: the handler raises for cuda_ipc off NVIDIA CUDA, +# ROCm included. +_CUDA_IPC_SHAPES = () +if is_cuda(): + _CUDA_IPC_SHAPES = ( + ( + "multimodal_cuda_ipc", + _MULTIMODAL_MINI_CONFIG, + dict(mm_feature_transport="cuda_ipc"), + ), + ) + _SHAPES = _SHAPES + _CUDA_IPC_SHAPES + # The one field a previous resolution genuinely dictates for the next one in # this process: `_handle_multimodal_feature_transport` writes # SGLANG_USE_CUDA_IPC_TRANSPORT so tokenizer workers inherit the decision, and @@ -298,6 +318,11 @@ def test_a_resolution_does_not_leak_into_the_next(self): ("multimodal", _MULTIMODAL_MINI_CONFIG, {}), ("torch_compile", _MINI_CONFIG, dict(enable_torch_compile=True)), ) + # Auto-resolution picks cpu on every runner this case runs on now that + # cuda_ipc is opt-in, so the auto shape above writes what the next + # resolution would have picked anyway; the explicit shape is what makes + # the carry observable at all. + intermediates += _CUDA_IPC_SHAPES if torch.cuda.is_available(): # Same device gate as _SHAPES: the DSA arm probes the device # capability during resolution. @@ -321,7 +346,7 @@ def test_a_resolution_does_not_leak_into_the_next(self): # against `default_before`, so clearing it here does not skew # that comparison. envs.SGLANG_USE_CUDA_IPC_TRANSPORT.clear() - self._resolved(self._config_dir(config), **kwargs) + intermediate = self._resolved(self._config_dir(config), **kwargs) after = self._resolved(model_path) without_sticky = lambda snapshot: { k: v @@ -332,15 +357,21 @@ def test_a_resolution_does_not_leak_into_the_next(self): without_sticky(self._comparable(after)), without_sticky(self._comparable(default_before)), ) - if label == "multimodal": - # And the documented exception, asserted rather than - # assumed: the multimodal handler's env write does reach - # the next resolution. What it carries is the - # intermediate's own device-dependent selection — cuda_ipc - # on single-node CUDA, cpu on the CPU/ROCm runners (the - # same `is_cuda()` gate the handler branches on). - expected = "cuda_ipc" if is_cuda() else "cpu" - self.assertEqual(after.mm_feature_transport, expected) + # And the documented exception, asserted rather than assumed, + # for every intermediate: each one runs the transport handler, + # so each one writes the variable the next resolution reads. + # What carries is the legacy *boolean*, not the tri-state field + # -- the handler writes 1 only for cuda_ipc -- so every other + # selection comes back as cpu, which is what keeps this honest + # if a cuda_vmm shape is ever added (its carry is cpu, not + # cuda_vmm). The `cuda_ipc` shape is the one whose carry + # differs from the cpu that `default_before` resolved to. + expected = ( + "cuda_ipc" + if intermediate.mm_feature_transport == "cuda_ipc" + else "cpu" + ) + self.assertEqual(after.mm_feature_transport, expected) def test_resolving_a_sibling_leaves_the_first_alone(self): for label, config, kwargs in _SHAPES: