A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 14, 05:30 UTC
Refactor sliding window configuration to Transformers best practice (#21927)
Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com>
Harry Mellor committed
Aug 10, 2025 at 04:50 UTC
c49848396d34a1059fbec2a197394484acf5a903
16 files changed
+124
-232
docs/contributing/model/basic.md
+1
-1
@@ -117,7 +117,7 @@ For models with interleaving sliding windows (e.g. `google/gemma-2-2b-it` and `m
117
118
To support a model with interleaving sliding windows, we need to take care of the following details:
119
120
-- Make sure the model's `config.json` contains `sliding_window_pattern`. vLLM then sets `self.hf_text_config.interleaved_sliding_window` to the value of `self.hf_text_config.sliding_window` and deletes `sliding_window` from `self.hf_text_config`. The model will then be treated as a full-attention model.
120
+- Make sure the model's `config.json` contains `layer_types`.
121
- In the modeling code, parse the correct sliding window value for every layer, and pass it to the attention layer's `per_layer_sliding_window` argument. For reference, check [this line](https://github.com/vllm-project/vllm/blob/996357e4808ca5eab97d4c97c7d25b3073f46aab/vllm/model_executor/models/llama.py#L171).
122
123
With these two steps, interleave sliding windows should work with the model.
tests/test_config.py
-22
@@ -200,28 +200,6 @@ def test_disable_sliding_window(model_id_expected):
200
assert model_config.max_model_len == expected
201
202
203
-def test_get_sliding_window():
204
- TEST_SLIDING_WINDOW = 4096
205
- # Test that the sliding window is correctly computed.
206
- # For Qwen1.5/Qwen2, get_sliding_window() should be None
207
- # when use_sliding_window is False.
208
- qwen2_model_config = ModelConfig("Qwen/Qwen1.5-7B")
209
-
210
- qwen2_model_config.hf_config.use_sliding_window = False
211
- qwen2_model_config.hf_config.sliding_window = TEST_SLIDING_WINDOW
212
- assert qwen2_model_config.get_sliding_window() is None
213
-
214
- qwen2_model_config.hf_config.use_sliding_window = True
215
- assert qwen2_model_config.get_sliding_window() == TEST_SLIDING_WINDOW
216
-
217
- mistral_model_config = ModelConfig("mistralai/Mistral-7B-v0.1")
218
- mistral_model_config.hf_config.sliding_window = None
219
- assert mistral_model_config.get_sliding_window() is None
220
-
221
- mistral_model_config.hf_config.sliding_window = TEST_SLIDING_WINDOW
222
- assert mistral_model_config.get_sliding_window() == TEST_SLIDING_WINDOW
223
-
224
-
203
@pytest.mark.skipif(current_platform.is_rocm(),
204
reason="Xformers backend is not supported on ROCm.")
205
def test_get_pooling_config():
vllm/config/__init__.py
+31
-80
@@ -40,8 +40,9 @@ from vllm.transformers_utils.config import (
40
ConfigFormat, get_config, get_hf_image_processor_config,
41
get_hf_text_config, get_pooling_config,
42
get_sentence_transformer_tokenizer_config, is_encoder_decoder,
43
- maybe_override_with_speculators_target_model, try_get_generation_config,
44
- try_get_safetensors_metadata, try_get_tokenizer_config, uses_mrope)
43
+ is_interleaved, maybe_override_with_speculators_target_model,
44
+ try_get_generation_config, try_get_safetensors_metadata,
45
+ try_get_tokenizer_config, uses_mrope)
46
from vllm.transformers_utils.s3_utils import S3Model
47
from vllm.transformers_utils.utils import is_s3, maybe_model_redirect
48
# yapf conflicts with isort for this block
@@ -714,53 +715,31 @@ class ModelConfig:
715
revision=self.revision,
716
)
717
717
- # Workaround for Gemma 2 which uses interleaved sliding window
718
- # attention, but it's not specified in its config.
719
- # TODO: remove this when Gemma 2 config updated in HuggingFace.
720
- if self.hf_text_config.model_type == "gemma2":
721
- self.hf_text_config.sliding_window_pattern = 2
722
-
723
- # TODO: remove this when Gemma 3n config updated in HuggingFace.
724
- if self.hf_text_config.model_type == "gemma3n_text":
725
- # 4 sliding window attention followed by 1 full attention
726
- self.hf_text_config.sliding_window_pattern = "LLLLG"
727
-
728
- sliding_window = getattr(self.hf_text_config, "sliding_window", None)
729
- sliding_window_pattern = getattr(self.hf_text_config,
730
- "sliding_window_pattern", None)
731
- has_interleaved_attention = sliding_window_pattern is not None or (
732
- isinstance(sliding_window, list))
733
-
734
- if not self.disable_sliding_window and has_interleaved_attention:
735
- if not envs.VLLM_USE_V1 and (backend := envs.VLLM_ATTENTION_BACKEND
736
- ) in ("XFORMERS", "FLASHINFER"):
737
- sliding_window_len_min = get_min_sliding_window(
738
- self.hf_text_config.sliding_window)
739
-
740
- logger.warning_once(
741
- "%s has interleaved attention, which is currently not supported by the %s backend. Disabling sliding window and capping the max length to the sliding window size (%d).", # noqa: E501
742
- self.hf_text_config.model_type,
743
- backend,
744
- sliding_window_len_min,
745
- )
746
- self.disable_sliding_window = True
747
- else:
748
- # for a model with interleaved attention,
749
- # the scheduler and the model treat it as full attention
750
- # (i.e., not dropping any tokens outside the window).
751
- # only the attention layer itself is aware of the sliding
752
- # window, and use the window size to compute the attention.
753
- self.hf_text_config.interleaved_sliding_window = sliding_window
754
-
755
- if hasattr(self.hf_text_config, "sliding_window"):
756
- delattr(self.hf_text_config, "sliding_window")
757
-
758
- sliding_window = None
718
+ # Interleaved attention is not supported by some backends in V0
719
+ if (not self.disable_sliding_window
720
+ and is_interleaved(self.hf_text_config)
721
+ and not envs.VLLM_USE_V1
722
+ and (backend := envs.VLLM_ATTENTION_BACKEND)
723
+ in ("XFORMERS", "FLASHINFER")):
724
+ logger.warning_once(
725
+ "%s has interleaved attention, which is currently not "
726
+ "supported by the %s backend. Disabling sliding window and "
727
+ "capping the max length to the sliding window size (%d).",
728
+ self.hf_text_config.model_type,
729
+ backend,
730
+ self.hf_text_config.sliding_window,
731
+ )
732
+ self.disable_sliding_window = True
733
734
self.original_max_model_len = self.max_model_len
735
self.max_model_len = self.get_and_verify_max_len(self.max_model_len)
736
self.multimodal_config = self._init_multimodal_config()
737
738
+ if self.disable_sliding_window:
739
+ # Set after get_and_verify_max_len to ensure that max_model_len
740
+ # can be correctly capped to sliding window size
741
+ self.hf_text_config.sliding_window = None
742
+
743
if not self.skip_tokenizer_init:
744
self._verify_tokenizer_mode()
745
@@ -1322,27 +1301,10 @@ class ModelConfig:
1301
if self.use_async_output_proc:
1302
self.use_async_output_proc = False
1303
1325
- def get_hf_config_sliding_window(
1326
- self) -> Union[Optional[int], list[Optional[int]]]:
1327
- """Get the sliding window size, or None if disabled."""
1328
-
1329
- # Some models, like Qwen2 and Qwen1.5, use `use_sliding_window` in
1330
- # addition to sliding window size. We check if that field is present
1331
- # and if it's False, return None.
1332
- if (hasattr(self.hf_text_config, "use_sliding_window")
1333
- and not self.hf_text_config.use_sliding_window):
1334
- return None
1304
+ def get_sliding_window(self) -> Optional[int]:
1305
+ """Get the sliding window size from the HF text config if present."""
1306
return getattr(self.hf_text_config, "sliding_window", None)
1307
1337
- def get_sliding_window(self) -> Optional[Union[int, list[Optional[int]]]]:
1338
- """Get the sliding window size, or None if disabled.
1339
- """
1340
- # If user disables sliding window, return None.
1341
- if self.disable_sliding_window:
1342
- return None
1343
- # Otherwise get the value from the hf config.
1344
- return self.get_hf_config_sliding_window()
1345
-
1308
def get_vocab_size(self) -> int:
1309
return getattr(self.hf_text_config, "vocab_size", 0)
1310
@@ -1762,7 +1724,7 @@ class ModelConfig:
1724
tokenizer_config=tokenizer_config,
1725
max_model_len=max_model_len,
1726
disable_sliding_window=self.disable_sliding_window,
1765
- sliding_window_len=self.get_hf_config_sliding_window(),
1727
+ sliding_window=self.get_sliding_window(),
1728
spec_target_max_model_len=self.spec_target_max_model_len,
1729
encoder_config=self.encoder_config)
1730
logger.info("Using max model len %s", max_model_len)
@@ -3305,7 +3267,7 @@ def _get_and_verify_max_len(
3267
tokenizer_config: Optional[dict],
3268
max_model_len: Optional[int],
3269
disable_sliding_window: bool,
3308
- sliding_window_len: Optional[Union[int, list[Optional[int]]]],
3270
+ sliding_window: Optional[int],
3271
spec_target_max_model_len: Optional[int] = None,
3272
encoder_config: Optional[Any] = None,
3273
) -> int:
@@ -3344,13 +3306,10 @@ def _get_and_verify_max_len(
3306
3307
# If sliding window is manually disabled, max_length should be less
3308
# than the sliding window length in the model config.
3347
- if disable_sliding_window and sliding_window_len is not None:
3348
-
3349
- sliding_window_len_min = get_min_sliding_window(sliding_window_len)
3350
- max_len_key = "sliding_window" \
3351
- if sliding_window_len_min < derived_max_model_len else max_len_key
3352
- derived_max_model_len = min(derived_max_model_len,
3353
- sliding_window_len_min)
3309
+ if (disable_sliding_window and sliding_window is not None
3310
+ and sliding_window < derived_max_model_len):
3311
+ max_len_key = "sliding_window"
3312
+ derived_max_model_len = sliding_window
3313
3314
# Consider model_max_length in tokenizer_config
3315
if tokenizer_config:
@@ -3451,14 +3410,6 @@ def _get_and_verify_max_len(
3410
return int(max_model_len)
3411
3412
3454
-def get_min_sliding_window(
3455
- sliding_window: Union[int, list[Optional[int]]]) -> int:
3456
- if isinstance(sliding_window, list):
3457
- return min(s for s in sliding_window if s is not None)
3458
-
3459
- return sliding_window
3460
-
3461
-
3413
def get_served_model_name(model: str,
3414
served_model_name: Optional[Union[str, list[str]]]):
3415
"""
vllm/engine/arg_utils.py
+9
-1
@@ -39,6 +39,7 @@ from vllm.plugins import load_general_plugins
39
from vllm.ray.lazy_utils import is_ray_initialized
40
from vllm.reasoning import ReasoningParserManager
41
from vllm.test_utils import MODEL_WEIGHTS_S3_BUCKET, MODELS_ON_S3
42
+from vllm.transformers_utils.config import is_interleaved
43
from vllm.transformers_utils.utils import check_gguf_file
44
from vllm.utils import (STR_DUAL_CHUNK_FLASH_ATTN_VAL, FlexibleArgumentParser,
45
GiB_bytes, get_ip, is_in_ray_actor)
@@ -1081,6 +1082,13 @@ class EngineArgs:
1082
"DualChunkFlashAttention is not supported on V1 engine. "
1083
"To run the model in V0 engine, try set 'VLLM_USE_V1=0'")
1084
1085
+ sliding_window: Optional[int] = None
1086
+ if not is_interleaved(model_config.hf_text_config):
1087
+ # Only set CacheConfig.sliding_window if the model is all sliding
1088
+ # window. Otherwise CacheConfig.sliding_window will override the
1089
+ # global layers in interleaved sliding window models.
1090
+ sliding_window = model_config.get_sliding_window()
1091
+
1092
cache_config = CacheConfig(
1093
block_size=self.block_size,
1094
gpu_memory_utilization=self.gpu_memory_utilization,
@@ -1088,7 +1096,7 @@ class EngineArgs:
1096
cache_dtype=self.kv_cache_dtype,
1097
is_attention_free=model_config.is_attention_free,
1098
num_gpu_blocks_override=self.num_gpu_blocks_override,
1091
- sliding_window=model_config.get_sliding_window(),
1099
+ sliding_window=sliding_window,
1100
enable_prefix_caching=self.enable_prefix_caching,
1101
prefix_caching_hash_algo=self.prefix_caching_hash_algo,
1102
cpu_offload_gb=self.cpu_offload_gb,
vllm/model_executor/models/commandr.py
+7
-15
@@ -182,21 +182,13 @@ class CohereAttention(nn.Module):
182
)
183
184
# Model v2 has interleaved sliding windows, v1 does not
185
- interleaved_sliding_window = getattr(config,
186
- "interleaved_sliding_window",
187
- None)
188
- self.v1 = interleaved_sliding_window is None
189
-
190
- layer_idx = extract_layer_index(prefix)
191
- layer_has_sliding_window = (
192
- getattr(config, "sliding_window_pattern", False) and
193
- (layer_idx + 1) % self.config.sliding_window_pattern
194
- != 0) or (getattr(config, "layer_types", False)
195
- and config.layer_types[layer_idx] == "sliding_attention")
196
-
197
- self.sliding_window = (interleaved_sliding_window
198
- or config.sliding_window
199
- if layer_has_sliding_window else None)
185
+ self.v1 = isinstance(config, CohereConfig)
186
+
187
+ self.sliding_window = None
188
+ if not self.v1:
189
+ layer_idx = extract_layer_index(prefix)
190
+ if config.layer_types[layer_idx] == "sliding_attention":
191
+ self.sliding_window = config.sliding_window
192
193
self.attn = Attention(self.num_heads,
194
self.head_dim,
vllm/model_executor/models/exaone4.py
+4
-17
@@ -159,25 +159,12 @@ class Exaone4Attention(nn.Module):
159
if quant_config is not None and quant_config.get_name() == "gguf":
160
is_neox_style = False
161
162
- self.apply_all_layers = False # apply rotary embeddings to every layer.
162
layer_idx = extract_layer_index(prefix)
164
- interleaved_sliding_window = getattr(config,
165
- "interleaved_sliding_window",
166
- 4096)
167
- sliding_window_pattern = getattr(config, "sliding_window_pattern",
168
- "LLLG")
169
-
170
- if sliding_window_pattern:
171
- layer_has_sliding_window = (
172
- layer_idx + 1) % sliding_window_pattern.__len__() != 0
173
- else:
174
- layer_has_sliding_window = False
175
- self.apply_all_layers = True
163
+ is_sliding = config.layer_types[layer_idx] == "sliding_attention"
164
+ self.sliding_window = config.sliding_window if is_sliding else None
165
177
- if layer_has_sliding_window:
178
- self.sliding_window = interleaved_sliding_window
179
- else:
180
- self.sliding_window = None
166
+ # apply rotary embeddings to every layer
167
+ self.apply_all_layers = not is_sliding
168
169
self.rotary_emb = get_rope(
170
self.head_dim,
vllm/model_executor/models/gemma2.py
+3
-6
@@ -144,13 +144,10 @@ class Gemma2Attention(nn.Module):
144
is_neox_style=True,
145
)
146
147
- # reference:
148
- # https://github.com/huggingface/transformers/blob/54be2d7ae87e873482b984cc956e165ca4dc0ba3/src/transformers/models/gemma2/modeling_gemma2.py#L312 # noqa
147
layer_idx = extract_layer_index(prefix)
150
- use_sliding_window = (layer_idx % 2 == 0 and getattr(
151
- config, "interleaved_sliding_window", None) is not None)
152
- sliding_window = config.interleaved_sliding_window if \
153
- use_sliding_window else None
148
+ is_sliding = config.layer_types[layer_idx] == "sliding_attention"
149
+ sliding_window = config.sliding_window if is_sliding else None
150
+
151
self.attn = Attention(self.num_heads,
152
self.head_dim,
153
self.scaling,
vllm/model_executor/models/gemma3.py
+4
-10
@@ -146,25 +146,19 @@ class Gemma3Attention(nn.Module):
146
self.q_norm = GemmaRMSNorm(self.head_dim, eps=config.rms_norm_eps)
147
self.k_norm = GemmaRMSNorm(self.head_dim, eps=config.rms_norm_eps)
148
149
- # TODO(woosuk): Add reference to the original HF implementation.
149
layer_idx = extract_layer_index(prefix)
151
- self.is_sliding = (getattr(
152
- config, "interleaved_sliding_window", None) is not None and (bool(
153
- (layer_idx + 1) % config.sliding_window_pattern))) or (
154
- getattr(config, "layer_types", None) is not None
155
- and config.layer_types[layer_idx] == "sliding_attention")
150
+ self.is_sliding = config.layer_types[layer_idx] == "sliding_attention"
151
+ sliding_window = config.sliding_window if self.is_sliding else None
152
+
153
# Initialize the rotary embedding.
154
if self.is_sliding:
155
# Local attention. Override the values in config.json.
156
self.rope_theta = config.rope_local_base_freq
157
self.rope_scaling = {"rope_type": "default"}
161
- self.sliding_window = (config.interleaved_sliding_window
162
- or config.sliding_window)
158
else:
159
# Global attention. Use the values in config.json.
160
self.rope_theta = config.rope_theta
161
self.rope_scaling = config.rope_scaling
167
- self.sliding_window = None
162
self.rotary_emb = get_rope(
163
self.head_dim,
164
rotary_dim=self.head_dim,
@@ -182,7 +176,7 @@ class Gemma3Attention(nn.Module):
176
cache_config=cache_config,
177
quant_config=quant_config,
178
logits_soft_cap=attn_logits_soft_cap,
185
- per_layer_sliding_window=self.sliding_window,
179
+ per_layer_sliding_window=sliding_window,
180
prefix=f"{prefix}.attn")
181
182
def forward(
vllm/model_executor/models/gemma3_mm.py
+2
-4
@@ -502,8 +502,6 @@ class Gemma3ForConditionalGeneration(nn.Module, SupportsMultiModal, SupportsPP,
502
self.config = config
503
self.quant_config = quant_config
504
self.multimodal_config = multimodal_config
505
- self.sliding_window = getattr(config.text_config,
506
- "interleaved_sliding_window", None)
505
506
self.vision_tower = SiglipVisionModel(config.vision_config,
507
quant_config,
@@ -690,11 +688,11 @@ class Gemma3ForConditionalGeneration(nn.Module, SupportsMultiModal, SupportsPP,
688
global_attn_mask = torch.where(img_mask == 2, 0, global_attn_mask)
689
global_attn_masks.append(global_attn_mask)
690
693
- if self.sliding_window is not None:
691
+ if (sliding_window := self.config.sliding_window) is not None:
692
# Create a local causal mask with sliding window (1024).
693
local_attn_mask = torch.ones_like(global_attn_mask)
694
local_attn_mask = torch.tril(local_attn_mask,
697
- diagonal=-self.sliding_window)
695
+ diagonal=-sliding_window)
696
local_attn_mask = torch.where(local_attn_mask == 0,
697
global_attn_mask, float("-inf"))
698
local_attn_masks.append(local_attn_mask)
vllm/model_executor/models/gemma3n.py
+6
-7
@@ -313,17 +313,16 @@ class Gemma3nAttention(nn.Module):
313
has_weight=False)
314
315
layer_idx = extract_layer_index(prefix)
316
+ is_sliding = config.layer_types[layer_idx] == "sliding_attention"
317
+ self.sliding_window = config.sliding_window if is_sliding else None
318
317
- is_sliding_window = (
318
- getattr(config, "interleaved_sliding_window", None) is not None
319
- and config.layer_types[layer_idx] == "sliding_attention")
320
-
321
- if is_sliding_window:
322
- self.sliding_window = config.interleaved_sliding_window
319
+ # Initialize the rotary embedding.
320
+ if is_sliding:
321
+ # Local attention. Override the values in config.json.
322
rope_theta = config.rope_local_base_freq
323
rope_scaling = {"rope_type": "default"}
324
else:
326
- self.sliding_window = None
325
+ # Global attention. Use the values in config.json.
326
rope_theta = config.rope_theta
327
rope_scaling = config.rope_scaling
328
vllm/model_executor/models/gritlm.py
+1
-3
@@ -248,9 +248,7 @@ class GritLM(LlamaForCausalLM, SupportsV0Only):
248
249
vllm_config.cache_config.sliding_window = None
250
251
- for attr in ("sliding_window", "interleaved_sliding_window"):
252
- if hasattr(hf_config, attr):
253
- delattr(hf_config, attr)
251
+ hf_config.sliding_window = None
252
253
super().__init__(vllm_config=vllm_config, prefix=prefix, **kwargs)
254
vllm/model_executor/models/llama.py
+5
-12
@@ -167,18 +167,11 @@ class LlamaAttention(nn.Module):
167
rope_scaling=rope_scaling,
168
quant_config=quant_config)
169
170
- if hasattr(config, "interleaved_sliding_window"):
171
- interleaved_sliding_window = config.interleaved_sliding_window
172
- if isinstance(interleaved_sliding_window, int):
173
- sliding_window = interleaved_sliding_window
174
- elif isinstance(interleaved_sliding_window, list):
175
- sw_idx = layer_idx % len(interleaved_sliding_window)
176
- sliding_window = interleaved_sliding_window[sw_idx]
177
- else:
178
- raise ValueError(
179
- f"{type(interleaved_sliding_window)} is not supported.")
180
- else:
181
- sliding_window = None
170
+ sliding_window = None
171
+ if layer_types := getattr(config, "layer_types", None):
172
+ is_sliding = layer_types[layer_idx] == "sliding_attention"
173
+ if is_sliding:
174
+ sliding_window = config.sliding_window
175
176
self.attn = Attention(
177
self.num_heads,
vllm/model_executor/models/phi4flash.py
+2
-7
@@ -116,13 +116,8 @@ class SambaYAttention(nn.Module):
116
self.Wqkv = nn.Linear(self.hidden_size, op_size, bias=True)
117
118
# disable sliding window for the second half of the model
119
- sliding_window = config.interleaved_sliding_window[layer_idx]
120
- if layer_idx >= config.num_hidden_layers // 2:
121
- assert sliding_window is None, \
122
- "sliding_window must be none for the second decoder"
123
- else:
124
- assert sliding_window is not None, \
125
- "sliding_window must be set for the first decoder"
119
+ is_sliding = config.layer_types[layer_idx] == "sliding_attention"
120
+ sliding_window = config.sliding_window if is_sliding else None
121
122
assert self.num_heads % 2 == 0, 'num_heads should be even'
123
assert self.num_key_value_heads % 2 == 0, 'num_heads should be even'
vllm/model_executor/models/qwen2.py
+2
-2
@@ -49,6 +49,7 @@ from vllm.model_executor.model_loader.weight_utils import (
49
default_weight_loader, maybe_remap_kv_scale_name)
50
from vllm.model_executor.sampling_metadata import SamplingMetadata
51
from vllm.sequence import IntermediateTensors
52
+from vllm.transformers_utils.config import is_interleaved
53
54
from .interfaces import SupportsLoRA, SupportsPP
55
from .utils import (AutoWeightsLoader, PPMissingLayer, extract_layer_index,
@@ -285,8 +286,7 @@ class Qwen2Model(nn.Module):
286
quant_config = vllm_config.quant_config
287
288
# TODO (@robertgshaw2): see if this can be moved out
288
- if (cache_config.sliding_window is not None
289
- and hasattr(config, "max_window_layers")):
289
+ if is_interleaved(vllm_config.model_config.hf_text_config):
290
assert config.max_window_layers == config.num_hidden_layers, (
291
"Sliding window for some but all layers is not supported. "
292
"This model uses sliding window but `max_window_layers` = {} "
vllm/model_executor/models/transformers.py
+7
-45
@@ -16,7 +16,7 @@
16
# limitations under the License.
17
"""Wrapper around `transformers` models"""
18
from collections.abc import Iterable, Mapping
19
-from contextlib import contextmanager, nullcontext
19
+from contextlib import contextmanager
20
from typing import Literal, Optional, Union
21
22
import regex as re
@@ -382,33 +382,6 @@ class MultiModalProcessor(BaseMultiModalProcessor[MultiModalProcessingInfo]):
382
)
383
384
385
-class ConfigOverride:
386
- """Context manager to temporarily override config attributes."""
387
-
388
- def __init__(self, config: PretrainedConfig, **kwargs):
389
- self.config = config
390
- self.kwargs = kwargs
391
- self.kwargs_original = {}
392
- self.kwargs_delete = set()
393
-
394
- def __enter__(self):
395
- """Override config attributes."""
396
- for key, value in self.kwargs.items():
397
- if not hasattr(self.config, key):
398
- self.kwargs_delete.add(key)
399
- self.kwargs_original[key] = getattr(self.config, key, None)
400
- setattr(self.config, key, value)
401
- return self.config
402
-
403
- def __exit__(self, exc_type, exc_value, traceback):
404
- """Restore original config attributes."""
405
- for key, value in self.kwargs_original.items():
406
- if key in self.kwargs_delete:
407
- delattr(self.config, key)
408
- else:
409
- setattr(self.config, key, value)
410
-
411
-
385
class TransformersBase(nn.Module, SupportsQuant, SupportsLoRA, SupportsPP):
386
embedding_padding_modules = ["lm_head"]
387
embedding_modules = ["embed_tokens"
@@ -434,21 +407,11 @@ class TransformersBase(nn.Module, SupportsQuant, SupportsLoRA, SupportsPP):
407
# To be updated in child classes for use in `load_weights`
408
self.skip_prefixes: Optional[list[str]] = None
409
437
- # vLLM handles interleaved sliding window attention by creating a new
438
- # interleaved_sliding_window attribute and deleting the sliding_window
439
- # attribute. This breaks the constructors in Transformers so we
440
- # temporarily add the attribute back to construct the model.
441
- config_override = nullcontext()
442
- if hasattr(self.config, "interleaved_sliding_window"):
443
- config_override = ConfigOverride(
444
- self.config,
445
- sliding_window=self.config.interleaved_sliding_window)
446
-
410
# Set correct attn and init on "meta" to delay allocating GPU tensors
411
# TODO: @raushan, use the public `model.set_attn_implementation()`
412
# method once its checks are fixed in Transformers.
413
self.text_config._attn_implementation = "vllm"
451
- with init_on_device_without_buffers("meta"), config_override:
414
+ with init_on_device_without_buffers("meta"):
415
self.model: PreTrainedModel = AutoModel.from_config(
416
self.config,
417
torch_dtype=self.model_config.dtype,
@@ -575,11 +538,10 @@ class TransformersBase(nn.Module, SupportsQuant, SupportsLoRA, SupportsPP):
538
attention_instances = {}
539
for i in range(start, end):
540
# Handle interleaved sliding window attention
578
- sliding_window = None
579
- if (hasattr(self.config, "interleaved_sliding_window")
580
- and hasattr(self.config, "sliding_window_pattern")
581
- and ((i + 1) % self.config.sliding_window_pattern > 0)):
582
- sliding_window = self.config.interleaved_sliding_window
541
+ per_layer_sliding_window = None
542
+ if (hasattr(self.config, "layer_types")
543
+ and self.config.layer_types[i] == "sliding_attention"):
544
+ per_layer_sliding_window = self.config.sliding_window
545
546
attention_instances[i] = Attention(
547
num_heads=num_heads,
@@ -590,7 +552,7 @@ class TransformersBase(nn.Module, SupportsQuant, SupportsLoRA, SupportsPP):
552
num_kv_heads=num_kv_heads,
553
cache_config=self.cache_config,
554
quant_config=self.quant_config,
593
- per_layer_sliding_window=sliding_window,
555
+ per_layer_sliding_window=per_layer_sliding_window,
556
prefix=f"{i}.attn")
557
return attention_instances
558
vllm/transformers_utils/config.py
+40
@@ -280,6 +280,17 @@ def is_encoder_decoder(config: PretrainedConfig) -> bool:
280
return getattr(config, "is_encoder_decoder", False)
281
282
283
+def is_interleaved(config: PretrainedConfig) -> bool:
284
+ """
285
+ Detect if the model with this config is used with interleaved attention.
286
+ """
287
+ text_config = config.get_text_config()
288
+ if layer_types := getattr(text_config, "layer_types", None):
289
+ interleaved_types = {"full_attention", "sliding_attention"}
290
+ return interleaved_types.issubset(layer_types)
291
+ return False
292
+
293
+
294
def _maybe_remap_hf_config_attrs(config: PretrainedConfig) -> PretrainedConfig:
295
"""Remap config attributes to match the expected names."""
296
for old_attr, new_attr in _CONFIG_ATTRS_MAPPING.items():
@@ -423,6 +434,23 @@ def get_config(
434
raise e
435
config = _maybe_remap_hf_config_attrs(config)
436
437
+ # Phi4Flash misuses this config as list[int]. Convert it to int and add
438
+ # the layer_types list[str] to make it HF compatible
439
+ if (config.model_type == "phi4flash"):
440
+ # TODO: Remove after the following PR is merged:
441
+ # https://huggingface.co/microsoft/Phi-4-mini-flash-reasoning/discussions/6
442
+ if not hasattr(config, "layer_types"):
443
+ config.layer_types = [
444
+ "sliding_attention" if i < config.num_hidden_layers // 2
445
+ and i % 2 == 1 else "full_attention"
446
+ for i in range(config.num_hidden_layers)
447
+ ]
448
+ # TODO: Remove after the following PR is merged:
449
+ # https://huggingface.co/microsoft/Phi-4-mini-flash-reasoning/discussions/7
450
+ if isinstance(config.sliding_window, list):
451
+ config.sliding_window = next(
452
+ filter(None, config.sliding_window), None)
453
+
454
elif config_format == ConfigFormat.MISTRAL:
455
# This function loads a params.json config which
456
# should be used when loading models in mistral format
@@ -434,6 +462,18 @@ def get_config(
462
config_dict["max_position_embeddings"] = max_position_embeddings
463
464
config = adapt_config_dict(config_dict)
465
+
466
+ # Mistral configs may define sliding_window as list[int]. Convert it
467
+ # to int and add the layer_types list[str] to make it HF compatible
468
+ if ((sliding_window := getattr(config, "sliding_window", None))
469
+ and isinstance(sliding_window, list)):
470
+ pattern_repeats = config.num_hidden_layers // len(sliding_window)
471
+ layer_types = sliding_window * pattern_repeats
472
+ config.layer_types = [
473
+ "full_attention" if layer_type is None else "sliding_attention"
474
+ for layer_type in layer_types
475
+ ]
476
+ config.sliding_window = next(filter(None, sliding_window), None)
477
else:
478
supported_formats = [
479
fmt.value for fmt in ConfigFormat if fmt != ConfigFormat.AUTO