Skip to content

Commit bd083a2

Browse files
committed
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]
1 parent 965949b commit bd083a2

2 files changed

Lines changed: 73 additions & 8 deletions

File tree

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

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -391,9 +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

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+
397406
int n_threads = threads;
398407
enum sd_type_t wtype = SD_TYPE_COUNT;
399408
enum rng_type_t rng_type = CUDA_RNG;
@@ -417,7 +426,9 @@ int load_model(const char *model, char *model_path, char* options[], int threads
417426
// If options is not NULL, parse options
418427
for (int i = 0; options[i] != NULL; i++) {
419428
const char *optname = strtok(options[i], ":");
420-
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, "");
421432
if (optval == NULL) {
422433
optval = "true";
423434
}
@@ -489,8 +500,18 @@ int load_model(const char *model, char *model_path, char* options[], int threads
489500
}
490501
}
491502
if (!strcmp(optname, "photo_maker_path")) photo_maker_path = strdup(optval);
503+
if (!strcmp(optname, "pulid_weights_path")) pulid_weights_path = strdup(optval);
492504
if (!strcmp(optname, "tensor_type_rules")) tensor_type_rules = strdup(optval);
493505

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+
494515
// vae_decode_only is still accepted for backwards compatibility with
495516
// existing gallery configs, but upstream dropped the option (the model
496517
// now decides), so it is parsed and ignored.
@@ -592,20 +613,22 @@ int load_model(const char *model, char *model_path, char* options[], int threads
592613
ctx_params.embeddings = embedding_vec.empty() ? NULL : embedding_vec.data();
593614
ctx_params.embedding_count = static_cast<uint32_t>(embedding_vec.size());
594615
ctx_params.photo_maker_path = photo_maker_path;
616+
if (strlen(pulid_weights_path) > 0) ctx_params.pulid_weights_path = pulid_weights_path;
595617
ctx_params.tensor_type_rules = tensor_type_rules;
596618
ctx_params.n_threads = n_threads;
597619
ctx_params.rng_type = rng_type;
598620
if (wtype != SD_TYPE_COUNT) ctx_params.wtype = wtype;
599621
if (sampler_rng_type != RNG_TYPE_COUNT) ctx_params.sampler_rng_type = sampler_rng_type;
600622
if (prediction != PREDICTION_COUNT) ctx_params.prediction = prediction;
601623
if (lora_apply_mode != LORA_APPLY_MODE_COUNT) ctx_params.lora_apply_mode = lora_apply_mode;
602-
// Upstream replaced the boolean CPU-offload knobs (offload_params_to_cpu,
603-
// keep_clip_on_cpu, keep_vae_on_cpu, keep_control_net_on_cpu) with backend
604-
// assignment specs. Translate the legacy options we still accept from
605-
// gallery configs into those specs, mirroring prepare_backend_assignments()
606-
// in the upstream CLI. These strings must outlive new_sd_ctx() below.
607-
std::string backend_spec;
608-
std::string params_backend_spec;
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;
609632
auto prepend_spec = [](std::string& spec, const char* assignment) {
610633
spec = spec.empty() ? std::string(assignment) : std::string(assignment) + "," + spec;
611634
};
@@ -615,6 +638,12 @@ int load_model(const char *model, char *model_path, char* options[], int threads
615638
if (keep_control_net_on_cpu) prepend_spec(backend_spec, "controlnet=cpu");
616639
if (!backend_spec.empty()) ctx_params.backend = backend_spec.c_str();
617640
if (!params_backend_spec.empty()) ctx_params.params_backend = params_backend_spec.c_str();
641+
if (strlen(rpc_servers_arg) > 0) ctx_params.rpc_servers = rpc_servers_arg;
642+
// max_vram: GiB budget or per-backend spec for graph-cut segmented param
643+
// offload ("0" = disabled, "-1" = auto). stream_layers only has effect when
644+
// max_vram is set.
645+
if (strlen(max_vram_arg) > 0) ctx_params.max_vram = max_vram_arg;
646+
ctx_params.stream_layers = stream_layers;
618647
ctx_params.diffusion_flash_attn = diffusion_flash_attn;
619648
ctx_params.tae_preview_only = tae_preview_only;
620649
ctx_params.diffusion_conv_direct = diffusion_conv_direct;

docs/content/features/image-generation.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,42 @@ 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+
74110

75111
### Diffusers
76112

0 commit comments

Comments
 (0)