Strip user data from workflows if configured

This commit is contained in:
space-nuko
2023-05-06 23:08:52 -05:00
parent da65d1b439
commit cce58dc9bf
9 changed files with 123 additions and 67 deletions

View File

@@ -102,7 +102,7 @@
if (!app?.lGraph)
return;
const promptFilename = false; // TODO
const promptFilename = true; // TODO
let filename = "workflow.json";
if (promptFilename) {
@@ -133,6 +133,7 @@
function loadWorkflow(): void {
app.handleFile(fileInput.files[0]);
fileInput.files = null;
}
function doSaveLocal(): void {

View File

@@ -27,7 +27,7 @@ import ComfyGraph from "$lib/ComfyGraph";
import { ComfyBackendNode } from "$lib/nodes/ComfyBackendNode";
import { get } from "svelte/store";
import uiState from "$lib/stores/uiState";
import { promptToGraphVis } from "$lib/utils";
import { promptToGraphVis, workflowToGraphVis } from "$lib/utils";
export const COMFYBOX_SERIAL_VERSION = 1;
@@ -145,9 +145,6 @@ export default class ComfyApp {
this.resizeCanvas();
window.addEventListener("resize", this.resizeCanvas.bind(this));
this.lGraph.start();
this.lGraph.eventBus.on("afterExecute", () => this.lCanvas.draw(true))
this.alreadySetup = true;
return Promise.resolve();
@@ -374,6 +371,9 @@ export default class ComfyApp {
this.lCanvas.deserialize(data.canvas)
await this.refreshComboInNodes();
this.lGraph.start();
this.lGraph.eventBus.on("afterExecute", () => this.lCanvas.draw(true))
}
async initDefaultGraph() {
@@ -606,6 +606,7 @@ export default class ComfyApp {
'--toastBackground': 'var(--color-red-500)',
}
})
console.error(promptToGraphVis(p))
console.error("Error queuing prompt", mes, num, p)
break;
}

View File

@@ -55,24 +55,21 @@ LiteGraph.registerNodeType({
type: "actions/queue_events"
})
export interface ComfyOnExecutedEventProperties extends Record<any, any> {
images: GalleryOutput | null,
filename: string | null
export interface ComfyStoreImagesActionProperties extends Record<any, any> {
images: GalleryOutput | null
}
export class ComfyOnExecutedEvent extends ComfyGraphNode {
override properties: ComfyOnExecutedEventProperties = {
images: null,
filename: null
export class ComfyStoreImagesAction extends ComfyGraphNode {
override properties: ComfyStoreImagesActionProperties = {
images: null
}
static slotLayout: SlotLayout = {
inputs: [
{ name: "images", type: "IMAGE" }
{ name: "output", type: BuiltInSlotType.ACTION, options: { color_off: "rebeccapurple", color_on: "rebeccapurple" } }
],
outputs: [
{ name: "images", type: "OUTPUT" },
{ name: "onExecuted", type: BuiltInSlotType.EVENT },
],
}
@@ -81,20 +78,20 @@ export class ComfyOnExecutedEvent extends ComfyGraphNode {
this.setOutputData(0, this.properties.images)
}
override receiveOutput(output: any) {
if (output && "images" in output) {
this.setProperty("images", output as GalleryOutput)
this.setOutputData(0, this.properties.images)
this.triggerSlot(1, "bang")
}
override onAction(action: any, param: any) {
if (action !== "store" || !param || !("images" in param))
return;
this.setProperty("images", param as GalleryOutput)
this.setOutputData(0, this.properties.images)
}
}
LiteGraph.registerNodeType({
class: ComfyOnExecutedEvent,
title: "Comfy.OnExecutedEvent",
desc: "Triggers a 'bang' event when a prompt output is received.",
type: "actions/on_executed"
class: ComfyStoreImagesAction,
title: "Comfy.StoreImagesAction",
desc: "Stores images from an onExecuted callback",
type: "actions/store_images"
})
export interface ComfyCopyActionProperties extends Record<any, any> {

View File

@@ -2,7 +2,9 @@ import LGraphCanvas from "@litegraph-ts/core/src/LGraphCanvas";
import ComfyGraphNode from "./ComfyGraphNode";
import ComfyWidgets from "$lib/widgets"
import type { ComfyWidgetNode } from "./ComfyWidgetNodes";
import type { SerializedLGraphNode } from "@litegraph-ts/core";
import { BuiltInSlotType, type SerializedLGraphNode } from "@litegraph-ts/core";
import type IComfyInputSlot from "$lib/IComfyInputSlot";
import type { ComfyInputConfig } from "$lib/IComfyInputSlot";
/*
* Base class for any node with configuration sent by the backend.
@@ -26,7 +28,7 @@ export class ComfyBackendNode extends ComfyGraphNode {
// It just returns a hash like { "ui": { "images": results } } internally.
// So this will need to be hardcoded for now.
if (["PreviewImage", "SaveImage"].indexOf(comfyClass) !== -1) {
this.addOutput("output", "IMAGE");
this.addOutput("onExecuted", BuiltInSlotType.EVENT, { color_off: "rebeccapurple", color_on: "rebeccapurple" });
}
}
@@ -36,14 +38,19 @@ export class ComfyBackendNode extends ComfyGraphNode {
*/
tags: string[] = []
private static defaultInputConfigs: Record<string, Record<string, ComfyInputConfig>> = {}
private setup(nodeData: any) {
var inputs = nodeData["input"]["required"];
if (nodeData["input"]["optional"] != undefined) {
inputs = Object.assign({}, nodeData["input"]["required"], nodeData["input"]["optional"])
}
const config = { minWidth: 1, minHeight: 1 };
ComfyBackendNode.defaultInputConfigs[this.type] = {}
for (const inputName in inputs) {
const config = { minWidth: 1, minHeight: 1 };
const inputData = inputs[inputName];
const type = inputData[0];
@@ -64,6 +71,9 @@ export class ComfyBackendNode extends ComfyGraphNode {
this.addInput(inputName, type);
}
}
if ("widgetNodeType" in config)
ComfyBackendNode.defaultInputConfigs[this.type][config.name] = config.config
}
for (const o in nodeData["output"]) {
@@ -72,39 +82,44 @@ export class ComfyBackendNode extends ComfyGraphNode {
this.addOutput(outputName, output);
}
const s = this.computeSize();
s[0] = Math.max(config.minWidth, s[0] * 1.5);
s[1] = Math.max(config.minHeight, s[1]);
this.size = s;
this.serialize_widgets = false;
// app.#invokeExtensionsAsync("nodeCreated", this);
}
override onSerialize(o: SerializedLGraphNode) {
super.onSerialize(o);
(o as any).tags = this.tags
for (const input of o.inputs) {
// strip user-identifying data, it will be reinstantiated later
if ((input as any).config != null) {
(input as any).config = {};
}
}
}
override onConfigure(o: SerializedLGraphNode) {
super.onConfigure(o);
this.tags = (o as any).tags || []
const configs = ComfyBackendNode.defaultInputConfigs[o.type]
for (let index = 0; index < this.inputs.length; index++) {
const input = this.inputs[index] as IComfyInputSlot
const config = configs[input.name]
if (config != null && index >= 0 && index < this.inputs.length) {
if (input.config == null || Object.keys(input.config).length !== Object.keys(config).length) {
console.error("SET", input, config)
input.config = config
}
}
else {
input.config = {}
}
}
}
override onExecuted(outputData: any) {
console.warn("onExecuted outputs", outputData)
for (let index = 0; index < this.outputs.length; index++) {
const output = this.outputs[index]
if (output.type === "IMAGE") {
this.setOutputData(index, outputData)
for (const node of this.getOutputNodes(index)) {
console.warn(node)
if ("receiveOutput" in node) {
const widgetNode = node as ComfyGraphNode;
widgetNode.receiveOutput(outputData);
}
}
}
}
this.triggerSlot(0, outputData)
}
}

View File

@@ -26,8 +26,17 @@ export default class ComfyGraphNode extends LGraphNode {
defaultWidgets?: DefaultWidgetLayout
/** Called when a backend node sends a ComfyUI output over a link */
receiveOutput(output: any) {
/*
* If false, don't serialize user-set properties into the workflow.
* Useful for removing personal information from shared workflows.
*/
saveUserState: boolean = true;
/*
* Called to remove user-set properties from this node.
*/
stripUserState(o: SerializedLGraphNode) {
o.widgets_values = []
}
override onResize(size: Vector2) {
@@ -56,6 +65,11 @@ export default class ComfyGraphNode extends LGraphNode {
(serInput as any).defaultWidgetNode = null
}
}
(o as any).saveUserState = this.saveUserState
if (!this.saveUserState) {
this.stripUserState(o)
console.warn("[ComfyGraphNode] stripUserState", this, o)
}
}
override onConfigure(o: SerializedLGraphNode) {
@@ -71,5 +85,8 @@ export default class ComfyGraphNode extends LGraphNode {
comfyInput.defaultWidgetNode = widgetNode.class as any
}
}
this.saveUserState = (o as any).saveUserState;
if (this.saveUserState == null)
this.saveUserState = true
}
}

View File

@@ -29,7 +29,7 @@ export default class ComfyImageCacheNode extends ComfyGraphNode {
inputs: [
{ name: "images", type: "OUTPUT" },
{ name: "index", type: "number" },
{ name: "store", type: BuiltInSlotType.ACTION },
{ name: "store", type: BuiltInSlotType.ACTION, options: { color_off: "rebeccapurple", color_on: "rebeccapurple" } },
{ name: "clear", type: BuiltInSlotType.ACTION }
],
outputs: [
@@ -204,7 +204,7 @@ export default class ComfyImageCacheNode extends ComfyGraphNode {
}
}
override onAction(action: any) {
override onAction(action: any, param: any) {
if (action === "clear") {
this.setProperty("images", null)
this.setProperty("filenames", {})
@@ -213,12 +213,10 @@ export default class ComfyImageCacheNode extends ComfyGraphNode {
return
}
const link = this.getInputLink(0)
if (link.data && "images" in link.data) {
if (param && "images" in param) {
this.setProperty("genNumber", this.properties.genNumber + 1)
const output = link.data as GalleryOutput;
const output = param as GalleryOutput;
if (this.properties.updateMode === "append" && this.properties.images != null) {
const newImages = this.properties.images.images.concat(output.images)
@@ -226,7 +224,7 @@ export default class ComfyImageCacheNode extends ComfyGraphNode {
this.setProperty("images", this.properties.images)
}
else {
this.setProperty("images", link.data as GalleryOutput)
this.setProperty("images", param as GalleryOutput)
this.setProperty("filenames", {})
}

View File

@@ -39,6 +39,8 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
copyFromInputLink: boolean = true;
abstract defaultValue: T;
/** Names of properties to add as inputs */
// shownInputProperties: string[] = []
@@ -204,15 +206,21 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
clampOneConfig(input: IComfyInputSlot) { }
override onSerialize(o: SerializedLGraphNode) {
super.onSerialize(o);
(o as any).comfyValue = get(this.value);
(o as any).shownOutputProperties = this.shownOutputProperties
super.onSerialize(o);
}
override onConfigure(o: SerializedLGraphNode) {
this.value.set((o as any).comfyValue);
this.shownOutputProperties = (o as any).shownOutputProperties;
}
override stripUserState(o: SerializedLGraphNode) {
super.stripUserState(o);
(o as any).comfyValue = this.defaultValue;
o.properties.defaultValue = null;
}
}
export interface ComfySliderProperties extends ComfyWidgetProperties {
@@ -232,6 +240,7 @@ export class ComfySliderNode extends ComfyWidgetNode<number> {
}
override svelteComponentType = RangeWidget
override defaultValue = 0;
static slotLayout: SlotLayout = {
inputs: [
@@ -303,6 +312,7 @@ export class ComfyComboNode extends ComfyWidgetNode<string> {
}
override svelteComponentType = ComboWidget
override defaultValue = "A";
constructor(name?: string) {
super(name, "A")
@@ -346,13 +356,20 @@ export class ComfyComboNode extends ComfyWidgetNode<string> {
}
override clampOneConfig(input: IComfyInputSlot) {
if (input.config.values.indexOf(this.properties.value) === -1) {
if (!input.config.values)
this.setValue("")
else if (input.config.values.indexOf(this.properties.value) === -1) {
if (input.config.values.length === 0)
this.setValue("")
else
this.setValue(input.config.defaultValue || input.config.values[0])
}
}
override stripUserState(o: SerializedLGraphNode) {
super.stripUserState(o);
o.properties.values = []
}
}
LiteGraph.registerNodeType({
@@ -384,6 +401,7 @@ export class ComfyTextNode extends ComfyWidgetNode<string> {
}
override svelteComponentType = TextWidget
override defaultValue = "";
constructor(name?: string) {
super(name, "")
@@ -433,7 +451,7 @@ export class ComfyGalleryNode extends ComfyWidgetNode<GradioFileData[]> {
static slotLayout: SlotLayout = {
inputs: [
{ name: "images", type: "OUTPUT" },
{ name: "store", type: BuiltInSlotType.ACTION },
{ name: "store", type: BuiltInSlotType.ACTION, options: { color_off: "rebeccapurple", color_on: "rebeccapurple" } },
{ name: "clear", type: BuiltInSlotType.ACTION }
],
outputs: [
@@ -446,6 +464,7 @@ export class ComfyGalleryNode extends ComfyWidgetNode<GradioFileData[]> {
]
override svelteComponentType = GalleryWidget
override defaultValue = []
override copyFromInputLink = false;
override outputIndex = null;
override changedIndex = null;
@@ -472,12 +491,11 @@ export class ComfyGalleryNode extends ComfyWidgetNode<GradioFileData[]> {
this.setValue([])
}
else if (action === "store") {
const link = this.getInputLink(0)
if (link.data && "images" in link.data) {
const data = link.data as GalleryOutput
if (param && "images" in param) {
const data = param as GalleryOutput
console.debug("[ComfyGalleryNode] Received output!", data)
const galleryItems: GradioFileData[] = this.convertItems(link.data)
const galleryItems: GradioFileData[] = this.convertItems(data)
if (this.properties.updateMode === "append") {
const currentValue = get(this.value)
@@ -546,8 +564,13 @@ export class ComfyButtonNode extends ComfyWidgetNode<boolean> {
]
}
override outputIndex = 1;
override svelteComponentType = ButtonWidget;
override defaultValue = false;
override outputIndex = 1;
constructor(name?: string) {
super(name, false)
}
override setValue(value: any) {
super.setValue(Boolean(value))
@@ -558,10 +581,6 @@ export class ComfyButtonNode extends ComfyWidgetNode<boolean> {
this.triggerSlot(0, this.properties.param);
this.setValue(false) // TODO onRelease
}
constructor(name?: string) {
super(name, false)
}
}
LiteGraph.registerNodeType({
@@ -587,6 +606,7 @@ export class ComfyCheckboxNode extends ComfyWidgetNode<boolean> {
}
override svelteComponentType = CheckboxWidget;
override defaultValue = false;
override setValue(value: any) {
value = Boolean(value)

View File

@@ -1,6 +1,6 @@
export { default as ComfyReroute } from "./ComfyReroute"
export { ComfyWidgetNode, ComfySliderNode, ComfyComboNode, ComfyTextNode } from "./ComfyWidgetNodes"
export { ComfyQueueEvents, ComfyCopyAction, ComfySwapAction, ComfyNotifyAction, ComfyOnExecutedEvent, ComfyExecuteSubgraphAction } from "./ComfyActionNodes"
export { ComfyQueueEvents, ComfyCopyAction, ComfySwapAction, ComfyNotifyAction, ComfyStoreImagesAction, ComfyExecuteSubgraphAction } from "./ComfyActionNodes"
export { default as ComfyValueControl } from "./ComfyValueControl"
export { default as ComfySelector } from "./ComfySelector"
export { default as ComfyImageCacheNode } from "./ComfyImageCacheNode"

View File

@@ -381,6 +381,13 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
serialize: serializeStringArray,
deserialize: deserializeStringArray
},
{
name: "saveUserState",
type: "boolean",
location: "nodeVars",
editable: true,
defaultValue: true,
},
// Range
{