Merge pull request #82 from Runware/fix-tiffNode/feature-outputNode

Create custom "Runware Save Image" node
This commit is contained in:
Sirshendu Ganguly
2026-02-02 14:21:15 -05:00
committed by GitHub
50 changed files with 203 additions and 119 deletions
+2
View File
@@ -71,6 +71,7 @@ from .modules.safetyInputs import safetyInputs
from .modules.settings import RunwareSettings
from .modules.videoAdvancedFeatureInputs import videoAdvancedFeatureInputs
from .modules.advancedFeatureSettings.wanAnimateAdvancedFeatureSettings import RunwareWanAnimateAdvancedFeatureSettings
from .modules.saveImage import RunwareSaveImage
from .modules.videoOutputs import RunwareVideoInferenceOutputs
from .modules.threeDInference import threeDInference
from .modules.threeDInferenceInputs import threeDInferenceInputs
@@ -160,6 +161,7 @@ NODE_CLASS_MAPPINGS = {
"Runware Settings": RunwareSettings,
"Runware Video Advanced Feature Inputs": videoAdvancedFeatureInputs,
"Runware Wan Animate Advanced Feature Settings": RunwareWanAnimateAdvancedFeatureSettings,
"Runware Save Image": RunwareSaveImage,
"Runware Video Inference Outputs": RunwareVideoInferenceOutputs,
"Runware 3D Inference": threeDInference,
"Runware 3D Inference Inputs": threeDInferenceInputs,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 706 KiB

+5
View File
@@ -121,6 +121,7 @@ const RUNWARE_NODE_TYPES = {
SETTINGS: "Runware Settings",
VIDEOADVANCEDFEATUREINPUTS: "Runware Video Advanced Feature Inputs",
WANANIMATEADVANCEDFEATURESETTINGS: "Runware Wan Animate Advanced Feature Settings",
SAVEIMAGE: "Runware Save Image",
VIDEOINFERENCEOUTPUTS: "Runware Video Inference Outputs",
THREEDINFERENCE: "Runware 3D Inference",
THREEDINFERENCEINPUTS: "Runware 3D Inference Inputs",
@@ -424,6 +425,10 @@ const RUNWARE_NODE_PROPS = {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.SAVEIMAGE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINFERENCEOUTPUTS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
+5 -4
View File
@@ -80,7 +80,8 @@ class bgremoval:
DESCRIPTION = "Remove backgrounds from images effortlessly using Runware's low-cost image editing Inference."
FUNCTION = "rembg"
RETURN_TYPES = ("IMAGE",)
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("IMAGE",)
CATEGORY = "Runware"
def rembg(self, **kwargs):
@@ -112,7 +113,7 @@ class bgremoval:
"model": modelAIR,
"outputFormat": outputFormat,
"outputQuality": rwUtils.OUTPUT_QUALITY,
"outputType": "base64Data",
"outputType": "URL",
}
if includeExtraSettings:
@@ -166,5 +167,5 @@ class bgremoval:
print(f"[DEBUG] Error in Background Removal Request: {str(e)}", flush=True)
raise
images = rwUtils.convertImageB64List(genResult)
return (images, )
images = rwUtils.extractImageURLs(genResult)
return (images,)
+4 -4
View File
@@ -76,7 +76,7 @@ class controlNetPreprocessor:
DESCRIPTION = "ControlNet offers advanced capabilities for precise image processing through the use of guide images in specific formats, known as preprocessed images. This powerful tool enhances the control and customization of image generation, enabling users to achieve desired artistic styles and detailed adjustments effectively."
FUNCTION = "controlNetPreProcess"
RETURN_TYPES = ("IMAGE",)
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("GUIDE IMAGE",)
CATEGORY = "Runware"
@@ -98,7 +98,7 @@ class controlNetPreprocessor:
"preProcessorType": preProcessorType,
"outputFormat": rwUtils.OUTPUT_FORMAT,
"outputQuality": rwUtils.OUTPUT_QUALITY,
"outputType": "base64Data",
"outputType": "URL",
}
]
@@ -115,5 +115,5 @@ class controlNetPreprocessor:
genConfig[0]["highThresholdCanny"] = cannyHighThreshold
genResult = rwUtils.inferenecRequest(genConfig)
images = rwUtils.convertImageB64List(genResult)
return (images, )
images = rwUtils.extractImageURLs(genResult)
return (images,)
+19 -10
View File
@@ -129,9 +129,9 @@ class txt2img:
"min": 32,
"max": 128,
}),
"outputFormat": (["WEBP", "PNG", "JPEG", "SVG", "TIFF"], {
"tooltip": "Choose the output image format.",
"default": "WEBP",
"outputFormat": (["WEBP", "PNG", "JPEG", "TIFF"], {
"tooltip": "Choose the output image format. This will be used for the API request. The actual saved format will match the format returned by the API (visible in imageURL).",
"default": rwUtils.getOutputFormat(),
}),
"batchSize": ("INT", {
"tooltip": "The number of images to generate in a single request.",
@@ -196,7 +196,7 @@ class txt2img:
DESCRIPTION = "Generates Images Lightning Fast With Runware Image Inference Sonic Engine."
FUNCTION = "generateImage"
RETURN_TYPES = ("IMAGE", "RUNWARETASK")
RETURN_TYPES = ("STRING", "RUNWARETASK")
RETURN_NAMES = ("IMAGE", "RW-Task")
CATEGORY = "Runware"
@@ -251,7 +251,7 @@ class txt2img:
useSeed = kwargs.get("useSeed", True)
useClipSkip = kwargs.get("useClipSkip", True)
dimensions = kwargs.get("dimensions", "Square (512x512)")
outputFormat = kwargs.get("outputFormat", "WEBP")
outputFormat = kwargs.get("outputFormat", rwUtils.getOutputFormat())
batchSize = kwargs.get("batchSize", 1)
acceleration = kwargs.get("acceleration", "none")
useResolution = kwargs.get("useResolution", False)
@@ -454,11 +454,20 @@ class txt2img:
status = image_data["status"]
if status == "success":
# Check for image data (imageURL, imageBase64Data, mediaURL)
if "imageURL" in image_data or "imageBase64Data" in image_data or "imageURL" in image_data:
# Convert and return images
images = rwUtils.convertImageB64List(pollResult)
return (images, None)
# Check for image data (imageURL, imageBase64Data)
if "imageURL" in image_data or "imageBase64Data" in image_data:
# Extract URLs from all results
image_urls = []
for result in pollResult.get("data", []):
imageURL = result.get("imageURL")
if imageURL:
image_urls.append(imageURL)
# Return URLs as comma-separated string (or single URL)
image_url_str = ",".join(image_urls) if image_urls else ""
return (image_url_str, None)
# If status is "processing", continue polling
+4 -4
View File
@@ -48,7 +48,7 @@ class imageMasking:
DESCRIPTION = "Image Masking provides intelligent detection and mask generation for specific elements in images, particularly optimized for faces, hands, and people. Built on advanced detection models, this feature enhances the inpainting workflow by automatically creating precise masks around detected elements, enabling targeted enhancement and detailing."
FUNCTION = "imageMasking"
RETURN_TYPES = ("IMAGE", "IMAGE", "MASK")
RETURN_TYPES = ("IMAGE", "STRING", "STRING")
RETURN_NAMES = ("Image", "Mask Preview", "Mask")
CATEGORY = "Runware"
@@ -87,7 +87,7 @@ class imageMasking:
"model": detectionModel,
"confidence": confidence,
"maxDetections": maxDetections,
"outputType": "base64Data",
"outputType": "URL",
"outputFormat": rwUtils.OUTPUT_FORMAT,
"outputQuality": rwUtils.OUTPUT_QUALITY,
}
@@ -97,5 +97,5 @@ class imageMasking:
genConfig[0]["maskBlur"] = maskBlur
genResult = rwUtils.inferenecRequest(genConfig)
images = rwUtils.convertImageB64List(genResult)
return (image, images[0], images[0])
image_url = rwUtils.extractImageURLs(genResult)
return (image, image_url, image_url)
+3 -3
View File
@@ -39,7 +39,7 @@ class multiInference:
DESCRIPTION = "Allows you to Run Multiple Inference Tasks in Parallel."
FUNCTION = "multiInference"
RETURN_TYPES = ("IMAGE", "IMAGE", "IMAGE", "IMAGE")
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING")
RETURN_NAMES = ("Result 1", "Result 2", "Result 3", "Result 4")
CATEGORY = "Runware"
@@ -60,8 +60,8 @@ class multiInference:
result = await loop.run_in_executor(
None, rwUtils.inferenecRequest, taskData
)
images = rwUtils.convertImageB64List(result)
return images
image_url = rwUtils.extractImageURLs(result)
return image_url
except Exception as e:
print(f"\n---- Runware Multi Inference Task {oindex + 1} Failed ----")
print(f"Task-Type: {taskData[0]['taskType']}")
+5 -4
View File
@@ -112,7 +112,8 @@ class photoMaker:
DESCRIPTION = "Transform and style images using PhotoMaker's advanced personalization technology. Create consistent, high-quality image variations with precise subject fidelity and style control."
FUNCTION = "photoMaker"
RETURN_TYPES = ("IMAGE",)
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("IMAGE",)
CATEGORY = "Runware"
@classmethod
@@ -171,7 +172,7 @@ class photoMaker:
"CFGScale": cfgScale,
"clipSkip": clipSkip,
"numberResults": batchSize,
"outputType": "base64Data",
"outputType": "URL",
"outputFormat": rwUtils.OUTPUT_FORMAT,
"outputQuality": rwUtils.OUTPUT_QUALITY,
}
@@ -186,5 +187,5 @@ class photoMaker:
genConfig[0]["promptWeighting"] = "compel"
genResult = rwUtils.inferenecRequest(genConfig)
images = rwUtils.convertImageB64List(genResult)
return (images, )
images = rwUtils.extractImageURLs(genResult)
return (images,)
+4 -5
View File
@@ -16,7 +16,7 @@ class RunwareSave3D:
"3dObject": ("STRING", {
"tooltip": "3D file URL from Runware 3D Inference node."
}),
"filename_prefix": ("STRING", {
"filenamePrefix": ("STRING", {
"default": "ComfyUI",
"tooltip": "Prefix for the filename."
}),
@@ -37,10 +37,9 @@ class RunwareSave3D:
CATEGORY = "Runware"
DESCRIPTION = "Save 3D model files (GLB/PLY) from Runware 3D Inference to the output folder."
def save_3d(self, **kwargs):
def save_3d(self, filenamePrefix="ComfyUI", **kwargs):
"""Save 3D file from URL"""
file_url = kwargs.get("3dObject", "")
filename_prefix = kwargs.get("filename_prefix", "ComfyUI")
if not file_url or not file_url.strip():
raise Exception("No 3D URL provided. Please connect the '3dObject' output from Runware 3D Inference node.")
@@ -59,9 +58,9 @@ class RunwareSave3D:
if extension not in ["glb", "ply"]:
raise Exception(f"Unsupported 3D file extension '{extension}' from URL. Expected .glb or .ply.")
# Sanitize filename_prefix to prevent path traversal or arbitrary subdir creation
# Sanitize filenamePrefix to prevent path traversal or arbitrary subdir creation
prefix_basename = os.path.basename(
filename_prefix.replace(os.sep, "_").replace(os.altsep or os.sep, "_")
filenamePrefix.replace(os.sep, "_").replace(os.altsep or os.sep, "_")
)
safe_prefix = re.sub(r"[^\w\-]", "", prefix_basename) or "ComfyUI"
+80
View File
@@ -0,0 +1,80 @@
from .utils import runwareUtils as rwUtils
import folder_paths
import os
import requests
from datetime import datetime
class RunwareSaveImage:
"""Runware Save Image node that respects format from imageURL"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Images": ("STRING", {
"tooltip": "Image URL(s) from Runware Image Inference node. For multiple images, URLs should be comma-separated."
}),
"filenamePrefix": ("STRING", {
"default": "ComfyUI",
"tooltip": "Prefix for the filename."
}),
"saveImage": ("BOOLEAN", {
"default": True,
"label_on": "Enabled",
"label_off": "Disabled",
"tooltip": "If true, save to output folder. If false, download and preview only (purged when ComfyUI restarts)."
}),
}
}
RETURN_TYPES = ()
FUNCTION = "save_images"
OUTPUT_NODE = True
CATEGORY = "Runware"
def save_images(self, Images, filenamePrefix="ComfyUI", saveImage=True):
"""Save images using URL directly"""
# Split URLs if comma-separated (for batch processing)
image_urls = [url.strip() for url in Images.split(",") if url.strip()]
if not image_urls:
raise Exception("No image added provided. Please connect the 'Image' from Runware Image Inference node.")
# Use output dir when saving, temp dir for preview only (purged on restart)
if saveImage:
output_dir = folder_paths.get_output_directory()
os.makedirs(output_dir, exist_ok=True)
file_type = "output"
else:
output_dir = folder_paths.get_temp_directory()
file_type = "temp"
saved_files = []
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
for i, image_url in enumerate(image_urls):
response = requests.get(image_url, timeout=30)
response.raise_for_status()
img_data = response.content
url_without_params = image_url.split('?')[0]
extension = "." + url_without_params.split('.')[-1]
if len(image_urls) > 1:
filename = f"{filenamePrefix}_{timestamp}_{i+1:03}.{extension.lstrip('.')}"
else:
filename = f"{filenamePrefix}_{timestamp}.{extension.lstrip('.')}"
filepath = os.path.join(output_dir, filename)
with open(filepath, 'wb') as f:
f.write(img_data)
print(f"[Runware] {'Saved' if saveImage else 'Preview'}: {filepath}")
saved_files.append({
"filename": filename,
"subfolder": "",
"type": file_type
})
return {"ui": {"images": saved_files}}
+18 -17
View File
@@ -127,7 +127,8 @@ class upscaler:
DESCRIPTION = "Enhance the resolution and quality of your images using Runware's advanced upscaling API. Transform low-resolution images into sharp, high-definition visuals."
FUNCTION = "upscale"
RETURN_TYPES = ("IMAGE",)
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("IMAGE",)
CATEGORY = "Runware"
def upscale(self, **kwargs):
@@ -168,28 +169,28 @@ class upscaler:
"upscaleFactor": int(upscaleFactor),
"outputFormat": rwUtils.OUTPUT_FORMAT,
"outputQuality": rwUtils.OUTPUT_QUALITY,
"outputType": "base64Data",
"outputType": "URL",
}
# Add model if specified
if model:
genConfig["model"] = model
# Add common parameters at top level - only add if their toggle is enabled
if useSteps:
genConfig["steps"] = steps
if useSeed:
genConfig["seed"] = seed
if useCFGScale:
genConfig["CFGScale"] = CFGScale
if usePrompts:
if positivePrompt:
genConfig["positivePrompt"] = positivePrompt
if negativePrompt:
genConfig["negativePrompt"] = negativePrompt
# Build settings object for advanced parameters
settings = {}
# Common parameters - only add if their toggle is enabled
if useSteps:
settings["steps"] = steps
if useSeed:
settings["seed"] = seed
if useCFGScale:
settings["CFGScale"] = CFGScale
if usePrompts:
if positivePrompt:
settings["positivePrompt"] = positivePrompt
if negativePrompt:
settings["negativePrompt"] = negativePrompt
# Clarity upscaler specific parameters
if useClarityParams:
@@ -249,5 +250,5 @@ class upscaler:
print(f"[DEBUG] Error in Image Upscale Request: {str(e)}", flush=True)
raise
images = rwUtils.convertImageB64List(genResult)
return (images, )
images = rwUtils.extractImageURLs(genResult)
return (images,)
+11 -2
View File
@@ -577,6 +577,15 @@ def convertIMG2Tensor(b64img):
tensorImage = torch.from_numpy(imageNP).squeeze()
return tensorImage
def extractImageURLs(imageDataObject):
"""Extract image URLs from API response and return as comma-separated string"""
image_urls = []
for result in imageDataObject.get("data", []):
imageURL = result.get("imageURL")
if imageURL:
image_urls.append(imageURL)
return ",".join(image_urls) if image_urls else ""
def convertImageB64List(imageDataObject):
images = ()
for result in imageDataObject["data"]:
@@ -591,8 +600,8 @@ def convertImageB64List(imageDataObject):
generatedImage = convertIMG2Tensor(generatedImage)
images += (generatedImage,)
else:
# If no base64 data, try to get image URL (check both imageURL and mediaURL)
imageURL = result.get("imageURL") or result.get("mediaURL")
# If no base64 data, try to get image URL
imageURL = result.get("imageURL")
if imageURL:
# Download image from URL and convert to tensor
try:
+6 -32
View File
@@ -1,14 +1,4 @@
from .utils import runwareUtils as rwUtils
import base64
import io
class SVGData:
"""Wrapper class to match RecraftIO.SVG format for SaveSVGNode"""
def __init__(self, svgContent):
svgBytes = io.BytesIO(svgContent.encode('utf-8'))
self.data = [svgBytes]
class vectorize:
@@ -35,12 +25,12 @@ class vectorize:
DESCRIPTION = "Convert images to vector format using Runware's vectorization service."
FUNCTION = "vectorizeImage"
RETURN_TYPES = ("SVG",)
RETURN_NAMES = ("SVG",)
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("IMAGE",)
CATEGORY = "Runware"
def vectorizeImage(self, **kwargs):
"""Vectorize image and return SVG data"""
"""Vectorize image and return SVG URL"""
image = kwargs.get("Image")
modelName = kwargs.get("Model", "recraft:1@1")
@@ -59,9 +49,9 @@ class vectorize:
self._validateResponse(genResult)
svgData = self._extractSvgData(genResult)
svg_url = rwUtils.extractImageURLs(genResult)
return (svgData,)
return (svg_url,)
def _buildGenConfig(self, model, imageUuid):
"""Build generation configuration for API request"""
@@ -72,7 +62,7 @@ class vectorize:
"inputs": {
"image": imageUuid
},
"outputType": "base64Data",
"outputType": "URL",
"outputFormat": "svg",
}]
@@ -81,22 +71,6 @@ class vectorize:
if "errors" in genResult:
errorMessage = genResult["errors"][0]["message"]
raise Exception(f"Vectorization failed: {errorMessage}")
if "data" not in genResult or len(genResult["data"]) == 0:
raise Exception("No data returned from vectorization API")
def _extractSvgData(self, genResult):
"""Extract SVG data from API response"""
resultData = genResult["data"][0]
if "imageBase64Data" not in resultData:
raise Exception("imageBase64Data not found in response")
base64Svg = resultData["imageBase64Data"]
svgContent = base64.b64decode(base64Svg).decode('utf-8')
svgData = SVGData(svgContent)
return svgData
# Node class mappings
+2 -1
View File
@@ -1,4 +1,5 @@
python-dotenv
brotli
zstd
librosa
librosa
imageio
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"id":"993f4b88-acb5-4f70-968f-e3a2c9c1c5b4","revision":0,"last_node_id":7,"last_link_id":8,"nodes":[{"id":6,"type":"SaveSVGNode","pos":[447.0441589355469,567.7747192382812],"size":[270,270],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"svg","name":"svg","type":"SVG","link":8},{"localized_name":"filename_prefix","name":"filename_prefix","type":"STRING","widget":{"name":"filename_prefix"},"link":null}],"outputs":[],"properties":{"cnr_id":"comfy-core","ver":"0.3.46","Node name for S&R":"SaveSVGNode"},"widgets_values":["svg/ComfyUI"]},{"id":4,"type":"LoadImage","pos":[-162.27017211914062,506.9244384765625],"size":[274.080078125,314.0000305175781],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"image","name":"image","type":"COMBO","widget":{"name":"image"},"link":null},{"localized_name":"choose file to upload","name":"upload","type":"IMAGEUPLOAD","widget":{"name":"upload"},"link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":[7]},{"localized_name":"MASK","name":"MASK","type":"MASK","links":null}],"properties":{"cnr_id":"comfy-core","ver":"0.3.46","Node name for S&R":"LoadImage"},"widgets_values":["bulldog_1.webp","image"]},{"id":7,"type":"Runware Vectorize","pos":[146.1571044921875,546.4293823242188],"size":[270,58],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"Image","name":"Image","type":"IMAGE","link":7},{"localized_name":"Model","name":"Model","type":"COMBO","widget":{"name":"Model"},"link":null}],"outputs":[{"localized_name":"SVG","name":"SVG","type":"SVG","links":[8]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"8d75a6933feda430268dd0c555ecde9ccd99c7ef","Node name for S&R":"Runware Vectorize"},"widgets_values":["recraft:1@1"],"bgcolor":"#5345bf"}],"links":[[7,4,0,7,0,"IMAGE"],[8,7,0,6,0,"SVG"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.02884607845781,"offset":[476.52544538804847,-345.82928772954745]}},"version":0.4}
{"id":"993f4b88-acb5-4f70-968f-e3a2c9c1c5b4","revision":0,"last_node_id":10,"last_link_id":11,"nodes":[{"id":9,"type":"Runware Save Image","pos":[452.51519775390625,576.1388549804688],"size":[270,82],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"Images","name":"Images","type":"STRING","widget":{"name":"Images"},"link":11},{"localized_name":"filename_prefix","name":"filename_prefix","type":"STRING","widget":{"name":"filename_prefix"},"link":null}],"outputs":[],"properties":{"cnr_id":"ComfyUI-Runware","ver":"91646ada844167e36188b535ff60063735d10ca3","Node name for S&R":"Runware Save Image"},"widgets_values":["","ComfyUI"],"bgcolor":"#5345bf"},{"id":4,"type":"LoadImage","pos":[-162.27017211914062,506.9244384765625],"size":[274.080078125,314.0000305175781],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"image","name":"image","type":"COMBO","widget":{"name":"image"},"link":null},{"localized_name":"choose file to upload","name":"upload","type":"IMAGEUPLOAD","widget":{"name":"upload"},"link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":[10]},{"localized_name":"MASK","name":"MASK","type":"MASK","links":null}],"properties":{"cnr_id":"comfy-core","ver":"0.3.46","Node name for S&R":"LoadImage"},"widgets_values":["bulldog_1.webp","image"]},{"id":10,"type":"Runware Vectorize","pos":[162.64442443847656,533.0270385742188],"size":[270,58],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"Image","name":"Image","type":"IMAGE","link":10},{"localized_name":"Model","name":"Model","type":"COMBO","widget":{"name":"Model"},"link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"STRING","links":[11]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"91646ada844167e36188b535ff60063735d10ca3","Node name for S&R":"Runware Vectorize"},"widgets_values":["recraft:1@1"],"bgcolor":"#5345bf"}],"links":[[10,4,0,10,0,"IMAGE"],[11,10,0,9,0,"STRING"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.02884607845781,"offset":[476.67256864649244,-345.82928772954745]}},"version":0.4}