diff --git a/__init__.py b/__init__.py index 2dd7519..e3cdd81 100644 --- a/__init__.py +++ b/__init__.py @@ -53,6 +53,11 @@ from .modules.textInferenceMessages import RunwareTextInferenceMessages from .modules.textInferenceMessage import RunwareTextInferenceMessage from .modules.textInference import RunwareTextInference from .modules.textInferenceSettings import RunwareTextInferenceSettings +from .modules.textInferenceToolChoice import RunwareTextInferenceToolChoice +from .modules.textInferenceSettingsCache import RunwareTextInferenceSettingsCache +from .modules.textInferenceInputs import RunwareTextInferenceInputs +from .modules.textInferenceInputsImages import RunwareTextInferenceInputsImages +from .modules.textInferenceInputsVideos import RunwareTextInferenceInputsVideos from .modules.audioSections import RunwareAudioSections from .modules.audioInferenceInputs import audioInferenceInputs from .modules.audioSettings import RunwareAudioSettings @@ -177,6 +182,11 @@ NODE_CLASS_MAPPINGS = { "Runware Text Inference Message": RunwareTextInferenceMessage, "Runware Text Inference": RunwareTextInference, "Runware Text Inference Settings": RunwareTextInferenceSettings, + "Runware Text Inference Tool Choice": RunwareTextInferenceToolChoice, + "Runware Text Inference Settings Cache": RunwareTextInferenceSettingsCache, + "Runware Text Inference Inputs": RunwareTextInferenceInputs, + "Runware Text Inference Inputs Images": RunwareTextInferenceInputsImages, + "Runware Text Inference Inputs Videos": RunwareTextInferenceInputsVideos, "Runware Audio Sections": RunwareAudioSections, "Runware Audio Inference Inputs": audioInferenceInputs, "Runware Audio Inference Settings": RunwareAudioSettings, diff --git a/clientlibs/types.js b/clientlibs/types.js index 26ed9f0..12443e7 100644 --- a/clientlibs/types.js +++ b/clientlibs/types.js @@ -89,6 +89,11 @@ const RUNWARE_NODE_TYPES = { TEXTINFERENCEMESSAGES: "Runware Text Inference Messages", TEXTINFERENCEMESSAGE: "Runware Text Inference Message", TEXTINFERENCESETTINGS: "Runware Text Inference Settings", + TEXTINFERENCETOOLCHOICE: "Runware Text Inference Tool Choice", + TEXTINFERENCESETTINGSCACHE: "Runware Text Inference Settings Cache", + TEXTINFERENCEINPUTS: "Runware Text Inference Inputs", + TEXTINFERENCEINPUTSIMAGES: "Runware Text Inference Inputs Images", + TEXTINFERENCEINPUTSVIDEOS: "Runware Text Inference Inputs Videos", AUDIOSECTIONS: "Runware Audio Sections", AUDIOINFERENCEINPUTS: "Runware Audio Inference Inputs", AUDIOSETTINGS: "Runware Audio Inference Settings", @@ -328,6 +333,26 @@ const RUNWARE_NODE_PROPS = { bgColor: DEFAULT_BGCOLOR, colorModeOnly: true, }, + [RUNWARE_NODE_TYPES.TEXTINFERENCETOOLCHOICE]: { + bgColor: DEFAULT_BGCOLOR, + colorModeOnly: true, + }, + [RUNWARE_NODE_TYPES.TEXTINFERENCESETTINGSCACHE]: { + bgColor: DEFAULT_BGCOLOR, + colorModeOnly: true, + }, + [RUNWARE_NODE_TYPES.TEXTINFERENCEINPUTSIMAGES]: { + bgColor: DEFAULT_BGCOLOR, + colorModeOnly: true, + }, + [RUNWARE_NODE_TYPES.TEXTINFERENCEINPUTSVIDEOS]: { + bgColor: DEFAULT_BGCOLOR, + colorModeOnly: true, + }, + [RUNWARE_NODE_TYPES.TEXTINFERENCEINPUTS]: { + bgColor: DEFAULT_BGCOLOR, + colorModeOnly: true, + }, [RUNWARE_NODE_TYPES.TEXTINFERENCESETTINGS]: { bgColor: DEFAULT_BGCOLOR, colorModeOnly: true, diff --git a/clientlibs/utils.js b/clientlibs/utils.js index 32fc1e5..ef7e4e7 100644 --- a/clientlibs/utils.js +++ b/clientlibs/utils.js @@ -1360,8 +1360,6 @@ function textInferenceSettingsToggleHandler(settingsNode) { const frequencyPenaltyWidget = settingsNode.widgets.find(w => w && w.name === "frequencyPenalty"); const useToolsWidget = settingsNode.widgets.find(w => w && w.name === "useTools"); const toolsWidget = settingsNode.widgets.find(w => w && w.name === "tools"); - const useToolChoiceWidget = settingsNode.widgets.find(w => w && w.name === "useToolChoice"); - const toolChoiceWidget = settingsNode.widgets.find(w => w && w.name === "toolChoice"); function toggleWidgetState(useWidget, paramWidget) { if (!useWidget || !paramWidget) return; @@ -1388,7 +1386,6 @@ function textInferenceSettingsToggleHandler(settingsNode) { if (usePresencePenaltyWidget && presencePenaltyWidget) toggleWidgetState(usePresencePenaltyWidget, presencePenaltyWidget); if (useFrequencyPenaltyWidget && frequencyPenaltyWidget) toggleWidgetState(useFrequencyPenaltyWidget, frequencyPenaltyWidget); if (useToolsWidget && toolsWidget) toggleWidgetState(useToolsWidget, toolsWidget); - if (useToolChoiceWidget && toolChoiceWidget) toggleWidgetState(useToolChoiceWidget, toolChoiceWidget); } function videoSettingsToggleHandler(settingsNode) { @@ -3281,9 +3278,17 @@ function textModelSearchFilterHandler(textModelSearchNode) { "openai:gpt@5.4-mini (GPT-5.4 Mini)", "openai:gpt@5.4-nano (GPT-5.4 Nano)", ], + "Anthropic": [ + "anthropic:claude@opus-4.7 (Claude Opus 4.7)", + "anthropic:claude@sonnet-4.6 (Claude Sonnet 4.6)", + "anthropic:claude@haiku-4.5 (Claude Haiku 4.5)", + ], "Google": [ "google:gemini@3-flash (Gemini 3 Flash)", ], + "MoonshotAI": [ + "moonshotai:kimi@k2.6 (Kimi K2.6)", + ], "Zai": [ "zai-glm-5-1 (ZAI GLM 5.1)", ], @@ -3359,6 +3364,7 @@ function audioModelSearchFilterHandler(audioModelSearchNode) { "minimax:music@2.6 (MiniMax Music 2.6)", ], "Inworld": [ + "inworld:tts@2 (Inworld Realtime TTS 2)", "inworld:tts@1.5-mini (Inworld TTS-1.5 Mini)", "inworld:tts@1.5-max (Inworld TTS-1.5 Max)", ], @@ -5137,6 +5143,10 @@ function settingsToggleHandler(settingsNode) { const thinkingLevelWidget = settingsNode.widgets.find(w => w.name === "thinkingLevel"); const useSequentialWidget = settingsNode.widgets.find(w => w.name === "useSequential"); const sequentialWidget = settingsNode.widgets.find(w => w.name === "sequential"); + const useRenderingSpeedWidget = settingsNode.widgets.find(w => w.name === "useRenderingSpeed"); + const renderingSpeedWidget = settingsNode.widgets.find(w => w.name === "renderingSpeed"); + const useMagicPromptWidget = settingsNode.widgets.find(w => w.name === "useMagicPrompt"); + const magicPromptWidget = settingsNode.widgets.find(w => w.name === "magicPrompt"); // Helper function to toggle widget enabled state function toggleWidgetState(useWidget, paramWidget, paramName) { @@ -5223,6 +5233,12 @@ function settingsToggleHandler(settingsNode) { if (useSequentialWidget && sequentialWidget) { toggleWidgetState(useSequentialWidget, sequentialWidget, "sequential"); } + if (useRenderingSpeedWidget && renderingSpeedWidget) { + toggleWidgetState(useRenderingSpeedWidget, renderingSpeedWidget, "renderingSpeed"); + } + if (useMagicPromptWidget && magicPromptWidget) { + toggleWidgetState(useMagicPromptWidget, magicPromptWidget, "magicPrompt"); + } } function safetyInputsToggleHandler(safetyNode) { diff --git a/modules/audioInference.py b/modules/audioInference.py index 921bc5f..363ad76 100644 --- a/modules/audioInference.py +++ b/modules/audioInference.py @@ -5,6 +5,7 @@ import numpy as np import librosa import tempfile import os +from urllib.parse import urlparse import comfy.model_management from .utils import runwareUtils as rwUtils @@ -22,10 +23,6 @@ class RunwareAudioInference: "model": ("RUNWAREAUDIOMODEL", { "tooltip": "AI model to use for audio generation" }), - "usePositivePrompt": ("BOOLEAN", { - "default": True, - "tooltip": "Enable/disable positivePrompt parameter in API request" - }), "positivePrompt": ("STRING", { "multiline": True, "default": "classical piano piece, gentle and melodic", @@ -51,17 +48,17 @@ class RunwareAudioInference: "default": False, "tooltip": "Enable/disable sampleRate parameter in API request" }), - "sampleRate": ([8000, 16000, 22050, 24000, 32000, 44100], { + "sampleRate": ([8000, 16000, 22050, 24000, 32000, 44100, 48000], { "default": 32000, - "tooltip": "Audio sample rate in Hz. Supported: 8000, 16000, 22050, 24000, 32000, 44100." + "tooltip": "Audio sample rate in Hz. Supported: 8000, 16000, 22050, 24000, 32000, 44100, 48000.", }), "useBitrate": ("BOOLEAN", { "default": False, - "tooltip": "Enable/disable bitrate parameter in API request (MP3 only)" + "tooltip": "Enable/disable bitrate parameter in API request" }), "bitrate": ([8, 16, 32, 64, 128, 256], { "default": 128, - "tooltip": "Audio bitrate in kbps. Allowed: 8, 16, 32, 64, 128, 256. Only applies to MP3 format." + "tooltip": "Audio bitrate in kbps. Allowed: 8, 16, 32, 64, 128, 256. Applies to compressed formats (e.g. MP3, OGG).", }), "useChannels": ("BOOLEAN", { "default": False, @@ -74,10 +71,6 @@ class RunwareAudioInference: "step": 1, "tooltip": "Number of audio channels (1=mono, 2=stereo)" }), - "outputType": (["URL", "dataURI", "base64Data"], { - "default": "URL", - "tooltip": "Output type for the generated audio" - }), "outputFormat": (["MP3", "MP4", "WAV", "FLAC", "OGG"], { "default": "MP3", "tooltip": "Format of the output audio" @@ -219,13 +212,16 @@ class RunwareAudioInference: status = audioData["status"] if status == "success": - audioUrl = self._extractAudioUrl(pollResult) - hasAudio = audioUrl is not None + audioUrls = self._extractAudioUrls(pollResult) + hasAudio = len(audioUrls) > 0 hasVideo = bool(audioData.get("videoURL") or audioData.get("videoBase64Data", False)) if hasAudio: - audioObj = self._downloadAndProcessAudio(audioUrl, params["sampleRate"]) - print(f"[DEBUG] Audio URL found, returning audio. Audio URL: {audioUrl}") + audioObjects = [] + for audioUrl in audioUrls: + audioObjects.append(self._downloadAndProcessAudio(audioUrl, params["sampleRate"], params["outputFormat"])) + audioObj = self._mergeAudioObjects(audioObjects, params["sampleRate"]) + print(f"[DEBUG] Audio URL(s) found, returning batched audio. Count: {len(audioUrls)}") # Return empty video object when only audio is present (prevents errors in downstream nodes) emptyVideoObj = rwUtils.VideoObject("", width=0, height=0) return (audioObj, emptyVideoObj) @@ -260,7 +256,6 @@ class RunwareAudioInference: """Extract and validate parameters from kwargs""" return { "positivePrompt": kwargs.get("positivePrompt", ""), - "usePositivePrompt": kwargs.get("usePositivePrompt", True), "model": kwargs.get("model", ""), "duration": kwargs.get("duration", 30), "useDuration": kwargs.get("useDuration", True), @@ -270,7 +265,6 @@ class RunwareAudioInference: "useBitrate": kwargs.get("useBitrate", False), "channels": kwargs.get("channels", 2), "useChannels": kwargs.get("useChannels", False), - "outputType": kwargs.get("outputType", "URL"), "outputFormat": kwargs.get("outputFormat", "MP3"), "negativePrompt": kwargs.get("negativePrompt", ""), "numberResults": kwargs.get("numberResults", 1), @@ -310,7 +304,7 @@ class RunwareAudioInference: "taskType": "audioInference", "taskUUID": taskUuid, "model": params["model"], - "outputType": params["outputType"], + "outputType": "URL", "outputFormat": params["outputFormat"], "deliveryMethod": "async", "includeCost": True, @@ -338,8 +332,8 @@ class RunwareAudioInference: params["useDuration"] = False print(f"[DEBUG] Disabled duration because sections are provided") - # Add prompts conditionally - if params["usePositivePrompt"]: + # Add positivePrompt only when non-empty + if len(params["positivePrompt"]) > 0: genConfig[0]["positivePrompt"] = params["positivePrompt"] print(f"[DEBUG] Added positivePrompt: '{params['positivePrompt']}'") else: @@ -447,26 +441,68 @@ class RunwareAudioInference: if not hasAudio and not hasVideo: raise Exception("No audio or video data received from API") - def _extractAudioUrl(self, genResult): - """Extract audio URL from API response. Returns None if no audio URL is present (e.g., when only video is returned).""" - audioData = genResult["data"][0] - - audioUrl = audioData.get("audioURL", "") - if not audioUrl: - audioUrl = audioData.get("audioDataURI", "") - if not audioUrl: - audioUrl = audioData.get("audioBase64Data", "") - + def _extractAudioUrls(self, genResult): + """Extract all available audio URLs from API response.""" + audioUrls = [] + for item in genResult.get("data", []): + audioUrl = item.get("audioURL", "") or item.get("audioDataURI", "") or item.get("audioBase64Data", "") + if audioUrl: + audioUrls.append(audioUrl) + return audioUrls - return audioUrl if audioUrl else None + def _mergeAudioObjects(self, audioObjects, targetSampleRate): + """Merge multiple audio objects into a single batched audio object.""" + if not audioObjects: + return { + "waveform": torch.zeros((1, 1, 1)), + "sample_rate": targetSampleRate, + "format": "wav" + } - def _downloadAndProcessAudio(self, audioUrl, targetSampleRate): + waveforms = [] + maxChannels = 0 + maxSamples = 0 + + for audioObj in audioObjects: + waveform = audioObj["waveform"] + if waveform.dim() == 2: + waveform = waveform.unsqueeze(0) + waveforms.append(waveform) + maxChannels = max(maxChannels, waveform.shape[1]) + maxSamples = max(maxSamples, waveform.shape[2]) + + paddedWaveforms = [] + for waveform in waveforms: + batchSize, channels, samples = waveform.shape + + if channels < maxChannels: + channelPadding = torch.zeros( + (batchSize, maxChannels - channels, samples), + dtype=waveform.dtype, + device=waveform.device + ) + waveform = torch.cat([waveform, channelPadding], dim=1) + + if samples < maxSamples: + waveform = torch.nn.functional.pad(waveform, (0, maxSamples - samples)) + + paddedWaveforms.append(waveform) + + outputFormat = audioObjects[0].get("format", "wav") + return { + "waveform": torch.cat(paddedWaveforms, dim=0), + "sample_rate": targetSampleRate, + "format": outputFormat + } + + def _downloadAndProcessAudio(self, audioUrl, targetSampleRate, outputFormat): """Download audio file and process it for ComfyUI""" try: response = requests.get(audioUrl, timeout=30) response.raise_for_status() - - with tempfile.NamedTemporaryFile(suffix='.mp3', delete=False) as tempFile: + + tempSuffix = self._inferAudioSuffix(audioUrl, outputFormat) + with tempfile.NamedTemporaryFile(suffix=tempSuffix, delete=False) as tempFile: tempFile.write(response.content) tempFilePath = tempFile.name @@ -477,7 +513,8 @@ class RunwareAudioInference: return { "waveform": waveformTensor, - "sample_rate": targetSampleRate + "sample_rate": targetSampleRate, + "format": tempSuffix.lstrip(".") } finally: if os.path.exists(tempFilePath): @@ -485,6 +522,23 @@ class RunwareAudioInference: except Exception as e: raise Exception(f"Failed to download audio: {e}") + def _inferAudioSuffix(self, audioUrl, outputFormat): + """Infer temporary audio file extension from URL or requested format.""" + parsedPath = urlparse(audioUrl).path if audioUrl else "" + ext = os.path.splitext(parsedPath)[1].lower() + allowed = {".mp3", ".mp4", ".wav", ".flac", ".ogg"} + if ext in allowed: + return ext + + formatMap = { + "MP3": ".mp3", + "MP4": ".mp4", + "WAV": ".wav", + "FLAC": ".flac", + "OGG": ".ogg", + } + return formatMap.get(str(outputFormat).upper(), ".wav") + def _loadAudioFile(self, filePath): """Load audio file using librosa""" waveformNp, originalSampleRate = librosa.load(filePath, sr=None, mono=False) diff --git a/modules/audioModelSearch.py b/modules/audioModelSearch.py index 0207b14..ae6f004 100644 --- a/modules/audioModelSearch.py +++ b/modules/audioModelSearch.py @@ -40,6 +40,7 @@ class RunwareAudioModelSearch: "minimax:music@2.6 (MiniMax Music 2.6)", ], "Inworld": [ + "inworld:tts@2 (Inworld Realtime TTS 2)", "inworld:tts@1.5-mini (Inworld TTS-1.5 Mini)", "inworld:tts@1.5-max (Inworld TTS-1.5 Max)", ], diff --git a/modules/settings.py b/modules/settings.py index 73fa519..7f2f31f 100644 --- a/modules/settings.py +++ b/modules/settings.py @@ -2,7 +2,8 @@ Runware Image Inference Settings (registered as Runware Settings for workflow compatibility). Provides settings for image generation including temperature, systemPrompt, topP, layers, quality, background, style, search, promptExtend, editRegions, thinking (boolean), -thinkingLevel (high/medium/low), sequential, and colorPalette +thinkingLevel (high/medium/low), sequential, renderingSpeed (TURBO/DEFAULT/QUALITY), +magicPrompt (AUTO/ON/OFF), and colorPalette (from Runware Image Inference Settings Color Palette). """ @@ -155,6 +156,22 @@ class RunwareSettings: "label_on": "true", "label_off": "false", }), + "useRenderingSpeed": ("BOOLEAN", { + "tooltip": "Enable to include renderingSpeed in settings.", + "default": False, + }), + "renderingSpeed": (["TURBO", "DEFAULT", "QUALITY"], { + "default": "DEFAULT", + "tooltip": "The rendering speed to use. Only used when 'Use Rendering Speed' is enabled.", + }), + "useMagicPrompt": ("BOOLEAN", { + "tooltip": "Enable to include magicPrompt in settings.", + "default": False, + }), + "magicPrompt": (["AUTO", "ON", "OFF"], { + "default": "AUTO", + "tooltip": "Determine if MagicPrompt should be used. Only used when 'Use Magic Prompt' is enabled.", + }), } } @@ -164,7 +181,8 @@ class RunwareSettings: CATEGORY = "Runware" 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, and optional colorPalette from the Color Palette node." + "promptExtend, editRegions (JSON), thinking (boolean), thinkingLevel (high/medium/low), sequential, " + "renderingSpeed (TURBO/DEFAULT/QUALITY), magicPrompt (AUTO/ON/OFF), and optional colorPalette from the Color Palette node." ) def createSettings(self, **kwargs) -> tuple[Dict[str, Any]]: @@ -186,6 +204,8 @@ class RunwareSettings: useThinking = kwargs.get("useThinking", False) useThinkingLevel = kwargs.get("useThinkingLevel", False) useSequential = kwargs.get("useSequential", False) + useRenderingSpeed = kwargs.get("useRenderingSpeed", False) + useMagicPrompt = kwargs.get("useMagicPrompt", False) # Get value parameters temperature = kwargs.get("temperature", 1.0) @@ -196,6 +216,8 @@ class RunwareSettings: quality = kwargs.get("quality", "medium") background = kwargs.get("background", "auto") style = kwargs.get("style", "auto") + renderingSpeed = kwargs.get("renderingSpeed", "DEFAULT") + magicPrompt = kwargs.get("magicPrompt", "AUTO") # Build settings dictionary - only include what is enabled settings: Dict[str, Any] = {} @@ -246,7 +268,10 @@ class RunwareSettings: if palette is not None and isinstance(palette, list) and len(palette) > 0: settings["colorPalette"] = palette - + if useRenderingSpeed: + settings["renderingSpeed"] = str(renderingSpeed) + if useMagicPrompt: + settings["magicPrompt"] = str(magicPrompt) # Clean up None values settings = {k: v for k, v in settings.items() if v is not None} diff --git a/modules/textInference.py b/modules/textInference.py index 2d514fe..3ab898a 100644 --- a/modules/textInference.py +++ b/modules/textInference.py @@ -25,6 +25,12 @@ class RunwareTextInference: "providerSettings": ("RUNWAREPROVIDERSETTINGS", { "tooltip": "Optional provider-specific settings (wrapped by provider prefix from the model AIR).", }), + "toolChoice": ("RUNWARETEXTINFERENCETOOLCHOICE", { + "tooltip": "Connect Runware Text Inference Tool Choice for tool-calling strategy (auto / any / tool / none).", + }), + "inputs": ("RUNWARETEXTINFERENCEINPUTS", { + "tooltip": "Connect Runware Text Inference Inputs for inputs.images / inputs.videos (URLs or mediaUUIDs).", + }), }, } @@ -32,7 +38,7 @@ class RunwareTextInference: RETURN_NAMES = ("text",) FUNCTION = "infer_text" CATEGORY = "Runware/Text" - DESCRIPTION = "Run text/chat inference using Runware textInference. Connect Text Model and Text Inference Messages." + DESCRIPTION = "Run text/chat inference using Runware textInference. Connect Text Model and Text Inference Messages. Optional: Settings, provider settings, tool choice, inputs (images/videos)." @staticmethod def _extract_text_from_data_item(item: Dict[str, Any]) -> Optional[str]: @@ -70,6 +76,8 @@ class RunwareTextInference: messages: List[Dict[str, str]] = kwargs.get("messages") or [] text_settings = kwargs.get("settings") provider_settings = kwargs.get("providerSettings") + tool_choice = kwargs.get("toolChoice") + text_inputs = kwargs.get("inputs") if not model: raise Exception("model is empty.") @@ -94,6 +102,12 @@ class RunwareTextInference: provider_name = model.split(":")[0] if ":" in model else model task["providerSettings"] = {provider_name: provider_settings} + if tool_choice is not None and isinstance(tool_choice, dict) and len(tool_choice) > 0: + task["toolChoice"] = tool_choice + + if text_inputs is not None and isinstance(text_inputs, dict) and len(text_inputs) > 0: + task["inputs"] = dict(text_inputs) + gen_config = [task] print(f"[Runware Text Inference] Request: {rwUtils.safe_json_dumps(gen_config, indent=2)}") diff --git a/modules/textInferenceInputs.py b/modules/textInferenceInputs.py new file mode 100644 index 0000000..923e4c7 --- /dev/null +++ b/modules/textInferenceInputs.py @@ -0,0 +1,52 @@ +"""Runware Text Inference Inputs — merge Images + Videos blocks into task.inputs for textInference.""" + +from typing import Any, Dict, Tuple + + +class RunwareTextInferenceInputs: + """Merge Runware Text Inference Inputs Images and/or Videos into a single inputs dict.""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": {}, + "optional": { + "images": ("RUNWARETEXTINFERENCEINPUTSIMAGES", { + "tooltip": "Connect Runware Text Inference Inputs Images for inputs.images[].", + }), + "videos": ("RUNWARETEXTINFERENCEINPUTSVIDEOS", { + "tooltip": "Connect Runware Text Inference Inputs Videos for inputs.videos[].", + }), + }, + } + + RETURN_TYPES = ("RUNWARETEXTINFERENCEINPUTS",) + RETURN_NAMES = ("inputs",) + FUNCTION = "merge_inputs" + CATEGORY = "Runware/Text" + DESCRIPTION = ( + "Combine Runware Text Inference Inputs Images and/or Videos into inputs for Runware Text Inference " + "(inputs.images, inputs.videos)." + ) + + def merge_inputs(self, **kwargs) -> Tuple[Dict[str, Any], ...]: + images_block = kwargs.get("images") + videos_block = kwargs.get("videos") + + out: Dict[str, Any] = {} + + if isinstance(images_block, dict) and images_block.get("images"): + imgs = images_block["images"] + if isinstance(imgs, list) and len(imgs) > 0: + filtered_imgs = [str(x).strip() for x in imgs if x is not None and str(x).strip()] + if filtered_imgs: + out["images"] = filtered_imgs + + if isinstance(videos_block, dict) and videos_block.get("videos"): + vids = videos_block["videos"] + if isinstance(vids, list) and len(vids) > 0: + filtered_vids = [str(x).strip() for x in vids if x is not None and str(x).strip()] + if filtered_vids: + out["videos"] = filtered_vids + + return (out,) diff --git a/modules/textInferenceInputsImages.py b/modules/textInferenceInputsImages.py new file mode 100644 index 0000000..6d44a83 --- /dev/null +++ b/modules/textInferenceInputsImages.py @@ -0,0 +1,50 @@ +"""Runware Text Inference Inputs Images — builds inputs.images from ComfyUI IMAGE tensors.""" + +from typing import Any, Dict, List, Tuple + +from .utils import runwareUtils as rwUtils + + +class RunwareTextInferenceInputsImages: + """Collect IMAGE tensors and convert them for textInference task.inputs.images. + + Each connected IMAGE is converted via runwareUtils.convertTensor2IMG, which returns + either a cached mediaUUID (when caching is enabled) or a base64 data URI. + """ + + _MAX_IMAGES = 8 + + @classmethod + def INPUT_TYPES(cls): + optional: Dict[str, tuple] = {} + for i in range(1, cls._MAX_IMAGES + 1): + ordinal = rwUtils.getOrdinal(i) + optional[f"Image {i}"] = ("IMAGE", { + "tooltip": ( + f"{ordinal.capitalize()} image for the model. Connect a Load Image (or any IMAGE source) node. " + "Tensor is converted to base64 / mediaUUID before being sent to the API." + ), + }) + return {"required": {}, "optional": optional} + + RETURN_TYPES = ("RUNWARETEXTINFERENCEINPUTSIMAGES",) + RETURN_NAMES = ("images",) + FUNCTION = "createImages" + CATEGORY = "Runware/Text" + DESCRIPTION = ( + "Collect one or more IMAGE tensors (Load Image, generated images, etc.) for Runware Text Inference " + "inputs.images. Connect the output to Runware Text Inference Inputs." + ) + + def createImages(self, **kwargs) -> Tuple[Dict[str, Any], ...]: + urls: List[str] = [] + for i in range(1, self._MAX_IMAGES + 1): + tensor = kwargs.get(f"Image {i}", None) + if tensor is None: + continue + urls.append(rwUtils.convertTensor2IMG(tensor)) + + if not urls: + return ({},) + + return ({"images": urls},) diff --git a/modules/textInferenceInputsVideos.py b/modules/textInferenceInputsVideos.py new file mode 100644 index 0000000..13680dd --- /dev/null +++ b/modules/textInferenceInputsVideos.py @@ -0,0 +1,51 @@ +"""Runware Text Inference Inputs Videos — builds inputs.videos (URL or mediaUUID per item).""" + +from typing import Any, Dict, List, Tuple + +from .utils import runwareUtils as rwUtils + + +class RunwareTextInferenceInputsVideos: + """Collect video references for textInference task.inputs.videos.""" + + _MAX_VIDEOS = 4 + + @classmethod + def INPUT_TYPES(cls): + optional: Dict[str, tuple] = { + "useVideos": ("BOOLEAN", { + "default": False, + "tooltip": "Enable to include inputs.videos in the text inference request.", + }), + } + for i in range(1, cls._MAX_VIDEOS + 1): + ordinal = rwUtils.getOrdinal(i) + optional[f"Video{i}"] = ("STRING", { + "default": "", + "tooltip": f"Video URL or mediaUUID for the {ordinal} video. Only used when 'Use Videos' is enabled.", + }) + return {"required": {}, "optional": optional} + + RETURN_TYPES = ("RUNWARETEXTINFERENCEINPUTSVIDEOS",) + RETURN_NAMES = ("videos",) + FUNCTION = "createVideos" + CATEGORY = "Runware/Text" + DESCRIPTION = ( + "Collect one or more video references (URL or mediaUUID) for Runware Text Inference " + "inputs.videos. Connect the output to Runware Text Inference Inputs." + ) + + def createVideos(self, **kwargs) -> Tuple[Dict[str, Any], ...]: + if not kwargs.get("useVideos", False): + return ({},) + + urls: List[str] = [] + for i in range(1, self._MAX_VIDEOS + 1): + raw = kwargs.get(f"Video{i}", "") + if raw is not None and str(raw).strip(): + urls.append(str(raw).strip()) + + if not urls: + return ({},) + + return ({"videos": urls},) diff --git a/modules/textInferenceSettings.py b/modules/textInferenceSettings.py index aed5a30..0aa93f6 100644 --- a/modules/textInferenceSettings.py +++ b/modules/textInferenceSettings.py @@ -70,9 +70,13 @@ class RunwareTextInferenceSettings: "tooltip": f"Top-k sampling (1–{cls._MAX_TOP_K}).", }), # Keep this in the original slot to avoid shifting legacy widgets_values. - "thinkingLevel": (["none", "low", "medium", "high"], { + "thinkingLevel": (["none", "low", "medium", "high", "max", "adaptive"], { "default": "none", - "tooltip": "Extended reasoning level: none, low, medium, high.", + "tooltip": ( + "Extended reasoning level: none, low, medium, high, max, adaptive. " + "none -> disabled. low/medium/high/max -> adaptive thinking with the chosen effort. " + "adaptive -> model picks budget. Legacy 'off' is mapped to 'none'." + ), }), "useStopSequences": ("BOOLEAN", { "default": False, @@ -114,14 +118,8 @@ class RunwareTextInferenceSettings: "default": "[]", "tooltip": "JSON array for tools.", }), - "useToolChoice": ("BOOLEAN", { - "default": False, - "tooltip": "Include toolChoice in settings.", - }), - "toolChoice": ("STRING", { - "multiline": True, - "default": "{}", - "tooltip": "JSON object for toolChoice.", + "cache": ("RUNWARETEXTINFERENCESETTINGSCACHE", { + "tooltip": "Connect Runware Text Inference Settings Cache for settings.cache.{scope, ttl}.", }), }, } @@ -132,8 +130,8 @@ class RunwareTextInferenceSettings: CATEGORY = "Runware/Text" DESCRIPTION = ( "Optional text inference settings (systemPrompt, maxTokens, temperature, topP, topK, " - "stopSequences, presencePenalty, frequencyPenalty, tools, toolChoice, thinkingLevel). " - "Connect to Runware Text Inference." + "stopSequences, presencePenalty, frequencyPenalty, tools, thinkingLevel, cache). " + "Connect to Runware Text Inference. For tool calling, use Runware Text Inference Tool Choice on the inference node." ) @staticmethod @@ -191,7 +189,7 @@ class RunwareTextInferenceSettings: "minimal": "low", } value = aliases.get(value, value) - if value not in ("none", "low", "medium", "high"): + if value not in ("none", "low", "medium", "high", "max", "adaptive"): return "none" return value @@ -233,15 +231,14 @@ class RunwareTextInferenceSettings: if parsed_tools: out["tools"] = parsed_tools - if kwargs.get("useToolChoice", False): - parsed_choice = self._parse_json_setting(kwargs.get("toolChoice"), dict, "toolChoice") - if parsed_choice: - out["toolChoice"] = parsed_choice - tl = self._normalize_thinking_level(kwargs.get("thinkingLevel", "none")) if tl != "none": out["thinkingLevel"] = tl + cache_cfg = kwargs.get("cache") + if cache_cfg is not None and isinstance(cache_cfg, dict) and len(cache_cfg) > 0: + out["cache"] = cache_cfg + if not out: return ({},) diff --git a/modules/textInferenceSettingsCache.py b/modules/textInferenceSettingsCache.py new file mode 100644 index 0000000..f5b91b1 --- /dev/null +++ b/modules/textInferenceSettingsCache.py @@ -0,0 +1,64 @@ +""" +Runware Text Inference Settings Cache Node +Builds the cache object for Runware Text Inference (settings.cache.{scope, ttl}). + +Maps cache_control placement (Anthropic): + - scope: where cache_control is attached + * system -> system block only + * system+history -> system block + last user message block (default) + - ttl: cache lifetime (5m or 1h) +""" + +from typing import Dict, Any + + +class RunwareTextInferenceSettingsCache: + """Runware Text Inference Settings Cache node.""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "scope": (["system", "system+history"], { + "default": "system+history", + "tooltip": ( + "Where cache_control is attached.\n" + "- system: cache the system block only.\n" + "- system+history: cache the system block and the last user message block." + ), + }), + "ttl": (["5m", "1h"], { + "default": "5m", + "tooltip": "Cache lifetime: 5m (5 minutes) or 1h (1 hour).", + }), + } + } + + RETURN_TYPES = ("RUNWARETEXTINFERENCESETTINGSCACHE",) + RETURN_NAMES = ("cache",) + FUNCTION = "createCache" + CATEGORY = "Runware/Text" + DESCRIPTION = ( + "Build a settings.cache object (scope + ttl) and connect it to Runware Text Inference. " + "Merged into the request as settings.cache.{scope, ttl}." + ) + + def createCache(self, **kwargs) -> tuple[Dict[str, Any]]: + scope = (kwargs.get("scope") or "system+history").strip().lower() + if scope not in ("system", "system+history"): + scope = "system+history" + + ttl = (kwargs.get("ttl") or "5m").strip().lower() + if ttl not in ("5m", "1h"): + ttl = "5m" + + return ({"scope": scope, "ttl": ttl},) + + +NODE_CLASS_MAPPINGS = { + "RunwareTextInferenceSettingsCache": RunwareTextInferenceSettingsCache, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "RunwareTextInferenceSettingsCache": "Runware Text Inference Settings Cache", +} diff --git a/modules/textInferenceToolChoice.py b/modules/textInferenceToolChoice.py new file mode 100644 index 0000000..3e33656 --- /dev/null +++ b/modules/textInferenceToolChoice.py @@ -0,0 +1,73 @@ +""" +Runware Text Inference Tool Choice Node +Builds the toolChoice object for Runware Text Inference (textInference). + +Strategies (type): + - auto: model decides whether to call a tool (recommended default) + - any: model must call at least one tool but chooses which one + - tool: model must call the specific tool identified by `name` + - none: model will not call any tool +""" + +from typing import Dict, Any + + +class RunwareTextInferenceToolChoice: + """Runware Text Inference Tool Choice node.""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "type": (["auto", "any", "tool", "none"], { + "default": "auto", + "tooltip": ( + "Strategy the model uses to decide when and which tools to call.\n" + "- auto: model decides (recommended default).\n" + "- any: model must call at least one tool, of its choosing.\n" + "- tool: model must call the specific tool named below.\n" + "- none: model will not call any tool." + ), + }), + "name": ("STRING", { + "default": "", + "tooltip": "Name of the specific tool the model must call. Required when type is 'tool'; ignored otherwise.", + }), + } + } + + RETURN_TYPES = ("RUNWARETEXTINFERENCETOOLCHOICE",) + RETURN_NAMES = ("toolChoice",) + FUNCTION = "createToolChoice" + CATEGORY = "Runware/Text" + DESCRIPTION = ( + "Build a toolChoice object (type + optional tool name) and connect it to " + "Runware Text Inference. Required when type is 'tool'." + ) + + def createToolChoice(self, **kwargs) -> tuple[Dict[str, Any]]: + choice_type = (kwargs.get("type") or "auto").strip().lower() + if choice_type not in ("auto", "any", "tool", "none"): + choice_type = "auto" + + name = (kwargs.get("name") or "").strip() + + result: Dict[str, Any] = {"type": choice_type} + if choice_type == "tool": + if not name: + raise Exception( + "Tool Choice: 'name' is required when type is 'tool'. " + "Provide the exact tool name the model must call." + ) + result["name"] = name + + return (result,) + + +NODE_CLASS_MAPPINGS = { + "RunwareTextInferenceToolChoice": RunwareTextInferenceToolChoice, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "RunwareTextInferenceToolChoice": "Runware Text Inference Tool Choice", +} diff --git a/modules/textModelSearch.py b/modules/textModelSearch.py index d81e7ed..53b38cb 100644 --- a/modules/textModelSearch.py +++ b/modules/textModelSearch.py @@ -13,9 +13,17 @@ class RunwareTextModelSearch: "openai:gpt@5.4-mini (GPT-5.4 Mini)", "openai:gpt@5.4-nano (GPT-5.4 Nano)", ], + "Anthropic": [ + "anthropic:claude@opus-4.7 (Claude Opus 4.7)", + "anthropic:claude@sonnet-4.6 (Claude Sonnet 4.6)", + "anthropic:claude@haiku-4.5 (Claude Haiku 4.5)", + ], "Google": [ "google:gemini@3-flash (Gemini 3 Flash)", ], + "MoonshotAI": [ + "moonshotai:kimi@k2.6 (Kimi K2.6)", + ], "Zai": [ "zai-glm-5-1 (ZAI GLM 5.1)", ], @@ -23,8 +31,10 @@ class RunwareTextModelSearch: MODEL_PROVIDERS = [ "All", + "Anthropic", "Google", "MiniMax", + "MoonshotAI", "OpenAI", "Zai", ] diff --git a/workflows/Runware_Audio_Inference_Inworld_TTS_2.json b/workflows/Runware_Audio_Inference_Inworld_TTS_2.json new file mode 100644 index 0000000..2246d78 --- /dev/null +++ b/workflows/Runware_Audio_Inference_Inworld_TTS_2.json @@ -0,0 +1 @@ +{"id":"63c5d240-4ae4-4bbc-9ac5-6987dc62e16d","revision":0,"last_node_id":4,"last_link_id":3,"nodes":[{"id":3,"type":"SaveAudio","pos":[1014.7385922796449,-134.91065284200786],"size":[270,112],"flags":{},"order":3,"mode":0,"inputs":[{"localized_name":"audio","name":"audio","type":"AUDIO","link":1},{"localized_name":"filename_prefix","name":"filename_prefix","type":"STRING","widget":{"name":"filename_prefix"},"link":null},{"localized_name":"audioUI","name":"audioUI","type":"AUDIO_UI","widget":{"name":"audioUI"},"link":null}],"outputs":[],"properties":{"cnr_id":"comfy-core","ver":"0.19.3"},"widgets_values":["audio/ComfyUI"]},{"id":2,"type":"Runware Audio Model Search","pos":[98.84826209096593,-173.39090048351736],"size":[270,130],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"Model Search","name":"Model Search","type":"STRING","widget":{"name":"Model Search"},"link":null},{"localized_name":"Model Provider","name":"Model Provider","type":"COMBO","widget":{"name":"Model Provider"},"link":null},{"localized_name":"AudioList","name":"AudioList","type":"COMBO","widget":{"name":"AudioList"},"link":null},{"localized_name":"Use Search Value","name":"Use Search Value","type":"BOOLEAN","widget":{"name":"Use Search Value"},"link":null}],"outputs":[{"localized_name":"model","name":"model","type":"RUNWAREAUDIOMODEL","links":[2]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"934b5cb7b56f417b05a73ff4e892e59a9a05dadc","Node name for S&R":"Runware Audio Model Search"},"widgets_values":["","Inworld","inworld:tts@2 (Inworld Realtime TTS 2)",false],"bgcolor":"#5345bf"},{"id":4,"type":"Runware Audio Inference Speech","pos":[10.43287294002262,67.55839196931291],"size":[400,454],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"voices","name":"voices","shape":7,"type":"RUNWAREAUDIOINFERENCESPEECHVOICES","link":null},{"localized_name":"text","name":"text","type":"STRING","widget":{"name":"text"},"link":null},{"localized_name":"useVoice","name":"useVoice","shape":7,"type":"BOOLEAN","widget":{"name":"useVoice"},"link":null},{"localized_name":"voice","name":"voice","shape":7,"type":"STRING","widget":{"name":"voice"},"link":null},{"localized_name":"useSpeed","name":"useSpeed","shape":7,"type":"BOOLEAN","widget":{"name":"useSpeed"},"link":null},{"localized_name":"speed","name":"speed","shape":7,"type":"FLOAT","widget":{"name":"speed"},"link":null},{"localized_name":"useVolume","name":"useVolume","shape":7,"type":"BOOLEAN","widget":{"name":"useVolume"},"link":null},{"localized_name":"volume","name":"volume","shape":7,"type":"FLOAT","widget":{"name":"volume"},"link":null},{"localized_name":"usePitch","name":"usePitch","shape":7,"type":"BOOLEAN","widget":{"name":"usePitch"},"link":null},{"localized_name":"pitch","name":"pitch","shape":7,"type":"FLOAT","widget":{"name":"pitch"},"link":null},{"localized_name":"useEmotion","name":"useEmotion","shape":7,"type":"BOOLEAN","widget":{"name":"useEmotion"},"link":null},{"localized_name":"emotion","name":"emotion","shape":7,"type":"COMBO","widget":{"name":"emotion"},"link":null},{"localized_name":"useTone","name":"useTone","shape":7,"type":"BOOLEAN","widget":{"name":"useTone"},"link":null},{"localized_name":"tone","name":"tone","shape":7,"type":"STRING","widget":{"name":"tone"},"link":null},{"localized_name":"useLanguage","name":"useLanguage","shape":7,"type":"BOOLEAN","widget":{"name":"useLanguage"},"link":null},{"localized_name":"language","name":"language","shape":7,"type":"STRING","widget":{"name":"language"},"link":null}],"outputs":[{"localized_name":"speech","name":"speech","type":"RUNWARESPEECH","links":[3]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"934b5cb7b56f417b05a73ff4e892e59a9a05dadc","Node name for S&R":"Runware Audio Inference Speech"},"widgets_values":["[speak quickly with a clear and direct manner] Your confirmation number is 8 4 7 2 9. Your order will arrive by FRIDAY at 3:45 PM. Total charged: forty-nine ninety-nine.",true,"Alex",true,1,false,1,false,0,false,"calm",false,"",false,"Auto"],"bgcolor":"#5345bf"},{"id":1,"type":"Runware Audio Inference","pos":[456.4160686947394,-174.43748067219647],"size":[400,746],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"model","name":"model","type":"RUNWAREAUDIOMODEL","link":2},{"localized_name":"inputs","name":"inputs","shape":7,"type":"RUNWAREAUDIOINFERENCEINPUTS","link":null},{"localized_name":"settings","name":"settings","shape":7,"type":"RUNWAREAUDIOSETTINGS","link":null},{"localized_name":"speech","name":"speech","shape":7,"type":"RUNWARESPEECH","link":3},{"localized_name":"voiceModify","name":"voiceModify","shape":7,"type":"RUNWAREVOICEMODIFY","link":null},{"localized_name":"providerSettings","name":"providerSettings","shape":7,"type":"RUNWAREPROVIDERSETTINGS","link":null},{"localized_name":"usePositivePrompt","name":"usePositivePrompt","type":"BOOLEAN","widget":{"name":"usePositivePrompt"},"link":null},{"localized_name":"positivePrompt","name":"positivePrompt","type":"STRING","widget":{"name":"positivePrompt"},"link":null},{"localized_name":"negativePrompt","name":"negativePrompt","type":"STRING","widget":{"name":"negativePrompt"},"link":null},{"localized_name":"useDuration","name":"useDuration","type":"BOOLEAN","widget":{"name":"useDuration"},"link":null},{"localized_name":"duration","name":"duration","type":"INT","widget":{"name":"duration"},"link":null},{"localized_name":"useSampleRate","name":"useSampleRate","type":"BOOLEAN","widget":{"name":"useSampleRate"},"link":null},{"localized_name":"sampleRate","name":"sampleRate","type":"COMBO","widget":{"name":"sampleRate"},"link":null},{"localized_name":"useBitrate","name":"useBitrate","type":"BOOLEAN","widget":{"name":"useBitrate"},"link":null},{"localized_name":"bitrate","name":"bitrate","type":"COMBO","widget":{"name":"bitrate"},"link":null},{"localized_name":"useChannels","name":"useChannels","type":"BOOLEAN","widget":{"name":"useChannels"},"link":null},{"localized_name":"channels","name":"channels","type":"INT","widget":{"name":"channels"},"link":null},{"localized_name":"outputType","name":"outputType","type":"COMBO","widget":{"name":"outputType"},"link":null},{"localized_name":"outputFormat","name":"outputFormat","type":"COMBO","widget":{"name":"outputFormat"},"link":null},{"localized_name":"numberResults","name":"numberResults","type":"INT","widget":{"name":"numberResults"},"link":null},{"localized_name":"useSeed","name":"useSeed","type":"BOOLEAN","widget":{"name":"useSeed"},"link":null},{"localized_name":"seed","name":"seed","type":"INT","widget":{"name":"seed"},"link":null},{"localized_name":"useSteps","name":"useSteps","type":"BOOLEAN","widget":{"name":"useSteps"},"link":null},{"localized_name":"steps","name":"steps","type":"INT","widget":{"name":"steps"},"link":null},{"localized_name":"useStrength","name":"useStrength","type":"BOOLEAN","widget":{"name":"useStrength"},"link":null},{"localized_name":"strength","name":"strength","type":"FLOAT","widget":{"name":"strength"},"link":null},{"localized_name":"useCFGScale","name":"useCFGScale","type":"BOOLEAN","widget":{"name":"useCFGScale"},"link":null},{"localized_name":"CFGScale","name":"CFGScale","type":"FLOAT","widget":{"name":"CFGScale"},"link":null}],"outputs":[{"localized_name":"audio","name":"audio","type":"AUDIO","links":[1]},{"localized_name":"video","name":"video","type":"VIDEO","links":null}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"934b5cb7b56f417b05a73ff4e892e59a9a05dadc","Node name for S&R":"Runware Audio Inference"},"widgets_values":[false,"classical piano piece, gentle and melodic","",false,10,false,32000,false,128,false,2,"URL","MP3",1,false,335374904,"randomize",false,20,false,0.8,false,12],"bgcolor":"#5345bf"}],"links":[[1,1,0,3,0,"AUDIO"],[2,2,0,1,0,"RUNWAREAUDIOMODEL"],[3,4,0,1,3,"RUNWARESPEECH"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.53,"offset":[625.7331058335624,492.72167878540404]}},"version":0.4} \ No newline at end of file diff --git a/workflows/Runware_Image_Inference_Ideogram_Layerize.json b/workflows/Runware_Image_Inference_Ideogram_Layerize.json new file mode 100644 index 0000000..f680c2f --- /dev/null +++ b/workflows/Runware_Image_Inference_Ideogram_Layerize.json @@ -0,0 +1 @@ +{"id":"abef544d-825f-4e9e-9da8-130dc6640a61","revision":0,"last_node_id":4,"last_link_id":3,"nodes":[{"id":2,"type":"Runware Save Image","pos":[1051.8684715878496,-95.69795800669296],"size":[270,318],"flags":{},"order":3,"mode":0,"inputs":[{"localized_name":"Images","name":"Images","type":"STRING","widget":{"name":"Images"},"link":1},{"localized_name":"filenamePrefix","name":"filenamePrefix","type":"STRING","widget":{"name":"filenamePrefix"},"link":null},{"localized_name":"saveImage","name":"saveImage","type":"BOOLEAN","widget":{"name":"saveImage"},"link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":null}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"44d583a6eb364d372620e5a95c15ce7f84511ac2","Node name for S&R":"Runware Save Image"},"widgets_values":["","ComfyUI",true],"bgcolor":"#5345bf"},{"id":3,"type":"Runware Model Search","pos":[-126.68251953827148,-21.87869500285009],"size":[272.111328125,154],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"Model Search","name":"Model Search","type":"STRING","widget":{"name":"Model Search"},"link":null},{"localized_name":"Model Architecture","name":"Model Architecture","type":"COMBO","widget":{"name":"Model Architecture"},"link":null},{"localized_name":"ModelType","name":"ModelType","type":"COMBO","widget":{"name":"ModelType"},"link":null},{"localized_name":"ModelList","name":"ModelList","type":"COMBO","widget":{"name":"ModelList"},"link":null},{"localized_name":"Use Search Value","name":"Use Search Value","type":"BOOLEAN","widget":{"name":"Use Search Value"},"link":null}],"outputs":[{"localized_name":"Runware Model","name":"Runware Model","type":"RUNWAREMODEL","links":[2]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"44d583a6eb364d372620e5a95c15ce7f84511ac2","Node name for S&R":"Runware Model Search"},"widgets_values":["ideogram:4@1","All","Base Model","ideogram:4@1 (Ideogram 3.0 3.0)",true],"bgcolor":"#5345bf"},{"id":4,"type":"Runware Settings","pos":[174.340592440895,-60.947705419516524],"size":[400,862],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"colorPalette","name":"colorPalette","shape":7,"type":"RUNWAREIMAGEINFERENCECOLORPALETTE","link":null},{"localized_name":"useTemperature","name":"useTemperature","shape":7,"type":"BOOLEAN","widget":{"name":"useTemperature"},"link":null},{"localized_name":"temperature","name":"temperature","shape":7,"type":"FLOAT","widget":{"name":"temperature"},"link":null},{"localized_name":"useSystemPrompt","name":"useSystemPrompt","shape":7,"type":"BOOLEAN","widget":{"name":"useSystemPrompt"},"link":null},{"localized_name":"systemPrompt","name":"systemPrompt","shape":7,"type":"STRING","widget":{"name":"systemPrompt"},"link":null},{"localized_name":"useTopP","name":"useTopP","shape":7,"type":"BOOLEAN","widget":{"name":"useTopP"},"link":null},{"localized_name":"topP","name":"topP","shape":7,"type":"FLOAT","widget":{"name":"topP"},"link":null},{"localized_name":"useLayers","name":"useLayers","shape":7,"type":"BOOLEAN","widget":{"name":"useLayers"},"link":null},{"localized_name":"layers","name":"layers","shape":7,"type":"INT","widget":{"name":"layers"},"link":null},{"localized_name":"useTrueCFGScale","name":"useTrueCFGScale","shape":7,"type":"BOOLEAN","widget":{"name":"useTrueCFGScale"},"link":null},{"localized_name":"trueCFGScale","name":"trueCFGScale","shape":7,"type":"FLOAT","widget":{"name":"trueCFGScale"},"link":null},{"localized_name":"useQuality","name":"useQuality","shape":7,"type":"BOOLEAN","widget":{"name":"useQuality"},"link":null},{"localized_name":"quality","name":"quality","shape":7,"type":"COMBO","widget":{"name":"quality"},"link":null},{"localized_name":"usePromptExtend","name":"usePromptExtend","shape":7,"type":"BOOLEAN","widget":{"name":"usePromptExtend"},"link":null},{"localized_name":"promptExtend","name":"promptExtend","shape":7,"type":"BOOLEAN","widget":{"name":"promptExtend"},"link":null},{"localized_name":"useEditRegions","name":"useEditRegions","shape":7,"type":"BOOLEAN","widget":{"name":"useEditRegions"},"link":null},{"localized_name":"editRegions","name":"editRegions","shape":7,"type":"STRING","widget":{"name":"editRegions"},"link":null},{"localized_name":"useThinking","name":"useThinking","shape":7,"type":"BOOLEAN","widget":{"name":"useThinking"},"link":null},{"localized_name":"thinking","name":"thinking","shape":7,"type":"BOOLEAN","widget":{"name":"thinking"},"link":null},{"localized_name":"useSequential","name":"useSequential","shape":7,"type":"BOOLEAN","widget":{"name":"useSequential"},"link":null},{"localized_name":"sequential","name":"sequential","shape":7,"type":"BOOLEAN","widget":{"name":"sequential"},"link":null},{"localized_name":"useThinkingLevel","name":"useThinkingLevel","shape":7,"type":"BOOLEAN","widget":{"name":"useThinkingLevel"},"link":null},{"localized_name":"thinkingLevel","name":"thinkingLevel","shape":7,"type":"COMBO","widget":{"name":"thinkingLevel"},"link":null},{"localized_name":"useBackground","name":"useBackground","shape":7,"type":"BOOLEAN","widget":{"name":"useBackground"},"link":null},{"localized_name":"background","name":"background","shape":7,"type":"COMBO","widget":{"name":"background"},"link":null},{"localized_name":"useStyle","name":"useStyle","shape":7,"type":"BOOLEAN","widget":{"name":"useStyle"},"link":null},{"localized_name":"style","name":"style","shape":7,"type":"STRING","widget":{"name":"style"},"link":null},{"localized_name":"useSearch","name":"useSearch","shape":7,"type":"BOOLEAN","widget":{"name":"useSearch"},"link":null},{"localized_name":"search","name":"search","shape":7,"type":"BOOLEAN","widget":{"name":"search"},"link":null},{"localized_name":"useRenderingSpeed","name":"useRenderingSpeed","shape":7,"type":"BOOLEAN","widget":{"name":"useRenderingSpeed"},"link":null},{"localized_name":"renderingSpeed","name":"renderingSpeed","shape":7,"type":"COMBO","widget":{"name":"renderingSpeed"},"link":null},{"localized_name":"useMagicPrompt","name":"useMagicPrompt","shape":7,"type":"BOOLEAN","widget":{"name":"useMagicPrompt"},"link":null},{"localized_name":"magicPrompt","name":"magicPrompt","shape":7,"type":"COMBO","widget":{"name":"magicPrompt"},"link":null}],"outputs":[{"localized_name":"Settings","name":"Settings","type":"RUNWARESETTINGS","links":[3]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"44d583a6eb364d372620e5a95c15ce7f84511ac2","Node name for S&R":"Runware Settings"},"widgets_values":[false,1,false,"",false,1,false,4,false,1,false,"medium",false,false,false,"",false,false,false,false,false,"high",false,"auto",false,"auto",false,false,true,"TURBO",true,"ON"],"bgcolor":"#5345bf"},{"id":1,"type":"Runware Image Inference","pos":[613.8994866719277,-118.46139475221767],"size":[400,1082],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"Model","name":"Model","type":"RUNWAREMODEL","link":2},{"localized_name":"Accelerator","name":"Accelerator","shape":7,"type":"RUNWAREACCELERATOR","link":null},{"localized_name":"Lora","name":"Lora","shape":7,"type":"RUNWARELORA","link":null},{"localized_name":"Outpainting","name":"Outpainting","shape":7,"type":"RUNWAREOUTPAINT","link":null},{"localized_name":"IPAdapters","name":"IPAdapters","shape":7,"type":"RUNWAREIPAdapter","link":null},{"localized_name":"ControlNet","name":"ControlNet","shape":7,"type":"RUNWARECONTROLNET","link":null},{"localized_name":"Refiner","name":"Refiner","shape":7,"type":"RUNWAREREFINER","link":null},{"localized_name":"seedImage","name":"seedImage","shape":7,"type":"IMAGE","link":null},{"localized_name":"maskImage","name":"maskImage","shape":7,"type":"MASK","link":null},{"localized_name":"Embeddings","name":"Embeddings","shape":7,"type":"RUNWAREEMBEDDING","link":null},{"localized_name":"VAE","name":"VAE","shape":7,"type":"RUNWAREVAE","link":null},{"localized_name":"referenceImages","name":"referenceImages","shape":7,"type":"RUNWAREREFERENCEIMAGES","link":null},{"localized_name":"inputs","name":"inputs","shape":7,"type":"RUNWAREIMAGEINFERENCEINPUTS","link":null},{"localized_name":"providerSettings","name":"providerSettings","shape":7,"type":"RUNWAREPROVIDERSETTINGS","link":null},{"localized_name":"safetyInputs","name":"safetyInputs","shape":7,"type":"RUNWARESAFETYINPUTS","link":null},{"localized_name":"settings","name":"settings","shape":7,"type":"RUNWARESETTINGS","link":3},{"localized_name":"ultralytics","name":"ultralytics","shape":7,"type":"RUNWAREULTRALYTICSINPUTS","link":null},{"localized_name":"advancedFeatures","name":"advancedFeatures","shape":7,"type":"RUNWAREIMAGEINFERENCEADVANCEDFEATURES","link":null},{"localized_name":"positivePrompt","name":"positivePrompt","type":"STRING","widget":{"name":"positivePrompt"},"link":null},{"localized_name":"negativePrompt","name":"negativePrompt","type":"STRING","widget":{"name":"negativePrompt"},"link":null},{"localized_name":"Multi Inference Mode","name":"Multi Inference Mode","type":"BOOLEAN","widget":{"name":"Multi Inference Mode"},"link":null},{"localized_name":"Prompt Weighting","name":"Prompt Weighting","type":"COMBO","widget":{"name":"Prompt Weighting"},"link":null},{"localized_name":"dimensions","name":"dimensions","type":"COMBO","widget":{"name":"dimensions"},"link":null},{"localized_name":"width","name":"width","type":"INT","widget":{"name":"width"},"link":null},{"localized_name":"height","name":"height","type":"INT","widget":{"name":"height"},"link":null},{"localized_name":"useResolution","name":"useResolution","type":"BOOLEAN","widget":{"name":"useResolution"},"link":null},{"localized_name":"resolution","name":"resolution","type":"COMBO","widget":{"name":"resolution"},"link":null},{"localized_name":"useSteps","name":"useSteps","type":"BOOLEAN","widget":{"name":"useSteps"},"link":null},{"localized_name":"steps","name":"steps","type":"INT","widget":{"name":"steps"},"link":null},{"localized_name":"useScheduler","name":"useScheduler","type":"BOOLEAN","widget":{"name":"useScheduler"},"link":null},{"localized_name":"scheduler","name":"scheduler","type":"COMBO","widget":{"name":"scheduler"},"link":null},{"localized_name":"useCFGScale","name":"useCFGScale","type":"BOOLEAN","widget":{"name":"useCFGScale"},"link":null},{"localized_name":"cfgScale","name":"cfgScale","type":"FLOAT","widget":{"name":"cfgScale"},"link":null},{"localized_name":"useSeed","name":"useSeed","type":"BOOLEAN","widget":{"name":"useSeed"},"link":null},{"localized_name":"seed","name":"seed","type":"INT","widget":{"name":"seed"},"link":null},{"localized_name":"useClipSkip","name":"useClipSkip","type":"BOOLEAN","widget":{"name":"useClipSkip"},"link":null},{"localized_name":"clipSkip","name":"clipSkip","type":"INT","widget":{"name":"clipSkip"},"link":null},{"localized_name":"strength","name":"strength","type":"FLOAT","widget":{"name":"strength"},"link":null},{"localized_name":"Mask Margin","name":"Mask Margin","type":"BOOLEAN","widget":{"name":"Mask Margin"},"link":null},{"localized_name":"maskMargin","name":"maskMargin","type":"INT","widget":{"name":"maskMargin"},"link":null},{"localized_name":"outputFormat","name":"outputFormat","type":"COMBO","widget":{"name":"outputFormat"},"link":null},{"localized_name":"batchSize","name":"batchSize","type":"INT","widget":{"name":"batchSize"},"link":null},{"localized_name":"acceleration","name":"acceleration","type":"COMBO","widget":{"name":"acceleration"},"link":null},{"localized_name":"outputQuality","name":"outputQuality","type":"INT","widget":{"name":"outputQuality"},"link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"STRING","links":[1]},{"localized_name":"RW-Task","name":"RW-Task","type":"RUNWARETASK","links":null}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"44d583a6eb364d372620e5a95c15ce7f84511ac2","Node name for S&R":"Runware Image Inference"},"widgets_values":["A low-poly 3D fox sitting upright, in warm orange and white geometric facets","blurry text, misspelled words, distorted letters, duplicated signage",false,"Disabled","Custom",1344,768,false,"1k",false,4,false,"Default",false,6.5,true,1730513817,"randomize",false,0,0.8,false,32,"WEBP",1,"none",95],"bgcolor":"#5345bf"}],"links":[[1,1,0,2,0,"STRING"],[2,3,0,1,0,"RUNWAREMODEL"],[3,4,0,1,15,"RUNWARESETTINGS"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.6,"offset":[488.335188809105,212.26280958618344]}},"version":0.4} \ No newline at end of file diff --git a/workflows/Runware_Text_Inference_Anthropic.json b/workflows/Runware_Text_Inference_Anthropic.json new file mode 100644 index 0000000..019f60a --- /dev/null +++ b/workflows/Runware_Text_Inference_Anthropic.json @@ -0,0 +1 @@ +{"id":"1f78f4f1-a1ae-47a1-af53-9b1aae2e4a79","revision":0,"last_node_id":15,"last_link_id":12,"nodes":[{"id":7,"type":"Runware Text Inference","pos":[443.9703710974516,-190.2246268986115],"size":[196.7369140625,126],"flags":{},"order":9,"mode":0,"inputs":[{"localized_name":"model","name":"model","type":"RUNWARETEXTMODEL","link":5},{"localized_name":"messages","name":"messages","type":"RUNWARETEXTINFERENCEMESSAGES","link":8},{"localized_name":"settings","name":"settings","shape":7,"type":"RUNWARETEXTINFERENCESETTINGS","link":7},{"localized_name":"providerSettings","name":"providerSettings","shape":7,"type":"RUNWAREPROVIDERSETTINGS","link":null},{"localized_name":"toolChoice","name":"toolChoice","shape":7,"type":"RUNWARETEXTINFERENCETOOLCHOICE","link":6},{"localized_name":"inputs","name":"inputs","shape":7,"type":"RUNWARETEXTINFERENCEINPUTS","link":11}],"outputs":[{"localized_name":"text","name":"text","type":"STRING","links":[4]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"100644fdfb3f0f8a3aacea07992e7aa9e284f17a","Node name for S&R":"Runware Text Inference"},"widgets_values":[],"bgcolor":"#5345bf"},{"id":4,"type":"Runware Text Inference Tool Choice","pos":[-29.744905656675527,30.964936780633753],"size":[339.18515625,82],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"type","name":"type","type":"COMBO","widget":{"name":"type"},"link":null},{"localized_name":"name","name":"name","type":"STRING","widget":{"name":"name"},"link":null}],"outputs":[{"localized_name":"toolChoice","name":"toolChoice","type":"RUNWARETEXTINFERENCETOOLCHOICE","links":[6]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"100644fdfb3f0f8a3aacea07992e7aa9e284f17a","Node name for S&R":"Runware Text Inference Tool Choice"},"widgets_values":["auto",""],"bgcolor":"#5345bf"},{"id":5,"type":"Runware Text Inference Settings Cache","pos":[-67.48363833651044,201.55750753535082],"size":[361.0806640625,82],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"scope","name":"scope","type":"COMBO","widget":{"name":"scope"},"link":null},{"localized_name":"ttl","name":"ttl","type":"COMBO","widget":{"name":"ttl"},"link":null}],"outputs":[{"localized_name":"cache","name":"cache","type":"RUNWARETEXTINFERENCESETTINGSCACHE","links":[3]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"100644fdfb3f0f8a3aacea07992e7aa9e284f17a","Node name for S&R":"Runware Text Inference Settings Cache"},"widgets_values":["system+history","5m"],"bgcolor":"#5345bf"},{"id":2,"type":"Runware Text Model Search","pos":[44.05757812870157,-203.29950897408327],"size":[270,130],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"Model Search","name":"Model Search","type":"STRING","widget":{"name":"Model Search"},"link":null},{"localized_name":"Model Provider","name":"Model Provider","type":"COMBO","widget":{"name":"Model Provider"},"link":null},{"localized_name":"TextList","name":"TextList","type":"COMBO","widget":{"name":"TextList"},"link":null},{"localized_name":"Use Search Value","name":"Use Search Value","type":"BOOLEAN","widget":{"name":"Use Search Value"},"link":null}],"outputs":[{"localized_name":"model","name":"model","type":"RUNWARETEXTMODEL","links":[5]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"100644fdfb3f0f8a3aacea07992e7aa9e284f17a","Node name for S&R":"Runware Text Model Search"},"widgets_values":["","Anthropic","anthropic:claude@opus-4.7 (Claude Opus 4.7)",false],"bgcolor":"#5345bf"},{"id":6,"type":"Runware Text Inference Settings","pos":[381.4308092607773,7.637991025916822],"size":[400,658],"flags":{},"order":5,"mode":0,"inputs":[{"localized_name":"cache","name":"cache","shape":7,"type":"RUNWARETEXTINFERENCESETTINGSCACHE","link":3},{"localized_name":"useSystemPrompt","name":"useSystemPrompt","shape":7,"type":"BOOLEAN","widget":{"name":"useSystemPrompt"},"link":null},{"localized_name":"systemPrompt","name":"systemPrompt","shape":7,"type":"STRING","widget":{"name":"systemPrompt"},"link":null},{"localized_name":"useMaxTokens","name":"useMaxTokens","shape":7,"type":"BOOLEAN","widget":{"name":"useMaxTokens"},"link":null},{"localized_name":"maxTokens","name":"maxTokens","shape":7,"type":"INT","widget":{"name":"maxTokens"},"link":null},{"localized_name":"useTemperature","name":"useTemperature","shape":7,"type":"BOOLEAN","widget":{"name":"useTemperature"},"link":null},{"localized_name":"temperature","name":"temperature","shape":7,"type":"FLOAT","widget":{"name":"temperature"},"link":null},{"localized_name":"useTopP","name":"useTopP","shape":7,"type":"BOOLEAN","widget":{"name":"useTopP"},"link":null},{"localized_name":"topP","name":"topP","shape":7,"type":"FLOAT","widget":{"name":"topP"},"link":null},{"localized_name":"useTopK","name":"useTopK","shape":7,"type":"BOOLEAN","widget":{"name":"useTopK"},"link":null},{"localized_name":"topK","name":"topK","shape":7,"type":"INT","widget":{"name":"topK"},"link":null},{"localized_name":"thinkingLevel","name":"thinkingLevel","shape":7,"type":"COMBO","widget":{"name":"thinkingLevel"},"link":null},{"localized_name":"useStopSequences","name":"useStopSequences","shape":7,"type":"BOOLEAN","widget":{"name":"useStopSequences"},"link":null},{"localized_name":"stopSequences","name":"stopSequences","shape":7,"type":"STRING","widget":{"name":"stopSequences"},"link":null},{"localized_name":"usePresencePenalty","name":"usePresencePenalty","shape":7,"type":"BOOLEAN","widget":{"name":"usePresencePenalty"},"link":null},{"localized_name":"presencePenalty","name":"presencePenalty","shape":7,"type":"FLOAT","widget":{"name":"presencePenalty"},"link":null},{"localized_name":"useFrequencyPenalty","name":"useFrequencyPenalty","shape":7,"type":"BOOLEAN","widget":{"name":"useFrequencyPenalty"},"link":null},{"localized_name":"frequencyPenalty","name":"frequencyPenalty","shape":7,"type":"FLOAT","widget":{"name":"frequencyPenalty"},"link":null},{"localized_name":"useTools","name":"useTools","shape":7,"type":"BOOLEAN","widget":{"name":"useTools"},"link":null},{"localized_name":"tools","name":"tools","shape":7,"type":"STRING","widget":{"name":"tools"},"link":null},{"localized_name":"useToolChoice","name":"useToolChoice","shape":7,"type":"BOOLEAN","widget":{"name":"useToolChoice"},"link":null},{"localized_name":"toolChoice","name":"toolChoice","shape":7,"type":"STRING","widget":{"name":"toolChoice"},"link":null}],"outputs":[{"localized_name":"settings","name":"settings","type":"RUNWARETEXTINFERENCESETTINGS","links":[7]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"100644fdfb3f0f8a3aacea07992e7aa9e284f17a","Node name for S&R":"Runware Text Inference Settings"},"widgets_values":[false,"",false,4096,false,1,false,1,false,40,"none",false,"",false,0,false,0,false,"[]",false,"{}"],"bgcolor":"#5345bf"},{"id":12,"type":"Runware Text Inference Inputs","pos":[33.68724167528191,384.2004910259165],"size":[241.3892578125,46],"flags":{},"order":8,"mode":0,"inputs":[{"localized_name":"images","name":"images","shape":7,"type":"RUNWARETEXTINFERENCEINPUTSIMAGES","link":10},{"localized_name":"videos","name":"videos","shape":7,"type":"RUNWARETEXTINFERENCEINPUTSVIDEOS","link":null}],"outputs":[{"localized_name":"inputs","name":"inputs","type":"RUNWARETEXTINFERENCEINPUTS","links":[11]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"100644fdfb3f0f8a3aacea07992e7aa9e284f17a","Node name for S&R":"Runware Text Inference Inputs"},"widgets_values":[],"bgcolor":"#5345bf"},{"id":15,"type":"LoadImage","pos":[-437.4081684809681,-220.5091198231399],"size":[282.798828125,314.00000000000006],"flags":{},"order":3,"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":[12]},{"localized_name":"MASK","name":"MASK","type":"MASK","links":null}],"properties":{"cnr_id":"comfy-core","ver":"0.19.3","Node name for S&R":"LoadImage"},"widgets_values":[".webp","image"]},{"id":3,"type":"Runware Save Text","pos":[810.6171300154941,-206.42450897408324],"size":[514.4604952830189,291.61261792452825],"flags":{},"order":10,"mode":0,"inputs":[{"localized_name":"text","name":"text","type":"STRING","widget":{"name":"text"},"link":4}],"outputs":[{"localized_name":"text","name":"text","type":"STRING","links":null}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"100644fdfb3f0f8a3aacea07992e7aa9e284f17a","Node name for S&R":"Runware Save Text"},"widgets_values":["This picture shows a young woman posing outdoors in what appears to be Paris, France. Here are the details:\n\n**The Subject:**\n- A young woman with dark hair styled in a messy updo or braid with wispy bangs framing her face\n- She's wearing soft, romantic makeup with reddish-pink eyeshadow and bold red-orange lipstick\n- Her outfit is a dusty pink/mauve blouse or dress with ruffled details, a scoop neckline, and long billowy sleeves\n- She has a brown leather bag strap visible over her shoulder\n- Her expression is calm and contemplative as she looks toward the camera\n\n**The Setting:**\n- She's leaning against a stone balustrade (railing) of what appears to be a bridge\n- In the background, you can see a classic Parisian stone arch bridge spanning a river (likely the Seine)\n- A grand Haussmann-style building with ornate architecture is visible on the right, which could be part of the Conciergerie or a similar historic Parisian landmark\n- The sky is overcast with soft, cloudy lighting\n- Some greenery/trees are visible on the left\n\nThe overall aesthetic is romantic, vintage, and editorial — reminiscent of a fashion or travel photoshoot with a dreamy, cinematic quality."],"bgcolor":"#5345bf"},{"id":8,"type":"Runware Text Inference Messages","pos":[826.051619254881,161.19783772402994],"size":[268.261328125,166],"flags":{},"order":7,"mode":0,"inputs":[{"localized_name":"Message Segment 1","name":"Message Segment 1","type":"RUNWARETEXTINFERENCEMESSAGE","link":9},{"localized_name":"Message Segment 2","name":"Message Segment 2","shape":7,"type":"RUNWARETEXTINFERENCEMESSAGE","link":null},{"localized_name":"Message Segment 3","name":"Message Segment 3","shape":7,"type":"RUNWARETEXTINFERENCEMESSAGE","link":null},{"localized_name":"Message Segment 4","name":"Message Segment 4","shape":7,"type":"RUNWARETEXTINFERENCEMESSAGE","link":null},{"localized_name":"Message Segment 5","name":"Message Segment 5","shape":7,"type":"RUNWARETEXTINFERENCEMESSAGE","link":null},{"localized_name":"Message Segment 6","name":"Message Segment 6","shape":7,"type":"RUNWARETEXTINFERENCEMESSAGE","link":null},{"localized_name":"Message Segment 7","name":"Message Segment 7","shape":7,"type":"RUNWARETEXTINFERENCEMESSAGE","link":null},{"localized_name":"Message Segment 8","name":"Message Segment 8","shape":7,"type":"RUNWARETEXTINFERENCEMESSAGE","link":null}],"outputs":[{"localized_name":"messages","name":"messages","type":"RUNWARETEXTINFERENCEMESSAGES","links":[8]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"100644fdfb3f0f8a3aacea07992e7aa9e284f17a","Node name for S&R":"Runware Text Inference Messages"},"widgets_values":[],"bgcolor":"#5345bf"},{"id":9,"type":"Runware Text Inference Message","pos":[1146.8125899211548,185.7113990447846],"size":[400,200],"flags":{},"order":4,"mode":0,"inputs":[{"localized_name":"role","name":"role","type":"COMBO","widget":{"name":"role"},"link":null},{"localized_name":"content","name":"content","type":"STRING","widget":{"name":"content"},"link":null}],"outputs":[{"localized_name":"message","name":"message","type":"RUNWARETEXTINFERENCEMESSAGE","links":[9]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"100644fdfb3f0f8a3aacea07992e7aa9e284f17a","Node name for S&R":"Runware Text Inference Message"},"widgets_values":["user","What is in this pciture?"],"bgcolor":"#5345bf"},{"id":14,"type":"Runware Text Inference Inputs Images","pos":[-452.9457458689397,186.80957121459585],"size":[353.34921875,250],"flags":{},"order":6,"mode":0,"inputs":[{"localized_name":"Image 1","name":"Image 1","shape":7,"type":"IMAGE","link":12},{"localized_name":"Image 2","name":"Image 2","shape":7,"type":"IMAGE","link":null},{"localized_name":"Image 3","name":"Image 3","shape":7,"type":"IMAGE","link":null},{"localized_name":"Image 4","name":"Image 4","shape":7,"type":"IMAGE","link":null},{"localized_name":"Image 5","name":"Image 5","shape":7,"type":"IMAGE","link":null},{"localized_name":"Image 6","name":"Image 6","shape":7,"type":"IMAGE","link":null},{"localized_name":"Image 7","name":"Image 7","shape":7,"type":"IMAGE","link":null},{"localized_name":"Image 8","name":"Image 8","shape":7,"type":"IMAGE","link":null},{"localized_name":"Image1","name":"Image1","shape":7,"type":"STRING","widget":{"name":"Image1"},"link":null},{"localized_name":"Image3","name":"Image3","shape":7,"type":"STRING","widget":{"name":"Image3"},"link":null},{"localized_name":"Image5","name":"Image5","shape":7,"type":"STRING","widget":{"name":"Image5"},"link":null},{"localized_name":"Image7","name":"Image7","shape":7,"type":"STRING","widget":{"name":"Image7"},"link":null}],"outputs":[{"localized_name":"images","name":"images","type":"RUNWARETEXTINFERENCEINPUTSIMAGES","links":[10]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"100644fdfb3f0f8a3aacea07992e7aa9e284f17a","Node name for S&R":"Runware Text Inference Inputs Images"},"widgets_values":[],"bgcolor":"#5345bf"}],"links":[[3,5,0,6,0,"RUNWARETEXTINFERENCESETTINGSCACHE"],[4,7,0,3,0,"STRING"],[5,2,0,7,0,"RUNWARETEXTMODEL"],[6,4,0,7,4,"RUNWARETEXTINFERENCETOOLCHOICE"],[7,6,0,7,2,"RUNWARETEXTINFERENCESETTINGS"],[8,8,0,7,1,"RUNWARETEXTINFERENCEMESSAGES"],[9,9,0,8,0,"RUNWARETEXTINFERENCEMESSAGE"],[10,14,0,12,0,"RUNWARETEXTINFERENCEINPUTSIMAGES"],[11,12,0,7,5,"RUNWARETEXTINFERENCEINPUTS"],[12,15,0,14,0,"IMAGE"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.53,"offset":[625.2835185694114,492.4932000118191]}},"version":0.4} \ No newline at end of file