feat(server): add recipe-based model management (#2176)

This commit is contained in:
leejet authored and GitHub committed 2026-10-21 00:17:30 +08:00
1 parent 9b7da27374
commit 0518b34374
132 files changed
+6688 -147

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
/start-server.sh text eol=lf
/start-server.bat text eol=crlf
+14 -6
View File
@@ -26,6 +26,8 @@ on:
"ggml",
"examples/server/frontend",
"examples/server/frontend/**",
"start-server.sh",
"start-server.bat",
]
pull_request:
types: [opened, synchronize, reopened]
@@ -44,6 +46,8 @@ on:
"ggml",
"examples/server/frontend",
"examples/server/frontend/**",
"start-server.sh",
"start-server.bat",
]
env:
@@ -107,7 +111,8 @@ jobs:
run: |
cp ggml/LICENSE ./build/bin/ggml.txt
cp LICENSE ./build/bin/stable-diffusion.cpp.txt
zip -j sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}.zip ./build/bin/*
chmod +x start-server.sh
zip -j sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}.zip ./build/bin/* ./start-server.sh ./start-server.bat
- name: Upload artifacts
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
@@ -170,7 +175,8 @@ jobs:
run: |
cp ggml/LICENSE ./build/bin/ggml.txt
cp LICENSE ./build/bin/stable-diffusion.cpp.txt
zip -j sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-vulkan.zip ./build/bin/*
chmod +x start-server.sh
zip -j sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-vulkan.zip ./build/bin/* ./start-server.sh ./start-server.bat
- name: Upload artifacts
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
@@ -325,7 +331,8 @@ jobs:
run: |
cp ggml/LICENSE ./build/bin/ggml.txt
cp LICENSE ./build/bin/stable-diffusion.cpp.txt
zip -j sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}.zip ./build/bin/*
chmod +x start-server.sh
zip -j sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}.zip ./build/bin/* ./start-server.sh ./start-server.bat
- name: Upload artifacts
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
@@ -420,7 +427,7 @@ jobs:
ls .\build\bin
throw "Can't find stable-diffusion.dll"
}
7z a sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-${{ matrix.build }}-x64.zip $filePath
7z a sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-${{ matrix.build }}-x64.zip $filePath .\start-server.sh .\start-server.bat
- name: Copy and pack Cuda runtime
id: pack_cuda_runtime
@@ -575,7 +582,7 @@ jobs:
- name: Pack artifacts
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
run: |
7z a sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-${{ env.ROCM_VERSION }}-x64.zip .\build\bin\*
7z a sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-${{ env.ROCM_VERSION }}-x64.zip .\build\bin\* .\start-server.sh .\start-server.bat
- name: Upload artifacts
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
@@ -737,7 +744,8 @@ jobs:
run: |
cp ggml/LICENSE ./build/bin/ggml.txt
cp LICENSE ./build/bin/stable-diffusion.cpp.txt
zip -y -r sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-Ubuntu-${{ env.UBUNTU_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-rocm-${{ matrix.ROCM_VERSION }}.zip ./build/bin
chmod +x start-server.sh
zip -y -r sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-Ubuntu-${{ env.UBUNTU_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-rocm-${{ matrix.ROCM_VERSION }}.zip ./build/bin ./start-server.sh ./start-server.bat
- name: Upload artifacts
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
+4
View File
@@ -7,6 +7,10 @@ tests/
.cache/
*.swp
*.bat
!/start-server.bat
/start-server.local.sh
/start-server.local.bat
/start-server.local.ps1
*.bin
*.exe
*.gguf
+2
View File
@@ -83,6 +83,8 @@ documentation.
and debugging.
* `examples/common/` - shared example support code.
* `examples/server/` - server application built on top of the library.
Before modifying built-in recipes, read and follow
[the built-in recipe policy](examples/server/recipes/README.md).
* `examples/server/frontend/` - git submodule containing independent frontend
code. Avoid modifying it unless explicitly requested.
+11
View File
@@ -33,6 +33,17 @@ PRs should include:
The [UIs list in the README](README.md#uis) only includes open-source projects that use `stable-diffusion.cpp` as a backend. Do not submit PRs to add closed-source UIs.
## Server Recipes
Before adding or changing a built-in server recipe, read and follow the
[built-in recipe policy](examples/server/recipes/README.md). The policy governs
native editing, community editing LoRAs, distinct task checkpoints, external
adapters, native components and layer-count variants. Do not add recipes solely
for style or acceleration LoRAs, separately stored native components, or added
or removed model layers. Explain the task, component compatibility or capability
difference that justifies a new recipe, and verify its required components,
feature declarations and embedded API representation.
## Code Style
Format code according to the repository style before submitting changes.
+13
View File
@@ -68,6 +68,8 @@ add_executable(${TARGET}
../common/media_io.cpp
main.cpp
runtime.cpp
model_catalog.cpp
routes_models.cpp
async_jobs.cpp
routes_index.cpp
routes_openai.cpp
@@ -88,6 +90,17 @@ else()
message(STATUS "HAVE_INDEX_HTML disabled")
endif()
file(GLOB BUILTIN_RECIPE_FILES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/recipes/*.json")
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${BUILTIN_RECIPE_FILES})
# Keep recipes in separate literals to stay within MSVC's string size limit.
set(SD_BUILTIN_RECIPES "")
foreach(RECIPE_FILE IN LISTS BUILTIN_RECIPE_FILES)
file(READ "${RECIPE_FILE}" RECIPE_JSON)
string(APPEND SD_BUILTIN_RECIPES "R\"recipes(${RECIPE_JSON})recipes\",\n")
endforeach()
configure_file(recipes_builtin.h.in "${CMAKE_CURRENT_BINARY_DIR}/recipes_builtin.h" @ONLY)
target_include_directories(${TARGET} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
install(TARGETS ${TARGET} RUNTIME)
target_link_libraries(${TARGET} PRIVATE stable-diffusion ${CMAKE_THREAD_LIBS_INIT})
if(SD_WEBP)
+245
View File
@@ -1,3 +1,248 @@
# Model selection
Start without model arguments to select models in the web UI:
```sh
sd-server --model-dir ./models
```
From a source checkout, you can also use `sh ./start-server.sh` on Linux/macOS
or `.\start-server.bat` on Windows. The launchers look for the server relative
to the script: beside the script, then in `bin/Release/`, `bin/`,
`build/bin/Release/`, and `build/bin/`, in that order.
For a different build directory, set `SD_SERVER_PATH` to the executable path:
```sh
SD_SERVER_PATH="./build_vulkan/bin/sd-server" sh ./start-server.sh --listen-port 8080
```
```powershell
$env:SD_SERVER_PATH = ".\build_vulkan\bin\Release\sd-server.exe"
.\start-server.bat --listen-port 8080
```
Binary release archives include both launchers. Keep the launchers under version
control and put personal settings in a separate file beside them:
- Windows batch: `start-server.local.bat`
- Linux/macOS: `start-server.local.sh`
These local files are ignored by Git and excluded from release archives. They
are loaded automatically after the launcher's defaults. They can override
`SD_SERVER_PATH`, `MODEL_DIR`, and `COMMANDLINE_ARGS`; there is no need to edit
the tracked launchers. For example:
```bat
@echo off
set "MODEL_DIR=D:\stable-diffusion-webui\models"
set "COMMANDLINE_ARGS=--listen-port 8080 --offload-to-cpu"
```
```sh
MODEL_DIR="/data/stable-diffusion-webui/models"
COMMANDLINE_ARGS="--listen-port 8080 --offload-to-cpu"
```
Each launcher prints the resolved command before execution and forwards the
server's exit code.
`SD_SERVER_PATH` inherits the environment value by default; leave it empty for
automatic discovery, or set it in the local file to select another executable.
Settings are applied in this order: launcher defaults, local configuration,
then invocation arguments. Repeated `--model-dir` arguments add roots, so they
do not replace the `MODEL_DIR` root. Local configuration files are shell scripts
and use the corresponding shell's syntax.
The launchers preserve the current working directory, so the default
`MODEL_DIR="./models"` is relative to where you start the launcher. Arguments
passed when invoking the launcher follow these defaults. Repeated `--model-dir`
arguments add search roots rather than replacing `MODEL_DIR`; set `MODEL_DIR` in
the local configuration to change the first root. Other single-value options can override the defaults.
In the shell script, `COMMANDLINE_ARGS` is a
whitespace-separated list; pass values containing spaces as quoted arguments
when invoking the script. `MODEL_DIR` itself supports spaces in both scripts.
A leading `--version` bypasses the configured defaults. An explicit
`SD_SERVER_PATH` is used without falling back to another build if it is invalid.
Choose a recipe, select the required component files, and click **Load models**.
Recipe and component selectors support searching by name or relative file ID.
Use the arrow keys and Enter to select, or Escape to cancel a search. With
**Apply recipe defaults** enabled, selecting a recipe immediately applies its
declared generation defaults while preserving prompts, input images and fields
not specified by the recipe. Disable it to keep your current parameters when
switching recipes. This preference defaults to enabled and is saved locally.
Model loading and server-info refreshes do not reapply defaults or reset the form.
Recipes declaring `video_frames` also switch the form to Video Generation;
other recipes switch to Image Generation. Generation is enabled after the
selected recipe is loaded. Changing a selection does not reload the model
until you click Load.
The server can also start with the existing model arguments shown below;
existing API clients then work without selecting a recipe. The UI automatically
selects **Command-line model (all features)** and uses the startup generation
defaults. This runtime-only recipe exposes all controls for the supported
image/video mode. Selecting another recipe previews a model switch; selecting
the command-line entry again restores its generation defaults. The entry is
removed when the command-line context is unloaded or replaced.
Built-in recipes cover SD 1/2/3/3.5, SDXL and distilled checkpoints;
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
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
editing a built-in requires rebuilding the server. User recipes are read from
`<model-dir>/recipes/*.json`. A valid user recipe with the same ID replaces
the entire built-in recipe. Across roots, the first valid user recipe wins;
overrides are logged. Duplicate user IDs within one root are rejected; invalid
recipes are reported without removing a valid recipe from another root or the
built-in version. Slots need only `id` and
`required`; the server supplies labels and search directories. Use `label`
only when a recipe needs a more specific display name.
Recipes use `features` to enable specialized controls and `disabled_features`
to turn off shared defaults. Disabled features take precedence. The UI hides
unsupported fields, empty sections and irrelevant sampler/cache options, and
omits hidden values from generation requests. See the
[feature declarations](api.md#recipe-feature-declarations) for the defaults
and complete list of supported names.
Built-in additions and changes must follow the [recipe policy](recipes/README.md).
Split recipes by task weights or added capabilities, not by style or acceleration
LoRAs. Quantizations, file formats and compatible layer-count variants share a
recipe. Native encoders, projectors and connectors stay in the model's recipe.
Community editing recipes require selecting the matching LoRA separately; a
recipe does not enable it automatically.
See [generation defaults and sources](recipes/defaults.md) for the selected
publisher recommendations and reference-code defaults, with step defaults capped
at 20. Recipes link to the
original publisher's Hugging Face repository where one is available. DiT
recipes default to Euler, except for models requiring a specialized sampler.
Compatible recipes offer a TAE slot alongside VAE. When the checkpoint does not
bundle a VAE, select at least one of VAE or TAE. The Models panel shows a reminder
below the model selectors until this requirement is met. A selected TAE is used for
encoding and decoding, including when a full VAE is also selected; use complete,
model-compatible TAE weights rather than decoder-only preview weights. Qwen
Image 2.1 uses the same recipe for its full VAE and TAEQI2.1 alternatives.
The following variants have dedicated recipes:
| Model | Recipe IDs / notes |
| --- | --- |
| Z-Image L2P | `z-image-l2p`; no VAE |
| LLaDA-Image | `llada-image` (base) and `llada-image-turbo`; use matching encoders and connectors |
| PixArt Alpha | `pixart-alpha-512`, `pixart-alpha-1024`; Alpha VAE scale and positional settings |
| PiD / PiD 1.5 | `pid` / `pid-15` for Flux or Z-Image VAE; append `-sd3`, `-flux2`, or `-qwen-image` for other latent formats |
| SDXL adapters | `sdxl-ip-adapter` supports classic and Plus ViT-H adapters; `sdxl-controlnet` requires compatible SDXL weights |
| FLUX.1 Fill / Depth | `flux1-fill` uses Init Image and Mask Image; `flux1-depth` uses a precomputed depth map in Control Image |
| SD2 / SDXL inpainting | `sd2-inpainting`, `sdxl-inpainting` |
| SD-Turbo | `sd-turbo`; single-file SD2.1-derived checkpoint |
| Community editing LoRAs | `anima-edit`, `krea2-raw-edit`, `krea2-turbo-edit`; apply a matching LoRA after loading |
| HunyuanVideo 1.5 | `hunyuan-video-15-t2v`, `hunyuan-video-15-i2v`; choose the task's checkpoint |
| Wan 2.1 / compatible single Wan 2.2 experts | `wan21-t2v`, `wan21-i2v`; the I2V recipe also accepts compatible SkyReels V2 weights |
| Qwen Image 2.1 Turbo | `qwen-image-21-turbo`; standalone Turbo weights with the fixed 8-step schedule, Euler and CFG 1 |
| MOSS Image | `moss-image-base`, `moss-image-sft`; choose the corresponding checkpoint |
PiD always requires a reference image and uses the first one. Its recipes expose
reference inputs rather than ordinary image-to-image inputs. IP-Adapter recipes
expose a separate IP-Adapter image and strength. FLUX Canny/Depth consume Control
Image directly without a separate ControlNet; the UI does not calculate edge
or depth maps. Their strength is controlled by guidance, not ControlNet strength.
Krea2 edit recipes use the default `krea2_ostris_edit` reference preset, so the
editing LoRA must match that convention. Ordinary Anima and Krea2 recipes do not
expose reference-image editing inputs.
Fill/Depth guidance defaults follow the [Diffusers FLUX documentation](https://huggingface.co/docs/diffusers/api/pipelines/flux).
SD-Turbo uses the [model card's one-step configuration](https://huggingface.co/stabilityai/sd-turbo),
with CFG disabled using scale 1 in sd.cpp.
SVD is not included: legacy model recognition and UNet branches remain, but the
current runner/conditioning setup does not provide a dedicated SVD image-conditioning
path. Adding a recipe alone would not establish a working SVD pipeline.
Reference images, masks, audio, first/last video frames, and generation options
such as upscaling remain generation inputs. Hardware and memory options remain
startup arguments. Tiny SD/Tiny SD2 and Diffusers LCM directories must first be
converted into single-file checkpoints for the file catalog. LCM checkpoints
and LoRAs use the matching SD family recipe; select LCM sampling and adjust
steps/CFG for the chosen weights.
Metadata inspection, conversion, standalone upscaling and ADetailer are CLI
workflows rather than model-loading recipes. ESRGAN and LTX latent upscalers
are selected through generation upscaling settings with `--hires-upscalers-dir`;
textual inversion uses the startup `--embd-dir` option.
Repeat `--model-dir` to share multiple model directories, including an existing
SD WebUI model directory:
```sh
sd-server --model-dir ./models --model-dir /data/stable-diffusion-webui/models
```
With no `--model-dir`, the server uses `./models`. Explicit roots replace that
default and are scanned in argument order. Duplicate canonical roots and file
paths are scanned once. Every root can contain a `recipes/` directory.
Directory names are matched case-insensitively on all platforms:
| Category | Directory names |
| --- | --- |
| Checkpoints | `checkpoints`, `Stable-diffusion` |
| VAE | `vae`, `VAE` |
| LoRA | `loras`, `Lora` |
| ControlNet | `controlnet`, `ControlNet` |
Other categories keep their existing directory names. Classification uses only
the containing directory: `*.vae.safetensors`, `*.vae.ckpt`, and similar names
inside `checkpoints/` or `Stable-diffusion/` remain checkpoints.
Place model files in category directories, for example:
```text
models/
checkpoints/
diffusion_models/
text_encoders/
vae/
clip_vision/
loras/
recipes/
my-flux.json
```
Subdirectories within a model category are supported. Use **Refresh files &
recipes** after adding files or changing recipes. Scanning lists file names
without loading model weights. Category membership does not guarantee
compatibility; the model loader performs the final compatibility check.
Symlinks resolving outside the model directory are rejected. Dropdown labels
omit the category prefix while retaining subdirectory names. Identical display
paths are displayed as full file paths, with source directories before the
filename instead of a hash suffix. The same applies to duplicate LoRA paths. File IDs include a
root qualifier for additional roots; clients should use the returned IDs.
Empty optional categories do not display missing-file hints.
Place Tiny AutoEncoder (TAE) files in `vae/` and select them in the **TAE**
slot. The legacy `vae_approx/` directory is also scanned for TAE selections.
LoRAs are discovered automatically in both `loras/` and `Lora/` under every
model root. Use `--lora-model-dir` to replace automatic discovery with one
explicit LoRA directory. Duplicate names remain separate selectable entries. The LoRA selectors support
searching by name or path with the same keyboard controls as model selectors.
The server keeps one model context loaded. Loading or unloading is rejected
while generation requests or queued jobs are pending. A switch validates the
selection before unloading the old context, then loads the replacement.
If loading fails, the service stays online with no active model and reports
the error. Backend and memory options supplied at startup apply to subsequent
loads. Recipe defaults apply to new loads; explicitly supplied generation
request parameters take precedence.
See [the API reference](api.md#model-management) for recipe fields, supported
slots, management endpoints, and compatibility behavior.
# Example
The following example starts `sd-server` with a standalone diffusion model, VAE, and LLM text encoder:
+312 -19
View File
@@ -59,6 +59,293 @@ Current endpoints include:
- `POST /sdcpp/v1/vid_gen`
- `POST /sdcpp/v1/upscale`
## Model management
Model management is additive: existing generation request bodies, successful
responses, job polling and cancellation remain unchanged. All three API families
use the current loaded context. Starting with the existing model arguments
preserves command-line preloading and its generation defaults; it does not
require a recipe.
Recipe defaults apply when a generation request omits the corresponding
parameters. Image editing endpoints retain their existing input-image size
inference when dimensions are omitted.
Without a loaded model, generation endpoints return HTTP 503 with
`{"error":"No model is loaded","code":"model_not_loaded"}`.
While loading/unloading, endpoints that access the context return HTTP 503
with `code: "model_loading"`. These errors retain the existing string-valued
`error` field. Job polling/cancellation, catalog queries and model status remain
available during loading. Model lists are empty when no model is loaded;
`/v1/models` retains the `sd-cpp-local` ID when ready.
| Method | Endpoint | Response / behavior |
| --- | --- | --- |
| GET | `/sdcpp/v1/recipes` | `{recipes: [...], errors: [...]}` |
| GET | `/sdcpp/v1/model-files` | `{files: [{id, name, category, root, relative_path}], errors: [...]}` |
| POST | `/sdcpp/v1/models/refresh` | Rescan recipes and files; return `{errors: [...]}` |
| GET | `/sdcpp/v1/models/status` | Current state, recipe, selection, error, loading time and optional revision |
| POST | `/sdcpp/v1/models/load` | Load the submitted recipe and selection; return status after completion |
| POST | `/sdcpp/v1/models/unload` | Release the active context; return empty status |
Load request:
```json
{
"recipe": "flux1-dev",
"models": {
"diffusion_model": "diffusion_models/flux1-dev.gguf",
"clip_l": "text_encoders/clip_l.safetensors",
"t5xxl": "text_encoders/t5xxl.safetensors",
"vae": "vae/ae.safetensors"
}
}
```
File IDs are opaque strings returned by `model-files`. The first model root
retains the existing relative IDs, such as `checkpoints/nested/model.gguf`.
Additional roots use `@<root-id>/<relative-path>` so identical paths in different
roots remain selectable. Root qualifiers derive from the canonical root path;
adding unrelated roots or refreshing the catalog does not change them. Moving
a root changes its qualifier. Do not construct IDs or use display names in load
requests.
The display `name` omits the physical category directory (including aliases such
as `Stable-diffusion`). Duplicate display paths use the full absolute file path,
with the source directories before the filename and no hash suffix.
`root` is an opaque source identifier, and
`relative_path` is the file path beneath that root. Duplicate canonical files
appear only once, using the first root that discovers them. Categories depend
on directories, never on filename suffixes such as `.vae.safetensors`.
Repeated `--model-dir` arguments merge roots in order; absent arguments default
to `./models`. User recipes override built-ins; the earliest root containing a
valid user recipe wins across roots. Duplicate recipe IDs within one root are
rejected. Directory aliases do not change recipe slot categories.
LoRA lists retain relative `path` IDs for `loras/` in the first model root and
for an explicit `--lora-model-dir`, and qualify other directories. The additive `display_path` field provides a readable
relative path, or the full absolute file path when needed to distinguish duplicates.
Continue sending `path` in structured LoRA requests. `--lora-model-dir` overrides
automatic discovery across all model roots.
Absolute paths, unknown files, wrong categories and
paths escaping the configured root are rejected. Optional slots can be omitted
or set to an empty string. Load/unload returns HTTP 409 (`model_busy`) while
requests or queued jobs are pending. Invalid configurations return HTTP 400
(`invalid_model_config`) before unloading the current context. An initialization
failure returns HTTP 422 (`model_load_failed`) and leaves no active context.
The load HTTP request waits for completion; a separate client can poll status
while it runs. Refreshing the catalog never modifies an already loaded context.
Status uses `state: empty | loading | ready | error`, `recipe` (ID or null),
`models` (slot-to-file-ID mapping), and `error` (string or null). A monotonically
increasing `revision` identifies runtime transitions within this server process.
Command-line preloading keeps `origin: command_line` and `recipe: null` for
compatibility, and adds `runtime_recipe`: a read-only `@command-line` recipe
with all feature declarations enabled. It is only available while that context
is loaded and cannot be submitted to `models/load`. The UI selects it by default
and uses command-line generation defaults, not defaults from a catalog recipe.
Capabilities also include this status as an additive `runtime` field.
`load_elapsed_seconds` is the server-measured duration in seconds (a number or
null). It updates while a model is loading and freezes on success or failure.
Timing starts after selection validation and includes releasing the previous
model. It resets for each load and is null before any load and during/after
unloading. Command-line preloading reports its completed initialization time.
Poll `/sdcpp/v1/models/status` to display elapsed time, including while loading;
timer updates alone do not change `revision`.
### Recipe feature declarations
Recipes may declare `features` and `disabled_features` arrays. The server starts
with shared defaults, enables features declared by component slots and `features`,
then applies `disabled_features` last. A disabled feature always wins. Unknown
names, non-array values and duplicate names within an array are rejected.
Both fields are optional; existing recipes remain valid.
```json
{
"features": ["flow_shift", "distilled_guidance"],
"disabled_features": ["clip_skip", "lora"]
}
```
Default-enabled features are `img2img`, `mask_image`, `lora`, `vae_tiling`,
`cache`, `cache_dit` and `preview`. All other features below default to false:
| Feature | Controls |
| --- | --- |
| `flow_shift` | Flow shift, where used by the selected scheduler |
| `distilled_guidance` | Distilled guidance embedding |
| `shifted_timestep` | SDXL timestep shifting |
| `img_cfg` | Image CFG |
| `slg` | Skip-layer guidance fields |
| `clip_skip` | CLIP Skip |
| `img2img`, `mask_image` | Image initialization/strength and masks |
| `ref_images` | Reference image inputs |
| `controlnet` | Control image and ControlNet strength |
| `control_image` | Control image without a separate ControlNet (FLUX Canny/Depth) |
| `ip_adapter` | IP-Adapter image and strength |
| `init_image`, `end_image` | Video start/end frames |
| `control_frames`, `vace` | Video control frames and VACE strength |
| `high_noise` | High-noise pass, MoE boundary and high-noise LoRAs |
| `lora`, `vae_tiling` | LoRA overrides and VAE tiling |
| `cache`, `cache_unet`, `cache_dit` | Cache panel and architecture-specific algorithms |
| `preview`, `preview_tae` | Preview panel and TAE preview option |
A `control_net` slot enables `controlnet`, `ip_adapter` enables `ip_adapter`,
`high_noise_diffusion_model` enables `high_noise`, and `tae`/`taesd` enables `preview_tae`, unless explicitly disabled.
ControlNet controls are shown when previewing a supporting recipe, even before
a model file is selected. High-noise and TAE controls additionally require their
component to be selected. Runtime capabilities require the component to be loaded.
PuLID disables SLG while selected.
Image-only and video-only inputs are filtered by the active generation mode.
Eta depends on the selected sampler; SCM fields only apply to DBCache,
TaylorSeer and Cache-DIT. Empty panels and advanced sections are hidden.
`GET /sdcpp/v1/recipes` adds a resolved boolean `effective_features` object to
each recipe. This is response-only metadata, like `source` and slot `categories`;
omit it when writing a user recipe file. The defaults are maintained by the
server, not duplicated by the frontend.
`GET /sdcpp/v1/capabilities` adds `generation_features` for the loaded context,
derived from the active recipe and component availability without inferring
features from model-family names. When no model is loaded, every feature is
present and false. The UI can still preview a selected recipe using its
`effective_features` before loading.
Existing `features` / `features_by_mode` keys are retained and their model-specific
values reflect these declarations. A loaded recipe is snapshotted: refreshing
recipe files does not change the active context's capabilities until reloading.
Command-line preloading uses the runtime-only all-features recipe instead of model-family inference
and component paths; variant-specific recipe declarations are more precise.
These declarations govern the Web UI and advertised capabilities, not API
request validation. Existing generation APIs continue accepting their existing
fields. The Web UI omits unsupported/hidden fields and ignores hidden image
inputs when building requests, so switching recipes cannot submit stale options.
### User recipe format
Contributions to the built-in catalog must follow the
[built-in recipe policy](recipes/README.md). Personal user recipes may define
workflow-specific presets without adding them to the built-in catalog.
Each UTF-8 JSON file in `<model-dir>/recipes/` defines one complete recipe.
The maximum recipe file size is 1 MiB. Unknown fields, invalid types and
duplicate slots are errors. Built-ins in `examples/server/recipes/*.json` use
the same one-recipe-per-file schema:
```json
{
"schema_version": 1,
"id": "my-flux",
"name": "My FLUX",
"description": "FLUX.1 dev",
"model_url": "https://huggingface.co/black-forest-labs/FLUX.1-dev",
"slots": [
{"id": "diffusion_model", "required": true},
{"id": "clip_l", "required": true},
{"id": "t5xxl", "required": true},
{"id": "vae", "required": false},
{"id": "tae", "required": false}
],
"required_any": [["vae", "tae"]],
"defaults": {"width": 1024, "height": 1024, "cfg_scale": 1.0, "distilled_guidance": 3.5, "sampling_method": "euler", "steps": 20}
}
```
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`.
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}`.
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.
`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
group must be selected. For example, `"required_any": [["vae", "tae"]]` with both
slots marked `required: false` allows either the full VAE or a compatible TAE.
Selecting both is allowed; the current server uses TAE for encoding/decoding when
TAE is selected. Checkpoint recipes with a bundled VAE do not need this group.
Use complete, model-compatible TAE weights; decoder-only preview weights cannot
replace a full VAE. Pixel-space models and unsupported latent formats have no
TAE slot.
| Slot IDs | Search directories |
| --- | --- |
| `model` | `checkpoints` |
| `diffusion_model`, `high_noise_diffusion_model`, `uncond_diffusion_model` | `diffusion_models` |
| `clip_l`, `clip_g`, `t5xxl`, `llm`, `audio_encoder`, `embeddings_connectors` | `text_encoders` |
| `clip_vision`, `llm_vision` | `clip_vision` |
| `vae`, `audio_vae` | `vae` |
| `tae` (legacy alias: `taesd`) | `vae` or `vae_approx` |
| `tokenizer` | `tokenizers` |
| `control_net` | `controlnet` |
| `ip_adapter` | `ipadapter` |
| `motion_module` | `motion_modules` |
| `photo_maker` | `photomaker` |
| `pulid_weights` | `pulid` |
Built-in recipes use `tae` for the Tiny AutoEncoder slot. TAE selections can use files from either `vae/` or the legacy
`vae_approx/` directory. Existing recipes and load requests using `taesd`
remain supported. Do not specify both `tae` and `taesd` in the same recipe
or load request.
Model files can use `.gguf`, `.safetensors`, `.sft`, `.ckpt`, `.pt`, `.pth`
or `.bin` extensions. The tokenizer category lists `.json` files only.
Custom recipes compose components supported by the backend; they do not add
new model architectures.
Optional `context` settings apply to each model load and override the matching
startup setting. Supported fields are `vae_format` (`auto`, `flux`, `sd3`,
`flux2`, or `wan`) and `model_args` (a string in the same format as
`--model-args`; replaces the startup string). Other startup settings remain
unchanged. Switching to a recipe that omits these fields restores the startup
values. For example, PiD uses `{"vae_format": "flux"}` and Qwen Image Edit
2511 uses `{"model_args": "qwen_image_zero_cond_t=true"}`.
Supported defaults:
- Integer `width`/`height` (64–4096), `steps` (1–1000), `video_frames`
(1–4096), `fps` (1–240), `shifted_timestep` (0–1000), and
`high_noise_steps` (1–1000 or -1 for automatic).
- Numeric `cfg_scale`, `img_cfg_scale`, `distilled_guidance`, `flow_shift`,
`high_noise_cfg_scale`, and `high_noise_flow_shift` (0–1000).
- Numeric `strength` (0–1), used for image-to-image denoising, and
`moe_boundary` (0–1), used for automatic Wan MoE expert switching.
- `custom_sigmas`: an empty array or 2–1001 finite, nonnegative numbers.
A nonempty array overrides the generated schedule; the sigma count minus one
determines the actual step count. Include the final zero. An empty array uses
the generated schedule. Loaded recipe sigmas apply to generation requests
unless `sample_params.custom_sigmas` overrides them; switching recipes restores
that recipe's schedule or the startup default.
- Supported `sampling_method`/`scheduler` and
`high_noise_sampling_method`/`high_noise_scheduler` names.
Other generation parameters continue to use startup defaults unless explicitly
supplied by the generation request. Built-in Wan recipes provide video defaults;
use `/sdcpp/v1/vid_gen` after loading a video model.
LoRAs are merged from `loras/` and `Lora/` in every model root unless
`--lora-model-dir` is supplied.
Recipe responses fill in each slot's `label`, `category` (the legacy value,
if supplied, or the first search directory), and `categories` (the complete
server-defined list of search directories). Clients should filter model files
using `categories`; `category` remains available for older clients.
For example, `{"id": "tae", "required": false}` expands to
`{"id": "tae", "required": false, "label": "TAE", "category": "vae", "categories": ["vae", "vae_approx"]}`.
Recipe responses also add `source: builtin | user` and, for user recipes, `file`.
Do not include `source`, `file`, or slot `categories` in a recipe file. A valid user recipe
replaces a built-in with the same ID in full; inheritance/partial merging is
not supported. Invalid recipes are skipped. Multiple valid user files sharing
an ID are all skipped, preserving any built-in with that ID. Catalog errors
are returned as `{file, message}` entries.
## `sd_cpp_extra_args`
`sd_cpp_extra_args` is an extension mechanism for the compatibility APIs.
@@ -413,7 +700,7 @@ Common job shape:
"id": "job_01HTXYZABC",
"kind": "img_gen",
"status": "queued",
"created": 1775401200,
"created": 1775401200000,
"started": null,
"completed": null,
"queue_position": 2,
@@ -437,6 +724,11 @@ Field types:
| `error` | `object \| null` |
| `preview` | `object \| null` |
`created`, `started`, and `completed` are integer Unix timestamps in
**milliseconds**. Subtract `started` from `completed` and divide by 1000 to get
the elapsed generation time in seconds, including output encoding and excluding
queue time.
`preview` sub-fields:
| Field | Type | Notes |
@@ -512,6 +804,7 @@ Shared nested fields:
| --- | --- |
| `loras[].name` | `string` |
| `loras[].path` | `string` |
| `loras[].display_path` | `string` (display only) |
`upscalers`
@@ -666,7 +959,7 @@ Example response:
"id": "job_01HTXYZABC",
"kind": "img_gen",
"status": "queued",
"created": 1775401200,
"created": 1775401200000,
"poll_url": "/sdcpp/v1/jobs/job_01HTXYZABC"
}
```
@@ -994,9 +1287,9 @@ Example completed job:
"id": "job_01HTXYZABC",
"kind": "img_gen",
"status": "completed",
"created": 1775401200,
"started": 1775401203,
"completed": 1775401215,
"created": 1775401200000,
"started": 1775401203000,
"completed": 1775401215237,
"queue_position": 0,
"result": {
"output_format": "png",
@@ -1020,9 +1313,9 @@ Example failed job:
"id": "job_01HTXYZABC",
"kind": "img_gen",
"status": "failed",
"created": 1775401200,
"started": 1775401203,
"completed": 1775401204,
"created": 1775401200000,
"started": 1775401203000,
"completed": 1775401204000,
"queue_position": 0,
"result": null,
"error": {
@@ -1041,9 +1334,9 @@ Example cancelled job:
"id": "job_01HTXYZABC",
"kind": "img_gen",
"status": "cancelled",
"created": 1775401200,
"created": 1775401200000,
"started": null,
"completed": 1775401202,
"completed": 1775401202000,
"queue_position": 0,
"result": null,
"error": {
@@ -1079,7 +1372,7 @@ Example response:
"id": "job_01HTXYZVID",
"kind": "vid_gen",
"status": "queued",
"created": 1775401200,
"created": 1775401200000,
"poll_url": "/sdcpp/v1/jobs/job_01HTXYZVID"
}
```
@@ -1407,9 +1700,9 @@ Example completed job:
"id": "job_01HTXYZVID",
"kind": "vid_gen",
"status": "completed",
"created": 1775401200,
"started": 1775401203,
"completed": 1775401215,
"created": 1775401200000,
"started": 1775401203000,
"completed": 1775401215237,
"queue_position": 0,
"result": {
"output_format": "webm",
@@ -1433,9 +1726,9 @@ Example failed job:
"id": "job_01HTXYZVID",
"kind": "vid_gen",
"status": "failed",
"created": 1775401200,
"started": 1775401203,
"completed": 1775401204,
"created": 1775401200000,
"started": 1775401203000,
"completed": 1775401204000,
"queue_position": 0,
"result": null,
"error": {
@@ -1454,9 +1747,9 @@ Example cancelled job:
"id": "job_01HTXYZVID",
"kind": "vid_gen",
"status": "cancelled",
"created": 1775401200,
"created": 1775401200000,
"started": null,
"completed": 1775401202,
"completed": 1775401202000,
"queue_position": 0,
"result": null,
"error": {
+7 -7
View File
@@ -88,7 +88,7 @@ const char* async_job_status_name(AsyncJobStatus status) {
}
void purge_expired_jobs(AsyncJobManager& manager) {
const int64_t now = unix_timestamp_now();
const int64_t now = unix_timestamp_now_ms();
for (auto it = manager.expired_jobs.begin(); it != manager.expired_jobs.end();) {
if (it->second <= now) {
@@ -108,8 +108,8 @@ void purge_expired_jobs(AsyncJobManager& manager) {
int64_t ttl_seconds = job->status == AsyncJobStatus::Completed
? manager.completed_ttl_seconds
: manager.failed_ttl_seconds;
if (now - job->completed_at >= ttl_seconds) {
manager.expired_jobs[job->id] = now + std::max<int64_t>(ttl_seconds, 60);
if (now - job->completed_at >= ttl_seconds * 1000) {
manager.expired_jobs[job->id] = now + std::max<int64_t>(ttl_seconds, 60) * 1000;
it = manager.jobs.erase(it);
} else {
++it;
@@ -130,7 +130,7 @@ size_t count_pending_jobs(const AsyncJobManager& manager) {
std::string make_async_job_id(AsyncJobManager& manager) {
std::ostringstream oss;
oss << "job_" << std::hex << unix_timestamp_now() << "_" << std::setw(8)
oss << "job_" << std::hex << unix_timestamp_now_ms() << "_" << std::setw(8)
<< std::setfill('0') << manager.next_id++;
return oss.str();
}
@@ -143,7 +143,7 @@ bool cancel_queued_job(AsyncJobManager& manager, AsyncGenerationJob& job) {
manager.queue.erase(new_end, manager.queue.end());
job.status = AsyncJobStatus::Cancelled;
job.completed_at = unix_timestamp_now();
job.completed_at = unix_timestamp_now_ms();
job.result_images_b64.clear();
job.result_media_b64.clear();
job.result_media_mime_type.clear();
@@ -373,7 +373,7 @@ void async_job_worker(ServerRuntime& runtime) {
job = it->second;
job->status = AsyncJobStatus::Generating;
job->started_at = unix_timestamp_now();
job->started_at = unix_timestamp_now_ms();
}
std::vector<std::string> output_images;
@@ -405,7 +405,7 @@ void async_job_worker(ServerRuntime& runtime) {
continue;
}
job->completed_at = unix_timestamp_now();
job->completed_at = unix_timestamp_now_ms();
if (ok) {
job->status = AsyncJobStatus::Completed;
job->result_images_b64 = std::move(output_images);
+1 -1
View File
@@ -32,7 +32,7 @@ struct AsyncGenerationJob {
std::string id;
AsyncJobKind kind = AsyncJobKind::ImgGen;
AsyncJobStatus status = AsyncJobStatus::Queued;
int64_t created_at = unix_timestamp_now();
int64_t created_at = unix_timestamp_now_ms();
int64_t started_at = 0;
int64_t completed_at = 0;
ImgGenJobRequest img_gen;
+20 -5
View File
@@ -10,6 +10,7 @@
#include "async_jobs.h"
#include "common/common.h"
#include "common/resource_owners.hpp"
#include "model_catalog.h"
#include "routes.h"
#include "runtime.h"
@@ -50,7 +51,7 @@ static void parse_args(int argc,
const bool random_seed_requested = default_gen_params.seed < 0;
if (!svr_params.resolve_and_validate() ||
!ctx_params.resolve_and_validate(IMG_GEN) ||
!ctx_params.resolve_and_validate(ctx_params.model_path.empty() && ctx_params.diffusion_model_path.empty() ? METADATA : IMG_GEN) ||
!default_gen_params.resolve_and_validate(IMG_GEN,
ctx_params.lora_model_dir,
ctx_params.hires_upscalers_dir)) {
@@ -84,14 +85,19 @@ int main(int argc, const char** argv) {
LOG_VERBOSE("version: %s", version_string().c_str());
LOG_VERBOSE("%s", sd_get_system_info());
LOG_VERBOSE("%s", svr_params.to_string().c_str());
LOG_DEBUG("%s", svr_params.to_string().c_str());
LOG_DEBUG("%s", ctx_params.to_string().c_str());
LOG_DEBUG("%s", default_gen_params.to_string().c_str());
sd_ctx_params_t sd_ctx_params = ctx_params.to_sd_ctx_params_t(false);
SDCtxPtr sd_ctx(new_sd_ctx(&sd_ctx_params));
ServerModels models;
if (!ctx_params.model_path.empty() || !ctx_params.diffusion_model_path.empty()) {
const auto started = std::chrono::steady_clock::now();
models.context.reset(new_sd_ctx(&sd_ctx_params));
models.status["load_elapsed_seconds"] = std::chrono::duration<double>(std::chrono::steady_clock::now() - started).count();
}
if (sd_ctx == nullptr) {
if ((!ctx_params.model_path.empty() || !ctx_params.diffusion_model_path.empty()) && !models.context) {
LOG_ERROR("new_sd_ctx_t failed");
return 1;
}
@@ -104,7 +110,7 @@ int main(int argc, const char** argv) {
std::mutex upscaler_mutex;
AsyncJobManager async_job_manager;
ServerRuntime runtime = {
sd_ctx.get(),
models.context.get(),
&sd_ctx_mutex,
&svr_params,
&ctx_params,
@@ -114,8 +120,16 @@ int main(int argc, const char** argv) {
&upscaler_cache,
&upscaler_mutex,
&async_job_manager,
&models,
};
try {
initialize_server_models(runtime);
} catch (const std::exception& e) {
LOG_ERROR("model catalog initialization failed: %s", e.what());
return 1;
}
std::thread async_worker(async_job_worker, std::ref(runtime));
httplib::Server svr;
@@ -147,6 +161,7 @@ int main(int argc, const char** argv) {
register_openai_api_endpoints(svr, runtime);
register_sdapi_endpoints(svr, runtime);
register_sdcpp_api_endpoints(svr, runtime);
register_model_endpoints(svr, runtime);
LOG_INFO("listening on: http://%s:%d\n", svr_params.listen_ip.c_str(), svr_params.listen_port);
svr.listen(svr_params.listen_ip, svr_params.listen_port);
+664
View File
@@ -0,0 +1,664 @@
#include "model_catalog.h"
#include <algorithm>
#include <cctype>
#include <cmath>
#include <fstream>
#include <iomanip>
#include <set>
#include <sstream>
#include <stdexcept>
#include <vector>
#include "common/log.h"
#include "recipes_builtin.h"
namespace fs = std::filesystem;
struct ModelSlot {
const char* id;
const char* label;
std::vector<std::string> categories;
std::string SDContextParams::*field;
};
static const ModelSlot k_model_slots[] = {
{"model", "Checkpoint", {"checkpoints"}, &SDContextParams::model_path},
{"diffusion_model", "Diffusion model", {"diffusion_models"}, &SDContextParams::diffusion_model_path},
{"high_noise_diffusion_model", "High-noise diffusion model", {"diffusion_models"}, &SDContextParams::high_noise_diffusion_model_path},
{"uncond_diffusion_model", "Unconditional diffusion model", {"diffusion_models"}, &SDContextParams::uncond_diffusion_model_path},
{"clip_l", "CLIP-L", {"text_encoders"}, &SDContextParams::clip_l_path},
{"clip_g", "CLIP-G", {"text_encoders"}, &SDContextParams::clip_g_path},
{"clip_vision", "CLIP Vision", {"clip_vision"}, &SDContextParams::clip_vision_path},
{"t5xxl", "T5", {"text_encoders"}, &SDContextParams::t5xxl_path},
{"llm", "LLM Text Encoder", {"text_encoders"}, &SDContextParams::llm_path},
{"llm_vision", "LLM Vision Encoder", {"clip_vision"}, &SDContextParams::llm_vision_path},
{"tokenizer", "Tokenizer", {"tokenizers"}, &SDContextParams::tokenizer},
{"vae", "VAE", {"vae"}, &SDContextParams::vae_path},
{"audio_vae", "Audio VAE", {"vae"}, &SDContextParams::audio_vae_path},
{"audio_encoder", "Audio Encoder", {"text_encoders"}, &SDContextParams::audio_encoder_path},
{"embeddings_connectors", "Embeddings Connectors", {"text_encoders"}, &SDContextParams::embeddings_connectors_path},
{"tae", "TAE", {"vae", "vae_approx"}, &SDContextParams::taesd_path},
{"taesd", "TAE", {"vae_approx", "vae"}, &SDContextParams::taesd_path},
{"control_net", "ControlNet", {"controlnet"}, &SDContextParams::control_net_path},
{"ip_adapter", "IP-Adapter", {"ipadapter"}, &SDContextParams::ip_adapter_path},
{"motion_module", "Motion Module", {"motion_modules"}, &SDContextParams::motion_module_path},
{"photo_maker", "PhotoMaker", {"photomaker"}, &SDContextParams::photo_maker_path},
{"pulid_weights", "PuLID", {"pulid"}, &SDContextParams::pulid_weights_path},
};
static const ModelSlot* find_model_slot(const std::string& id) {
for (const auto& slot : k_model_slots) {
if (id == slot.id) {
return &slot;
}
}
return nullptr;
}
static bool matches_model_category(const ModelSlot& slot, const std::string& category) {
return std::find(slot.categories.begin(), slot.categories.end(), category) != slot.categories.end();
}
static void clear_model_paths(SDContextParams& params) {
for (const auto& slot : k_model_slots) {
(params.*slot.field).clear();
}
}
std::string model_path_key(const fs::path& path) {
#ifdef _WIN32
const auto value = path.generic_wstring();
const int length = LCMapStringEx(LOCALE_NAME_INVARIANT, LCMAP_LOWERCASE, value.data(),
static_cast<int>(value.size()), nullptr, 0, nullptr, nullptr, 0);
if (length > 0) {
std::wstring folded(length, L'\0');
LCMapStringEx(LOCALE_NAME_INVARIANT, LCMAP_LOWERCASE, value.data(),
static_cast<int>(value.size()), &folded[0], length, nullptr, nullptr, 0);
return fs::path(folded).generic_u8string();
}
#endif
return path.generic_u8string();
}
bool model_path_inside(const fs::path& root, const fs::path& path) {
auto parent = root.begin();
auto child = path.begin();
for (; parent != root.end(); ++parent, ++child) {
if (child == path.end() || model_path_key(*parent) != model_path_key(*child)) {
return false;
}
}
return child != path.end();
}
ModelRoot make_model_root(const fs::path& path, bool primary) {
ModelRoot root;
const auto absolute = fs::absolute(path);
std::error_code ec;
root.path = fs::weakly_canonical(absolute, ec);
if (ec)
root.path = absolute.lexically_normal();
root.primary = primary;
uint64_t hash = 14695981039346656037ULL;
for (unsigned char c : model_path_key(root.path)) {
hash ^= c;
hash *= 1099511628211ULL;
}
std::ostringstream id;
id << '@' << std::hex << std::setfill('0') << std::setw(16) << hash;
root.id = id.str();
root.label = root.path.filename().u8string() + " [" + root.id.substr(1, 8) + "]";
return root;
}
std::vector<ModelRoot> get_model_roots(const SDSvrParams& params) {
const auto directories = params.model_dirs.empty() ? std::vector<std::string>{"models"} : params.model_dirs;
std::vector<ModelRoot> roots;
std::set<std::string> seen;
for (size_t i = 0; i < directories.size(); ++i) {
auto root = make_model_root(fs::u8path(directories[i]), i == 0);
if (seen.insert(model_path_key(root.path)).second)
roots.push_back(std::move(root));
}
return roots;
}
std::vector<fs::path> model_category_directories(const ModelRoot& root, const std::string& category) {
std::vector<fs::path> directories;
if (!fs::exists(root.path))
return directories;
for (const auto& entry : fs::directory_iterator(root.path)) {
auto name = entry.path().filename().u8string();
std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); });
if (name == category || (category == "checkpoints" && name == "stable-diffusion") ||
(category == "loras" && name == "lora")) {
directories.push_back(entry.path());
}
}
std::sort(directories.begin(), directories.end(), [&](const fs::path& a, const fs::path& b) {
const bool a_standard = a.filename().u8string() == category;
const bool b_standard = b.filename().u8string() == category;
return a_standard != b_standard ? a_standard : a < b;
});
return directories;
}
static bool valid_identifier(const std::string& id) {
return !id.empty() && id.size() <= 128 &&
std::all_of(id.begin(), id.end(), [](unsigned char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.';
});
}
static void check_keys(const json& object, const std::set<std::string>& keys) {
if (!object.is_object()) {
throw std::runtime_error("expected an object");
}
for (auto it = object.begin(); it != object.end(); ++it) {
if (!keys.count(it.key())) {
throw std::runtime_error("unknown field: " + it.key());
}
}
}
static void apply_recipe_defaults(const json& defaults, SDGenerationParams& params) {
check_keys(defaults, {"width", "height", "steps", "cfg_scale", "strength", "distilled_guidance", "sampling_method", "scheduler", "custom_sigmas",
"video_frames", "fps", "flow_shift", "moe_boundary", "high_noise_steps", "high_noise_cfg_scale",
"high_noise_flow_shift", "high_noise_sampling_method", "high_noise_scheduler", "img_cfg_scale", "shifted_timestep"});
for (const char* key : {"width", "height", "steps", "video_frames", "fps", "high_noise_steps", "shifted_timestep"}) {
if (!defaults.contains(key)) {
continue;
}
if (!defaults[key].is_number_integer()) {
throw std::runtime_error(std::string(key) + " must be an integer");
}
const int64_t value = defaults[key].get<int64_t>();
const std::string field = key;
const bool dimension = field == "width" || field == "height";
const int64_t maximum = dimension || field == "video_frames" ? 4096 : field == "fps" ? 240
: 1000;
const int64_t minimum = dimension ? 64 : field == "shifted_timestep" ? 0
: 1;
if ((value < minimum || value > maximum) && !(field == "high_noise_steps" && value == -1)) {
throw std::runtime_error(std::string(key) + " is out of range");
}
if (std::string(key) == "width")
params.width = static_cast<int>(value);
if (std::string(key) == "height")
params.height = static_cast<int>(value);
if (std::string(key) == "steps")
params.sample_params.sample_steps = static_cast<int>(value);
if (field == "shifted_timestep")
params.sample_params.shifted_timestep = static_cast<int>(value);
if (field == "video_frames")
params.video_frames = static_cast<int>(value);
if (field == "fps")
params.fps = static_cast<int>(value);
if (field == "high_noise_steps")
params.high_noise_sample_params.sample_steps = static_cast<int>(value);
}
for (const char* key : {"cfg_scale", "strength", "moe_boundary", "img_cfg_scale", "distilled_guidance", "flow_shift", "high_noise_cfg_scale", "high_noise_flow_shift"}) {
if (!defaults.contains(key)) {
continue;
}
if (!defaults[key].is_number()) {
throw std::runtime_error(std::string(key) + " must be a number");
}
const float value = defaults[key].get<float>();
if (!std::isfinite(value) || value < 0 || value > ((std::string(key) == "strength" || std::string(key) == "moe_boundary") ? 1.f : 1000.f)) {
throw std::runtime_error(std::string(key) + " is out of range");
}
if (std::string(key) == "strength")
params.strength = value;
if (std::string(key) == "moe_boundary")
params.moe_boundary = value;
if (std::string(key) == "cfg_scale")
params.sample_params.guidance.txt_cfg = value;
if (std::string(key) == "img_cfg_scale")
params.sample_params.guidance.img_cfg = value;
if (std::string(key) == "distilled_guidance")
params.sample_params.guidance.distilled_guidance = value;
if (std::string(key) == "flow_shift")
params.sample_params.flow_shift = value;
if (std::string(key) == "high_noise_cfg_scale")
params.high_noise_sample_params.guidance.txt_cfg = value;
if (std::string(key) == "high_noise_flow_shift")
params.high_noise_sample_params.flow_shift = value;
}
if (defaults.contains("custom_sigmas")) {
const auto& sigmas = defaults.at("custom_sigmas");
if (!sigmas.is_array() || sigmas.size() == 1 || sigmas.size() > 1001)
throw std::runtime_error("custom_sigmas must be empty or contain 2-1001 values");
for (const auto& sigma : sigmas) {
if (!sigma.is_number() || !std::isfinite(sigma.get<float>()) || sigma.get<float>() < 0)
throw std::runtime_error("custom_sigmas must contain finite nonnegative numbers");
}
params.custom_sigmas = sigmas.get<std::vector<float>>();
}
for (const char* key : {"sampling_method", "high_noise_sampling_method"}) {
if (!defaults.contains(key))
continue;
auto& sample = std::string(key) == "sampling_method" ? params.sample_params : params.high_noise_sample_params;
const auto name = defaults.at(key).get<std::string>();
bool found = false;
for (int i = 0; i < SAMPLE_METHOD_COUNT; ++i) {
if (name == sd_sample_method_name(static_cast<sample_method_t>(i))) {
sample.sample_method = static_cast<sample_method_t>(i);
found = true;
break;
}
}
if (!found)
throw std::runtime_error(std::string("unknown ") + key);
}
for (const char* key : {"scheduler", "high_noise_scheduler"}) {
if (!defaults.contains(key))
continue;
auto& sample = std::string(key) == "scheduler" ? params.sample_params : params.high_noise_sample_params;
const auto name = defaults.at(key).get<std::string>();
bool found = false;
for (int i = 0; i < SCHEDULER_COUNT; ++i) {
if (name == sd_scheduler_name(static_cast<scheduler_t>(i)) ||
(name == "normal" && i == DISCRETE_SCHEDULER)) {
sample.scheduler = static_cast<scheduler_t>(i);
found = true;
break;
}
}
if (!found)
throw std::runtime_error(std::string("unknown ") + key);
}
}
static void apply_recipe_context(const json& settings, SDContextParams& params) {
check_keys(settings, {"vae_format", "model_args"});
if (settings.contains("vae_format")) {
const auto format = settings.at("vae_format").get<std::string>();
if (format != "auto" && format != "flux" && format != "sd3" && format != "flux2" && format != "wan")
throw std::runtime_error("unknown vae_format");
params.vae_format = format;
}
if (settings.contains("model_args")) {
params.model_args = settings.at("model_args").get<std::string>();
}
}
static const std::map<std::string, bool> k_recipe_features = {
{"img2img", true},
{"mask_image", true},
{"lora", true},
{"vae_tiling", true},
{"cache", true},
{"cache_dit", true},
{"preview", true},
{"flow_shift", false},
{"distilled_guidance", false},
{"shifted_timestep", false},
{"img_cfg", false},
{"slg", false},
{"clip_skip", false},
{"ref_images", false},
{"controlnet", false},
{"control_image", false},
{"ip_adapter", false},
{"init_image", false},
{"end_image", false},
{"control_frames", false},
{"vace", false},
{"high_noise", false},
{"cache_unet", false},
{"preview_tae", false},
};
static void validate_recipe_features(const json& recipe) {
for (const char* key : {"features", "disabled_features"}) {
if (!recipe.contains(key))
continue;
const auto& values = recipe.at(key);
if (!values.is_array() || values.size() > k_recipe_features.size())
throw std::runtime_error(std::string(key) + " must be an array of feature names");
std::set<std::string> seen;
for (const auto& value : values) {
const auto name = value.get<std::string>();
if (!k_recipe_features.count(name) || !seen.insert(name).second)
throw std::runtime_error("unknown or duplicate feature: " + name);
}
}
}
json resolve_recipe_features(const json& recipe) {
json features = k_recipe_features;
for (const auto& slot : recipe.at("slots")) {
const auto id = slot.at("id").get<std::string>();
if (id == "ip_adapter")
features["ip_adapter"] = true;
if (id == "control_net")
features["controlnet"] = true;
if (id == "high_noise_diffusion_model")
features["high_noise"] = true;
if (id == "tae" || id == "taesd")
features["preview_tae"] = true;
}
for (const auto& name : recipe.value("features", json::array()))
features[name.get<std::string>()] = true;
for (const auto& name : recipe.value("disabled_features", json::array()))
features[name.get<std::string>()] = false;
return features;
}
json model_generation_features(const ServerRuntime& runtime) {
if (!runtime.sd_ctx || runtime.models->active_recipe.is_null()) {
json features = k_recipe_features;
for (auto& feature : features)
feature = false;
return features;
}
json features = resolve_recipe_features(runtime.models->active_recipe);
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();
if (!runtime.ctx_params->pulid_weights_path.empty())
features["slg"] = false;
return features;
}
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"});
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");
}
if (recipe.contains("description") && !recipe["description"].is_string()) {
throw std::runtime_error("description must be a string");
}
if (recipe.contains("model_url")) {
const auto url = recipe.at("model_url").get<std::string>();
const std::string prefix = "https://huggingface.co/";
if (url.size() <= prefix.size() || url.size() > 2048 || url.rfind(prefix, 0) != 0 ||
std::any_of(url.begin(), url.end(), [](unsigned char c) { return std::isspace(c) || std::iscntrl(c); })) {
throw std::runtime_error("model_url must be a Hugging Face HTTPS URL");
}
}
const auto& slots = recipe.at("slots");
if (!slots.is_array() || slots.empty() || slots.size() > 32) {
throw std::runtime_error("slots must be a nonempty array of at most 32 entries");
}
std::set<std::string> ids;
bool has_required_model = false;
for (const auto& slot : slots) {
check_keys(slot, {"id", "label", "category", "required"});
const auto id = slot.at("id").get<std::string>();
const auto* spec = find_model_slot(id);
if (!spec || !ids.insert(id == "taesd" ? "tae" : id).second ||
(slot.contains("category") && !matches_model_category(*spec, slot.at("category").get<std::string>())) ||
(slot.contains("label") && slot.at("label").get<std::string>().empty()) || !slot.at("required").is_boolean()) {
throw std::runtime_error("invalid or duplicate model slot: " + id);
}
has_required_model |= (id == "model" || id == "diffusion_model") && slot["required"].get<bool>();
}
if (!has_required_model) {
throw std::runtime_error("a required model or diffusion_model slot is needed");
}
if (recipe.contains("required_any")) {
const auto& groups = recipe.at("required_any");
if (!groups.is_array() || groups.size() > 32)
throw std::runtime_error("required_any must be an array of at most 32 slot groups");
for (const auto& group : groups) {
if (!group.is_array() || group.size() < 2 || group.size() > slots.size())
throw std::runtime_error("required_any groups need at least two declared slots");
std::set<std::string> seen;
for (const auto& value : group) {
const auto id = value.get<std::string>();
if (!seen.insert(id).second || std::none_of(slots.begin(), slots.end(), [&](const json& slot) { return slot.at("id") == id; }))
throw std::runtime_error("unknown or duplicate required_any slot: " + id);
}
}
}
validate_recipe_features(recipe);
SDContextParams context;
apply_recipe_context(recipe.value("context", json::object()), context);
SDGenerationParams defaults;
apply_recipe_defaults(recipe.value("defaults", json::object()), defaults);
}
static json normalize_recipe(json recipe) {
validate_recipe(recipe);
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>());
slot["label"] = slot.value("label", std::string(spec.label));
slot["category"] = slot.value("category", spec.categories.front());
slot["categories"] = spec.categories;
}
return recipe;
}
static bool supported_model_file(const fs::path& path, const std::string& category) {
std::string ext = path.extension().u8string();
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c) { return std::tolower(c); });
if (category == "tokenizers") {
return ext == ".json";
}
return ext == ".gguf" || ext == ".safetensors" || ext == ".sft" ||
ext == ".ckpt" || ext == ".pt" || ext == ".pth" || ext == ".bin";
}
void refresh_model_catalog(ServerRuntime& runtime) {
auto& models = *runtime.models;
std::lock_guard<std::mutex> lock(models.catalog_mutex);
std::map<std::string, json> recipes;
std::map<std::string, ModelFile> files;
json errors = json::array();
auto report = [&](const std::string& file, const std::string& message) {
errors.push_back({{"file", file}, {"message", message}});
};
for (const char* builtin : k_builtin_recipes) {
auto recipe = normalize_recipe(json::parse(builtin));
const auto id = recipe.at("id").get<std::string>();
recipe["source"] = "builtin";
recipes[id] = std::move(recipe);
}
std::set<std::string> seen_files;
for (const auto& source : get_model_roots(*runtime.svr_params)) {
const auto& root = source.path;
auto display_path = [&](const fs::path& path) {
return source.id + "/" + path.lexically_relative(root).generic_u8string();
};
const fs::path recipe_dir = root / "recipes";
std::map<std::string, std::vector<json>> user_recipes;
try {
if (fs::exists(recipe_dir)) {
if (!model_path_inside(root, fs::canonical(recipe_dir)))
throw std::runtime_error("recipes directory escapes model directory");
for (const auto& entry : fs::directory_iterator(recipe_dir)) {
const std::string display = display_path(entry.path());
if (entry.path().extension() != ".json")
continue;
try {
if (!entry.is_regular_file() || !model_path_inside(root, fs::canonical(entry.path())))
throw std::runtime_error("recipe must be a file inside the model directory");
if (entry.file_size() > 1024 * 1024)
throw std::runtime_error("recipe exceeds 1 MiB");
std::ifstream input(entry.path(), std::ios::binary);
json recipe;
input >> recipe;
recipe = normalize_recipe(std::move(recipe));
recipe["source"] = "user";
recipe["file"] = entry.path().filename().u8string();
recipe["root"] = source.id;
const auto id = recipe.at("id").get<std::string>();
user_recipes[id].push_back(std::move(recipe));
} catch (const std::exception& e) {
report(display, e.what());
}
}
}
} catch (const std::exception& e) {
report(display_path(recipe_dir), e.what());
}
for (auto& item : user_recipes) {
if (item.second.size() != 1) {
for (const auto& recipe : item.second)
report(source.id + "/recipes/" + recipe["file"].get<std::string>(), "duplicate user recipe id: " + item.first);
continue;
}
const auto existing = recipes.find(item.first);
if (existing != recipes.end()) {
if (existing->second["source"] == "user") {
LOG_WARN("recipe '%s' from %s is shadowed by an earlier model directory", item.first.c_str(), source.label.c_str());
continue;
}
LOG_INFO("user recipe '%s' from %s overrides the builtin", item.first.c_str(), source.label.c_str());
}
recipes[item.first] = std::move(item.second.front());
}
std::set<std::string> categories;
for (const auto& slot : k_model_slots)
categories.insert(slot.categories.begin(), slot.categories.end());
for (const auto& category : categories) {
try {
for (const auto& directory : model_category_directories(source, category)) {
if (!model_path_inside(root, fs::canonical(directory))) {
report(display_path(directory), "category directory escapes model directory");
continue;
}
for (fs::recursive_directory_iterator it(directory), end; it != end; ++it) {
std::error_code path_error;
const fs::path canonical = fs::canonical(it->path(), path_error);
if (path_error || !model_path_inside(root, canonical)) {
it.disable_recursion_pending();
report(display_path(it->path()), path_error ? path_error.message() : "path escapes model directory");
continue;
}
if (!it->is_regular_file() || !supported_model_file(it->path(), category) ||
!seen_files.insert(model_path_key(canonical)).second)
continue;
const auto relative = it->path().lexically_relative(root).generic_u8string();
const auto id = source.primary ? relative : source.id + "/" + relative;
files[id] = {category, it->path().lexically_relative(directory).generic_u8string(), relative, source};
}
}
} catch (const std::exception& e) {
report(source.id + "/" + category, e.what());
}
}
}
std::map<std::pair<std::string, std::string>, size_t> names;
for (const auto& entry : files)
++names[{entry.second.category, entry.second.name}];
for (auto& entry : files) {
auto& file = entry.second;
if (names[{file.category, file.name}] > 1)
file.name = (file.root.path / fs::u8path(file.relative_path)).generic_u8string();
}
models.recipes = std::move(recipes);
models.files = std::move(files);
models.catalog_errors = std::move(errors);
}
json model_status(ServerRuntime& runtime) {
std::lock_guard<std::mutex> lock(runtime.models->status_mutex);
auto& models = *runtime.models;
json status = models.status;
if (status["state"] == "loading" && !status["load_elapsed_seconds"].is_null()) {
status["load_elapsed_seconds"] = std::chrono::duration<double>(std::chrono::steady_clock::now() - models.load_started_at).count();
}
return status;
}
void initialize_server_models(ServerRuntime& runtime) {
auto& models = *runtime.models;
models.base_context = *runtime.ctx_params;
clear_model_paths(models.base_context);
models.base_defaults = *runtime.default_gen_params;
if (runtime.sd_ctx) {
models.status["state"] = "ready";
models.status["origin"] = "command_line";
json features = json::array();
for (const auto& feature : k_recipe_features)
features.push_back(feature.first);
models.active_recipe = {
{"id", "@command-line"},
{"name", "Command-line model (all features)"},
{"source", "command_line"},
{"description", "Uses the loaded command-line model and its generation defaults."},
{"slots", json::array()},
{"defaults", json::object()},
{"features", features},
};
models.active_recipe["effective_features"] = resolve_recipe_features(models.active_recipe);
models.status["runtime_recipe"] = models.active_recipe;
}
refresh_model_catalog(runtime);
}
void prepare_model_load(ServerRuntime& runtime, const json& body, SDContextParams& context, SDGenerationParams& defaults, json* loaded_recipe) {
check_keys(body, {"recipe", "models"});
const auto recipe_id = body.at("recipe").get<std::string>();
auto selection = body.at("models");
if (!selection.is_object())
throw std::runtime_error("models must be an object");
auto& models = *runtime.models;
std::lock_guard<std::mutex> lock(models.catalog_mutex);
const auto found = models.recipes.find(recipe_id);
if (found == models.recipes.end())
throw std::runtime_error("unknown recipe");
const auto& recipe = found->second;
if (loaded_recipe)
*loaded_recipe = recipe;
context = models.base_context;
defaults = models.base_defaults;
apply_recipe_context(recipe.value("context", json::object()), context);
apply_recipe_defaults(recipe.value("defaults", json::object()), defaults);
std::set<std::string> allowed;
for (const auto& slot : recipe.at("slots")) {
const auto id = slot.at("id").get<std::string>();
allowed.insert(id);
if (id == "tae" || id == "taesd") {
const std::string alias = id == "tae" ? "taesd" : "tae";
allowed.insert(alias);
if (selection.contains(alias)) {
if (selection.contains(id))
throw std::runtime_error("specify only one of tae and taesd");
selection[id] = selection.at(alias);
}
}
const std::string file_id = selection.value(id, std::string());
if (file_id.empty()) {
if (slot.at("required").get<bool>())
throw std::runtime_error("missing required model: " + id);
continue;
}
const auto file = models.files.find(file_id);
if (file == models.files.end() || !matches_model_category(*find_model_slot(id), file->second.category)) {
throw std::runtime_error("unknown model or incorrect category: " + id);
}
const auto& root = file->second.root.path;
const fs::path path = fs::canonical(root / fs::u8path(file->second.relative_path));
if (!model_path_inside(root, path) || !fs::is_regular_file(path)) {
throw std::runtime_error("model file must remain inside the model directory: " + id);
}
context.*find_model_slot(id)->field = path.u8string();
}
for (auto it = selection.begin(); it != selection.end(); ++it) {
if (!allowed.count(it.key()))
throw std::runtime_error("unknown model slot: " + it.key());
}
for (const auto& group : recipe.value("required_any", json::array())) {
if (std::none_of(group.begin(), group.end(), [&](const json& value) {
return !selection.value(value.get<std::string>(), std::string()).empty();
})) {
throw std::runtime_error("select at least one model from: " + group.dump());
}
}
if (!context.resolve_and_validate(IMG_GEN))
throw std::runtime_error("invalid model context parameters");
}
+56
View File
@@ -0,0 +1,56 @@
#ifndef __SD_EXAMPLES_SERVER_MODEL_CATALOG_H__
#define __SD_EXAMPLES_SERVER_MODEL_CATALOG_H__
#include <chrono>
#include <map>
#include <mutex>
#include <shared_mutex>
#include <string>
#include "runtime.h"
struct ModelRoot {
std::filesystem::path path;
std::string id;
std::string label;
bool primary = false;
};
struct ModelFile {
std::string category;
std::string name;
std::string relative_path;
ModelRoot root;
};
std::string model_path_key(const std::filesystem::path& path);
bool model_path_inside(const std::filesystem::path& root, const std::filesystem::path& path);
ModelRoot make_model_root(const std::filesystem::path& path, bool primary);
std::vector<ModelRoot> get_model_roots(const SDSvrParams& params);
std::vector<std::filesystem::path> model_category_directories(const ModelRoot& root, const std::string& category);
struct ServerModels {
std::shared_mutex runtime_mutex;
std::mutex catalog_mutex;
std::mutex status_mutex;
SDCtxPtr context;
SDContextParams base_context;
SDGenerationParams base_defaults;
std::map<std::string, json> recipes;
std::map<std::string, ModelFile> files;
std::chrono::steady_clock::time_point load_started_at;
json active_recipe = nullptr;
json catalog_errors = json::array();
json status = {{"state", "empty"}, {"recipe", nullptr}, {"models", json::object()}, {"error", nullptr}, {"load_elapsed_seconds", nullptr}};
};
void refresh_model_catalog(ServerRuntime& runtime);
json model_status(ServerRuntime& runtime);
void initialize_server_models(ServerRuntime& runtime);
json resolve_recipe_features(const json& recipe);
json model_generation_features(const ServerRuntime& runtime);
void prepare_model_load(ServerRuntime& runtime, const json& body, SDContextParams& context, SDGenerationParams& defaults, json* loaded_recipe = nullptr);
#endif
+79
View File
@@ -0,0 +1,79 @@
# Built-in recipe policy
Read these rules before adding or changing JSON files in this directory. A recipe
represents a supported model-loading and capability configuration, not a saved
prompt, a style preset, or a collection of sampling settings.
## Required rules
1. **Community editing adapters get a separate recipe.** When the base model
does not natively support instruction/reference-image editing and needs a
community LoRA to gain it, provide a dedicated editing recipe. Keep editing
inputs disabled in the base recipe. Examples: `anima` / `anima-edit` and
`krea2-raw` / `krea2-raw-edit`.
2. **Split different task weights; share native multi-task weights.** If editing
and text-to-image use different generation checkpoints, use separate recipes.
If the same checkpoint natively supports both, use one recipe. Examples:
`boogu-image` / `boogu-image-edit` are separate; `flux2-dev` and
`qwen-image-21` each cover both generation and editing. Apply the same rule
to video: separate HunyuanVideo 1.5 T2V/I2V and Wan 2.2 A14B T2V/I2V weights,
but share one `wan22-ti2v-5b` recipe for its native T2V/I2V checkpoint.
3. **Capability-extending weights get a separate recipe.** ControlNet,
IP-Adapter, PuLID, PhotoMaker, motion modules, and similar adapters that add
a distinct conditioning or generation capability belong in dedicated
recipes. Make the extension's model slots required when the schema provides
them. Do not add these as optional slots on the ordinary base recipe.
4. **A LoRA alone is not a reason to split.** If adding a LoRA does not change
supported functionality, reuse the base recipe. Style, character, quality,
acceleration and distillation LoRAs do not get dedicated built-in recipes.
Users select the LoRA and change steps, guidance or other generation settings
separately. Standalone base/turbo/distilled checkpoints still follow the task,
component and layer-count rules; a different filename alone is not a reason
for another recipe.
5. **Keep native components in the model's recipe.** Native vision encoders,
projectors, connectors and other components required by the model belong in
its existing recipe. Splitting a native component into another file does not
make it a capability-extending adapter under rule 3. For example, Qwen vision
projectors and LLaDA/LTX connectors do not get separate recipes. A slot may be
optional when its weights can be bundled in another selected file.
6. **Layer-count variants share the family recipe.** Adding, repeating, removing
or pruning model layers does not justify a separate recipe when the runtime
supports the variant with the same components and capabilities. This also
applies to distilled checkpoints whose relevant difference is reduced depth.
Use `flux1-dev` for FLUX.1 Lite, `sdxl-checkpoint` for SSD-1B and Segmind Vega,
and the corresponding SD 1/2 checkpoint recipes for Tiny SD/Tiny SD2. Only an
independent difference in task, required component compatibility or latent
format can justify splitting such a variant; document that difference.
## Interpretation and review
- Instruction/reference-image editing is distinct from generic image-to-image
denoising. The shared `img2img` capability does not imply native editing.
- Native components and external capability adapters are distinguished by their
role, not by whether their weights are stored in a separate file. LLaDA's
SigVQ-containing connectors remain in its shared generation/editing recipe.
- Do not merge models solely because they descend from the same family. Flex.2
has different native inpainting/control conditioning, beyond its layer count
([model card](https://huggingface.co/ostris/Flex.2-preview)). SDXS uses an
adjusted AutoEncoderTiny and a one-step model, rather than only a shallower
ordinary SD checkpoint ([runtime guide](../../../docs/distilled_sd.md#sdxs-512-dreamshaper)).
Their native components stay in their own model recipe.
- Quantization, file format, prompt wording, seeds, step counts and CFG values
are not grounds for another recipe. Differences in required components,
latent formats or supported capabilities must be documented.
- Evaluate what an adapter actually changes. Do not infer new functionality just
from a LoRA's name or from the presence of another weight file. In particular,
do not create an Omni-Kontext recipe solely because a LoRA is selected.
- A recipe does not load generation LoRAs automatically. Community-edit recipes
must explain that the matching LoRA is required and must be selected separately.
- Only advertise features implemented by the current runtime. Check the relevant
model documentation and code, and match each task's slots and feature flags to
its weights. Record any unverified inference paths in the change description.
- Before submitting, validate all recipe JSON, inspect the embedded recipe API,
and check that task-specific inputs appear only on the appropriate recipes.
A catalog check is not a substitute for model-generation verification.
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.
+44
View File
@@ -0,0 +1,44 @@
{
"schema_version": 1,
"id": "anima-edit",
"name": "Anima Edit (LoRA)",
"description": "Requires a matching community editing LoRA. Apply it after loading and supply reference images; uses the cosmos_reference preset.",
"model_url": "https://huggingface.co/circlestone-labs/Anima",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 0.6B Base Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"scheduler": "discrete",
"cfg_scale": 4,
"steps": 20,
"width": 1024,
"height": 1024
},
"features": [
"flow_shift",
"ref_images"
]
}
+42
View File
@@ -0,0 +1,42 @@
{
"schema_version": 1,
"id": "anima",
"name": "Anima",
"description": "Supports all Anima variants supported by sd.cpp. Use a compatible Qwen Image VAE or TAE; adjust generation settings for distilled variants.",
"model_url": "https://huggingface.co/circlestone-labs/Anima",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 0.6B Base Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"cfg_scale": 4,
"steps": 20,
"width": 1024,
"height": 1024
},
"features": [
"flow_shift"
]
}
@@ -0,0 +1,44 @@
{
"schema_version": 1,
"id": "animatediff-sd15",
"name": "AnimateDiff SD 1.5",
"description": "Use Video Generation with an SD 1.5 checkpoint and compatible motion module.",
"model_url": "https://huggingface.co/guoyww/animatediff",
"slots": [
{
"id": "model",
"required": true
},
{
"id": "motion_module",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"defaults": {
"sampling_method": "euler",
"video_frames": 8,
"fps": 8,
"cfg_scale": 7.5,
"steps": 20,
"width": 512,
"height": 512
},
"features": [
"cache_unet",
"clip_skip",
"init_image"
],
"disabled_features": [
"cache_dit",
"img2img",
"mask_image"
]
}
@@ -0,0 +1,50 @@
{
"schema_version": 1,
"id": "boogu-image-edit",
"name": "Boogu Image Edit",
"description": "Supply reference images. The separate vision projector is needed only when it is not bundled in the text encoder.",
"model_url": "https://huggingface.co/Boogu/Boogu-Image-0.1-Edit",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 8B Text Encoder"
},
{
"id": "llm_vision",
"required": false,
"label": "Vision projector (required for separate GGUF text encoders)"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 5,
"steps": 20,
"width": 1024,
"height": 1024,
"img_cfg_scale": 1,
"sampling_method": "euler"
},
"features": [
"flow_shift",
"img_cfg",
"ref_images"
]
}
+41
View File
@@ -0,0 +1,41 @@
{
"schema_version": 1,
"id": "boogu-image",
"name": "Boogu Image Base",
"model_url": "https://huggingface.co/Boogu/Boogu-Image-0.1-Base",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 8B Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 4,
"steps": 20,
"width": 1024,
"height": 1024,
"sampling_method": "euler"
},
"features": [
"flow_shift"
]
}
@@ -0,0 +1,32 @@
{
"schema_version": 1,
"id": "chroma-radiance",
"name": "Chroma Radiance",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "t5xxl",
"required": true
}
],
"defaults": {
"sampling_method": "euler",
"cfg_scale": 3.5,
"steps": 20,
"width": 1024,
"height": 1024
},
"features": [
"flow_shift",
"slg"
],
"disabled_features": [
"preview",
"vae_tiling"
]
}
+41
View File
@@ -0,0 +1,41 @@
{
"schema_version": 1,
"id": "chroma",
"name": "Chroma",
"model_url": "https://huggingface.co/lodestones/Chroma1-HD",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "t5xxl",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"cfg_scale": 3,
"steps": 20,
"width": 1024,
"height": 1024
},
"features": [
"flow_shift",
"slg"
]
}
+36
View File
@@ -0,0 +1,36 @@
{
"schema_version": 1,
"id": "cosxl-edit",
"name": "CosXL Edit",
"description": "Supply the image to edit as a reference image.",
"model_url": "https://huggingface.co/stabilityai/cosxl",
"slots": [
{
"id": "model",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"defaults": {
"img_cfg_scale": 1.5,
"sampling_method": "euler_a",
"scheduler": "karras"
},
"features": [
"cache_unet",
"clip_skip",
"img_cfg",
"ref_images",
"shifted_timestep"
],
"disabled_features": [
"cache_dit"
]
}
+124
View File
@@ -0,0 +1,124 @@
# Built-in generation defaults
Reviewed against model-publisher cards and reference implementations on 2026-10-20.
These are starting values, not limits on supported resolutions or generation settings.
Built-in step defaults above 20 are reduced to 20; the cited sources may recommend more steps.
Users can still choose a higher step count.
When several aspect ratios or ranges are published, the selected example or square bucket is noted below.
A dash means no recipe override: the available primary sources did not establish a fixed value, or the size depends on the input image.
Ordinary DiT recipes explicitly use Euler; PiD retains its LCM sampling path.
Guidance-distilled models use `cfg_scale: 1` to disable external CFG in sd.cpp. FLUX embedded guidance is stored separately as `distilled_guidance`.
For community edit adapters and other optional extensions, these are the base-model settings unless the source specifies otherwise.
| Recipe | CFG | Steps | Width × height | Source / notes |
| --- | --- | --- | --- | --- |
| `anima` | 4 | 20 | 1024 × 1024 | [Source](https://huggingface.co/circlestone-labs/Anima) The official ranges are 30–50 steps / CFG 4–5; CFG 4 selects the lower end. Edit uses the same base; tune the chosen editing LoRA separately. |
| `anima-edit` | 4 | 20 | 1024 × 1024 | [Source](https://huggingface.co/circlestone-labs/Anima) The official ranges are 30–50 steps / CFG 4–5; CFG 4 selects the lower end. Edit uses the same base; tune the chosen editing LoRA separately. |
| `animatediff-sd15` | 7.5 | 20 | 512 × 512 | [Source](https://raw.githubusercontent.com/guoyww/AnimateDiff/main/configs/prompts/1_animate/1_3_animate_ToonYou.yaml) |
| `boogu-image` | 4 | 20 | 1024 × 1024 | [Source](https://huggingface.co/Boogu/Boogu-Image-0.1-Base) |
| `boogu-image-edit` | 5 | 20 | 1024 × 1024 | [Source](https://huggingface.co/Boogu/Boogu-Image-0.1-Edit) |
| `chroma` | 3 | 20 | 1024 × 1024 | [Source](https://huggingface.co/lodestones/Chroma1-HD/raw/main/README.md) |
| `chroma-radiance` | 3.5 | 20 | 1024 × 1024 | [Source](https://huggingface.co/lodestones/Chroma1-Radiance/raw/main/inference_reference_code.py) |
| `cosxl-edit` | — | — | — | [Source](https://huggingface.co/stabilityai/cosxl) No numerical generation defaults in the author’s model card; previous test values were removed. |
| `ernie-image` | 4 | 20 | 1024 × 1024 | [Source](https://huggingface.co/baidu/ERNIE-Image) |
| `ernie-image-turbo` | 1 | 8 | 1024 × 1024 | [Source](https://huggingface.co/baidu/ERNIE-Image-Turbo) |
| `flex2` | 1 | 20 | 1024 × 1024 | [Source](https://huggingface.co/ostris/Flex.2-preview) |
| `flux1-canny` | 1 | 20 | 1024 × 1024 | [Source](https://huggingface.co/black-forest-labs/FLUX.1-Canny-dev) |
| `flux1-depth` | 1 | 20 | 1024 × 1024 | [Source](https://huggingface.co/black-forest-labs/FLUX.1-Depth-dev) |
| `flux1-dev` | 1 | 20 | 1024 × 1024 | [Source](https://huggingface.co/black-forest-labs/FLUX.1-dev) |
| `flux1-fill` | 1 | 20 | 1232 × 1632 | [Source](https://huggingface.co/black-forest-labs/FLUX.1-Fill-dev) |
| `flux1-kontext` | 1 | 20 | — | [Source](https://raw.githubusercontent.com/huggingface/diffusers/main/src/diffusers/pipelines/flux/pipeline_flux_kontext.py) Output size normally follows the reference image; no fixed size is imposed by this recipe. |
| `flux1-pulid` | 1 | 20 | 1024 × 1024 | [Source](https://huggingface.co/black-forest-labs/FLUX.1-dev) FLUX.1-dev defaults; identity conditioning is provided by PuLID. |
| `flux1-schnell` | 1 | 4 | 1024 × 1024 | [Source](https://huggingface.co/black-forest-labs/FLUX.1-schnell) |
| `flux2-dev` | 1 | 20 | 1024 × 1024 | [Source](https://huggingface.co/black-forest-labs/FLUX.2-dev) |
| `flux2-klein-4b` | 1 | 4 | 1024 × 1024 | [Source](https://huggingface.co/black-forest-labs/FLUX.2-klein-4B) |
| `flux2-klein-9b` | 1 | 4 | 1024 × 1024 | [Source](https://huggingface.co/black-forest-labs/FLUX.2-klein-9B) |
| `flux2-klein-base-4b` | 4 | 20 | 1024 × 1024 | [Source](https://huggingface.co/black-forest-labs/FLUX.2-klein-base-4B) |
| `flux2-klein-base-9b` | 4 | 20 | 1024 × 1024 | [Source](https://huggingface.co/black-forest-labs/FLUX.2-klein-base-9B) |
| `flux3-klein` | 1 | 16 | 1024 × 1024 | [Source](https://raw.githubusercontent.com/black-forest-labs/flux-3-image-klein/main/README.md) |
| `flux3-klein-base` | 4 | 20 | 1024 × 1024 | [Source](https://raw.githubusercontent.com/black-forest-labs/flux-3-image-klein/main/src/flux3/pipeline.py) |
| `hidream-o1-dev` | 1 | 20 | 2048 × 2048 | [Source](https://huggingface.co/HiDream-ai/HiDream-O1-Image-Dev) |
| `hunyuan-video-15-i2v` | 6 | 20 | 1280 × 720 | [Source](https://huggingface.co/tencent/HunyuanVideo-1.5) 720p base I2V defaults; CFG/step-distilled and 480p checkpoints use different settings. |
| `hunyuan-video-15-t2v` | 6 | 20 | 1280 × 720 | [Source](https://huggingface.co/tencent/HunyuanVideo-1.5) 720p base T2V defaults; CFG-distilled / 480p checkpoints need their published settings. |
| `ideogram4` | 7 | 20 | 1024 × 1024 | [Source](https://raw.githubusercontent.com/ideogram-oss/ideogram4/main/docs/inference.md) Constant-guidance pipeline defaults; this recipe does not reproduce the CLI’s per-step guidance schedule. |
| `instruct-pix2pix` | — | 10 | — | [Source](https://huggingface.co/timbrooks/instruct-pix2pix) Official quick-start example; output size follows the input image. |
| `iris-3b` | 3 | 20 | 1024 × 1024 | [Source](https://huggingface.co/speridlabs/iris-3b) |
| `krea2-raw` | 3.5 | 20 | 1024 × 1024 | [Source](https://huggingface.co/krea/Krea-2-Raw) Base model defaults; edit requires a matching community LoRA with its own settings. |
| `krea2-raw-edit` | 3.5 | 20 | 1024 × 1024 | [Source](https://huggingface.co/krea/Krea-2-Raw) Base model defaults; edit requires a matching community LoRA with its own settings. |
| `krea2-turbo` | 1 | 8 | 2048 × 2048 | [Source](https://huggingface.co/krea/Krea-2-Turbo) Official CLI example; edit requires a matching community LoRA. |
| `krea2-turbo-edit` | 1 | 8 | 2048 × 2048 | [Source](https://huggingface.co/krea/Krea-2-Turbo) Official CLI example; edit requires a matching community LoRA. |
| `lens` | 5 | 20 | 1440 × 1440 | [Source](https://huggingface.co/microsoft/Lens) Official model card: RL model, default base_resolution 1440. |
| `lens-turbo` | 1 | 4 | 1440 × 1440 | [Source](https://huggingface.co/microsoft/Lens-Turbo) Official model card: 4-step distilled model, default base_resolution 1440. |
| `lingbot-video-dense` | 3 | 20 | 832 × 480 | [Source](https://huggingface.co/robbyant/lingbot-video-dense-1.3b) Official first-stage inference example; optional refiner is separate. |
| `lingbot-video-moe` | 3 | 20 | 832 × 480 | [Source](https://huggingface.co/robbyant/lingbot-video-moe-30b-a3b) Official inference example. |
| `llada-image` | 5 | 20 | 1024 × 1024 | [Source](https://huggingface.co/inclusionAI/LLaDA-Image) |
| `llada-image-turbo` | 1 | 4 | 1024 × 1024 | [Source](https://huggingface.co/inclusionAI/LLaDA-Image-Turbo) |
| `longcat-image` | 4 | 20 | 1344 × 768 | [Source](https://huggingface.co/meituan-longcat/LongCat-Image) |
| `longcat-image-edit` | 4.5 | 20 | — | [Source](https://huggingface.co/meituan-longcat/LongCat-Image-Edit) Reference-image-dependent output size; no fixed size is imposed. |
| `ltx23-dev` | 3 | 20 | 768 × 512 | [Source](https://raw.githubusercontent.com/Lightricks/LTX-2/main/packages/ltx-pipelines/src/ltx_pipelines/utils/constants.py) Official single-stage parameters, before optional latent upscaling. |
| `ltx23-distilled` | 1 | 8 | 768 × 512 | [Source](https://raw.githubusercontent.com/Lightricks/LTX-2/main/packages/ltx-pipelines/src/ltx_pipelines/utils/constants.py) Model card specifies 8 steps / CFG 1; size uses official pipeline’s first stage. |
| `ltx25-dev` | 3 | 20 | 768 × 512 | [Source](https://raw.githubusercontent.com/Lightricks/LTX-2/main/packages/ltx-pipelines/src/ltx_pipelines/utils/constants.py) Official non-distilled pipeline parameters for model versions >= 2.4, before optional upscaling. |
| `mage-flow` | 5 | 20 | 1024 × 1024 | [Source](https://huggingface.co/microsoft/Mage-Flow) |
| `mage-flow-edit` | 5 | 20 | 1024 × 1024 | [Source](https://huggingface.co/microsoft/Mage-Flow) |
| `mage-flow-turbo` | 1 | 4 | 1024 × 1024 | [Source](https://huggingface.co/microsoft/Mage-Flow) |
| `ming-image` | 1 | 12 | 2048 × 2048 | [Source](https://huggingface.co/inclusionAI/Ming-Image-0.1-Design) |
| `minimax-h3-fl2va` | 1 | — | 1344 × 768 | [Source](https://huggingface.co/docs/diffusers/main/en/api/pipelines/minimax_h3) Guidance-distilled; official 768p canvas. No fixed step count is prescribed here; the upstream API counts sigma grid points rather than sd.cpp denoising steps. |
| `minimax-h3-ref2va` | 1 | — | 1344 × 768 | [Source](https://huggingface.co/docs/diffusers/main/en/api/pipelines/minimax_h3) Guidance-distilled; official 768p canvas. No fixed step count is prescribed here; the upstream API counts sigma grid points rather than sd.cpp denoising steps. |
| `minit2i` | 2.5 | 20 | 512 × 512 | [Source](https://huggingface.co/MiniT2I/MiniT2I/raw/main/pipeline.py) B/16 example and native model size; L/16 uses CFG 6. |
| `moss-image-base` | 3 | 20 | 1024 × 1024 | [Source](https://huggingface.co/OpenMOSS-Team/MOSS-Image-Base) |
| `moss-image-sft` | 3 | 20 | 1024 × 1024 | [Source](https://huggingface.co/OpenMOSS-Team/MOSS-Image-SFT) |
| `nitrosd-realism` | 1 | 1 | 1024 × 1024 | [Source](https://huggingface.co/ChenDY/NitroFusion) |
| `ovis-image` | 5 | 20 | 1024 × 1024 | [Source](https://huggingface.co/ATH-MaaS/Ovis-Image-7B) |
| `pid` | 1 | 4 | 2048 × 2048 | [Source](https://huggingface.co/nvidia/PiD) Standalone PiD decoding at 2K; guidance is handled by the upstream generator. |
| `pid-15` | 1 | 4 | 2048 × 2048 | [Source](https://huggingface.co/nvidia/PiD) Standalone PiD decoding at 2K; guidance is handled by the upstream generator. |
| `pid-15-flux2` | 1 | 4 | 2048 × 2048 | [Source](https://huggingface.co/nvidia/PiD) Standalone PiD decoding at 2K; guidance is handled by the upstream generator. |
| `pid-15-qwen-image` | 1 | 4 | 2048 × 2048 | [Source](https://huggingface.co/nvidia/PiD) Standalone PiD decoding at 2K; guidance is handled by the upstream generator. |
| `pid-15-sd3` | 1 | 4 | 2048 × 2048 | [Source](https://huggingface.co/nvidia/PiD) Standalone PiD decoding at 2K; guidance is handled by the upstream generator. |
| `pid-flux2` | 1 | 4 | 2048 × 2048 | [Source](https://huggingface.co/nvidia/PiD) Standalone PiD decoding at 2K; guidance is handled by the upstream generator. |
| `pid-qwen-image` | 1 | 4 | 2048 × 2048 | [Source](https://huggingface.co/nvidia/PiD) Standalone PiD decoding at 2K; guidance is handled by the upstream generator. |
| `pid-sd3` | 1 | 4 | 2048 × 2048 | [Source](https://huggingface.co/nvidia/PiD) Standalone PiD decoding at 2K; guidance is handled by the upstream generator. |
| `pixart-alpha-1024` | 4.5 | 20 | 1024 × 1024 | [Source](https://raw.githubusercontent.com/huggingface/diffusers/main/src/diffusers/pipelines/pixart_alpha/pipeline_pixart_alpha.py) |
| `pixart-alpha-512` | 4.5 | 20 | 512 × 512 | [Source](https://raw.githubusercontent.com/huggingface/diffusers/main/src/diffusers/pipelines/pixart_alpha/pipeline_pixart_alpha.py) |
| `pixart-sigma` | 4.5 | 20 | 1024 × 1024 | [Source](https://raw.githubusercontent.com/huggingface/diffusers/main/src/diffusers/pipelines/pixart_alpha/pipeline_pixart_sigma.py) |
| `qwen-image` | 4 | 20 | 1328 × 1328 | [Source](https://huggingface.co/Qwen/Qwen-Image) Official square resolution bucket; true_cfg_scale maps to cfg_scale. |
| `qwen-image-21` | 1 | 20 | 2048 × 2048 | [Source](https://raw.githubusercontent.com/QwenLM/Qwen-Image-2.1/main/README.md) Original checkpoint defaults; select the matching TAEQI2.1 to replace its VAE. |
| `qwen-image-21-turbo` | 1 | 8 | 2048 × 2048 | [Model card](https://huggingface.co/Qwen/Qwen-Image-2.1-Turbo), [fixed sigmas](https://huggingface.co/Qwen/Qwen-Image-2.1-Turbo/blob/main/model_index.json). Standalone Turbo weights; the fixed 8-step schedule is applied on model load. |
| `qwen-image-edit` | 4 | 20 | — | [Source](https://huggingface.co/Qwen/Qwen-Image-Edit) Official editing example; output dimensions follow reference-image aspect ratio. |
| `qwen-image-edit-2509` | 4 | 20 | — | [Source](https://huggingface.co/Qwen/Qwen-Image-Edit-2509) Official editing example; output dimensions follow reference-image aspect ratio. |
| `qwen-image-edit-2511` | 4 | 20 | — | [Source](https://huggingface.co/Qwen/Qwen-Image-Edit-2511) Official editing example; output dimensions follow reference-image aspect ratio. |
| `qwen-image-layered` | 4 | 20 | 640 × 640 | [Source](https://huggingface.co/Qwen/Qwen-Image-Layered) Official recommended 640 resolution bucket; layered RGBA VAE is required. |
| `sd-turbo` | 1 | 1 | 512 × 512 | [Source](https://huggingface.co/stabilityai/sd-turbo) |
| `sd1-checkpoint` | 7.5 | 20 | 512 × 512 | [Source](https://raw.githubusercontent.com/huggingface/diffusers/main/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion.py) |
| `sd1-controlnet` | 7.5 | 20 | 512 × 512 | [Source](https://huggingface.co/lllyasviel/control_v11p_sd15_canny) |
| `sd1-inpainting` | 7.5 | 20 | 512 × 512 | [Source](https://raw.githubusercontent.com/huggingface/diffusers/main/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion_inpaint.py) |
| `sd1-ip-adapter` | 7.5 | 20 | 512 × 512 | [Source](https://raw.githubusercontent.com/huggingface/diffusers/main/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion.py) SD 1.x base pipeline defaults; adapter strength is configured separately. |
| `sd2-512-checkpoint` | 7.5 | 20 | 512 × 512 | [Source](https://raw.githubusercontent.com/huggingface/diffusers/main/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion.py) |
| `sd2-768-checkpoint` | 7.5 | 20 | 768 × 768 | [Source](https://raw.githubusercontent.com/huggingface/diffusers/main/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion.py) |
| `sd2-inpainting` | 7.5 | 20 | 512 × 512 | [Source](https://raw.githubusercontent.com/huggingface/diffusers/main/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion_inpaint.py) |
| `sd3-medium` | 7 | 20 | 1024 × 1024 | [Source](https://huggingface.co/stabilityai/stable-diffusion-3-medium-diffusers) |
| `sd35-large` | 3.5 | 20 | 1024 × 1024 | [Source](https://huggingface.co/stabilityai/stable-diffusion-3.5-large) |
| `sd35-medium` | 4.5 | 20 | 1024 × 1024 | [Source](https://huggingface.co/stabilityai/stable-diffusion-3.5-medium) |
| `sdxl-checkpoint` | 5 | 20 | 1024 × 1024 | [Source](https://raw.githubusercontent.com/huggingface/diffusers/main/src/diffusers/pipelines/stable_diffusion_xl/pipeline_stable_diffusion_xl.py) |
| `sdxl-controlnet` | 5 | 20 | 1024 × 1024 | [Source](https://raw.githubusercontent.com/huggingface/diffusers/main/src/diffusers/pipelines/stable_diffusion_xl/pipeline_stable_diffusion_xl.py) SDXL pipeline defaults; compatible ControlNet weights are required. |
| `sdxl-inpainting` | 8 | 20 | 1024 × 1024 | [Source](https://huggingface.co/diffusers/stable-diffusion-xl-1.0-inpainting-0.1/raw/main/README.md) |
| `sdxl-ip-adapter` | 5 | 20 | 1024 × 1024 | [Source](https://raw.githubusercontent.com/huggingface/diffusers/main/src/diffusers/pipelines/stable_diffusion_xl/pipeline_stable_diffusion_xl.py) SDXL base pipeline defaults; adapter strength is configured separately. |
| `sdxl-photomaker` | — | — | 1024 × 1024 | [Source](https://huggingface.co/TencentARC/PhotoMaker) Use settings for the selected compatible SDXL checkpoint; no unique CFG/steps are prescribed by the model card. |
| `sdxl-turbo` | 1 | 1 | 512 × 512 | [Source](https://huggingface.co/stabilityai/sdxl-turbo) |
| `sdxs` | 1 | 1 | 512 × 512 | [Source](https://huggingface.co/IDKiro/sdxs-512-dreamshaper) |
| `sefi-image` | 4 | 20 | — | [Source](https://huggingface.co/SeFi-Image/SeFi-Image-5B-Base) Base/RL family defaults; Turbo checkpoints require 4 steps / CFG 1. |
| `sensenova-u15` | 4 | 20 | 2048 × 2048 | [Source](https://raw.githubusercontent.com/OpenSenseNova/SenseNova-U1/main/examples/README.md) |
| `wan21-flf2v` | 5 | 20 | 1280 × 720 | [Source](https://raw.githubusercontent.com/Wan-Video/Wan2.1/main/generate.py) |
| `wan21-i2v` | 5 | 20 | 832 × 480 | [Source](https://raw.githubusercontent.com/Wan-Video/Wan2.1/main/generate.py) 480p base defaults; also accepts compatible SkyReels V2 and single Wan 2.2 I2V experts. |
| `wan21-t2v` | 5 | 20 | 1280 × 720 | [Source](https://raw.githubusercontent.com/Wan-Video/Wan2.1/main/generate.py) 14B base defaults; single Wan 2.2 experts and acceleration LoRAs need matching settings. |
| `wan21-vace` | 5 | 20 | 832 × 480 | [Source](https://raw.githubusercontent.com/Wan-Video/Wan2.1/main/generate.py) |
| `wan22-i2v` | 3.5 | 20 | 1280 × 720 | [Source](https://raw.githubusercontent.com/Wan-Video/Wan2.2/main/wan/configs/wan_i2v_A14B.py) |
| `wan22-s2v` | 4.5 | 20 | — | [Source](https://raw.githubusercontent.com/Wan-Video/Wan2.2/main/wan/configs/wan_s2v_14B.py) The official S2V pipeline derives output dimensions from the input. |
| `wan22-t2v` | 3 | 20 | 1280 × 720 | [Source](https://raw.githubusercontent.com/Wan-Video/Wan2.2/main/wan/configs/wan_t2v_A14B.py) |
| `wan22-ti2v-5b` | 5 | 20 | 1280 × 704 | [Source](https://raw.githubusercontent.com/Wan-Video/Wan2.2/main/wan/configs/wan_ti2v_5B.py) |
| `z-image` | 4 | 20 | 720 × 1280 | [Source](https://huggingface.co/Tongyi-MAI/Z-Image) |
| `z-image-l2p` | — | — | — | [Source](https://huggingface.co/zhen-nan/L2P) No numerical inference recommendations in the author’s model card; previous test values were removed. |
| `z-image-turbo` | 1 | 9 | 1024 × 1024 | [Source](https://huggingface.co/Tongyi-MAI/Z-Image-Turbo) |
TAE alternatives use the model-compatible weights listed by [TAESD](https://github.com/madebyollin/taesd) and [TAEHV](https://github.com/madebyollin/taehv).
TAE slots are omitted for pixel-space models, PiD, layered RGBA decoding and Mage-VAE paths that the current runtime does not map to a compatible TAE.
@@ -0,0 +1,41 @@
{
"schema_version": 1,
"id": "ernie-image-turbo",
"name": "ERNIE Image Turbo",
"model_url": "https://huggingface.co/baidu/ERNIE-Image-Turbo",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Ministral 3 3B Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 1,
"steps": 8,
"width": 1024,
"height": 1024,
"sampling_method": "euler"
},
"features": [
"flow_shift"
]
}
+41
View File
@@ -0,0 +1,41 @@
{
"schema_version": 1,
"id": "ernie-image",
"name": "ERNIE Image",
"model_url": "https://huggingface.co/baidu/ERNIE-Image",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Ministral 3 3B Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 4,
"steps": 20,
"width": 1024,
"height": 1024,
"sampling_method": "euler"
},
"features": [
"flow_shift"
]
}
+48
View File
@@ -0,0 +1,48 @@
{
"schema_version": 1,
"id": "flex2",
"name": "Flex.2 Preview",
"model_url": "https://huggingface.co/ostris/Flex.2-preview",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "clip_l",
"required": true
},
{
"id": "t5xxl",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"cfg_scale": 1,
"steps": 20,
"width": 1024,
"height": 1024,
"distilled_guidance": 3.5
},
"features": [
"clip_skip",
"distilled_guidance",
"flow_shift",
"slg"
]
}
+53
View File
@@ -0,0 +1,53 @@
{
"schema_version": 1,
"id": "flux1-canny",
"name": "FLUX.1 Canny dev",
"description": "Select Canny weights and supply a precomputed edge map as Control Image. Edge detection is not performed by this recipe.",
"model_url": "https://huggingface.co/black-forest-labs/FLUX.1-Canny-dev",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "clip_l",
"required": true
},
{
"id": "t5xxl",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"cfg_scale": 1,
"steps": 20,
"width": 1024,
"height": 1024,
"distilled_guidance": 30
},
"features": [
"clip_skip",
"distilled_guidance",
"flow_shift",
"slg",
"control_image"
],
"disabled_features": [
"mask_image"
]
}
+54
View File
@@ -0,0 +1,54 @@
{
"schema_version": 1,
"id": "flux1-depth",
"name": "FLUX.1 Depth dev",
"description": "Select Depth weights and supply a precomputed depth map as Control Image. Depth estimation is not performed by this recipe.",
"model_url": "https://huggingface.co/black-forest-labs/FLUX.1-Depth-dev",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "clip_l",
"required": true
},
{
"id": "t5xxl",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"distilled_guidance": 10,
"scheduler": "flux",
"cfg_scale": 1,
"steps": 20,
"width": 1024,
"height": 1024
},
"features": [
"clip_skip",
"distilled_guidance",
"flow_shift",
"slg",
"control_image"
],
"disabled_features": [
"mask_image"
]
}
+49
View File
@@ -0,0 +1,49 @@
{
"schema_version": 1,
"id": "flux1-dev",
"name": "FLUX.1 dev",
"description": "Supports FLUX.1 dev and compatible layer-count variants such as FLUX.1 Lite. Select the transformer with matching CLIP-L, T5XXL and FLUX.1 VAE.",
"model_url": "https://huggingface.co/black-forest-labs/FLUX.1-dev",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "clip_l",
"required": true
},
{
"id": "t5xxl",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"cfg_scale": 1,
"steps": 20,
"width": 1024,
"height": 1024,
"distilled_guidance": 3.5
},
"features": [
"clip_skip",
"distilled_guidance",
"flow_shift",
"slg"
]
}
+52
View File
@@ -0,0 +1,52 @@
{
"schema_version": 1,
"id": "flux1-fill",
"name": "FLUX.1 Fill dev",
"description": "Select Fill weights and supply Init Image plus Mask Image for inpainting. Denoising strength defaults to 1 for a full denoising schedule.",
"model_url": "https://huggingface.co/black-forest-labs/FLUX.1-Fill-dev",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "clip_l",
"required": true
},
{
"id": "t5xxl",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"distilled_guidance": 30,
"strength": 1.0,
"scheduler": "flux",
"cfg_scale": 1,
"steps": 20,
"width": 1232,
"height": 1632
},
"features": [
"clip_skip",
"distilled_guidance",
"flow_shift",
"slg",
"img_cfg"
]
}
@@ -0,0 +1,49 @@
{
"schema_version": 1,
"id": "flux1-kontext",
"name": "FLUX.1 Kontext dev",
"description": "Select FLUX.1 Kontext weights. Supports text-to-image and native reference-image editing; an editing LoRA is not required.",
"model_url": "https://huggingface.co/black-forest-labs/FLUX.1-Kontext-dev",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "clip_l",
"required": true
},
{
"id": "t5xxl",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"cfg_scale": 1,
"steps": 20,
"distilled_guidance": 2.5
},
"features": [
"clip_skip",
"distilled_guidance",
"flow_shift",
"img_cfg",
"ref_images",
"slg"
]
}
+56
View File
@@ -0,0 +1,56 @@
{
"schema_version": 1,
"id": "flux1-pulid",
"name": "FLUX.1 dev PuLID",
"description": "Supply a precomputed PuLID identity embedding with the generation request.",
"model_url": "https://huggingface.co/guozinan/PuLID",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "clip_l",
"required": true
},
{
"id": "t5xxl",
"required": true
},
{
"id": "pulid_weights",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"cfg_scale": 1,
"steps": 20,
"width": 1024,
"height": 1024,
"distilled_guidance": 3.5
},
"features": [
"clip_skip",
"distilled_guidance",
"flow_shift",
"slg"
],
"disabled_features": [
"slg"
]
}
@@ -0,0 +1,46 @@
{
"schema_version": 1,
"id": "flux1-schnell",
"name": "FLUX.1 schnell",
"model_url": "https://huggingface.co/black-forest-labs/FLUX.1-schnell",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "clip_l",
"required": true
},
{
"id": "t5xxl",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"cfg_scale": 1,
"steps": 4,
"width": 1024,
"height": 1024
},
"features": [
"clip_skip",
"flow_shift",
"slg"
]
}
+47
View File
@@ -0,0 +1,47 @@
{
"schema_version": 1,
"id": "flux2-dev",
"name": "FLUX.2 dev",
"description": "Supports text-to-image and reference-image editing.",
"model_url": "https://huggingface.co/black-forest-labs/FLUX.2-dev",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Mistral Small 3.2 24B Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"distilled_guidance": 4,
"sampling_method": "euler",
"cfg_scale": 1,
"steps": 20,
"width": 1024,
"height": 1024
},
"features": [
"distilled_guidance",
"flow_shift",
"img_cfg",
"ref_images",
"slg"
]
}
@@ -0,0 +1,45 @@
{
"schema_version": 1,
"id": "flux2-klein-4b",
"name": "FLUX.2 Klein 4B",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 4B Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 1,
"steps": 4,
"width": 1024,
"height": 1024,
"sampling_method": "euler"
},
"features": [
"flow_shift",
"img_cfg",
"ref_images",
"slg"
]
}
@@ -0,0 +1,45 @@
{
"schema_version": 1,
"id": "flux2-klein-9b",
"name": "FLUX.2 Klein 9B",
"description": "Supports text-to-image and reference-image editing.",
"model_url": "https://huggingface.co/black-forest-labs/FLUX.2-klein-9B",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 8B Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 1,
"steps": 4,
"width": 1024,
"height": 1024,
"sampling_method": "euler"
},
"features": [
"flow_shift",
"img_cfg",
"ref_images",
"slg"
]
}
@@ -0,0 +1,45 @@
{
"schema_version": 1,
"id": "flux2-klein-base-4b",
"name": "FLUX.2 Klein Base 4B",
"description": "Supports text-to-image and reference-image editing.",
"model_url": "https://huggingface.co/black-forest-labs/FLUX.2-klein-base-4B",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 4B Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"cfg_scale": 4,
"steps": 20,
"width": 1024,
"height": 1024
},
"features": [
"flow_shift",
"img_cfg",
"ref_images",
"slg"
]
}
@@ -0,0 +1,45 @@
{
"schema_version": 1,
"id": "flux2-klein-base-9b",
"name": "FLUX.2 Klein Base 9B",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 8B Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 4,
"steps": 20,
"width": 1024,
"height": 1024,
"sampling_method": "euler"
},
"features": [
"flow_shift",
"img_cfg",
"ref_images",
"slg"
]
}
@@ -0,0 +1,42 @@
{
"schema_version": 1,
"id": "flux3-klein-base",
"name": "FLUX.3 Image Klein Base",
"model_url": "https://huggingface.co/black-forest-labs/flux-3-image-klein-base",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 4B Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"cfg_scale": 4,
"steps": 20,
"width": 1024,
"height": 1024
},
"features": [
"flow_shift",
"ref_images"
]
}
+43
View File
@@ -0,0 +1,43 @@
{
"schema_version": 1,
"id": "flux3-klein",
"name": "FLUX.3 Image Klein",
"description": "Supports text-to-image and reference-image editing.",
"model_url": "https://huggingface.co/black-forest-labs/flux-3-image-klein",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 4B Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"cfg_scale": 1,
"steps": 16,
"width": 1024,
"height": 1024
},
"features": [
"flow_shift",
"ref_images"
]
}
@@ -0,0 +1,24 @@
{
"schema_version": 1,
"id": "hidream-o1-dev",
"name": "HiDream O1 Image dev",
"description": "Bundled checkpoint; also supports reference-image editing.",
"model_url": "https://huggingface.co/HiDream-ai/HiDream-O1-Image-Dev",
"slots": [
{
"id": "model",
"required": true
}
],
"defaults": {
"cfg_scale": 1,
"steps": 20,
"width": 2048,
"height": 2048,
"sampling_method": "euler"
},
"features": [
"flow_shift",
"ref_images"
]
}
@@ -0,0 +1,56 @@
{
"schema_version": 1,
"id": "hunyuan-video-15-i2v",
"name": "HunyuanVideo 1.5 I2V",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 2.5 VL 7B Text Encoder"
},
{
"id": "t5xxl",
"required": true,
"label": "ByT5 Small GlyphXL"
},
{
"id": "vae",
"required": false,
"label": "HunyuanVideo 1.5 VAE"
},
{
"id": "tae",
"required": false,
"label": "TAEHV 1.5"
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"video_frames": 33,
"cfg_scale": 6,
"steps": 20,
"width": 1280,
"height": 720,
"flow_shift": 7
},
"features": [
"flow_shift",
"init_image"
],
"disabled_features": [
"img2img",
"mask_image"
]
}
@@ -0,0 +1,55 @@
{
"schema_version": 1,
"id": "hunyuan-video-15-t2v",
"name": "HunyuanVideo 1.5 T2V",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 2.5 VL 7B Text Encoder"
},
{
"id": "t5xxl",
"required": true,
"label": "ByT5 Small GlyphXL"
},
{
"id": "vae",
"required": false,
"label": "HunyuanVideo 1.5 VAE"
},
{
"id": "tae",
"required": false,
"label": "TAEHV 1.5"
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"video_frames": 33,
"cfg_scale": 6,
"steps": 20,
"width": 1280,
"height": 720,
"flow_shift": 9
},
"features": [
"flow_shift"
],
"disabled_features": [
"img2img",
"mask_image"
]
}
+46
View File
@@ -0,0 +1,46 @@
{
"schema_version": 1,
"id": "ideogram4",
"name": "Ideogram 4",
"description": "Select the matching unconditional transformer for classifier-free guidance.",
"model_url": "https://huggingface.co/ideogram-ai/ideogram-4-fp8",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "uncond_diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 8B Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 7,
"steps": 20,
"width": 1024,
"height": 1024,
"sampling_method": "euler"
},
"features": [
"flow_shift"
]
}
@@ -0,0 +1,36 @@
{
"schema_version": 1,
"id": "instruct-pix2pix",
"name": "Instruct-Pix2Pix",
"description": "Supply the image to edit as a reference image.",
"model_url": "https://huggingface.co/timbrooks/instruct-pix2pix",
"slots": [
{
"id": "model",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"defaults": {
"img_cfg_scale": 1,
"sampling_method": "euler_a",
"scheduler": "karras",
"steps": 10
},
"features": [
"cache_unet",
"clip_skip",
"img_cfg",
"ref_images"
],
"disabled_features": [
"cache_dit"
]
}
+32
View File
@@ -0,0 +1,32 @@
{
"schema_version": 1,
"id": "iris-3b",
"name": "Iris 3B",
"description": "Pixel-space model; no VAE is required.",
"model_url": "https://huggingface.co/speridlabs/iris-3b",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 4B Text Encoder"
}
],
"defaults": {
"cfg_scale": 3,
"steps": 20,
"width": 1024,
"height": 1024,
"sampling_method": "euler"
},
"features": [
"flow_shift"
],
"disabled_features": [
"preview",
"vae_tiling"
]
}
@@ -0,0 +1,49 @@
{
"schema_version": 1,
"id": "krea2-raw-edit",
"name": "Krea 2 Raw Edit (LoRA)",
"description": "Requires a matching Ostris-compatible community editing LoRA. Apply it after loading and supply reference images; uses the krea2_ostris_edit preset. Select a separate vision projector when it is not bundled in the text encoder.",
"model_url": "https://huggingface.co/krea/Krea-2-Raw",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 4B Text Encoder"
},
{
"id": "llm_vision",
"required": false,
"label": "Vision projector (required for separate GGUF text encoders)"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"scheduler": "discrete",
"sampling_method": "euler",
"cfg_scale": 3.5,
"steps": 20,
"width": 1024,
"height": 1024
},
"features": [
"flow_shift",
"ref_images"
]
}
+42
View File
@@ -0,0 +1,42 @@
{
"schema_version": 1,
"id": "krea2-raw",
"name": "Krea 2 Raw",
"description": "For reference-image editing with a community LoRA, use the corresponding Krea 2 Edit recipe.",
"model_url": "https://huggingface.co/krea/Krea-2-Raw",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 4B Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 3.5,
"steps": 20,
"width": 1024,
"height": 1024,
"sampling_method": "euler"
},
"features": [
"flow_shift"
]
}
@@ -0,0 +1,49 @@
{
"schema_version": 1,
"id": "krea2-turbo-edit",
"name": "Krea 2 Turbo Edit (LoRA)",
"description": "Requires a matching Ostris-compatible community editing LoRA. Apply it after loading and supply reference images; uses the krea2_ostris_edit preset. Select a separate vision projector when it is not bundled in the text encoder.",
"model_url": "https://huggingface.co/krea/Krea-2-Turbo",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 4B Text Encoder"
},
{
"id": "llm_vision",
"required": false,
"label": "Vision projector (required for separate GGUF text encoders)"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"scheduler": "discrete",
"sampling_method": "euler",
"cfg_scale": 1,
"steps": 8,
"width": 2048,
"height": 2048
},
"features": [
"flow_shift",
"ref_images"
]
}
+42
View File
@@ -0,0 +1,42 @@
{
"schema_version": 1,
"id": "krea2-turbo",
"name": "Krea 2 Turbo",
"description": "For reference-image editing with a community LoRA, use the corresponding Krea 2 Edit recipe.",
"model_url": "https://huggingface.co/krea/Krea-2-Turbo",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 4B Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 1,
"steps": 8,
"width": 2048,
"height": 2048,
"sampling_method": "euler"
},
"features": [
"flow_shift"
]
}
+46
View File
@@ -0,0 +1,46 @@
{
"schema_version": 1,
"id": "lens-turbo",
"name": "LENS Turbo",
"model_url": "https://huggingface.co/microsoft/Lens-Turbo",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "GPT-OSS 20B Text Encoder"
},
{
"id": "tokenizer",
"required": true,
"label": "GPT-OSS tokenizer.json"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 1,
"steps": 4,
"width": 1440,
"height": 1440,
"sampling_method": "euler"
},
"features": [
"flow_shift"
]
}
+46
View File
@@ -0,0 +1,46 @@
{
"schema_version": 1,
"id": "lens",
"name": "LENS",
"model_url": "https://huggingface.co/microsoft/Lens",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "GPT-OSS 20B Text Encoder"
},
{
"id": "tokenizer",
"required": true,
"label": "GPT-OSS tokenizer.json"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 5,
"steps": 20,
"width": 1440,
"height": 1440,
"sampling_method": "euler"
},
"features": [
"flow_shift"
]
}
@@ -0,0 +1,49 @@
{
"schema_version": 1,
"id": "lingbot-video-dense",
"name": "LingBot Video Dense 1.3B",
"description": "Use Video Generation.",
"model_url": "https://huggingface.co/robbyant/lingbot-video-dense-1.3b",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 4B Text Encoder"
},
{
"id": "vae",
"required": false,
"label": "Wan 2.1 VAE"
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"video_frames": 33,
"cfg_scale": 3,
"steps": 20,
"width": 832,
"height": 480,
"sampling_method": "euler"
},
"features": [
"flow_shift",
"init_image"
],
"disabled_features": [
"img2img",
"mask_image"
]
}
@@ -0,0 +1,49 @@
{
"schema_version": 1,
"id": "lingbot-video-moe",
"name": "LingBot Video MoE 30B A3B",
"description": "Use Video Generation.",
"model_url": "https://huggingface.co/robbyant/lingbot-video-moe-30b-a3b",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 4B Text Encoder"
},
{
"id": "vae",
"required": false,
"label": "Wan 2.1 VAE"
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"video_frames": 33,
"cfg_scale": 3,
"steps": 20,
"width": 832,
"height": 480,
"sampling_method": "euler"
},
"features": [
"flow_shift",
"init_image"
],
"disabled_features": [
"img2img",
"mask_image"
]
}
@@ -0,0 +1,52 @@
{
"schema_version": 1,
"id": "llada-image-turbo",
"name": "LLaDA Image Turbo",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "LLaDA-Image Text Encoder"
},
{
"id": "tokenizer",
"required": true,
"label": "LLaDA tokenizer.json"
},
{
"id": "embeddings_connectors",
"required": true,
"label": "LLaDA Turbo Connectors"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 1,
"steps": 4,
"width": 1024,
"height": 1024,
"sampling_method": "euler"
},
"features": [
"ref_images"
]
}
+53
View File
@@ -0,0 +1,53 @@
{
"schema_version": 1,
"id": "llada-image",
"name": "LLaDA Image (base)",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "LLaDA-Image Base Text Encoder"
},
{
"id": "tokenizer",
"required": true,
"label": "LLaDA tokenizer.json"
},
{
"id": "embeddings_connectors",
"required": true,
"label": "LLaDA Base Connectors"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"scheduler": "llada_image",
"cfg_scale": 5,
"steps": 20,
"width": 1024,
"height": 1024
},
"features": [
"ref_images"
]
}
@@ -0,0 +1,43 @@
{
"schema_version": 1,
"id": "longcat-image-edit",
"name": "LongCat Image Edit",
"description": "Supply reference images for editing.",
"model_url": "https://huggingface.co/meituan-longcat/LongCat-Image-Edit",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 2.5 VL 7B Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 4.5,
"steps": 20,
"sampling_method": "euler"
},
"features": [
"flow_shift",
"img_cfg",
"ref_images",
"slg"
]
}
@@ -0,0 +1,42 @@
{
"schema_version": 1,
"id": "longcat-image",
"name": "LongCat Image",
"model_url": "https://huggingface.co/meituan-longcat/LongCat-Image",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 2.5 VL 7B Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 4,
"steps": 20,
"width": 1344,
"height": 768,
"sampling_method": "euler"
},
"features": [
"flow_shift",
"slg"
]
}
+60
View File
@@ -0,0 +1,60 @@
{
"schema_version": 1,
"id": "ltx23-dev",
"name": "LTX 2.3 dev",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Gemma 3 12B Text Encoder"
},
{
"id": "embeddings_connectors",
"required": true
},
{
"id": "vae",
"required": false,
"label": "LTX 2.3 Video VAE"
},
{
"id": "tae",
"required": false
},
{
"id": "audio_vae",
"required": false,
"label": "LTX 2.3 Audio VAE"
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"video_frames": 33,
"fps": 24,
"cfg_scale": 3,
"steps": 20,
"width": 768,
"height": 512
},
"features": [
"end_image",
"flow_shift",
"init_image"
],
"disabled_features": [
"img2img",
"mask_image"
]
}
@@ -0,0 +1,60 @@
{
"schema_version": 1,
"id": "ltx23-distilled",
"name": "LTX 2.3 Distilled 1.1",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Gemma 3 12B Text Encoder"
},
{
"id": "embeddings_connectors",
"required": true
},
{
"id": "vae",
"required": false,
"label": "LTX 2.3 Video VAE"
},
{
"id": "tae",
"required": false
},
{
"id": "audio_vae",
"required": false,
"label": "LTX 2.3 Audio VAE"
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"video_frames": 121,
"fps": 24,
"cfg_scale": 1,
"steps": 8,
"width": 768,
"height": 512
},
"features": [
"end_image",
"flow_shift",
"init_image"
],
"disabled_features": [
"img2img",
"mask_image"
]
}
+56
View File
@@ -0,0 +1,56 @@
{
"schema_version": 1,
"id": "ltx25-dev",
"name": "LTX 2.5 dev",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "LTX-2.5 Gemma 4 12B with projection"
},
{
"id": "vae",
"required": false,
"label": "LTX 2.5 Conv Video VAE"
},
{
"id": "tae",
"required": false
},
{
"id": "audio_vae",
"required": false,
"label": "LTX 2.5 Audio VAE"
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"fps": 24,
"video_frames": 33,
"cfg_scale": 3,
"steps": 20,
"width": 768,
"height": 512
},
"features": [
"end_image",
"flow_shift",
"init_image"
],
"disabled_features": [
"img2img",
"mask_image"
]
}
@@ -0,0 +1,39 @@
{
"schema_version": 1,
"id": "mage-flow-edit",
"name": "Mage Flow Edit",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 4B Text Encoder"
},
{
"id": "vae",
"required": true
},
{
"id": "llm_vision",
"required": false,
"label": "Vision projector (required for separate GGUF text encoders)"
}
],
"defaults": {
"sampling_method": "euler",
"cfg_scale": 5,
"steps": 20,
"width": 1024,
"height": 1024
},
"features": [
"flow_shift",
"img_cfg",
"ref_images"
]
}
@@ -0,0 +1,31 @@
{
"schema_version": 1,
"id": "mage-flow-turbo",
"name": "Mage Flow Turbo",
"model_url": "https://huggingface.co/microsoft/Mage-Flow-Turbo",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 4B Text Encoder"
},
{
"id": "vae",
"required": true
}
],
"defaults": {
"cfg_scale": 1,
"steps": 4,
"width": 1024,
"height": 1024,
"sampling_method": "euler"
},
"features": [
"flow_shift"
]
}
+31
View File
@@ -0,0 +1,31 @@
{
"schema_version": 1,
"id": "mage-flow",
"name": "Mage Flow",
"model_url": "https://huggingface.co/microsoft/Mage-Flow",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 4B Text Encoder"
},
{
"id": "vae",
"required": true
}
],
"defaults": {
"cfg_scale": 5,
"steps": 20,
"width": 1024,
"height": 1024,
"sampling_method": "euler"
},
"features": [
"flow_shift"
]
}
+46
View File
@@ -0,0 +1,46 @@
{
"schema_version": 1,
"id": "ming-image",
"name": "Ming Image 0.1",
"model_url": "https://huggingface.co/inclusionAI/Ming-Image-0.1-Design",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Ling Mini 2.0 Text Encoder"
},
{
"id": "tokenizer",
"required": true,
"label": "Ling tokenizer.json"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 1,
"steps": 12,
"width": 2048,
"height": 2048,
"sampling_method": "euler"
},
"features": [
"flow_shift"
]
}
@@ -0,0 +1,55 @@
{
"schema_version": 1,
"id": "minimax-h3-fl2va",
"name": "MiniMax H3 FL2VA",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "MiniMax-H3 Qwen 3 VL 32B Text Encoder"
},
{
"id": "vae",
"required": false,
"label": "MiniMax H3 Video VAE"
},
{
"id": "tae",
"required": false
},
{
"id": "audio_vae",
"required": false,
"label": "MiniMax H3 Audio VAE"
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"video_frames": 56,
"fps": 24,
"cfg_scale": 1,
"width": 1344,
"height": 768,
"sampling_method": "euler"
},
"features": [
"end_image",
"flow_shift",
"init_image"
],
"disabled_features": [
"img2img",
"mask_image"
]
}
@@ -0,0 +1,54 @@
{
"schema_version": 1,
"id": "minimax-h3-ref2va",
"name": "MiniMax H3 Ref2VA",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "MiniMax-H3 Qwen 3 VL 32B Text Encoder"
},
{
"id": "vae",
"required": false,
"label": "MiniMax H3 Video VAE"
},
{
"id": "tae",
"required": false
},
{
"id": "audio_vae",
"required": false,
"label": "MiniMax H3 Audio VAE"
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"video_frames": 56,
"fps": 24,
"cfg_scale": 1,
"width": 1344,
"height": 768,
"sampling_method": "euler"
},
"features": [
"flow_shift",
"ref_images"
],
"disabled_features": [
"img2img",
"mask_image"
]
}
+29
View File
@@ -0,0 +1,29 @@
{
"schema_version": 1,
"id": "minit2i",
"name": "MiniT2I",
"description": "Pixel-space model; no VAE is required.",
"model_url": "https://huggingface.co/MiniT2I/MiniT2I",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "t5xxl",
"required": true,
"label": "FLAN-T5 Large"
}
],
"defaults": {
"cfg_scale": 2.5,
"steps": 20,
"width": 512,
"height": 512,
"sampling_method": "euler"
},
"disabled_features": [
"preview",
"vae_tiling"
]
}
@@ -0,0 +1,49 @@
{
"schema_version": 1,
"id": "moss-image-base",
"name": "MOSS Image Base",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3.5 9B Text Encoder"
},
{
"id": "tokenizer",
"required": true,
"label": "Qwen 3.5 tokenizer.json"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 3,
"steps": 20,
"width": 1024,
"height": 1024,
"flow_shift": 5,
"sampling_method": "euler"
},
"features": [
"flow_shift",
"slg"
]
}
@@ -0,0 +1,49 @@
{
"schema_version": 1,
"id": "moss-image-sft",
"name": "MOSS Image SFT",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3.5 9B Text Encoder"
},
{
"id": "tokenizer",
"required": true,
"label": "Qwen 3.5 tokenizer.json"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 3,
"steps": 20,
"width": 1024,
"height": 1024,
"flow_shift": 5,
"sampling_method": "euler"
},
"features": [
"flow_shift",
"slg"
]
}
@@ -0,0 +1,36 @@
{
"schema_version": 1,
"id": "nitrosd-realism",
"name": "NitroSD Realism",
"model_url": "https://huggingface.co/ChenDY/NitroFusion",
"slots": [
{
"id": "model",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"defaults": {
"scheduler": "sgm_uniform",
"shifted_timestep": 250,
"cfg_scale": 1,
"steps": 1,
"width": 1024,
"height": 1024
},
"features": [
"cache_unet",
"clip_skip",
"shifted_timestep"
],
"disabled_features": [
"cache_dit"
]
}
+42
View File
@@ -0,0 +1,42 @@
{
"schema_version": 1,
"id": "ovis-image",
"name": "Ovis Image",
"model_url": "https://huggingface.co/ATH-MaaS/Ovis-Image-7B",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Ovis 2.5 Text Encoder"
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 5,
"steps": 20,
"width": 1024,
"height": 1024,
"sampling_method": "euler"
},
"features": [
"flow_shift",
"slg"
]
}
+47
View File
@@ -0,0 +1,47 @@
{
"schema_version": 1,
"id": "pid-15-flux2",
"name": "PiD 1.5 (Flux.2 VAE)",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Gemma 2 2B ELM Text Encoder"
},
{
"id": "tokenizer",
"required": true,
"label": "Gemma 2 tokenizer.json"
},
{
"id": "vae",
"required": true
}
],
"context": {
"vae_format": "flux2"
},
"defaults": {
"sampling_method": "lcm",
"scheduler": "lcm",
"cfg_scale": 1,
"steps": 4,
"width": 2048,
"height": 2048
},
"features": [
"flow_shift",
"ref_images"
],
"disabled_features": [
"img2img",
"mask_image",
"preview"
]
}
@@ -0,0 +1,47 @@
{
"schema_version": 1,
"id": "pid-15-qwen-image",
"name": "PiD 1.5 (Qwen Image VAE)",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Gemma 2 2B ELM Text Encoder"
},
{
"id": "tokenizer",
"required": true,
"label": "Gemma 2 tokenizer.json"
},
{
"id": "vae",
"required": true
}
],
"context": {
"vae_format": "wan"
},
"defaults": {
"sampling_method": "lcm",
"scheduler": "lcm",
"cfg_scale": 1,
"steps": 4,
"width": 2048,
"height": 2048
},
"features": [
"flow_shift",
"ref_images"
],
"disabled_features": [
"img2img",
"mask_image",
"preview"
]
}
+47
View File
@@ -0,0 +1,47 @@
{
"schema_version": 1,
"id": "pid-15-sd3",
"name": "PiD 1.5 (SD3 VAE)",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Gemma 2 2B ELM Text Encoder"
},
{
"id": "tokenizer",
"required": true,
"label": "Gemma 2 tokenizer.json"
},
{
"id": "vae",
"required": true
}
],
"context": {
"vae_format": "sd3"
},
"defaults": {
"sampling_method": "lcm",
"scheduler": "lcm",
"cfg_scale": 1,
"steps": 4,
"width": 2048,
"height": 2048
},
"features": [
"flow_shift",
"ref_images"
],
"disabled_features": [
"img2img",
"mask_image",
"preview"
]
}
+47
View File
@@ -0,0 +1,47 @@
{
"schema_version": 1,
"id": "pid-15",
"name": "PiD 1.5 (Flux / Z-Image VAE)",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Gemma 2 2B ELM Text Encoder"
},
{
"id": "tokenizer",
"required": true,
"label": "Gemma 2 tokenizer.json"
},
{
"id": "vae",
"required": true
}
],
"context": {
"vae_format": "flux"
},
"defaults": {
"sampling_method": "lcm",
"scheduler": "lcm",
"cfg_scale": 1,
"steps": 4,
"width": 2048,
"height": 2048
},
"features": [
"flow_shift",
"ref_images"
],
"disabled_features": [
"img2img",
"mask_image",
"preview"
]
}
+47
View File
@@ -0,0 +1,47 @@
{
"schema_version": 1,
"id": "pid-flux2",
"name": "PiD (Flux.2 VAE)",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Gemma 2 2B ELM Text Encoder"
},
{
"id": "tokenizer",
"required": true,
"label": "Gemma 2 tokenizer.json"
},
{
"id": "vae",
"required": true
}
],
"context": {
"vae_format": "flux2"
},
"defaults": {
"sampling_method": "lcm",
"scheduler": "lcm",
"cfg_scale": 1,
"steps": 4,
"width": 2048,
"height": 2048
},
"features": [
"flow_shift",
"ref_images"
],
"disabled_features": [
"img2img",
"mask_image",
"preview"
]
}
@@ -0,0 +1,47 @@
{
"schema_version": 1,
"id": "pid-qwen-image",
"name": "PiD (Qwen Image VAE)",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Gemma 2 2B ELM Text Encoder"
},
{
"id": "tokenizer",
"required": true,
"label": "Gemma 2 tokenizer.json"
},
{
"id": "vae",
"required": true
}
],
"context": {
"vae_format": "wan"
},
"defaults": {
"sampling_method": "lcm",
"scheduler": "lcm",
"cfg_scale": 1,
"steps": 4,
"width": 2048,
"height": 2048
},
"features": [
"flow_shift",
"ref_images"
],
"disabled_features": [
"img2img",
"mask_image",
"preview"
]
}
+47
View File
@@ -0,0 +1,47 @@
{
"schema_version": 1,
"id": "pid-sd3",
"name": "PiD (SD3 VAE)",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Gemma 2 2B ELM Text Encoder"
},
{
"id": "tokenizer",
"required": true,
"label": "Gemma 2 tokenizer.json"
},
{
"id": "vae",
"required": true
}
],
"context": {
"vae_format": "sd3"
},
"defaults": {
"sampling_method": "lcm",
"scheduler": "lcm",
"cfg_scale": 1,
"steps": 4,
"width": 2048,
"height": 2048
},
"features": [
"flow_shift",
"ref_images"
],
"disabled_features": [
"img2img",
"mask_image",
"preview"
]
}
+47
View File
@@ -0,0 +1,47 @@
{
"schema_version": 1,
"id": "pid",
"name": "PiD (Flux / Z-Image VAE)",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Gemma 2 2B ELM Text Encoder"
},
{
"id": "tokenizer",
"required": true,
"label": "Gemma 2 tokenizer.json"
},
{
"id": "vae",
"required": true
}
],
"context": {
"vae_format": "flux"
},
"defaults": {
"sampling_method": "lcm",
"scheduler": "lcm",
"cfg_scale": 1,
"steps": 4,
"width": 2048,
"height": 2048
},
"features": [
"flow_shift",
"ref_images"
],
"disabled_features": [
"img2img",
"mask_image",
"preview"
]
}
@@ -0,0 +1,42 @@
{
"schema_version": 1,
"id": "pixart-alpha-1024",
"name": "PixArt Alpha XL 1024",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "t5xxl",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"context": {
"model_args": "pixart_vae_scale_factor=0.18215"
},
"defaults": {
"scheduler": "discrete",
"sampling_method": "euler",
"cfg_scale": 4.5,
"steps": 20,
"width": 1024,
"height": 1024
}
}
@@ -0,0 +1,42 @@
{
"schema_version": 1,
"id": "pixart-alpha-512",
"name": "PixArt Alpha XL 512",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "t5xxl",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"context": {
"model_args": "pixart_vae_scale_factor=0.18215,pixart_pos_embed_base_size=32,pixart_interpolation_scale=1"
},
"defaults": {
"scheduler": "discrete",
"sampling_method": "euler",
"cfg_scale": 4.5,
"steps": 20,
"width": 512,
"height": 512
}
}
+37
View File
@@ -0,0 +1,37 @@
{
"schema_version": 1,
"id": "pixart-sigma",
"name": "PixArt Sigma XL 1024",
"model_url": "https://huggingface.co/PixArt-alpha/PixArt-Sigma-XL-2-1024-MS",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "t5xxl",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"cfg_scale": 4.5,
"steps": 20,
"width": 1024,
"height": 1024,
"sampling_method": "euler"
}
}
@@ -0,0 +1,60 @@
{
"schema_version": 1,
"id": "qwen-image-21-turbo",
"name": "Qwen Image 2.1 Turbo",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 8B Text Encoder"
},
{
"id": "llm_vision",
"required": false,
"label": "Vision projector (required for separate GGUF text encoders)"
},
{
"id": "vae",
"required": false,
"label": "Qwen Image 2.1 VAE"
},
{
"id": "tae",
"required": false,
"label": "TAEQI2.1"
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"cfg_scale": 1,
"steps": 8,
"width": 2048,
"height": 2048,
"custom_sigmas": [
1.0,
0.978453,
0.95418,
0.926626,
0.89508,
0.845148,
0.704534,
0.414568,
0.0
]
},
"features": [
"ref_images"
]
}
@@ -0,0 +1,50 @@
{
"schema_version": 1,
"id": "qwen-image-21",
"name": "Qwen Image 2.1",
"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": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 3 VL 8B Text Encoder"
},
{
"id": "llm_vision",
"required": false,
"label": "Vision projector (required for separate GGUF text encoders)"
},
{
"id": "vae",
"required": false,
"label": "Qwen Image 2.1 VAE"
},
{
"id": "tae",
"required": false,
"label": "TAEQI2.1"
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"cfg_scale": 1,
"steps": 20,
"width": 2048,
"height": 2048
},
"features": [
"flow_shift",
"ref_images"
]
}
@@ -0,0 +1,49 @@
{
"schema_version": 1,
"id": "qwen-image-edit-2509",
"name": "Qwen Image Edit 2509",
"description": "Supply reference images. Select a vision projector for GGUF text encoders; omit it if vision weights are included in the text encoder.",
"model_url": "https://huggingface.co/Qwen/Qwen-Image-Edit-2509",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 2.5 VL 7B Text Encoder"
},
{
"id": "llm_vision",
"required": false,
"label": "Vision projector (required for separate GGUF text encoders)"
},
{
"id": "vae",
"required": false,
"label": "Qwen Image VAE"
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"flow_shift": 3.0,
"cfg_scale": 4,
"steps": 20
},
"features": [
"flow_shift",
"img_cfg",
"ref_images"
]
}
@@ -0,0 +1,52 @@
{
"schema_version": 1,
"id": "qwen-image-edit-2511",
"name": "Qwen Image Edit 2511",
"description": "Supply reference images. Select a vision projector for GGUF text encoders; omit it if vision weights are included in the text encoder.",
"model_url": "https://huggingface.co/Qwen/Qwen-Image-Edit-2511",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 2.5 VL 7B Text Encoder"
},
{
"id": "llm_vision",
"required": false,
"label": "Vision projector (required for separate GGUF text encoders)"
},
{
"id": "vae",
"required": false,
"label": "Qwen Image VAE"
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"context": {
"model_args": "qwen_image_zero_cond_t=true"
},
"defaults": {
"sampling_method": "euler",
"flow_shift": 3.0,
"cfg_scale": 4,
"steps": 20
},
"features": [
"flow_shift",
"img_cfg",
"ref_images"
]
}
@@ -0,0 +1,49 @@
{
"schema_version": 1,
"id": "qwen-image-edit",
"name": "Qwen Image Edit",
"description": "Supply reference images. Select a vision projector for GGUF text encoders; omit it if vision weights are included in the text encoder.",
"model_url": "https://huggingface.co/Qwen/Qwen-Image-Edit",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 2.5 VL 7B Text Encoder"
},
{
"id": "llm_vision",
"required": false,
"label": "Vision projector (required for separate GGUF text encoders)"
},
{
"id": "vae",
"required": false,
"label": "Qwen Image VAE"
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"flow_shift": 3.0,
"cfg_scale": 4,
"steps": 20
},
"features": [
"flow_shift",
"img_cfg",
"ref_images"
]
}
@@ -0,0 +1,36 @@
{
"schema_version": 1,
"id": "qwen-image-layered",
"name": "Qwen Image Layered",
"description": "Supply a reference image and use the dedicated layered VAE.",
"model_url": "https://huggingface.co/Qwen/Qwen-Image-Layered",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 2.5 VL 7B Text Encoder"
},
{
"id": "vae",
"required": true,
"label": "Qwen Image Layered VAE"
}
],
"defaults": {
"sampling_method": "euler",
"flow_shift": 3.0,
"cfg_scale": 4,
"steps": 20,
"width": 640,
"height": 640
},
"features": [
"flow_shift",
"img_cfg",
"ref_images"
]
}
+44
View File
@@ -0,0 +1,44 @@
{
"schema_version": 1,
"id": "qwen-image",
"name": "Qwen Image",
"description": "Supports text-to-image and image-to-image; pruned transformer variants use the same components.",
"model_url": "https://huggingface.co/Qwen/Qwen-Image",
"slots": [
{
"id": "diffusion_model",
"required": true
},
{
"id": "llm",
"required": true,
"label": "Qwen 2.5 VL 7B Text Encoder"
},
{
"id": "vae",
"required": false,
"label": "Qwen Image VAE"
},
{
"id": "tae",
"required": false
}
],
"required_any": [
[
"vae",
"tae"
]
],
"defaults": {
"sampling_method": "euler",
"flow_shift": 3.0,
"cfg_scale": 4,
"steps": 20,
"width": 1328,
"height": 1328
},
"features": [
"flow_shift"
]
}
+36
View File
@@ -0,0 +1,36 @@
{
"schema_version": 1,
"id": "sd-turbo",
"name": "SD Turbo",
"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": [
{
"id": "model",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"defaults": {
"sampling_method": "euler",
"scheduler": "discrete",
"cfg_scale": 1,
"steps": 1,
"width": 512,
"height": 512
},
"features": [
"cache_unet",
"clip_skip"
],
"disabled_features": [
"cache_dit"
]
}
@@ -0,0 +1,35 @@
{
"schema_version": 1,
"id": "sd1-checkpoint",
"name": "SD 1.x",
"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": [
{
"id": "model",
"required": true
},
{
"id": "vae",
"label": "VAE override",
"required": false
},
{
"id": "tae",
"required": false
}
],
"defaults": {
"cfg_scale": 7.5,
"steps": 20,
"width": 512,
"height": 512
},
"features": [
"cache_unet",
"clip_skip"
],
"disabled_features": [
"cache_dit"
]
}
@@ -0,0 +1,38 @@
{
"schema_version": 1,
"id": "sd1-controlnet",
"name": "SD 1.5 ControlNet",
"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": [
{
"id": "model",
"required": true
},
{
"id": "control_net",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"defaults": {
"cfg_scale": 7.5,
"steps": 20,
"width": 512,
"height": 512
},
"features": [
"cache_unet",
"clip_skip"
],
"disabled_features": [
"cache_dit"
]
}
@@ -0,0 +1,34 @@
{
"schema_version": 1,
"id": "sd1-inpainting",
"name": "SD 1.5 Inpainting",
"description": "Select an inpainting checkpoint, then supply an input image and mask.",
"slots": [
{
"id": "model",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"defaults": {
"cfg_scale": 7.5,
"steps": 20,
"width": 512,
"height": 512
},
"features": [
"cache_unet",
"clip_skip",
"img_cfg"
],
"disabled_features": [
"cache_dit"
]
}
@@ -0,0 +1,43 @@
{
"schema_version": 1,
"id": "sd1-ip-adapter",
"name": "SD 1.5 IP-Adapter / Plus",
"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": [
{
"id": "model",
"required": true
},
{
"id": "clip_vision",
"required": true,
"label": "CLIP Vision H"
},
{
"id": "ip_adapter",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"defaults": {
"cfg_scale": 7.5,
"steps": 20,
"width": 512,
"height": 512
},
"features": [
"cache_unet",
"clip_skip"
],
"disabled_features": [
"cache_dit"
]
}
@@ -0,0 +1,33 @@
{
"schema_version": 1,
"id": "sd2-512-checkpoint",
"name": "SD 2.1 512",
"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": [
{
"id": "model",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"defaults": {
"cfg_scale": 7.5,
"steps": 20,
"width": 512,
"height": 512
},
"features": [
"cache_unet",
"clip_skip"
],
"disabled_features": [
"cache_dit"
]
}
@@ -0,0 +1,32 @@
{
"schema_version": 1,
"id": "sd2-768-checkpoint",
"name": "SD 2.1 768",
"slots": [
{
"id": "model",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"defaults": {
"cfg_scale": 7.5,
"steps": 20,
"width": 768,
"height": 768
},
"features": [
"cache_unet",
"clip_skip"
],
"disabled_features": [
"cache_dit"
]
}
@@ -0,0 +1,36 @@
{
"schema_version": 1,
"id": "sd2-inpainting",
"name": "SD 2 Inpainting",
"description": "Select an inpainting checkpoint, then supply Init Image and Mask Image.",
"slots": [
{
"id": "model",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"defaults": {
"scheduler": "discrete",
"sampling_method": "euler_a",
"cfg_scale": 7.5,
"steps": 20,
"width": 512,
"height": 512
},
"features": [
"cache_unet",
"clip_skip",
"img_cfg"
],
"disabled_features": [
"cache_dit"
]
}
+33
View File
@@ -0,0 +1,33 @@
{
"schema_version": 1,
"id": "sd3-medium",
"name": "SD 3 Medium",
"description": "Checkpoint with CLIP-L, CLIP-G and T5 included.",
"model_url": "https://huggingface.co/stabilityai/stable-diffusion-3-medium-diffusers",
"slots": [
{
"id": "model",
"required": true
},
{
"id": "vae",
"required": false
},
{
"id": "tae",
"required": false
}
],
"defaults": {
"sampling_method": "euler",
"cfg_scale": 7,
"steps": 20,
"width": 1024,
"height": 1024
},
"features": [
"clip_skip",
"flow_shift",
"slg"
]
}
Loaded 100 of 132 files, more files were not shown because too many files have changed in this diff. Show more