[Core][Model] Unify XD-RoPE into M-RoPE and derive the channel count (#56078)

Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

Harry Mellor committed Sep 9, 2026 at 17:23 UTC 719284fe158f1be8a9dd92953295fc9d49015730
18 files changed +186 -505
tests/kernels/core/test_apply_rotary_emb.py
-15
@@ -46,7 +46,6 @@ def get_test_cases() -> list[RotaryEmbeddingTestCase]:
46 Ernie4_5_VLRotaryEmbedding,
47 )
48 from vllm.model_executor.layers.rotary_embedding.mrope import MRotaryEmbedding
49 - from vllm.model_executor.layers.rotary_embedding.xdrope import XDRotaryEmbedding
49
50 common_kwargs = {
51 "head_size": 128,
@@ -77,20 +76,6 @@ def get_test_cases() -> list[RotaryEmbeddingTestCase]:
76 expect_forward_native=False,
77 expect_forward=True,
78 ),
80 - # XDRotaryEmbedding tests
81 - RotaryEmbeddingTestCase(
82 - name="XDRotaryEmbedding.forward",
83 - rope_class=XDRotaryEmbedding,
84 - rope_kwargs={
85 - **common_kwargs,
86 - "scaling_alpha": 1.0,
87 - "xdrope_section": [16, 16, 16, 16],
88 - },
89 - method_name="forward",
90 - positions_shape=(4, 32), # 4D for P/W/H/T
91 - expect_forward_native=False,
92 - expect_forward=True,
93 - ),
79 # Ernie4_5_VLRotaryEmbedding tests
80 RotaryEmbeddingTestCase(
81 name="Ernie4_5_VLRotaryEmbedding.forward_native",
tests/models/transformers/test_backend.py
+19
@@ -769,3 +769,22 @@ def test_attention_scale_rejects_unresolvable_expression():
769
770 with pytest.raises(ValueError, match="Cannot resolve attention scaling expression"):
771 fuser.scale(Attention())
772 +
773 +
774 +class _ImageOnlyMRoPEModel:
775 + """`get_rope_index` without `video_grid_thw` or `**kwargs` (e.g. HunYuanVL)."""
776 +
777 + def get_rope_index(self, input_ids, image_grid_thw):
778 + seq_len = input_ids.shape[-1]
779 + positions = torch.arange(seq_len).view(1, 1, -1).expand(4, 1, -1)
780 + return positions, torch.tensor([0])
781 +
782 +
783 +def test_get_mrope_input_positions_omits_unsupported_grid_kwargs():
784 + """Optional grids the model can't accept must not be passed when empty."""
785 + mixin = SimpleNamespace(model=_ImageOnlyMRoPEModel())
786 +
787 + positions, delta = MultiModalMixin.get_mrope_input_positions(mixin, [1, 2, 3], [])
788 +
789 + assert positions.shape == (4, 3)
790 + assert delta == 0
tests/transformers_utils/test_config.py
+48
@@ -18,8 +18,10 @@ 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 + mrope_num_dims,
22 patch_legacy_rope_type,
23 try_get_generation_config,
24 + uses_mrope,
25 )
26 from vllm.transformers_utils.configs.glm5_next import (
27 Glm5NextConfig,
@@ -176,3 +178,49 @@ def test_safetensors_metadata_of_repo_without_safetensors():
178 assert get_safetensors_params_metadata("some/pytorch-only-model") == {}
179
180 get_safetensors_metadata.assert_called_once()
181 +
182 +
183 +@pytest.mark.parametrize(
184 + ("section_key", "mrope_section", "expected_num_dims"),
185 + [
186 + ("mrope_section", [16, 24, 24], 3),
187 + ("mrope_section", [16, 16, 16, 16], 4),
188 + # Interleaved M-RoPE takes 2 sections but still consumes 3D positions
189 + ("mrope_section", [32, 32], 3),
190 + # HunYuan-VL checkpoints ship the section under its legacy name
191 + ("xdrope_section", [16, 16, 16, 16], 4),
192 + ],
193 +)
194 +def test_mrope_num_dims(section_key, mrope_section, expected_num_dims):
195 + config = PretrainedConfig()
196 + config.rope_parameters = {"rope_type": "default", section_key: mrope_section}
197 +
198 + assert uses_mrope(config)
199 + assert mrope_num_dims(config) == expected_num_dims
200 +
201 +
202 +@pytest.mark.parametrize("section_name", ["mrope_section", "xdrope_section"])
203 +def test_mrope_num_dims_from_config_attribute(section_name):
204 + """Some configs expose the section as an attribute rather than under
205 + `rope_parameters`."""
206 + config = PretrainedConfig()
207 + setattr(config, section_name, [16, 16, 16, 16])
208 +
209 + assert uses_mrope(config)
210 + assert mrope_num_dims(config) == 4
211 +
212 +
213 +def test_mrope_num_dims_from_nested_rope_parameters():
214 + """Sections nested by layer type must be found, not silently defaulted."""
215 + config = PretrainedConfig()
216 + config.rope_parameters = {
217 + "full_attention": {"mrope_section": [16, 16, 16, 16]},
218 + "linear_attention": {"rope_type": "default"},
219 + }
220 +
221 + assert uses_mrope(config)
222 + assert mrope_num_dims(config) == 4
223 +
224 +
225 +def test_mrope_num_dims_without_mrope():
226 + assert mrope_num_dims(PretrainedConfig()) == 0
tests/v1/core/test_output.py
-18
@@ -105,24 +105,6 @@ def _mm_feature_mixed(offset: int, length: int) -> MultiModalFeatureSpec:
105 )
106
107
108 -def test_strip_covered_mm_data_xdrope() -> None:
109 - """XD-RoPE models (e.g. HunyuanOCR) compute positions the same way, so a
110 - covered item must keep its grid dims: stripping them made the worker index
111 - an empty ``image_grid_thw`` and crash the engine on the second request with
112 - an identical prompt."""
113 - covered = _mm_feature_mixed(offset=0, length=100)
114 - uncovered = _mm_feature_mixed(offset=300, length=100)
115 -
116 - stripped = strip_covered_mm_data(
117 - [covered, uncovered], num_computed_tokens=250, uses_xdrope=True
118 - )
119 -
120 - assert stripped[0].data is not None
121 - assert list(stripped[0].data.keys()) == ["image_grid_thw"]
122 - assert stripped[1].data is not None
123 - assert set(stripped[1].data.keys()) == {"pixel_values", "image_grid_thw"}
124 -
125 -
108 def test_strip_covered_mm_data_mrope() -> None:
109 """For M-RoPE models, covered items keep their keep_on_cpu metadata fields
110 (the worker needs them to compute positions); payload fields are dropped."""
vllm/config/model.py
+3 -7
@@ -38,11 +38,11 @@ from vllm.transformers_utils.config import (
38 get_sentence_transformer_tokenizer_config,
39 is_encoder_decoder,
40 is_rope_parameters_nested,
41 + mrope_num_dims,
42 try_get_dense_modules,
43 try_get_generation_config,
44 try_get_tokenizer_config,
45 uses_mrope,
45 - uses_xdrope_dim,
46 )
47 from vllm.transformers_utils.model_arch_config_convertor import (
48 MODEL_ARCH_CONFIG_CONVERTORS,
@@ -1850,12 +1850,8 @@ class ModelConfig:
1850 return uses_mrope(self.hf_config)
1851
1852 @property
1853 - def uses_xdrope_dim(self) -> int:
1854 - return uses_xdrope_dim(self.hf_config)
1855 -
1856 - @property
1857 - def uses_xdrope(self) -> bool:
1858 - return self.uses_xdrope_dim > 0
1853 + def mrope_num_dims(self) -> int:
1854 + return mrope_num_dims(self.hf_config)
1855
1856 @property
1857 def is_multimodal_model(self) -> bool:
vllm/model_executor/layers/rotary_embedding/__init__.py
-13
@@ -24,7 +24,6 @@ from .mrope_interleaved import MRotaryEmbeddingInterleaved
24 from .ntk_scaling_rope import NTKScalingRotaryEmbedding
25 from .phi3_long_rope_scaled_rope import Phi3LongRoPEScaledRotaryEmbedding
26 from .telechat3_scaling_rope import TeleChat3RoPEScaledRotaryEmbedding
27 -from .xdrope import XDRotaryEmbedding
27 from .yarn_scaling_rope import YaRNScalingRotaryEmbedding
28
29 _ROPE_DICT: dict[tuple[Any, ...], RotaryEmbedding] = {}
@@ -228,18 +227,6 @@ def get_rope(
227 raise ValueError(
228 "Dynamic rope scaling must contain either 'alpha' or 'factor' field"
229 )
231 - elif scaling_type == "xdrope":
232 - scaling_alpha = rope_parameters["alpha"]
233 - rotary_emb = XDRotaryEmbedding(
234 - head_size,
235 - rotary_dim,
236 - max_position,
237 - base,
238 - is_neox_style,
239 - scaling_alpha,
240 - dtype,
241 - xdrope_section=rope_parameters["xdrope_section"],
242 - )
230 elif scaling_type == "yarn":
231 scaling_factor = rope_parameters["factor"]
232 original_max_position = rope_parameters["original_max_position_embeddings"]
vllm/model_executor/layers/rotary_embedding/xdrope.py deleted
-172
@@ -1,172 +0,0 @@
1 -# SPDX-License-Identifier: Apache-2.0
2 -# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 -
4 -import numpy as np
5 -import torch
6 -
7 -from .dynamic_ntk_alpha_rope import DynamicNTKAlphaRotaryEmbedding
8 -
9 -
10 -class XDRotaryEmbedding(DynamicNTKAlphaRotaryEmbedding):
11 - """DynamicNTKAlphaRotaryEmbedding extended with MultiModal(XD) Sections.
12 -
13 - Based on the original DynamicNTKAlphaRotaryEmbedding implementation.
14 - """
15 -
16 - def __init__(
17 - self,
18 - head_size: int,
19 - rotary_dim: int,
20 - max_position_embeddings: int,
21 - base: float,
22 - is_neox_style: bool,
23 - scaling_alpha: float,
24 - dtype: torch.dtype,
25 - xdrope_section: list[int],
26 - ) -> None:
27 - self.xdrope_section = xdrope_section
28 - super().__init__(
29 - head_size,
30 - rotary_dim,
31 - max_position_embeddings,
32 - base,
33 - is_neox_style,
34 - scaling_alpha,
35 - dtype,
36 - )
37 -
38 - def forward_native(
39 - self,
40 - positions: torch.Tensor,
41 - query: torch.Tensor,
42 - key: torch.Tensor | None = None,
43 - offsets: torch.Tensor | None = None,
44 - ) -> tuple[torch.Tensor, torch.Tensor | None]:
45 - """PyTorch-native implementation equivalent to forward().
46 -
47 - Args:
48 - positions:
49 - [4, num_tokens] (P/W/H/T positions with multimodal inputs)
50 - query: [num_tokens, num_heads * head_size]
51 - key: [num_tokens, num_kv_heads * head_size]
52 - """
53 - assert positions.ndim == 2
54 - assert key is not None
55 -
56 - num_tokens = positions.shape[-1]
57 - cos_sin = self.cos_sin_cache[positions]
58 - cos, sin = cos_sin.chunk(2, dim=-1)
59 - cos = torch.cat(
60 - [m[i] for i, m in enumerate(cos.split(self.xdrope_section, dim=-1))], dim=-1
61 - )
62 - sin = torch.cat(
63 - [m[i] for i, m in enumerate(sin.split(self.xdrope_section, dim=-1))], dim=-1
64 - )
65 -
66 - query_shape = query.shape
67 - query = query.view(num_tokens, -1, self.head_size)
68 - query_rot = query[..., : self.rotary_dim]
69 - query_pass = query[..., self.rotary_dim :]
70 - query_rot = self.apply_rotary_emb.forward_native(
71 - query_rot,
72 - cos,
73 - sin,
74 - )
75 - query = torch.cat((query_rot, query_pass), dim=-1).reshape(query_shape)
76 -
77 - key_shape = key.shape
78 - key = key.view(num_tokens, -1, self.head_size)
79 - key_rot = key[..., : self.rotary_dim]
80 - key_pass = key[..., self.rotary_dim :]
81 - key_rot = self.apply_rotary_emb.forward_native(
82 - key_rot,
83 - cos,
84 - sin,
85 - )
86 - key = torch.cat((key_rot, key_pass), dim=-1).reshape(key_shape)
87 - return query, key
88 -
89 - def forward_cuda(
90 - self,
91 - positions: torch.Tensor,
92 - query: torch.Tensor,
93 - key: torch.Tensor | None = None,
94 - offsets: torch.Tensor | None = None,
95 - ) -> tuple[torch.Tensor, torch.Tensor | None]:
96 - """PyTorch-native implementation equivalent to forward().
97 -
98 - Args:
99 - positions:
100 - [4, num_tokens] (P/W/H/T positions with multimodal inputs)
101 - query: [num_tokens, num_heads * head_size]
102 - key: [num_tokens, num_kv_heads * head_size]
103 - """
104 - assert positions.ndim == 2
105 - assert key is not None
106 -
107 - num_tokens = positions.shape[-1]
108 - cos_sin = self.cos_sin_cache[positions]
109 - cos, sin = cos_sin.chunk(2, dim=-1)
110 - cos = torch.cat(
111 - [m[i] for i, m in enumerate(cos.split(self.xdrope_section, dim=-1))], dim=-1
112 - )
113 - sin = torch.cat(
114 - [m[i] for i, m in enumerate(sin.split(self.xdrope_section, dim=-1))], dim=-1
115 - )
116 -
117 - query_shape = query.shape
118 - query = query.view(num_tokens, -1, self.head_size)
119 - query_rot = query[..., : self.rotary_dim]
120 - query_pass = query[..., self.rotary_dim :]
121 - query_rot = self.apply_rotary_emb(
122 - query_rot,
123 - cos,
124 - sin,
125 - )
126 - query = torch.cat((query_rot, query_pass), dim=-1).reshape(query_shape)
127 -
128 - key_shape = key.shape
129 - key = key.view(num_tokens, -1, self.head_size)
130 - key_rot = key[..., : self.rotary_dim]
131 - key_pass = key[..., self.rotary_dim :]
132 - key_rot = self.apply_rotary_emb(
133 - key_rot,
134 - cos,
135 - sin,
136 - )
137 - key = torch.cat((key_rot, key_pass), dim=-1).reshape(key_shape)
138 - return query, key
139 -
140 - def forward_xpu(
141 - self,
142 - positions: torch.Tensor,
143 - query: torch.Tensor,
144 - key: torch.Tensor | None = None,
145 - offsets: torch.Tensor | None = None,
146 - ) -> tuple[torch.Tensor, torch.Tensor | None]:
147 - # No fused XPU kernel for XD-sectioned RoPE; the base
148 - # RotaryEmbedding.forward_xpu calls the generic C++ kernel, which
149 - # rejects the 2-D [4, num_tokens] xdrope positions. Use the native path.
150 - return self.forward_native(positions, query, key, offsets)
151 -
152 - @staticmethod
153 - def get_next_input_positions(
154 - context_len: int,
155 - seq_len: int,
156 - xd_sections: int = 4,
157 - ) -> list[list[int]]:
158 - return [list(range(context_len, seq_len)) for _ in range(xd_sections)]
159 -
160 - @staticmethod
161 - def get_next_input_positions_tensor(
162 - out: np.ndarray,
163 - out_offset: int,
164 - context_len: int,
165 - num_new_tokens: int,
166 - ):
167 - values = np.arange(
168 - context_len,
169 - context_len + num_new_tokens,
170 - dtype=out.dtype,
171 - )
172 - out[:, out_offset : out_offset + num_new_tokens] = values
vllm/model_executor/models/interfaces.py
+2 -50
@@ -1783,7 +1783,8 @@ class SupportsMRoPE(Protocol):
1783
1784 Returns:
1785 Tuple of `(llm_positions, mrope_position_delta)`
1786 - - llm_positions: Tensor of shape `[3, num_tokens]` with T/H/W positions
1786 + - llm_positions: Tensor of shape `[num_dims, num_tokens]`, one row
1787 + per M-RoPE position channel (e.g. T/H/W)
1788 - mrope_position_delta: Delta for position calculations
1789 """
1790 ...
@@ -1803,55 +1804,6 @@ def supports_mrope(
1804 return isinstance(model, SupportsMRoPE)
1805
1806
1806 -@runtime_checkable
1807 -class SupportsXDRoPE(Protocol):
1808 - """The interface required for all models that support XD-RoPE."""
1809 -
1810 - supports_xdrope: ClassVar[Literal[True]] = True
1811 - """
1812 - A flag that indicates this model supports XD-RoPE.
1813 -
1814 - Note:
1815 - There is no need to redefine this flag if this class is in the
1816 - XDRope of your model class.
1817 - """
1818 -
1819 - def get_xdrope_input_positions(
1820 - self,
1821 - input_tokens: list[int],
1822 - mm_features: list["MultiModalFeatureSpec"],
1823 - ) -> torch.Tensor:
1824 - """
1825 - Get XD-RoPE input positions and delta value for this specific model.
1826 -
1827 - This method should be implemented by each model that supports XD-RoPE
1828 - to provide model-specific logic for computing input positions.
1829 -
1830 - Args:
1831 - input_tokens: List of input token IDs
1832 - mm_features: Information about each multi-modal data item
1833 -
1834 - Returns:
1835 - llm_positions: Tensor of shape `[xdrope_dim, num_tokens]` with
1836 - 4D(P/W/H/T) or 3D(W/H/T) positions.
1837 - """
1838 - ...
1839 -
1840 -
1841 -@overload
1842 -def supports_xdrope(model: type[object]) -> TypeIs[type[SupportsXDRoPE]]: ...
1843 -
1844 -
1845 -@overload
1846 -def supports_xdrope(model: object) -> TypeIs[SupportsXDRoPE]: ...
1847 -
1848 -
1849 -def supports_xdrope(
1850 - model: type[object] | object,
1851 -) -> TypeIs[type[SupportsXDRoPE]] | TypeIs[SupportsXDRoPE]:
1852 - return isinstance(model, SupportsXDRoPE)
1853 -
1854 -
1807 @runtime_checkable
1808 class SupportsEncoderCudaGraph(Protocol):
1809 """Interface for models whose vision encoder supports CUDA graph
vllm/model_executor/models/transformers/multimodal.py
+22 -10
@@ -1236,17 +1236,31 @@ class MultiModalMixin(SupportsMultiModal, SupportsMRoPE):
1236 video_grid_thw = torch.stack(video_grid_thw) if video_grid_thw else None
1237
1238 # `get_rope_index` doesn't always accept arbitrary `kwargs`
1239 - kwargs = {}
1240 - if not hasattr(self, "_get_rope_index_accepts_mm_token_type_ids"):
1239 + if not hasattr(self, "_get_rope_index_kwarg_names"):
1240 import inspect
1241
1243 - sig = inspect.signature(self.model.get_rope_index)
1244 - params = sig.parameters
1245 - self._get_rope_index_accepts_mm_token_type_ids = (
1246 - "mm_token_type_ids" in params
1247 - or any(p.kind == inspect.Parameter.VAR_KEYWORD for p in params.values())
1242 + params = inspect.signature(self.model.get_rope_index).parameters
1243 + self._get_rope_index_kwarg_names = (
1244 + None
1245 + if any(p.kind == inspect.Parameter.VAR_KEYWORD for p in params.values())
1246 + else frozenset(params)
1247 )
1249 - if self._get_rope_index_accepts_mm_token_type_ids:
1248 +
1249 + kwarg_names = self._get_rope_index_kwarg_names
1250 +
1251 + def accepts_kwarg(name: str) -> bool:
1252 + return kwarg_names is None or name in kwarg_names
1253 +
1254 + # Drop a grid the model can't accept only when there is nothing to pass.
1255 + kwargs = {
1256 + name: value
1257 + for name, value in (
1258 + ("image_grid_thw", image_grid_thw),
1259 + ("video_grid_thw", video_grid_thw),
1260 + )
1261 + if value is not None or accepts_kwarg(name)
1262 + }
1263 + if accepts_kwarg("mm_token_type_ids"):
1264 mm_token_type_ids = torch.zeros(len(input_tokens), dtype=torch.int)
1265 for feature in mm_features:
1266 position = feature.mm_position
@@ -1257,8 +1271,6 @@ class MultiModalMixin(SupportsMultiModal, SupportsMRoPE):
1271
1272 mrope_positions, mrope_position_delta = self.model.get_rope_index(
1273 input_ids=torch.tensor(input_tokens).unsqueeze(0),
1260 - image_grid_thw=image_grid_thw,
1261 - video_grid_thw=video_grid_thw,
1274 **kwargs,
1275 )
1276
vllm/multimodal/utils.py
+3 -4
@@ -216,12 +216,11 @@ def strip_covered_mm_data(
216 mm_features: list[MultiModalFeatureSpec],
217 num_computed_tokens: int,
218 uses_mrope: bool = False,
219 - uses_xdrope: bool = False,
219 ) -> list[MultiModalFeatureSpec]:
220 """Drop the tensor data of mm items whose placeholder span is fully inside
221 a prefix-cache-covered region: no encoder run can be scheduled for them,
223 - so the workers never consume the payload fields. M-RoPE and XD-RoPE models
224 - keep CPU-side metadata fields used to compute positions. SHM address items
222 + so the workers never consume the payload fields. M-RoPE models keep
223 + CPU-side metadata fields used to compute positions. SHM address items
224 are also kept so workers can balance the sender's reference count. The
225 scheduler-side ``Request`` keeps the full features."""
226 if not mm_features or num_computed_tokens == 0:
@@ -239,7 +238,7 @@ def strip_covered_mm_data(
238 return f
239
240 data = None
242 - if uses_mrope or uses_xdrope:
241 + if uses_mrope:
242 data = MultiModalKwargsItem(
243 {k: elem for k, elem in f.data.items() if elem.field.keep_on_cpu}
244 )
vllm/transformers_utils/config.py
+54 -20
@@ -2,7 +2,7 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 import os
5 -from collections.abc import Callable, Iterator
5 +from collections.abc import Callable, Iterator, Sequence
6 from contextlib import contextmanager
7 from dataclasses import asdict
8 from functools import cache, partial, wraps
@@ -569,15 +569,47 @@ def patch_rope_parameters(config: PretrainedConfig) -> None:
569 config.validate_rope()
570
571
572 -def _uses_mrope(config: PretrainedConfig) -> bool:
572 +def _iter_rope_parameters(config: PretrainedConfig) -> Iterator[dict[str, Any]]:
573 + """Yield a config's rope parameters, one dict per layer type if nested."""
574 rope_parameters = getattr(config, "rope_parameters", None)
574 - if rope_parameters is None:
575 - return False
575 + if not isinstance(rope_parameters, dict):
576 + return
577
577 - return "mrope_section" in rope_parameters or any(
578 - isinstance(params, dict) and "mrope_section" in params
579 - for params in rope_parameters.values()
580 - )
578 + if is_rope_parameters_nested(rope_parameters):
579 + yield from (p for p in rope_parameters.values() if isinstance(p, dict))
580 + else:
581 + yield rope_parameters
582 +
583 +
584 +def _mrope_section(config: PretrainedConfig) -> Sequence[int] | None:
585 + """Return the M-RoPE section this config declares, if any.
586 +
587 + `xdrope_section` is the legacy name HunYuan-VL checkpoints use for the same
588 + field; upstream Transformers normalises it to `mrope_section`.
589 + """
590 + from vllm.config.utils import getattr_iter
591 +
592 + names = ("mrope_section", "xdrope_section")
593 +
594 + for params in _iter_rope_parameters(config):
595 + for i, name in enumerate(names):
596 + section = params.get(name)
597 + if isinstance(section, (list, tuple)):
598 + if i > 0:
599 + logger.warning_once(
600 + "rope_parameters contains a deprecated key '%s'. "
601 + "Please use the preferred key '%s' instead.",
602 + name,
603 + names[0],
604 + )
605 + return section
606 +
607 + section = getattr_iter(config, names, None, warn=True)
608 + return section if isinstance(section, (list, tuple)) else None
609 +
610 +
611 +def _uses_mrope(config: PretrainedConfig) -> bool:
612 + return _mrope_section(config) is not None
613
614
615 def uses_mrope(config: PretrainedConfig) -> bool:
@@ -602,21 +634,23 @@ def thinker_uses_mrope(config: PretrainedConfig) -> bool:
634 return uses_mrope(thinker_text_config)
635
636
605 -def uses_xdrope_dim(config: PretrainedConfig) -> int:
606 - """Detect if the model with this config uses XD-ROPE."""
607 - xdrope_section = getattr(config, "xdrope_section", None)
608 - if xdrope_section is not None and isinstance(xdrope_section, list):
609 - return len(xdrope_section)
610 - rope_scaling = getattr(config, "rope_scaling", None)
611 - if rope_scaling is None:
637 +def mrope_num_dims(config: PretrainedConfig) -> int:
638 + """Number of M-RoPE position channels the model consumes.
639 +
640 + Each section entry sizes one channel, so the section length is the channel
641 + count. Interleaved M-RoPE also accepts a 2 section variant whose positions
642 + are still 3D, so never return fewer than 3.
643 + """
644 + if not uses_mrope(config):
645 return 0
646
614 - if isinstance(rope_scaling, dict) and "xdrope_section" in rope_scaling:
615 - xdrope_section = rope_scaling["xdrope_section"]
616 - if xdrope_section is not None and isinstance(xdrope_section, list):
617 - return len(xdrope_section)
647 + for candidate in (config.get_text_config(), config):
648 + mrope_section = _mrope_section(candidate)
649 + if mrope_section is not None:
650 + return max(len(mrope_section), 3)
651
619 - return 0
652 + # Custom configs may declare M-RoPE without exposing the sections.
653 + return 3
654
655
656 def is_encoder_decoder(config: PretrainedConfig) -> bool:
vllm/v1/core/sched/output.py
-2
@@ -56,7 +56,6 @@ class NewRequestData:
56 block_ids: tuple[list[int], ...],
57 prefill_token_ids: list[int] | None = None,
58 uses_mrope: bool = False,
59 - uses_xdrope: bool = False,
59 ) -> "NewRequestData":
60 return cls(
61 req_id=request.request_id,
@@ -65,7 +64,6 @@ class NewRequestData:
64 request.mm_features,
65 request.num_computed_tokens,
66 uses_mrope=uses_mrope,
68 - uses_xdrope=uses_xdrope,
67 ),
68 sampling_params=request.sampling_params,
69 pooling_params=request.pooling_params,
vllm/v1/core/sched/scheduler.py
-3
@@ -94,7 +94,6 @@ class Scheduler(SchedulerInterface):
94 self.cache_config = vllm_config.cache_config
95 self.lora_config = vllm_config.lora_config
96 self.model_uses_mrope = vllm_config.model_config.uses_mrope
97 - self.model_uses_xdrope = vllm_config.model_config.uses_xdrope
97 self.kv_cache_config = kv_cache_config
98 self.kv_events_config = vllm_config.kv_events_config
99 self.parallel_config = vllm_config.parallel_config
@@ -1324,7 +1323,6 @@ class Scheduler(SchedulerInterface):
1323 req_to_new_blocks[req.request_id].get_block_ids(),
1324 req._all_token_ids,
1325 uses_mrope=self.model_uses_mrope,
1327 - uses_xdrope=self.model_uses_xdrope,
1326 )
1327 for req in scheduled_new_reqs
1328 ]
@@ -1334,7 +1332,6 @@ class Scheduler(SchedulerInterface):
1332 req,
1333 req_to_new_blocks[req.request_id].get_block_ids(),
1334 uses_mrope=self.model_uses_mrope,
1337 - uses_xdrope=self.model_uses_xdrope,
1335 )
1336 for req in scheduled_new_reqs
1337 ]
vllm/v1/spec_decode/llm_base_proposer.py
+3 -24
@@ -173,25 +173,17 @@ class SpecDecodeBaseProposer:
173 # Use draft model's M-RoPE setting, not target model's
174 # Draft models may be text-only even if target is multimodal
175 self.uses_mrope = self.draft_model_config.uses_mrope
176 - self.uses_xdrope_dim = self.vllm_config.model_config.uses_xdrope_dim
177 - self.draft_uses_xdrope_dim = self.draft_model_config.uses_xdrope_dim
176 if self.uses_mrope:
177 # NOTE: `mrope_positions` is implemented with one additional dummy
178 # position on purpose to make it non-contiguous so that it can work
179 # with torch compile.
180 # See detailed explanation in https://github.com/vllm-project/vllm/pull/12128#discussion_r1926431923
181
184 - # NOTE: When M-RoPE is enabled, position ids are 3D regardless of
185 - # the modality of inputs. For text-only inputs, each dimension has
186 - # identical position IDs, making M-RoPE functionally equivalent to
187 - # 1D-RoPE.
182 + # NOTE: For text-only inputs, each dimension has identical position
183 + # IDs, making M-RoPE functionally equivalent to 1D-RoPE.
184 # See page 5 of https://arxiv.org/abs/2409.12191
185 self.mrope_positions = torch.zeros(
190 - (3, self.max_positions + 1), dtype=torch.int64, device=device
191 - )
192 - elif self.uses_xdrope_dim > 0 and self.draft_uses_xdrope_dim > 0:
193 - self.xdrope_positions = torch.zeros(
194 - (self.uses_xdrope_dim, self.max_positions + 1),
186 + (self.draft_model_config.mrope_num_dims, self.max_positions + 1),
187 dtype=torch.int64,
188 device=device,
189 )
@@ -381,15 +373,11 @@ class SpecDecodeBaseProposer:
373 def _get_positions(self, num_tokens: int):
374 if self.uses_mrope:
375 return self.mrope_positions[:, :num_tokens]
384 - if self.uses_xdrope_dim > 0 and self.draft_uses_xdrope_dim > 0:
385 - return self.xdrope_positions[:, :num_tokens]
376 return self.positions[:num_tokens]
377
378 def _set_positions(self, num_tokens: int, positions: torch.Tensor):
379 if self.uses_mrope:
380 self.mrope_positions[:, :num_tokens] = positions
391 - elif self.uses_xdrope_dim > 0 and self.draft_uses_xdrope_dim > 0:
392 - self.xdrope_positions[:, :num_tokens] = positions
381 else:
382 # Convert M-RoPE positions if target model uses M-RoPE
383 # but draft doesn't, For text inputs, all M-RoPE
@@ -787,8 +775,6 @@ class SpecDecodeBaseProposer:
775 positions_1d = positions[0] if self.uses_mrope else positions
776 if self.uses_mrope:
777 out_pos = self.mrope_positions[0, :batch_size]
790 - elif self.uses_xdrope_dim > 0 and self.draft_uses_xdrope_dim > 0:
791 - out_pos = self.xdrope_positions[0, :batch_size]
778 else:
779 out_pos = self.positions[:batch_size]
780 eagle_step_update_slot_mapping_and_metadata(
@@ -805,11 +791,6 @@ class SpecDecodeBaseProposer:
791 if self.uses_mrope:
792 self.mrope_positions[1:, :batch_size] = self.mrope_positions[0, :batch_size]
793 positions = self.mrope_positions[:, :batch_size]
808 - elif self.uses_xdrope_dim > 0 and self.draft_uses_xdrope_dim > 0:
809 - self.xdrope_positions[1:, :batch_size] = self.xdrope_positions[
810 - 0, :batch_size
811 - ]
812 - positions = self.xdrope_positions[0, :batch_size]
794 else:
795 positions = self.positions[:batch_size]
796 common_attn_metadata.max_seq_len = min(
@@ -859,8 +840,6 @@ class SpecDecodeBaseProposer:
840 self.input_ids[token_indices_to_sample] = next_token_ids
841
842 # copy inputs to buffer for cudagraph
862 - if self.uses_xdrope_dim > 0 and self.draft_uses_xdrope_dim == 0:
863 - target_positions = target_positions[0]
843 self._set_positions(num_tokens, target_positions)
844
845 self.hidden_states[:num_tokens] = target_hidden_states
vllm/v1/worker/gpu/mm/rope.py
+24 -46
@@ -6,39 +6,36 @@ import torch
6 import torch.nn as nn
7
8 from vllm.config import ModelConfig
9 -from vllm.model_executor.models.interfaces import SupportsMRoPE, SupportsXDRoPE
9 +from vllm.model_executor.models.interfaces import SupportsMRoPE
10 from vllm.triton_utils import tl, triton
11 from vllm.v1.worker.gpu.buffer_utils import StagedWriteTensor, UvaBackedTensor
12
13
14 class RopeState:
15 - """Unified state for multi-dimensional RoPE variants (M-RoPE, XD-RoPE).
15 + """State for multi-dimensional (M-RoPE) positions.
16
17 - M-RoPE: 3 dims, uses position delta for decode.
18 - XD-RoPE: 3 or 4 dims, delta is 0 (decode uses orig_pos for all dims).
17 + `num_dims` is the number of position channels the model consumes, which
18 + the model config derives from its M-RoPE sections.
19
20 NOTE: `positions` is implemented with one additional dummy position on
21 purpose to make it non-contiguous so that it can work with torch compile.
22 See detailed explanation in
23 https://github.com/vllm-project/vllm/pull/12128#discussion_r1926431923
24
25 - NOTE: When M-RoPE is enabled, position ids are 3D regardless of the
26 - modality of inputs. For text-only inputs, each dimension has identical
27 - position IDs, making M-RoPE functionally equivalent to 1D-RoPE.
25 + NOTE: For text-only inputs, each dimension has identical position IDs,
26 + making M-RoPE functionally equivalent to 1D-RoPE.
27 See page 5 of https://arxiv.org/abs/2409.12191
28 """
29
30 def __init__(
31 self,
32 num_dims: int,
34 - has_delta: bool,
33 max_num_reqs: int,
34 max_num_tokens: int,
35 max_model_len: int,
36 device: torch.device,
37 ):
38 self.num_dims = num_dims
41 - self.has_delta = has_delta
39 self.max_num_reqs = max_num_reqs
40 self.max_num_tokens = max_num_tokens
41 self.max_model_len = max_model_len
@@ -56,7 +53,6 @@ class RopeState:
53 (num_dims, max_num_tokens + 1), dtype=torch.int64, device=device
54 )
55
59 - # Delta is non-zero for M-RoPE, always 0 for XD-RoPE.
56 self.prefill_delta = UvaBackedTensor(max_num_reqs, dtype=torch.int32)
57
58 def init_prefill_positions(
@@ -66,17 +62,11 @@ class RopeState:
62 prefill_token_ids: list[int],
63 mm_features: list,
64 ) -> None:
69 - if self.has_delta:
70 - mrope_model = cast(SupportsMRoPE, model)
71 - prefill_positions, delta = mrope_model.get_mrope_input_positions(
72 - prefill_token_ids, mm_features
73 - )
74 - self.prefill_delta.np[req_idx] = delta
75 - else:
76 - xdrope_model = cast(SupportsXDRoPE, model)
77 - prefill_positions = xdrope_model.get_xdrope_input_positions(
78 - prefill_token_ids, mm_features
79 - )
65 + mrope_model = cast(SupportsMRoPE, model)
66 + prefill_positions, delta = mrope_model.get_mrope_input_positions(
67 + prefill_token_ids, mm_features
68 + )
69 + self.prefill_delta.np[req_idx] = delta
70
71 for i in range(self.num_dims):
72 pos = prefill_positions[i].tolist()
@@ -84,8 +74,7 @@ class RopeState:
74
75 def apply_staged_writes(self) -> None:
76 self.prefill_positions.apply_write()
87 - if self.has_delta:
88 - self.prefill_delta.copy_to_uva()
77 + self.prefill_delta.copy_to_uva()
78
79 def get_positions(self, num_tokens: int) -> torch.Tensor:
80 return self.positions[:, :num_tokens]
@@ -104,8 +93,7 @@ class RopeState:
93 self.prefill_positions.gpu[base : base + self.num_dims, :length].copy_(
94 positions
95 )
107 - if self.has_delta:
108 - self.prefill_delta.np[req_idx] = delta
96 + self.prefill_delta.np[req_idx] = delta
97
98 def prepare_positions(
99 self,
@@ -140,27 +128,17 @@ def get_rope_state(
128 device: torch.device,
129 ) -> RopeState | None:
130 """Create a RopeState if the model uses multi-dimensional RoPE."""
143 - if model_config.uses_mrope:
144 - assert isinstance(model, SupportsMRoPE)
145 - return RopeState(
146 - num_dims=3,
147 - has_delta=True,
148 - max_num_reqs=max_num_reqs,
149 - max_num_tokens=max_num_tokens,
150 - max_model_len=max_model_len,
151 - device=device,
152 - )
153 - if model_config.uses_xdrope_dim > 0:
154 - assert isinstance(model, SupportsXDRoPE)
155 - return RopeState(
156 - num_dims=model_config.uses_xdrope_dim,
157 - has_delta=False,
158 - max_num_reqs=max_num_reqs,
159 - max_num_tokens=max_num_tokens,
160 - max_model_len=max_model_len,
161 - device=device,
162 - )
163 - return None
131 + if not model_config.uses_mrope:
132 + return None
133 +
134 + assert isinstance(model, SupportsMRoPE)
135 + return RopeState(
136 + num_dims=model_config.mrope_num_dims,
137 + max_num_reqs=max_num_reqs,
138 + max_num_tokens=max_num_tokens,
139 + max_model_len=max_model_len,
140 + device=device,
141 + )
142
143
144 @triton.jit
vllm/v1/worker/gpu/model_states/mm_pruning.py
-1
@@ -132,7 +132,6 @@ def maybe_create_mm_pruner(
132 """Create a MultiModalPruner if the model prunes embeddings and uses M-RoPE."""
133 if (
134 rope_state is None
135 - or not rope_state.has_delta
135 or encoder_cache is None
136 or model_config.multimodal_config is None
137 or not model_config.multimodal_config.is_multimodal_pruning_enabled()
vllm/v1/worker/gpu_input_batch.py
-2
@@ -46,8 +46,6 @@ class CachedRequestState:
46 mrope_positions: torch.Tensor | None = None
47 mrope_position_delta: int | None = None
48
49 - xdrope_positions: torch.Tensor | None = None
50 -
49 lora_request: LoRARequest | None = None
50 prompt_embeds: torch.Tensor | None = None
51 # To accumulate prompt logprobs tensor chunks across prefill steps.
vllm/v1/worker/gpu_model_runner.py
+8 -118
@@ -70,10 +70,7 @@ from vllm.model_executor.layers.mamba.mamba_utils import MambaStateCopyFuncsByTy
70 from vllm.model_executor.layers.mamba.ops.ssu_dispatch import (
71 initialize_mamba_ssu_backend,
72 )
73 -from vllm.model_executor.layers.rotary_embedding import (
74 - MRotaryEmbedding,
75 - XDRotaryEmbedding,
76 -)
73 +from vllm.model_executor.layers.rotary_embedding import MRotaryEmbedding
74 from vllm.model_executor.model_loader import get_model_loader
75 from vllm.model_executor.model_loader.reload import (
76 finalize_layerwise_reload,
@@ -84,14 +81,12 @@ from vllm.model_executor.models.interfaces import (
81 MultiModalEmbeddings,
82 SupportsMRoPE,
83 SupportsMultiModal,
87 - SupportsXDRoPE,
84 get_mixture_of_experts_model,
85 supports_eagle3,
86 supports_mrope,
87 supports_multimodal_pruning,
88 supports_realtime,
89 supports_transcription,
94 - supports_xdrope,
90 )
91 from vllm.model_executor.models.interfaces_base import (
92 VllmModelForPooling,
@@ -576,7 +571,7 @@ class GPUModelRunner(
571 # Multi-modal data support
572 self.mm_registry = MULTIMODAL_REGISTRY
573 self.uses_mrope = model_config.uses_mrope
579 - self.uses_xdrope_dim = model_config.uses_xdrope_dim
574 + self.mrope_num_dims = model_config.mrope_num_dims
575 self.supports_mm_inputs = self.mm_registry.supports_multimodal_inputs(
576 model_config
577 )
@@ -872,20 +867,11 @@ class GPUModelRunner(
867 # with torch compile.
868 # See detailed explanation in https://github.com/vllm-project/vllm/pull/12128#discussion_r1926431923
869
875 - # NOTE: When M-RoPE is enabled, position ids are 3D regardless of
876 - # the modality of inputs. For text-only inputs, each dimension has
877 - # identical position IDs, making M-RoPE functionally equivalent to
878 - # 1D-RoPE.
870 + # NOTE: For text-only inputs, each dimension has identical position
871 + # IDs, making M-RoPE functionally equivalent to 1D-RoPE.
872 # See page 5 of https://arxiv.org/abs/2409.12191
873 self.mrope_positions = self._make_buffer(
881 - (3, self.max_num_tokens + 1), dtype=torch.int64
882 - )
883 -
884 - # Only relevant for models using XD-RoPE (e.g, HunYuan-VL)
885 - if self.uses_xdrope_dim > 0:
886 - # Similar to mrope but use assigned dimension number for RoPE, 4 as default.
887 - self.xdrope_positions = self._make_buffer(
888 - (self.uses_xdrope_dim, self.max_num_tokens + 1), dtype=torch.int64
874 + (self.mrope_num_dims, self.max_num_tokens + 1), dtype=torch.int64
875 )
876
877 # None in the first PP rank. The rest are set after load_model.
@@ -1036,14 +1022,10 @@ class GPUModelRunner(
1022 if isinstance(num_tokens, int):
1023 if self.uses_mrope:
1024 return self.mrope_positions.gpu[:, :num_tokens]
1039 - if self.uses_xdrope_dim > 0:
1040 - return self.xdrope_positions.gpu[:, :num_tokens]
1025 return self.positions[:num_tokens]
1026 else:
1027 if self.uses_mrope:
1028 return self.mrope_positions.gpu[:, num_tokens]
1045 - if self.uses_xdrope_dim > 0:
1046 - return self.xdrope_positions.gpu[:, num_tokens]
1029 return self.positions[num_tokens]
1030
1031 def _make_buffer(
@@ -1332,10 +1314,6 @@ class GPUModelRunner(
1314 if self.uses_mrope:
1315 self._init_mrope_positions(req_state)
1316
1335 - # Only relevant for models using XD-RoPE (e.g, HunYuan-VL)
1336 - if self.uses_xdrope_dim > 0:
1337 - self._init_xdrope_positions(req_state)
1338 -
1317 reqs_to_add.append(req_state)
1318 # Track new requests for ngram_gpu full tensor copy
1319 if is_ngram_gpu:
@@ -1702,19 +1680,6 @@ class GPUModelRunner(
1680 )
1681 )
1682
1705 - def _init_xdrope_positions(self, req_state: CachedRequestState):
1706 - model = self.get_model()
1707 - xdrope_model = cast(SupportsXDRoPE, model)
1708 - assert req_state.prompt_token_ids is not None, (
1709 - "XD-RoPE requires prompt_token_ids to be available."
1710 - )
1711 - assert supports_xdrope(model), "XD-RoPE support is not implemented."
1712 -
1713 - req_state.xdrope_positions = xdrope_model.get_xdrope_input_positions(
1714 - req_state.prompt_token_ids,
1715 - req_state.mm_features,
1716 - )
1717 -
1683 def _extract_mm_kwargs(
1684 self,
1685 scheduler_output: "SchedulerOutput",
@@ -2017,11 +1982,6 @@ class GPUModelRunner(
1982 if self.uses_mrope:
1983 self._calc_mrope_positions(scheduler_output)
1984
2020 - # Calculate XD-RoPE positions.
2021 - # Only relevant for models using XD-RoPE (e.g, HunYuan-VL)
2022 - if self.uses_xdrope_dim > 0:
2023 - self._calc_xdrope_positions(scheduler_output)
2024 -
1985 # Get token indices.
1986 # E.g., [0, 1, 0, 1, 2, 3, 4, 0, 1, 2]
1987 # -> [0, 1, M, M + 1, M + 2, M + 3, M + 4, 2 * M, 2 * M + 1, 2 * M + 2]
@@ -2229,7 +2189,7 @@ class GPUModelRunner(
2189 if self.uses_mrope:
2190 # Only relevant for models using M-RoPE (e.g, Qwen2-VL)
2191 # Copy one row at a time. mrope_positions is allocated as
2232 - # [3, max_num_tokens + 1] with a dummy trailing column to keep it
2192 + # [num_dims, max_num_tokens + 1] with a dummy trailing column to keep it
2193 # non-contiguous for torch.compile, so cpu[:, :N] is a strided view.
2194 # copy_() cannot express a strided source as a single
2195 # cudaMemcpyAsync, so it first gathers into a contiguous *pageable*
@@ -2242,20 +2202,7 @@ class GPUModelRunner(
2202 self.mrope_positions.cpu[row, :total_num_scheduled_tokens],
2203 non_blocking=True,
2204 )
2245 - elif self.uses_xdrope_dim > 0:
2246 - # Only relevant for models using XD-RoPE (e.g, HunYuan-VL).
2247 - # xdrope_positions is allocated as [uses_xdrope_dim, max_num_tokens
2248 - # + 1] with the same trailing-column trick as mrope_positions above,
2249 - # so cpu[:, :N] is a strided view of the pinned buffer and the
2250 - # single-slice copy_() runs into the same pageable-fallback silent
2251 - # sync described in PR #51841. Split into per-row copies for the
2252 - # same reason.
2253 - for row in range(self.xdrope_positions.gpu.shape[0]):
2254 - self.xdrope_positions.gpu[row, :total_num_scheduled_tokens].copy_(
2255 - self.xdrope_positions.cpu[row, :total_num_scheduled_tokens],
2256 - non_blocking=True,
2257 - )
2258 - if self.use_async_spec_decode and (self.uses_mrope or self.uses_xdrope_dim > 0):
2205 + if self.use_async_spec_decode and self.uses_mrope:
2206 drift = self.num_computed_tokens[req_indices_gpu].to(
2207 torch.int64
2208 ) - async_tensor_h2d(
@@ -2263,8 +2210,7 @@ class GPUModelRunner(
2210 device=self.device,
2211 dtype=torch.int64,
2212 )
2266 - target = self.mrope_positions if self.uses_mrope else self.xdrope_positions
2267 - target.gpu[:, :total_num_scheduled_tokens] += drift
2213 + self.mrope_positions.gpu[:, :total_num_scheduled_tokens] += drift
2214
2215 use_spec_decode = len(scheduler_output.scheduled_spec_decode_tokens) > 0
2216 if not use_spec_decode:
@@ -2862,53 +2808,6 @@ class GPUModelRunner(
2808
2809 mrope_pos_ptr += completion_part_len
2810
2865 - def _calc_xdrope_positions(self, scheduler_output: "SchedulerOutput"):
2866 - xdrope_pos_ptr = 0
2867 - for index, req_id in enumerate(self.input_batch.req_ids):
2868 - req = self.requests[req_id]
2869 - assert req.xdrope_positions is not None
2870 -
2871 - num_computed_tokens = self.input_batch.num_computed_tokens_cpu[index]
2872 - num_scheduled_tokens = scheduler_output.num_scheduled_tokens[req_id]
2873 - num_prompt_tokens = length_from_prompt_token_ids_or_embeds(
2874 - req.prompt_token_ids, req.prompt_embeds
2875 - )
2876 -
2877 - if num_computed_tokens + num_scheduled_tokens > num_prompt_tokens:
2878 - prompt_part_len = max(0, num_prompt_tokens - num_computed_tokens)
2879 - completion_part_len = max(0, num_scheduled_tokens - prompt_part_len)
2880 - else:
2881 - prompt_part_len = num_scheduled_tokens
2882 - completion_part_len = 0
2883 -
2884 - assert num_scheduled_tokens == prompt_part_len + completion_part_len
2885 -
2886 - if prompt_part_len > 0:
2887 - # prompt's xdrope_positions are pre-computed
2888 - dst_start = xdrope_pos_ptr
2889 - dst_end = xdrope_pos_ptr + prompt_part_len
2890 - src_start = num_computed_tokens
2891 - src_end = num_computed_tokens + prompt_part_len
2892 -
2893 - self.xdrope_positions.cpu[:, dst_start:dst_end] = req.xdrope_positions[
2894 - :, src_start:src_end
2895 - ]
2896 - xdrope_pos_ptr += prompt_part_len
2897 -
2898 - if completion_part_len > 0:
2899 - # compute completion's xdrope_positions on-the-fly
2900 - dst_start = xdrope_pos_ptr
2901 - dst_end = xdrope_pos_ptr + completion_part_len
2902 -
2903 - XDRotaryEmbedding.get_next_input_positions_tensor(
2904 - out=self.xdrope_positions.np,
2905 - out_offset=dst_start,
2906 - context_len=num_computed_tokens + prompt_part_len,
2907 - num_new_tokens=completion_part_len,
2908 - )
2909 -
2910 - xdrope_pos_ptr += completion_part_len
2911 -
2811 def _calc_spec_decode_metadata(
2812 self,
2813 num_draft_tokens: np.ndarray,
@@ -3303,7 +3202,6 @@ class GPUModelRunner(
3202
3203 req_start_idx = 0
3204 should_sync_mrope_positions = False
3306 - should_sync_xdrope_positions = False
3205
3206 for req_id in self.input_batch.req_ids:
3207 mm_embeds_req: list[torch.Tensor] = []
@@ -3396,10 +3294,6 @@ class GPUModelRunner(
3294 self._calc_mrope_positions(scheduler_output)
3295 self.mrope_positions.copy_to_gpu(total_num_scheduled_tokens)
3296
3399 - if should_sync_xdrope_positions:
3400 - self._calc_xdrope_positions(scheduler_output)
3401 - self.xdrope_positions.copy_to_gpu(total_num_scheduled_tokens)
3402 -
3297 return mm_embeds, is_mm_embed
3298
3299 def get_model(self) -> nn.Module:
@@ -3709,8 +3603,6 @@ class GPUModelRunner(
3603
3604 if self.uses_mrope:
3605 positions = self.mrope_positions.gpu[:, :num_input_tokens]
3712 - elif self.uses_xdrope_dim > 0:
3713 - positions = self.xdrope_positions.gpu[:, :num_input_tokens]
3606 else:
3607 positions = self.positions[:num_input_tokens]
3608 if num_input_tokens > num_scheduled_tokens:
@@ -6202,8 +6094,6 @@ class GPUModelRunner(
6094
6095 if self.uses_mrope:
6096 positions = self.mrope_positions.gpu[:, :num_tokens_padded]
6205 - elif self.uses_xdrope_dim > 0:
6206 - positions = self.xdrope_positions.gpu[:, :num_tokens_padded]
6097 else:
6098 positions = self.positions[:num_tokens_padded]
6099