Prhub

#36883 [Diffusion] Resolve indexed component weight sets

原始 PR 作者 mickqian 合并时间 2026-08-29 14:21 文件变更 5 提交数 5 评论 1 代码增减 +496 / -197

执行摘要

新增索引式组件权重集解析,重构 transformer 覆盖加载

PR body 将其定位为“shared checkpoint infrastructure for pipeline-declared components”:需要一个模型无关的组件权重集解析器,统一处理本地路径、HF repo、子文件夹、精确文件和标准 safetensors indexes,并“treat the index as shard authority,reject ambiguous unindexed variants”,同时把权重与相邻量化元数据固定到同一不可变 Hub revision,避免加载到混搭的不同 checkpoint 版本。历史 PR #22360 曾因重复加载 transformer safetensors 变体引入防御测试,本 PR 正是从解析层根治该问题。

值得精读。重点看 weights/source.py 中 index 权威解析与错误类型区分的设计,以及 transformer_load_utils.py 如何从自维护下载逻辑收敛到共享解析器;这套解析器可视为给其他组件加权重覆盖的现成模板。

讨论亮点

PR 没有 review 评论,唯一讨论是作者 mickqian 在 issue 评论中的说明:重构把 checkpoint 物化移入通用 weight-source resolver 后,测试仍 patch 了已删除的私有导入 transformer_load_utils.maybe_download_model,导致 NVIDIA 单元测试失败。修复后,Nunchaku 测试保持聚焦量化 hook 解析,本地文件 resolver 测试验证既不调用 Hub 元数据也不调用下载 API,且没有新增任何运行时行为或兼容 shim。

实现拆解

  1. 权重源层新增索引解析python/sglang/multimodal_gen/runtime/weights/source.py,+236/-13):新增 _SAFETENSORS_INDEX_SUFFIX_WEIGHT_REFERENCE_SUFFIXES 常量,使 .safetensors.index.json 也能作为显式权重引用;新增 ResolvedWeightSet(选中文件元组 + 权威 index 文件名)与 NoSafetensorsWeightsError 异常;_local_index_inventory 只按 index 的 weight_map 收集本地分片;resolve_safetensors_weight_set / materialize_weight_set 及私有辅助 _read_safetensors_index_resolve_index_shard_materialize_inventory_file 完成索引解析与物化,无索引歧义目录抛 ValueError,无 safetensors 载荷抛专有异常。

  2. transformer 覆盖加载收敛到共享解析器python/sglang/multimodal_gen/runtime/loader/transformer_load_utils.py,+54/-101):删除 _HF_SAFETENSORS_URL_RE 正则以及直接调用 hf_hub_downloadmaybe_download_modelsnapshot_download 的分支;resolve_transformer_safetensors_to_load 重构为 resolve_transformer_checkpoint_files,返回新增的 TransformerCheckpointFilessafetensors 元组 + config_path),覆盖路径全部委托给 resolve_safetensors_weight_set,同时保留 _select_single_mixed_safetensors_file 作为既有 *-mixed.safetensors 单文件兼容路径。

  3. 加载器接入python/sglang/multimodal_gen/runtime/loader/component_loaders/transformer_loader.py,+6/-2):load_customized 改用 resolve_transformer_checkpoint_files,并把 checkpoint_files.config_path 传入 TransformerQuantLoadSpec 的新字段 transformer_override_config_path;GGUF 分支保持原样(safetensors_list = [])。

  4. 测试配套test_weight_source.py 新增 8 个用例,覆盖 index 只选声明 shard、精确 index 解析相邻 shard、拒绝无索引变体、拒绝非权重 shard、缺失 shard 不算“无权重”、远端多 shard 固定单一 revision 等;test_transformer_quant.py 把 mock 目标从 transformer_load_utils.hf_hub_download 迁移到 weights.source.HfApi.model_info / hf_hub_download,并验证单文件覆盖、HF revision 固定与 mixed 兼容路径。

文件 模块 状态 重要度
python/sglang/multimodal_gen/runtime/weights/source.py 权重解析 modified 8.84
python/sglang/multimodal_gen/runtime/loader/transformer_load_utils.py 加载逻辑 modified 8.49
python/sglang/multimodal_gen/runtime/loader/component_loaders/transformer_loader.py 加载器 modified 5.27
python/sglang/multimodal_gen/test/unit/test_weight_source.py 单元测试 modified 7.03
python/sglang/multimodal_gen/test/unit/test_transformer_quant.py 单元测试 modified 6.97

关键符号

resolve_safetensors_weight_set materialize_weight_set materialize_weight_set_config _local_index_inventory _resolve_index_shard _read_safetensors_index resolve_transformer_checkpoint_files _select_single_mixed_safetensors_file materialize_weight

关键源码片段

python/sglang/multimodal_gen/runtime/weights/source.py core-logic

新增 ResolvedWeightSet、NoSafetensorsWeightsError 与 resolve_safetensors_weight_set 等索引权重集解析核心,是本次变更的主实现。

# python/sglang/multimodal_gen/runtime/weights/source.py
# 权重集解析核心:新增常量、数据结构和 index 解析辅助函数_SAFETENSORS_INDEX_SUFFIX = ".safetensors.index.json"
_WEIGHT_REFERENCE_SUFFIXES = _WEIGHT_SUFFIXES + (_SAFETENSORS_INDEX_SUFFIX,)
​
​
@dataclass(frozen=True)
class ResolvedWeightSet:
    """一次解析得到的权重集:同一 checkpoint revision 下的一组文件。    与 ResolvedWeight 不同,它承载多个 safetensors 文件(分片),
    并保留权威 index 文件名供日志与排障使用。
    """
    inventory: WeightInventory
    selected_files: tuple[str, ...]
    index_file: str | None = None
​
​
class NoSafetensorsWeightsError(FileNotFoundError):
    """源中没有 safetensors 载荷可解析为权重集。    和普通 FileNotFoundError 区分开,让上层能识别
    “没有权重”而不是“路径写错”。
    """
​
​
def _local_index_inventory(index_path: Path) -> tuple[str, ...]:
    """只列出精确的本地 index 及其声明、且物理存在的分片。    目录里可能还有别的 safetensors 变体(如 distilled/base),
    但这里严格以 index 的 weight_map 为权威,避免歧义加载。
    """
    with index_path.open(encoding="utf-8") as index_stream:
        index = json.load(index_stream)
    weight_map = index.get("weight_map") if isinstance(index, Mapping) else None
    shard_names = weight_map.values() if isinstance(weight_map, Mapping) else ()
    files = {index_path.name}
    for shard_name in shard_names:
        # 校验 shard 相对路径是否合法,避免目录穿越
        if not isinstance(shard_name, str):
            continue
        shard_name = _validate_relative_hub_path(shard_name, "index shard")
        shard_path = index_path.parent / shard_name
        if shard_path.is_file():
            files.add(shard_path.relative_to(index_path.parent).as_posix())
    return tuple(sorted(files))
python/sglang/multimodal_gen/runtime/loader/transformer_load_utils.py refactor

将 transformer 权重覆盖从自维护的 HF 下载 / 正则分支迁移到共享解析器,新增 TransformerCheckpointFiles 返回结构,删除约 100 行重复逻辑。

# python/sglang/multimodal_gen/runtime/loader/transformer_load_utils.py
# transformer 覆盖加载统一走共享权重集解析器
​
​
@dataclass(frozen=True)
class TransformerCheckpointFiles:
    """transformer 加载所需文件:safetensors 列表 + 相邻 config.json。"""
    safetensors: tuple[str, ...]
    config_path: str | None
​
​
def resolve_transformer_checkpoint_files(
    server_args: ServerArgs, component_model_path: str
) -> TransformerCheckpointFiles:
    """从基础组件路径或量化覆盖路径解析 transformer 权重文件。    有覆盖时统一委托给 resolve_safetensors_weight_set,覆盖源可以
    是本地目录、Hugging Face repo、子文件夹、精确文件或
    safetensors index;解析出的 config.json 会作为配置路径带出。
    无覆盖时回退到基础组件路径,并保留 *-mixed.safetensors
    单文件兼容选择。
    """
    quantized_path = server_args.transformer_weights_path
    if quantized_path:
        resolved = resolve_safetensors_weight_set(quantized_path)
        safetensors = tuple(materialize_weight_set(resolved))
        config_path = next(
            (path for path in resolved.inventory.files
             if path.endswith("config.json")),
            None,
        )
        return TransformerCheckpointFiles(
            safetensors=safetensors, config_path=config_path
        )
    # 基础组件路径:沿用原有探测逻辑,优先单一 mixed 导出
    selected = _select_single_mixed_safetensors_file(component_model_path)
    return TransformerCheckpointFiles(safetensors=selected, config_path=None)

评论区精华

CI 失败:测试仍 patch 已删除的 maybe_download_model 测试

作者 mickqian 在 issue 评论中说明:重构把 checkpoint 物化移入通用 weight-source resolver 后,测试仍 patch 已被删除的 transformer_load_utils.maybe_download_model 私有导入,导致 NVIDIA 单测源失败。

结论:测试改为 patch weights.source.HfApi.model_info 与 hf_hub_download,Nunchaku 测试保持专注 quant hook 解析,本地文件 resolver 测试验证不触发 Hub 元数据或下载 API。 · 已解决

风险与影响

  1. 行为变更:resolve_safetensors_weight_set 会拒绝没有 index 的 safetensors 目录(报错信息含 “without an index”),原来依赖目录自动探测的用户配置可能报错。
  2. 下载逻辑集中化:transformer 覆盖中所有 HF URL 处理移入 weights/source.py,若存在未被 parse_weight_source 覆盖的 URL 形态,可能出现回归。
  3. 配置键调整:TransformerQuantLoadSpec 新增 transformer_override_config_path 字段,需要确认所有构造点已同步更新。
  4. CI 信号:Extra 与 AMD ROCm 跑失败过,NVIDIA 修复后已重跑,仍提示跨平台回归风险。
  5. 兼容性保护:*-mixed.safetensors 单文件路径与 GGUF 行为保持不变,风险总体可控。

变更影响 sglang diffusion 运行时中 transformer 组件的权重覆盖加载路径,并为其建立共享基础设施。用户现在可以用 safetensors index 声明分片权重集,也可以继续使用单一 *-mixed.safetensors;GGUF 与基础组件加载不受影响。对团队而言,weights/source.py 成为各组件加载器的统一入口,后续新增组件权重覆盖可直接复用这套解析逻辑。

无 index 权重目录将报错(行为变更) HF 下载逻辑集中迁移 CI Extra/AMD 曾失败 量化覆盖配置键调整

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论