ai-toolkit inference pipeline and related ComfyUI nodes for inferencing.
6
stars
93
commits
Python
primary language
Aug 11, 2026
updated
Reference Diffusers LoRA inference pipelines (plus an optional HTTP server) for LoRAs trained with ostris/ai-toolkit, designed to minimize AI Toolkit training sample vs inference output drift so your real inference outputs are consistent with the samples you validated during training, and remain reproducible across environments.
Supports 35+ models across image generation, editing, and video: FLUX.1/FLUX.2 (including FLUX.2-klein), Flex, SD/SDXL, Qwen Image (and Edit variants), Z-Image, Krea 2 (including Turbo and Edit), Wan 2.1/2.2, LTX-2/2.3, Chroma, HiDream, Lumina2, OmniGen2, and more.
Docs Home · Model Catalog · ComfyUI · Cloud AI Toolkit (Train+Inference) · Quickstart · API
You're in the right place if any of these is true:
ostris/ai-toolkit and now need a known-good Diffusers inference pipeline (Python or HTTP API).Note: if your main blocker is environment drift (CUDA/PyTorch/Diffusers versions, large model downloads, custom pipeline deps), running the same stack in a fixed runtime/container helps. RunComfy provides a managed runtime for AI Toolkit training + inference, but the reference behavior is still defined by the code in this repo.
Popular model docs (each page includes defaults + what commonly causes preview mismatch for that model):
model="ltx2")model="ltx2.3")model="wan22_14b_t2v")model="wan22_14b_i2v")model="zimage_turbo")model="krea2", plus krea2_turbo / krea2_o_edit / krea2_o_edit_turbo)model="minimax_h3") — video with joint stereo audiomodel="flux2")model="flux2_klein_4b" / flux2_klein_9b)model="flux_kontext")model="flex1")model="qwen_image" and variants)model="sdxl")This repo publishes tags and releases. Each tag corresponds to a specific ai-toolkit version as defined by its version.py.
Since ai-toolkit does not publish tags or releases, we pin and document the exact ai-toolkit commit that contains that version.py.
| ai-toolkit-inference tag | ai-toolkit version (version.py) | ai-toolkit commit |
|---|---|---|
v0.7.19.202601281 | 0.7.19 | 73dedbf662ca604a3035daff2d2ba4635473b7bd |
v0.7.20.202601291 | 0.7.20 | a6da9e37ac414658fce66646846648b6ee0407a8 |
v0.7.21.202601291 | 0.7.21 | 2db090144a8e6b568104ec5808a2f957545d9c50 |
v0.7.23.202602241 | 0.7.23 | de7d22c9becf5f3385348d9d5ff901536c340d0c |
v0.7.24.202603201 | 0.7.24 | 57d407cfd4e2ab884993fb5c7a6373d7e6785b51 |
v0.7.29.202603241 | 0.7.29 | 4ad14d211a969c217bf5470213c04c6052d17592 |
v0.7.29.202604051 | 0.7.29 | 4ad14d211a969c217bf5470213c04c6052d17592 |
v0.11.0.202607241 | 0.11.0 | c4db100e172064117ad18b46898226a036c1b197 |
v0.12.2.202608041 | 0.12.2 | a9a04547e92a51583999ea2e7da8792da80e0d7a |
v0.12.2.202608111 | 0.12.2 | a9a04547e92a51583999ea2e7da8792da80e0d7a |
v0.12.11.202608111 | 0.12.11 | 356ce7e84eb00222a594f3faf1117c3b9313a0d5 |
The first 0.12.2 row is the MiniMax-H3 release, built from ai-toolkit tag
v0.12.2-202608042. Both registries carry that image at digest
sha256:435e4722351ceb5caa672f5f7b201089b09a7bcfe2fca8f3112c93098cbf5da6 —
the immutable identity to pull or verify by, where the tag is merely mutable.
v0.12.2.202608111 is a hotfix rebuild against ai-toolkit v0.12.2-202608111,
which cherry-picks upstream 139a38f5
(fp32 AdaLN — pruned checkpoints store those projections fp16, whose 65504
ceiling overflows; suspected trigger of illegal-memory-access crashes on
production workers) and its follow-up 7309db4d.
Nothing else moves relative to -202608042. Side effect worth knowing: the fix
calls the adaln projection functionally, so LoRA deltas on
blocks.N.adaln_proj.linear no longer apply — on the trainer and here, in
lockstep (both attach via the module-call machinery the functional call
bypasses). This release also makes the worker exit (code 70) on a fatal CUDA
error instead of serving further requests from a poisoned context.
v0.12.11.202608111 pairs with ai-toolkit v0.12.11-202608111 — the trainer
and inference moving to the same toolkit generation together. Relative to the
0.12.2 hotfix it inherits upstream's H3 sampler regrid (685ce37a: the same
sample_steps now yields exactly that many model evaluations, previously one
fewer) and carries the finished fatal-CUDA fail-fast (PR #31, all review rounds:
probe-only kill decisions on the configured device, OOM never fatal, config
errors never poison). The H3 training adapter that 0.12.11 makes the default
training method does NOT need mirroring here: the trainer deactivates it while
rendering previews, so previews remain base + user LoRA — which is what this
server runs.
Note that a9a04547 predates upstream's 18f5810d ("Adjust default
alpha for h3"), which pairs network.linear_alpha with the rank in the H3 arch
entry. Our build carries an equivalent change, so H3 LoRAs train at the intended
1.0x strength rather than 2.0x — the pinned commit alone would not tell you that.
This repo has two parts that work together:
src/ — the runnable inference implementation:
docs/ — developer docs:
If you only read one thing: treat src/ as the source of truth.
This repo can be used as a ComfyUI custom node pack. Install via ComfyUI-Manager for automatic dependency setup (including ostris/ai-toolkit for extended models).
See: ComfyUI.md
This is the smallest runnable path if you just want an HTTP endpoint for Diffusers LoRA inference.
See: Installation
curl http://localhost:8000/v1/models
The API takes loras[].path and resolves local paths under WORKFLOWS_BASE_PATH:
{WORKFLOWS_BASE_PATH}/{loras[].path}
Path resolution and validation live in:
Notes:
loras is required for all requests.loras[].path must include the full filename (e.g. my_lora.safetensors).transformer: "low" / "high" (see the API section).loras[].path can be a URL; the server will download and cache it.python -m uvicorn src.server:app --host 0.0.0.0 --port 8000
Entry point:
Settings / environment variables:
FastAPI docs (interactive):
GET /docs (Swagger UI)GET /redoccurl -X POST "http://localhost:8000/v1/inference" \
-H "Content-Type: application/json" \
-d '{
"model": "zimage_turbo",
"trigger_word": "sks",
"loras": [{"path": "my_lora_job/my_lora_job.safetensors", "network_multiplier": 1.0}],
"prompts": [
{
"prompt": "[trigger] a photo of a person",
"width": 1024,
"height": 1024,
"seed": 42,
"sample_steps": 8,
"guidance_scale": 1.0,
"neg": ""
}
]
}'
You'll get a request_id plus status_url and result_url, then poll:
GET /v1/requests/{request_id}/statusGET /v1/requests/{request_id}/resultRequest schema (authoritative):
Response schema:
Outputs are written under OUTPUT_BASE_PATH/ as local files prefixed by the request_id:
OUTPUT_BASE_PATH/{request_id}_output_{i}.jpgOUTPUT_BASE_PATH/{request_id}_output_{i}.mp4The result endpoint returns local file_path values for images/videos (no object storage integration by default).
Requirements:
ostris/ai-toolkit (optional; required for extended models—see below)# Install PyTorch with CUDA (adjust cu126 to match your CUDA version)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
# Install inference dependencies
pip install -r requirements-inference.txt
Some pipelines (FLUX.2, Chroma, HiDream, OmniGen2, Wan 2.2 I2V/5B, LTX-2, Krea 2) require custom classes from ostris/ai-toolkit.
Automatic setup (recommended):
Run the install script to clone ai-toolkit into vendor/ai-toolkit:
python install.py
The code automatically detects vendor/ai-toolkit at runtime—no environment variable needed.
Manual setup (advanced):
If you prefer to manage ai-toolkit separately, clone it anywhere and set AI_TOOLKIT_PATH:
git clone https://github.com/ostris/ai-toolkit.git /path/to/ai-toolkit
export AI_TOOLKIT_PATH=/path/to/ai-toolkit
Tip: if a model requires ai-toolkit and it's missing, you'll see an ImportError referencing extensions_built_in... or toolkit....
| Variable | Description | Default |
|---|---|---|
HOST | Server host | 0.0.0.0 |
PORT | Server port | 8000 |
BASE_URL | Used to build status_url / result_url in responses | http://localhost:8000 |
DEVICE | Device: cuda or cpu | cuda |
ENABLE_CPU_OFFLOAD | Enable model CPU offload (helps fit big models) | false |
WORKFLOWS_BASE_PATH | Base path for LoRA weight directories | /app/ai-toolkit/lora_weights |
OUTPUT_BASE_PATH | Output path for images/videos | /tmp/inference_output |
AI_TOOLKIT_PATH | Path to ai-toolkit (only needed for some models) | auto-detected (vendor/ai-toolkit if present) |
For the full set of settings (e.g. DEBUG, HF_TOKEN, MODEL_CACHE_DIR, INFERENCE_TIMEOUT), see:
POST /v1/inference — submit an async inference requestGET /v1/requests/{request_id}/status — returns: in_queue, in_progress, succeeded, failedGET /v1/requests/{request_id}/result — returns the generated images/videos (local file paths)GET /v1/models — list supported model IDs + defaultsImplementation:
src/api/v1/inference.pyIf you're unsure which model values are accepted (or what defaults a model uses), call:
GET /v1/modelsThe canonical list in code:
src/schemas/models.pysrc/pipelines/__init__.pyFor model="wan22_14b_t2v" and model="wan22_14b_i2v", loras must use MoE format:
{
"model": "wan22_14b_t2v",
"loras": [
{ "path": "my_wan_lora/low_noise.safetensors", "transformer": "low", "network_multiplier": 1.0 },
{ "path": "my_wan_lora/high_noise.safetensors", "transformer": "high", "network_multiplier": 1.0 }
],
"prompts": [
{ "prompt": "a cinematic shot", "width": 1280, "height": 720, "seed": 42 }
]
}
If you send MoE format to a non-MoE model (or multiple LoRAs to a single-LoRA model), the API will return a 400 with details.
AI Toolkit "Samples" are generated by a specific inference graph: base model variant + scheduler/timestep logic + guidance behavior + prompt encoding + resolution rules + LoRA injection + seed handling.
If your inference environment changes any of those (even with the same prompt/seed), results can drift. This tends to show up most aggressively on:
A pragmatic checklist (common mismatch causes):
resolution_divisor.src/pipelines/base.pysrc/pipelines/base.pyfuse_lora vs model-specific merges.src/pipelines/base.pyctrl_img (or ctrl_img_1..3).src/api/v1/inference.pyIf you're trying to reproduce the preview you validated during training:
For "by model" notes: https://ai-toolkit-docs.runcomfy.com/models/
If you're integrating these pipelines into your own app (instead of running the server as-is), these are the files that define behavior:
src/pipelines/base.pymodel → pipeline class):src/pipelines/__init__.pysrc/schemas/request.pysrc/api/v1/inference.py# Run tests
pytest
# Run with hot reload
python -m uvicorn src.server:app --reload
The included Dockerfile may be tailored to a specific production runtime and may not be a drop-in build for all environments.
If you just want to run the server locally, follow:
If you need a portable container build, use this repo as the source of truth and create a minimal CUDA-enabled image that:
requirements-inference.txtWORKFLOWS_BASE_PATH and OUTPUT_BASE_PATHpython -m uvicorn src.server:app ...Most often it's not "the scale is wrong", it's one of:
A reliable starting point is to run through the server once, then mirror the pipeline code.
Treat this as an inference-graph mismatch problem. Verify steps/guidance, resolution snapping, LoRA loading mode (adapter vs fuse), and any required control inputs. Then check the model page for model-specific mismatch causes.
Different stacks often implement slightly different step semantics, schedulers, or LoRA application order. This repo is meant to give you a concrete Diffusers reference to compare against.
Python
100.0%
ai-toolkit inference pipeline and related ComfyUI nodes for inferencing.
6
stars
93
commits
Python
primary language
Aug 11, 2026
updated
Reference Diffusers LoRA inference pipelines (plus an optional HTTP server) for LoRAs trained with ostris/ai-toolkit, designed to minimize AI Toolkit training sample vs inference output drift so your real inference outputs are consistent with the samples you validated during training, and remain reproducible across environments.
Supports 35+ models across image generation, editing, and video: FLUX.1/FLUX.2 (including FLUX.2-klein), Flex, SD/SDXL, Qwen Image (and Edit variants), Z-Image, Krea 2 (including Turbo and Edit), Wan 2.1/2.2, LTX-2/2.3, Chroma, HiDream, Lumina2, OmniGen2, and more.
Docs Home · Model Catalog · ComfyUI · Cloud AI Toolkit (Train+Inference) · Quickstart · API
You're in the right place if any of these is true:
ostris/ai-toolkit and now need a known-good Diffusers inference pipeline (Python or HTTP API).Note: if your main blocker is environment drift (CUDA/PyTorch/Diffusers versions, large model downloads, custom pipeline deps), running the same stack in a fixed runtime/container helps. RunComfy provides a managed runtime for AI Toolkit training + inference, but the reference behavior is still defined by the code in this repo.
Popular model docs (each page includes defaults + what commonly causes preview mismatch for that model):
model="ltx2")model="ltx2.3")model="wan22_14b_t2v")model="wan22_14b_i2v")model="zimage_turbo")model="krea2", plus krea2_turbo / krea2_o_edit / krea2_o_edit_turbo)model="minimax_h3") — video with joint stereo audiomodel="flux2")model="flux2_klein_4b" / flux2_klein_9b)model="flux_kontext")model="flex1")model="qwen_image" and variants)model="sdxl")This repo publishes tags and releases. Each tag corresponds to a specific ai-toolkit version as defined by its version.py.
Since ai-toolkit does not publish tags or releases, we pin and document the exact ai-toolkit commit that contains that version.py.
| ai-toolkit-inference tag | ai-toolkit version (version.py) | ai-toolkit commit |
|---|---|---|
v0.7.19.202601281 | 0.7.19 | 73dedbf662ca604a3035daff2d2ba4635473b7bd |
v0.7.20.202601291 | 0.7.20 | a6da9e37ac414658fce66646846648b6ee0407a8 |
v0.7.21.202601291 | 0.7.21 | 2db090144a8e6b568104ec5808a2f957545d9c50 |
v0.7.23.202602241 | 0.7.23 | de7d22c9becf5f3385348d9d5ff901536c340d0c |
v0.7.24.202603201 | 0.7.24 | 57d407cfd4e2ab884993fb5c7a6373d7e6785b51 |
v0.7.29.202603241 | 0.7.29 | 4ad14d211a969c217bf5470213c04c6052d17592 |
v0.7.29.202604051 | 0.7.29 | 4ad14d211a969c217bf5470213c04c6052d17592 |
v0.11.0.202607241 | 0.11.0 | c4db100e172064117ad18b46898226a036c1b197 |
v0.12.2.202608041 | 0.12.2 | a9a04547e92a51583999ea2e7da8792da80e0d7a |
v0.12.2.202608111 | 0.12.2 | a9a04547e92a51583999ea2e7da8792da80e0d7a |
v0.12.11.202608111 | 0.12.11 | 356ce7e84eb00222a594f3faf1117c3b9313a0d5 |
The first 0.12.2 row is the MiniMax-H3 release, built from ai-toolkit tag
v0.12.2-202608042. Both registries carry that image at digest
sha256:435e4722351ceb5caa672f5f7b201089b09a7bcfe2fca8f3112c93098cbf5da6 —
the immutable identity to pull or verify by, where the tag is merely mutable.
v0.12.2.202608111 is a hotfix rebuild against ai-toolkit v0.12.2-202608111,
which cherry-picks upstream 139a38f5
(fp32 AdaLN — pruned checkpoints store those projections fp16, whose 65504
ceiling overflows; suspected trigger of illegal-memory-access crashes on
production workers) and its follow-up 7309db4d.
Nothing else moves relative to -202608042. Side effect worth knowing: the fix
calls the adaln projection functionally, so LoRA deltas on
blocks.N.adaln_proj.linear no longer apply — on the trainer and here, in
lockstep (both attach via the module-call machinery the functional call
bypasses). This release also makes the worker exit (code 70) on a fatal CUDA
error instead of serving further requests from a poisoned context.
v0.12.11.202608111 pairs with ai-toolkit v0.12.11-202608111 — the trainer
and inference moving to the same toolkit generation together. Relative to the
0.12.2 hotfix it inherits upstream's H3 sampler regrid (685ce37a: the same
sample_steps now yields exactly that many model evaluations, previously one
fewer) and carries the finished fatal-CUDA fail-fast (PR #31, all review rounds:
probe-only kill decisions on the configured device, OOM never fatal, config
errors never poison). The H3 training adapter that 0.12.11 makes the default
training method does NOT need mirroring here: the trainer deactivates it while
rendering previews, so previews remain base + user LoRA — which is what this
server runs.
Note that a9a04547 predates upstream's 18f5810d ("Adjust default
alpha for h3"), which pairs network.linear_alpha with the rank in the H3 arch
entry. Our build carries an equivalent change, so H3 LoRAs train at the intended
1.0x strength rather than 2.0x — the pinned commit alone would not tell you that.
This repo has two parts that work together:
src/ — the runnable inference implementation:
docs/ — developer docs:
If you only read one thing: treat src/ as the source of truth.
This repo can be used as a ComfyUI custom node pack. Install via ComfyUI-Manager for automatic dependency setup (including ostris/ai-toolkit for extended models).
See: ComfyUI.md
This is the smallest runnable path if you just want an HTTP endpoint for Diffusers LoRA inference.
See: Installation
curl http://localhost:8000/v1/models
The API takes loras[].path and resolves local paths under WORKFLOWS_BASE_PATH:
{WORKFLOWS_BASE_PATH}/{loras[].path}
Path resolution and validation live in:
Notes:
loras is required for all requests.loras[].path must include the full filename (e.g. my_lora.safetensors).transformer: "low" / "high" (see the API section).loras[].path can be a URL; the server will download and cache it.python -m uvicorn src.server:app --host 0.0.0.0 --port 8000
Entry point:
Settings / environment variables:
FastAPI docs (interactive):
GET /docs (Swagger UI)GET /redoccurl -X POST "http://localhost:8000/v1/inference" \
-H "Content-Type: application/json" \
-d '{
"model": "zimage_turbo",
"trigger_word": "sks",
"loras": [{"path": "my_lora_job/my_lora_job.safetensors", "network_multiplier": 1.0}],
"prompts": [
{
"prompt": "[trigger] a photo of a person",
"width": 1024,
"height": 1024,
"seed": 42,
"sample_steps": 8,
"guidance_scale": 1.0,
"neg": ""
}
]
}'
You'll get a request_id plus status_url and result_url, then poll:
GET /v1/requests/{request_id}/statusGET /v1/requests/{request_id}/resultRequest schema (authoritative):
Response schema:
Outputs are written under OUTPUT_BASE_PATH/ as local files prefixed by the request_id:
OUTPUT_BASE_PATH/{request_id}_output_{i}.jpgOUTPUT_BASE_PATH/{request_id}_output_{i}.mp4The result endpoint returns local file_path values for images/videos (no object storage integration by default).
Requirements:
ostris/ai-toolkit (optional; required for extended models—see below)# Install PyTorch with CUDA (adjust cu126 to match your CUDA version)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
# Install inference dependencies
pip install -r requirements-inference.txt
Some pipelines (FLUX.2, Chroma, HiDream, OmniGen2, Wan 2.2 I2V/5B, LTX-2, Krea 2) require custom classes from ostris/ai-toolkit.
Automatic setup (recommended):
Run the install script to clone ai-toolkit into vendor/ai-toolkit:
python install.py
The code automatically detects vendor/ai-toolkit at runtime—no environment variable needed.
Manual setup (advanced):
If you prefer to manage ai-toolkit separately, clone it anywhere and set AI_TOOLKIT_PATH:
git clone https://github.com/ostris/ai-toolkit.git /path/to/ai-toolkit
export AI_TOOLKIT_PATH=/path/to/ai-toolkit
Tip: if a model requires ai-toolkit and it's missing, you'll see an ImportError referencing extensions_built_in... or toolkit....
| Variable | Description | Default |
|---|---|---|
HOST | Server host | 0.0.0.0 |
PORT | Server port | 8000 |
BASE_URL | Used to build status_url / result_url in responses | http://localhost:8000 |
DEVICE | Device: cuda or cpu | cuda |
ENABLE_CPU_OFFLOAD | Enable model CPU offload (helps fit big models) | false |
WORKFLOWS_BASE_PATH | Base path for LoRA weight directories | /app/ai-toolkit/lora_weights |
OUTPUT_BASE_PATH | Output path for images/videos | /tmp/inference_output |
AI_TOOLKIT_PATH | Path to ai-toolkit (only needed for some models) | auto-detected (vendor/ai-toolkit if present) |
For the full set of settings (e.g. DEBUG, HF_TOKEN, MODEL_CACHE_DIR, INFERENCE_TIMEOUT), see:
POST /v1/inference — submit an async inference requestGET /v1/requests/{request_id}/status — returns: in_queue, in_progress, succeeded, failedGET /v1/requests/{request_id}/result — returns the generated images/videos (local file paths)GET /v1/models — list supported model IDs + defaultsImplementation:
src/api/v1/inference.pyIf you're unsure which model values are accepted (or what defaults a model uses), call:
GET /v1/modelsThe canonical list in code:
src/schemas/models.pysrc/pipelines/__init__.pyFor model="wan22_14b_t2v" and model="wan22_14b_i2v", loras must use MoE format:
{
"model": "wan22_14b_t2v",
"loras": [
{ "path": "my_wan_lora/low_noise.safetensors", "transformer": "low", "network_multiplier": 1.0 },
{ "path": "my_wan_lora/high_noise.safetensors", "transformer": "high", "network_multiplier": 1.0 }
],
"prompts": [
{ "prompt": "a cinematic shot", "width": 1280, "height": 720, "seed": 42 }
]
}
If you send MoE format to a non-MoE model (or multiple LoRAs to a single-LoRA model), the API will return a 400 with details.
AI Toolkit "Samples" are generated by a specific inference graph: base model variant + scheduler/timestep logic + guidance behavior + prompt encoding + resolution rules + LoRA injection + seed handling.
If your inference environment changes any of those (even with the same prompt/seed), results can drift. This tends to show up most aggressively on:
A pragmatic checklist (common mismatch causes):
resolution_divisor.src/pipelines/base.pysrc/pipelines/base.pyfuse_lora vs model-specific merges.src/pipelines/base.pyctrl_img (or ctrl_img_1..3).src/api/v1/inference.pyIf you're trying to reproduce the preview you validated during training:
For "by model" notes: https://ai-toolkit-docs.runcomfy.com/models/
If you're integrating these pipelines into your own app (instead of running the server as-is), these are the files that define behavior:
src/pipelines/base.pymodel → pipeline class):src/pipelines/__init__.pysrc/schemas/request.pysrc/api/v1/inference.py# Run tests
pytest
# Run with hot reload
python -m uvicorn src.server:app --reload
The included Dockerfile may be tailored to a specific production runtime and may not be a drop-in build for all environments.
If you just want to run the server locally, follow:
If you need a portable container build, use this repo as the source of truth and create a minimal CUDA-enabled image that:
requirements-inference.txtWORKFLOWS_BASE_PATH and OUTPUT_BASE_PATHpython -m uvicorn src.server:app ...Most often it's not "the scale is wrong", it's one of:
A reliable starting point is to run through the server once, then mirror the pipeline code.
Treat this as an inference-graph mismatch problem. Verify steps/guidance, resolution snapping, LoRA loading mode (adapter vs fuse), and any required control inputs. Then check the model page for model-specific mismatch causes.
Different stacks often implement slightly different step semantics, schedulers, or LoRA application order. This repo is meant to give you a concrete Diffusers reference to compare against.
Python
100.0%