using System.Diagnostics.CodeAnalysis; using StabilityMatrix.Core.Models.Api.Comfy.NodeTypes; namespace StabilityMatrix.Core.Models.Api.Comfy.Nodes; /// /// Builder functions for comfy nodes /// [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] public class ComfyNodeBuilder { private readonly NodeDictionary nodes; public ComfyNodeBuilder(NodeDictionary nodes) { this.nodes = nodes; } private static string GetRandomPrefix() => Guid.NewGuid().ToString()[..8]; public static NamedComfyNode VAEEncode( string name, ImageNodeConnection pixels, VAENodeConnection vae ) { return new NamedComfyNode(name) { ClassType = "VAEEncode", Inputs = new Dictionary { ["pixels"] = pixels.Data, ["vae"] = vae.Data } }; } public static NamedComfyNode VAEDecode( string name, LatentNodeConnection samples, VAENodeConnection vae ) { return new NamedComfyNode(name) { ClassType = "VAEDecode", Inputs = new Dictionary { ["samples"] = samples.Data, ["vae"] = vae.Data } }; } public static NamedComfyNode KSampler( string name, ModelNodeConnection model, ulong seed, int steps, double cfg, string samplerName, string scheduler, ConditioningNodeConnection positive, ConditioningNodeConnection negative, LatentNodeConnection latentImage, double denoise ) { return new NamedComfyNode(name) { ClassType = "KSampler", Inputs = new Dictionary { ["model"] = model.Data, ["seed"] = seed, ["steps"] = steps, ["cfg"] = cfg, ["sampler_name"] = samplerName, ["scheduler"] = scheduler, ["positive"] = positive.Data, ["negative"] = negative.Data, ["latent_image"] = latentImage.Data, ["denoise"] = denoise } }; } public static NamedComfyNode ImageUpscaleWithModel( string name, UpscaleModelNodeConnection upscaleModel, ImageNodeConnection image ) { return new NamedComfyNode(name) { ClassType = "ImageUpscaleWithModel", Inputs = new Dictionary { ["upscale_model"] = upscaleModel.Data, ["image"] = image.Data } }; } public static NamedComfyNode UpscaleModelLoader( string name, string modelName) { return new NamedComfyNode(name) { ClassType = "UpscaleModelLoader", Inputs = new Dictionary { ["model_name"] = modelName } }; } public static NamedComfyNode ImageScale( string name, ImageNodeConnection image, string method, int height, int width, bool crop) { return new NamedComfyNode(name) { ClassType = "ImageScale", Inputs = new Dictionary { ["image"] = image.Data, ["upscale_method"] = method, ["height"] = height, ["width"] = width, ["crop"] = crop ? "center" : "disabled" } }; } public static NamedComfyNode VAELoader( string name, string vaeModelName) { return new NamedComfyNode(name) { ClassType = "VAELoader", Inputs = new Dictionary { ["vae_name"] = vaeModelName } }; } public ImageNodeConnection Lambda_LatentToImage(LatentNodeConnection latent, VAENodeConnection vae) { return nodes.AddNamedNode(VAEDecode($"{GetRandomPrefix()}_VAEDecode", latent, vae)).Output; } public LatentNodeConnection Lambda_ImageToLatent(ImageNodeConnection pixels, VAENodeConnection vae) { return nodes.AddNamedNode(VAEEncode($"{GetRandomPrefix()}_VAEEncode", pixels, vae)).Output; } /// /// Create a group node that upscales a given image with a given model /// public NamedComfyNode Group_UpscaleWithModel(string name, string modelName, ImageNodeConnection image) { var modelLoader = nodes.AddNamedNode( UpscaleModelLoader($"{name}_UpscaleModelLoader", modelName)); var upscaler = nodes.AddNamedNode( ImageUpscaleWithModel($"{name}_ImageUpscaleWithModel", modelLoader.Output, image)); return upscaler; } /// /// Create a group node that scales a given image to a given size /// public NamedComfyNode Group_UpscaleToLatent(string name, LatentNodeConnection latent, VAENodeConnection vae, ComfyUpscaler upscaleInfo, int width, int height) { if (upscaleInfo.Type == ComfyUpscalerType.Latent) { return nodes .AddNamedNode( new NamedComfyNode($"{name}_LatentUpscale") { ClassType = "LatentUpscale", Inputs = new Dictionary { ["upscale_method"] = upscaleInfo.Name, ["width"] = width, ["height"] = height, ["crop"] = "disabled", ["samples"] = latent.Data, } } ); } if (upscaleInfo.Type == ComfyUpscalerType.ESRGAN) { // Convert to image space var samplerImage = nodes.AddNamedNode( VAEDecode( $"{name}_VAEDecode", latent, vae ) ); // Do group upscale var modelUpscaler = Group_UpscaleWithModel( $"{name}_ModelUpscale", upscaleInfo.Name, samplerImage.Output ); // Since the model upscale is fixed to model (2x/4x), scale it again to the requested size var resizedScaled = nodes.AddNamedNode( ImageScale( $"{name}_ImageScale", modelUpscaler.Output, "bilinear", height, width, false ) ); // Convert back to latent space return nodes .AddNamedNode( VAEEncode( $"{name}_VAEEncode", resizedScaled.Output, vae ) ); } throw new InvalidOperationException($"Unknown upscaler type: {upscaleInfo.Type}"); } /// /// Create a group node that scales a given image to image output /// public NamedComfyNode Group_UpscaleToImage(string name, LatentNodeConnection latent, VAENodeConnection vae, ComfyUpscaler upscaleInfo, int width, int height) { if (upscaleInfo.Type == ComfyUpscalerType.Latent) { var latentUpscale = nodes .AddNamedNode( new NamedComfyNode($"{name}_LatentUpscale") { ClassType = "LatentUpscale", Inputs = new Dictionary { ["upscale_method"] = upscaleInfo.Name, ["width"] = width, ["height"] = height, ["crop"] = "disabled", ["samples"] = latent.Data, } } ); // Convert to image space return nodes.AddNamedNode( VAEDecode( $"{name}_VAEDecode", latentUpscale.Output, vae ) ); } if (upscaleInfo.Type == ComfyUpscalerType.ESRGAN) { // Convert to image space var samplerImage = nodes.AddNamedNode( VAEDecode( $"{name}_VAEDecode", latent, vae ) ); // Do group upscale var modelUpscaler = Group_UpscaleWithModel( $"{name}_ModelUpscale", upscaleInfo.Name, samplerImage.Output ); // Since the model upscale is fixed to model (2x/4x), scale it again to the requested size var resizedScaled = nodes.AddNamedNode( ImageScale( $"{name}_ImageScale", modelUpscaler.Output, "bilinear", height, width, false ) ); // No need to convert back to latent space return resizedScaled; } throw new InvalidOperationException($"Unknown upscaler type: {upscaleInfo.Type}"); } }