import { z, type ZodTypeAny } from "zod" /* * This metadata can be attached to each entry in a group to assist in * identifying the correct nodes to apply it to. * * As an example, positive and negative conditioning are deployed as two * separate nodes in ComfyUI. This makes bundling them into a { positive, * negative } entry difficult as either one can be missing. So instead they're * tagged like * * { * conditioning: [ * { text: "masterpiece", "$meta": { types: ["positive"] } }, * { text: "worst quality", "$meta": { types: ["negative"] } }, * ] * } * * The reasoning is the "types" information isn't required to reinstantiate * the node, it's only semantic information describing how the node is used in * the encompassing workflow. When the prompt is loaded the workflow can be * searched for a node with the compatible type to attach the information to. */ const GroupMetadata = z.object({ types: z.array(z.string()).nonempty().optional() }) export type ComfyBoxStdGroupMetadata = z.infer const group = (obj: Record): ZodTypeAny => { const meta = z.object({ "$meta": GroupMetadata.optional() }) return z.object(obj).and(meta) } const ModelHashes = z.object({ a1111_shorthash: z.string().optional(), sha256: z.string().optional(), }).refine(({ a1111_shorthash, sha256 }) => a1111_shorthash !== undefined || sha256 !== undefined, { message: "At least one model hash must be specified" }) const GroupConditioning = group({ text: z.string(), }) export type ComfyBoxStdGroupConditioning = z.infer const GroupCheckpoint = group({ model_name: z.string().optional(), model_hashes: ModelHashes.optional(), }).refine(({ model_name, model_hashes }) => model_name !== undefined || model_hashes !== undefined, { message: "Must include either model name or model hash" } ) export type ComfyBoxStdGroupCheckpoint = z.infer const GroupVAE = group({ model_name: z.string().optional(), model_hashes: ModelHashes.optional(), type: z.enum(["internal", "external"]) }).refine(({ model_name, model_hashes }) => model_name !== undefined || model_hashes !== undefined, { message: "Must include either model name or model hashes" } ) export type ComfyBoxStdGroupVAE = z.infer const GroupKSampler = group({ cfg_scale: z.number(), seed: z.number(), steps: z.number(), sampler_name: z.string(), scheduler: z.string(), denoise: z.number().default(1.0), type: z.enum(["empty", "image", "upscale"]).optional() }) export type ComfyBoxStdGroupKSampler = z.infer const GroupLatentImage = group({ width: z.number(), height: z.number(), mask_blur: z.number().optional(), batch_count: z.number().default(1).optional(), batch_pos: z.number().default(0).optional() }) export type ComfyBoxStdGroupLatentImage = z.infer const GroupLatentUpscale = group({ width: z.number(), height: z.number(), upscale_method: z.string().optional(), upscale_by: z.number().optional(), crop: z.string().optional() }) export type ComfyBoxStdGroupLatentUpscale = z.infer const GroupSDUpscale = group({ upscaler: z.string(), overlap: z.number(), }) export type ComfyBoxStdGroupSDUpscale = z.infer const GroupSelfAttentionGuidance = group({ guidance_scale: z.number(), mask_threshold: z.number(), }) export type ComfyBoxStdGroupSelfAttentionGuidance = z.infer const GroupHypernetwork = group({ model_name: z.string(), model_hashes: ModelHashes.optional(), strength: z.number() }) export type ComfyBoxStdGroupHypernetwork = z.infer const LoRAModelHashes = z.object({ addnet_shorthash: z.string().optional(), addnet_shorthash_legacy: z.string().optional(), sha256: z.string().optional(), }).refine(({ addnet_shorthash, addnet_shorthash_legacy, sha256 }) => addnet_shorthash !== undefined || addnet_shorthash_legacy !== undefined || sha256 !== undefined, { message: "At least one model hash must be specified" }) const GroupLoRA = group({ model_name: z.string(), module_name: z.string().optional(), model_hashes: LoRAModelHashes.optional(), strength_unet: z.number(), strength_tenc: z.number() }) export type ComfyBoxStdGroupLoRA = z.infer const GroupControlNet = group({ model: z.string(), model_hashes: ModelHashes.optional(), strength: z.number(), }) export type ComfyBoxStdGroupControlNet = z.infer const GroupCLIP = group({ clip_skip: z.number().optional() }) export type ComfyBoxStdGroupCLIP = z.infer const GroupDynamicThresholding = group({ mimic_scale: z.number(), threshold_percentile: z.number(), mimic_mode: z.string(), mimic_scale_minimum: z.number(), cfg_mode: z.string(), cfg_scale_minimum: z.number() }) export type ComfyBoxStdGroupDynamicThresholding = z.infer const GroupAestheticEmbedding = group({ model_name: z.string(), lr: z.number(), slerp: z.boolean(), slerp_angle: z.number().optional(), steps: z.number(), text: z.string(), text_negative: z.boolean(), weight: z.number(), }) export type ComfyBoxStdGroupAestheticEmbedding = z.infer const GroupDDetailer = group({ positive_prompt: z.string(), negative_prompt: z.string(), bitwise: z.string(), model: z.string().optional(), model_hashes: ModelHashes.optional(), conf: z.number(), mask_blur: z.number(), denoise: z.number(), dilation: z.number(), offset_x: z.number(), offset_y: z.number(), preprocess: z.boolean(), inpaint_full: z.boolean(), inpaint_padding: z.number(), cfg: z.number() }) export type ComfyBoxStdGroupDDetailer = z.infer const groupArray = (entry: ZodTypeAny) => { return z.optional(z.array(entry).nonempty()); } const Parameters = z.object({ conditioning: groupArray(GroupConditioning), checkpoint: groupArray(GroupCheckpoint), vae: groupArray(GroupVAE), k_sampler: groupArray(GroupKSampler), clip: groupArray(GroupCLIP), latent_image: groupArray(GroupLatentImage), latent_upscale: groupArray(GroupLatentUpscale), sd_upscale: groupArray(GroupSDUpscale), hypernetwork: groupArray(GroupHypernetwork), lora: groupArray(GroupLoRA), control_net: groupArray(GroupControlNet), dynamic_thresholding: groupArray(GroupDynamicThresholding), aesthetic_embedding: groupArray(GroupAestheticEmbedding), self_attention_guidance: groupArray(GroupSelfAttentionGuidance), ddetailer: groupArray(GroupDDetailer) }).partial() export type ComfyBoxStdParameters = z.infer const ComfyBoxExtraData = z.object({ workflows: z.array(z.string()) }) const A1111ExtraData = z.object({ params: z.any() }) const ExtraData = z.object({ comfybox: ComfyBoxExtraData.optional(), a1111: A1111ExtraData.optional() }) const Metadata = z.object({ created_with: z.string(), author: z.string().optional(), app_version: z.string().optional(), commit_hash: z.string().optional(), extra_data: ExtraData }) const ComfyBoxStdPrompt = z.object({ version: z.number(), metadata: Metadata, parameters: Parameters }) export default ComfyBoxStdPrompt /* * A standardized Stable Diffusion parameter format that should be used with an * encompassing workflow. Aims to encompass an arbitrary number of parameter * counts and types, so that most ComfyUI workflows can have parts of their * prompts transferred between each other. * * This format does *not* describe how the information should be used in the * underlying workflow, i.e. it does not specify the structure of a ComfyUI * execution graph. It only gives hints via tagged input types on each input * entry as to where the data should be inserted. To recreate a ComfyBox * workflow with the exact state of the UI intact, the `SerializedAppState` type * should be used instead. It suffices to embed data of that type in the output * PNGs for recreating their workflows. This type is meant as an interchange * format *between* workflows so their inputs can be copied to and from each * other in a sane-enough manner. (In ComfyBox, copying workflow outputs like * images to other workflows is handled separately, since this type does not * retain the actual image data.) * * In contrast with a serialized workflow, which is concerned with the * connections between nodes and the state of the frontend's UI, this format * concerns itself with the exact values that the execution backend receives, * after the data in the UI have finished processing. * * (Take for example a "scale by" slider that adjusts the width and height of an * img2img input image of 512 x 512 resolution by 2x. The backend will only * "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