Prhub

#35713 [diffusion] feat: support out-of-tree models and pipelines

原始 PR 作者 mickqian 合并时间 2026-08-21 00:33 文件变更 8 提交数 2 评论 1 代码增减 +297 / -16

执行摘要

Diffusion 支持外部模型与 pipeline 插件注册

PR body 指出:“An external package can now register its native component models and composed pipeline from init.py, then run through the standard sglang serve or sglang generate CLI without changing SGLang-Diffusion source.” 过去第三方模型只能通过 fork 源码或提交内置支持才能接入,维护成本高。本 PR 通过开放注册 API 降低接入门槛,避免每次接入新模型都要改动核心代码。

值得精读,特别是注册表惰性初始化与公共 API 的设计。该 PR 是 SGLang-Diffusion 插件化演进的关键一步,理解它有助于后续接入第三方模型或开发类似插件机制。如果团队计划扩展模型生态,建议关注 register_pipeline 的语义与 overwrite 冲突策略的文档化。

讨论亮点

本 PR 没有收到 review 评论;唯一 issue 评论是作者触发的 /tag-and-rerun-ci CI 重跑。作者在 PR body 中自述了验证结果:在 RTX 5090 devbox 上以独立外部包注册 ExternalMiniMaxH3Pipeline 并成功跑通 4 秒 T2VA 请求。设计层面的取舍(如懒加载时机、overwrite 冲突处理)未经过额外公开讨论,由作者直接决定。

实现拆解

  1. 环境变量与加载器:在 python/sglang/multimodal_gen/envs.py 新增 SGLANG_EXTERNAL_MODEL_PACKAGE 环境变量定义;新增 runtime/utils/external_model_package.py,实现 load_external_model_package(),用 functools.lru_cache 确保进程内只导入一次外部包。
  2. 注册表惰性初始化与公共 APImultimodal_gen/registry.py 引入 _BUILTIN_PIPELINES_DISCOVERED 标志替代原先“注册表非空即返回”的逻辑,解决外部注册后内置发现可能被跳过的问题;新增 _ensure_registry_initialized() 统一执行内置发现与外部包加载;新增 get_pipeline_class()get_registered_pipeline_names()register_pipeline() 三个公共 API,其中 register_pipeline() 以 keyword-only 参数接收 pipeline 类、采样/配置类、hf_model_pathsmodel_detectorsoverwrite,并复用 register_configs() 完成配置类注册。
  3. 模型注册集成runtime/models/registry.pyget_supported_archs()resolve_by_alias()_normalize_archs() 三处调用 load_external_model_package(),使模型解析前先加载外部包;_LazyRegisteredModel 移除冗余的 component_name 字段,改为从 <module>:<class> 字符串直接解析模块与类名。
  4. 流水线构建接入runtime/pipelines_core/__init__.pybuild_pipeline 中,显式 --pipeline 分支从直接访问私有 _PIPELINE_REGISTRY 改为调用 get_registered_pipeline_names()get_pipeline_class(),保证显式指定的 pipeline 也能命中外部注册。
  5. 测试与文档配套:新增 test/unit/test_external_model_package.py,在临时目录构造一个外部插件包,通过子进程设置环境变量 SGLANG_EXTERNAL_MODEL_PACKAGEPYTHONPATH,断言模型解析、get_model_infoget_pipeline_config_classes 均能命中外部类;文档 support_new_models.mdxenvironment_variables.mdx 补充 out-of-tree 示例和环境变量说明。
文件 模块 状态 重要度
python/sglang/multimodal_gen/registry.py 注册中心 modified 8.32
python/sglang/multimodal_gen/test/unit/test_external_model_package.py 测试覆盖 added 7.71
python/sglang/multimodal_gen/runtime/utils/external_model_package.py 插件加载 added 7.27
python/sglang/multimodal_gen/runtime/models/registry.py 模型注册 modified 6.23
python/sglang/multimodal_gen/runtime/pipelines_core/__init__.py 流水线构建 modified 6.06
python/sglang/multimodal_gen/envs.py 环境配置 modified 5.07
docs/docs/sglang-diffusion/support_new_models.mdx 文档 modified 4.84
docs/docs/sglang-diffusion/environment_variables.mdx 文档 modified 2.78

关键符号

_ensure_registry_initialized get_pipeline_class get_registered_pipeline_names register_pipeline load_external_model_package build_pipeline

关键源码片段

python/sglang/multimodal_gen/registry.py dependency-wiring

核心变更:引入惰性初始化与公共注册 API,是 out-of-tree 支持的主入口。

# python/sglang/multimodal_gen/registry.py
# 惰性初始化标志:确保内置 pipeline 发现只执行一次
_BUILTIN_PIPELINES_DISCOVERED = False
​
​
def _ensure_registry_initialized() -> None:
    # 统一入口:先发现内置 pipeline,再导入外部模型包
    _discover_and_register_pipelines()
    load_external_model_package()
​
​
def get_pipeline_class(pipeline_class_name: str) -> Type[ComposedPipelineBase] | None:
    # 按名称获取 pipeline 类,查找时自动完成注册初始化
    _ensure_registry_initialized()
    return _PIPELINE_REGISTRY.get(pipeline_class_name)
​
​
def get_registered_pipeline_names() -> List[str]:
    # 返回全部已注册 pipeline 名称,含外部包注册项
    _ensure_registry_initialized()
    return list(_PIPELINE_REGISTRY)
python/sglang/multimodal_gen/runtime/utils/external_model_package.py data-contract

新增懒加载函数,是外部包导入的唯一入口,进程内仅执行一次。

# python/sglang/multimodal_gen/runtime/utils/external_model_package.py
import importlib
from functools import lru_cachefrom sglang.multimodal_gen import envs
from sglang.multimodal_gen.runtime.utils.logging_utils import init_loggerlogger = init_logger(__name__)
​
​
@lru_cache(maxsize=1)
def load_external_model_package() -> None:
    # 按配置导入 out-of-tree 模型包,进程内只执行一次
    package_name = envs.SGLANG_EXTERNAL_MODEL_PACKAGE
    if not package_name:
        return
​
    logger.info("Loading external model package: %s", package_name)
    importlib.import_module(package_name)

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

  1. 外部包导入失败即启动失败load_external_model_package() 直接调用 importlib.import_module() 且未捕获异常,若外部包存在依赖缺失或语法错误,服务进程会在注册初始化时直接退出。
  2. 同名 pipeline 注册冲突register_pipeline() 提供 overwrite 参数,但默认值 False 下若与内置 pipeline 同名,具体处理是抛错还是忽略取决于实现,存在行为模糊性,且外部包与内置包的加载顺序会影响最终结果。
  3. 懒加载入口覆盖不全_ensure_registry_initialized() 是统一入口,但若未来有代码绕过该入口直接访问 _PIPELINE_REGISTRY(例如旧代码或第三方模块),外部注册将不可见。
  4. 子进程环境依赖:模型架构检查通过 _run_in_subprocess 在子进程中执行,依赖环境变量传递;若外部包除环境变量外还依赖配置文件或工作目录,子进程可能无法正确加载模型。

对第三方模型开发者是重大利好,接入成本从“改源码”降为“写一个 Python 插件包并设置环境变量”。对现有用户无行为变化(未设置 SGLANG_EXTERNAL_MODEL_PACKAGE 时路径与原先一致)。对系统而言,服务启动时会额外导入外部包,增加启动时间和潜在依赖冲突风险。对团队而言,新增了公共 API 和文档的长期维护负担,且 SGLANG_EXTERNAL_MODEL_PACKAGE 与 SRT 模型插件机制共享环境变量名,需注意命名空间的相互影响。改动集中在 multimodal_gen 模块,不影响 SRT 核心路径。

外部包导入失败即启动失败 同名 pipeline 注册冲突 懒加载入口覆盖不全 子进程环境依赖敏感

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论