[Bugfix] Handle null RoPE parameters for NoPE layers (#55949)

Signed-off-by: Andreas Karatzas <akaratza@amd.com>

Andreas Karatzas committed Sep 8, 2026 at 15:20 UTC 18615ad1ee11ae54a996a0c49dd6860630b3bca0
4 files changed +75 -1
tests/test_config.py
+43
@@ -1533,6 +1533,49 @@ def test_get_and_verify_max_len(
1533 assert actual_max_len == expected_max_len
1534
1535
1536 +@pytest.mark.parametrize("max_model_len", [None, 1024])
1537 +@pytest.mark.parametrize(
1538 + ("rope_parameters", "expected_max_len"),
1539 + [
1540 + ({"rope_type": "default"}, 4096),
1541 + ({"rope_type": "linear", "factor": 2.0}, 8192),
1542 + ({"rope_type": "longrope"}, 2048),
1543 + ],
1544 +)
1545 +def test_get_and_verify_max_len_with_nope_layers(
1546 + max_model_len, rope_parameters, expected_max_len
1547 +):
1548 + """NoPE layers do not prevent deriving or scaling the context length."""
1549 + from transformers import PretrainedConfig
1550 +
1551 + from vllm.config.model import _get_and_verify_max_len
1552 + from vllm.transformers_utils.model_arch_config_convertor import (
1553 + ModelArchConfigConvertorBase,
1554 + )
1555 +
1556 + hf_config = PretrainedConfig(
1557 + max_position_embeddings=4096,
1558 + original_max_position_embeddings=2048,
1559 + )
1560 + hf_config.rope_parameters = {
1561 + "full_attention": None,
1562 + "sliding_attention": rope_parameters,
1563 + }
1564 + model_arch_config = ModelArchConfigConvertorBase(hf_config, hf_config).convert()
1565 +
1566 + actual_max_len = _get_and_verify_max_len(
1567 + hf_config=hf_config,
1568 + model_arch_config=model_arch_config,
1569 + tokenizer_config=None,
1570 + max_model_len=max_model_len,
1571 + disable_sliding_window=False,
1572 + sliding_window=None,
1573 + )
1574 +
1575 + assert actual_max_len == (max_model_len or expected_max_len)
1576 + assert hf_config.rope_parameters["full_attention"] is None
1577 +
1578 +
1579 class MockConfig:
1580 """Simple mock object for testing maybe_pull_model_tokenizer_for_runai"""
1581
tests/transformers_utils/test_config.py
+23
@@ -18,6 +18,7 @@ from vllm.tokenizers import get_tokenizer
18 from vllm.transformers_utils import config as config_module
19 from vllm.transformers_utils.config import (
20 get_safetensors_params_metadata,
21 + patch_legacy_rope_type,
22 try_get_generation_config,
23 )
24 from vllm.transformers_utils.configs.glm5_next import (
@@ -27,6 +28,28 @@ from vllm.transformers_utils.configs.glm5_next import (
28 )
29
30
31 +def test_patch_legacy_rope_type_preserves_nope_layers():
32 + """NoPE layers stay disabled while later RoPE layers are normalized."""
33 + rope_parameters = {
34 + "full_attention": None,
35 + "sliding_attention": {
36 + "type": "mrope",
37 + "mrope_section": [24, 20, 20],
38 + },
39 + }
40 +
41 + patch_legacy_rope_type(rope_parameters)
42 +
43 + assert rope_parameters == {
44 + "full_attention": None,
45 + "sliding_attention": {
46 + "type": "mrope",
47 + "rope_type": "default",
48 + "mrope_section": [24, 20, 20],
49 + },
50 + }
51 +
52 +
53 def test_glm5_next_accepts_deepseek_sparse_attention_layers():
54 layer_types = ["linear_attention", "deepseek_sparse_attention"]
55
vllm/config/model.py
+7
@@ -2449,6 +2449,13 @@ def _get_and_verify_max_len(
2449 rope_parameters = getattr(hf_config, "rope_parameters", None)
2450 if rope_parameters and not is_rope_parameters_nested(rope_parameters):
2451 rope_parameters = {"": rope_parameters}
2452 + if rope_parameters is not None:
2453 + # Layers without RoPE do not contribute to context length scaling.
2454 + rope_parameters = {
2455 + layer_type: rp
2456 + for layer_type, rp in rope_parameters.items()
2457 + if rp is not None
2458 + }
2459
2460 # NOTE(woosuk): Gemma3's max_model_len (128K) is already scaled by RoPE
2461 # scaling, so we skip applying the scaling factor again.
vllm/transformers_utils/config.py
+2 -1
@@ -541,7 +541,8 @@ def patch_legacy_rope_type(rope_parameters: dict[str, Any] | None) -> None:
541 # Handle nested rope_parameters in interleaved sliding attention models
542 if is_rope_parameters_nested(rope_parameters):
543 for rope_parameters_layer_type in rope_parameters.values():
544 - _patch_legacy_rope_type(rope_parameters_layer_type)
544 + if rope_parameters_layer_type is not None:
545 + _patch_legacy_rope_type(rope_parameters_layer_type)
546 else:
547 _patch_legacy_rope_type(rope_parameters)
548