fix(diffusers): pin diffusers and transformers to a known-good pair The diffusers backend tracked git+https://github.com/huggingface/diffusers (main) with an unpinned transformers. transformers v5 restructured CLIPTextModel and removed the .text_model attribute that diffusers' single -file loader reads, so loading any single-file Stable Diffusion checkpoint fails: create_diffusers_clip_model_from_ldm (single_file_utils.py) position_embedding_dim = model.text_model.embeddings.position_embedding... AttributeError: 'CLIPTextModel' object has no attribute 'text_model' No released diffusers (<=0.38.0) supports transformers v5 - only unreleased diffusers main does. Because the requirements tracked main plus an unpinned transformers, every backend image froze whichever pair existed at build time, and images built once transformers v5 shipped but before diffusers main caught up are permanently broken. Pin the last known-good released pair across all requirements files: diffusers==0.38.0 and transformers==4.57.6. 0.38.0 still exposes every pipeline backend.py imports (Flux, Wan, Sana, LTX2, Qwen, GGUF), so no functionality is lost, and builds become reproducible instead of drifting into the broken window. Fixes #9979 Assisted-by: Claude:claude-opus-4-8 [Claude Code] Signed-off-by: Adira Denis Muhando <dennisadira@gmail.com>
LocalAI Diffusers Backend
This backend provides gRPC access to Hugging Face diffusers pipelines with dynamic pipeline loading.
Creating a separate environment for the diffusers project
make diffusers
Dynamic Pipeline Loader
The diffusers backend includes a dynamic pipeline loader (diffusers_dynamic_loader.py) that automatically discovers and loads diffusers pipelines at runtime. This eliminates the need for per-pipeline conditional statements - new pipelines added to diffusers become available automatically without code changes.
How It Works
-
Pipeline Discovery: On first use, the loader scans the
diffuserspackage to find all classes that inherit fromDiffusionPipeline. -
Registry Caching: Discovery results are cached for the lifetime of the process to avoid repeated scanning.
-
Task Aliases: The loader automatically derives task aliases from class names (e.g., "text-to-image", "image-to-image", "inpainting") without hardcoding.
-
Multiple Resolution Methods: Pipelines can be resolved by:
- Exact class name (e.g.,
StableDiffusionPipeline) - Task alias (e.g.,
text-to-image,img2img) - Model ID (uses HuggingFace Hub to infer pipeline type)
- Exact class name (e.g.,
Usage Examples
from diffusers_dynamic_loader import (
load_diffusers_pipeline,
get_available_pipelines,
get_available_tasks,
resolve_pipeline_class,
discover_diffusers_classes,
get_available_classes,
)
# List all available pipelines
pipelines = get_available_pipelines()
print(f"Available pipelines: {pipelines[:10]}...")
# List all task aliases
tasks = get_available_tasks()
print(f"Available tasks: {tasks}")
# Resolve a pipeline class by name
cls = resolve_pipeline_class(class_name="StableDiffusionPipeline")
# Resolve by task alias
cls = resolve_pipeline_class(task="stable-diffusion")
# Load and instantiate a pipeline
pipe = load_diffusers_pipeline(
class_name="StableDiffusionPipeline",
model_id="runwayml/stable-diffusion-v1-5",
torch_dtype=torch.float16
)
# Load from single file
pipe = load_diffusers_pipeline(
class_name="StableDiffusionPipeline",
model_id="/path/to/model.safetensors",
from_single_file=True,
torch_dtype=torch.float16
)
# Discover other diffusers classes (schedulers, models, etc.)
schedulers = discover_diffusers_classes("SchedulerMixin")
print(f"Available schedulers: {list(schedulers.keys())[:5]}...")
# Get list of available scheduler classes
scheduler_list = get_available_classes("SchedulerMixin")
Generic Class Discovery
The dynamic loader can discover not just pipelines but any class type from diffusers:
# Discover all scheduler classes
schedulers = discover_diffusers_classes("SchedulerMixin")
# Discover all model classes
models = discover_diffusers_classes("ModelMixin")
# Get a sorted list of available classes
scheduler_names = get_available_classes("SchedulerMixin")
Special Pipeline Handling
Most pipelines are loaded dynamically through load_diffusers_pipeline(). Only pipelines requiring truly custom initialization logic are handled explicitly:
FluxTransformer2DModel: Requires quantization and custom transformer loading (cannot use dynamic loader)WanPipeline/WanImageToVideoPipeline: Uses dynamic loader with special VAE (float32 dtype)SanaPipeline: Uses dynamic loader with post-load dtype conversion for VAE/text encoderStableVideoDiffusionPipeline: Uses dynamic loader with CPU offload handlingVideoDiffusionPipeline: Alias for DiffusionPipeline with video flags
All other pipelines (StableDiffusionPipeline, StableDiffusionXLPipeline, FluxPipeline, etc.) are loaded purely through the dynamic loader.
Error Handling
When a pipeline cannot be resolved, the loader provides helpful error messages listing available pipelines and tasks:
ValueError: Unknown pipeline class 'NonExistentPipeline'.
Available pipelines: AnimateDiffPipeline, AnimateDiffVideoToVideoPipeline, ...
Environment Variables
| Variable | Default | Description |
|---|---|---|
COMPEL |
0 |
Enable Compel for prompt weighting |
SD_EMBED |
0 |
Enable sd_embed for prompt weighting |
XPU |
0 |
Enable Intel XPU support |
CLIPSKIP |
1 |
Enable CLIP skip support |
SAFETENSORS |
1 |
Use safetensors format |
CHUNK_SIZE |
8 |
Decode chunk size for video |
FPS |
7 |
Video frames per second |
DISABLE_CPU_OFFLOAD |
0 |
Disable CPU offload |
FRAMES |
64 |
Number of video frames |
BFL_REPO |
ChuckMcSneed/FLUX.1-dev |
Flux base repo |
PYTHON_GRPC_MAX_WORKERS |
1 |
Max gRPC workers |
Running Tests
./test.sh
The test suite includes:
- Unit tests for the dynamic loader (
test_dynamic_loader.py) - Integration tests for the gRPC backend (
test.py)