diff --git a/src/lib/ComfyBoxStdPrompt.ts b/src/lib/ComfyBoxStdPrompt.ts index 61833a0..0f93f12 100644 --- a/src/lib/ComfyBoxStdPrompt.ts +++ b/src/lib/ComfyBoxStdPrompt.ts @@ -224,13 +224,13 @@ const Metadata = z.object({ extra_data: ExtraData }) -const ComfyBoxStdPrompt = z.object({ +const StdPrompt = z.object({ version: z.number(), metadata: Metadata, parameters: Parameters }) -export default ComfyBoxStdPrompt +export default StdPrompt /* * A standardized Stable Diffusion parameter format that should be used with an @@ -260,4 +260,4 @@ export default ComfyBoxStdPrompt * "see" width 1024 and height 1024, even though the only parameter exposed from * the frontend was the scale of 2.) */ -export type ComfyBoxStdPrompt = z.infer +export type ComfyBoxStdPrompt = z.infer diff --git a/src/lib/ComfyBoxStdPromptSerializer.ts b/src/lib/ComfyBoxStdPromptSerializer.ts index 86f9714..7e33d21 100644 --- a/src/lib/ComfyBoxStdPromptSerializer.ts +++ b/src/lib/ComfyBoxStdPromptSerializer.ts @@ -1,31 +1,88 @@ import type { ComfyBoxStdGroupLoRA, ComfyBoxStdPrompt } from "$lib/ComfyBoxStdPrompt"; -import type { SerializedPrompt, SerializedPromptInputs } from "./components/ComfyApp"; +import StdPrompt from "$lib/ComfyBoxStdPrompt"; +import type { SafeParseReturnType, ZodError } from "zod"; +import type { ComfyNodeID } from "./api"; +import type { SerializedAppState, SerializedPrompt, SerializedPromptInputs, SerializedPromptInputsAll } from "./components/ComfyApp"; +import { ComfyComboNode, type ComfyWidgetNode } from "./nodes/widgets"; +import { basename, isSerializedPromptInputLink } from "./utils"; -export type ComfyPromptConverter = (stdPrompt: ComfyBoxStdPrompt, inputs: SerializedPromptInputs, nodeID: ComfyNodeID) => void; - -function LoraLoader(stdPrompt: ComfyBoxStdPrompt, inputs: SerializedPromptInputs) { - const params = stdPrompt.parameters - - const lora: ComfyBoxStdGroupLoRA = { - model_name: inputs["lora_name"], - strength_unet: inputs["strength_model"], - strength_tenc: inputs["strength_clip"] - } - - if (params.lora) - params.lora.push(lora) - else - params.lora = [lora] +export type ComfyPromptConverter = { + encoder: ComfyPromptEncoder, + decoder: ComfyPromptDecoder } -const ALL_CONVERTERS: Record = { - LoraLoader +// +export type ComfyDecodeArgument = { + groupName: string, + keyName: string, + value: any, + widgetNode: ComfyWidgetNode +}; + +export type ComfyPromptEncoder = (stdPrompt: ComfyBoxStdPrompt, inputs: SerializedPromptInputs, nodeID: ComfyNodeID) => void; +export type ComfyPromptDecoder = (args: ComfyDecodeArgument[]) => void; + +const LoraLoader: ComfyPromptConverter = { + encoder: (stdPrompt: ComfyBoxStdPrompt, inputs: SerializedPromptInputs) => { + const params = stdPrompt.parameters + const loras: ComfyBoxStdGroupLoRA[] = params.lora + + for (const lora of loras) { + lora.model_hashes = { + addnet_shorthash: null // TODO find hashes for model! + } + } + }, + decoder: (args: ComfyDecodeArgument[]) => { + // Find corresponding model names in the ComfyUI models folder from the model base filename + for (const arg of args) { + if (arg.groupName === "lora" && arg.keyName === "model_name" && arg.widgetNode.is(ComfyComboNode)) { + const modelBasename = basename(arg.value); + const found = arg.widgetNode.properties.values.find(k => k.indexOf(modelBasename) !== -1) + if (found) + arg.value = found; + } + } + } +} + +// input name -> group/key in standard prompt +type ComfyStdPromptMapping = Record + +type ComfyStdPromptSpec = { + paramMapping: ComfyStdPromptMapping, + extraParams?: Record, + converter?: ComfyPromptConverter, +} + +const ALL_SPECS: Record = { + "KSampler": { + paramMapping: { + cfg: "k_sampler.cfg_scale", + seed: "k_sampler.seed", + steps: "k_sampler.steps", + sampler_name: "k_sampler.sampler_name", + scheduler: "k_sampler.scheduler", + denoise: "k_sampler.denoise", + }, + }, + "LoraLoader": { + paramMapping: { + lora_name: "lora.model_name", + strength_model: "lora.strength_unet", + strength_clip: "lora.strength_tenc", + }, + extraParams: { + "lora.module_name": "LoRA", + }, + converter: LoraLoader, + } } const COMMIT_HASH: string = __GIT_COMMIT_HASH__; export default class ComfyBoxStdPromptSerializer { - serialize(prompt: SerializedPrompt): ComfyBoxStdPrompt { + serialize(prompt: SerializedPromptInputsAll, workflow?: SerializedAppState): [SafeParseReturnType, any] { const stdPrompt: ComfyBoxStdPrompt = { version: 1, metadata: { @@ -33,23 +90,57 @@ export default class ComfyBoxStdPromptSerializer { commit_hash: COMMIT_HASH, extra_data: { comfybox: { + workflows: [] // TODO!!! } } }, parameters: {} } - for (const [nodeID, inputs] of Object.entries(prompt.output)) { + for (const [nodeID, inputs] of Object.entries(prompt)) { const classType = inputs.class_type - const converter = ALL_CONVERTERS[classType] - if (converter) { - converter(stdPrompt, inputs.inputs, nodeID) + const spec = ALL_SPECS[classType] + if (spec) { + console.warn("SPEC", spec, inputs) + let targets = {} + for (const [comfyKey, stdPromptKey] of Object.entries(spec.paramMapping)) { + const inputValue = inputs.inputs[comfyKey]; + if (inputValue != null && !isSerializedPromptInputLink(inputValue)) { + console.warn("GET", comfyKey, inputValue) + const trail = stdPromptKey.split("."); + let target = null; + + console.warn(trail, trail.length - 2); + for (let index = 0; index < trail.length - 1; index++) { + const name = trail[index]; + if (index === 0) { + targets[name] ||= {} + target = targets[name] + } + else { + target = target[name] + } + console.warn(index, name, target) + } + + let name = trail[trail.length - 1] + target[name] = inputValue + console.warn(stdPrompt.parameters) + } + } + + // TODO converter.encode + + for (const [groupName, group] of Object.entries(targets)) { + stdPrompt.parameters[groupName] ||= [] + stdPrompt.parameters[groupName].push(group) + } } else { - console.warn("No StdPrompt type converter for comfy class!", classType) + console.warn("No StdPrompt type spec for comfy class!", classType) } } - return stdPrompt + return [StdPrompt.safeParse(stdPrompt), stdPrompt]; } } diff --git a/src/lib/components/ComfyApp.ts b/src/lib/components/ComfyApp.ts index 97abc08..a711f05 100644 --- a/src/lib/components/ComfyApp.ts +++ b/src/lib/components/ComfyApp.ts @@ -1064,9 +1064,6 @@ export default class ComfyApp { // console.debug(graphToGraphVis(workflow.graph)) // console.debug(promptToGraphVis(p)) - const stdPrompt = this.stdPromptSerializer.serialize(p); - // console.warn("STD", stdPrompt); - const extraData: ComfyBoxPromptExtraData = { extra_pnginfo: { comfyBoxWorkflow: wf, diff --git a/src/lib/components/ComfyQueue.svelte b/src/lib/components/ComfyQueue.svelte index 31719e2..a34af04 100644 --- a/src/lib/components/ComfyQueue.svelte +++ b/src/lib/components/ComfyQueue.svelte @@ -32,6 +32,7 @@ import ComfyQueueGridDisplay from "./ComfyQueueGridDisplay.svelte"; import { WORKFLOWS_VIEW } from "./ComfyBoxWorkflowsView.svelte"; import uiQueueState from "$lib/stores/uiQueueState"; + import type { SerializedAppState, SerializedPromptInputsAll } from "./ComfyApp"; export let app: ComfyApp; @@ -124,19 +125,22 @@ let showModal = false; let expandAll = false; - let selectedPrompt = null; + let selectedPrompt: SerializedPromptInputsAll | null = null; + let selectedWorkflow: SerializedAppState | null = null; let selectedImages = []; function showPrompt(entry: QueueUIEntry) { if (entry.error != null) { showModal = false; expandAll = false; selectedPrompt = null; + selectedWorkflow = null; selectedImages = []; showError(entry.entry.promptID); } else { - selectedPrompt = entry.entry.prompt; + selectedPrompt = entry.entry.prompt, + selectedWorkflow = entry.entry.extraData.extra_pnginfo.comfyBoxWorkflow selectedImages = entry.images; showModal = true; expandAll = false @@ -145,6 +149,7 @@ function closeModal() { selectedPrompt = null + selectedWorkflow = null; selectedImages = [] showModal = false; expandAll = false; @@ -165,7 +170,7 @@ {#if selectedPrompt} - { closeModal(); closeDialog(); }} {app} prompt={selectedPrompt} images={selectedImages} {expandAll} /> + { closeModal(); closeDialog(); }} {app} prompt={selectedPrompt} workflow={selectedWorkflow} images={selectedImages} {expandAll} /> {/if}
diff --git a/src/lib/components/ComfySettingsView.svelte b/src/lib/components/ComfySettingsView.svelte index 15d3dcd..f2643a1 100644 --- a/src/lib/components/ComfySettingsView.svelte +++ b/src/lib/components/ComfySettingsView.svelte @@ -173,7 +173,7 @@ } .comfy-settings-entries { - padding: 3rem 3rem; + padding: 2rem 0.75rem; height: 100%; } diff --git a/src/lib/components/PromptDisplay.svelte b/src/lib/components/PromptDisplay.svelte index b96e2c7..88cc5d8 100644 --- a/src/lib/components/PromptDisplay.svelte +++ b/src/lib/components/PromptDisplay.svelte @@ -1,6 +1,6 @@