A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 2, 18:15 UTC
[BugFix] HFValidationError with cloud storage URIs when HF_HUB_OFFLINE=1 (#39155)
Signed-off-by: Injae Ryou <injaeryou@gmail.com>
Injae Ryou committed
May 28, 2026 at 00:53 UTC
165460941f5e82b8ddb344515163b43e537f9c3f
4 files changed
+63
-10
tests/engine/test_arg_utils.py
+28
@@ -613,3 +613,31 @@ def test_ir_op_priority():
613
)
614
def test_expand_json_human_readable_numbers(input_json, expected_json):
615
assert _expand_json_human_readable_numbers(input_json) == expected_json
616
+
617
+
618
+@pytest.mark.parametrize(
619
+ "uri",
620
+ ["s3://bucket/model", "gs://bucket/model", "az://container/model"],
621
+)
622
+def test_cloud_storage_uri_skips_get_model_path(uri, monkeypatch):
623
+ """Cloud storage URIs should not be passed to get_model_path()
624
+ when HF_HUB_OFFLINE=1, as they are not valid HF repo IDs."""
625
+ import huggingface_hub
626
+
627
+ monkeypatch.setattr(huggingface_hub.constants, "HF_HUB_OFFLINE", True)
628
+
629
+ args = EngineArgs(model=uri)
630
+ # model should remain the original cloud URI, not raise
631
+ assert args.model == uri
632
+
633
+
634
+def test_cloud_storage_tokenizer_skips_get_model_path(monkeypatch):
635
+ """Cloud storage tokenizer URI should not be passed to
636
+ get_model_path() when HF_HUB_OFFLINE=1."""
637
+ import huggingface_hub
638
+
639
+ monkeypatch.setattr(huggingface_hub.constants, "HF_HUB_OFFLINE", True)
640
+
641
+ args = EngineArgs(model="s3://bucket/model", tokenizer="s3://bucket/tokenizer")
642
+ assert args.model == "s3://bucket/model"
643
+ assert args.tokenizer == "s3://bucket/tokenizer"
tests/test_config.py
+20
@@ -754,6 +754,26 @@ def test_s3_url_different_models_create_different_directories(mock_pull_files):
754
assert os.path.exists(config2.tokenizer) and os.path.isdir(config2.tokenizer)
755
756
757
+@patch("vllm.transformers_utils.runai_utils.ObjectStorageModel.pull_files")
758
+def test_s3_url_different_model_and_tokenizer(mock_pull_files):
759
+ """Test that when model and tokenizer are different cloud URIs,
760
+ pull_files receives the correct URI for each."""
761
+ mock_pull_files.return_value = None
762
+
763
+ model_url = "s3://bucket/model/"
764
+ tokenizer_url = "s3://bucket/tokenizer/"
765
+
766
+ config = MockConfig(model=model_url, tokenizer=tokenizer_url)
767
+ ModelConfig.maybe_pull_model_tokenizer_for_runai(config, model_url, tokenizer_url)
768
+
769
+ # pull_files should be called twice: once for model, once for tokenizer
770
+ assert mock_pull_files.call_count == 2
771
+ # First call: model URI with allow_pattern
772
+ assert mock_pull_files.call_args_list[0][0][0] == model_url
773
+ # Second call: tokenizer URI with ignore_pattern
774
+ assert mock_pull_files.call_args_list[1][0][0] == tokenizer_url
775
+
776
+
777
@pytest.mark.parametrize(
778
("model_id", "expected_attn_type", "expected_result", "reason"),
779
[
vllm/config/model.py
+1
-1
@@ -888,7 +888,7 @@ class ModelConfig:
888
if is_runai_obj_uri(tokenizer):
889
object_storage_tokenizer = ObjectStorageModel(url=tokenizer)
890
object_storage_tokenizer.pull_files(
891
- model,
891
+ tokenizer,
892
ignore_pattern=["*.pt", "*.safetensors", "*.bin", "*.tensors", "*.pth"],
893
)
894
self.tokenizer = object_storage_tokenizer.dir
vllm/engine/arg_utils.py
+14
-9
@@ -747,15 +747,20 @@ class EngineArgs:
747
load_general_plugins()
748
# when use hf offline,replace model and tokenizer id to local model path
749
if huggingface_hub.constants.HF_HUB_OFFLINE:
750
- model_id = self.model
751
- self.model = get_model_path(self.model, self.revision)
752
- if model_id is not self.model:
753
- logger.info(
754
- "HF_HUB_OFFLINE is True, replace model_id [%s] to model_path [%s]",
755
- model_id,
756
- self.model,
757
- )
758
- if self.tokenizer is not None:
750
+ # Skip cloud storage URIs (s3://, gs://, az://) — they are not
751
+ # HF repo IDs and will be resolved later by
752
+ # ModelConfig.maybe_pull_model_tokenizer_for_runai().
753
+ if not is_cloud_storage(self.model):
754
+ model_id = self.model
755
+ self.model = get_model_path(self.model, self.revision)
756
+ if model_id is not self.model:
757
+ logger.info(
758
+ "HF_HUB_OFFLINE is True, replace model_id "
759
+ "[%s] to model_path [%s]",
760
+ model_id,
761
+ self.model,
762
+ )
763
+ if self.tokenizer is not None and not is_cloud_storage(self.tokenizer):
764
tokenizer_id = self.tokenizer
765
self.tokenizer = get_model_path(self.tokenizer, self.tokenizer_revision)
766
if tokenizer_id is not self.tokenizer: