Merge pull request #96 from Runware/feature-KlingMultiprompt

Add support for Kling Multiprompt
This commit is contained in:
Sirshendu Ganguly
2026-02-17 13:36:46 -05:00
committed by GitHub
8 changed files with 220 additions and 1 deletions
+4
View File
@@ -53,6 +53,8 @@ from .modules.videoInferenceInputs import videoInferenceInputs
from .modules.providerSettings.lightricksProviderSettings import RunwareLightricksProviderSettings
from .modules.providerSettings.minimaxProviderSettings import RunwareMiniMaxProviderSettings
from .modules.providerSettings.klingProviderSettings import RunwareKlingProviderSettings
from .modules.providerSettings.klingMultiPromptSegment import RunwareKlingMultiPromptSegment
from .modules.providerSettings.klingProviderSettingsMultiPrompt import RunwareKlingProviderSettingsMultiPrompt
from .modules.providerSettings.runwayProviderSettings import RunwareRunwayProviderSettings
from .modules.providerSettings.lumaProviderSettings import RunwareLumaProviderSettings
from .modules.providerSettings.midjourneyProviderSettings import RunwareMidjourneyProviderSettings
@@ -153,6 +155,8 @@ NODE_CLASS_MAPPINGS = {
"Runware MiniMax Provider Settings": RunwareMiniMaxProviderSettings,
"Runware Luma Provider Settings": RunwareLumaProviderSettings,
"Runware KlingAI Provider Settings": RunwareKlingProviderSettings,
"Runware Kling MultiPrompt Segment": RunwareKlingMultiPromptSegment,
"Runware Kling Provider Settings MultiPrompt": RunwareKlingProviderSettingsMultiPrompt,
"Runware Runway Provider Settings": RunwareRunwayProviderSettings,
"Runware Midjourney Provider Settings": RunwareMidjourneyProviderSettings,
"Runware Mirelo Provider Settings": RunwareMireloProviderSettings,
+10
View File
@@ -100,6 +100,8 @@ const RUNWARE_NODE_TYPES = {
VIDEOINFERENCEINPUTS: "Runware Video Inference Inputs",
LIGHTRICKSPROVIDERSETTINGS: "Runware Lightricks Provider Settings",
KLINGPROVIDERSETTINGS: "Runware KlingAI Provider Settings",
KLINGMULTIPROMPTSEGMENT: "Runware Kling MultiPrompt Segment",
KLINGPROVIDERSETTINGSMULTIPROMPT: "Runware Kling Provider Settings MultiPrompt",
RUNWAYPROVIDERSETTINGS: "Runware Runway Provider Settings",
LUMAPROVIDERSETTINGS: "Runware Luma Provider Settings",
MIDJOURNEYPROVIDERSETTINGS: "Runware Midjourney Provider Settings",
@@ -349,6 +351,14 @@ const RUNWARE_NODE_PROPS = {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.KLINGMULTIPROMPTSEGMENT]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.KLINGPROVIDERSETTINGSMULTIPROMPT]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.RUNWAYPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
+26
View File
@@ -2127,6 +2127,8 @@ function videoModelSearchFilterHandler(videoModelSearchNode) {
"klingai:7@1 (KlingAI Lip-Sync)",
"klingai:kling@o1 (Kling VIDEO O1)",
"klingai:kling-video@2.6-pro (Kling VIDEO 2.6 Pro)",
"klingai:kling-video@3-pro (Kling VIDEO 3.0 Pro)",
"klingai:kling-video@3-standard (Kling VIDEO 3.0 Standard)",
"klingai:avatar@2.0-standard (KlingAI Avatar 2.0 Standard)",
"klingai:avatar@2.0-pro (KlingAI Avatar 2.0 Pro)",
],
@@ -2222,6 +2224,8 @@ function videoModelSearchFilterHandler(videoModelSearchNode) {
"klingai:7@1": {"width": 0, "height": 0},
"klingai:kling@o1": {"width": 1440, "height": 1440},
"klingai:kling-video@2.6-pro": {"width": 1920, "height": 1080},
"klingai:kling-video@3-pro": {"width": 1920, "height": 1080},
"klingai:kling-video@3-standard": {"width": 1920, "height": 1080},
"klingai:avatar@2.0-standard": {"width": 0, "height": 0},
"klingai:avatar@2.0-pro": {"width": 0, "height": 0},
"google:2@0": {"width": 1280, "height": 720},
@@ -2297,6 +2301,8 @@ function videoModelSearchFilterHandler(videoModelSearchNode) {
"klingai:7@1": null, // No resolution support
"klingai:kling@o1": null, // No standard resolution
"klingai:kling-video@2.6-pro": "1080p",
"klingai:kling-video@3-pro": "1080p",
"klingai:kling-video@3-standard": "1080p",
"klingai:avatar@2.0-standard": null, // No resolution support
"klingai:avatar@2.0-pro": null, // No resolution support
"google:2@0": "720p",
@@ -2616,6 +2622,7 @@ function klingProviderSettingsToggleHandler(klingNode) {
const soundWidget = klingNode.widgets.find(w => w.name === "sound");
const useCharacterOrientationWidget = klingNode.widgets.find(w => w.name === "useCharacterOrientation");
const characterOrientationWidget = klingNode.widgets.find(w => w.name === "characterOrientation");
const useMultipromptWidget = klingNode.widgets.find(w => w.name === "useMultiprompt");
// Helper function to toggle widget enabled state (EXACT same pattern as imageInferenceToggleHandler)
// Callers guard with if (useX && xWidget) before invoking
@@ -2698,6 +2705,25 @@ function klingProviderSettingsToggleHandler(klingNode) {
if (useCharacterOrientationWidget && characterOrientationWidget) {
toggleWidgetState(useCharacterOrientationWidget, characterOrientationWidget, "characterOrientation");
}
// Toggle MultiPrompt Segment input slots when useMultiprompt changes
if (useMultipromptWidget) {
function toggleMultipromptInputs() {
const enabled = useMultipromptWidget.value === true;
klingNode.inputs?.forEach(inp => {
if (inp?.name?.startsWith("MultiPrompt Segment")) {
inp.disabled = !enabled;
if (inp.domElement) {
inp.domElement.style.opacity = enabled ? "1" : "0.4";
inp.domElement.style.pointerEvents = enabled ? "auto" : "none";
}
}
});
klingNode.setDirtyCanvas(true);
}
appendWidgetCB(useMultipromptWidget, () => setTimeout(toggleMultipromptInputs, 50));
setTimeout(toggleMultipromptInputs, 100);
}
}
function alibabaProviderSettingsToggleHandler(alibabaNode) {
@@ -0,0 +1,47 @@
"""
Runware Kling MultiPrompt Segment Node
Creates a single prompt/duration entry for Kling multiPrompt provider settings
"""
from typing import Dict, Any
class RunwareKlingMultiPromptSegment:
"""Runware Kling MultiPrompt Segment Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"prompt": ("STRING", {
"multiline": True,
"default": "",
"tooltip": "Prompt for this segment of the video",
}),
"duration": ("INT", {
"default": 4,
"min": 1,
"max": 30,
"step": 1,
"tooltip": "Duration in seconds for this segment",
}),
},
}
RETURN_TYPES = ("RUNWAREKLINGMULTIPROMPTSEGMENT",)
RETURN_NAMES = ("MultiPrompt Segment",)
FUNCTION = "createSegment"
CATEGORY = "Runware/Provider Settings"
DESCRIPTION = "Create a prompt/duration segment for Kling multiPrompt. Connect multiple segments to Runware Kling Provider Settings MultiPrompt."
def createSegment(self, prompt: str, duration: int) -> tuple[Dict[str, Any]]:
return ({"prompt": prompt.strip(), "duration": int(duration)},)
NODE_CLASS_MAPPINGS = {
"RunwareKlingMultiPromptSegment": RunwareKlingMultiPromptSegment,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareKlingMultiPromptSegment": "Runware Kling MultiPrompt Segment",
}
@@ -3,7 +3,7 @@ Runware KlingAI Provider Settings Node
Provides KlingAI-specific settings for video generation
"""
from typing import Optional, Dict, Any
from typing import Optional, Dict, Any, List
class RunwareKlingProviderSettings:
@@ -98,6 +98,27 @@ class RunwareKlingProviderSettings:
"tooltip": "Character orientation mode. 'image': has the same orientation as the person in the picture (reference video duration should not exceed 10 seconds). 'video': consistent with the orientation of the characters in the video (reference video duration should not exceed 30 seconds). Only used when 'Use Character Orientation' is enabled.",
"default": "image",
}),
"useMultiprompt": ("BOOLEAN", {
"tooltip": "Enable to include multiPrompt in provider settings. When enabled, connect Runware Kling MultiPrompt Segment nodes.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"MultiPrompt Segment 1": ("RUNWAREKLINGMULTIPROMPTSEGMENT", {
"tooltip": "Connect a Runware Kling MultiPrompt Segment. Only used when 'Use Multiprompt' is enabled.",
}),
"MultiPrompt Segment 2": ("RUNWAREKLINGMULTIPROMPTSEGMENT", {
"tooltip": "Connect a Runware Kling MultiPrompt Segment. Only used when 'Use Multiprompt' is enabled.",
}),
"MultiPrompt Segment 3": ("RUNWAREKLINGMULTIPROMPTSEGMENT", {
"tooltip": "Connect a Runware Kling MultiPrompt Segment. Only used when 'Use Multiprompt' is enabled.",
}),
"MultiPrompt Segment 4": ("RUNWAREKLINGMULTIPROMPTSEGMENT", {
"tooltip": "Connect a Runware Kling MultiPrompt Segment. Only used when 'Use Multiprompt' is enabled.",
}),
"MultiPrompt Segment 5": ("RUNWAREKLINGMULTIPROMPTSEGMENT", {
"tooltip": "Connect a Runware Kling MultiPrompt Segment. Only used when 'Use Multiprompt' is enabled.",
}),
}
}
@@ -120,6 +141,7 @@ class RunwareKlingProviderSettings:
useKeepOriginalSound = kwargs.get("useKeepOriginalSound", False)
useSound = kwargs.get("useSound", False)
useCharacterOrientation = kwargs.get("useCharacterOrientation", False)
useMultiprompt = kwargs.get("useMultiprompt", False)
# Get value parameters
cameraControl = kwargs.get("cameraControl", "none")
@@ -171,6 +193,21 @@ class RunwareKlingProviderSettings:
if useCharacterOrientation and characterOrientation:
klingSettings["characterOrientation"] = characterOrientation
# Add multiPrompt only if useMultiprompt is enabled
if useMultiprompt:
segments: List[Dict[str, Any]] = []
for i in range(1, 6):
segment = kwargs.get(f"MultiPrompt Segment {i}")
if segment is not None and isinstance(segment, dict):
prompt = segment.get("prompt", "").strip()
if prompt:
segments.append({
"prompt": prompt,
"duration": int(segment.get("duration", 4)),
})
if segments:
klingSettings["multiPrompt"] = segments
# Clean up None values
klingSettings = {k: v for k, v in klingSettings.items() if v is not None}
@@ -0,0 +1,88 @@
"""
Runware Kling Provider Settings MultiPrompt Node
Adds multiPrompt to Kling provider settings. Connects to Runware Kling Provider Settings
and outputs merged provider settings for Video Inference.
"""
from typing import Dict, Any, List
class RunwareKlingProviderSettingsMultiPrompt:
"""Runware Kling Provider Settings MultiPrompt Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"providerSettings": ("RUNWAREPROVIDERSETTINGS", {
"tooltip": "Connect Runware KlingAI Provider Settings to merge with multiPrompt",
}),
},
"optional": {
"MultiPrompt Segment 1": ("RUNWAREKLINGMULTIPROMPTSEGMENT", {
"tooltip": "Connect a Runware Kling MultiPrompt Segment",
}),
"MultiPrompt Segment 2": ("RUNWAREKLINGMULTIPROMPTSEGMENT", {
"tooltip": "Connect a Runware Kling MultiPrompt Segment",
}),
"MultiPrompt Segment 3": ("RUNWAREKLINGMULTIPROMPTSEGMENT", {
"tooltip": "Connect a Runware Kling MultiPrompt Segment",
}),
"MultiPrompt Segment 4": ("RUNWAREKLINGMULTIPROMPTSEGMENT", {
"tooltip": "Connect a Runware Kling MultiPrompt Segment",
}),
"MultiPrompt Segment 5": ("RUNWAREKLINGMULTIPROMPTSEGMENT", {
"tooltip": "Connect a Runware Kling MultiPrompt Segment",
}),
}
}
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("Provider Settings",)
FUNCTION = "mergeProviderSettings"
CATEGORY = "Runware/Provider Settings"
DESCRIPTION = "Add multiPrompt to Kling provider settings. Connect Runware KlingAI Provider Settings and one or more Runware Kling MultiPrompt Segment nodes. Output connects to Runware Video Inference."
def mergeProviderSettings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Merge Kling provider settings with multiPrompt segments"""
provider_settings = kwargs.get("providerSettings")
segments: List[Dict[str, Any]] = []
for i in range(1, 6):
segment = kwargs.get(f"MultiPrompt Segment {i}")
if segment is not None and isinstance(segment, dict):
prompt = segment.get("prompt", "").strip()
if prompt:
segments.append({
"prompt": prompt,
"duration": int(segment.get("duration", 4)),
})
# Start with existing provider settings or empty dict
if isinstance(provider_settings, dict):
# May be flat (from Kling) or wrapped {"klingai": {...}}
if "klingai" in provider_settings:
kling_settings = dict(provider_settings["klingai"])
else:
kling_settings = dict(provider_settings)
else:
kling_settings = {}
if segments:
kling_settings["multiPrompt"] = segments
if not kling_settings:
return ({},)
# Output format expected by video inference: wrap with klingai if flat
return ({"klingai": kling_settings},)
NODE_CLASS_MAPPINGS = {
"RunwareKlingProviderSettingsMultiPrompt": RunwareKlingProviderSettingsMultiPrompt,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareKlingProviderSettingsMultiPrompt": "Runware Kling Provider Settings MultiPrompt",
}
+6
View File
@@ -18,6 +18,8 @@ class videoModelSearch:
"klingai:7@1 (KlingAI Lip-Sync)",
"klingai:kling@o1 (Kling VIDEO O1)",
"klingai:kling-video@2.6-pro (Kling VIDEO 2.6 Pro)",
"klingai:kling-video@3-pro (Kling VIDEO 3.0 Pro)",
"klingai:kling-video@3-standard (Kling VIDEO 3.0 Standard)",
],
"Veo": [
"google:2@0 (Veo 2.0)",
@@ -132,6 +134,8 @@ class videoModelSearch:
"klingai:7@1": {"width": 0, "height": 0},
"klingai:kling@o1": {"width": 1440, "height": 1440},
"klingai:kling-video@2.6-pro": {"width": 1920, "height": 1080},
"klingai:kling-video@3-pro": {"width": 1920, "height": 1080},
"klingai:kling-video@3-standard": {"width": 1920, "height": 1080},
# Veo Models
"google:2@0": {"width": 1280, "height": 720},
@@ -245,6 +249,8 @@ class videoModelSearch:
"klingai:7@1": None, # No resolution support
"klingai:kling@o1": None, # No standard resolution (1440x1440)
"klingai:kling-video@2.6-pro": "1080p",
"klingai:kling-video@3-pro": "1080p",
"klingai:kling-video@3-standard": "1080p",
# Veo Models
"google:2@0": "720p",
File diff suppressed because one or more lines are too long