Resolve revision to commit_hash once per model load, via huggingface_hub's `resolve_revision` (#49990)

Signed-off-by: Wauplin <lucainp@gmail.com> Signed-off-by: Lucain <lucainp@gmail.com> Signed-off-by: Lucain Pouget <lucain@huggingface.co> Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> Co-authored-by: OpenAI Codex <codex@openai.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Harry Mellor <19981378+hmellor@users.noreply.github.com>

Lucain committed Aug 5, 2026 at 15:49 UTC 397094da1768c7a6f29dfa4f70079d793ac747df
9 files changed +84 -6
requirements/common.txt
+1
@@ -8,6 +8,7 @@ tqdm
8 blake3
9 py-cpuinfo
10 transformers >= 5.5.3
11 +huggingface_hub >= 1.26.0
12 tokenizers >= 0.21.1 # Required for fast incremental detokenization.
13 safetensors >= 0.6.2 # MXFP4/MXFP6 dtype support (F8_E8M0, F4) added in 0.6.0: https://github.com/huggingface/safetensors/pull/611
14 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
requirements/test/cpu.txt
+2 -1
@@ -341,8 +341,9 @@ httpx==0.27.2
341 # schemathesis
342 httpx-sse==0.4.3
343 # via mcp
344 -huggingface-hub==1.22.0
344 +huggingface-hub==1.26.0
345 # via
346 + # -r requirements/test/../common.txt
347 # accelerate
348 # datasets
349 # evaluate
requirements/test/cuda.txt
+3 -1
@@ -360,8 +360,10 @@ httpx==0.27.2
360 # schemathesis
361 httpx-sse==0.4.3
362 # via mcp
363 -huggingface-hub==1.22.0
363 +huggingface-hub==1.26.0
364 # via
365 + # -c requirements/common.txt
366 + # -r requirements/test/../common.txt
367 # accelerate
368 # datasets
369 # evaluate
requirements/test/rocm.txt
+3 -1
@@ -351,8 +351,10 @@ httpx==0.27.2
351 # schemathesis
352 httpx-sse==0.4.3
353 # via mcp
354 -huggingface-hub==1.22.0
354 +huggingface-hub==1.26.0
355 # via
356 + # -c requirements/common.txt
357 + # -r requirements/test/../common.txt
358 # accelerate
359 # datasets
360 # evaluate
requirements/test/xpu.txt
+3 -1
@@ -224,8 +224,10 @@ httpx==0.28.1
224 # schemathesis
225 httpx-sse==0.4.3
226 # via mcp
227 -huggingface-hub==1.22.0
227 +huggingface-hub==1.26.0
228 # via
229 + # -c requirements/common.txt
230 + # -r requirements/test/../common.txt
231 # accelerate
232 # datasets
233 # evaluate
vllm/config/model.py
+27
@@ -47,6 +47,7 @@ from vllm.transformers_utils.model_arch_config_convertor import (
47 MODEL_ARCH_CONFIG_CONVERTORS,
48 ModelArchConfigConvertorBase,
49 )
50 +from vllm.transformers_utils.repo_utils import resolve_revision
51 from vllm.transformers_utils.runai_utils import ObjectStorageModel, is_runai_obj_uri
52 from vllm.transformers_utils.utils import maybe_model_redirect
53 from vllm.utils.import_utils import LazyLoader
@@ -529,6 +530,7 @@ class ModelConfig:
530 self.served_model_name = get_served_model_name(
531 self.model, self.served_model_name
532 )
533 + requested_revision = self.revision
534 self.model = maybe_model_redirect(self.model)
535 # The tokenizer is consistent with the model by default.
536 if self.tokenizer is None:
@@ -558,6 +560,31 @@ class ModelConfig:
560
561 self.maybe_pull_model_tokenizer_for_runai(self.model, self.tokenizer)
562
563 + # If loading model/tokenizer from HF Hub, resolve the revision once
564 + # to prevent resolving it multiple times downstream.
565 + can_resolve_model_revision = (
566 + self.hf_config_path is None or self.hf_config_path == self.model
567 + )
568 + if can_resolve_model_revision:
569 + self.revision = resolve_revision(
570 + self.model,
571 + self.revision,
572 + self.hf_token,
573 + )
574 +
575 + if (
576 + can_resolve_model_revision
577 + and self.tokenizer == self.model
578 + and self.tokenizer_revision == requested_revision
579 + ):
580 + self.tokenizer_revision = self.revision
581 + else:
582 + self.tokenizer_revision = resolve_revision(
583 + self.tokenizer,
584 + self.tokenizer_revision,
585 + self.hf_token,
586 + )
587 +
588 if self.override_attention_dtype is not None and not current_platform.is_rocm():
589 warnings.warn(
590 "override-attention-dtype is set but not using ROCm platform",
vllm/model_executor/models/ultravox.py
+4 -2
@@ -544,6 +544,8 @@ class UltravoxModel(nn.Module, SupportsMultiModal, SupportsPP, SupportsLoRA):
544 [self.config.audio_token_index],
545 )
546
547 + # The towers live in their own Hub repos, so the revision of the
548 + # Ultravox repo does not apply to them.
549 self.secondary_weights = []
550 if config.audio_model_id is not None:
551 # this prefix is not for initialization, but for loading weights
@@ -551,7 +553,7 @@ class UltravoxModel(nn.Module, SupportsMultiModal, SupportsPP, SupportsLoRA):
553 self.secondary_weights.append(
554 DefaultModelLoader.Source(
555 model_or_path=config.audio_model_id,
554 - revision=vllm_config.model_config.revision,
556 + revision=None,
557 prefix="audio_tower.",
558 )
559 )
@@ -561,7 +563,7 @@ class UltravoxModel(nn.Module, SupportsMultiModal, SupportsPP, SupportsLoRA):
563 self.secondary_weights.append(
564 DefaultModelLoader.Source(
565 model_or_path=config.text_model_id,
564 - revision=vllm_config.model_config.revision,
566 + revision=None,
567 prefix="language_model.",
568 )
569 )
vllm/transformers_utils/repo_utils.py
+38
@@ -49,6 +49,44 @@ def hf_fs() -> "huggingface_hub.HfFileSystem":
49 )
50
51
52 +def resolve_revision(
53 + repo_id: str,
54 + revision: str | None = None,
55 + token: str | bool | None = None,
56 +) -> str | None:
57 + """Best-effort attempt to resolve a Hub revision to a commit hash.
58 +
59 + Resolving the commit hash once prevents repeated HTTP calls downstream and
60 + avoids races if the branch moves while the model is loading.
61 +
62 + `HfApi.resolve_revision` returns a `ResolvedRevision`: a `str` equal to the
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 + Returns:
67 + The resolved revision, or `revision` unchanged if it cannot be resolved
68 + (local path, ModelScope, or any Hub error).
69 + """
70 + if Path(repo_id).exists() or envs.VLLM_USE_MODELSCOPE:
71 + return revision
72 +
73 + try:
74 + return hf_api().resolve_revision(
75 + repo_id,
76 + revision=revision,
77 + local_files_only=huggingface_hub.constants.HF_HUB_OFFLINE,
78 + token=token,
79 + )
80 + except Exception:
81 + logger.debug(
82 + "Failed to resolve revision for %s; falling back to %s.",
83 + repo_id,
84 + revision,
85 + exc_info=True,
86 + )
87 + return revision
88 +
89 +
90 _R = TypeVar("_R")
91
92
vllm/v1/worker/gpu_model_runner.py
+3
@@ -5553,7 +5553,10 @@ class GPUModelRunner(
5553 )
5554
5555 if weights_path is not None:
5556 + # The revision belongs to the model we are reloading away from,
5557 + # so it must not be carried over to the new path.
5558 self.model_config.model = weights_path
5559 + self.model_config.revision = None
5560 weights_iterator = model_loader.get_all_weights(self.model_config, model)
5561 weights_iterator = cast(
5562 Iterable[tuple[str, torch.Tensor]], weights_iterator