Fix workflow graph map sampler params

This commit is contained in:
Ewald
2026-06-03 17:15:29 +02:00
parent e4f9053219
commit 0a3271cdd7
7 changed files with 643 additions and 353 deletions
File diff suppressed because it is too large Load Diff
@@ -1,5 +1,5 @@
import { R as e } from "./config-Cxv7acF8.js";
import { t } from "./floatingViewerManager-I_zH7WzK.js";
import { t } from "./floatingViewerManager-CYnEwEgo.js";
import { r as n } from "./events-BnkL6-b6.js";
//#region ui/features/viewer/LiveStreamTracker.ts
var r = !1, i = null, a = null, o = null, s = null, c = null, l = 0, u = 0, d = 400, f = new Set([
@@ -219,7 +219,7 @@ function ye() {
//#region ui/features/viewer/floatingViewerManager.ts
var S = null, be = null;
async function xe() {
return S || (be ||= import("./FloatingViewer-Bn-T0zen.js").then((e) => (S = e.FloatingViewer, S)), be);
return S || (be ||= import("./FloatingViewer-iLhVlgRh.js").then((e) => (S = e.FloatingViewer, S)), be);
}
var C = null, Se = null;
async function Ce() {
+2 -2
View File
@@ -1,7 +1,7 @@
import { B as e, Ct as t, F as n, G as r, H as i, I as a, L as o, M as s, R as c, St as l, U as u, V as d, W as f, _ as p, _t as m, a as h, at as g, b as _, bt as v, c as y, ct as b, d as x, ft as S, g as C, h as w, ht as T, i as E, it as D, l as O, lt as k, m as A, mt as j, n as M, o as N, ot as P, p as F, pt as I, r as ee, s as te, t as ne, u as re, v as ie, vt as ae, wt as oe, x as se, xt as ce, yt as le, z as ue } from "./chunks/client-DZE_lzdb.js";
import { $ as de, B as fe, F as pe, G as L, H as me, J as he, L as ge, N as _e, P as ve, Q as ye, T as be, U as xe, V as Se, W as Ce, X as we, Y as Te, Z as Ee, at as De, ct as Oe, d as ke, et as Ae, f as je, it as Me, l as R, nt as Ne, ot as Pe, q as Fe, st as Ie, t as z, tt as Le, u as Re, w as ze } from "./chunks/config-Cxv7acF8.js";
import { a as Be, i as Ve, n as He, t as Ue } from "./chunks/graphTraversal-CjIZsRsP.js";
import { _ as We, a as Ge, c as Ke, d as B, f as qe, g as Je, h as Ye, i as Xe, l as Ze, m as Qe, n as $e, o as et, p as tt, r as nt, s as rt, t as it, u as at, v as ot, y as st } from "./chunks/floatingViewerManager-I_zH7WzK.js";
import { _ as We, a as Ge, c as Ke, d as B, f as qe, g as Je, h as Ye, i as Xe, l as Ze, m as Qe, n as $e, o as et, p as tt, r as nt, s as rt, t as it, u as at, v as ot, y as st } from "./chunks/floatingViewerManager-CYnEwEgo.js";
import { A as ct, B as lt, C as V, D as ut, E as dt, F as ft, G as pt, H as mt, I as ht, J as gt, K as _t, L as vt, M as yt, N as bt, O as xt, P as St, R as Ct, S as wt, T as Tt, U as Et, V as Dt, W as Ot, Y as kt, b as At, d as jt, f as Mt, g as Nt, h as Pt, j as Ft, k as It, l as Lt, m as Rt, p as zt, q as Bt, s as Vt, t as Ht, u as Ut, v as Wt, w as Gt, x as Kt, y as qt, z as Jt } from "./chunks/SidebarWorkflowSection-Dw9T8b4-.js";
import { i as Yt, n as Xt, r as H, t as Zt } from "./chunks/events-BnkL6-b6.js";
import { a as Qt, c as $t, d as en, f as tn, l as nn, m as rn, o as an, p as on, r as sn, s as cn, t as ln, u as un, v as dn } from "./chunks/openMajoorSettings-BbE45fW7.js";
@@ -31286,7 +31286,7 @@ function Sw(e = 1200) {
}, Math.max(250, Number(e) || 0));
}
function Cw(e) {
import("./chunks/LiveStreamTracker-C7oNiQmZ.js").then((t) => {
import("./chunks/LiveStreamTracker-CEpotRa4.js").then((t) => {
nw = t;
try {
t.initLiveStreamTracker(e);
@@ -633,10 +633,21 @@ function _isExpandedTextWidgetLabel(label: any) {
function _normalizePorts(ports: any) {
if (!Array.isArray(ports)) return [];
return ports.filter(Boolean).map((port: any) => ({
name: String(port?.name || port?.label || "").trim(),
type: String(port?.type || port?.slot_type || port?.data_type || "").trim(),
}));
return ports
.filter((port: any) => port && !_portLooksLikeWidget(port))
.map((port: any) => ({
name: String(port?.name || port?.label || "").trim(),
type: String(port?.type || port?.slot_type || port?.data_type || "").trim(),
}));
}
function _portLooksLikeWidget(port: any) {
if (!port || typeof port !== "object") return false;
if (port.widget === true) return true;
if (port.widget && typeof port.widget === "object") return true;
if (typeof port.widget === "string" && port.widget.trim()) return true;
if (port.widget_index != null || port.widgetIndex != null) return true;
return false;
}
function _formatPortInfo(item: any) {
@@ -658,6 +669,12 @@ function _normalizePortToken(value: any) {
}
function _normalizeWidgetEntries(node: any): Array<{ label: any; key?: any; value: any }> {
const fromParams = getNodeParamEntries(node);
const entries = Array.isArray(fromParams)
? fromParams.map(([label, value]: any) => ({ label, value }))
: [];
if (entries.length) return entries.slice(0, 160);
const fromWidgets = getNodeWidgetValueEntries(node);
if (Array.isArray(fromWidgets) && fromWidgets.length) {
return fromWidgets.map((entry: any) => ({
@@ -666,11 +683,7 @@ function _normalizeWidgetEntries(node: any): Array<{ label: any; key?: any; valu
value: entry?.value,
}));
}
const fromParams = getNodeParamEntries(node);
const entries = Array.isArray(fromParams)
? fromParams.map(([label, value]: any) => ({ label, value }))
: [];
return entries.slice(0, 160);
return [];
}
function _getNodeVisualCategory(node: any) {
@@ -44,10 +44,12 @@ export async function ensureWorkflowObjectInfo(workflow: any): Promise<unknown>
export function resolveAssetWorkflow(asset: any): any {
const candidates = _collectWorkflowCandidates(asset);
const promptGraph = _resolveAssetPromptGraph(asset);
for (const candidate of candidates) {
const parsed = _coerceObject(candidate);
const workflow = _normalizeWorkflow(parsed);
if (workflow) {
_decorateWorkflowPromptInputs(workflow, promptGraph);
_decorateWorkflowSubgraphNames(workflow);
return workflow;
}
@@ -133,11 +135,21 @@ export function getNodeParamEntries(node: any) {
function _getNodeValueParamEntries(node: any) {
const entries = [];
const promptInputs =
node?._mjrPromptInputs && typeof node._mjrPromptInputs === "object" ? node._mjrPromptInputs : null;
const inputs = node?.inputs && typeof node.inputs === "object" ? node.inputs : null;
if (promptInputs && !Array.isArray(promptInputs)) {
for (const [key, value] of Object.entries(promptInputs)) {
if (_valueLooksLikePromptLink(value)) continue;
entries.push([key, value]);
}
}
if (inputs && !Array.isArray(inputs)) {
for (const [key, value] of Object.entries(inputs)) {
if (_valueLooksLikePromptLink(value)) continue;
if (entries.some(([existingKey]) => String(existingKey) === String(key))) continue;
entries.push([key, value]);
}
}
@@ -163,10 +175,10 @@ export function getNodeWidgetValueEntries(node: any) {
return widgetsValues.map((value, index) => {
const guessedLabel = _guessWidgetLabel(node, index, value);
const label =
objectInputNames[index] ||
inputSlotNames[index] ||
widgets[index]?.name ||
widgets[index]?.label ||
objectInputNames[index] ||
guessedLabel ||
`param ${index + 1}`;
return { label, value, index };
@@ -203,9 +215,6 @@ function _looksArrayLikeWidgetsValues(widgetsValuesRaw: any) {
export function getNodeInputSlotNames(node: any) {
const inputs = Array.isArray(node?.inputs) ? node.inputs : [];
const widgetInputs = inputs.filter(_inputLooksLikeWidget);
const unlinkedWidgetLikeInputs = inputs.filter(
(input: any) => !_inputHasLink(input) && _inputTypeLooksWidgetCapable(input?.type),
);
const labelSource: any[] = [];
const seen = new Set();
const addInput = (input: any) => {
@@ -215,7 +224,6 @@ export function getNodeInputSlotNames(node: any) {
labelSource.push(input);
};
for (const input of widgetInputs) addInput(input);
for (const input of unlinkedWidgetLikeInputs) addInput(input);
return labelSource.map((input: any) =>
String(
input?.label ||
@@ -303,6 +311,7 @@ function _inputLooksLikeWidget(input: any) {
if (input.widget === true) return true;
if (input.widget && typeof input.widget === "object") return true;
if (typeof input.widget === "string" && input.widget.trim()) return true;
if (input.widget_index != null || input.widgetIndex != null) return true;
return false;
}
@@ -345,6 +354,17 @@ function _guessWidgetLabel(node: any, index: any, value: any) {
const haystack = `${type} ${title}`.toLowerCase();
const valueText = String(value ?? "").toLowerCase();
if (haystack.includes("ksamplerselect")) return "sampler_name";
if (haystack.includes("ksampler")) {
return [
"seed",
"control_after_generate",
"steps",
"cfg",
"sampler_name",
"scheduler",
"denoise",
][index] || null;
}
if (haystack.includes("manualsigmas")) return "sigmas";
if (haystack.includes("randomnoise")) return index === 0 ? "noise_seed" : index === 1 ? "control_after_generate" : null;
if (haystack.includes("cfgguider")) return "cfg";
@@ -417,6 +437,24 @@ function _collectWorkflowCandidates(asset: any) {
].filter((value: any) => value != null);
}
function _resolveAssetPromptGraph(asset: any) {
const raw = _coerceObject(asset?.metadata_raw);
const meta = _coerceObject(asset?.metadata);
const candidates = [
asset?.prompt,
asset?.Prompt,
raw?.prompt,
raw?.Prompt,
meta?.prompt,
meta?.Prompt,
];
for (const candidate of candidates) {
const parsed = _coerceObject(candidate);
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
}
return null;
}
function _coerceObject(value: any) {
if (!value) return null;
if (typeof value === "object") return value;
@@ -467,6 +505,61 @@ function _decorateWorkflowSubgraphNames(workflow: any, visited = new WeakSet())
}
}
function _decorateWorkflowPromptInputs(workflow: any, promptGraph: any) {
if (!workflow || typeof workflow !== "object" || !promptGraph || typeof promptGraph !== "object") return;
const promptNodes = _getPromptNodeEntries(promptGraph);
if (!promptNodes.length) return;
_decorateWorkflowPromptInputsInGraph(workflow, promptNodes, new WeakSet());
}
function _decorateWorkflowPromptInputsInGraph(workflow: any, promptNodes: any[], visited: WeakSet<object>) {
if (!workflow || typeof workflow !== "object" || visited.has(workflow)) return;
visited.add(workflow);
const definitions = _getSubgraphDefinitions(workflow);
for (const node of Array.isArray(workflow?.nodes) ? workflow.nodes : []) {
const match = _findPromptNodeMatch(node, promptNodes);
if (match?.inputs && typeof match.inputs === "object" && !Array.isArray(match.inputs)) {
node._mjrPromptInputs = match.inputs;
}
const subgraph = _getNodeSubgraph(workflow, node, definitions);
if (subgraph) _decorateWorkflowPromptInputsInGraph(subgraph, promptNodes, visited);
}
}
function _getPromptNodeEntries(promptGraph: any) {
if (!promptGraph || typeof promptGraph !== "object" || Array.isArray(promptGraph)) return [];
const entries = [];
for (const [id, value] of Object.entries(promptGraph)) {
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
const classType = String((value as any).class_type || (value as any).type || "").trim();
const inputs = (value as any).inputs;
if (!classType || !inputs || typeof inputs !== "object" || Array.isArray(inputs)) continue;
const leafId = String(id).split(":").pop() || String(id);
entries.push({ id: String(id), leafId, classType, inputs });
}
return entries;
}
function _findPromptNodeMatch(node: any, promptNodes: any[]) {
const nodeId = String(node?.id ?? node?.ID ?? "").trim();
const nodeType = _normalizeNodeTypeForPromptMatch(getNodeType(node));
if (!nodeId || !nodeType) return null;
const exact = promptNodes.find(
(entry) => entry.id === nodeId && _normalizeNodeTypeForPromptMatch(entry.classType) === nodeType,
);
if (exact) return exact;
const matches = promptNodes.filter(
(entry) => entry.leafId === nodeId && _normalizeNodeTypeForPromptMatch(entry.classType) === nodeType,
);
return matches.length === 1 ? matches[0] : null;
}
function _normalizeNodeTypeForPromptMatch(value: any) {
return String(value || "")
.trim()
.toLowerCase();
}
function _applySubgraphDefinitionMetadata(node: any, definitions: any) {
if (!node || typeof node !== "object") return;
const definitionKey = _getNodeSubgraphDefinitionKeys(node).find((key) => definitions.has(String(key)));
+117 -1
View File
@@ -84,7 +84,7 @@ describe("workflow graph map data", () => {
{ name: "width", type: "INT", link: 23, widget: { name: "width" } },
{ name: "height", type: "INT", link: 24, widget: { name: "height" } },
{ name: "length", type: "INT", link: 25, widget: { name: "length" } },
{ name: "batch_size", type: "INT" },
{ name: "batch_size", type: "INT", widget: { name: "batch_size" } },
],
widgets_values: [832, 480, 81, 1],
};
@@ -98,6 +98,122 @@ describe("workflow graph map data", () => {
]);
});
it("does not infer unmarked socket inputs as widgets for core sampler nodes", () => {
const node = {
id: 3,
type: "KSampler",
inputs: [
{ name: "model", type: "MODEL", link: 1 },
{ name: "positive", type: "CONDITIONING", link: 2 },
{ name: "negative", type: "CONDITIONING", link: 3 },
{ name: "latent_image", type: "LATENT", link: 4 },
{ name: "seed", type: "INT" },
{ name: "steps", type: "INT" },
{ name: "cfg", type: "FLOAT" },
{ name: "sampler_name", type: "COMBO" },
{ name: "scheduler", type: "COMBO" },
{ name: "denoise", type: "FLOAT" },
],
widgets_values: [1088132929177955, "randomize", 20, 8, "euler", "simple", 1],
};
expect(getNodeInputSlotNames(node)).toEqual([]);
expect(getNodeWidgetValueEntries(node)).toEqual([
{ label: "seed", value: 1088132929177955, index: 0 },
{ label: "control_after_generate", value: "randomize", index: 1 },
{ label: "steps", value: 20, index: 2 },
{ label: "cfg", value: 8, index: 3 },
{ label: "sampler_name", value: "euler", index: 4 },
{ label: "scheduler", value: "simple", index: 5 },
{ label: "denoise", value: 1, index: 6 },
]);
expect(getNodeParamEntries(node)).toEqual([
["seed", 1088132929177955],
["control_after_generate", "randomize"],
["steps", 20],
["cfg", 8],
["sampler_name", "euler"],
["scheduler", "simple"],
["denoise", 1],
]);
});
it("uses prompt inputs for subgraph sampler parameters when widgets_values are ambiguous", () => {
const workflow = resolveAssetWorkflow({
workflow: {
nodes: [
{
id: 95,
type: "c20beb1e-2a45-4872-913e-21018c09c578",
properties: {
proxyWidgets: [
["3", "seed"],
["3", "steps"],
],
},
},
],
definitions: {
subgraphs: [
{
id: "c20beb1e-2a45-4872-913e-21018c09c578",
nodes: [
{
id: 3,
type: "KSampler",
inputs: [
{ name: "model", type: "MODEL", link: 13 },
{ name: "positive", type: "CONDITIONING", link: 30 },
{ name: "negative", type: "CONDITIONING", link: 33 },
{ name: "latent_image", type: "LATENT", link: 427 },
{ name: "seed", type: "INT", link: 71, widget: { name: "seed" } },
{ name: "steps", type: "INT", link: 72, widget: { name: "steps" } },
],
widgets_values: [
1088132929177955,
"randomize",
8,
1,
"res_multistep",
"simple",
1,
],
},
],
},
],
},
},
prompt: {
"95:3": {
class_type: "KSampler",
inputs: {
seed: 1088132929177955,
steps: 8,
cfg: 1,
sampler_name: "res_multistep",
scheduler: "simple",
denoise: 1,
model: ["95:11", 0],
positive: ["95:27", 0],
negative: ["95:33", 0],
latent_image: ["95:242", 0],
},
},
},
});
const sampler = findWorkflowNode(workflow, "95::3");
expect(getNodeParamEntries(sampler)).toEqual([
["seed", 1088132929177955],
["steps", 8],
["cfg", 1],
["sampler_name", "res_multistep"],
["scheduler", "simple"],
["denoise", 1],
]);
});
it("supports custom nodes that save widgets_values as a dictionary", () => {
expect(
getNodeWidgetValueEntries({