[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