fix: read the matching output of multi-output settings nodes
KSampler settings linked to a node like SamplerCombo all resolved to its first value, so steps, cfg and sampler showed the sampler name. Scalar resolution now prefers the linked node's input with the same field name.
This commit is contained in:
@@ -147,6 +147,25 @@ test("custom sampler graphs (Flux: SamplerCustomAdvanced + CFGGuider) resolve ev
|
||||
);
|
||||
});
|
||||
|
||||
test("multi-output settings nodes (SamplerCombo) map each field to its own value", () => {
|
||||
const prompt = {
|
||||
5: {
|
||||
class_type: "KSampler",
|
||||
inputs: {
|
||||
seed: ["162", 0], steps: ["165", 2], cfg: ["165", 3], sampler_name: ["165", 0],
|
||||
positive: ["6", 0], negative: ["7", 0], model: ["4", 0],
|
||||
},
|
||||
},
|
||||
4: { class_type: "CheckpointLoaderSimple", inputs: { ckpt_name: "m.safetensors" } },
|
||||
6: { class_type: "CLIPTextEncode", inputs: { text: "a cat" } },
|
||||
7: { class_type: "CLIPTextEncode", inputs: { text: "blurry" } },
|
||||
162: { class_type: "SeedHistory", inputs: { seed: 1495646836, seed_history_ui: "" } },
|
||||
165: { class_type: "SamplerCombo", inputs: { sampler_name: "euler_ancestral", scheduler: "normal", steps: 50, cfg: 7 } },
|
||||
};
|
||||
const r = extractGenerationParams({ prompt });
|
||||
assert.deepEqual([r.seed, r.steps, r.cfgScale, r.sampler], [1495646836, 50, 7, "euler_ancestral"]);
|
||||
});
|
||||
|
||||
test("workflow-only images account for the hidden control_after_generate widget", () => {
|
||||
const workflow = {
|
||||
nodes: [
|
||||
|
||||
@@ -81,13 +81,20 @@
|
||||
return [id, node];
|
||||
}
|
||||
|
||||
/** Resolve a scalar input, following a link to a primitive node if needed. */
|
||||
function resolveScalar(ctx, value, seen = new Set()) {
|
||||
/**
|
||||
* Resolve a scalar input, following a link to a primitive node if needed.
|
||||
* Multi-output settings nodes (e.g. SamplerCombo) expose several values, so an
|
||||
* input with the same `name` as the requested field wins over the first scalar.
|
||||
*/
|
||||
function resolveScalar(ctx, value, name, seen = new Set()) {
|
||||
if (!isLink(value)) return value;
|
||||
const [, node] = enter(ctx, value, seen);
|
||||
if (!node) return undefined;
|
||||
if (name && Object.prototype.hasOwnProperty.call(node.inputs, name)) {
|
||||
return resolveScalar(ctx, node.inputs[name], name, seen);
|
||||
}
|
||||
for (const v of Object.values(node.inputs)) {
|
||||
const resolved = resolveScalar(ctx, v, seen);
|
||||
const resolved = resolveScalar(ctx, v, name, seen);
|
||||
if (resolved !== undefined && typeof resolved !== "object") return resolved;
|
||||
}
|
||||
return undefined;
|
||||
@@ -99,7 +106,7 @@
|
||||
const [, node] = enter(ctx, link, seen);
|
||||
if (!node) return undefined;
|
||||
const key = keys.find((k) => k in node.inputs);
|
||||
return key === undefined ? undefined : resolveScalar(ctx, node.inputs[key], seen);
|
||||
return key === undefined ? undefined : resolveScalar(ctx, node.inputs[key], key, seen);
|
||||
}
|
||||
|
||||
/** Resolve a string input; text-producing nodes may be chained (concat, primitives). */
|
||||
@@ -179,12 +186,12 @@
|
||||
negativePrompt: traceConditioning(ctx, inputs.negative, "negative"),
|
||||
checkpoint: traceModelName(ctx, inputs.model) || findAnyModelName(nodes),
|
||||
seed:
|
||||
resolveScalar(ctx, inputs.seed ?? inputs.noise_seed) ??
|
||||
resolveScalar(ctx, inputs.seed ?? inputs.noise_seed, "seed" in inputs ? "seed" : "noise_seed") ??
|
||||
fromCustom(custom && custom.inputs.noise, ["noise_seed", "seed"]),
|
||||
steps: resolveScalar(ctx, inputs.steps) ?? fromCustom(custom && custom.inputs.sigmas, ["steps"]),
|
||||
cfgScale: resolveScalar(ctx, inputs.cfg),
|
||||
steps: resolveScalar(ctx, inputs.steps, "steps") ?? fromCustom(custom && custom.inputs.sigmas, ["steps"]),
|
||||
cfgScale: resolveScalar(ctx, inputs.cfg, "cfg"),
|
||||
sampler:
|
||||
resolveScalar(ctx, inputs.sampler_name) ??
|
||||
resolveScalar(ctx, inputs.sampler_name, "sampler_name") ??
|
||||
fromCustom(custom && custom.inputs.sampler, ["sampler_name"]),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user