[Bugfix] Resolve the Hub revision once per repo (#56092)
Signed-off-by: Wauplin <lucainp@gmail.com> Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Lucain committed
Sep 24, 2026 at 17:48 UTC
344fcc252ab15541fc50250ebdfcf691e92dd6e6
5 files changed
+32
-22
tests/test_config.py
+2
-9
@@ -3867,19 +3867,12 @@ def test_load_config_rejects_non_string_load_format(bad_load_format):
3867
LoadConfig(load_format=bad_load_format)
3868
3869
3870
-# A real Qwen3-0.6B model revision that is used in the tests below.
3870
+# A real Qwen3-0.6B model revision that is used in the test below.
3871
REVISION = "c1899de289a04d12100db370d81485cdf75e47ca"
3872
3873
3874
@patch("vllm.config.model.resolve_revision", return_value=ResolvedRevision(REVISION))
3875
-def test_revision_not_resolved_when_weights_differ_from_model(mock_resolve):
3876
- model_weights = "unsloth/Qwen3-0.6B-GGUF:Q8_0"
3877
- config = ModelConfig("Qwen/Qwen3-0.6B", model_weights=model_weights)
3878
- assert config.revision is None
3879
-
3880
-
3881
-@patch("vllm.config.model.resolve_revision", return_value=ResolvedRevision(REVISION))
3882
-def test_revision_resolved_when_weights_match_model(mock_resolve):
3875
+def test_revision_resolved_for_model(mock_resolve):
3876
model = "Qwen/Qwen3-0.6B"
3877
config = ModelConfig(model)
3878
assert isinstance(config.revision, ResolvedRevision)
tests/transformers_utils/test_config.py
+1
@@ -208,6 +208,7 @@ def test_model_config_generation_fallback_forwards_code_revision():
208
model="org/model",
209
trust_remote_code=True,
210
revision="model-pin",
211
+ _hf_config_revision=None,
212
code_revision="code-pin",
213
config_format="auto",
214
hf_token=None,
vllm/config/model.py
+18
-13
@@ -589,22 +589,27 @@ class ModelConfig:
589
self.maybe_pull_model_tokenizer_for_runai(self.model, self.tokenizer)
590
591
# If loading model/tokenizer from HF Hub, resolve the revision once
592
- # to prevent resolving it multiple times downstream.
593
- # If the weights come from a different repo, we cannot eagerly resolve revision
594
- weights_from_model = not self.model_weights or self.model_weights == self.model
595
- # If the config comes from a different repo, we cannot eagerly resolve revision
596
- config_from_model = not self.hf_config_path or self.hf_config_path == self.model
597
- can_resolve_model_revision = config_from_model and weights_from_model
598
- if can_resolve_model_revision:
599
- self.revision = resolve_revision(
600
- self.model,
592
+ # to prevent resolving it multiple times downstream. A resolved revision
593
+ # only pins the repo it was resolved for, so each repo needs its own call.
594
+ self.revision = resolve_revision(
595
+ self.model,
596
+ self.revision,
597
+ self.hf_token,
598
+ )
599
+
600
+ # The config can live in another repo, which `self.revision` does not pin.
601
+ # It stays `None` if the config comes from `self.model`, so that call sites
602
+ # fall back to `self.revision` the same way they fall back to `self.model`.
603
+ self._hf_config_revision = None
604
+ if self.hf_config_path and self.hf_config_path != self.model:
605
+ self._hf_config_revision = resolve_revision(
606
+ self.hf_config_path,
607
self.revision,
608
self.hf_token,
609
)
610
611
if (
606
- can_resolve_model_revision
607
- and self.tokenizer == self.model
612
+ self.tokenizer == self.model
613
and self.tokenizer_revision == requested_revision
614
):
615
self.tokenizer_revision = self.revision
@@ -632,7 +637,7 @@ class ModelConfig:
637
hf_config = get_config(
638
self.hf_config_path or self.model,
639
self.trust_remote_code,
635
- self.revision,
640
+ self._hf_config_revision or self.revision,
641
self.code_revision,
642
self.config_format,
643
hf_overrides_kw=hf_overrides_kw,
@@ -1779,7 +1784,7 @@ class ModelConfig:
1784
config = try_get_generation_config(
1785
self.hf_config_path or self.model,
1786
trust_remote_code=self.trust_remote_code,
1782
- revision=self.revision,
1787
+ revision=self._hf_config_revision or self.revision,
1788
code_revision=self.code_revision,
1789
config_format=self.config_format,
1790
hf_token=self.hf_token,
vllm/model_executor/model_loader/runai_streamer_loader.py
+6
@@ -15,6 +15,7 @@ from vllm.model_executor.model_loader.weight_utils import (
15
download_weights_from_hf,
16
runai_safetensors_weights_iterator,
17
)
18
+from vllm.transformers_utils.repo_utils import resolve_revision
19
from vllm.transformers_utils.runai_utils import is_runai_obj_uri, list_safetensors
20
21
@@ -87,6 +88,11 @@ class RunaiModelStreamerLoader(BaseModelLoader):
88
safetensors_pattern = "*.safetensors"
89
index_file = SAFE_WEIGHTS_INDEX_NAME
90
91
+ if not is_local and not is_object_storage_path:
92
+ # `model_weights` can point to another repo than the one `revision` was
93
+ # resolved for, which does not pin this one.
94
+ revision = resolve_revision(model_name_or_path, revision)
95
+
96
hf_folder = (
97
model_name_or_path
98
if (is_local or is_object_storage_path)
vllm/transformers_utils/repo_utils.py
+5
@@ -63,6 +63,11 @@ def resolve_revision(
63
requested revision that also carries the commit hash, so downstream error
64
messages stay readable and offline loads reuse the cached `refs/` entry.
65
66
+ A commit hash only means something for the repo it was resolved against, so
67
+ a repo that is not `repo_id` needs its own call. Passing a revision resolved
68
+ for another repo back in is safe: the revision initially requested is
69
+ resolved again, against `repo_id` this time.
70
+
71
Returns:
72
The resolved revision, or `revision` unchanged if it cannot be resolved
73
(local path, ModelScope, or any Hub error).