Another test

This commit is contained in:
space-nuko
2023-05-21 18:27:37 -05:00
parent 57f635bbfd
commit 93afb64d63
5 changed files with 178 additions and 34 deletions

View File

@@ -169,41 +169,46 @@ function rewriteIDsInGraph(vanillaWorkflow: ComfyVanillaWorkflow) {
}
/*
* Returns [nodeType, inputType] for a config type, like "FLOAT" -> ["ui/number", "number"]
* Returns [nodeType, inputType, addedWidgetCount] for a config type, like "FLOAT" -> ["ui/number", "number", 1]
* For "INT:seed" it's ["ui/number", "number", 2] since that type adds a randomizer combo widget
*/
function getWidgetTypesFromConfig(inputType: ComfyNodeDefInputType): [string, SlotType] | null {
function getWidgetTypesFromConfig(inputName: string, inputType: ComfyNodeDefInputType): [string, SlotType, number] | null {
let widgetNodeType = null;
let widgetInputType = null;
let addedWidgetCount = 1;
if (Array.isArray(inputType)) {
// Combo options of string[]
widgetNodeType = "ui/combo";
widgetInputType = "string"
addedWidgetCount = 1;
}
else if (`${inputType}:${inputName}` in ComfyWidgets) {
// Widget type override for input of type with given name ("seed", "noise_seed")
const widgetFactory = ComfyWidgets[`${inputType}:${inputName}`]
widgetNodeType = widgetFactory.nodeType;
widgetInputType = widgetFactory.inputType
addedWidgetCount = widgetFactory.addedWidgetCount
}
else if (inputType in ComfyWidgets) {
// Widget type
const widgetFactory = ComfyWidgets[inputType]
widgetNodeType = widgetFactory.nodeType;
widgetInputType = widgetFactory.inputType
}
else if ("${inputType}:{inputName}" in ComfyWidgets) {
// Widget type override for input of type with given name ("seed", "noise_seed")
const widgetFactory = ComfyWidgets["${inputType}:{inputName}"]
widgetNodeType = widgetFactory.nodeType;
widgetInputType = widgetFactory.inputType
addedWidgetCount = widgetFactory.addedWidgetCount
}
else {
// Backend type, we can safely ignore this
return null;
}
return [widgetNodeType, widgetInputType]
return [widgetNodeType, widgetInputType, addedWidgetCount]
}
function configureWidgetNodeProperties(serWidgetNode: SerializedComfyWidgetNode, inputOpts?: ComfyNodeDefInputOptions) {
inputOpts ||= {}
switch (serWidgetNode.type) {
case "ui/number":
case `ui/number`:
serWidgetNode.properties.min = inputOpts.min || 0;
serWidgetNode.properties.max = inputOpts.max || 100;
serWidgetNode.properties.step = inputOpts.step || 1;
@@ -240,17 +245,17 @@ function convertPrimitiveNode(vanillaWorkflow: ComfyVanillaWorkflow, node: Seria
return false;
}
let pair = getWidgetTypesFromConfig(widgetType);
let pair = getWidgetTypesFromConfig(widget.name, widgetType);
if (pair == null) {
// This should never happen! Primitive nodes only deal with frontend types!
console.error("PrimitiveNode had a backend type configured!", node)
return false;
}
let [widgetNodeType, widgetInputType] = pair
let [widgetNodeType, widgetInputType, addedWidgetCount] = pair
// PrimitiveNode will have a widget in the first slot with the actual value.
// The rest are configuration values for e.g. seed action onprompt queue.
// The rest are configuration values for e.g. seed action on prompt queue.
const value = node.widgets_values[0];
const [comfyWidgetNode, serWidgetNode] = createSerializedWidgetNode(
@@ -384,23 +389,26 @@ export default function convertVanillaWorkflow(vanillaWorkflow: ComfyVanillaWork
return i.widget?.name === inputName;
})
let pair = getWidgetTypesFromConfig(inputName, inputType);
if (pair == null) {
// Input type is backend-only, we can skip adding a UI node here
continue
}
let [widgetNodeType, widgetInputType, widgetCount] = pair
if (convertedWidget != null) {
// This input is an extra input slot on the node that should be
// accounted for.
const [value] = node.widgets_values.splice(0, 1);
const values = node.widgets_values.splice(0, widgetCount);
const value = values[0]
// TODO
}
else {
// This input is a widget, it should be converted to an input
// connected to a ComfyWidgetNode.
let pair = getWidgetTypesFromConfig(inputType);
if (pair == null) {
// Input type is backend-only, we can skip adding a UI node here
continue
}
let [widgetNodeType, widgetInputType] = pair
const newInput: IComfyInputSlot = {
name: inputName,
link: null,
@@ -417,7 +425,17 @@ export default function convertVanillaWorkflow(vanillaWorkflow: ComfyVanillaWork
const connInputIndex = node.inputs.length - 1;
// Now get the widget value.
const [value] = node.widgets_values.splice(0, 1);
//
// Assumes the value is the first in the widget list for the
// case of e.g. the seed randomizer
// That input type adds a number widget and a combo widget so
// the widgets_values will have entries like
//
// [ 8, "randomize", ... ]
//
// Only care about 8 and want to skip "randomize", that's the purpose of `widgetCount`
const values = node.widgets_values.splice(0, widgetCount);
const value = values[0]
const [comfyWidgetNode, serWidgetNode] = createSerializedWidgetNode(
vanillaWorkflow,

View File

@@ -312,7 +312,7 @@ export default class ComfyGraphNode extends LGraphNode {
(o as any).saveUserState = this.saveUserState
if (!this.saveUserState && (!get(uiState).isSavingToLocalStorage || get(configState).alwaysStripUserState)) {
this.stripUserState(o)
console.warn("[ComfyGraphNode] stripUserState", this, o)
console.debug("[ComfyGraphNode] stripUserState", this, o)
}
}

View File

@@ -12,7 +12,9 @@ type WidgetFactory = {
/* Input type as used by litegraph */
inputType: string,
/* Node type to instantiate */
nodeType: string
nodeType: string,
/* Number of widgets this factory instantiates. */
addedWidgetCount: number
}
function getNumberDefaults(inputData: ComfyNodeDefInput, defaultStep: number): ComfyInputConfig {
@@ -49,7 +51,8 @@ const FLOAT: WidgetFactory = {
return addComfyInput(node, inputName, { type: "number", config, defaultWidgetNode: ComfyNumberNode })
},
inputType: "number",
nodeType: "ui/number"
nodeType: "ui/number",
addedWidgetCount: 1
}
const INT: WidgetFactory = {
@@ -58,7 +61,8 @@ const INT: WidgetFactory = {
return addComfyInput(node, inputName, { type: "number", config, defaultWidgetNode: ComfyNumberNode })
},
nodeType: "ui/number",
inputType: "number"
inputType: "number",
addedWidgetCount: 1
}
const STRING: WidgetFactory = {
@@ -69,7 +73,8 @@ const STRING: WidgetFactory = {
return addComfyInput(node, inputName, { type: "string", config: { defaultValue, multiline }, defaultWidgetNode: ComfyTextNode })
},
inputType: "number",
nodeType: "ui/text"
nodeType: "ui/text",
addedWidgetCount: 1
}
const COMBO: WidgetFactory = {
@@ -82,7 +87,8 @@ const COMBO: WidgetFactory = {
return addComfyInput(node, inputName, { type: "string", config: { values: type, defaultValue }, defaultWidgetNode: ComfyComboNode })
},
inputType: "number",
nodeType: "ui/combo"
nodeType: "ui/combo",
addedWidgetCount: 1
}
const IMAGEUPLOAD: WidgetFactory = {
@@ -90,14 +96,25 @@ const IMAGEUPLOAD: WidgetFactory = {
return addComfyInput(node, inputName, { type: "number", config: {} })
},
inputType: "COMFY_IMAGES",
nodeType: "ui/image_upload"
nodeType: "ui/image_upload",
addedWidgetCount: 1
}
const INT_seed: WidgetFactory = {
...INT,
// Adds a "randomize" combo box
// When converting from vanilla it should be skipped in the widgets_values
// array, so indicate this here
// litegraph really ought to key these by name instead of array indices...
addedWidgetCount: 2
}
export type WidgetRepository = Record<string, WidgetFactory>
const ComfyWidgets: WidgetRepository = {
"INT:seed": INT,
"INT:noise_seed": INT,
"INT:seed": INT_seed,
"INT:noise_seed": INT_seed,
FLOAT,
INT,
STRING,

View File

@@ -10,10 +10,11 @@ import { LiteGraph } from '@litegraph-ts/core';
import type { ComfyNodeDef } from '$lib/ComfyNodeDef';
const objectInfo: Record<string, ComfyNodeDef> = await import("./data/objectInfo.json")
const json1: ComfyVanillaWorkflow = await import("./data/convertedWidgetAndPrimitiveNode.json")
const json1: ComfyVanillaWorkflow = await import("./data/convertedWidget.json")
const json2: ComfyVanillaWorkflow = await import("./data/convertedWidgetAndPrimitiveNode.json")
export default class convertVanillaWorkflowTests extends UnitTest {
test__convertsPrimitiveNodeAndConvertedInput() {
test__convertsWidget() {
const workflow = LiteGraph.cloneObject(json1)
const attrs: WorkflowAttributes = { ...defaultWorkflowAttributes }
@@ -31,6 +32,46 @@ export default class convertVanillaWorkflowTests extends UnitTest {
expect(Object.keys(layout.allItems)).toHaveLength(10)
const widgets = Object.values(layout.allItems).filter(di => di.dragItem.type === "widget").map(di => di.dragItem) as WidgetLayout[];
expect(widgets).toHaveLength(6);
const widgetsValues = widgets.map(w => { return [w.node.type, w.node.getValue(), w.attrs.title] })
expect(widgetsValues).toEqual([
["ui/number", 0, 'seed'],
["ui/number", 20, 'steps'],
["ui/number", 8.5, 'cfg'],
["ui/combo", 'euler', 'sampler_name'],
["ui/combo", 'normal', 'scheduler'],
["ui/number", 1, 'denoise']
]);
const widget = widgets.find(w => w.attrs.title === "cfg") as WidgetLayout | null;
expect(widget).toBeDefined();
expect(widget.node).toBeDefined();
expect(widget.node.type).toEqual("ui/number")
expect(widget.node.getValue()).toEqual(8.5)
expect(convWorkflow.graph.getNodeById(widget.node.id)).toEqual(widget.node)
}
test__convertsPrimitiveNodeAndConvertedInput() {
const workflow = LiteGraph.cloneObject(json2)
const attrs: WorkflowAttributes = { ...defaultWorkflowAttributes }
ComfyApp.knownBackendNodes["KSampler"] = {
nodeDef: objectInfo["KSampler"]
}
const converted = convertVanillaWorkflow(workflow, attrs)
expect(converted).toBeInstanceOf(Array)
const [convWorkflow, convLayout] = converted;
const layout = get(convLayout)
expect(Object.keys(layout.allItems)).toHaveLength(10)
const widgets = Object.values(layout.allItems).filter(di => di.dragItem.type === "widget").map(di => di.dragItem);
expect(widgets).toHaveLength(6);

View File

@@ -0,0 +1,68 @@
{
"last_node_id": 1,
"last_link_id": 0,
"nodes": [
{
"id": 1,
"type": "KSampler",
"pos": [
707,
502
],
"size": {
"0": 315,
"1": 262
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null
},
{
"name": "latent_image",
"type": "LATENT",
"link": null
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
0,
"randomize",
20,
8.5,
"euler",
"normal",
1
]
}
],
"links": [],
"groups": [],
"config": {},
"extra": {},
"version": 0.4
}