Prhub

#39155 [BugFix] HFValidationError with cloud storage URIs when HF_HUB_OFFLINE=1

原始 PR 作者 sts07142 合并时间 2026-05-27 23:53 文件变更 4 提交数 6 评论 3 代码增减 +63 / -10

执行摘要

修复 HF_HUB_OFFLINE=1 时云存储 URI 导致崩溃的 bug

来自 issue #39112:用户设置 HF_HUB_OFFLINE=1 并使用 s3:// 路径后立即收到 HFValidationError,因为 get_model_path 将云 URI 当作 HF repo ID 验证。用户本意是避免与 HF Hub 通信,云存储应由 runai_model_streamer 处理。此外发现当 model 与 tokenizer 为不同云 URI 时 pull_files 被传入了错误的 URI。

此 PR 值得精读,特别是如果有云部署或离线环境需求。它展示了如何通过早期判断避免 HuggingFace Hub 的输入验证,以及如何修复易被忽视的 URI 传递错误。设计上,它选择在 EngineArgs 层面做防御性检查而非修改 get_model_path,这是一个合理且侵入性小的方案。

讨论亮点

审核人 gshtras 要求更新分支以测试最新代码库,作者执行了多次 merge main 后满足要求,最终 gshtras 批准。无其他实质性技术讨论。

实现拆解

  1. 跳过云存储 URI(vllm/engine/arg_utils.py::EngineArgs.post_init:在 if huggingface_hub.constants.HF_HUB_OFFLINE: 块内,对 self.modelself.tokenizer 分别增加 is_cloud_storage() 检查。若是云 URI 则跳过 get_model_path 调用,保留原值等待后续 ModelConfig.maybe_pull_model_tokenizer_for_runai 处理。
  2. 修复 tokenizer URI 传递错误(vllm/config/model.py::ModelConfig.maybe_pull_model_tokenizer_for_runai):将 object_storage_tokenizer.pull_files(model, ...) 改为 object_storage_tokenizer.pull_files(tokenizer, ...),确保使用正确的 tokenizer URI。
  3. 新增覆盖测试
    • tests/engine/test_arg_utils.pytest_cloud_storage_uri_skips_get_model_path 参数化测试 s3/gs/az 三种 URI 在 HF_HUB_OFFLINE=1 时不修改;test_cloud_storage_tokenizer_skips_get_model_path 验证 model 和 tokenizer 同时为云 URI 时均不被转换。
    • tests/test_config.pytest_s3_url_different_model_and_tokenizer 使用 mock 验证当 model 与 tokenizer URI 不同时,pull_files 被调用两次且参数正确。
文件 模块 状态 重要度
vllm/engine/arg_utils.py 引擎参数 modified 6.68
vllm/config/model.py 模型配置 modified 4.53
tests/engine/test_arg_utils.py 引擎测试 modified 6.24
tests/test_config.py 配置测试 modified 5.29

关键符号

EngineArgs.__post_init__ ModelConfig.maybe_pull_model_tokenizer_for_runai test_cloud_storage_uri_skips_get_model_path test_cloud_storage_tokenizer_skips_get_model_path test_s3_url_different_model_and_tokenizer

关键源码片段

vllm/engine/arg_utils.py core-logic

核心修复:在 EngineArgs.__post_init__ 中添加 is_cloud_storage 检查,避免云 URI 被传给 get_model_path 导致崩溃。

# vllm/engine/arg_utils.py (EngineArgs.__post_init__)
        if huggingface_hub.constants.HF_HUB_OFFLINE:
            # Skip cloud storage URIs (s3://, gs://, az://) — they are not
            # HF repo IDs and will be resolved later by
            # ModelConfig.maybe_pull_model_tokenizer_for_runai().
            if not is_cloud_storage(self.model):
                model_id = self.model
                self.model = get_model_path(self.model, self.revision)
                if model_id is not self.model:
                    logger.info(
                        "HF_HUB_OFFLINE is True, replace model_id "
                        "[%s] to model_path [%s]",
                        model_id,
                        self.model,
                    )
            if self.tokenizer is not None and not is_cloud_storage(self.tokenizer):
                tokenizer_id = self.tokenizer
                self.tokenizer = get_model_path(self.tokenizer, self.tokenizer_revision)
                if tokenizer_id is not self.tokenizer:
                    logger.info(
                        "HF_HUB_OFFLINE is True, replace tokenizer_id [%s] "
                        "to tokenizer_path [%s]",
                        tokenizer_id,
                        self.tokenizer,
                    )
vllm/config/model.py data-contract

次要修复:将 tokenizer pull_files 错误传递的 model URI 修正为 tokenizer URI。

# vllm/config/model.py (ModelConfig.maybe_pull_model_tokenizer_for_runai)
        if is_runai_obj_uri(tokenizer):
            object_storage_tokenizer = ObjectStorageModel(url=tokenizer)
            # FIX: pass tokenizer URI instead of model URI
            object_storage_tokenizer.pull_files(
                tokenizer, # was incorrectly 'model' before
                ignore_pattern=["*.pt", "*.safetensors", "*.bin", "*.tensors", "*.pth"],
            )
            self.tokenizer = object_storage_tokenizer.dir

评论区精华

要求更新分支以测试最新代码 other

审核人 gshtras 留言 'Looks good. Could you please update the branch to test against the up to date codebase'

结论:作者连续执行多次 merge main 操作后,分支已更新,最终 gshtras 批准。 · 已解决

风险与影响

  1. 云 URI 检测覆盖不全is_cloud_storage 若未识别所有可能的云存储格式(如 hdfs://, oss://),仍可能导致类似崩溃。
  2. 环境变量依赖:行为依赖于 HF_HUB_OFFLINE,若用户未设置该变量则云 URI 仍会走原有路径(但实际场景中云 URI 通常与 OFFLINE 搭配)。
  3. 兼容性:修改的是 __post_init__,这是引擎参数初始化的关键路径,影响所有引擎启动;虽然改动很小,但任何疏忽都可能导致启动失败。
  4. 回归风险:新增的测试覆盖了主要场景,但未覆盖混合使用(如 model 为云 URI 而 tokenizer 为 HF 路径)的情况。

用户影响:使用云存储(S3/GCS/Azure Blob)且设置 HF_HUB_OFFLINE=1 的用户可正常启动,不再崩溃;修复了不同 tokenizer URI 时文件下载错误的隐蔽 bug。系统影响:对非云存储用户无行为变化;对云存储用户,模型加载路径保持不变,由下游的 maybe_pull_model_tokenizer_for_runai 处理。团队影响:无,维持向后兼容。

核心路径变更 云存储格式兼容性 环境变量依赖

关联 Issue

#39112 [Bug]: HFValidationError when loading model from cloud storage (s3://) with `HF_HUB_OFFLINE=1`

完整报告

参与讨论