- Added provider settings and reference images support to image inference - Created Runware Bytedance Provider Settings node with cameraFixed and maxSequentialImages - Updated image inference to handle provider settings and reference images - Added debug logging for request/response tracking - Increased timeout from 90 to 180 seconds - Fixed parameter handling for conditional inclusion
325 lines
18 KiB
Python
325 lines
18 KiB
Python
from .utils import runwareUtils as rwUtils
|
|
import json
|
|
|
|
|
|
class txt2img:
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"Model": ("RUNWAREMODEL", {
|
|
"tooltip": "Connect a Runware Model From Runware Model Node.",
|
|
}),
|
|
"positivePrompt": ("STRING", {
|
|
"multiline": True,
|
|
"placeholder": "Positive Prompt: a text instruction to guide the model on generating the image. It is usually a sentence or a paragraph that provides positive guidance for the task. This parameter is essential to shape the desired results.\n\nYou Can Press (Ctrl + Alt + E) To Enhance The Prompt!",
|
|
"tooltip": "Positive Prompt: a text instruction to guide the model on generating the image. You Can Also Press (Ctrl + Alt + E) To Enhance The Prompt!"
|
|
}),
|
|
"negativePrompt": ("STRING", {
|
|
"multiline": True,
|
|
"placeholder": "Negative Prompt: a text instruction to guide the model on generating the image. It is usually a sentence or a paragraph that provides negative guidance for the task. This parameter helps to avoid certain undesired results.",
|
|
"tooltip": "Negative Prompt: a text instruction to guide the model on generating the image."
|
|
}),
|
|
"Multi Inference Mode": ("BOOLEAN", {
|
|
"tooltip": "If Enabled the node will skip the image generation process and will only return the Runware Task Object to be used in the Multi Inference Node.",
|
|
"default": False,
|
|
"label_on": "Enabled",
|
|
"label_off": "Disabled",
|
|
}),
|
|
"Prompt Weighting": (["Disabled", "sdEmbeds", "Compel"], {
|
|
"default": "Disabled",
|
|
"tooltip": "Prompt weighting allows you to adjust how strongly different parts of your prompt influence the generated image.\n\nChoose between \"compel\" notation with advanced weighting operations or \"sdEmbeds\" for simple emphasis adjustments.\n\nCompel Example: \"small+ dog, pixar style\"\n\nsdEmbeds Example: \"(small:2.5) dog, pixar style\"",
|
|
}),
|
|
"dimensions": ([
|
|
"Square (512x512)", "Square HD (1024x1024)", "Portrait 3:4 (768x1024)",
|
|
"Portrait 9:16 (576x1024)", "Landscape 4:3 (1024x768)",
|
|
"Landscape 16:9 (1024x576)", "Custom"
|
|
], {
|
|
"default": "Square (512x512)",
|
|
"tooltip": "Adjust the dimensions of the generated image by specifying its width and height in pixels, or select from the predefined options. Image dimensions must be multiples of 64 (e.g., 512x512, 1024x768).",
|
|
}),
|
|
"width": ("INT", {
|
|
"tooltip": "The Width of the image in pixels.",
|
|
"default": 512,
|
|
"min": 128,
|
|
"max": 2048,
|
|
"step": 64,
|
|
}),
|
|
"height": ("INT", {
|
|
"tooltip": "The Height of the image in pixels.",
|
|
"default": 512,
|
|
"min": 128,
|
|
"max": 2048,
|
|
"step": 64,
|
|
}),
|
|
"useSteps": ("BOOLEAN", {
|
|
"tooltip": "Enable to include steps parameter in API request. Disable if your model doesn't support steps (like nano banana).",
|
|
"default": True,
|
|
}),
|
|
"steps": ("INT", {
|
|
"tooltip": "The number of steps is the number of iterations the model will perform to generate the image. Only used when 'Use Steps' is enabled.",
|
|
"default": 4,
|
|
"min": 1,
|
|
"max": 100,
|
|
}),
|
|
"useScheduler": ("BOOLEAN", {
|
|
"tooltip": "Enable to include scheduler parameter in API request. Disable if your model doesn't support scheduler.",
|
|
"default": True,
|
|
}),
|
|
"scheduler": (['Default', 'DDIM', 'DDIMScheduler', 'DDPMScheduler', 'DEISMultistepScheduler', 'DPMSolverSinglestepScheduler', 'DPMSolverMultistepScheduler', 'DPMSolverMultistepInverse', 'DPM++', 'DPM++ Karras', 'DPM++ 2M', 'DPM++ 2M Karras', 'DPM++ 2M SDE Karras', 'DPM++ 2M SDE', 'DPM++ 3M', 'DPM++ 3M Karras', 'DPM++ SDE Karras', 'DPM++ SDE', 'EDMEulerScheduler', 'EDMDPMSolverMultistepScheduler', 'Euler', 'EulerDiscreteScheduler', 'Euler Karras', 'Euler a', 'EulerAncestralDiscreteScheduler', 'FlowMatchEulerDiscreteScheduler', 'Heun', 'HeunDiscreteScheduler', 'Heun Karras', 'IPNDMScheduler', 'KDPM2DiscreteScheduler', 'KDPM2AncestralDiscreteScheduler', 'LCM', 'LCMScheduler', 'LMS', 'LMSDiscreteScheduler', 'LMS Karras', 'PNDMScheduler', 'TCDScheduler', 'UniPC', 'UniPCMultistepScheduler', 'UniPC Karras', 'UniPC 2M', 'UniPC 2M Karras', 'UniPC 3M', 'UniPC 3M Karras'], {
|
|
"tooltip": "An scheduler is a component that manages the inference process. Different schedulers can be used to achieve different results like more detailed images, faster inference, or more accurate results.",
|
|
"default": "Default",
|
|
}),
|
|
"useCFGScale": ("BOOLEAN", {
|
|
"tooltip": "Enable to include CFG scale parameter in API request. Disable if your model doesn't support CFG scale (like nano banana).",
|
|
"default": True,
|
|
}),
|
|
"cfgScale": ("FLOAT", {
|
|
"tooltip": "Guidance scale represents how closely the images will resemble the prompt or how much freedom the AI model has. Only used when 'Use CFG Scale' is enabled.",
|
|
"default": 6.5,
|
|
"min": 1.0,
|
|
"max": 50.0,
|
|
"step": 0.5,
|
|
}),
|
|
"useSeed": ("BOOLEAN", {
|
|
"tooltip": "Enable to include seed parameter in API request. Disable if your model doesn't support seed.",
|
|
"default": True,
|
|
}),
|
|
"seed": ("INT", {
|
|
"tooltip": "A value used to randomize the image generation. If you want to make images reproducible (generate the same image multiple times), you can use the same seed value.",
|
|
"default": rwUtils.genRandSeed(),
|
|
"min": 1,
|
|
"max": 9223372036854776000,
|
|
}),
|
|
"clipSkip": ("INT", {
|
|
"tooltip": "Enables skipping layers of the CLIP embedding process, leading to quicker and more varied image generation.",
|
|
"default": 0,
|
|
"min": 0,
|
|
"max": 2,
|
|
}),
|
|
"strength": ("FLOAT", {
|
|
"tooltip": "When doing Image-to-Image or Inpainting, this parameter is used to determine the influence of the seedImage image in the generated output. A lower value results in more influence from the original image, while a higher value allows more creative deviation.",
|
|
"default": 0.8,
|
|
"min": 0.0,
|
|
"max": 1.0,
|
|
"step": 0.01,
|
|
}),
|
|
"Mask Margin": ("BOOLEAN", {
|
|
"tooltip": "Enables Or Disables Mask Margin Feature.",
|
|
"default": False,
|
|
"label_on": "Enabled",
|
|
"label_off": "Disabled",
|
|
}),
|
|
"maskMargin": ("INT", {
|
|
"tooltip": "Adds extra context pixels around the masked region during inpainting. When this parameter is present, the model will zoom into the masked area, considering these additional pixels to create more coherent and well-integrated details.",
|
|
"default": 32,
|
|
"min": 32,
|
|
"max": 128,
|
|
}),
|
|
"batchSize": ("INT", {
|
|
"tooltip": "The number of images to generate in a single request.",
|
|
"default": 1,
|
|
"min": 1,
|
|
"max": 10,
|
|
}),
|
|
},
|
|
"optional": {
|
|
"Accelerator": ("RUNWAREACCELERATOR", {
|
|
"tooltip": "Connect a Runware Accelerator Node (TeaCache, DeepCache) to speed up the image generation process.",
|
|
}),
|
|
"Lora": ("RUNWARELORA", {
|
|
"tooltip": "Connect a Runware Lora From Lora Search Node Or Lora Combine For Multiple Lora's Together.",
|
|
}),
|
|
"Outpainting": ("RUNWAREOUTPAINT", {
|
|
"tooltip": "Connect a Runware Outpainting Node to extend the image boundaries in different directions.",
|
|
}),
|
|
"IPAdapters": ("RUNWAREIPAdapter", {
|
|
"tooltip": "Connect a Runware IP Adapter node or IP Adapter Combine node to use reference images for guiding the generation.",
|
|
}),
|
|
"ControlNet": ("RUNWARECONTROLNET", {
|
|
"tooltip": "Connect a Runware ControlNet Guidance Node to help the model generate images that align with the desired structure.",
|
|
}),
|
|
"Refiner": ("RUNWAREREFINER", {
|
|
"tooltip": "Connect a Runware Refiner Node to help create higher quality image outputs by incorporating specialized models designed to enhance image details and overall coherence.",
|
|
}),
|
|
"seedImage": ("IMAGE", {
|
|
"tooltip": "Specifies the seed image to be used for the diffusion process, when doing Image-to-Image, Inpainting or Outpainting, this parameter is required.",
|
|
}),
|
|
"maskImage": ("MASK", {
|
|
"tooltip": "Specifies the mask image to be used for the inpainting process, when doing Inpainting, this parameter is required.",
|
|
}),
|
|
"Embeddings": ("RUNWAREEMBEDDING", {
|
|
"tooltip": "Connect a Runware Embedding Node to help the model generate images that align with the desired structure.",
|
|
}),
|
|
"VAE": ("RUNWAREVAE", {
|
|
"tooltip": "Connect a Runware VAE Node to help the model generate images that align with the desired structure.",
|
|
}),
|
|
"referenceImages": ("RUNWAREREFERENCEIMAGES", {
|
|
"tooltip": "Connect a Runware Reference Images Node to provide visual guidance for image generation.",
|
|
}),
|
|
"providerSettings": ("RUNWAREPROVIDERSETTINGS", {
|
|
"tooltip": "Connect a Runware Provider Settings Node to configure provider-specific parameters.",
|
|
}),
|
|
}
|
|
}
|
|
|
|
@classmethod
|
|
def VALIDATE_INPUTS(cls, positivePrompt, negativePrompt):
|
|
if (positivePrompt is not None and (positivePrompt == "" or len(positivePrompt) < 3 or len(positivePrompt) > 2000)):
|
|
raise Exception(
|
|
"Positive Prompt Must Be Between 3 And 2000 characters!")
|
|
if (negativePrompt is not None and negativePrompt != "" and (len(negativePrompt) < 3 or len(negativePrompt) > 2000)):
|
|
raise Exception(
|
|
"Negative Prompt Must Be Between 3 And 2000 characters!")
|
|
return True
|
|
|
|
DESCRIPTION = "Generates Images Lightning Fast With Runware Image Inference Sonic Engine."
|
|
FUNCTION = "generateImage"
|
|
RETURN_TYPES = ("IMAGE", "RUNWARETASK")
|
|
RETURN_NAMES = ("IMAGE", "RW-Task")
|
|
CATEGORY = "Runware"
|
|
|
|
def generateImage(self, **kwargs):
|
|
runwareModel = kwargs.get("Model")
|
|
positivePrompt = kwargs.get("positivePrompt")
|
|
negativePrompt = kwargs.get("negativePrompt", None)
|
|
multiInferenceMode = kwargs.get("Multi Inference Mode", False)
|
|
promptWeighting = kwargs.get("Prompt Weighting", "Disabled")
|
|
runwareControlNet = kwargs.get("ControlNet", None)
|
|
runwareAccelerator = kwargs.get("Accelerator", None)
|
|
runwareLora = kwargs.get("Lora", None)
|
|
runwareOutpainting = kwargs.get("Outpainting", None)
|
|
runwareIPAdapters = kwargs.get("IPAdapters", None)
|
|
runwareRefiner = kwargs.get("Refiner", None)
|
|
runwareEmbedding = kwargs.get("Embeddings", None)
|
|
runwareVAE = kwargs.get("VAE", None)
|
|
referenceImages = kwargs.get("referenceImages", None)
|
|
providerSettings = kwargs.get("providerSettings", None)
|
|
seedImage = kwargs.get("seedImage", None)
|
|
seedImageStrength = kwargs.get("strength", 0.8)
|
|
maskImage = kwargs.get("maskImage", None)
|
|
enableMaskMargin = kwargs.get("Mask Margin", False)
|
|
maskImageMargin = kwargs.get("maskMargin", 32)
|
|
clipSkip = kwargs.get("clipSkip", 0)
|
|
height = kwargs.get("height", 512)
|
|
width = kwargs.get("width", 512)
|
|
steps = kwargs.get("steps", 4)
|
|
useSteps = kwargs.get("useSteps", True)
|
|
scheduler = kwargs.get("scheduler", "Default")
|
|
useScheduler = kwargs.get("useScheduler", True)
|
|
cfgScale = kwargs.get("cfgScale", 6.5)
|
|
useCFGScale = kwargs.get("useCFGScale", True)
|
|
seed = kwargs.get("seed")
|
|
useSeed = kwargs.get("useSeed", True)
|
|
batchSize = kwargs.get("batchSize", 1)
|
|
|
|
if (maskImage is not None and seedImage is None):
|
|
raise Exception("Mask Image Requires Seed Image To Be Provided!")
|
|
|
|
genConfig = [
|
|
{
|
|
"taskType": "imageInference",
|
|
"taskUUID": rwUtils.genRandUUID(),
|
|
"positivePrompt": positivePrompt,
|
|
"height": height,
|
|
"width": width,
|
|
"model": runwareModel,
|
|
"clipSkip": clipSkip,
|
|
"outputType": "base64Data",
|
|
"outputFormat": rwUtils.OUTPUT_FORMAT,
|
|
"outputQuality": rwUtils.OUTPUT_QUALITY,
|
|
"numberResults": batchSize,
|
|
}
|
|
]
|
|
|
|
# For Debugging Purposes Only
|
|
print(f"[Debugging] Task UUID: {genConfig[0]['taskUUID']}")
|
|
|
|
# Add steps, CFGScale, seed, and scheduler only if enabled
|
|
if useSteps:
|
|
genConfig[0]["steps"] = steps
|
|
if useCFGScale:
|
|
genConfig[0]["CFGScale"] = cfgScale
|
|
if useSeed:
|
|
genConfig[0]["seed"] = seed
|
|
if useScheduler:
|
|
genConfig[0]["scheduler"] = scheduler
|
|
|
|
if (negativePrompt is not None and negativePrompt != ""):
|
|
genConfig[0]["negativePrompt"] = negativePrompt
|
|
if (promptWeighting != "Disabled"):
|
|
if (promptWeighting == "sdEmbeds"):
|
|
genConfig[0]["promptWeighting"] = "sdEmbeds"
|
|
else:
|
|
genConfig[0]["promptWeighting"] = "compel"
|
|
if(runwareAccelerator is not None):
|
|
genConfig[0]["acceleratorOptions"] = runwareAccelerator
|
|
if (runwareLora is not None):
|
|
if (isinstance(runwareLora, list)):
|
|
genConfig[0]["lora"] = runwareLora
|
|
elif (isinstance(runwareLora, dict)):
|
|
genConfig[0]["lora"] = [runwareLora]
|
|
if (runwareIPAdapters is not None):
|
|
if (isinstance(runwareIPAdapters, list)):
|
|
genConfig[0]["ipAdapters"] = runwareIPAdapters
|
|
elif (isinstance(runwareIPAdapters, dict)):
|
|
genConfig[0]["ipAdapters"] = [runwareIPAdapters]
|
|
if (runwareEmbedding is not None):
|
|
if (isinstance(runwareEmbedding, list)):
|
|
genConfig[0]["embeddings"] = runwareEmbedding
|
|
elif (isinstance(runwareEmbedding, dict)):
|
|
genConfig[0]["embeddings"] = [runwareEmbedding]
|
|
if (runwareOutpainting is not None):
|
|
genConfig[0]["outpaint"] = runwareOutpainting
|
|
if (runwareVAE is not None):
|
|
genConfig[0]["vae"] = runwareVAE
|
|
if (runwareControlNet is not None):
|
|
genConfig[0]["controlNet"] = runwareControlNet
|
|
if (runwareRefiner is not None):
|
|
genConfig[0]["refiner"] = runwareRefiner
|
|
if (seedImage is not None):
|
|
seedImage = rwUtils.convertTensor2IMG(seedImage)
|
|
genConfig[0]["seedImage"] = seedImage
|
|
genConfig[0]["strength"] = seedImageStrength
|
|
if (maskImage is not None):
|
|
maskImage = rwUtils.convertTensor2IMG(maskImage)
|
|
genConfig[0]["maskImage"] = maskImage
|
|
if (enableMaskMargin):
|
|
genConfig[0]["maskMargin"] = maskImageMargin
|
|
|
|
# Handle referenceImages
|
|
if referenceImages is not None:
|
|
genConfig[0]["referenceImages"] = referenceImages
|
|
|
|
# Handle providerSettings - extract provider name from model and merge with custom settings
|
|
if providerSettings is not None:
|
|
# Extract provider name from model (e.g., "bytedance:1@1" -> "bytedance")
|
|
provider_name = runwareModel.split(":")[0] if ":" in runwareModel else runwareModel
|
|
|
|
# If providerSettings is a dictionary, create the correct API format
|
|
if isinstance(providerSettings, dict):
|
|
# Create the providerSettings object with provider name as key
|
|
final_provider_settings = {
|
|
provider_name: providerSettings
|
|
}
|
|
genConfig[0]["providerSettings"] = final_provider_settings
|
|
else:
|
|
# If it's already in the correct format, use it directly
|
|
genConfig[0]["providerSettings"] = providerSettings
|
|
|
|
if (multiInferenceMode):
|
|
return (None, genConfig)
|
|
else:
|
|
# Debug: Print the request being sent
|
|
print(f"[DEBUG] Sending Image Inference Request:")
|
|
print(f"[DEBUG] Request Payload: {json.dumps(genConfig, indent=2)}")
|
|
|
|
genResult = rwUtils.inferenecRequest(genConfig)
|
|
|
|
# Debug: Print the response received
|
|
print(f"[DEBUG] Received Image Inference Response:")
|
|
print(f"[DEBUG] Response: {json.dumps(genResult, indent=2)}")
|
|
|
|
images = rwUtils.convertImageB64List(genResult)
|
|
return (images, None)
|