feat(server): add recipe constraints and update generation workspace (#2217)

This commit is contained in:
leejet
2026-10-26 01:01:43 +08:00
committed by GitHub
parent 012c7f8866
commit e41fbcf455
88 changed files with 339 additions and 24 deletions
+7 -4
View File
@@ -100,10 +100,13 @@ for individual references, identity images, or video control frames.
--image-preprocess "target=control-frame,index=2,canny=true"
```
Init and mask sources must have the same dimensions. The mask inherits the
init crop, resize, and padding coordinates, while retaining its own filter,
padding value, and Canny setting. Conflicting mask geometry is rejected. An
omitted mask remains absent until the original pipeline creates its default mask.
If init and mask source dimensions differ, the mask is first center-cropped to
the init source aspect ratio and resized to the init source dimensions using
nearest-exact interpolation. The mask then inherits the init crop, resize, and
padding coordinates, while retaining its own filter, padding value, and Canny
setting for that transform. Explicit mask geometry is evaluated after this
alignment; conflicting geometry is rejected. An omitted mask remains absent
until the original pipeline creates its default mask.
## Downstream behavior
+1 -1
View File
@@ -1499,7 +1499,7 @@ bool load_wav_from_memory(const uint8_t* data,
const size_t remaining = size - chunk_data_pos; // safe: chunk_data_pos <= size
// Chunk sizes come from the payload, so compare against the remaining bytes
// instead of adding them to offsets (avoids size_t wrap on 32-bit builds).
const bool data_fits = static_cast<size_t>(chunk_size) <= remaining;
const bool data_fits = static_cast<size_t>(chunk_size) <= remaining;
if (std::memcmp(data + pos, "fmt ", 4) == 0) {
if (chunk_size < 16 || !data_fits) {
+2 -2
View File
@@ -89,8 +89,8 @@ FLUX 1/2/3, Chroma, Qwen Image and editing variants; Z-Image, Anima, ERNIE,
LENS, PiD and other supported image families; AnimateDiff, Wan, LTX,
HunyuanVideo, LingBot Video and MiniMax-H3. Model-specific text encoder
labels identify the expected encoder family and size. Loading a video model enables **Video Generation**; I2V also requires a
start image. The Models panel can be collapsed and remembers its open state.
Its header shows elapsed loading time from the status API and keeps the final
start image. The model button below the prompt opens model and recipe selection.
Its status summary shows elapsed loading time from the status API and keeps the final
time after success or failure, including after a page refresh.
Built-ins are embedded in the
binary from [recipes/](recipes/), with one JSON file per recipe. Adding or
+56 -4
View File
@@ -244,6 +244,7 @@ the same one-recipe-per-file schema:
"id": "my-flux",
"name": "My FLUX",
"description": "FLUX.1 dev",
"size_multiple": 16,
"model_url": "https://huggingface.co/black-forest-labs/FLUX.1-dev",
"slots": [
{"id": "diffusion_model", "required": true},
@@ -259,7 +260,8 @@ the same one-recipe-per-file schema:
IDs contain 1–128 ASCII letters, digits, dots, underscores or hyphens.
`name`, `schema_version: 1` and `slots` are required; `description` and
`model_url`, `required_any`, `defaults`, `context`, `features` and `disabled_features` are optional. Each slot requires only `id` and boolean `required`.
`model_url`, `required_any`, `defaults`, `context`, `features`, `disabled_features`
and `size_multiple`, `video_constraints` are optional. Each slot requires only `id` and boolean `required`.
The server supplies the default label and search directories for each slot.
An optional nonempty `label` overrides the display name, for example
`{"id": "diffusion_model", "label": "Low-noise diffusion model", "required": true}`.
@@ -267,6 +269,45 @@ Legacy `category` fields remain accepted, but must match one of the slot's
supported directories; they cannot redefine or restrict its search directories.
At least one required `model` or `diffusion_model` slot is necessary.
`size_multiple` is a positive integer from 1 to 4096 that declares the width and
height increment used by the web UI. It defaults to `8` when omitted; the UI also
uses `8` before a recipe is selected. For example, `"size_multiple": 16` makes
generated size presets multiples of 16. Exact dimensions that do not match the
increment require correction before submission. This is recipe metadata, not a
generation parameter, and does not change the core model's alignment behavior.
`video_constraints` is optional metadata for the web UI's Frames / Duration
controls. It does not override the runtime's model-specific rules. For example:
```json
"video_constraints": {
"frame_step": 4,
"frame_offset": 1,
"frame_rounding": "floor",
"min_frames": 1
}
```
Legal frame counts are `frame_step * n + frame_offset`, with integer `n >= 0`,
within the inclusive `min_frames` / `max_frames` range. All fields are optional:
| Field | Default | Accepted values |
| --- | --- | --- |
| `frame_step` | `1` | Integer 1–4096 |
| `frame_offset` | `0` | Integer 0 to `frame_step - 1` |
| `frame_rounding` | `"floor"` | `"floor"` or `"ceil"` |
| `min_frames` | `1` | Integer 1–4096 |
| `max_frames` | `4096` | Integer 1–4096, at least `min_frames` |
| `fixed_fps` | Unset | Integer 1–240; locks the FPS control |
The range must contain at least one legal frame count. Unknown keys are rejected.
Omitting the object adds no frame alignment in the UI. `defaults.fps` only sets
the initial FPS; it does not lock it. The UI multiplies requested duration by FPS,
aligns using the declared rounding direction, then clamps to the legal range.
Duration stays in the UI; generation requests still send only `video_frames` and
`fps`. Switching input modes preserves the aligned length. Changing FPS keeps
frames in Frames mode, and keeps the requested seconds in Duration mode.
`model_url` is an optional HTTPS Hugging Face model repository link, shown beside
the recipe description. Built-ins link to the model's author/publisher, not repacks.
`required_any` contains groups of declared slot IDs. At least one model in each
@@ -1693,15 +1734,26 @@ When omitted, backend defaults apply to these fields:
### Frame Count Semantics
`video_frames` is the requested target length, but the current core video path internally normalizes the effective frame count to the largest `4n + 1` value that does not exceed the requested count.
`video_frames` is the requested target length. The core normalizes it according
to the loaded model:
Examples:
| Model family | Frame alignment | Fixed FPS |
| --- | --- | --- |
| Wan, HunyuanVideo, LingBot | `4n + 1`, round down | Wan 2.2 S2V: 16 |
| LTX | `8n + 1`, round down | None |
| MiniMax-H3 | `17n + 5`, round up, minimum 5 | 24 |
| AnimateDiff | Whole frames, maximum 32 | None |
Wan examples:
- `video_frames = 33` stays `33`
- `video_frames = 34` becomes `33`
- `video_frames = 32` becomes `29`
The completed job payload includes the actual decoded `frame_count`.
At 16 FPS, a 5-second target requests 80 frames and aligns to 77 frames for Wan,
or approximately 4.813 seconds. Audio-driven generation and temporal upscaling
can change the final length. Use the completed job's actual decoded `frame_count`
and `fps` for the final playback duration.
### Completion Result
+40 -7
View File
@@ -369,20 +369,44 @@ json model_generation_features(const ServerRuntime& runtime) {
if (runtime.models->active_recipe.value("source", "") == "command_line")
return features;
// Optional components only become usable once selected and loaded.
features["ip_adapter"] = features.value("ip_adapter", false) && !runtime.ctx_params->ip_adapter_path.empty();
features["controlnet"] = features.value("controlnet", false) && !runtime.ctx_params->control_net_path.empty();
features["high_noise"] = features.value("high_noise", false) && !runtime.ctx_params->high_noise_diffusion_model_path.empty();
features["preview_tae"] = features.value("preview_tae", false) && !runtime.ctx_params->taesd_path.empty();
features["ref_audios"] = features.value("ref_audios", false) &&
(!runtime.ctx_params->audio_vae_path.empty() || !runtime.ctx_params->audio_encoder_path.empty());
features["ip_adapter"] = features.value("ip_adapter", false) && !runtime.ctx_params->ip_adapter_path.empty();
features["controlnet"] = features.value("controlnet", false) && !runtime.ctx_params->control_net_path.empty();
features["high_noise"] = features.value("high_noise", false) && !runtime.ctx_params->high_noise_diffusion_model_path.empty();
features["preview_tae"] = features.value("preview_tae", false) && !runtime.ctx_params->taesd_path.empty();
features["ref_audios"] = features.value("ref_audios", false) &&
(!runtime.ctx_params->audio_vae_path.empty() || !runtime.ctx_params->audio_encoder_path.empty());
features["ref_video_audios"] = features.value("ref_video_audios", false) && !runtime.ctx_params->audio_vae_path.empty();
if (!runtime.ctx_params->pulid_weights_path.empty())
features["slg"] = false;
return features;
}
static void validate_video_constraints(const json& constraints) {
check_keys(constraints, {"frame_step", "frame_offset", "frame_rounding", "min_frames", "max_frames", "fixed_fps"});
for (const auto* key : {"frame_step", "frame_offset", "min_frames", "max_frames", "fixed_fps"}) {
const int minimum = std::string(key) == "frame_offset" ? 0 : 1;
const int maximum = std::string(key) == "fixed_fps" ? 240 : 4096;
if (constraints.contains(key) &&
(!constraints[key].is_number_integer() || constraints[key] < minimum || constraints[key] > maximum)) {
throw std::runtime_error(std::string("video_constraints.") + key + " is outside its integer range");
}
}
const int step = constraints.value("frame_step", 1);
const int offset = constraints.value("frame_offset", 0);
const int minimum = constraints.value("min_frames", 1);
const int maximum = constraints.value("max_frames", 4096);
if (offset >= step || minimum > maximum ||
offset + std::max(0, (minimum - offset + step - 1) / step) * step > maximum) {
throw std::runtime_error("video_constraints must describe a nonempty frame range with frame_offset < frame_step");
}
if (constraints.contains("frame_rounding") &&
constraints["frame_rounding"] != "floor" && constraints["frame_rounding"] != "ceil") {
throw std::runtime_error("video_constraints.frame_rounding must be floor or ceil");
}
}
static void validate_recipe(const json& recipe) {
check_keys(recipe, {"schema_version", "id", "name", "description", "model_url", "slots", "required_any", "defaults", "context", "features", "disabled_features"});
check_keys(recipe, {"schema_version", "id", "name", "description", "model_url", "slots", "required_any", "defaults", "context", "features", "disabled_features", "size_multiple", "video_constraints"});
if (!recipe.at("schema_version").is_number_integer() || recipe.at("schema_version") != 1 ||
!valid_identifier(recipe.at("id").get<std::string>()) || recipe.at("name").get<std::string>().empty()) {
throw std::runtime_error("invalid schema_version, id or name");
@@ -390,6 +414,13 @@ static void validate_recipe(const json& recipe) {
if (recipe.contains("description") && !recipe["description"].is_string()) {
throw std::runtime_error("description must be a string");
}
if (recipe.contains("size_multiple") &&
(!recipe["size_multiple"].is_number_integer() || recipe["size_multiple"] < 1 || recipe["size_multiple"] > 4096)) {
throw std::runtime_error("size_multiple must be an integer from 1 to 4096");
}
if (recipe.contains("video_constraints")) {
validate_video_constraints(recipe["video_constraints"]);
}
if (recipe.contains("model_url")) {
const auto url = recipe.at("model_url").get<std::string>();
const std::string prefix = "https://huggingface.co/";
@@ -442,6 +473,7 @@ static void validate_recipe(const json& recipe) {
static json normalize_recipe(json recipe) {
validate_recipe(recipe);
recipe["size_multiple"] = recipe.value("size_multiple", 8);
recipe["effective_features"] = resolve_recipe_features(recipe);
for (auto& slot : recipe.at("slots")) {
const auto& spec = *find_model_slot(slot.at("id").get<std::string>());
@@ -605,6 +637,7 @@ void initialize_server_models(ServerRuntime& runtime) {
{"description", "Uses the loaded command-line model and its generation defaults."},
{"slots", json::array()},
{"defaults", json::object()},
{"size_multiple", 8},
{"features", features},
};
models.active_recipe["effective_features"] = resolve_recipe_features(models.active_recipe);
+32
View File
@@ -77,3 +77,35 @@ These are built-in catalog rules. Users may keep personal presets in
`<model-dir>/recipes/`; they need not add those presets to the built-in list.
See [the recipe schema and feature reference](../api.md#user-recipe-format)
for supported fields.
## Output dimension alignment
Set `size_multiple` when the runtime's output dimension alignment differs from
the default of `8`. Match `GenerationRequest::align_image_size` in
[`src/pipeline/request.cpp`](../../../src/pipeline/request.cpp): the VAE scale
factor multiplied by the model definition's `diffusion_down_factor`. The scale
comes from [`VAE::get_scale_factor`](../../../src/model/vae/vae.hpp), with the
PiD override in [`diffusion_engine.cpp`](../../../src/pipeline/diffusion_engine.cpp);
model geometry is defined under [`src/model/integrations`](../../../src/model/integrations/).
Use the runtime's output alignment, not the nominal patch size: some models pad
patches internally and crop their output back to the requested size. Pixel-space
models with an alignment of `1` explicitly use `size_multiple: 1`. MiniT2I also
has this alignment, but its separate fixed `512 × 512` output restriction still
applies. Reference-image preprocessing constraints are independent of this field.
## Video length constraints
Video recipes declare `video_constraints` to match `align_video_frames` in
[`diffusion_engine.cpp`](../../../src/pipeline/diffusion_engine.cpp) and the FPS
and AnimateDiff overrides in
[`request.cpp`](../../../src/pipeline/request.cpp). Wan, HunyuanVideo and LingBot
round down to `4n + 1`; LTX rounds down to `8n + 1`. MiniMax-H3 rounds up to
`17n + 5`, with at least 5 frames and fixed 24 FPS. AnimateDiff accepts whole
frames up to 32. Wan 2.2 S2V fixes FPS at 16.
Declare fixed FPS only where the runtime enforces it; recommended FPS belongs
in `defaults.fps`. These constraints guide UI conversion and estimates, not
runtime behavior. Audio-driven output and temporal upscaling may still change
the final frame count. See the [recipe schema](../api.md#user-recipe-format)
for field defaults and validation rules.
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "animatediff-sd15",
"name": "AnimateDiff SD 1.5",
"size_multiple": 64,
"description": "Use Video Generation with an SD 1.5 checkpoint and compatible motion module.",
"model_url": "https://huggingface.co/guoyww/animatediff",
"slots": [
@@ -22,6 +23,13 @@
"required": false
}
],
"video_constraints": {
"frame_step": 1,
"frame_offset": 0,
"frame_rounding": "floor",
"min_frames": 1,
"max_frames": 32
},
"defaults": {
"sampling_method": "euler",
"video_frames": 8,
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "chroma-radiance",
"name": "Chroma Radiance",
"size_multiple": 1,
"description": "Pixel-space generation without a VAE. Also accepts the x0 and experimental patch-32 variants.",
"model_url": "https://huggingface.co/lodestones/Chroma1-Radiance",
"slots": [
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "cosxl-edit",
"name": "CosXL Edit",
"size_multiple": 64,
"description": "Supply the image to edit as a reference image.",
"model_url": "https://huggingface.co/stabilityai/cosxl",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "ernie-image-turbo",
"name": "ERNIE Image Turbo",
"size_multiple": 16,
"model_url": "https://huggingface.co/baidu/ERNIE-Image-Turbo",
"slots": [
{
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "ernie-image",
"name": "ERNIE Image",
"size_multiple": 16,
"model_url": "https://huggingface.co/baidu/ERNIE-Image",
"slots": [
{
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "fibo2-dev",
"name": "Fibo 2.0 Dev",
"size_multiple": 16,
"description": "Text-to-image generation and native reference-image editing. Select a separate vision projector for editing when it is not bundled in the text encoder.",
"model_url": "https://huggingface.co/briaai/Fibo-2.0-dev",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "fibo2-edit-turbo",
"name": "Fibo 2.0 Edit Turbo",
"size_multiple": 16,
"description": "Dedicated reference-image editing checkpoint; supply reference images. Select a separate vision projector when it is not bundled in the text encoder.",
"model_url": "https://huggingface.co/briaai/Fibo-2.0-edit-turbo",
"slots": [
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "flux2-dev",
"name": "FLUX.2 dev",
"size_multiple": 16,
"description": "Supports text-to-image and reference-image editing.",
"model_url": "https://huggingface.co/black-forest-labs/FLUX.2-dev",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "flux2-klein-4b",
"name": "FLUX.2 Klein 4B",
"size_multiple": 16,
"description": "Supports reference-image editing. The FLUX.2 small decoder is an alternative VAE.",
"model_url": "https://huggingface.co/black-forest-labs/FLUX.2-klein-4B",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "flux2-klein-9b",
"name": "FLUX.2 Klein 9B",
"size_multiple": 16,
"description": "Supports text-to-image and reference-image editing.",
"model_url": "https://huggingface.co/black-forest-labs/FLUX.2-klein-9B",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "flux2-klein-base-4b",
"name": "FLUX.2 Klein Base 4B",
"size_multiple": 16,
"description": "Supports text-to-image and reference-image editing.",
"model_url": "https://huggingface.co/black-forest-labs/FLUX.2-klein-base-4B",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "flux2-klein-base-9b",
"name": "FLUX.2 Klein Base 9B",
"size_multiple": 16,
"description": "Use the 9B base transformer with Qwen 3 8B. Supports reference-image editing.",
"model_url": "https://huggingface.co/black-forest-labs/FLUX.2-klein-base-9B",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "flux3-klein-base",
"name": "FLUX.3 Image Klein Base",
"size_multiple": 16,
"model_url": "https://huggingface.co/black-forest-labs/flux-3-image-klein-base",
"slots": [
{
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "flux3-klein",
"name": "FLUX.3 Image Klein",
"size_multiple": 16,
"description": "Supports text-to-image and reference-image editing.",
"model_url": "https://huggingface.co/black-forest-labs/flux-3-image-klein",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "hidream-o1-dev",
"name": "HiDream O1 Image dev",
"size_multiple": 1,
"description": "Bundled checkpoint; also supports reference-image editing.",
"model_url": "https://huggingface.co/HiDream-ai/HiDream-O1-Image-Dev",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "hunyuan-video-15-i2v",
"name": "HunyuanVideo 1.5 I2V",
"size_multiple": 16,
"description": "Select HunyuanVideo 1.5 I2V weights and supply a start image in Video Generation.",
"model_url": "https://huggingface.co/tencent/HunyuanVideo-1.5",
"slots": [
@@ -36,6 +37,12 @@
"tae"
]
],
"video_constraints": {
"frame_step": 4,
"frame_offset": 1,
"frame_rounding": "floor",
"min_frames": 1
},
"defaults": {
"sampling_method": "euler",
"video_frames": 33,
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "hunyuan-video-15-t2v",
"name": "HunyuanVideo 1.5 T2V",
"size_multiple": 16,
"description": "Select HunyuanVideo 1.5 T2V weights and use Video Generation. Use the separate I2V recipe for I2V weights.",
"model_url": "https://huggingface.co/tencent/HunyuanVideo-1.5",
"slots": [
@@ -36,6 +37,12 @@
"tae"
]
],
"video_constraints": {
"frame_step": 4,
"frame_offset": 1,
"frame_rounding": "floor",
"min_frames": 1
},
"defaults": {
"sampling_method": "euler",
"video_frames": 33,
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "ideogram4",
"name": "Ideogram 4",
"size_multiple": 16,
"description": "Select the matching unconditional transformer for classifier-free guidance.",
"model_url": "https://huggingface.co/ideogram-ai/ideogram-4-fp8",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "instruct-pix2pix",
"name": "Instruct-Pix2Pix",
"size_multiple": 64,
"description": "Supply the image to edit as a reference image.",
"model_url": "https://huggingface.co/timbrooks/instruct-pix2pix",
"slots": [
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "iris-3b",
"name": "Iris 3B",
"size_multiple": 16,
"description": "Pixel-space model; no VAE is required.",
"model_url": "https://huggingface.co/speridlabs/iris-3b",
"slots": [
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "lens-turbo",
"name": "LENS Turbo",
"size_multiple": 16,
"model_url": "https://huggingface.co/microsoft/Lens-Turbo",
"slots": [
{
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "lens",
"name": "LENS",
"size_multiple": 16,
"model_url": "https://huggingface.co/microsoft/Lens",
"slots": [
{
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "lingbot-video-dense",
"name": "LingBot Video Dense 1.3B",
"size_multiple": 16,
"description": "Use Video Generation.",
"model_url": "https://huggingface.co/robbyant/lingbot-video-dense-1.3b",
"slots": [
@@ -30,6 +31,12 @@
"tae"
]
],
"video_constraints": {
"frame_step": 4,
"frame_offset": 1,
"frame_rounding": "floor",
"min_frames": 1
},
"defaults": {
"video_frames": 33,
"cfg_scale": 3,
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "lingbot-video-moe",
"name": "LingBot Video MoE 30B A3B",
"size_multiple": 16,
"description": "Use Video Generation.",
"model_url": "https://huggingface.co/robbyant/lingbot-video-moe-30b-a3b",
"slots": [
@@ -30,6 +31,12 @@
"tae"
]
],
"video_constraints": {
"frame_step": 4,
"frame_offset": 1,
"frame_rounding": "floor",
"min_frames": 1
},
"defaults": {
"video_frames": 33,
"cfg_scale": 3,
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "llada-image-turbo",
"name": "LLaDA Image Turbo",
"size_multiple": 16,
"description": "Use matching turbo text encoder and connectors. Editing needs connectors that include the SigVQ encoder.",
"model_url": "https://huggingface.co/inclusionAI/LLaDA-Image-Turbo",
"slots": [
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "llada-image",
"name": "LLaDA Image (base)",
"size_multiple": 16,
"description": "Use matching base transformer, text encoder and connectors; do not mix them with Turbo components. Editing needs connectors with SigVQ and 1024x1024 output.",
"model_url": "https://huggingface.co/inclusionAI/LLaDA-Image",
"slots": [
+7
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "ltx23-dev",
"name": "LTX 2.3 dev",
"size_multiple": 32,
"description": "Supports text, start-image and start/end-image video. Audio VAE is optional for silent video. Configure latent upscaling separately in generation settings. Optional distillation LoRAs use this recipe; select the LoRA and adjust the sampling settings separately.",
"model_url": "https://huggingface.co/Lightricks/LTX-2.3",
"slots": [
@@ -39,6 +40,12 @@
"tae"
]
],
"video_constraints": {
"frame_step": 8,
"frame_offset": 1,
"frame_rounding": "floor",
"min_frames": 1
},
"defaults": {
"sampling_method": "euler",
"video_frames": 33,
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "ltx23-distilled",
"name": "LTX 2.3 Distilled 1.1",
"size_multiple": 32,
"description": "Supports text, start-image and start/end-image video. Audio VAE is optional for silent video. Configure latent upscaling separately in generation settings.",
"model_url": "https://huggingface.co/Lightricks/LTX-2.3",
"slots": [
@@ -39,6 +40,12 @@
"tae"
]
],
"video_constraints": {
"frame_step": 8,
"frame_offset": 1,
"frame_rounding": "floor",
"min_frames": 1
},
"defaults": {
"sampling_method": "euler",
"video_frames": 121,
+7
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "ltx25-dev",
"name": "LTX 2.5 dev",
"size_multiple": 32,
"description": "Use the conv video VAE and LTX-specific Gemma 4 with bundled projection. Audio VAE is optional for silent video.",
"model_url": "https://huggingface.co/Lightricks/LTX-2.5",
"slots": [
@@ -35,6 +36,12 @@
"tae"
]
],
"video_constraints": {
"frame_step": 8,
"frame_offset": 1,
"frame_rounding": "floor",
"min_frames": 1
},
"defaults": {
"sampling_method": "euler",
"fps": 24,
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "mage-flow-edit",
"name": "Mage Flow Edit",
"size_multiple": 16,
"description": "Supply reference images. Select a separate vision projector when it is not bundled in the text encoder.",
"model_url": "https://huggingface.co/microsoft/Mage-Flow-Edit",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "mage-flow-turbo",
"name": "Mage Flow Turbo",
"size_multiple": 16,
"model_url": "https://huggingface.co/microsoft/Mage-Flow-Turbo",
"slots": [
{
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "mage-flow",
"name": "Mage Flow",
"size_multiple": 16,
"model_url": "https://huggingface.co/microsoft/Mage-Flow",
"slots": [
{
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "ming-image",
"name": "Ming Image 0.1",
"size_multiple": 16,
"model_url": "https://huggingface.co/inclusionAI/Ming-Image-0.1-Design",
"slots": [
{
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "minimax-h3-fl2va",
"name": "MiniMax H3 FL2VA",
"size_multiple": 32,
"description": "Generate video and audio, optionally with start/end images. Supports full and pruned FL2VA weights. Audio VAE is optional for silent video.",
"model_url": "https://huggingface.co/MiniMaxAI/MiniMax-H3",
"slots": [
@@ -35,6 +36,13 @@
"tae"
]
],
"video_constraints": {
"frame_step": 17,
"frame_offset": 5,
"frame_rounding": "ceil",
"min_frames": 5,
"fixed_fps": 24
},
"defaults": {
"video_frames": 56,
"fps": 24,
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "minimax-h3-ref2va",
"name": "MiniMax H3 Ref2VA",
"size_multiple": 32,
"description": "Supply image, video or audio references with compatible Ref2VA weights. Audio VAE is optional for silent video.",
"model_url": "https://huggingface.co/MiniMaxAI/MiniMax-H3",
"slots": [
@@ -35,6 +36,13 @@
"tae"
]
],
"video_constraints": {
"frame_step": 17,
"frame_offset": 5,
"frame_rounding": "ceil",
"min_frames": 5,
"fixed_fps": 24
},
"defaults": {
"video_frames": 56,
"fps": 24,
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "minit2i",
"name": "MiniT2I",
"size_multiple": 1,
"description": "Pixel-space model; no VAE is required.",
"model_url": "https://huggingface.co/MiniT2I/MiniT2I",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "moss-image-base",
"name": "MOSS Image Base",
"size_multiple": 16,
"description": "Select the MOSS-Image-Base transformer with its matching text encoder and FLUX.2 VAE or TAE.",
"model_url": "https://huggingface.co/OpenMOSS-Team/MOSS-Image-Base",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "moss-image-sft",
"name": "MOSS Image SFT",
"size_multiple": 16,
"description": "Select the MOSS-Image-SFT transformer with its matching text encoder and FLUX.2 VAE or TAE.",
"model_url": "https://huggingface.co/OpenMOSS-Team/MOSS-Image-SFT",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "nitrosd-realism",
"name": "NitroSD Realism",
"size_multiple": 64,
"model_url": "https://huggingface.co/ChenDY/NitroFusion",
"slots": [
{
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "pid-15-flux2",
"name": "PiD 1.5 (Flux.2 VAE)",
"size_multiple": 1,
"description": "Supply a reference image; the first image is used as the source latent. Select a matching Flux.2 PiD checkpoint and VAE.",
"model_url": "https://huggingface.co/nvidia/PiD",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "pid-15-qwen-image",
"name": "PiD 1.5 (Qwen Image VAE)",
"size_multiple": 1,
"description": "Supply a reference image; the first image is used as the source latent. Select a matching Qwen Image PiD checkpoint and VAE. Use the Qwen Image 2D VAE.",
"model_url": "https://huggingface.co/nvidia/PiD",
"slots": [
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "pid-15-sd3",
"name": "PiD 1.5 (SD3 VAE)",
"size_multiple": 1,
"description": "Supply a reference image; the first image is used as the source latent. Select a matching SD3 PiD checkpoint and VAE.",
"model_url": "https://huggingface.co/nvidia/PiD",
"slots": [
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "pid-15",
"name": "PiD 1.5 (Flux / Z-Image VAE)",
"size_multiple": 1,
"description": "Supply a reference image; the first image is used as the source latent. Select a Flux/Z-Image PiD checkpoint and matching Flux VAE.",
"model_url": "https://huggingface.co/nvidia/PiD",
"slots": [
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "pid-flux2",
"name": "PiD (Flux.2 VAE)",
"size_multiple": 1,
"description": "Supply a reference image; the first image is used as the source latent. Select a matching Flux.2 PiD checkpoint and VAE.",
"model_url": "https://huggingface.co/nvidia/PiD",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "pid-qwen-image",
"name": "PiD (Qwen Image VAE)",
"size_multiple": 1,
"description": "Supply a reference image; the first image is used as the source latent. Select a matching Qwen Image PiD checkpoint and VAE. Use the Qwen Image 2D VAE.",
"model_url": "https://huggingface.co/nvidia/PiD",
"slots": [
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "pid-sd3",
"name": "PiD (SD3 VAE)",
"size_multiple": 1,
"description": "Supply a reference image; the first image is used as the source latent. Select a matching SD3 PiD checkpoint and VAE.",
"model_url": "https://huggingface.co/nvidia/PiD",
"slots": [
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "pid",
"name": "PiD (Flux / Z-Image VAE)",
"size_multiple": 1,
"description": "Supply a reference image; the first image is used as the source latent. Select a Flux/Z-Image PiD checkpoint and matching Flux VAE.",
"model_url": "https://huggingface.co/nvidia/PiD",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "pixart-alpha-1024",
"name": "PixArt Alpha XL 1024",
"size_multiple": 16,
"description": "Use the matching PixArt Alpha transformer and SD 1.x-style VAE. Resolution/aspect-ratio micro-conditioning is not currently applied by the runtime.",
"model_url": "https://huggingface.co/PixArt-alpha/PixArt-XL-2-1024-MS",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "pixart-alpha-512",
"name": "PixArt Alpha XL 512",
"size_multiple": 16,
"description": "Use the matching PixArt Alpha transformer and SD 1.x-style VAE. Resolution/aspect-ratio micro-conditioning is not currently applied by the runtime.",
"model_url": "https://huggingface.co/PixArt-alpha/PixArt-XL-2-512x512",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "pixart-sigma",
"name": "PixArt Sigma XL 1024",
"size_multiple": 16,
"model_url": "https://huggingface.co/PixArt-alpha/PixArt-Sigma-XL-2-1024-MS",
"slots": [
{
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "qwen-image-21-turbo",
"name": "Qwen Image 2.1 Turbo",
"size_multiple": 32,
"description": "Use the standalone Turbo diffusion weights for generation and editing with the fixed 8-step schedule. Select the Qwen Image 2.1 VAE or TAEQI2.1, and supply a vision projector when needed.",
"model_url": "https://huggingface.co/Qwen/Qwen-Image-2.1-Turbo",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "qwen-image-21",
"name": "Qwen Image 2.1",
"size_multiple": 32,
"description": "Supports generation and editing. Select the full Qwen Image 2.1 VAE or TAEQI2.1. Supply a separate vision projector when needed. Select acceleration LoRAs and adjust their settings separately.",
"model_url": "https://huggingface.co/Qwen/Qwen-Image-2.1",
"slots": [
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sd-turbo",
"name": "SD Turbo",
"size_multiple": 64,
"description": "Select a single-file SD-Turbo checkpoint. Uses one step with CFG disabled (scale 1 in sd.cpp); use more steps for partial image-to-image denoising.",
"model_url": "https://huggingface.co/stabilityai/sd-turbo",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sd1-checkpoint",
"name": "SD 1.x",
"size_multiple": 64,
"description": "Supports SD 1.x, compatible LCM checkpoints and layer-count variants. For LCM weights, select LCM sampling and adjust steps/CFG. Tiny SD Diffusers directories must first be converted into a single-file checkpoint.",
"model_url": "https://huggingface.co/CompVis/stable-diffusion-v1-4",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sd1-controlnet",
"name": "SD 1.5 ControlNet",
"size_multiple": 64,
"description": "Supply a control image; input image and mask are optional for image-to-image or inpainting.",
"model_url": "https://huggingface.co/lllyasviel/control_v11p_sd15_canny",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sd1-inpainting",
"name": "SD 1.5 Inpainting",
"size_multiple": 64,
"description": "Select an inpainting checkpoint, then supply an input image and mask.",
"slots": [
{
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sd1-ip-adapter",
"name": "SD 1.5 IP-Adapter / Plus",
"size_multiple": 64,
"description": "Select matching SD 1.5 IP-Adapter or Plus weights and CLIP Vision H, then supply an IP-Adapter image.",
"model_url": "https://huggingface.co/h94/IP-Adapter",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sd2-512-checkpoint",
"name": "SD 2.1 512",
"size_multiple": 64,
"description": "Supports SD 2.x 512 and compatible layer-count variants. Tiny SD2 Diffusers directories must first be converted into a single-file checkpoint.",
"slots": [
{
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sd2-768-checkpoint",
"name": "SD 2.1 768",
"size_multiple": 64,
"slots": [
{
"id": "model",
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sd2-inpainting",
"name": "SD 2 Inpainting",
"size_multiple": 64,
"description": "Select an inpainting checkpoint, then supply Init Image and Mask Image.",
"slots": [
{
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sdxl-checkpoint",
"name": "SDXL",
"size_multiple": 64,
"description": "Supports SDXL and compatible reduced-depth checkpoints such as SSD-1B and Segmind Vega.",
"model_url": "https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sdxl-controlnet",
"name": "SDXL ControlNet",
"size_multiple": 64,
"description": "Select a compatible SDXL ControlNet and supply a preprocessed control image. Input image and mask are optional for image-to-image or inpainting.",
"model_url": "https://huggingface.co/diffusers/controlnet-canny-sdxl-1.0",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sdxl-inpainting",
"name": "SDXL Inpainting",
"size_multiple": 64,
"description": "Select an inpainting checkpoint, then supply Init Image and Mask Image.",
"model_url": "https://huggingface.co/diffusers/stable-diffusion-xl-1.0-inpainting-0.1",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sdxl-ip-adapter",
"name": "SDXL IP-Adapter / Plus",
"size_multiple": 64,
"description": "Select matching SDXL ViT-H IP-Adapter or Plus weights and CLIP Vision H, then supply an IP-Adapter image.",
"model_url": "https://huggingface.co/h94/IP-Adapter",
"slots": [
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sdxl-photomaker",
"name": "SDXL PhotoMaker",
"size_multiple": 64,
"description": "Select PhotoMaker v1 and supply identity images. Include the img trigger in the prompt.",
"model_url": "https://huggingface.co/TencentARC/PhotoMaker",
"slots": [
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sdxl-turbo",
"name": "SDXL Turbo",
"size_multiple": 64,
"model_url": "https://huggingface.co/stabilityai/sdxl-turbo",
"slots": [
{
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sdxs",
"name": "SDXS",
"size_multiple": 64,
"model_url": "https://huggingface.co/IDKiro/sdxs-512-dreamshaper",
"slots": [
{
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sefi-image",
"name": "SeFi Image 5B RL",
"size_multiple": 16,
"model_url": "https://huggingface.co/SeFi-Image/SeFi-Image-5B-Base",
"slots": [
{
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "sensenova-u15",
"name": "SenseNova U1.5",
"size_multiple": 32,
"description": "Bundled MoT checkpoint.",
"model_url": "https://huggingface.co/sensenova/SenseNova-U1.5-8B-MoT",
"slots": [
+7
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "wan21-flf2v",
"name": "Wan 2.1 FLF2V 14B",
"size_multiple": 16,
"description": "Select FLF2V weights and supply both start and end images.",
"model_url": "https://huggingface.co/Wan-AI/Wan2.1-FLF2V-14B-720P",
"slots": [
@@ -35,6 +36,12 @@
"tae"
]
],
"video_constraints": {
"frame_step": 4,
"frame_offset": 1,
"frame_rounding": "floor",
"min_frames": 1
},
"defaults": {
"sampling_method": "euler",
"flow_shift": 16,
+7
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "wan21-i2v",
"name": "Wan 2.1 I2V",
"size_multiple": 16,
"description": "Image-to-video with Wan 2.1, compatible SkyReels V2 weights, or a single Wan 2.2 I2V expert. CLIP Vision is required by Wan 2.1 I2V and is unused by Wan 2.2. Defaults target Wan 2.1 480p.",
"model_url": "https://huggingface.co/Wan-AI/Wan2.1-I2V-14B-480P",
"slots": [
@@ -35,6 +36,12 @@
"tae"
]
],
"video_constraints": {
"frame_step": 4,
"frame_offset": 1,
"frame_rounding": "floor",
"min_frames": 1
},
"defaults": {
"sampling_method": "euler",
"video_frames": 33,
+7
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "wan21-t2v",
"name": "Wan 2.1 T2V",
"size_multiple": 16,
"description": "Text-to-video with Wan 2.1 or a compatible single Wan 2.2 T2V expert. Defaults target Wan 2.1 14B; adjust settings for the selected weights and acceleration LoRAs.",
"model_url": "https://huggingface.co/Wan-AI/Wan2.1-T2V-14B",
"slots": [
@@ -30,6 +31,12 @@
"tae"
]
],
"video_constraints": {
"frame_step": 4,
"frame_offset": 1,
"frame_rounding": "floor",
"min_frames": 1
},
"defaults": {
"sampling_method": "euler",
"video_frames": 33,
+7
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "wan21-vace",
"name": "Wan 2.1 VACE 1.3B / 14B",
"size_multiple": 16,
"description": "Select matching VACE weights. Supports text, reference images and control video through Video Generation.",
"model_url": "https://huggingface.co/Wan-AI/Wan2.1-VACE-14B",
"slots": [
@@ -30,6 +31,12 @@
"tae"
]
],
"video_constraints": {
"frame_step": 4,
"frame_offset": 1,
"frame_rounding": "floor",
"min_frames": 1
},
"defaults": {
"sampling_method": "euler",
"video_frames": 33,
+7
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "wan22-i2v",
"name": "Wan 2.2 I2V A14B",
"size_multiple": 16,
"description": "Image-to-video. Select a start image in Video Generation after loading. LightX2V acceleration LoRAs must match the I2V low- and high-noise models; configure LoRAs and sampling separately.",
"model_url": "https://huggingface.co/Wan-AI/Wan2.2-I2V-A14B",
"slots": [
@@ -35,6 +36,12 @@
"tae"
]
],
"video_constraints": {
"frame_step": 4,
"frame_offset": 1,
"frame_rounding": "floor",
"min_frames": 1
},
"defaults": {
"sampling_method": "euler",
"video_frames": 33,
+8
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "wan22-s2v",
"name": "Wan 2.2 S2V 14B",
"size_multiple": 16,
"description": "Supply a start image and reference audio through Video Generation.",
"model_url": "https://huggingface.co/Wan-AI/Wan2.2-S2V-14B",
"slots": [
@@ -35,6 +36,13 @@
"tae"
]
],
"video_constraints": {
"frame_step": 4,
"frame_offset": 1,
"frame_rounding": "floor",
"min_frames": 1,
"fixed_fps": 16
},
"defaults": {
"sampling_method": "euler",
"flow_shift": 3,
+7
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "wan22-t2v",
"name": "Wan 2.2 T2V A14B",
"size_multiple": 16,
"description": "Text-to-video. Use Video Generation after loading. LightX2V acceleration LoRAs must match the T2V low- and high-noise models; configure LoRAs and sampling separately.",
"model_url": "https://huggingface.co/Wan-AI/Wan2.2-T2V-A14B",
"slots": [
@@ -35,6 +36,12 @@
"tae"
]
],
"video_constraints": {
"frame_step": 4,
"frame_offset": 1,
"frame_rounding": "floor",
"min_frames": 1
},
"defaults": {
"sampling_method": "euler",
"video_frames": 33,
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "wan22-ti2v-5b",
"name": "Wan 2.2 TI2V 5B",
"size_multiple": 32,
"description": "Use Wan 2.2 VAE. Supports text, start-image and start/end-image conditioning.",
"model_url": "https://huggingface.co/Wan-AI/Wan2.2-TI2V-5B",
"slots": [
@@ -30,6 +31,12 @@
"tae"
]
],
"video_constraints": {
"frame_step": 4,
"frame_offset": 1,
"frame_rounding": "floor",
"min_frames": 1
},
"defaults": {
"sampling_method": "euler",
"video_frames": 33,
+1
View File
@@ -2,6 +2,7 @@
"schema_version": 1,
"id": "z-image-l2p",
"name": "Z-Image L2P",
"size_multiple": 16,
"description": "Pixel-space L2P checkpoint with Qwen 3 4B; no VAE is needed. Use dimensions divisible by 16.",
"model_url": "https://huggingface.co/zhen-nan/L2P",
"slots": [
+1 -1
View File
@@ -185,7 +185,7 @@ namespace MOSSImage {
ggml_build_forward_expand(ctx->graph, parts[i]);
}
}
x = ggml_concat(c, x, text, 1);
x = ggml_concat(c, x, text, 1);
for (int64_t i = 0; i < config.depth; ++i) {
if (std::find(skip.begin(), skip.end(), i) == skip.end()) {
x = std::dynamic_pointer_cast<Block>(blocks["layers." + std::to_string(i)])->forward(ctx, x, pe, parts);
+2 -2
View File
@@ -314,8 +314,8 @@ namespace sd::pipeline {
}
bool skip_vae_encoding = (sd->version == VERSION_HIDREAM_O1) || !(
ref_image_params.pass_to_dit || ref_image_params.vlm_use_vae_input ||
sd_version_is_unet_edit(sd->version) || sd_version_is_pid(sd->version));
ref_image_params.pass_to_dit || ref_image_params.vlm_use_vae_input ||
sd_version_is_unet_edit(sd->version) || sd_version_is_pid(sd->version));
std::vector<sd::Tensor<float>> ref_latents;
for (size_t i = 0; i < ref_images.size(); i++) {
+7 -2
View File
@@ -379,8 +379,13 @@ namespace sd {
if (target == ImageTarget::Mask && has_init_transform_) {
auto options = resolve_options(target, index);
if (image.width != init_transform_.source_width || image.height != init_transform_.source_height) {
fail("mask and init source dimensions must match");
return {};
ImagePreprocessor alignment;
tensor = alignment.apply_geometry(tensor, target, index, init_transform_.source_width, init_transform_.source_height,
ImageResizeMode::CropResize, ops::InterpolateMode::NearestExact);
if (tensor.empty()) {
fail("could not align mask to init source dimensions");
return {};
}
}
bool geometry_override = options.count("width") || options.count("anchor") ||
(options.count("mode") && options.at("mode") != "auto");