Preserve revision pins in secondary artifact loaders (#51446)

Signed-off-by: Clinton Thomas <1033162+KernelClint@users.noreply.github.com>
Co-authored-by: Lucas Bourtoule <35483370+dhalf@users.noreply.github.com>
This commit is contained in:
Clinton Thomas
2026-08-11 04:33:10 +00:00
committed by GitHub
co-authored by Lucas Bourtoule
parent adc1200597
commit 90fd4a333f
11 changed files with 226 additions and 13 deletions
+27
View File
@@ -7,6 +7,7 @@ from copy import copy
from pathlib import Path from pathlib import Path
from types import SimpleNamespace from types import SimpleNamespace
from typing import cast from typing import cast
from unittest.mock import Mock
import pytest import pytest
from transformers import PretrainedConfig from transformers import PretrainedConfig
@@ -138,6 +139,32 @@ def test_head_size_falls_back_when_head_dim_is_zero():
assert convertor.get_head_size() == 128 assert convertor.get_head_size() == 128
def test_qk_rope_head_dim_recovery_uses_model_revision(monkeypatch: pytest.MonkeyPatch):
hf_config = PretrainedConfig(
model_type="deepseek_v2",
qk_rope_head_dim=128,
qk_nope_head_dim=128,
)
hf_config.name_or_path = "org/model"
get_hf_file_to_dict = Mock(return_value={"qk_rope_head_dim": 64})
monkeypatch.setattr(
"vllm.transformers_utils.repo_utils.get_hf_file_to_dict",
get_hf_file_to_dict,
)
convertor = ModelArchConfigConvertorBase(
hf_config,
hf_config,
revision="pinned-revision",
)
assert convertor._get_qk_rope_head_dim() == 64
get_hf_file_to_dict.assert_called_once_with(
"config.json", "org/model", "pinned-revision"
)
@pytest.mark.parametrize( @pytest.mark.parametrize(
"attribute", "attribute",
[ [
+40
View File
@@ -0,0 +1,40 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
from types import SimpleNamespace
from unittest.mock import Mock
import pytest
import torch
from vllm.config.vllm import VllmConfig
pytestmark = [pytest.mark.cpu_test, pytest.mark.skip_global_cleanup]
def test_quantization_config_forwards_model_revision(monkeypatch: pytest.MonkeyPatch):
quant_config = Mock()
quant_config.get_min_capability.return_value = 0
quant_config.get_supported_act_dtypes.return_value = [torch.float32]
model_config = SimpleNamespace(
quantization="auto_gptq",
model="repo/model",
hf_config=object(),
dtype=torch.float32,
revision="pinned-revision",
)
monkeypatch.setattr(
"vllm.model_executor.model_loader.weight_utils.get_quant_config",
lambda model_config, load_config: quant_config,
)
monkeypatch.setattr(
"vllm.platforms.current_platform.get_device_capability", lambda: None
)
VllmConfig._get_quantization_config(model_config, SimpleNamespace())
quant_config.maybe_update_config.assert_called_once_with(
"repo/model",
hf_config=model_config.hf_config,
revision="pinned-revision",
)
+51
View File
@@ -0,0 +1,51 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import contextlib
import io
from types import SimpleNamespace
from unittest.mock import Mock
import pytest
import torch
from vllm.model_executor.model_loader import tensorizer
pytestmark = [pytest.mark.cpu_test, pytest.mark.skip_global_cleanup]
def test_serialize_vllm_model_forwards_model_revision(
monkeypatch: pytest.MonkeyPatch,
):
@contextlib.contextmanager
def fake_open_stream(*args, **kwargs):
yield io.BytesIO()
snapshot_download = Mock()
monkeypatch.setattr(tensorizer, "open_stream", fake_open_stream)
monkeypatch.setattr(tensorizer, "TensorSerializer", Mock())
monkeypatch.setattr(
tensorizer,
"hf_api",
lambda: SimpleNamespace(snapshot_download=snapshot_download),
)
model = torch.nn.Module()
tensorizer_config = SimpleNamespace(
_construct_tensorizer_args=lambda: SimpleNamespace(
tensorizer_uri="/tmp/model.tensors",
tensorizer_dir="/tmp/out",
stream_kwargs={},
),
encryption_keyfile=None,
_is_sharded=False,
serialization_kwargs=None,
)
model_config = SimpleNamespace(
served_model_name="repo/model",
revision="pinned-revision",
)
tensorizer.serialize_vllm_model(model, tensorizer_config, model_config)
assert snapshot_download.call_args.args == ("repo/model",)
assert snapshot_download.call_args.kwargs["revision"] == "pinned-revision"
+47
View File
@@ -6,7 +6,15 @@ only get the `eos_token_id` from the tokenizer as defined by
`BaseRenderer.get_eos_token_id`. `BaseRenderer.get_eos_token_id`.
""" """
from types import SimpleNamespace
from typing import cast
from unittest.mock import patch
from transformers import PretrainedConfig
from vllm.config.model import ModelConfig
from vllm.tokenizers import get_tokenizer from vllm.tokenizers import get_tokenizer
from vllm.transformers_utils import config as config_module
from vllm.transformers_utils.config import try_get_generation_config from vllm.transformers_utils.config import try_get_generation_config
@@ -30,3 +38,42 @@ def test_get_blip2_eos_token():
generation_config = try_get_generation_config(model_name, trust_remote_code=False) generation_config = try_get_generation_config(model_name, trust_remote_code=False)
assert generation_config is not None assert generation_config is not None
assert generation_config.eos_token_id == 50118 assert generation_config.eos_token_id == 50118
def test_model_config_generation_fallback_forwards_code_revision():
model_config = cast(
ModelConfig,
SimpleNamespace(
generation_config="auto",
hf_config_path=None,
model="org/model",
trust_remote_code=True,
revision="model-pin",
code_revision="code-pin",
config_format="auto",
hf_token=None,
),
)
with (
patch.object(
config_module.GenerationConfig,
"from_pretrained",
side_effect=OSError,
),
patch.object(
config_module,
"get_config",
return_value=PretrainedConfig(),
) as get_config,
):
ModelConfig.try_get_generation_config(model_config)
get_config.assert_called_once_with(
"org/model",
trust_remote_code=True,
revision="model-pin",
code_revision="code-pin",
config_format="auto",
token=None,
)
+7 -1
View File
@@ -890,7 +890,11 @@ class ModelConfig:
convertor_cls = MODEL_ARCH_CONFIG_CONVERTORS.get( convertor_cls = MODEL_ARCH_CONFIG_CONVERTORS.get(
self.hf_config.model_type, ModelArchConfigConvertorBase self.hf_config.model_type, ModelArchConfigConvertorBase
) )
convertor = convertor_cls(self.hf_config, self.hf_text_config) convertor = convertor_cls(
self.hf_config,
self.hf_text_config,
revision=getattr(self, "revision", None),
)
return convertor.convert( return convertor.convert(
supports_multimodal=self._supports_multimodal_for_mm_prefix() supports_multimodal=self._supports_multimodal_for_mm_prefix()
) )
@@ -1651,6 +1655,7 @@ class ModelConfig:
self.hf_config_path or self.model, self.hf_config_path or self.model,
trust_remote_code=self.trust_remote_code, trust_remote_code=self.trust_remote_code,
revision=self.revision, revision=self.revision,
code_revision=self.code_revision,
config_format=self.config_format, config_format=self.config_format,
hf_token=self.hf_token, hf_token=self.hf_token,
) )
@@ -1658,6 +1663,7 @@ class ModelConfig:
config = try_get_generation_config( config = try_get_generation_config(
self.generation_config, self.generation_config,
trust_remote_code=self.trust_remote_code, trust_remote_code=self.trust_remote_code,
code_revision=self.code_revision,
config_format=self.config_format, config_format=self.config_format,
hf_token=self.hf_token, hf_token=self.hf_token,
) )
+1
View File
@@ -779,6 +779,7 @@ class VllmConfig:
quant_config.maybe_update_config( quant_config.maybe_update_config(
model_config.model, model_config.model,
hf_config=model_config.hf_config, hf_config=model_config.hf_config,
revision=model_config.revision,
) )
return quant_config return quant_config
return None return None
@@ -621,7 +621,9 @@ def is_vllm_tensorized(tensorizer_config: "TensorizerConfig") -> bool:
def serialize_extra_artifacts( def serialize_extra_artifacts(
tensorizer_args: TensorizerArgs, served_model_name: str | list[str] | None tensorizer_args: TensorizerArgs,
served_model_name: str | list[str] | None,
revision: str | None = None,
) -> None: ) -> None:
if not isinstance(served_model_name, str): if not isinstance(served_model_name, str):
raise ValueError( raise ValueError(
@@ -632,6 +634,7 @@ def serialize_extra_artifacts(
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
hf_api().snapshot_download( hf_api().snapshot_download(
served_model_name, served_model_name,
revision=revision,
local_dir=tmpdir, local_dir=tmpdir,
ignore_patterns=[ ignore_patterns=[
"*.pt", "*.pt",
@@ -693,7 +696,11 @@ def serialize_vllm_model(
serializer.write_module(model) serializer.write_module(model)
serializer.close() serializer.close()
serialize_extra_artifacts(tensorizer_args, model_config.served_model_name) serialize_extra_artifacts(
tensorizer_args,
model_config.served_model_name,
revision=model_config.revision,
)
logger.info("Successfully serialized model to %s", str(output_file)) logger.info("Successfully serialized model to %s", str(output_file))
return model return model
+7 -2
View File
@@ -519,12 +519,17 @@ class FunAudioChatProcessingInfo(BaseProcessingInfo):
@cached_property @cached_property
def feature_extractor(self) -> WhisperFeatureExtractor: def feature_extractor(self) -> WhisperFeatureExtractor:
return WhisperFeatureExtractor.from_pretrained(self.model_id) return WhisperFeatureExtractor.from_pretrained(
self.model_id,
revision=self.ctx.model_config.revision,
)
@cached_property @cached_property
def speech_tokenizer(self) -> TokenizersBackend: def speech_tokenizer(self) -> TokenizersBackend:
return TokenizersBackend.from_pretrained( return TokenizersBackend.from_pretrained(
self.model_id, subfolder="speech_tokenizer" self.model_id,
subfolder="speech_tokenizer",
revision=self.ctx.model_config.tokenizer_revision,
) )
def get_feature_extractor(self) -> WhisperFeatureExtractor: def get_feature_extractor(self) -> WhisperFeatureExtractor:
+13 -2
View File
@@ -210,7 +210,7 @@ def _build_mixed_prompt_embeds(
return full_embeds, is_token_ids.tolist() return full_embeds, is_token_ids.tolist()
_PROCESSOR_CHAT_TEMPLATES = dict[tuple[str, bool], str | None]() _PROCESSOR_CHAT_TEMPLATES = dict[tuple[str, str | None, str | None, bool], str | None]()
""" """
Used in `_try_get_processor_chat_template` to avoid calling Used in `_try_get_processor_chat_template` to avoid calling
`cached_get_processor` again if the processor fails to be loaded. `cached_get_processor` again if the processor fails to be loaded.
@@ -222,9 +222,16 @@ This is needed because `lru_cache` does not cache when an exception happens.
def _try_get_processor_chat_template( def _try_get_processor_chat_template(
tokenizer: HfTokenizer, tokenizer: HfTokenizer,
*, *,
revision: str | None,
code_revision: str | None,
trust_remote_code: bool, trust_remote_code: bool,
) -> str | None: ) -> str | None:
cache_key = (tokenizer.name_or_path, trust_remote_code) cache_key = (
tokenizer.name_or_path,
revision,
code_revision,
trust_remote_code,
)
if cache_key in _PROCESSOR_CHAT_TEMPLATES: if cache_key in _PROCESSOR_CHAT_TEMPLATES:
return _PROCESSOR_CHAT_TEMPLATES[cache_key] return _PROCESSOR_CHAT_TEMPLATES[cache_key]
@@ -234,6 +241,8 @@ def _try_get_processor_chat_template(
processor = cached_get_processor( processor = cached_get_processor(
tokenizer.name_or_path, tokenizer.name_or_path,
processor_cls=(PythonBackend, TokenizersBackend, ProcessorMixin), processor_cls=(PythonBackend, TokenizersBackend, ProcessorMixin),
revision=revision,
code_revision=code_revision,
trust_remote_code=trust_remote_code, trust_remote_code=trust_remote_code,
) )
if ( if (
@@ -271,6 +280,8 @@ def resolve_chat_template(
if tools is None: if tools is None:
chat_template = _try_get_processor_chat_template( chat_template = _try_get_processor_chat_template(
tokenizer, tokenizer,
revision=model_config.revision,
code_revision=model_config.code_revision,
trust_remote_code=model_config.trust_remote_code, trust_remote_code=model_config.trust_remote_code,
) )
if chat_template is not None: if chat_template is not None:
+2
View File
@@ -1071,6 +1071,7 @@ def try_get_generation_config(
model: str, model: str,
trust_remote_code: bool, trust_remote_code: bool,
revision: str | None = None, revision: str | None = None,
code_revision: str | None = None,
config_format: str | ConfigFormat = "auto", config_format: str | ConfigFormat = "auto",
hf_token: bool | str | None = None, hf_token: bool | str | None = None,
) -> GenerationConfig | None: ) -> GenerationConfig | None:
@@ -1086,6 +1087,7 @@ def try_get_generation_config(
model, model,
trust_remote_code=trust_remote_code, trust_remote_code=trust_remote_code,
revision=revision, revision=revision,
code_revision=code_revision,
config_format=config_format, config_format=config_format,
token=hf_token, token=hf_token,
) )
@@ -24,9 +24,15 @@ logger = init_logger(__name__)
class ModelArchConfigConvertorBase: class ModelArchConfigConvertorBase:
def __init__(self, hf_config: PretrainedConfig, hf_text_config: PretrainedConfig): def __init__(
self,
hf_config: PretrainedConfig,
hf_text_config: PretrainedConfig,
revision: str | None = None,
):
self.hf_config = hf_config self.hf_config = hf_config
self.hf_text_config = hf_text_config self.hf_text_config = hf_text_config
self.revision = revision
def get_per_layer_hf_configs( def get_per_layer_hf_configs(
self, self,
@@ -122,7 +128,7 @@ class ModelArchConfigConvertorBase:
model_path = self.hf_config.name_or_path model_path = self.hf_config.name_or_path
if not model_path: if not model_path:
return qk_rope_head_dim return qk_rope_head_dim
raw = get_hf_file_to_dict("config.json", model_path) raw = get_hf_file_to_dict("config.json", model_path, self.revision)
if raw and "qk_rope_head_dim" in raw: if raw and "qk_rope_head_dim" in raw:
correct = raw["qk_rope_head_dim"] correct = raw["qk_rope_head_dim"]
if correct != qk_rope_head_dim: if correct != qk_rope_head_dim:
@@ -595,16 +601,26 @@ def _strip_mimo_v2_attention_chunk_size(
class MimoV2ModelArchConfigConvertor(ModelArchConfigConvertorBase): class MimoV2ModelArchConfigConvertor(ModelArchConfigConvertorBase):
def __init__(self, hf_config: PretrainedConfig, hf_text_config: PretrainedConfig): def __init__(
self,
hf_config: PretrainedConfig,
hf_text_config: PretrainedConfig,
revision: str | None = None,
):
if getattr(hf_config, "vision_config", None): if getattr(hf_config, "vision_config", None):
hf_config.architectures = ["MiMoV2OmniForCausalLM"] hf_config.architectures = ["MiMoV2OmniForCausalLM"]
super().__init__(hf_config, hf_text_config) super().__init__(hf_config, hf_text_config, revision)
_strip_mimo_v2_attention_chunk_size(hf_config, hf_text_config) _strip_mimo_v2_attention_chunk_size(hf_config, hf_text_config)
class MimoV2MTPModelArchConfigConvertor(ModelArchConfigConvertorBase): class MimoV2MTPModelArchConfigConvertor(ModelArchConfigConvertorBase):
def __init__(self, hf_config: PretrainedConfig, hf_text_config: PretrainedConfig): def __init__(
super().__init__(hf_config, hf_text_config) self,
hf_config: PretrainedConfig,
hf_text_config: PretrainedConfig,
revision: str | None = None,
):
super().__init__(hf_config, hf_text_config, revision)
_strip_mimo_v2_attention_chunk_size(hf_config, hf_text_config) _strip_mimo_v2_attention_chunk_size(hf_config, hf_text_config)
def get_num_hidden_layers(self) -> int: def get_num_hidden_layers(self) -> int: