فهرست منبع

fix: the cache mode list was short, and caching could not be turned off

Two problems behind one report.

The list had two modes. The field's own description in the schema names six -
easycache, ucache, dbcache, taylorseer, cache_dit, spectrum - and the values
map in /options/generation had caught only two of them. cache_mode has no
enum in the schema either, so nothing machine-readable is complete; the list
is written out in the generator with a note of where it came from.

Turning caching off was impossible, which is the worse half. The server reads
an absent cache_mode as "use whatever the loaded architecture prefers" and an
empty one as "off" - and a dropdown cannot say both with the same empty
string. Choosing the blank entry sent nothing, so Z-Image's preset, which
switches easycache on, won every time. There is now an explicit "Off" that
sends the empty the server wants.

Verified against the running server, by reading back what it recorded for
each job: the architecture default sends no cache_mode at all, "Off" sends
cache_mode="" and the server keeps it rather than substituting the preset,
and a named mode sends that mode. What the renderer then does with an empty
is its own business - its description says empty is disabled, and that is as
far as this can be checked from outside.

The generic empty label now says what it means: "(architecture default, which
may switch it on)". "(architecture default)" gave no hint that the default
might not be off.

66 passed.
fszontagh 1 ماه پیش
والد
کامیت
030a997c07

+ 8 - 1
nodes/sdcpp/sdcpp-edit.js

@@ -172,7 +172,7 @@ const configSchema = {
             dynamicOptions: { source: 'credentials', filter: { type: ['sdcpp', 'basic'] } }
         },
         batchCount: { title: "Batch Count", description: "Number of independent images to produce in this single job. Each image gets a different seed (`seed`, `seed+1`, …). Total VRAM doesn't grow with batch_count - sd.cpp serializes. Recommended: 1 for interactive, higher when you want a grid of variations from one prompt. Leave empty for the architecture default.", type: "number" },
-        cacheMode: { title: "Cache Mode", description: "DiT-model intermediate caching strategy. Skips redundant computation across consecutive sampler steps when the model's intermediate state hasn't changed enough to matter. `easycache` is the simplest; `spectrum` is the newest, generally best for Flux/SD3/Z-Image. Recommended: spectrum for Flux/SD3/Z-Image when generation is too slow. Off for SD1.5/SDXL (UNet is too small for caching to win). Leave empty for the architecture default.", type: "string", enum: ["", "easycache", "spectrum"], enumLabels: ["(architecture default)", "EasyCache - single threshold, simple", "Spectrum - frequency-domain analysis (best quality/speed tradeoff)"], default: "" },
+        cacheMode: { title: "Cache Mode", description: "DiT-model intermediate caching strategy. Skips redundant computation across consecutive sampler steps when the model's intermediate state hasn't changed enough to matter. `easycache` is the simplest; `spectrum` is the newest, generally best for Flux/SD3/Z-Image. Recommended: spectrum for Flux/SD3/Z-Image when generation is too slow. Off for SD1.5/SDXL (UNet is too small for caching to win). Leave empty for the architecture default.", type: "string", enum: ["", "off", "easycache", "ucache", "dbcache", "taylorseer", "cache_dit", "spectrum"], enumLabels: ["(architecture default, which may switch it on)", "Off - no caching, whatever the architecture prefers", "EasyCache - single threshold, simple", "UCache", "DBCache", "TaylorSeer", "Cache-DiT", "Spectrum - frequency-domain analysis (best quality/speed tradeoff)"], default: "" },
         cfgScale: { title: "CFG Scale", description: "Classifier-Free Guidance scale. Strength of pushing the cond toward the prompt vs the uncond. Higher = follows prompt more aggressively but can over-saturate. Set to 1.0 to disable CFG (skips the uncond pass - twice as fast). Recommended: SD1.5: 7. SDXL: 4-8. Flux Dev: 1 (CFG bypassed in flow models). Z-Image: 1. Schnell/Turbo: 1. Leave empty for the architecture default.", type: "number" },
         clipSkip: { title: "CLIP Skip", description: "Skip the last N layers of CLIP when encoding the prompt. -1 = use the model's recommended default. 2 is the classic anime-model setting. Recommended: -1 (auto). Set to 2 for anime/cartoon SD1.5 fine-tunes. Leave empty for the architecture default.", type: "number" },
         controlImageBase64: { title: "ControlNet Image (base64)", description: "Base64-encoded pre-processed control image. Must match the ControlNet model loaded - Canny edges for canny model, depth map for depth model, etc. sd.cpp does not pre-process; you do that client-side. Recommended: Required when using ControlNet. Leave empty for the architecture default.", type: "string" },
@@ -429,6 +429,13 @@ async function execute(config, input, context) {
             if (flat.length > 0) {
                 body[option.server] = flat;
             }
+        } else if (option.server === 'cache_mode' && value === 'off') {
+            // The server reads an absent cache_mode as "use the architecture's
+            // preference" and an empty one as "off". A dropdown cannot express
+            // both with the same empty string, so "off" is sent as the explicit
+            // empty the server wants - which is the only way to stop a preset
+            // that switches caching on.
+            body.cache_mode = '';
         } else {
             putIfSet(body, option.server, value);
         }

+ 8 - 1
nodes/sdcpp/sdcpp-img2img.js

@@ -170,7 +170,7 @@ const configSchema = {
             dynamicOptions: { source: 'credentials', filter: { type: ['sdcpp', 'basic'] } }
         },
         batchCount: { title: "Batch Count", description: "Number of independent images to produce in this single job. Each image gets a different seed (`seed`, `seed+1`, …). Total VRAM doesn't grow with batch_count - sd.cpp serializes. Recommended: 1 for interactive, higher when you want a grid of variations from one prompt. Leave empty for the architecture default.", type: "number" },
-        cacheMode: { title: "Cache Mode", description: "DiT-model intermediate caching strategy. Skips redundant computation across consecutive sampler steps when the model's intermediate state hasn't changed enough to matter. `easycache` is the simplest; `spectrum` is the newest, generally best for Flux/SD3/Z-Image. Recommended: spectrum for Flux/SD3/Z-Image when generation is too slow. Off for SD1.5/SDXL (UNet is too small for caching to win). Leave empty for the architecture default.", type: "string", enum: ["", "easycache", "spectrum"], enumLabels: ["(architecture default)", "EasyCache - single threshold, simple", "Spectrum - frequency-domain analysis (best quality/speed tradeoff)"], default: "" },
+        cacheMode: { title: "Cache Mode", description: "DiT-model intermediate caching strategy. Skips redundant computation across consecutive sampler steps when the model's intermediate state hasn't changed enough to matter. `easycache` is the simplest; `spectrum` is the newest, generally best for Flux/SD3/Z-Image. Recommended: spectrum for Flux/SD3/Z-Image when generation is too slow. Off for SD1.5/SDXL (UNet is too small for caching to win). Leave empty for the architecture default.", type: "string", enum: ["", "off", "easycache", "ucache", "dbcache", "taylorseer", "cache_dit", "spectrum"], enumLabels: ["(architecture default, which may switch it on)", "Off - no caching, whatever the architecture prefers", "EasyCache - single threshold, simple", "UCache", "DBCache", "TaylorSeer", "Cache-DiT", "Spectrum - frequency-domain analysis (best quality/speed tradeoff)"], default: "" },
         cfgScale: { title: "CFG Scale", description: "Classifier-Free Guidance scale. Strength of pushing the cond toward the prompt vs the uncond. Higher = follows prompt more aggressively but can over-saturate. Set to 1.0 to disable CFG (skips the uncond pass - twice as fast). Recommended: SD1.5: 7. SDXL: 4-8. Flux Dev: 1 (CFG bypassed in flow models). Z-Image: 1. Schnell/Turbo: 1. Leave empty for the architecture default.", type: "number" },
         clipSkip: { title: "CLIP Skip", description: "Skip the last N layers of CLIP when encoding the prompt. -1 = use the model's recommended default. 2 is the classic anime-model setting. Recommended: -1 (auto). Set to 2 for anime/cartoon SD1.5 fine-tunes. Leave empty for the architecture default.", type: "number" },
         controlImageBase64: { title: "ControlNet Image (base64)", description: "Base64-encoded pre-processed control image. Must match the ControlNet model loaded - Canny edges for canny model, depth map for depth model, etc. sd.cpp does not pre-process; you do that client-side. Recommended: Required when using ControlNet. Leave empty for the architecture default.", type: "string" },
@@ -423,6 +423,13 @@ async function execute(config, input, context) {
             if (flat.length > 0) {
                 body[option.server] = flat;
             }
+        } else if (option.server === 'cache_mode' && value === 'off') {
+            // The server reads an absent cache_mode as "use the architecture's
+            // preference" and an empty one as "off". A dropdown cannot express
+            // both with the same empty string, so "off" is sent as the explicit
+            // empty the server wants - which is the only way to stop a preset
+            // that switches caching on.
+            body.cache_mode = '';
         } else {
             putIfSet(body, option.server, value);
         }

+ 8 - 1
nodes/sdcpp/sdcpp-txt2img.js

@@ -176,7 +176,7 @@ const configSchema = {
             dynamicOptions: { source: 'credentials', filter: { type: ['sdcpp', 'basic'] } }
         },
         batchCount: { title: "Batch Count", description: "Number of independent images to produce in this single job. Each image gets a different seed (`seed`, `seed+1`, …). Total VRAM doesn't grow with batch_count - sd.cpp serializes. Recommended: 1 for interactive, higher when you want a grid of variations from one prompt. Leave empty for the architecture default.", type: "number" },
-        cacheMode: { title: "Cache Mode", description: "DiT-model intermediate caching strategy. Skips redundant computation across consecutive sampler steps when the model's intermediate state hasn't changed enough to matter. `easycache` is the simplest; `spectrum` is the newest, generally best for Flux/SD3/Z-Image. Recommended: spectrum for Flux/SD3/Z-Image when generation is too slow. Off for SD1.5/SDXL (UNet is too small for caching to win). Leave empty for the architecture default.", type: "string", enum: ["", "easycache", "spectrum"], enumLabels: ["(architecture default)", "EasyCache - single threshold, simple", "Spectrum - frequency-domain analysis (best quality/speed tradeoff)"], default: "" },
+        cacheMode: { title: "Cache Mode", description: "DiT-model intermediate caching strategy. Skips redundant computation across consecutive sampler steps when the model's intermediate state hasn't changed enough to matter. `easycache` is the simplest; `spectrum` is the newest, generally best for Flux/SD3/Z-Image. Recommended: spectrum for Flux/SD3/Z-Image when generation is too slow. Off for SD1.5/SDXL (UNet is too small for caching to win). Leave empty for the architecture default.", type: "string", enum: ["", "off", "easycache", "ucache", "dbcache", "taylorseer", "cache_dit", "spectrum"], enumLabels: ["(architecture default, which may switch it on)", "Off - no caching, whatever the architecture prefers", "EasyCache - single threshold, simple", "UCache", "DBCache", "TaylorSeer", "Cache-DiT", "Spectrum - frequency-domain analysis (best quality/speed tradeoff)"], default: "" },
         cfgScale: { title: "CFG Scale", description: "Classifier-Free Guidance scale. Strength of pushing the cond toward the prompt vs the uncond. Higher = follows prompt more aggressively but can over-saturate. Set to 1.0 to disable CFG (skips the uncond pass - twice as fast). Recommended: SD1.5: 7. SDXL: 4-8. Flux Dev: 1 (CFG bypassed in flow models). Z-Image: 1. Schnell/Turbo: 1. Leave empty for the architecture default.", type: "number" },
         clipSkip: { title: "CLIP Skip", description: "Skip the last N layers of CLIP when encoding the prompt. -1 = use the model's recommended default. 2 is the classic anime-model setting. Recommended: -1 (auto). Set to 2 for anime/cartoon SD1.5 fine-tunes. Leave empty for the architecture default.", type: "number" },
         controlImageBase64: { title: "ControlNet Image (base64)", description: "Base64-encoded pre-processed control image. Must match the ControlNet model loaded - Canny edges for canny model, depth map for depth model, etc. sd.cpp does not pre-process; you do that client-side. Recommended: Required when using ControlNet. Leave empty for the architecture default.", type: "string" },
@@ -431,6 +431,13 @@ async function execute(config, input, context) {
             if (flat.length > 0) {
                 body[option.server] = flat;
             }
+        } else if (option.server === 'cache_mode' && value === 'off') {
+            // The server reads an absent cache_mode as "use the architecture's
+            // preference" and an empty one as "off". A dropdown cannot express
+            // both with the same empty string, so "off" is sent as the explicit
+            // empty the server wants - which is the only way to stop a preset
+            // that switches caching on.
+            body.cache_mode = '';
         } else {
             putIfSet(body, option.server, value);
         }

+ 8 - 1
nodes/sdcpp/sdcpp-txt2vid.js

@@ -171,7 +171,7 @@ const configSchema = {
             description: 'A basic credential holding the sdcpp-restapi username and password',
             dynamicOptions: { source: 'credentials', filter: { type: ['sdcpp', 'basic'] } }
         },
-        cacheMode: { title: "Cache Mode", description: "DiT-model intermediate caching strategy. Skips redundant computation across consecutive sampler steps when the model's intermediate state hasn't changed enough to matter. `easycache` is the simplest; `spectrum` is the newest, generally best for Flux/SD3/Z-Image. Recommended: spectrum for Flux/SD3/Z-Image when generation is too slow. Off for SD1.5/SDXL (UNet is too small for caching to win). Leave empty for the architecture default.", type: "string", enum: ["", "easycache", "spectrum"], enumLabels: ["(architecture default)", "EasyCache - single threshold, simple", "Spectrum - frequency-domain analysis (best quality/speed tradeoff)"], default: "" },
+        cacheMode: { title: "Cache Mode", description: "DiT-model intermediate caching strategy. Skips redundant computation across consecutive sampler steps when the model's intermediate state hasn't changed enough to matter. `easycache` is the simplest; `spectrum` is the newest, generally best for Flux/SD3/Z-Image. Recommended: spectrum for Flux/SD3/Z-Image when generation is too slow. Off for SD1.5/SDXL (UNet is too small for caching to win). Leave empty for the architecture default.", type: "string", enum: ["", "off", "easycache", "ucache", "dbcache", "taylorseer", "cache_dit", "spectrum"], enumLabels: ["(architecture default, which may switch it on)", "Off - no caching, whatever the architecture prefers", "EasyCache - single threshold, simple", "UCache", "DBCache", "TaylorSeer", "Cache-DiT", "Spectrum - frequency-domain analysis (best quality/speed tradeoff)"], default: "" },
         cfgScale: { title: "CFG Scale", description: "Classifier-Free Guidance scale. Strength of pushing the cond toward the prompt vs the uncond. Higher = follows prompt more aggressively but can over-saturate. Set to 1.0 to disable CFG (skips the uncond pass - twice as fast). Recommended: SD1.5: 7. SDXL: 4-8. Flux Dev: 1 (CFG bypassed in flow models). Z-Image: 1. Schnell/Turbo: 1. Leave empty for the architecture default.", type: "number" },
         clipSkip: { title: "CLIP Skip", description: "Skip the last N layers of CLIP when encoding the prompt. -1 = use the model's recommended default. 2 is the classic anime-model setting. Recommended: -1 (auto). Set to 2 for anime/cartoon SD1.5 fine-tunes. Leave empty for the architecture default.", type: "number" },
         controlFrames: { title: "ControlNet Frames", description: "Per-frame control images for video generation. Length should match `video_frames` for full coverage. Recommended: Empty unless using video-ControlNet. Leave empty for the architecture default.", type: "array", items: {"type": "string"} },
@@ -435,6 +435,13 @@ async function execute(config, input, context) {
             if (flat.length > 0) {
                 body[option.server] = flat;
             }
+        } else if (option.server === 'cache_mode' && value === 'off') {
+            // The server reads an absent cache_mode as "use the architecture's
+            // preference" and an empty one as "off". A dropdown cannot express
+            // both with the same empty string, so "off" is sent as the explicit
+            // empty the server wants - which is the only way to stop a preset
+            // that switches caching on.
+            body.cache_mode = '';
         } else {
             putIfSet(body, option.server, value);
         }

+ 7 - 0
nodes/sdcpp/sdcpp-upscale.js

@@ -247,6 +247,13 @@ async function execute(config, input, context) {
             if (flat.length > 0) {
                 body[option.server] = flat;
             }
+        } else if (option.server === 'cache_mode' && value === 'off') {
+            // The server reads an absent cache_mode as "use the architecture's
+            // preference" and an empty one as "off". A dropdown cannot express
+            // both with the same empty string, so "off" is sent as the explicit
+            // empty the server wants - which is the only way to stop a preset
+            // that switches caching on.
+            body.cache_mode = '';
         } else {
             putIfSet(body, option.server, value);
         }

+ 27 - 0
scripts/gen-sdcpp-generation-options.py

@@ -56,6 +56,18 @@ SKIP = {'title'}
 # reference simply has not caught up. Declared here in the same shape the
 # reference uses, so that when it does catch up these can be deleted and
 # nothing else changes.
+# The values map for cache_mode lists two modes; the field's own description in
+# the schema names six. Neither source is machine-readable and complete, so the
+# list is written here from the description, with a note of where it came from.
+CACHE_MODES = {
+    'easycache': 'EasyCache - single threshold, simple',
+    'ucache': 'UCache',
+    'dbcache': 'DBCache',
+    'taylorseer': 'TaylorSeer',
+    'cache_dit': 'Cache-DiT',
+    'spectrum': 'Spectrum - frequency-domain analysis (best quality/speed tradeoff)',
+}
+
 UNDOCUMENTED = {
     'ip_adapter_image_base64': {
         'applies_to': ['txt2img', 'img2img', 'txt2vid'],
@@ -178,6 +190,8 @@ def prop_for(name: str, opt: dict) -> dict:
             # Keep the wording from the values map where there is any, but the
             # list itself comes from the schema.
             values = {v: values.get(v, v) for v in authoritative}
+        elif name == 'cache_mode':
+            values = dict(CACHE_MODES)
         # The empty entry is what "leave it to the architecture" looks like in a
         # dropdown; without it a select cannot express "unset". Some of the
         # server's own value maps already carry one, so it is not added twice.
@@ -188,6 +202,19 @@ def prop_for(name: str, opt: dict) -> dict:
             ' '.join(plain(values[k]).split())[:70] or k for k in keys
         ]
         prop['default'] = ''
+
+        # For most of these, empty means "say nothing and let the loaded
+        # architecture decide". For cache_mode the server also uses empty to
+        # mean "off" - so leaving it empty is indistinguishable from not
+        # choosing, and the preset wins. Z-Image's preset turns caching on,
+        # which is why it could not be switched off. A separate entry says it
+        # outright, and the body builder sends an explicit empty for it.
+        if name == 'cache_mode':
+            prop['enum'] = ['', 'off'] + keys
+            prop['enumLabels'] = [
+                '(architecture default, which may switch it on)',
+                'Off - no caching, whatever the architecture prefers',
+            ] + [' '.join(plain(values[k]).split())[:70] or k for k in keys]
     elif kind == 'boolean':
         prop['type'] = 'boolean'
     elif kind == 'number':