diff --git a/tests/test_config.py b/tests/test_config.py index dd1d93c87a..c391216d9a 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -3867,19 +3867,12 @@ def test_load_config_rejects_non_string_load_format(bad_load_format): LoadConfig(load_format=bad_load_format) -# A real Qwen3-0.6B model revision that is used in the tests below. +# A real Qwen3-0.6B model revision that is used in the test below. REVISION = "c1899de289a04d12100db370d81485cdf75e47ca" @patch("vllm.config.model.resolve_revision", return_value=ResolvedRevision(REVISION)) -def test_revision_not_resolved_when_weights_differ_from_model(mock_resolve): - model_weights = "unsloth/Qwen3-0.6B-GGUF:Q8_0" - config = ModelConfig("Qwen/Qwen3-0.6B", model_weights=model_weights) - assert config.revision is None - - -@patch("vllm.config.model.resolve_revision", return_value=ResolvedRevision(REVISION)) -def test_revision_resolved_when_weights_match_model(mock_resolve): +def test_revision_resolved_for_model(mock_resolve): model = "Qwen/Qwen3-0.6B" config = ModelConfig(model) assert isinstance(config.revision, ResolvedRevision) diff --git a/tests/transformers_utils/test_config.py b/tests/transformers_utils/test_config.py index b6ab0ea917..4dee8fab60 100644 --- a/tests/transformers_utils/test_config.py +++ b/tests/transformers_utils/test_config.py @@ -208,6 +208,7 @@ def test_model_config_generation_fallback_forwards_code_revision(): model="org/model", trust_remote_code=True, revision="model-pin", + _hf_config_revision=None, code_revision="code-pin", config_format="auto", hf_token=None, diff --git a/vllm/config/model.py b/vllm/config/model.py index 82b5efc8e4..6196749540 100644 --- a/vllm/config/model.py +++ b/vllm/config/model.py @@ -589,22 +589,27 @@ class ModelConfig: self.maybe_pull_model_tokenizer_for_runai(self.model, self.tokenizer) # If loading model/tokenizer from HF Hub, resolve the revision once - # to prevent resolving it multiple times downstream. - # If the weights come from a different repo, we cannot eagerly resolve revision - weights_from_model = not self.model_weights or self.model_weights == self.model - # If the config comes from a different repo, we cannot eagerly resolve revision - config_from_model = not self.hf_config_path or self.hf_config_path == self.model - can_resolve_model_revision = config_from_model and weights_from_model - if can_resolve_model_revision: - self.revision = resolve_revision( - self.model, + # to prevent resolving it multiple times downstream. A resolved revision + # only pins the repo it was resolved for, so each repo needs its own call. + self.revision = resolve_revision( + self.model, + self.revision, + self.hf_token, + ) + + # The config can live in another repo, which `self.revision` does not pin. + # It stays `None` if the config comes from `self.model`, so that call sites + # fall back to `self.revision` the same way they fall back to `self.model`. + self._hf_config_revision = None + if self.hf_config_path and self.hf_config_path != self.model: + self._hf_config_revision = resolve_revision( + self.hf_config_path, self.revision, self.hf_token, ) if ( - can_resolve_model_revision - and self.tokenizer == self.model + self.tokenizer == self.model and self.tokenizer_revision == requested_revision ): self.tokenizer_revision = self.revision @@ -632,7 +637,7 @@ class ModelConfig: hf_config = get_config( self.hf_config_path or self.model, self.trust_remote_code, - self.revision, + self._hf_config_revision or self.revision, self.code_revision, self.config_format, hf_overrides_kw=hf_overrides_kw, @@ -1779,7 +1784,7 @@ class ModelConfig: config = try_get_generation_config( self.hf_config_path or self.model, trust_remote_code=self.trust_remote_code, - revision=self.revision, + revision=self._hf_config_revision or self.revision, code_revision=self.code_revision, config_format=self.config_format, hf_token=self.hf_token, diff --git a/vllm/model_executor/model_loader/runai_streamer_loader.py b/vllm/model_executor/model_loader/runai_streamer_loader.py index e07330ae57..2f18a2e3ac 100644 --- a/vllm/model_executor/model_loader/runai_streamer_loader.py +++ b/vllm/model_executor/model_loader/runai_streamer_loader.py @@ -15,6 +15,7 @@ from vllm.model_executor.model_loader.weight_utils import ( download_weights_from_hf, runai_safetensors_weights_iterator, ) +from vllm.transformers_utils.repo_utils import resolve_revision from vllm.transformers_utils.runai_utils import is_runai_obj_uri, list_safetensors @@ -87,6 +88,11 @@ class RunaiModelStreamerLoader(BaseModelLoader): safetensors_pattern = "*.safetensors" index_file = SAFE_WEIGHTS_INDEX_NAME + if not is_local and not is_object_storage_path: + # `model_weights` can point to another repo than the one `revision` was + # resolved for, which does not pin this one. + revision = resolve_revision(model_name_or_path, revision) + hf_folder = ( model_name_or_path if (is_local or is_object_storage_path) diff --git a/vllm/transformers_utils/repo_utils.py b/vllm/transformers_utils/repo_utils.py index 6b9ff9ec5e..b6a23b007a 100644 --- a/vllm/transformers_utils/repo_utils.py +++ b/vllm/transformers_utils/repo_utils.py @@ -63,6 +63,11 @@ def resolve_revision( requested revision that also carries the commit hash, so downstream error messages stay readable and offline loads reuse the cached `refs/` entry. + A commit hash only means something for the repo it was resolved against, so + a repo that is not `repo_id` needs its own call. Passing a revision resolved + for another repo back in is safe: the revision initially requested is + resolved again, against `repo_id` this time. + Returns: The resolved revision, or `revision` unchanged if it cannot be resolved (local path, ModelScope, or any Hub error).