mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-08-06 22:10:14 -03:00
fix(workflow): accept non-string widget values and support GlobalSeed node in gen-params (#1026)
This commit is contained in:
@@ -3471,7 +3471,7 @@ class NodeRegistryHandler:
|
|||||||
status=400,
|
status=400,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not isinstance(value, str) or not value:
|
if value is None or (isinstance(value, str) and not value):
|
||||||
return web.json_response(
|
return web.json_response(
|
||||||
{"success": False, "error": "Missing value parameter"}, status=400
|
{"success": False, "error": "Missing value parameter"}, status=400
|
||||||
)
|
)
|
||||||
@@ -3578,7 +3578,7 @@ class NodeRegistryHandler:
|
|||||||
status=400,
|
status=400,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not isinstance(value, str) or not value:
|
if value is None or (isinstance(value, str) and not value):
|
||||||
return web.json_response(
|
return web.json_response(
|
||||||
{"success": False, "error": "Missing value parameter"}, status=400
|
{"success": False, "error": "Missing value parameter"}, status=400
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -141,6 +141,20 @@ const PARAM_TO_WIDGET_CANDIDATES = {
|
|||||||
scheduler: ['scheduler'],
|
scheduler: ['scheduler'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Node-type-specific widget name overrides.
|
||||||
|
// Keys are ComfyUI node class names (e.g. "GlobalSeed //Inspire").
|
||||||
|
// Values are partial PARAM_TO_WIDGET_CANDIDATES maps; the per-node candidates
|
||||||
|
// are tried *before* the global ones. Only the params listed here are
|
||||||
|
// overridden — every other param still uses the global candidates.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
const NODE_TYPE_WIDGET_OVERRIDES = {
|
||||||
|
// Inspire Pack — Global Seed node stores the seed in a widget named "value"
|
||||||
|
'GlobalSeed //Inspire': {
|
||||||
|
seed: ['value'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Parse a combined sampler+scheduler value (space-separated or underscore)
|
// Parse a combined sampler+scheduler value (space-separated or underscore)
|
||||||
// e.g., "Euler a Karras", "DPM++ 2M beta", "er_sde_beta"
|
// e.g., "Euler a Karras", "DPM++ 2M beta", "er_sde_beta"
|
||||||
@@ -235,7 +249,7 @@ function resolveSamplerScheduler(rawValue) {
|
|||||||
// Find which gen params can be sent to a given node, matching by widget names
|
// Find which gen params can be sent to a given node, matching by widget names
|
||||||
// Returns array of { widgetName, value } objects
|
// Returns array of { widgetName, value } objects
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
function findMatchingWidgets(nodeWidgetNames, resolvedParams) {
|
function findMatchingWidgets(nodeWidgetNames, resolvedParams, nodeType) {
|
||||||
if (!nodeWidgetNames || !Array.isArray(nodeWidgetNames) || nodeWidgetNames.length === 0) {
|
if (!nodeWidgetNames || !Array.isArray(nodeWidgetNames) || nodeWidgetNames.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -243,6 +257,26 @@ function findMatchingWidgets(nodeWidgetNames, resolvedParams) {
|
|||||||
const widgetSet = new Set(nodeWidgetNames.map(w => String(w).toLowerCase()));
|
const widgetSet = new Set(nodeWidgetNames.map(w => String(w).toLowerCase()));
|
||||||
const updates = [];
|
const updates = [];
|
||||||
|
|
||||||
|
// Resolve node-type-specific overrides (if any)
|
||||||
|
const typeOverrides =
|
||||||
|
nodeType && typeof nodeType === 'string'
|
||||||
|
? (NODE_TYPE_WIDGET_OVERRIDES[nodeType] || {})
|
||||||
|
: {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the effective candidate list for a parameter:
|
||||||
|
* type-specific overrides (if any) come first, then the global candidates.
|
||||||
|
*/
|
||||||
|
function getCandidates(key) {
|
||||||
|
const global = PARAM_TO_WIDGET_CANDIDATES[key] || [key];
|
||||||
|
const extra = typeOverrides[key];
|
||||||
|
if (extra && Array.isArray(extra) && extra.length > 0) {
|
||||||
|
// Prepend type-specific candidates; keep global as fallback
|
||||||
|
return [...extra, ...global];
|
||||||
|
}
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
// Simple numeric/string params: seed, steps, cfg
|
// Simple numeric/string params: seed, steps, cfg
|
||||||
const simpleParams = [
|
const simpleParams = [
|
||||||
{ key: 'seed', value: resolvedParams.seed },
|
{ key: 'seed', value: resolvedParams.seed },
|
||||||
@@ -251,7 +285,7 @@ function findMatchingWidgets(nodeWidgetNames, resolvedParams) {
|
|||||||
];
|
];
|
||||||
for (const { key, value } of simpleParams) {
|
for (const { key, value } of simpleParams) {
|
||||||
if (value === undefined || value === null || value === '') continue;
|
if (value === undefined || value === null || value === '') continue;
|
||||||
const candidates = PARAM_TO_WIDGET_CANDIDATES[key] || [key];
|
const candidates = getCandidates(key);
|
||||||
for (const candidate of candidates) {
|
for (const candidate of candidates) {
|
||||||
if (widgetSet.has(candidate.toLowerCase())) {
|
if (widgetSet.has(candidate.toLowerCase())) {
|
||||||
updates.push({ widgetName: candidate, value });
|
updates.push({ widgetName: candidate, value });
|
||||||
@@ -262,7 +296,7 @@ function findMatchingWidgets(nodeWidgetNames, resolvedParams) {
|
|||||||
|
|
||||||
// Sampler
|
// Sampler
|
||||||
if (resolvedParams.sampler) {
|
if (resolvedParams.sampler) {
|
||||||
const candidates = PARAM_TO_WIDGET_CANDIDATES.sampler;
|
const candidates = getCandidates('sampler');
|
||||||
for (const candidate of candidates) {
|
for (const candidate of candidates) {
|
||||||
if (widgetSet.has(candidate.toLowerCase())) {
|
if (widgetSet.has(candidate.toLowerCase())) {
|
||||||
updates.push({ widgetName: candidate, value: resolvedParams.sampler });
|
updates.push({ widgetName: candidate, value: resolvedParams.sampler });
|
||||||
@@ -273,7 +307,7 @@ function findMatchingWidgets(nodeWidgetNames, resolvedParams) {
|
|||||||
|
|
||||||
// Scheduler
|
// Scheduler
|
||||||
if (resolvedParams.scheduler) {
|
if (resolvedParams.scheduler) {
|
||||||
const candidates = PARAM_TO_WIDGET_CANDIDATES.scheduler;
|
const candidates = getCandidates('scheduler');
|
||||||
for (const candidate of candidates) {
|
for (const candidate of candidates) {
|
||||||
if (widgetSet.has(candidate.toLowerCase())) {
|
if (widgetSet.has(candidate.toLowerCase())) {
|
||||||
updates.push({ widgetName: candidate, value: resolvedParams.scheduler });
|
updates.push({ widgetName: candidate, value: resolvedParams.scheduler });
|
||||||
@@ -290,6 +324,7 @@ export {
|
|||||||
SCHEDULER_SUFFIXES,
|
SCHEDULER_SUFFIXES,
|
||||||
SCHEDULER_ONLY_VALUES,
|
SCHEDULER_ONLY_VALUES,
|
||||||
PARAM_TO_WIDGET_CANDIDATES,
|
PARAM_TO_WIDGET_CANDIDATES,
|
||||||
|
NODE_TYPE_WIDGET_OVERRIDES,
|
||||||
parseCombinedSamplerName,
|
parseCombinedSamplerName,
|
||||||
resolveSamplerScheduler,
|
resolveSamplerScheduler,
|
||||||
findMatchingWidgets,
|
findMatchingWidgets,
|
||||||
|
|||||||
@@ -1144,8 +1144,8 @@ export async function sendGenParamsToWorkflow(genParams) {
|
|||||||
const node = targetNodes[nodeKey];
|
const node = targetNodes[nodeKey];
|
||||||
if (!node) continue;
|
if (!node) continue;
|
||||||
|
|
||||||
const widgetNames = node.widget_names || [];
|
const widgetNames = getWidgetNames(node);
|
||||||
const updates = findMatchingWidgets(widgetNames, raw);
|
const updates = findMatchingWidgets(widgetNames, raw, node.type_name);
|
||||||
|
|
||||||
if (updates.length === 0) {
|
if (updates.length === 0) {
|
||||||
showToast(`Node "${node.title || node.type}" has no matching widgets for these parameters`, {}, 'warning');
|
showToast(`Node "${node.title || node.type}" has no matching widgets for these parameters`, {}, 'warning');
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
parseCombinedSamplerName,
|
parseCombinedSamplerName,
|
||||||
resolveSamplerScheduler,
|
resolveSamplerScheduler,
|
||||||
findMatchingWidgets,
|
findMatchingWidgets,
|
||||||
|
NODE_TYPE_WIDGET_OVERRIDES,
|
||||||
} from '../../../static/js/utils/genParamsMapper.js';
|
} from '../../../static/js/utils/genParamsMapper.js';
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -243,4 +244,53 @@ describe('findMatchingWidgets', () => {
|
|||||||
const updates = findMatchingWidgets(['seed', 'steps', 'cfg', 'sampler_name', 'scheduler'], resolved);
|
const updates = findMatchingWidgets(['seed', 'steps', 'cfg', 'sampler_name', 'scheduler'], resolved);
|
||||||
expect(updates.map(u => u.widgetName)).toEqual(['seed', 'steps', 'cfg', 'sampler_name', 'scheduler']);
|
expect(updates.map(u => u.widgetName)).toEqual(['seed', 'steps', 'cfg', 'sampler_name', 'scheduler']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// --- node-type-specific overrides ---
|
||||||
|
it('matches GlobalSeed //Inspire value widget for seed param', () => {
|
||||||
|
const updates = findMatchingWidgets(
|
||||||
|
['value', 'mode', 'action', 'last_seed'],
|
||||||
|
{ seed: 42 },
|
||||||
|
'GlobalSeed //Inspire'
|
||||||
|
);
|
||||||
|
expect(updates).toHaveLength(1);
|
||||||
|
expect(updates[0]).toEqual({ widgetName: 'value', value: 42 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignores nodeType when it does not match any override entry', () => {
|
||||||
|
const updates = findMatchingWidgets(
|
||||||
|
['value', 'mode', 'action', 'last_seed'],
|
||||||
|
{ seed: 42 },
|
||||||
|
'SomeOtherNode'
|
||||||
|
);
|
||||||
|
expect(updates).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('still falls back to global candidates when override candidates do not match', () => {
|
||||||
|
// GlobalSeed override does not include steps — should use global candidate "steps"
|
||||||
|
const updates = findMatchingWidgets(
|
||||||
|
['steps', 'cfg', 'sampler_name'],
|
||||||
|
{ steps: 20 },
|
||||||
|
'GlobalSeed //Inspire'
|
||||||
|
);
|
||||||
|
expect(updates).toHaveLength(1);
|
||||||
|
expect(updates[0]).toEqual({ widgetName: 'steps', value: 20 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prefers overrides when both override and global candidates match', () => {
|
||||||
|
// If a hypothetical node has both "value" and "seed" widgets AND a
|
||||||
|
// GlobalSeed override, the override candidate "value" should take precedence
|
||||||
|
const updates = findMatchingWidgets(
|
||||||
|
['seed', 'noise_seed', 'value', 'mode'],
|
||||||
|
{ seed: 99 },
|
||||||
|
'GlobalSeed //Inspire'
|
||||||
|
);
|
||||||
|
expect(updates).toHaveLength(1);
|
||||||
|
expect(updates[0].widgetName).toBe('value');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('omits nodeType argument and still matches via global candidates', () => {
|
||||||
|
const updates = findMatchingWidgets(['seed', 'steps', 'cfg'], { seed: 7 });
|
||||||
|
expect(updates).toHaveLength(1);
|
||||||
|
expect(updates[0]).toEqual({ widgetName: 'seed', value: 7 });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user