Deprecate Transformers v4 support (#45161)

Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com>
This commit is contained in:
Harry Mellor
2026-06-11 11:04:01 +08:00
committed by GitHub
parent b038a2f73b
commit 18d87a87dc
19 changed files with 61 additions and 267 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ requests >= 2.26.0
tqdm
blake3
py-cpuinfo
transformers >= 4.56.0, != 5.0.*, != 5.1.*, != 5.2.*, != 5.3.*, != 5.4.*, != 5.5.0
transformers >= 5.5.3
tokenizers >= 0.21.1 # Required for fast incremental detokenization.
safetensors >= 0.6.2 # MXFP4/MXFP6 dtype support (F8_E8M0, F4) added in 0.6.0: https://github.com/huggingface/safetensors/pull/611
protobuf >= 5.29.6, !=6.30.*, !=6.31.*, !=6.32.*, !=6.33.0.*, !=6.33.1.*, !=6.33.2.*, !=6.33.3.*, !=6.33.4.* # Required by LlamaTokenizer, gRPC. CVE-2026-0994
+2 -6
View File
@@ -14,12 +14,10 @@ from dataclasses import is_dataclass
from datetime import datetime
from enum import IntEnum
from functools import lru_cache
from importlib.metadata import version
from pathlib import Path
from typing import TYPE_CHECKING, Any, Literal, TypeVar, get_args
import torch
from packaging.version import Version
from pydantic import ConfigDict, Field, model_validator
import vllm.envs as envs
@@ -697,10 +695,8 @@ class VllmConfig:
# Therefore, the presence of tie_word_embeddings in SomeVLTextConfig cannot
# be used as a signal for whether tie_word_embeddings should be copied from
# hf_config to the language_model config.
if (
Version(version("transformers")) >= Version("5.0.0")
and model_config.is_multimodal_model
and hasattr(model_config.hf_config, "tie_word_embeddings")
if model_config.is_multimodal_model and hasattr(
model_config.hf_config, "tie_word_embeddings"
):
tie_word_embeddings = model_config.hf_config.tie_word_embeddings
hf_config.get_text_config().tie_word_embeddings = tie_word_embeddings
@@ -77,30 +77,13 @@ logger = init_logger(__name__)
temp_dir = tempfile.gettempdir()
def enable_hf_transfer():
"""automatically activates hf_transfer"""
if "HF_HUB_ENABLE_HF_TRANSFER" not in os.environ:
try:
# enable hf hub transfer if available
import hf_transfer # type: ignore # noqa
huggingface_hub.constants.HF_HUB_ENABLE_HF_TRANSFER = True
except ImportError:
pass
def enable_xet_high_performance():
"""automatically activates xet high performance mode"""
if "HF_XET_HIGH_PERFORMANCE" not in os.environ:
huggingface_hub.constants.HF_XET_HIGH_PERFORMANCE = True
if hasattr(huggingface_hub.constants, "HF_XET_HIGH_PERFORMANCE"):
# Transformers v5
enable_xet_high_performance()
else:
# Transformers v4
enable_hf_transfer()
enable_xet_high_performance()
class DisabledTqdm(tqdm):
+2 -7
View File
@@ -618,13 +618,8 @@ class Gemma3nForConditionalGeneration(
input_features = audio_input["input_features_padded"].squeeze(1)
input_features_mask = audio_input["input_features_mask"].squeeze(1)
audio_outputs = self.audio_tower(input_features, ~input_features_mask)
if isinstance(audio_outputs, tuple):
# Transformers v4
audio_encodings, audio_mask = audio_outputs
else:
# Transformers v5
audio_encodings = audio_outputs.last_hidden_state
audio_mask = audio_outputs.audio_mel_mask
audio_encodings = audio_outputs.last_hidden_state
audio_mask = audio_outputs.audio_mel_mask
audio_features = self.embed_audio(inputs_embeds=audio_encodings)
# The Gemma3nProcessor expects all audio will be 30s in length and
@@ -30,9 +30,7 @@ import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from packaging.version import Version
from transformers import PretrainedConfig
from transformers import __version__ as TRANSFORMERS_VERSION
from transformers.feature_extraction_utils import BatchFeature
from transformers.models.qwen3_omni_moe.configuration_qwen3_omni_moe import (
Qwen3OmniMoeAudioEncoderConfig,
@@ -1261,40 +1259,6 @@ class Qwen3OmniMoeThinkerMultiModalProcessor(
tok_kwargs = dict(tok_kwargs)
mm_kwargs["audio_kwargs"] = dict(mm_kwargs.get("audio_kwargs") or {})
mm_kwargs["text_kwargs"] = dict(mm_kwargs.get("text_kwargs") or {})
if Version(TRANSFORMERS_VERSION) < Version("4.58.0"):
# Extract audio_sample_rate before restructuring
audio_sample_rate = mm_kwargs.pop("audio_sample_rate", None)
# move truncation to audio_kwargs level to avoid conflict
# with tok_kwargs
mm_kwargs["audio_kwargs"].setdefault(
"truncation", mm_kwargs.pop("truncation", False)
)
mm_kwargs["text_kwargs"].setdefault(
"truncation", tok_kwargs.pop("truncation", False)
)
# Validate and conditionally pass audio_sample_rate
# WhisperFeatureExtractor has a fixed sampling rate, and vLLM's
# audio loader already resamples audio to the target rate.
# Only pass the value if it matches to avoid unexpected behavior.
if audio_sample_rate is not None:
expected_sr = feature_extractor.sampling_rate
if audio_sample_rate != expected_sr:
logger.warning(
"[%s] audio_sample_rate mismatch: user provided %dHz "
"but model expects %dHz. Ignoring user value. "
"vLLM's audio loader already resampled to %dHz.",
self.__class__.__name__,
audio_sample_rate,
expected_sr,
expected_sr,
)
else:
# Sample rate matches, safe to pass
mm_kwargs["audio_kwargs"]["audio_sample_rate"] = (
audio_sample_rate
)
hf_inputs = super()._call_hf_processor(
prompt=prompt,
+16 -42
View File
@@ -27,6 +27,10 @@ import transformers
from packaging.version import Version
from torch import nn
from transformers import AutoModel
from transformers.conversion_mapping import (
WeightRenaming,
get_model_conversion_mapping,
)
from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS
from vllm.compilation.decorators import support_torch_compile
@@ -212,16 +216,9 @@ class Base(
`create_attention_instances` are used
- Sets the dtype to the default torch dtype set by vLLM because Transformers
uses the config dtype when creating the model
- Propagates this dtype to any sub-configs because Transformers model
implementations do not support/use different dtypes in sub-models
"""
self.text_config._attn_implementation = "vllm"
self.config.dtype = torch.get_default_dtype()
# TODO(hmellor): Remove this when Transformers v4 support is dropped
for sub_config_name in getattr(self.config, "sub_configs", {}):
sub_config = getattr(self.config, sub_config_name)
if sub_config.dtype != (dtype := self.config.dtype):
sub_config.dtype = dtype
def _get_decoder_cls(self, **kwargs: dict) -> type[PreTrainedModel]:
"""
@@ -300,9 +297,7 @@ class Base(
This handles:
- Transformers weight renaming:
- from `WeightRenaming` in Transformers v5
- from `_checkpoint_conversion_mapping` in Transformers v4
- Transformers weight renaming from `WeightRenaming`
- Checkpoints saved with a base model prefix that is not `model`
- Checkpoints saved with no base model prefix
- Any quantization config specific mappings
@@ -310,37 +305,16 @@ class Base(
self.hf_to_vllm_mapper = WeightsMapper()
orig_to_new_regex = self.hf_to_vllm_mapper.orig_to_new_regex
if Version(transformers.__version__) >= Version("5.0.0"):
from transformers.conversion_mapping import (
WeightRenaming,
get_model_conversion_mapping,
)
for mapping in get_model_conversion_mapping(self.model):
# Handle weights which have been renamed in Transformers
if isinstance(mapping, WeightRenaming):
# Recompile using regex (Transformers used re)
compiled_sources = re.compile(
mapping.compiled_sources.pattern, mapping.compiled_sources.flags
)
target_pattern = mapping.target_patterns[0]
orig_to_new_regex[compiled_sources] = target_pattern
# TODO: Handle WeightConverter to enable layer merging
else:
# Replace legacy suffixes used for norms
# TODO(hmellor): Remove this when Transformers v4 support is dropped
orig_to_new_regex.update(
{
re.compile(r"\.gamma$"): ".weight",
re.compile(r"\.beta$"): ".bias",
}
)
# Handle weights which have been renamed in Transformers
# TODO(hmellor): Remove this when Transformers v4 support is dropped
ccm = getattr(self.model, "_checkpoint_conversion_mapping", {})
for source, target in ccm.items():
orig_to_new_regex[re.compile(source)] = target
for mapping in get_model_conversion_mapping(self.model):
# Handle weights which have been renamed in Transformers
if isinstance(mapping, WeightRenaming):
# Recompile using regex (Transformers used re)
compiled_sources = re.compile(
mapping.compiled_sources.pattern, mapping.compiled_sources.flags
)
target_pattern = mapping.target_patterns[0]
orig_to_new_regex[compiled_sources] = target_pattern
# TODO: Handle WeightConverter to enable layer merging
# Handle unexpected weights which should be ignored
if self.model._keys_to_ignore_on_load_unexpected is not None:
@@ -377,7 +351,7 @@ class Base(
"""
Check if the model has tied word embeddings.
"""
# Transformers v4 and v5 will store this in different places
# Models created with Transformers v4 and v5 will store this in different places
tie_word_embeddings_v4 = getattr(self.text_config, "tie_word_embeddings", False)
tie_word_embeddings_v5 = getattr(self.config, "tie_word_embeddings", False)
return tie_word_embeddings_v4 or tie_word_embeddings_v5
@@ -101,8 +101,6 @@ Style = Literal[
"replicate",
"colwise_gather_output",
"rowwise_split_input",
"colwise_rep",
"rowwise_rep",
]
@@ -131,12 +129,8 @@ def replace_linear_class(
"colwise": (ColumnParallelLinear, {}),
"rowwise": (RowParallelLinear, {}),
"replicate": (ReplicatedLinear, {}),
# Transformers v5
"colwise_gather_output": (ColumnParallelLinear, {"gather_output": True}),
"rowwise_split_input": (RowParallelLinear, {"input_is_parallel": False}),
# Transformers v4
"colwise_rep": (ColumnParallelLinear, {"gather_output": True}),
"rowwise_rep": (RowParallelLinear, {"input_is_parallel": False}),
}.get(style, (ReplicatedLinear, {}))
return vllm_linear_cls(
-15
View File
@@ -5,7 +5,6 @@
"""PyTorch Ultravox model."""
import copy
import inspect
from collections.abc import Iterable, Mapping, Sequence
from types import SimpleNamespace
from typing import Annotated, Any, Literal, TypeAlias
@@ -397,17 +396,10 @@ class UltravoxTransformerProjector(nn.Module, ModuleUtilsMixin):
)
hidden_states = hidden_states + positions
# Backward compatibility for Transformers v4 where layer_head_mask
# was a required argument for WhisperEncoderLayer.forward
kwargs = {}
if "layer_head_mask" in inspect.signature(self.layers[0].forward).parameters:
kwargs["layer_head_mask"] = None
for layer in self.layers:
hidden_states = layer(
hidden_states,
attention_mask=extended_attention_mask,
**kwargs,
)
# BC version that allows for the old tupled output
if isinstance(hidden_states, tuple):
@@ -504,17 +496,10 @@ class ModifiedWhisperEncoder(WhisperEncoder):
attention_mask = self.get_attention_mask_by_audio_len(audio_lens, hidden_states)
# Backward compatibility for Transformers v4 where layer_head_mask
# was a required argument for WhisperEncoderLayer.forward
kwargs = {}
if "layer_head_mask" in inspect.signature(self.layers[0].forward).parameters:
kwargs["layer_head_mask"] = None
for encoder_layer in self.layers:
hidden_states = encoder_layer(
hidden_states,
attention_mask,
**kwargs,
)
# BC version that allows for the old tupled output
if isinstance(hidden_states, tuple):
+1 -9
View File
@@ -31,21 +31,13 @@ from mistral_common.tokens.tokenizers.sentencepiece import (
)
from mistral_common.tokens.tokenizers.tekken import Tekkenizer
from pydantic import ValidationError
from transformers.tokenization_mistral_common import MistralCommonBackend
from vllm.entrypoints.chat_utils import ChatCompletionMessageParam
from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
from vllm.logger import init_logger
from vllm.tokenizers.protocol import TokenizerLike
try:
# Transformers v5
from transformers.tokenization_mistral_common import MistralCommonBackend
except ImportError:
# Transformers v4
from transformers.tokenization_mistral_common import (
MistralCommonTokenizer as MistralCommonBackend,
)
if TYPE_CHECKING:
import llguidance
from transformers import BatchEncoding
+18 -56
View File
@@ -16,6 +16,7 @@ from huggingface_hub import constants
from packaging.version import Version
from safetensors.torch import _TYPES as _SAFETENSORS_TO_TORCH_DTYPE
from transformers import GenerationConfig, PretrainedConfig
from transformers.configuration_utils import ALLOWED_LAYER_TYPES
from transformers.models.auto.image_processing_auto import get_image_processor_config
from transformers.models.auto.modeling_auto import (
MODEL_FOR_CAUSAL_LM_MAPPING_NAMES,
@@ -49,15 +50,6 @@ from .repo_utils import (
with_retry,
)
try:
# Transformers v5
from transformers.configuration_utils import ALLOWED_ATTENTION_LAYER_TYPES
except ImportError:
# Transformers v4
from transformers.configuration_utils import (
ALLOWED_LAYER_TYPES as ALLOWED_ATTENTION_LAYER_TYPES,
)
if envs.VLLM_USE_MODELSCOPE:
from modelscope import AutoConfig
else:
@@ -68,9 +60,8 @@ MISTRAL_CONFIG_NAME = "params.json"
logger = init_logger(__name__)
if Version(version("transformers")) < Version("5.0.0"):
logger.warning(
"Support for Transformers v4 is deprecated. The Transformers v4 codepath will "
"become unmaintained in vLLM v0.22.0 and will be removed in vLLM v0.24.0. "
raise ImportError(
"Support for Transformers v4 is deprecated and was removed in vLLM v0.24.0. "
"Please upgrade to Transformers v5: pip install --upgrade transformers"
)
@@ -159,7 +150,7 @@ def is_rope_parameters_nested(rope_parameters: dict[str, Any]) -> bool:
# Cannot be nested if rope_parameters is empty
if not rope_parameters:
return False
return set(rope_parameters.keys()).issubset(ALLOWED_ATTENTION_LAYER_TYPES)
return set(rope_parameters.keys()).issubset(ALLOWED_LAYER_TYPES)
@contextmanager
@@ -183,24 +174,21 @@ def _patch_hf_transformers_validate_rope():
hf transformers (from v5 onwards)
"""
if Version(version("transformers")) >= Version("5.0.0"):
if hasattr(PretrainedConfig.validate_rope, "__vllm_patched__"):
return
if hasattr(PretrainedConfig.validate_rope, "__vllm_patched__"):
return
_original_validate_rope = PretrainedConfig.validate_rope
_original_validate_rope = PretrainedConfig.validate_rope
@wraps(_original_validate_rope)
def patched_validate_rope(self, *args, **kwargs):
ignore_keys_param = kwargs.pop("ignore_keys", None)
original_ignore_keys = self.ignore_keys_at_rope_validation
self.ignore_keys_at_rope_validation = (
original_ignore_keys or ignore_keys_param
)
result = _original_validate_rope(self, *args, **kwargs)
return result
@wraps(_original_validate_rope)
def patched_validate_rope(self, *args, **kwargs):
ignore_keys_param = kwargs.pop("ignore_keys", None)
original_ignore_keys = self.ignore_keys_at_rope_validation
self.ignore_keys_at_rope_validation = original_ignore_keys or ignore_keys_param
result = _original_validate_rope(self, *args, **kwargs)
return result
patched_validate_rope.__vllm_patched__ = True # type: ignore[attr-defined]
PretrainedConfig.validate_rope = patched_validate_rope
patched_validate_rope.__vllm_patched__ = True # type: ignore[attr-defined]
PretrainedConfig.validate_rope = patched_validate_rope
class HFConfigParser(ConfigParserBase):
@@ -493,39 +481,13 @@ def patch_rope_parameters(config: PretrainedConfig) -> None:
"""Provide backwards compatibility for RoPE."""
from vllm.config.utils import getattr_iter
# Older custom models may use non-standard field names
# which need patching for both Transformers v4 and v5.
# Older custom models may use non-standard field names which need patching.
names = ["rope_theta", "rotary_emb_base"]
rope_theta = getattr_iter(config, names, None, warn=True)
names = ["partial_rotary_factor", "rotary_pct", "rotary_emb_fraction"]
partial_rotary_factor = getattr_iter(config, names, None, warn=True)
ompe = getattr(config, "original_max_position_embeddings", None)
if Version(version("transformers")) < Version("5.0.0"):
# Transformers v4 installed, legacy config fields may be present.
if is_rope_parameters_nested(getattr(config, "rope_parameters", {})):
# Loading nested rope_parameters (from Transformers v5) in Transformers v4.
# Skip legacy patching since it should already be in the correct format.
pass
else:
if (rope_scaling := getattr(config, "rope_scaling", None)) is not None:
config.rope_parameters = rope_scaling
if (
rope_theta is not None
or partial_rotary_factor is not None
or ompe is not None
) and not getattr(config, "rope_parameters", None):
config.rope_parameters = {"rope_type": "default"}
# Patch legacy fields into rope_parameters
if rope_theta is not None:
config.rope_parameters["rope_theta"] = rope_theta
if partial_rotary_factor is not None:
config.rope_parameters["partial_rotary_factor"] = partial_rotary_factor
if ompe is not None:
config.rope_parameters["original_max_position_embeddings"] = ompe
patch_legacy_rope_type(getattr(config, "rope_parameters", None))
elif rope_theta is not None or getattr(config, "rope_parameters", None):
# Transformers v5 installed
if rope_theta is not None or getattr(config, "rope_parameters", None):
# Patch these fields in case they used non-standard names
if rope_theta is not None:
config.rope_theta = rope_theta
@@ -3,6 +3,7 @@
# adapted from https://github.com/deepseek-ai/DeepSeek-VL2/blob/faf18023f24b962b32d9f0a2d89e402a8d383a78/deepseek_vl2/models/modeling_deepseek_vl_v2.py#L115-L268
from huggingface_hub.dataclasses import strict
from transformers import DeepseekV2Config, PretrainedConfig
@@ -87,16 +88,9 @@ class MlpProjectorConfig(PretrainedConfig):
super().__init__(**kwargs)
if hasattr(DeepseekV2Config, "validate"):
# Transformers v5
from huggingface_hub.dataclasses import strict
@strict
class DeepseekVLV2TextConfig(DeepseekV2Config):
kv_lora_rank: int | None = None
else:
# Transformers v4
DeepseekVLV2TextConfig = DeepseekV2Config # type: ignore[misc]
@strict
class DeepseekVLV2TextConfig(DeepseekV2Config):
kv_lora_rank: int | None = None
class DeepseekVLV2Config(PretrainedConfig):
@@ -228,15 +228,8 @@ class OlmoHybridConfig(PretrainedConfig):
if "full_attention" not in layer_types:
layer_types[-1] = "full_attention"
if hasattr(self, "validate_layer_type"):
# Transformers v5
self.layer_types = layer_types
self.validate_layer_type()
else:
# Transformers v4
from transformers.configuration_utils import layer_type_validation
layer_type_validation(layer_types, num_hidden_layers)
self.layer_types = layer_types
self.validate_layer_type()
if "linear_attention" not in layer_types:
raise ValueError(
"OLMoHybrid expects at least one 'linear_attention' layer."
+5 -12
View File
@@ -94,18 +94,11 @@ class Qwen3_5TextConfig(PretrainedConfig):
else "full_attention"
for i in range(self.num_hidden_layers)
]
if hasattr(self, "validate_layer_type"):
# Transformers v5
kwargs["ignore_keys_at_rope_validation"] = {
"mrope_section",
"mrope_interleaved",
}
self.validate_layer_type()
else:
# Transformers v4
from transformers.configuration_utils import layer_type_validation
layer_type_validation(self.layer_types, self.num_hidden_layers)
kwargs["ignore_keys_at_rope_validation"] = {
"mrope_section",
"mrope_interleaved",
}
self.validate_layer_type()
# linear attention part
self.linear_conv_kernel_dim = linear_conv_kernel_dim
+5 -12
View File
@@ -100,18 +100,11 @@ class Qwen3_5MoeTextConfig(PretrainedConfig):
else "full_attention"
for i in range(self.num_hidden_layers)
]
if hasattr(self, "validate_layer_type"):
# Transformers v5
kwargs["ignore_keys_at_rope_validation"] = {
"mrope_section",
"mrope_interleaved",
}
self.validate_layer_type()
else:
# Transformers v4
from transformers.configuration_utils import layer_type_validation
layer_type_validation(self.layer_types, self.num_hidden_layers)
kwargs["ignore_keys_at_rope_validation"] = {
"mrope_section",
"mrope_interleaved",
}
self.validate_layer_type()
# linear attention part
self.linear_conv_kernel_dim = linear_conv_kernel_dim
@@ -252,14 +252,7 @@ class Qwen3NextConfig(PretrainedConfig):
"linear_attention" if bool((i + 1) % 4) else "full_attention"
for i in range(self.num_hidden_layers)
]
if hasattr(self, "validate_layer_type"):
# Transformers v5
self.validate_layer_type()
else:
# Transformers v4
from transformers.configuration_utils import layer_type_validation
layer_type_validation(self.layer_types)
self.validate_layer_type()
# linear attention part
self.linear_conv_kernel_dim = linear_conv_kernel_dim
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import os
from dataclasses import fields, is_dataclass
from dataclasses import fields
from typing import Any
from transformers import PretrainedConfig
@@ -16,11 +16,8 @@ class SpeculatorsConfig(PretrainedConfig):
model_type = "speculators"
def __init__(self, **kwargs):
# Transformers v4 - super().__init__ which sets all kwargs as attributes
if not is_dataclass(PretrainedConfig):
return super().__init__(**kwargs)
# Transformers v5 - super().__init__ performs some validation before
# setting all kwargs as attributes, so we set them first to be safe
# super().__init__ performs some validation before setting all kwargs as
# attributes, so we set them first to be safe
pre_trained_config_fields = {f.name for f in fields(PretrainedConfig)}
super_kwargs = dict()
for key, value in kwargs.items():
-4
View File
@@ -59,10 +59,6 @@ def _transformers_v4_compatibility_init() -> Any:
This can be removed if `Molmo2ForConditionalGeneration` is upstreamed to
Transformers."""
# Transformers v4
if hasattr(ProcessorMixin, "optional_attributes"):
return
# Transformers v5
if hasattr(ProcessorMixin.__init__, "_vllm_patched"):
return
@@ -56,11 +56,6 @@ class MistralCommonPixtralProcessor(ProcessorMixin):
image_processor: MistralCommonImageProcessor,
) -> None:
self.tokenizer = tokenizer.transformers_tokenizer
# Back-compatibility for Transformers v4
if not hasattr(self.tokenizer, "init_kwargs"):
self.tokenizer.init_kwargs = {}
self.image_processor = image_processor
image_special_ids = self.image_processor.mm_encoder.special_ids
@@ -111,11 +111,6 @@ class MistralCommonVoxtralProcessor(ProcessorMixin):
feature_extractor: MistralCommonFeatureExtractor,
) -> None:
self.tokenizer = tokenizer.transformers_tokenizer
# Back-compatibility for Transformers v4
if not hasattr(self.tokenizer, "init_kwargs"):
self.tokenizer.init_kwargs = {}
self.feature_extractor = feature_extractor
audio_special_ids = self.feature_extractor.audio_encoder.special_ids