Skip to content

Commit f440340

Browse files
localai-botmudler
andauthored
chore: ⬆️ Update leejet/stable-diffusion.cpp to 5a34bc7f6e0621dd2f899daa64476eac667d7ed3 (#10335)
* ⬆️ Update leejet/stable-diffusion.cpp Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix(stablediffusion-ggml): adapt gosd.cpp to upstream sd_ctx_params_t API The bump to 5a34bc7 restructured sd_ctx_params_t: the boolean CPU-offload knobs (offload_params_to_cpu, keep_clip_on_cpu, keep_vae_on_cpu, keep_control_net_on_cpu) were replaced by backend assignment specs (backend/params_backend), and vae_decode_only / free_params_immediately were dropped entirely. The build broke with "no member named ..." on every arch. Translate the legacy options we still accept from gallery configs into the new backend assignment specs, mirroring prepare_backend_assignments() in the upstream CLI, so offload_params_to_cpu / keep_*_on_cpu keep working. vae_decode_only is parsed and ignored for config compatibility. Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:claude-opus-4-8 [Claude Code] * feat(stablediffusion-ggml): expose backend/params placement options The upstream bump introduced new sd_ctx_params_t fields for device and memory placement (backend, params_backend, rpc_servers, max_vram, stream_layers) plus PuLID-Flux weights (pulid_weights_path). Wire them up as backend options so models can be split across CPU/GPU/disk/RPC: - backend: per-component compute placement (e.g. clip=cpu,vae=cuda0) - params_backend: per-component weight storage incl. disk mmap - max_vram / stream_layers: graph-cut segmented parameter offload budget - rpc_servers: offload compute to remote RPC servers - pulid_weights_path: PuLID-Flux identity injection The legacy keep_*_on_cpu / offload_params_to_cpu booleans now seed and compose with the explicit backend/params_backend specs, matching upstream prepare_backend_assignments(). Option values are taken as everything after the first ':' so colon-bearing values (rpc_servers host:port) survive parsing. Documented the new options in the image-generation guide. Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:claude-opus-4-8 [Claude Code] * feat(stablediffusion-ggml): distributed RPC across ggml workers Enable the ggml RPC backend (-DSD_RPC=ON) so image generation can be sharded across remote rpc-server workers. The ggml rpc-server is backend-agnostic, so this reuses the exact same worker pool as the llama.cpp backend - one set of `local-ai worker llama-cpp-rpc` / `p2p-llama-cpp-rpc` workers accelerates both text and image generation. RPC servers are selected by precedence: - the explicit `rpc_servers` option, else - the LLAMACPP_GRPC_SERVERS env var, which LocalAI's p2p worker mode populates automatically with discovered workers (the backend inherits it from the parent process env), so distributed image generation needs no per-model configuration. Documented manual and p2p setup in the image-generation guide. Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:claude-opus-4-8 [Claude Code] --------- Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: mudler <2420543+mudler@users.noreply.github.com> Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
1 parent 6b9f1bd commit f440340

3 files changed

Lines changed: 124 additions & 11 deletions

File tree

backend/go/stablediffusion-ggml/Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,16 @@ JOBS?=$(shell nproc --ignore=1)
88

99
# stablediffusion.cpp (ggml)
1010
STABLEDIFFUSION_GGML_REPO?=https://github.com/leejet/stable-diffusion.cpp
11-
STABLEDIFFUSION_GGML_VERSION?=276025e054555166ec419413c6748ca79986ee93
11+
STABLEDIFFUSION_GGML_VERSION?=5a34bc7f6e0621dd2f899daa64476eac667d7ed3
1212

1313
CMAKE_ARGS+=-DGGML_MAX_NAME=128
1414

15+
# Enable the ggml RPC backend so generation can be sharded across remote
16+
# rpc-server workers (the same backend-agnostic ggml rpc-server used by the
17+
# llama.cpp backend). Servers are selected via the `rpc_servers` option or the
18+
# LLAMACPP_GRPC_SERVERS env var (populated automatically in p2p worker mode).
19+
CMAKE_ARGS+=-DSD_RPC=ON
20+
1521
ifeq ($(NATIVE),false)
1622
CMAKE_ARGS+=-DGGML_NATIVE=OFF
1723
endif

backend/go/stablediffusion-ggml/cpp/gosd.cpp

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -391,10 +391,18 @@ int load_model(const char *model, char *model_path, char* options[], int threads
391391
const char *control_net_path = "";
392392
const char *embedding_dir = "";
393393
const char *photo_maker_path = "";
394+
const char *pulid_weights_path = "";
394395
const char *tensor_type_rules = "";
395396
char *lora_dir = model_path;
396397

397-
bool vae_decode_only = true;
398+
// Upstream backend/parameter placement specs (see docs/.../stablediffusion).
399+
// Empty means "leave at upstream default" (nullptr).
400+
const char *backend_arg = "";
401+
const char *params_backend_arg = "";
402+
const char *rpc_servers_arg = "";
403+
const char *max_vram_arg = "";
404+
bool stream_layers = false;
405+
398406
int n_threads = threads;
399407
enum sd_type_t wtype = SD_TYPE_COUNT;
400408
enum rng_type_t rng_type = CUDA_RNG;
@@ -418,7 +426,9 @@ int load_model(const char *model, char *model_path, char* options[], int threads
418426
// If options is not NULL, parse options
419427
for (int i = 0; options[i] != NULL; i++) {
420428
const char *optname = strtok(options[i], ":");
421-
const char *optval = strtok(NULL, ":");
429+
// Take everything after the first ':' as the value so values may
430+
// themselves contain colons (e.g. rpc_servers host:port lists).
431+
const char *optval = strtok(NULL, "");
422432
if (optval == NULL) {
423433
optval = "true";
424434
}
@@ -490,9 +500,21 @@ int load_model(const char *model, char *model_path, char* options[], int threads
490500
}
491501
}
492502
if (!strcmp(optname, "photo_maker_path")) photo_maker_path = strdup(optval);
503+
if (!strcmp(optname, "pulid_weights_path")) pulid_weights_path = strdup(optval);
493504
if (!strcmp(optname, "tensor_type_rules")) tensor_type_rules = strdup(optval);
494505

495-
if (!strcmp(optname, "vae_decode_only")) vae_decode_only = (strcmp(optval, "true") == 0 || strcmp(optval, "1") == 0);
506+
// Backend / parameter placement specs (see prepare_backend_assignments
507+
// in the upstream CLI). These compose with the legacy keep_*_on_cpu /
508+
// offload_params_to_cpu booleans below.
509+
if (!strcmp(optname, "backend")) backend_arg = strdup(optval);
510+
if (!strcmp(optname, "params_backend")) params_backend_arg = strdup(optval);
511+
if (!strcmp(optname, "rpc_servers")) rpc_servers_arg = strdup(optval);
512+
if (!strcmp(optname, "max_vram")) max_vram_arg = strdup(optval);
513+
if (!strcmp(optname, "stream_layers")) stream_layers = (strcmp(optval, "true") == 0 || strcmp(optval, "1") == 0);
514+
515+
// vae_decode_only is still accepted for backwards compatibility with
516+
// existing gallery configs, but upstream dropped the option (the model
517+
// now decides), so it is parsed and ignored.
496518
if (!strcmp(optname, "offload_params_to_cpu")) offload_params_to_cpu = (strcmp(optval, "true") == 0 || strcmp(optval, "1") == 0);
497519
if (!strcmp(optname, "keep_clip_on_cpu")) keep_clip_on_cpu = (strcmp(optval, "true") == 0 || strcmp(optval, "1") == 0);
498520
if (!strcmp(optname, "keep_control_net_on_cpu")) keep_control_net_on_cpu = (strcmp(optval, "true") == 0 || strcmp(optval, "1") == 0);
@@ -591,20 +613,48 @@ int load_model(const char *model, char *model_path, char* options[], int threads
591613
ctx_params.embeddings = embedding_vec.empty() ? NULL : embedding_vec.data();
592614
ctx_params.embedding_count = static_cast<uint32_t>(embedding_vec.size());
593615
ctx_params.photo_maker_path = photo_maker_path;
616+
if (strlen(pulid_weights_path) > 0) ctx_params.pulid_weights_path = pulid_weights_path;
594617
ctx_params.tensor_type_rules = tensor_type_rules;
595-
ctx_params.vae_decode_only = vae_decode_only;
596-
// XXX: Setting to true causes a segfault on the second run
597-
ctx_params.free_params_immediately = false;
598618
ctx_params.n_threads = n_threads;
599619
ctx_params.rng_type = rng_type;
600-
ctx_params.keep_clip_on_cpu = keep_clip_on_cpu;
601620
if (wtype != SD_TYPE_COUNT) ctx_params.wtype = wtype;
602621
if (sampler_rng_type != RNG_TYPE_COUNT) ctx_params.sampler_rng_type = sampler_rng_type;
603622
if (prediction != PREDICTION_COUNT) ctx_params.prediction = prediction;
604623
if (lora_apply_mode != LORA_APPLY_MODE_COUNT) ctx_params.lora_apply_mode = lora_apply_mode;
605-
ctx_params.offload_params_to_cpu = offload_params_to_cpu;
606-
ctx_params.keep_control_net_on_cpu = keep_control_net_on_cpu;
607-
ctx_params.keep_vae_on_cpu = keep_vae_on_cpu;
624+
// Backend / parameter placement specs. Upstream replaced the boolean
625+
// CPU-offload knobs (offload_params_to_cpu, keep_clip_on_cpu, keep_vae_on_cpu,
626+
// keep_control_net_on_cpu) with these specs. Seed from the explicit
627+
// backend/params_backend options, then prepend the legacy boolean-derived
628+
// assignments, mirroring prepare_backend_assignments() in the upstream CLI.
629+
// These strings must outlive new_sd_ctx() below.
630+
std::string backend_spec = backend_arg;
631+
std::string params_backend_spec = params_backend_arg;
632+
auto prepend_spec = [](std::string& spec, const char* assignment) {
633+
spec = spec.empty() ? std::string(assignment) : std::string(assignment) + "," + spec;
634+
};
635+
if (offload_params_to_cpu) prepend_spec(params_backend_spec, "*=cpu");
636+
if (keep_clip_on_cpu) prepend_spec(backend_spec, "te=cpu");
637+
if (keep_vae_on_cpu) prepend_spec(backend_spec, "vae=cpu");
638+
if (keep_control_net_on_cpu) prepend_spec(backend_spec, "controlnet=cpu");
639+
if (!backend_spec.empty()) ctx_params.backend = backend_spec.c_str();
640+
if (!params_backend_spec.empty()) ctx_params.params_backend = params_backend_spec.c_str();
641+
// RPC servers: prefer the explicit option, otherwise fall back to the
642+
// LLAMACPP_GRPC_SERVERS env var. LocalAI's p2p worker mode populates that
643+
// var with discovered ggml rpc-server workers (shared with the llama.cpp
644+
// backend), so distributed image generation works with no extra config.
645+
if (strlen(rpc_servers_arg) > 0) {
646+
ctx_params.rpc_servers = rpc_servers_arg;
647+
} else {
648+
const char* env_rpc_servers = std::getenv("LLAMACPP_GRPC_SERVERS");
649+
if (env_rpc_servers != NULL && strlen(env_rpc_servers) > 0) {
650+
ctx_params.rpc_servers = env_rpc_servers;
651+
}
652+
}
653+
// max_vram: GiB budget or per-backend spec for graph-cut segmented param
654+
// offload ("0" = disabled, "-1" = auto). stream_layers only has effect when
655+
// max_vram is set.
656+
if (strlen(max_vram_arg) > 0) ctx_params.max_vram = max_vram_arg;
657+
ctx_params.stream_layers = stream_layers;
608658
ctx_params.diffusion_flash_attn = diffusion_flash_attn;
609659
ctx_params.tae_preview_only = tae_preview_only;
610660
ctx_params.diffusion_conv_direct = diffusion_conv_direct;

docs/content/features/image-generation.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,63 @@ options:
7171
2. Download the required assets to the `models` repository
7272
3. Start LocalAI
7373

74+
#### Memory and device placement options
75+
76+
When a model does not fit entirely in VRAM, the following `options:` control where weights and computation are placed. They map directly to the upstream stable-diffusion.cpp options.
77+
78+
| Option | Example | Description |
79+
|--------|---------|-------------|
80+
| `backend` | `backend:clip=cpu,vae=cuda0,diffusion=vulkan0` | Runtime (compute) backend assignment per component. Use `cpu` to place a component's compute on the CPU. Component keys include `te` (text encoder / CLIP), `vae`, `diffusion`, `controlnet`. |
81+
| `params_backend` | `params_backend:diffusion=disk,clip=cpu` | Where parameters (weights) are stored. Supports `cpu`, `disk` (mmap weights from disk to save RAM/VRAM), or per-component specs. |
82+
| `max_vram` | `max_vram:8` or `max_vram:-1` | VRAM budget (in GiB) for graph-cut segmented parameter offload. `0` disables it, `-1` auto-selects (free VRAM minus ~1 GiB). Also accepts per-backend budgets. |
83+
| `stream_layers` | `stream_layers:true` | Enable residency + prefetch streaming on top of `max_vram` (no effect unless `max_vram` is set). |
84+
| `rpc_servers` | `rpc_servers:localhost:50052,192.168.1.3:50052` | Comma-separated list of `host:port` RPC servers to offload compute to. |
85+
| `pulid_weights_path` | `pulid_weights_path:pulid.safetensors` | Path to PuLID-Flux weights for identity injection. |
86+
87+
The following convenience booleans are still accepted and are translated into the `backend` / `params_backend` specs above:
88+
89+
| Option | Equivalent spec |
90+
|--------|-----------------|
91+
| `offload_params_to_cpu:true` | `params_backend` += `*=cpu` |
92+
| `keep_clip_on_cpu:true` | `backend` += `te=cpu` |
93+
| `keep_vae_on_cpu:true` | `backend` += `vae=cpu` |
94+
| `keep_control_net_on_cpu:true` | `backend` += `controlnet=cpu` |
95+
96+
For example, to mmap the diffusion weights from disk while keeping the text encoder on the CPU:
97+
98+
```yaml
99+
options:
100+
- "diffusion_model"
101+
- "sampler:euler"
102+
- "params_backend:diffusion=disk"
103+
- "keep_clip_on_cpu:true"
104+
```
105+
106+
{{% alert note %}}
107+
`vae_decode_only` is still accepted for backwards compatibility but is now a no-op: upstream removed the flag and the model decides automatically.
108+
{{% /alert %}}
109+
110+
#### Distributed inference (RPC workers)
111+
112+
The `stablediffusion-ggml` backend can offload computation to remote `ggml` RPC workers, sharding a model that does not fit on a single machine. It reuses the **same backend-agnostic `rpc-server` workers as the llama.cpp backend**, so one worker pool can serve both.
113+
114+
**Manual:** point the model at running workers with the `rpc_servers` option:
115+
116+
```yaml
117+
options:
118+
- "rpc_servers:192.168.1.10:50052,192.168.1.11:50052"
119+
```
120+
121+
Start a worker on each remote machine the same way you would for llama.cpp:
122+
123+
```bash
124+
local-ai worker llama-cpp-rpc --llama-cpp-args="--host 0.0.0.0 --port 50052"
125+
```
126+
127+
**Automatic (peer-to-peer):** when LocalAI runs in [p2p worker mode]({{%relref "features/distributed_inferencing" %}}), discovered workers are published in the `LLAMACPP_GRPC_SERVERS` environment variable. The image-generation backend reads that variable automatically (when `rpc_servers` is not set), so the same `local-ai worker p2p-llama-cpp-rpc` workers used for text generation also accelerate image generation - no per-model configuration needed.
128+
129+
By default the RPC devices join the pool and participate in placement; combine with the `backend` / `params_backend` options above to pin specific components to them (e.g. `backend:diffusion=rpc0`).
130+
74131

75132
### Diffusers
76133

0 commit comments

Comments
 (0)