Skip to main content
Use this guide as a triage flow for finding the smallest change that can support a model. Most new model work should touch a small set of files, even though the runtime is split into separate folders.

Read the Code in This Order

The files are split by runtime responsibility. For a new model, read the request path first:
  1. registry.py chooses the model family, sampling params, and pipeline config.
  2. configs/pipeline_configs/{model}.py defines model-specific denoising and decoding behavior.
  3. runtime/pipelines/{model}.py wires modules into stages.
  4. runtime/pipelines_core/stages/ runs the shared stage logic.
  5. runtime/models/ contains native model components only when the architecture cannot be reused.
That is the dependency direction. Avoid making a model PR that requires readers to jump between folders in a different order. runtime/models/ owns modeling code: checkpoint-defined neural modules, architecture wrappers, and weight-loading or forward-path details that are intrinsic to one model family. Reusable serving infrastructure belongs in SGLang-Diffusion runtime folders such as runtime/cache/, runtime/distributed/, runtime/utils/, or shared pipeline stages. This includes cache managers, graph runners, process-group transport, request utilities, and common action-policy helpers. Model packages may call these helpers. Keep ownership in shared runtime folders unless the code is truly architecture-specific.

Out-of-Tree Models and Pipelines

An installed package can register native component models and a pipeline without modifying SGLang-Diffusion. Register them in the package’s __init__.py:
Install the package in the server environment and set the same environment variable used by SRT plugins:
Notes:
  • The string form of register_model keeps component imports lazy.
  • hf_model_paths also supports checkpoints without model_index.json. Other Diffusers checkpoints can select the pipeline through _class_name.
  • For a standalone safetensors file, pass --pipeline CustomPipeline.
  • Set the environment variable before startup. Each process imports the package once. Use overwrite=True only to intentionally replace a built-in pipeline.

Start With the Smallest Change

Before adding files, decide which path fits the model. Do not add a folder just to mirror the Diffusers repository layout. Add a new file only when an existing pipeline, stage, module, config, or sampler cannot express the behavior clearly.

Minimal File Map

The source tree is split by runtime responsibility. That split is useful for optimization. Keep new model PRs focused on the files required by model behavior. For a new native architecture, the common minimum is:
  1. registry.py
  2. configs/sample/{model}.py
  3. configs/pipeline_configs/{model}.py
  4. runtime/pipelines/{model}.py
  5. runtime/models/dits/{model}.py
Every extra file should map to model behavior that existing code cannot express clearly. For dVLA or other non-image diffusion policies, keep the same ownership rule. The policy network, VLM/action expert modules, checkpoint mapping, and model-specific forward code belong under runtime/models/. Prefix caches, request-local contexts, denoising graph runners, OpenPI-compatible transport, and prefix/action process-group utilities should be shared SGLang-Diffusion runtime infrastructure when they are useful beyond the first model.

Read the Reference First

Use the model’s Diffusers pipeline, official implementation, or model_index.json as the source of truth. Write down:
  • Which modules must be loaded: tokenizer, text encoder, image encoder, transformer, scheduler, VAE, processor, and any extra adapters.
  • The prompt and image encoding flow.
  • Latent shape, packing, scale, shift, dtype, and device rules.
  • Timestep and sigma schedule.
  • The exact forward() kwargs expected by the denoising network.
  • VAE decode rules and output post-processing.
If the new model is close to Flux, Qwen-Image, GLM-Image, Wan, HunyuanVideo, or LTX, extend that implementation before starting from an empty file.

Choose a Pipeline Shape

SGLang-Diffusion uses ComposedPipelineBase to wire stages together. Most native pipelines should choose the least invasive stage shape that preserves the runtime semantics. Prefer this order:
  1. Use native stages directly. This keeps the model on shared code paths for offload, component readiness, profiling, disaggregation, batching, and future stage-level optimizations.
  2. Subclass the narrowest native stage. If only prompt processing differs, inherit from TextEncodingStage. If only latent setup, timestep setup, denoising, or decode differs, inherit from that specific native stage. Preserve the existing input/output fields whenever possible.
  3. Add a custom single-purpose stage only when no native stage contract fits. Keep the stage owner narrow: one stage should own one coherent transformation, such as a custom condition assembly step or a model-specific policy/action bridge.
  4. Use an aggregated BeforeDenoisingStage only as a last resort. This is the least preferred shape because it hides multiple runtime responsibilities in one stage, increases code size and review cost, and bypasses shared hooks for offload, profiling, disaggregation, batching, and future stage-level optimizations.

Implement the Pieces

1. Sampling Params

Create request parameters only for values users can set at runtime.

2. Pipeline Config

PipelineConfig is where shared denoising and decoding stages get model-specific callbacks.
Make these kwargs match the denoising module’s forward() signature exactly.

3. Pipeline Wiring

Use the standard helper when the model fits it.
When a standard helper is not enough, first check whether only one native stage needs model-specific behavior. In that case, subclass that stage and keep the rest of the pipeline standard. For example, custom tokenization or prompt-window logic should usually inherit from TextEncodingStage directly.
Use a custom single-purpose stage only when the reference pipeline has one step that cannot be represented cleanly by a hook or native-stage subclass. Keep the custom stage narrow and reuse native stages before and after it.
Use an aggregated BeforeDenoisingStage only when the reference pipeline couples several preparation steps so tightly that splitting them would require fragile duplicate state or extra synchronization. Do not start with this shape.

4. Last-Resort Before-Denoising Stage

A BeforeDenoisingStage is not a catch-all replacement for the native stages. Use it when the model has custom latent packing, conditioning assembly, timestep preparation, or request-local state that does not fit LatentPreparationStage or TimestepPreparationStage, and only after checking whether the work can be a native-stage subclass or a custom single-purpose stage. If the difference is prompt handling, subclass TextEncodingStage instead. A proper BeforeDenoisingStage should populate the batch fields consumed by DenoisingStage.
Required fields for DenoisingStage:

5. Distributed and memory integration

Single-GPU parity is only the first milestone. Complete native support also requires:
  • Encoder and DiT TP/SP: use native parallel projections and sharded weight loading for TP, and USPAttention for SP. Handle masks, RoPE, padding, and output gathering without falling back to a replicated full model. TP and SP must work together.
  • VAE parallel decode: subclass ParallelTiledVAE, or reuse an existing native base with the same contract. Support tiled and spatial_shard decode through DecodingStage and the shared decode group. Reuse runtime/layers/parallel_conv.py and runtime/models/vaes/parallel/diffusers_spatial.py where applicable.
  • Layerwise offload: every loaded neural module must inherit LayerwiseOffloadableModuleMixin and list all repeated block paths in layer_names. Set layerwise_offload_dit_group_enabled = False for non-DiT modules. Component CPU offload is not a substitute.
See wanvideo.py and qwen_image.py for DiT TP/SP, gemma_3.py for encoder TP and offload, and autoencoder_kl_qwenimage.py or ltx_2_vae.py for VAE decode. The Diffusers backend is compatibility-first and does not need to meet this native integration contract.

6. Registry

Register the family once the sampling params and pipeline config exist.
The pipeline file is discovered through its EntryClass; do not add a second pipeline registry unless the existing registry requires it.

Verify the Port

Use one deterministic prompt and seed while comparing with the reference implementation.
  1. Run a single-GPU smoke test and check that the output contains coherent content.
  2. Compare latent scale and shift, timestep order, sigma values, and conditioning kwargs against Diffusers or the official implementation.
  3. Compare VAE decode separately, including tiled and multi-GPU spatial_shard.
  4. Run encoder and DiT TP, SP, combined TP x SP, and --layerwise-offload-components all; compare with the single-GPU resident baseline.
  5. If the model supports LoRA, CFG parallelism, or disaggregation, test each feature explicitly.
  6. Add or update the cookbook, examples, and Supported Models catalog when users need a new launch command.
Common failure points:
  • Wrong latent scale or shift.
  • Reversed or dtype-mismatched timesteps.
  • Missing negative embeddings when CFG is enabled.
  • Conditioning kwarg names mismatched with the DiT forward().
  • Rotary embedding shape or style mismatch.
  • Decoding packed latents without restoring raw_latent_shape.

PR Checklist

  • Reused an existing family, stage, module, scheduler, or VAE wherever possible.
  • Kept the new-model touch surface small and justified any extra files.
  • Added SamplingParams, PipelineConfig, pipeline wiring, DiT module, and registry entry when native support is needed.
  • Confirmed pipeline_name matches the Diffusers model_index.json _class_name when applicable.
  • Confirmed _required_config_modules matches the model repo.
  • Verified image or video quality against a reference output.
  • Completed the distributed and memory integration checks above.
  • Tested CFG parallelism and distributed serving paths when they apply.