From 09e806bd3e7a001c4ca960b47fb5261da968d5d8 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Sat, 20 May 2023 21:42:38 -0500 Subject: [PATCH] Prompt serializer and test fixes --- litegraph | 2 +- public/workflows/defaultWorkflow.json | 9 +-- src/lib/ComfyGraph.ts | 5 +- src/lib/components/ComfyApp.ts | 13 ++-- src/lib/components/ComfyPromptSerializer.ts | 14 +++- src/lib/components/ComfyProperties.svelte | 20 ++++-- src/lib/components/ComfyQueue.svelte | 16 +++-- src/lib/components/ComfyWorkflowsView.svelte | 17 ++--- src/lib/defaultGraph.ts | 14 ++-- .../nodes/ComfyConfigureQueuePromptButton.ts | 11 ++- src/lib/nodes/ComfyGraphNode.ts | 5 ++ src/lib/stores/layoutStates.ts | 40 +---------- src/lib/stores/workflowState.ts | 68 +++++++++++++++---- src/lib/utils.ts | 22 ++++-- src/mobile/GenToolbar.svelte | 9 +-- src/tests/ComfyGraphTests.ts | 2 +- src/tests/ComfyPromptSerializerTests.ts | 35 +++++++++- 17 files changed, 196 insertions(+), 106 deletions(-) diff --git a/litegraph b/litegraph index cd4f68e..6e4c830 160000 --- a/litegraph +++ b/litegraph @@ -1 +1 @@ -Subproject commit cd4f68ef42c52e7337009eec81f5e539de8999ad +Subproject commit 6e4c8301cd53fb33ded2f3fd7bd8c98e73d45ee8 diff --git a/public/workflows/defaultWorkflow.json b/public/workflows/defaultWorkflow.json index f445e4d..1e89326 100644 --- a/public/workflows/defaultWorkflow.json +++ b/public/workflows/defaultWorkflow.json @@ -1,6 +1,11 @@ { "createdBy": "ComfyBox", "version": 1, + "attrs": { + "title": "Default", + "queuePromptButtonName": "Queue txt2img", + "queuePromptButtonRunWorkflow": false + }, "workflow": { "last_node_id": 0, "last_link_id": 0, @@ -25709,10 +25714,6 @@ ], "parent": "eae32e42-1ccc-4a4a-923f-7ab4ccdac97a" } - }, - "attrs": { - "queuePromptButtonName": "Queue txt2img", - "queuePromptButtonRunWorkflow": false } }, "canvas": { diff --git a/src/lib/ComfyGraph.ts b/src/lib/ComfyGraph.ts index bab45cb..10ea7dc 100644 --- a/src/lib/ComfyGraph.ts +++ b/src/lib/ComfyGraph.ts @@ -118,7 +118,6 @@ export default class ComfyGraph extends LGraph { } if (get(uiState).autoAddUI) { - console.warn("ADD", node.type, options) if (!("svelteComponentType" in node) && options.addedBy == null) { console.debug("[ComfyGraph] AutoAdd UI") const comfyNode = node as ComfyGraphNode; @@ -167,10 +166,10 @@ export default class ComfyGraph extends LGraph { override onNodeRemoved(node: LGraphNode, options: LGraphRemoveNodeOptions) { selectionState.clear(); // safest option - if (node.getRootGraph() != null && !this._is_subgraph && this.workflowID != null) { + if (!this._is_subgraph && this.workflowID != null) { const layoutState = get(layoutStates).all[this.workflowID] if (layoutState === null) { - throw new Error(`LGraph with workflow missing layout! ${this.workflowID}`) + throw new Error(`ComfyGraph with workflow missing layout! ${this.workflowID}`) } layoutState.nodeRemoved(node, options); diff --git a/src/lib/components/ComfyApp.ts b/src/lib/components/ComfyApp.ts index 158af72..31fa2f1 100644 --- a/src/lib/components/ComfyApp.ts +++ b/src/lib/components/ComfyApp.ts @@ -42,7 +42,7 @@ import type { ComfyBoxStdPrompt } from "$lib/ComfyBoxStdPrompt"; import ComfyBoxStdPromptSerializer from "$lib/ComfyBoxStdPromptSerializer"; import selectionState from "$lib/stores/selectionState"; import layoutStates from "$lib/stores/layoutStates"; -import { ComfyWorkflow } from "$lib/stores/workflowState"; +import { ComfyWorkflow, type WorkflowAttributes } from "$lib/stores/workflowState"; import workflowState from "$lib/stores/workflowState"; export const COMFYBOX_SERIAL_VERSION = 1; @@ -80,8 +80,8 @@ export type SerializedAppState = { commitHash?: string, /** Graph state */ workflow: SerializedLGraph, - /** Workflow name */ - workflowName: string, + /** Workflow attributes */ + attrs: WorkflowAttributes, /** UI state */ layout: SerializedLayoutState, /** Position/offset of the canvas at the time of saving */ @@ -165,7 +165,7 @@ export default class ComfyApp { async setup(): Promise { if (get(this.alreadySetup)) { - console.error("Already setup") + console.log("Already setup") return; } @@ -241,7 +241,7 @@ export default class ComfyApp { if (layoutState == null) throw new Error("Workflow has no layout!") - const { graph, layout } = workflow.serialize(layoutState); + const { graph, layout, attrs } = workflow.serialize(layoutState); const canvas = this.lCanvas.serialize(); return { @@ -249,6 +249,7 @@ export default class ComfyApp { version: COMFYBOX_SERIAL_VERSION, commitHash: __GIT_COMMIT_HASH__, workflow: graph, + attrs, layout, canvas } @@ -566,7 +567,7 @@ export default class ComfyApp { } } - if (get(workflow.layout).attrs.queuePromptButtonRunWorkflow) { + if (workflow.attrs.queuePromptButtonRunWorkflow) { // Hold control to queue at the front const num = this.ctrlDown ? -1 : 0; this.queuePrompt(num, 1); diff --git a/src/lib/components/ComfyPromptSerializer.ts b/src/lib/components/ComfyPromptSerializer.ts index de55bf8..792724c 100644 --- a/src/lib/components/ComfyPromptSerializer.ts +++ b/src/lib/components/ComfyPromptSerializer.ts @@ -35,7 +35,15 @@ export function isActiveBackendNode(node: LGraphNode, tag: string | null = null) if (!(node as any).isBackendNode) return false; - return isActiveNode(node, tag); + if (!isActiveNode(node, tag)) + return false; + + // Make sure this node is not contained in an inactive subgraph, even if the + // node itself is active + if (node.is(Subgraph) && !Array.from(node.iterateParentNodes()).every(n => isActiveNode(n, tag))) + return false; + + return true; } export class UpstreamNodeLocator { @@ -166,7 +174,7 @@ export default class ComfyPromptSerializer { // We don't check tags for non-backend nodes. // Just check for node inactivity (so you can toggle groups of // tagged frontend nodes on/off) - if (inputNode && inputNode.mode === NodeMode.NEVER) { + if (inputNode && inputNode.mode !== NodeMode.ALWAYS) { console.debug("Skipping inactive node", inputNode) continue; } @@ -248,6 +256,8 @@ export default class ComfyPromptSerializer { const inputs = this.serializeInputValues(node); const links = this.serializeBackendLinks(node, tag); + console.warn("OUTPUT", node.id, node.comfyClass, node.mode) + output[String(node.id)] = { inputs: { ...inputs, ...links }, class_type: node.comfyClass, diff --git a/src/lib/components/ComfyProperties.svelte b/src/lib/components/ComfyProperties.svelte index 86d9019..feb628f 100644 --- a/src/lib/components/ComfyProperties.svelte +++ b/src/lib/components/ComfyProperties.svelte @@ -4,6 +4,7 @@ import { LGraphNode } from "@litegraph-ts/core" import { type IDragItem, type WidgetLayout, ALL_ATTRIBUTES, type AttributesSpec, type WritableLayoutStateStore } from "$lib/stores/layoutStates" import uiState from "$lib/stores/uiState" + import workflowState from "$lib/stores/workflowState" import layoutStates from "$lib/stores/layoutStates" import selectionState from "$lib/stores/selectionState" import { get, type Writable, writable } from "svelte/store" @@ -127,7 +128,10 @@ if (spec.location !== "workflow") return false; - return spec.name in $layoutState.attrs + if (workflow == null) + return false; + + return spec.name in workflow.attrs } function getAttribute(target: IDragItem, spec: AttributesSpec): any { @@ -240,7 +244,10 @@ } function getWorkflowAttribute(spec: AttributesSpec): any { - let value = $layoutState.attrs[spec.name] + if (workflow == null) + throw new Error("Active workflow is null!"); + + let value = workflow.attrs[spec.name] if (value == null) value = spec.defaultValue else if (spec.serialize) @@ -253,17 +260,20 @@ if (!spec.editable) return; + if (workflow == null) + throw new Error("Active workflow is null!"); + const name = spec.name // console.warn("[ComfyProperties] updateWorkflowAttribute", name, value) const prevValue = value - $layoutState.attrs[name] = value - $layoutState = $layoutState + workflow.attrs[name] = value + $workflowState = $workflowState; if (spec.onChanged) spec.onChanged($layoutState, value, prevValue) - if (spec.refreshPanelOnChange) + // if (spec.refreshPanelOnChange) doRefreshPanel() } diff --git a/src/lib/components/ComfyQueue.svelte b/src/lib/components/ComfyQueue.svelte index edaaaee..bdf0b2b 100644 --- a/src/lib/components/ComfyQueue.svelte +++ b/src/lib/components/ComfyQueue.svelte @@ -13,6 +13,7 @@ import { tick } from "svelte"; import Modal from "./Modal.svelte"; import DropZone from "./DropZone.svelte"; + import workflowState from "$lib/stores/workflowState"; export let app: ComfyApp; @@ -71,10 +72,17 @@ const subgraphs: string[] | null = entry.extraData?.extra_pnginfo?.comfyBoxSubgraphs; let message = "Prompt"; - if (subgraphs?.length > 0) - message = `Prompt: ${subgraphs.join(', ')}` + if (entry.workflowID != null) { + const workflow = workflowState.getWorkflow(entry.workflowID); + if (workflow != null && workflow.attrs.title) { + message = `Workflow: ${workflow.attrs.title}` + } + if (subgraphs?.length > 0) + message += ` (${subgraphs.join(', ')})` + } let submessage = `Nodes: ${Object.keys(entry.prompt).length}` + if (Object.keys(entry.outputs).length > 0) { const imageCount = Object.values(entry.outputs).flatMap(o => o.images).length submessage = `Images: ${imageCount}` @@ -84,7 +92,7 @@ entry, message, submessage, - dateStr, + date: dateStr, status: "pending", images: [] } @@ -387,7 +395,7 @@ &.all_cached, &.interrupted { filter: brightness(80%); - color: var(--neutral-300); + color: var(--comfy-accent-soft); } } diff --git a/src/lib/components/ComfyWorkflowsView.svelte b/src/lib/components/ComfyWorkflowsView.svelte index 118d3d8..2d9eaeb 100644 --- a/src/lib/components/ComfyWorkflowsView.svelte +++ b/src/lib/components/ComfyWorkflowsView.svelte @@ -10,7 +10,7 @@ import { get, writable, type Writable } from "svelte/store"; import ComfyProperties from "./ComfyProperties.svelte"; import uiState from "$lib/stores/uiState"; - import workflowState from "$lib/stores/workflowState"; + import workflowState, { ComfyWorkflow } from "$lib/stores/workflowState"; import selectionState from "$lib/stores/selectionState"; import type ComfyApp from './ComfyApp'; import { onMount } from "svelte"; @@ -19,7 +19,7 @@ export let app: ComfyApp; export let uiTheme: string = "gradio-dark" // TODO config - let layoutState: WritableLayoutStateStore | null = null; + let workflow: ComfyWorkflow | null = null; let containerElem: HTMLDivElement; let resizeTimeout: NodeJS.Timeout | null; @@ -29,7 +29,7 @@ let appSetupPromise: Promise = null; - $: layoutState = $workflowState.activeWorkflow?.layout; + $: workflow = $workflowState.activeWorkflow; onMount(async () => { appSetupPromise = app.setup().then(() => { @@ -189,10 +189,10 @@ {/each} @@ -200,9 +200,9 @@
- {#if layoutState != null && $layoutState.attrs.queuePromptButtonName != ""} + {#if workflow != null && workflow.attrs.queuePromptButtonName != ""} {/if}