Merge branch 'main' into feature-FishAudio

Resolve import conflict in clientlibs/main.js by combining handlers
from both branches (MeshCluster, Moodboards from main; ReferenceVoice
from feature-FishAudio).
This commit is contained in:
Sirshendu Ganguly
2026-06-03 14:42:20 +05:30
17 changed files with 366 additions and 24 deletions
+19 -8
View File
@@ -27,6 +27,10 @@ class imageInferenceInputs:
"tooltip": f"Optional tag describing {ordinal.capitalize()} Reference Image. Leave empty to omit.",
"default": "",
})
optionalInputs[f"Reference Role {i}"] = ("STRING", {
"tooltip": f"Optional role for {ordinal} reference image (e.g. person, garment). Leave empty to omit.",
"default": "",
})
optionalInputs[f"Reference Type {i}"] = ("STRING", {
"tooltip": f"Optional type for {ordinal} reference, e.g. 'sketch' for illustrative style models. Leave empty to omit.",
"default": "",
@@ -44,13 +48,13 @@ class imageInferenceInputs:
optionalInputs[f"Super Resolution Reference Image {i}"] = ("IMAGE", {
"tooltip": f"Specifies {ordinal.capitalize()} Super Resolution Reference Image for the inputs.",
})
return {
"required": {},
"optional": optionalInputs
}
DESCRIPTION = "Configure custom inputs for Runware Image Inference, including reference images (with optional type e.g. 'sketch' for illustrative style models, and strength 0-1 for sketch) that can be passed to the inference node."
DESCRIPTION = "Configure custom inputs for Runware Image Inference, including image/mask, reference images (with optional tag, role, type e.g. 'sketch' for illustrative style models, and strength 0-1 for sketch), and super resolution references."
FUNCTION = "createInputs"
RETURN_TYPES = ("RUNWAREIMAGEINFERENCEINPUTS",)
RETURN_NAMES = ("Inference Inputs",)
@@ -85,34 +89,41 @@ class imageInferenceInputs:
for i in range(1, self.MAX_REFERENCE_IMAGES + 1):
image = kwargs.get(f"Reference Image {i}", None)
tag = kwargs.get(f"Reference Tag {i}", "")
role = kwargs.get(f"Reference Role {i}", "")
ref_type = kwargs.get(f"Reference Type {i}", "")
strength = kwargs.get(f"Reference Strength {i}", 0.0)
if image is not None:
reference_slots.append((image, tag, ref_type, strength))
reference_slots.append((image, tag, role, ref_type, strength))
if not reference_slots:
return []
has_tags = any(
isinstance(tag, str) and tag.strip() != ""
for _, tag, _, _ in reference_slots
for _, tag, _, _, _ in reference_slots
)
has_roles = any(
isinstance(role, str) and role.strip() != ""
for _, _, role, _, _ in reference_slots
)
has_type = any(
isinstance(rt, str) and rt.strip() != ""
for _, _, rt, _ in reference_slots
for _, _, _, rt, _ in reference_slots
)
if not has_tags and not has_type:
if not has_tags and not has_roles and not has_type:
return [
rwUtils.convertTensor2IMG(image)
for image, _, _, _ in reference_slots
for image, _, _, _, _ in reference_slots
]
references = []
for image, tag, ref_type, strength in reference_slots:
for image, tag, role, ref_type, strength in reference_slots:
entry = {"image": rwUtils.convertTensor2IMG(image)}
if isinstance(tag, str) and tag.strip() != "":
entry["tag"] = tag.strip()
if isinstance(role, str) and role.strip() != "":
entry["role"] = role.strip()
if isinstance(ref_type, str) and ref_type.strip() != "":
entry["type"] = ref_type.strip()
if ref_type.strip().lower() == "sketch":
@@ -0,0 +1,56 @@
"""
Runware Image Inference Settings Moodboards
Builds settings.moodboards: list of { "id": "<uuid>", "strength": <number> }.
"""
from typing import Any, Dict, List, Tuple
class RunwareImageInferenceSettingsMoodboards:
"""Build moodboards list for image inference settings."""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"id": ("STRING", {
"default": "",
"tooltip": "Moodboard UUID (settings.moodboards[].id).",
}),
},
"optional": {
"useStrength": ("BOOLEAN", {
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
"tooltip": "Include strength in moodboards entry.",
}),
"strength": ("FLOAT", {
"default": 0.35,
"min": -0.5,
"max": 1.5,
"step": 0.01,
"tooltip": "Moodboard strength (-0.5 to 1.5). Only used when useStrength is enabled.",
}),
},
}
RETURN_TYPES = ("RUNWAREIMAGEINFERENCEMOODBOARDS",)
RETURN_NAMES = ("moodboards",)
FUNCTION = "build_moodboards"
CATEGORY = "Runware"
DESCRIPTION = (
"Build settings.moodboards for image inference settings. "
"Outputs one-element array with required id and optional strength."
)
def build_moodboards(self, **kwargs) -> Tuple[List[Dict[str, Any]]]:
moodboard_id = str(kwargs.get("id") or "").strip()
if not moodboard_id:
return ([],)
entry: Dict[str, Any] = {"id": moodboard_id}
if kwargs.get("useStrength", False):
entry["strength"] = float(kwargs.get("strength", 0.35))
return ([entry],)
+25 -2
View File
@@ -3,7 +3,8 @@ Runware Image Inference Settings (registered as Runware Settings for workflow co
Provides settings for image generation including temperature, systemPrompt, topP, layers,
quality, background, style, search, promptExtend, editRegions, thinking (boolean),
thinkingLevel (high/medium/low), sequential, renderingSpeed (TURBO/DEFAULT/QUALITY),
magicPrompt (AUTO/ON/OFF), autoCrop, dilatePixels, and colorPalette
magicPrompt (AUTO/ON/OFF), autoCrop, dilatePixels, creativity (raw/low/medium/high), colorPalette,
and moodboards
(from Runware Image Inference Settings Color Palette).
"""
@@ -193,6 +194,17 @@ class RunwareSettings:
"max": 100,
"step": 1,
}),
"useCreativity": ("BOOLEAN", {
"tooltip": "Enable to include creativity in settings.",
"default": False,
}),
"creativity": (["raw", "low", "medium", "high"], {
"default": "medium",
"tooltip": "Creativity level. Only used when 'Use Creativity' is enabled.",
}),
"moodboards": ("RUNWAREIMAGEINFERENCEMOODBOARDS", {
"tooltip": "Connect Runware Image Inference Settings Moodboards. When connected with at least one entry, it is merged into settings.moodboards.",
}),
}
}
@@ -203,7 +215,8 @@ class RunwareSettings:
DESCRIPTION = (
"Configure general settings for image generation: temperature, system prompt, top-p, layers, quality, background, style, search, "
"promptExtend, editRegions (JSON), thinking (boolean), thinkingLevel (high/medium/low), sequential, "
"renderingSpeed (TURBO/DEFAULT/QUALITY), magicPrompt (AUTO/ON/OFF), autoCrop, dilatePixels, and optional colorPalette from the Color Palette node."
"renderingSpeed (TURBO/DEFAULT/QUALITY), magicPrompt (AUTO/ON/OFF), autoCrop, dilatePixels, "
"creativity (raw/low/medium/high), and optional colorPalette and moodboards from dedicated settings nodes."
)
def createSettings(self, **kwargs) -> tuple[Dict[str, Any]]:
@@ -229,6 +242,7 @@ class RunwareSettings:
useMagicPrompt = kwargs.get("useMagicPrompt", False)
useAutoCrop = kwargs.get("useAutoCrop", False)
useDilatePixels = kwargs.get("useDilatePixels", False)
useCreativity = kwargs.get("useCreativity", False)
# Get value parameters
temperature = kwargs.get("temperature", 1.0)
@@ -241,6 +255,7 @@ class RunwareSettings:
style = kwargs.get("style", "auto")
renderingSpeed = kwargs.get("renderingSpeed", "DEFAULT")
magicPrompt = kwargs.get("magicPrompt", "AUTO")
creativity = kwargs.get("creativity", "medium")
# Build settings dictionary - only include what is enabled
settings: Dict[str, Any] = {}
@@ -290,6 +305,9 @@ class RunwareSettings:
palette: Optional[List[Dict[str, Any]]] = kwargs.get("colorPalette")
if palette is not None and isinstance(palette, list) and len(palette) > 0:
settings["colorPalette"] = palette
moodboards: Optional[List[Dict[str, Any]]] = kwargs.get("moodboards")
if moodboards is not None and isinstance(moodboards, list) and len(moodboards) > 0:
settings["moodboards"] = moodboards
if useRenderingSpeed:
settings["renderingSpeed"] = str(renderingSpeed)
@@ -299,6 +317,11 @@ class RunwareSettings:
settings["autoCrop"] = bool(kwargs.get("autoCrop", False))
if useDilatePixels:
settings["dilatePixels"] = int(kwargs.get("dilatePixels", 10))
if useCreativity:
creativity = str(creativity)
if creativity not in ("raw", "low", "medium", "high"):
raise ValueError("creativity must be raw, low, medium, or high when useCreativity is enabled.")
settings["creativity"] = creativity
# Clean up None values
settings = {k: v for k, v in settings.items() if v is not None}
+60 -6
View File
@@ -1,9 +1,9 @@
"""
Runware 3D Inference Settings Node
Main settings for 3D inference: textureSize, decimationTarget, remesh, resolution,
imageAutoFix, faceLimit, texture, pbr, Tripo mesh/texture options, material/quality,
Main settings for 3D inference: textureSize, decimationTarget, remesh, remeshBand, remeshProject,
resolution, imageAutoFix, faceLimit, texture, pbr, textureFormat, alphaMode, Tripo mesh/texture options, material/quality,
polyCount, taPose, boundingBox, meshMode, addons, hdTexture, and sub-configs
sparseStructure, shapeSlat, texSlat from connected nodes.
sparseStructure, shapeSlat, texSlat, meshCluster from connected nodes.
"""
from typing import Dict, Any
@@ -425,6 +425,47 @@ class Runware3DInferenceSettings:
"label_on": "true",
"label_off": "false",
}),
"meshCluster": ("RUNWARE3DINFERENCESETTINGSLAT", {
"tooltip": "Connect Runware 3D Inference Settings Mesh Cluster node.",
}),
"useRemeshBand": ("BOOLEAN", {
"tooltip": "Enable to include remeshBand in settings.",
"default": False,
}),
"remeshBand": ("FLOAT", {
"tooltip": "Narrow-band width (in voxels) for dual-contouring remeshing. Must be greater than 0 when remesh is true. Only used when enabled.",
"default": 1.0,
"min": 0.1,
"max": 16.0,
"step": 0.1,
}),
"useRemeshProject": ("BOOLEAN", {
"tooltip": "Enable to include remeshProject in settings.",
"default": False,
}),
"remeshProject": ("FLOAT", {
"tooltip": "Projection factor for snapping remeshed vertices back to the original surface. Must be greater than 0 when remesh is true. Only used when enabled.",
"default": 0.01,
"min": 0.01,
"max": 1.0,
"step": 0.01,
}),
"useTextureFormat": ("BOOLEAN", {
"tooltip": "Enable to include textureFormat in settings.",
"default": False,
}),
"textureFormat": (["WEBP", "PNG"], {
"tooltip": "Texture format stored in the GLB output: WEBP or PNG. Only used when 'Use Texture Format' is enabled.",
"default": "WEBP",
}),
"useAlphaMode": ("BOOLEAN", {
"tooltip": "Enable to include alphaMode in settings.",
"default": False,
}),
"alphaMode": (["OPAQUE", "BLEND", "MASK"], {
"tooltip": "Material alpha mode. Only used when 'Use Alpha Mode' is enabled.",
"default": "OPAQUE",
}),
}
}
@@ -433,10 +474,11 @@ class Runware3DInferenceSettings:
FUNCTION = "create"
CATEGORY = "Runware"
DESCRIPTION = (
"Configure Runware 3D Inference settings: textureSize, decimationTarget, remesh, resolution, imageAutoFix, faceLimit, "
"texture, pbr, textureSeed, textureAlignment, textureQuality, autoSize, orientation, quad, compress, "
"Configure Runware 3D Inference settings: textureSize, decimationTarget, remesh, remeshBand, remeshProject, "
"resolution, imageAutoFix, faceLimit, texture, pbr, textureSeed, textureAlignment, textureQuality, "
"textureFormat, alphaMode, autoSize, orientation, quad, compress, "
"smartLowPoly, generateParts, exportUv, geometryQuality, originalAlpha, material, quality, polyCount, "
"taPose, boundingBox, meshMode, addons, hdTexture, and lat configs."
"taPose, boundingBox, meshMode, addons, hdTexture, lat configs, and meshCluster."
)
def create(self, **kwargs) -> tuple[Dict[str, Any]]:
@@ -448,6 +490,10 @@ class Runware3DInferenceSettings:
settings["decimationTarget"] = int(kwargs.get("decimationTarget", 500000))
if kwargs.get("useRemesh", False):
settings["remesh"] = bool(kwargs.get("remesh", True))
if kwargs.get("useRemeshBand", False):
settings["remeshBand"] = float(kwargs.get("remeshBand", 1.0))
if kwargs.get("useRemeshProject", False):
settings["remeshProject"] = float(kwargs.get("remeshProject", 0.01))
if kwargs.get("useResolution", False):
settings["resolution"] = int(kwargs.get("resolution", 1024))
@@ -473,6 +519,10 @@ class Runware3DInferenceSettings:
settings["textureAlignment"] = kwargs.get("textureAlignment", "original_image")
if kwargs.get("useTextureQuality", False):
settings["textureQuality"] = kwargs.get("textureQuality", "standard")
if kwargs.get("useTextureFormat", False):
settings["textureFormat"] = kwargs.get("textureFormat", "WEBP")
if kwargs.get("useAlphaMode", False):
settings["alphaMode"] = kwargs.get("alphaMode", "OPAQUE")
if kwargs.get("useAutoSize", False):
settings["autoSize"] = bool(kwargs.get("autoSize", False))
if kwargs.get("useOrientation", False):
@@ -556,6 +606,10 @@ class Runware3DInferenceSettings:
if tex is not None and isinstance(tex, dict) and len(tex) > 0:
settings["texSlat"] = tex
mesh_cluster = kwargs.get("meshCluster", None)
if mesh_cluster is not None and isinstance(mesh_cluster, dict) and len(mesh_cluster) > 0:
settings["meshCluster"] = mesh_cluster
return (settings,)
@@ -0,0 +1,90 @@
"""
Runware 3D Inference Settings Mesh Cluster Node
Outputs meshCluster config: thresholdConeHalfAngleRad, refineIterations,
globalIterations, smoothStrength
"""
import math
from typing import Dict, Any
class Runware3DInferenceSettingsMeshCluster:
"""Runware 3D Inference Settings Mesh Cluster Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useThresholdConeHalfAngleRad": ("BOOLEAN", {
"tooltip": "Enable to include thresholdConeHalfAngleRad in meshCluster",
"default": False,
}),
"thresholdConeHalfAngleRad": ("FLOAT", {
"tooltip": "Cone half-angle threshold (radians) used during UV chart clustering. Only used when enabled.",
"default": math.pi / 2,
"min": 0.0,
"max": math.pi,
"step": 0.01,
}),
"useRefineIterations": ("BOOLEAN", {
"tooltip": "Enable to include refineIterations in meshCluster",
"default": False,
}),
"refineIterations": ("INT", {
"tooltip": "Local refinement iterations for chart clustering. Only used when enabled.",
"default": 0,
"min": 0,
"max": 64,
"step": 1,
}),
"useGlobalIterations": ("BOOLEAN", {
"tooltip": "Enable to include globalIterations in meshCluster",
"default": False,
}),
"globalIterations": ("INT", {
"tooltip": "Global iterations for chart clustering. Only used when enabled.",
"default": 1,
"min": 0,
"max": 64,
"step": 1,
}),
"useSmoothStrength": ("BOOLEAN", {
"tooltip": "Enable to include smoothStrength in meshCluster",
"default": False,
}),
"smoothStrength": ("INT", {
"tooltip": "Smoothing strength applied during chart clustering. Only used when enabled.",
"default": 1,
"min": 0,
"max": 10,
"step": 1,
}),
}
}
RETURN_TYPES = ("RUNWARE3DINFERENCESETTINGSLAT",)
RETURN_NAMES = ("Mesh Cluster",)
FUNCTION = "create"
CATEGORY = "Runware"
DESCRIPTION = "Configure mesh cluster parameters for Runware 3D Inference settings."
def create(self, **kwargs) -> tuple[Dict[str, Any]]:
out: Dict[str, Any] = {}
if kwargs.get("useThresholdConeHalfAngleRad", False):
out["thresholdConeHalfAngleRad"] = float(kwargs.get("thresholdConeHalfAngleRad", math.pi / 2))
if kwargs.get("useRefineIterations", False):
out["refineIterations"] = int(kwargs.get("refineIterations", 0))
if kwargs.get("useGlobalIterations", False):
out["globalIterations"] = int(kwargs.get("globalIterations", 1))
if kwargs.get("useSmoothStrength", False):
out["smoothStrength"] = int(kwargs.get("smoothStrength", 1))
return (out,)
NODE_CLASS_MAPPINGS = {
"Runware3DInferenceSettingsMeshCluster": Runware3DInferenceSettingsMeshCluster,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"Runware3DInferenceSettingsMeshCluster": "Runware 3D Inference Settings Mesh Cluster",
}
+13
View File
@@ -96,6 +96,17 @@ class RunwareUltralyticsInputs:
"step": 0.01,
"tooltip": "Denoising strength (0.0001-1).",
}),
"useInpaintSize": ("BOOLEAN", {
"tooltip": "Enable to include inpaintSize parameter.",
"default": False,
}),
"inpaintSize": ("INT", {
"default": 1024,
"min": 128,
"max": 2048,
"step": 1,
"tooltip": "Image size to use for each inpainting region. Only used when useInpaintSize is enabled.",
}),
}
}
@@ -130,6 +141,8 @@ class RunwareUltralyticsInputs:
settings["CFGScale"] = kwargs.get("CFGScale", 8.0)
if kwargs.get("useStrength", False):
settings["strength"] = kwargs.get("strength", 0.3)
if kwargs.get("useInpaintSize", False):
settings["inpaintSize"] = int(kwargs.get("inpaintSize", 1024))
return (settings,)
+1 -7
View File
@@ -422,14 +422,8 @@ class txt2vid:
genConfig[0]["providerSettings"] = providerSettings
# Add safety inputs if provided
# Note: Video inference API only supports 'mode' key for safety
if safetyInputs is not None and isinstance(safetyInputs, dict) and len(safetyInputs) > 0:
# Filter to only include 'mode' (only allowed key for video inference)
filtered_safety = {}
if "mode" in safetyInputs:
filtered_safety["mode"] = safetyInputs["mode"]
if filtered_safety:
genConfig[0]["safety"] = filtered_safety
genConfig[0]["safety"] = safetyInputs
# Add video advanced feature inputs if provided
if videoAdvancedFeatureInputs is not None and isinstance(videoAdvancedFeatureInputs, dict) and len(videoAdvancedFeatureInputs) > 0:
+3
View File
@@ -139,6 +139,7 @@ class videoModelSearch:
"Pruna": [
"prunaai:p-video@0 (P-Video)",
"prunaai:p-video@avatar (P-Video Avatar)",
"prunaai:p-video@animate (P-Video Animate)",
],
"SkyReels": [
"skywork:skyreels@v4 (SkyReels V4)",
@@ -278,6 +279,7 @@ class videoModelSearch:
# Pruna Models (720p)
"prunaai:p-video@0": {"width": 1280, "height": 720},
"prunaai:p-video@avatar": {"width": 1280, "height": 720},
"prunaai:p-video@animate": {"width": 1280, "height": 720},
# HeyGen Models (Model ID: heygen-avatar-iv, heygen-avatar-v)
"heygen:avatar@4": {"width": 1280, "height": 720},
@@ -421,6 +423,7 @@ class videoModelSearch:
# Pruna Models
"prunaai:p-video@0": "720p",
"prunaai:p-video@avatar": "720p",
"prunaai:p-video@animate": "720p",
# HeyGen Models
"heygen:avatar@4": "720p",
+14
View File
@@ -225,6 +225,16 @@ class RunwareVideoSettings:
"label_on": "true",
"label_off": "false",
}),
"usePreserveAudio": ("BOOLEAN", {
"tooltip": "Enable to include preserveAudio in video inference settings.",
"default": False,
}),
"preserveAudio": ("BOOLEAN", {
"tooltip": "Preserve source audio in output video. Only used when 'Use Preserve Audio' is enabled.",
"default": True,
"label_on": "Enabled",
"label_off": "Disabled",
}),
}
}
@@ -243,6 +253,8 @@ class RunwareVideoSettings:
draft = kwargs.get("draft", False)
use_audio = kwargs.get("useAudio", False)
audio = kwargs.get("audio", False)
use_preserve_audio = kwargs.get("usePreserveAudio", False)
preserve_audio = kwargs.get("preserveAudio", True)
use_voice_prompt = kwargs.get("useVoicePrompt", False)
voice_prompt = (kwargs.get("voicePrompt") or "").strip()
use_safety_filter = kwargs.get("useSafetyFilter", False)
@@ -293,6 +305,8 @@ class RunwareVideoSettings:
settings["draft"] = bool(draft)
if use_audio:
settings["audio"] = bool(audio)
if use_preserve_audio:
settings["preserveAudio"] = bool(preserve_audio)
if use_voice_prompt and voice_prompt:
settings["voicePrompt"] = voice_prompt
if use_safety_filter: