v2.0.0: schema-driven node pack (one node per Runware model)

This commit is contained in:
Félix Sanz
2026-07-06 13:42:59 +02:00
parent 0c24c6a32e
commit 53b2b84a89
353 changed files with 2769 additions and 26781 deletions
+24
View File
@@ -0,0 +1,24 @@
name: Publish to Comfy Registry
# Publishes to the ComfyUI Registry whenever the version in pyproject.toml changes on main.
# Requires a REGISTRY_ACCESS_TOKEN repo secret (create at https://registry.comfy.org).
on:
push:
branches: [main]
paths:
- "pyproject.toml"
permissions:
contents: read
jobs:
publish-node:
name: Publish custom node
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'Runware' }}
steps:
- uses: actions/checkout@v4
- name: Publish to Comfy Registry
uses: Comfy-Org/publish-node-action@v1
with:
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
+4 -16
View File
@@ -1,16 +1,4 @@
__pycache__/
*.py[cod]
*$py.class
imagesCache.json
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
cython_debug/
.pyre/
.DS_Store
__pycache__/
*.pyc
.pytest_cache/
schema-map.json
+69 -155
View File
@@ -1,168 +1,82 @@
# ComfyUI-Runware Integration 🚀
# ComfyUI-Runware
Welcome to **ComfyUI-Runware**! 🌟 This is the official integration for **[Runware](https://runware.ai/?utm_source=github&utm_medium=referral&utm_campaign=comfyui)**, bringing you the power of **inference-as-a-service** directly into **ComfyUI**!
Every Runware model as a ComfyUI node — image, video, audio, 3D, and text. The
whole catalog stays current as new models launch, and each node's parameters
match the live model exactly.
## 🌟 What is ComfyUI-Runware?
## Install
Imagine creating stunning images or videos, refining details, or even removing backgrounds—all without needing a powerful GPU. With **Runware**, all the heavy lifting happens on our servers. No GPU? No problem! 🖥️💨
**ComfyUI Manager** (recommended): search **Runware** and install.
This integration adds a suite of custom nodes to ComfyUI, allowing you to:
- Perform advanced image inference tasks, including image generation and editing with powerful models.
- Utilize PhotoMaker V2 pipelines
- Leverage cutting-edge image models like **Flux .1 Kontext Pro**, **Flux .1 Kontext Max**, **Nano Banana**, **Seedream**, and more for superior image generation and editing.
- Upscale images
- Remove backgrounds
- Generate videos from text prompts and/or images with video inference
- Generate audio from text
- Upscale videos
- Generate video captions
- Remove video backgrounds
- Leverage specialized models and LoRAs
- Use your custom uploaded models and LoRAs
... and so much more! Let’s make your workflows seamless and efficient with **Runware**. 🖌️✨
---
## 📖 Table of Contents
1. [Introduction](#-what-is-comfyui-runware)
2. [Workflows](#-workflows)
3. [Installation](#-installation)
4. [Node Features](#-node-features)
5. [Support & Community](#-support--community)
---
## 🎬 Workflows
Here are some example workflows showcasing the power of ComfyUI-Runware:
### Image Workflows
![Image Inference Workflow](assets/image-inference.png)
### Video Workflows
![Video Inference Workflow](assets/video-inference.png)
---
## 🔧 Installation
### Step 1: Install ComfyUI
First, ensure you have ComfyUI installed. You can follow the [pre-built package guide](https://docs.comfy.org/get_started/pre_package) or the [manual installation guide](https://docs.comfy.org/get_started/manual_install).
Make sure your system meets these requirements:
- **Python 3.10+**
- **ComfyUI installed**
### Step 2: Install ComfyUI-Runware
You have two options to install this integration:
#### Option 1: Using ComfyUI Manager (Recommended)
First, ensure you have the `ComfyUI-Manager` custom node installed. If you don't have it or are using the beta ComfyUI desktop version, follow the instructions on this [GitHub Repo](https://github.com/ltdrdata/ComfyUI-Manager?tab=readme-ov-file#installation).
After installing ComfyUI-Manager, open it and click on "Custom Nodes Manager". Search for "Runware" or **"Runware.ai"** and then click on install or update. Finally, restart your ComfyUI. ✨
#### Option 2: Manual Installation
1. Navigate to the custom nodes directory:
```bash
cd custom_nodes
```
2. Clone the repository:
```bash
git clone https://github.com/Runware/ComfyUI-Runware.git
```
3. Navigate to the repository folder:
```bash
cd ComfyUI-Runware
```
4. Install dependencies:
```bash
pip install -r requirements.txt
```
### Step 3: Run ComfyUI
Install latest ComfyUI frontend package
**Manual:**
```bash
pip install -U comfyui-frontend-package
````
Start ComfyUI with the following command:
```bash
python main.py
cd ComfyUI/custom_nodes
git clone https://github.com/Runware/ComfyUI-Runware
pip install -r ComfyUI-Runware/requirements.txt
```
Restart ComfyUI.
#### Optional: CPU-Only Mode
If you want to run ComfyUI without a GPU, add the `--cpu` flag:
```bash
python main.py --cpu
```
## API key
### Step 4: Explore Workflows
Inside the `ComfyUI-Runware` custom node folder, you’ll find a `workflows` folder with pre-made workflows to get you started! 🚀
Use any of these (the environment variable wins if more than one is set):
---
- **ComfyUI Settings → Runware API key** — paste your key in the UI, no terminal needed.
- **`RUNWARE_API_KEY`** in the environment ComfyUI runs in.
- **[Runware CLI](https://runware.ai/docs/platform/cli)** — run `runware auth login`
once and the nodes reuse the stored key.
## 🧩 Node Features
## Use
Here's a breakdown of the amazing nodes included in this integration:
Search the node menu for **Runware** — one node per model, grouped
`Runware/<Modality>/<creator>`. Set the parameters, wire the output, and queue:
- **Runware API Manager**: Set or change your API keys, adjust the max connection timeout, adjust the image output quality and format, and enable or disable image caching directly in ComfyUI—no need to edit config files manually! 🔑
- **Runware Image Inference**: Perform advanced tasks like inpainting, outpainting, and more 🎨
- **Runware PhotoMakerV2**: Create consistent identities with our photomaker pipeline. 🖼️
- **Runware Image Upscale**: Upscale your images up to 4x. 🔍
- **Runware Background Removal**: Effortlessly remove image backgrounds. 🧹
- **Runware LoRA**: Search and select LoRAs to enhance your workflow. 📂
- **Runware Model**: Choose specific models to connect with image inference. 🤖
- **Runware ControlNet**: Guide your image generation with ControlNet and guide images. 🗺️
- **Runware Refiner**: Refine your images with advanced tools. ✨
- **Runware LoRA Combine**: Combine up to 3 LoRAs together. 🔗
- **Runware ControlNet Combine**: Combine multiple ControlNets for complex workflows. 🧩
- **Runware Image Masking**: Automatically mask elements like faces, hands, and more. 🖌️
- **Runware ControlNet Preprocessor**: Preprocess images before using them as guide images in ControlNet. 🔄
- **Runware VAE**: Search and connect a VAE to Image inference. 🖼️
- **Runware Embedding**: Search and connect Embedding to image inference. 🧩
- **Runware Embedding Combine**: Combine multiple embeddings together. 🔗
- **Runware Image Caption**: Generate descriptive text from images for further workflow integration. 🖼️
- **Runware IPAdapter**: Use reference images to guide the style and content of generated images. 🖌️
- **Runware IPAdapters Combine**: Combine multiple IP-Adapter inputs for sophisticated image conditioning. 🔗
- **Runware Video Inference**: Generate videos from text prompts using advanced AI models with support for frame images and reference images. 🎬
- **Image** models output a native ComfyUI IMAGE.
- **Audio / video** models output native ComfyUI AUDIO / VIDEO (chain them straight
into preview or save nodes); **3D** and other files download to your output folder
and return a path.
- **Reference / seed images** are IMAGE inputs and **inpainting masks** are MASK
inputs; audio/video inputs take a URL or path.
- **Builder nodes keep model nodes clean** (`Runware/Params`). Stackable features
live in their own nodes instead of crowding every model node: `LoRA`, `ControlNet`,
`IP-Adapter`, `Embeddings`, `Refiner`, feature blocks (`PuLID`, `PhotoMaker`,
`Watermark`, `Ultralytics`, `Accelerator Options`), and reference inputs. Each feature is a distinct socket
type, so you wire a builder into the model's matching typed socket (`lora`,
`controlNet`, ...) and chain the stackable ones to combine them. The model node only
shows the features a generation actually uses. LoRA, Embeddings, and Refiner have an
editable **model** field plus a **search catalog** button beneath it that opens a live
catalog search to fill the AIR. ControlNet and IP-Adapter instead scope their **model**
field to the model/architecture node you wire them into (chaining through any other same
builders): it becomes a dropdown of exactly the models that node accepts, or a free AIR
field until it reaches one. The Speech, reference, Accelerator Options, and Outpaint
builders scope their **fields** the same way: wired into a model, each shows only what that
model supports and hides the rest (Speech's `voice`/`language` become that model's own
lists, Accelerator Options shows only its caching strategies, Outpaint drops `blur` where
unsupported).
- **Model-specific config flattens onto the model node** as dotted fields under a
**Settings** section: `safety.checkContent`, `providerSettings.google.webSearch`
(only your model's provider is shown).
- **Widgets are grouped into sections** (inputs, core, settings, output). Where a model
has no fixed default for a setting, the node leaves it to the model rather than guess:
a number like `steps` or `CFGScale` shows a **set** toggle that reveals its field only
when you opt in, and a dropdown like `scheduler` carries a `(default)` option. Leave
these to use the model's own default, or set them to take control. Dimensions and
`seed` always show a value. A cohesive settings group reveals behind one toggle too:
`safety`, `toolChoice`, and `advancedFeatures` each hide their fields (e.g.
`safety.checkContent` / `safety.mode`) until you enable the group.
- Niche per-model params (and anything not auto-typed) go into the raw
`advanced_json` input.
- **Runware Upload Image** / **Runware Load Image (URL)** — upload an image once for
a reusable UUID, or pull any image URL (including a Runware result) into the graph
as an IMAGE.
- After a run, each model, architecture, and custom node shows its cost and, when the
model ran a content check, the NSFW result along its title bar (e.g. `$0.00078 · NSFW: no`).
- **Runware (custom)** — a generic node for any model AIR + taskType, for models
newer than your installed version. It makes no assumption about modality: you give
it the request as JSON (`request_json`) and it returns the raw response as
`result_json`. Feed media in as UUIDs/URLs (see **Runware Upload Image**).
- **Runware Get** — pulls a field out of a `result_json` by dot-path (e.g.
`0.imageURL`, `0.videoURL`, `0.outputs.files.0.url`; leave it empty to grab the
first URL). The `STRING` it outputs goes to **Runware Load Image (URL)** to preview
an image, or to a Save/Preview node for any other result.
- **Runware Frame Images**: Configure keyframe images with precise positioning control for video generation. 🖼️➡️🎥
- **Runware Reference Images**: Provide reference images for overall visual guidance in video generation. 🖼️
### Provider Settings
Configure provider-specific settings for various services:
- **Runware Alibaba Provider Settings**: Configure Alibaba-specific settings. ☁️
- **Runware BRIA Image Provider Settings**: Configure BRIA Image-specific settings. 🖼️
- **Runware ByteDance Provider Settings**: Configure ByteDance-specific settings. 🎬
- **Runware ElevenLabs Provider Settings**: Configure ElevenLabs-specific settings for audio generation. 🎤
- **Runware Kling Provider Settings**: Configure Kling-specific settings. 🎥
- **Runware Lightricks Provider Settings**: Configure Lightricks-specific settings. ✨
- **Runware Luma Provider Settings**: Configure Luma-specific settings. 🎬
- **Runware Midjourney Provider Settings**: Configure Midjourney-specific settings. 🎨
- **Runware MiniMax Provider Settings**: Configure MiniMax-specific settings. 🤖
- **Runware OpenAI Provider Settings**: Configure OpenAI-specific settings with quality, background, and style options. 🤖
- **Runware Pixverse Provider Settings**: Configure Pixverse-specific settings with effect, style, motion, and sound options. 🎬
- **Runware Runway Provider Settings**: Configure Runway-specific settings. 🎥
- **Runware Vidu Provider Settings**: Configure Vidu-specific settings. 🎬
---
## 🤝 Support & Community
This is the **official Runware integration**, maintained by **Runware Inc**. We’re here to help you every step of the way! 💬
Join our community on Discord for support, updates, and to connect with fellow creators: [Runware Discord](https://discord.com/invite/aJ4UzvBqNU) 🎉
---
Thank you for using **ComfyUI-Runware**! Let’s create something amazing together. 🌟
Browse models and get an API key at [runware.ai](https://runware.ai/docs).
+7 -316
View File
@@ -1,318 +1,9 @@
from .modules.imageInference import txt2img
from .modules.outpaintSettings import outpaintSettings
from .modules.bgremoval import bgremoval
from .modules.photoMaker import photoMaker
from .modules.upscaler import upscaler
from .modules.imageUpscalerSettings import RunwareImageUpscalerSettings
from .modules.modelSearch import modelSearch
from .modules.bridges import mainRoute
from .modules.controlNet import controlNet
from .modules.multiInference import multiInference
from .modules.runwareBFL import runwareKontext
from .modules.runwareImagen import runwareImagen
from .modules.loraSearch import loraSearch
from .modules.loraCombine import loraCombine
from .modules.refiner import refiner
from .modules.imageMasking import imageMasking
from .modules.controlNetPreprocessor import controlNetPreprocessor
from .modules.apiManager import apiManager
from .modules.imageCaptioning import imageCaptioning
from .modules.videoTranscription import videoTranscription
from .modules.controlNetCombine import controlNetCombine
from .modules.embeddingSearch import embeddingSearch
from .modules.embeddingsCombine import embeddingsCombine
from .modules.ipAdapter import ipAdapter
from .modules.ipAdapterCombine import ipAdapterCombine
from .modules.vaeSearch import vaeSearch
from .modules.referenceImages import referenceImages
from .modules.imageInferenceInputs import imageInferenceInputs
from .modules.imageInferenceInputsFonts import RunwareImageInferenceInputsFonts
from .modules.imageInferenceAdvancedFeatures import RunwareImageInferenceAdvancedFeatures
from .modules.imageWatermarkAdvancedFeature import RunwareWatermarkAdvancedFeature
from .modules.imageRegionalPromptingAdvancedFeature import RunwareRegionalPromptingAdvancedFeature
from .modules.imageRegionalPromptingAdvancedFeatureRegions import RunwareRegionalPromptingAdvancedFeatureRegions
from .modules.videoInference import txt2vid
from .modules.videoInferenceSpeechInput import RunwareVideoInferenceSpeechInput
from .modules.videoInferenceLora import RunwareVideoInferenceLora
from .modules.videoModelSearch import videoModelSearch
from .modules.videoSettings import RunwareVideoSettings
from .modules.videoInferenceSettingsTts import RunwareVideoInferenceSettingsTts
from .modules.videoInferenceSettingsActiveSpeakerDetection import RunwareVideoInferenceSettingsActiveSpeakerDetection
from .modules.videoInferenceSettingsActiveSpeakerBoundingBoxes import RunwareVideoInferenceSettingsActiveSpeakerBoundingBoxes
from .modules.videoInferenceSettingsSegments import RunwareVideoInferenceSettingsSegments
from .modules.videoInferenceSettingsControl import RunwareVideoInferenceSettingsControl
from .modules.videoInferenceSettingsSourcePosition import RunwareVideoInferenceSettingsSourcePosition
from .modules.frameImages import RunwareFrameImages
from .modules.providerSettings.pixverseProviderSettings import RunwarePixverseProviderSettings
from .modules.providerSettings.openaiProviderSettings import RunwareOpenAIProviderSettings
from .modules.providerSettings.bytedanceProviderSettings import RunwareBytedanceProviderSettings
from .modules.providerSettings.briaProviderSettings import RunwareBriaProviderSettings
from .modules.providerSettings.briaProviderMask import RunwareBriaProviderMask
from .modules.audioInference import RunwareAudioInference
from .modules.audioInferenceSpeech import RunwareAudioInferenceSpeech
from .modules.audioInferenceSpeechVoices import RunwareAudioInferenceSpeechVoices
from .modules.audioModelSearch import RunwareAudioModelSearch
from .modules.textModelSearch import RunwareTextModelSearch
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.audioInferenceReferenceVoices import RunwareAudioInferenceReferenceVoices
from .modules.audioSettings import RunwareAudioSettings
from .modules.audioSettingsVoiceModify import RunwareAudioSettingsVoiceModify
from .modules.providerSettings.elevenlabsProviderSettings import RunwareElevenLabsProviderSettings
from .modules.providerSettings.elevenlabsProviderSettingsSections import RunwareElevenLabsProviderSettingsSections
from .modules.acceleratorOptions import acceleratorOptions
from .modules.providerSettings.viduProviderSettings import RunwareViduProviderSettings
from .modules.mediaUpload import runwareMediaUpload
from .modules.inputAudios import inputAudios
from .modules.audioInput import RunwareAudioInput
from .modules.speechInput import RunwareSpeechInput
from .modules.speechInputCombine import RunwareSpeechInputCombine
from .modules.referenceVideos import referenceVideos
from .modules.referenceVoices import referenceVoices
from .modules.referenceAudios import referenceAudios
from .modules.videoInferenceInputs import videoInferenceInputs
from .modules.videoInferenceElements import (
RunwareVideoInferenceElements,
RunwareVideoInferenceElementsCombine,
)
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
from .modules.providerSettings.mireloProviderSettings import RunwareMireloProviderSettings
from .modules.providerSettings.alibabaProviderSettings import RunwareAlibabaProviderSettings
from .modules.providerSettings.googleProviderSettings import RunwareGoogleProviderSettings
from .modules.providerSettings.blackForestProviderSettings import RunwareBlackForestProviderSettings
from .modules.providerSettings.xaiProviderSettings import RunwareXAIProviderSettings
from .modules.providerSettings.recraftProviderSettings import RunwareRecraftProviderSettings
from .modules.providerSettings.recraftProviderSettingsColor import RunwareRecraftColor
from .modules.providerSettings.sourcefulProviderSettings import RunwareSourcefulProviderSettings
from .modules.providerSettings.sourcefulProviderSettingsFonts import RunwareSourcefulProviderSettingsFonts
from .modules.ultralyticsInputs import RunwareUltralyticsInputs
from .modules.providerSettings.syncSegment import RunwareSyncSegment
from .modules.providerSettings.syncProviderSettings import RunwareSyncProviderSettings
from .modules.vectorize import vectorize
from .modules.videoBgRemoval import videoBgRemoval
from .modules.videoUpscaler import videoUpscaler
from .modules.videoInputsReferences import videoInputsReferences
from .modules.videoInputsReferenceImagesMultipleImagesConnector import RunwareVideoInputsReferenceImagesMultipleImagesConnector
from .modules.videoInputsFrame import RunwareVideoInputsFrameImages
from .modules.safetyInputs import safetyInputs
from .modules.settings import RunwareSettings
from .modules.imageInferenceSettingsColorPalette import RunwareImageInferenceSettingsColorPalette
from .modules.imageInferenceSettingsMoodboards import RunwareImageInferenceSettingsMoodboards
from .modules.imageInferenceSettingsStructuredPrompt import RunwareImageInferenceSettingsStructuredPrompt
from .modules.imageInferenceSettingsPromptEnhance import RunwareImageInferenceSettingsPromptEnhance
from .modules.imageInferenceSettingsScoringRubric import (
RunwareImageInferenceSettingsScoringRubric,
RunwareImageInferenceSettingsScoringRubricCombine,
)
from .modules.videoAdvancedFeatureInputs import videoAdvancedFeatureInputs
from .modules.advancedFeatureSettings.wanAnimateAdvancedFeatureSettings import RunwareWanAnimateAdvancedFeatureSettings
from .modules.saveImage import RunwareSaveImage
from .modules.videoOutputs import RunwareVideoInferenceOutputs
from .modules.threeDInference import threeDInference
from .modules.threeDModelSearch import Runware3DModelSearch
from .modules.threeDInferenceInputs import threeDInferenceInputs
from .modules.threeDInferenceSettings import Runware3DInferenceSettings
from .modules.threeDInferenceSettingsSparseStructure import Runware3DInferenceSettingsSparseStructure
from .modules.threeDInferenceSettingsShapeSlat import Runware3DInferenceSettingsShapeSlat
from .modules.threeDInferenceSettingsTexSlat import Runware3DInferenceSettingsTexSlat
from .modules.threeDInferenceSettingsMeshCluster import Runware3DInferenceSettingsMeshCluster
from .modules.threeDInferenceSettingsDracoCompression import Runware3DInferenceSettingsDracoCompression
from .modules.save3D import RunwareSave3D
from .modules.saveText import RunwareSaveText
from .modules.loadMesh import RunwareLoadMesh
"""ComfyUI entry point — exposes the generated Runware nodes."""
RUNWARE_COMFYUI_VERSION = "1.4.0 Beta"
try: # package import under ComfyUI
from .runware_nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
except ImportError: # flat import (tests, tooling)
from runware_nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
RESET_COLOR = "\033[0m"
BLUE_COLOR = "\033[94m"
GREEN_COLOR = "\033[92m"
print(BLUE_COLOR + "##############################################################" + RESET_COLOR)
print(GREEN_COLOR + " Runware ComfyUI Inference Services Are Loaded Successfully" + RESET_COLOR)
print(GREEN_COLOR + " Version: " + RUNWARE_COMFYUI_VERSION + " | Maintained by: Runware Inc" + RESET_COLOR)
print(GREEN_COLOR + " Official Website: https://my.runware.ai" + RESET_COLOR)
print(BLUE_COLOR + "##############################################################" + RESET_COLOR)
NODE_CLASS_MAPPINGS = {
"Runware Image Inference": txt2img,
"Runware Outpaint": outpaintSettings,
"Runware Background Removal": bgremoval,
"Runware PhotoMaker V2": photoMaker,
"Runware Image Upscaler": upscaler,
"Runware Image Upscaler Settings": RunwareImageUpscalerSettings,
"Runware Model Search": modelSearch,
"Runware Kontext Inference": runwareKontext,
"Runware Imagen Inference": runwareImagen,
"Runware Multi Inference": multiInference,
"Runware Lora Search": loraSearch,
"Runware Embedding Search": embeddingSearch,
"Runware VAE Search": vaeSearch,
"Runware Embeddings Combine": embeddingsCombine,
"Runware ControlNet": controlNet,
"Runware Lora Combine": loraCombine,
"Runware Refiner": refiner,
"Runware Image Masking": imageMasking,
"Runware ControlNet PreProcessor": controlNetPreprocessor,
"Runware API Manager": apiManager,
"Runware Image Caption": imageCaptioning,
"Runware Video Transcription": videoTranscription,
"Runware ControlNet Combine": controlNetCombine,
"Runware IPAdapter": ipAdapter,
"Runware IPAdapters Combine": ipAdapterCombine,
"Runware Reference Images": referenceImages,
"Runware Image Inference Inputs": imageInferenceInputs,
"Runware Image Inference Inputs Fonts": RunwareImageInferenceInputsFonts,
"Runware Image Advanced Feature Input": RunwareImageInferenceAdvancedFeatures,
"Runware Watermark Advanced Feature": RunwareWatermarkAdvancedFeature,
"Runware Regional Prompting Advanced Feature": RunwareRegionalPromptingAdvancedFeature,
"Runware Regional Prompting Advanced Feature Regions": RunwareRegionalPromptingAdvancedFeatureRegions,
"Runware Video Inference": txt2vid,
"Runware Video Model Search": videoModelSearch,
"Runware Frame Images": RunwareFrameImages,
"Runware Pixverse Provider Settings": RunwarePixverseProviderSettings,
"Runware OpenAI Provider Settings": RunwareOpenAIProviderSettings,
"Runware Bytedance Provider Settings": RunwareBytedanceProviderSettings,
"Runware Bria Provider Settings": RunwareBriaProviderSettings,
"Runware Bria Provider Mask": RunwareBriaProviderMask,
"Runware Audio Inference": RunwareAudioInference,
"Runware Audio Inference Speech": RunwareAudioInferenceSpeech,
"Runware Audio Inference Speech Voices": RunwareAudioInferenceSpeechVoices,
"Runware Audio Model Search": RunwareAudioModelSearch,
"Runware Text Model Search": RunwareTextModelSearch,
"Runware Text Inference Messages": RunwareTextInferenceMessages,
"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 Inputs Reference Audio": RunwareAudioInferenceReferenceVoices,
"Runware Audio Inference Settings": RunwareAudioSettings,
"Runware Audio Inference Settings Voice Modify": RunwareAudioSettingsVoiceModify,
"Runware ElevenLabs Provider Settings": RunwareElevenLabsProviderSettings,
"Runware ElevenLabs Provider Settings Sections": RunwareElevenLabsProviderSettingsSections,
"Runware Accelerator Options": acceleratorOptions,
"Runware Vidu Provider Settings": RunwareViduProviderSettings,
"Runware Media Upload": runwareMediaUpload,
"Runware Input Audios": inputAudios,
"Runware Video Audio Input": RunwareAudioInput,
"Runware Video Speech Input": RunwareSpeechInput,
"Runware Speech Inputs Combine": RunwareSpeechInputCombine,
# Backward-compatible aliases for existing workflows
"Runware Reference Videos": referenceVideos,
"Runware Reference Voices": referenceVoices,
"Runware Video Inputs References": videoInputsReferences,
"Runware Video Inference Inputs Reference Videos": referenceVideos,
"Runware Video Inference Inputs Reference Voices": referenceVoices,
"Runware Video Inference Inputs Reference Audios": referenceAudios,
"Runware Video Inference Inputs": videoInferenceInputs,
"Runware Video Inference Elements": RunwareVideoInferenceElements,
"Runware Video Inference Elements Combine": RunwareVideoInferenceElementsCombine,
"Runware Video Inference Speech Input": RunwareVideoInferenceSpeechInput,
"Runware Video Inference Lora": RunwareVideoInferenceLora,
"Runware Video Inference Settings": RunwareVideoSettings,
"Runware Video Inference Settings TTS": RunwareVideoInferenceSettingsTts,
"Runware Video Inference Settings Active Speaker Detection": RunwareVideoInferenceSettingsActiveSpeakerDetection,
"Runware Video Inference Settings Active Speaker Bounding Boxes": RunwareVideoInferenceSettingsActiveSpeakerBoundingBoxes,
"Runware Video Inference Settings Segments": RunwareVideoInferenceSettingsSegments,
"Runware Video Inference Settings Edit": RunwareVideoInferenceSettingsControl,
"Runware Video Inference Settings Source Position": RunwareVideoInferenceSettingsSourcePosition,
"Runware Lightricks Provider Settings": RunwareLightricksProviderSettings,
"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,
"Runware Alibaba Provider Settings": RunwareAlibabaProviderSettings,
"Runware Google Provider Settings": RunwareGoogleProviderSettings,
"Runware BlackForest Labs Provider Settings": RunwareBlackForestProviderSettings,
"Runware xAI Provider Settings": RunwareXAIProviderSettings,
"Runware Recraft Provider Settings": RunwareRecraftProviderSettings,
"Runware Provider Settings Recraft Color": RunwareRecraftColor,
"Runware Sourceful Provider Settings": RunwareSourcefulProviderSettings,
"Runware Sourceful Provider Settings Fonts": RunwareSourcefulProviderSettingsFonts,
"Runware Image Inference Ultralytics": RunwareUltralyticsInputs,
"Runware Sync Segment": RunwareSyncSegment,
"Runware Sync Provider Settings": RunwareSyncProviderSettings,
"Runware Vectorize": vectorize,
"Runware Video Background Removal": videoBgRemoval,
"Runware Video Upscaler": videoUpscaler,
"Runware Video Inference Inputs Reference Images": videoInputsReferences,
"Runware Video Inference Inputs Reference Images Multiple Images Connector": RunwareVideoInputsReferenceImagesMultipleImagesConnector,
"Runware Video Inputs Frame Images": RunwareVideoInputsFrameImages,
"Runware Safety Inputs": safetyInputs,
"Runware Settings": RunwareSettings,
"Runware Image Inference Settings Color Palette": RunwareImageInferenceSettingsColorPalette,
"Runware Image Inference Settings Moodboards": RunwareImageInferenceSettingsMoodboards,
"Runware Image Inference Settings Structured Prompt": RunwareImageInferenceSettingsStructuredPrompt,
"Runware Image Inference Settings Prompt Enhance": RunwareImageInferenceSettingsPromptEnhance,
"Runware Image Inference Settings Scoring Rubric": RunwareImageInferenceSettingsScoringRubric,
"Runware Image Inference Settings Scoring Rubric Combine": RunwareImageInferenceSettingsScoringRubricCombine,
"Runware Video Advanced Feature Inputs": videoAdvancedFeatureInputs,
"Runware Wan Animate Advanced Feature Settings": RunwareWanAnimateAdvancedFeatureSettings,
"Runware Save Image": RunwareSaveImage,
"Runware Video Inference Outputs": RunwareVideoInferenceOutputs,
"Runware 3D Inference": threeDInference,
"Runware 3D Model Search": Runware3DModelSearch,
"Runware 3D Inference Inputs": threeDInferenceInputs,
"Runware 3D Inference Settings": Runware3DInferenceSettings,
"Runware 3D Inference Settings Sparse Structure": Runware3DInferenceSettingsSparseStructure,
"Runware 3D Inference Settings Shape Slat": Runware3DInferenceSettingsShapeSlat,
"Runware 3D Inference Settings Tex Slat": Runware3DInferenceSettingsTexSlat,
"Runware 3D Inference Settings Mesh Cluster": Runware3DInferenceSettingsMeshCluster,
"Runware 3D Inference Settings Draco Compression": Runware3DInferenceSettingsDracoCompression,
"Runware Save 3D": RunwareSave3D,
"Runware Save Text": RunwareSaveText,
"Runware Load Mesh": RunwareLoadMesh,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"Runware Settings": "Runware Image Inference Settings",
"Runware Model Search": "Runware Model",
"Runware Lora Search": "Runware Lora",
"Runware Embedding Search": "Runware Embedding",
"Runware VAE Search": "Runware VAE",
"Runware Multi Inference": "Runware Multi Inference [BETA]",
"Runware Video Model Search": "Runware Video Model",
"Runware 3D Model Search": "Runware 3D Model",
"Runware Audio Model Search": "Runware Audio Model",
"Runware Text Model Search": "Runware Text Model",
"Runware Image Inference Settings Color Palette": "Runware Image Inference Color Palette",
"Runware Image Inference Settings Structured Prompt": "Runware Image Inference Structured Prompt",
"Runware Image Inference Settings Prompt Enhance": "Runware Image Inference Settings Prompt Enhance",
"Runware Video Transcription": "Runware Video Caption",
"Runware Video Inputs References": "Runware Video Inference Inputs Reference Images",
"Runware Video Inference Inputs Reference Images": "Runware Video Inference Inputs Reference Images",
"Runware Reference Voices": "Runware Video Inference Inputs Reference Voices",
"Runware Video Inference Inputs Reference Voices": "Runware Video Inference Inputs Reference Voices",
"Runware Reference Videos": "Runware Video Inference Inputs Reference Videos",
"Runware Video Inference Inputs Reference Videos": "Runware Video Inference Inputs Reference Videos",
"Runware Reference Audios": "Runware Video Inference Inputs Reference Audios",
"Runware Video Inference Inputs Reference Audios": "Runware Video Inference Inputs Reference Audios",
"Runware Video Inference Inputs Reference Images Multiple Images Connector": "Runware Video Inference Inputs Reference Images Multiple Images Connector",
}
WEB_DIRECTORY = "./clientlibs"
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
WEB_DIRECTORY = "./web"
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 706 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 780 KiB

-237
View File
@@ -1,237 +0,0 @@
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js";
import { promptEnhanceHandler, syncDimensionsNodeHandler, searchNodeHandler, APIKeyHandler, captionNodeHandler, saveTextHandler, mediaUUIDHandler, save3DFilepathHandler, videoTranscriptionHandler, videoOutputsHandler, handleCustomErrors, videoInferenceDimensionsHandler, videoModelSearchFilterHandler, threeDModelSearchFilterHandler, audioModelSearchFilterHandler, textModelSearchFilterHandler, vectorizeModelSearchFilterHandler, vectorizeToggleHandler, useParameterToggleHandler, imageInferenceToggleHandler, imageInferenceAdvancedFeaturesToggleHandler, watermarkAdvancedFeatureToggleHandler, videoInferenceSpeechInputToggleHandler, regionalPromptingRegionsToggleHandler, upscalerToggleHandler, imageUpscalerSettingsToggleHandler, videoUpscalerToggleHandler, audioInferenceToggleHandler, audioInferenceSpeechToggleHandler, audioSettingsToggleHandler, textInferenceSettingsToggleHandler, videoSettingsToggleHandler, videoInferenceSettingsTtsToggleHandler, videoInferenceSettingsActiveSpeakerDetectionToggleHandler, videoInferenceSettingsActiveSpeakerBoundingBoxesToggleHandler, videoInferenceSettingsSegmentsToggleHandler, videoInferenceSettingsControlToggleHandler, acceleratorOptionsToggleHandler, bytedanceProviderSettingsToggleHandler, xaiProviderSettingsToggleHandler, viduProviderSettingsToggleHandler, sourcefulProviderSettingsToggleHandler, sourcefulProviderSettingsFontsToggleHandler, threeDInferenceToggleHandler, threeDInferenceSettingsToggleHandler, threeDInferenceSettingsLatToggleHandler, threeDInferenceSettingsMeshClusterToggleHandler, threeDInferenceSettingsDracoCompressionToggleHandler, ultralyticsProviderSettingsToggleHandler, openaiProviderSettingsToggleHandler, lightricksProviderSettingsToggleHandler, klingProviderSettingsToggleHandler, lumaProviderSettingsToggleHandler, briaProviderSettingsToggleHandler, pixverseProviderSettingsToggleHandler, alibabaProviderSettingsToggleHandler, mireloProviderSettingsToggleHandler, googleProviderSettingsToggleHandler, syncProviderSettingsToggleHandler, syncSegmentToggleHandler, settingsToggleHandler, outpaintSettingsToggleHandler, safetyInputsToggleHandler, imageInferenceSettingsColorPaletteToggleHandler, imageInferenceSettingsMoodboardsToggleHandler, imageInferenceSettingsStructuredPromptToggleHandler, imageInferenceSettingsPromptEnhanceToggleHandler, imageInferenceSettingsScoringRubricToggleHandler, audioInputToggleHandler, speechInputToggleHandler, briaProviderMaskToggleHandler, wanAnimateAdvancedFeatureSettingsToggleHandler, videoAdvancedFeatureInputsToggleHandler, audioInferenceInputsToggleHandler, audioInferenceReferenceVoiceToggleHandler, audioInferenceSpeechVoicesToggleHandler, videoInputsFrameToggleHandler, referenceVideosToggleHandler } from "./utils.js";
import { RUNWARE_NODE_TYPES, RUNWARE_NODE_PROPS, SEARCH_TERMS } from "./types.js";
const nodeInitList = [];
app.registerExtension({
name: "runware.ai",
async setup() {
api.addEventListener('runwareError', handleCustomErrors);
api.addEventListener('runwareImageCaption', captionNodeHandler);
api.addEventListener('runwareSaveText', saveTextHandler);
api.addEventListener('runwareMediaUUID', mediaUUIDHandler);
api.addEventListener('runwareSave3DFilepath', save3DFilepathHandler);
api.addEventListener('runwareVideoTranscription', videoTranscriptionHandler);
api.addEventListener('runwareVideoOutputs', videoOutputsHandler);
},
async nodeCreated(node) {
const nodeClass = node.comfyClass;
let crNodeProps = false;
if(typeof nodeClass === "string" && Object.values(RUNWARE_NODE_TYPES).includes(nodeClass)) {
crNodeProps = RUNWARE_NODE_PROPS[nodeClass];
} else {
return;
}
node.bgcolor = crNodeProps.bgColor;
if(nodeClass === RUNWARE_NODE_TYPES.APIMANAGER) {
APIKeyHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.IMAGECAPTION) {
const captionInput = node.widgets[1].inputEl;
captionInput.style.outline = "none";
captionInput.readOnly = true;
return;
} else if(nodeClass === RUNWARE_NODE_TYPES.MEDIAUPLOAD) {
// Style the mediaUUID widget to show full UUID
const mediaUUIDWidget = node.widgets.find(widget => widget.name === "mediaUUID");
if(mediaUUIDWidget && mediaUUIDWidget.inputEl) {
mediaUUIDWidget.inputEl.style.width = "100%";
mediaUUIDWidget.inputEl.style.minWidth = "300px";
mediaUUIDWidget.inputEl.style.fontSize = "11px";
mediaUUIDWidget.inputEl.style.fontFamily = "monospace";
}
} else if(nodeClass === RUNWARE_NODE_TYPES.VIDEOINFERENCEOUTPUTS) {
// Style draftId and videoId widgets to show full IDs (like Media Upload)
const draftIdWidget = node.widgets.find(widget => widget.name === "draftId");
const videoIdWidget = node.widgets.find(widget => widget.name === "videoId");
for(const w of [draftIdWidget, videoIdWidget]) {
if(w && w.inputEl) {
w.inputEl.style.width = "100%";
w.inputEl.style.minWidth = "300px";
w.inputEl.style.fontSize = "11px";
w.inputEl.style.fontFamily = "monospace";
}
}
} else if(nodeClass === RUNWARE_NODE_TYPES.SAVE3D) {
// Style the filepath widget to show full path (like Media Upload mediaUUID)
const filepathWidget = node.widgets.find(widget => widget.name === "filepath");
if(filepathWidget && filepathWidget.inputEl) {
filepathWidget.inputEl.style.width = "100%";
filepathWidget.inputEl.style.minWidth = "300px";
filepathWidget.inputEl.style.fontSize = "11px";
filepathWidget.inputEl.style.fontFamily = "monospace";
}
}
const nodeWidgets = node.widgets;
// Handle node-specific handlers first - each node has its own dedicated handler
// These must be called BEFORE the colorModeOnly check
if(nodeClass === RUNWARE_NODE_TYPES.IMAGEINFERENCE) {
imageInferenceToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.VIDEOINFERENCE) {
videoInferenceDimensionsHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.UPSCALER) {
upscalerToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.IMAGEUPSCALERSETTINGS) {
imageUpscalerSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.VIDEOUPSCALER) {
videoUpscalerToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.AUDIOINFERENCE) {
audioInferenceToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.IMAGEINFERENCEADVANCEDFEATURES) {
imageInferenceAdvancedFeaturesToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.WATERMARKADVANCEDFEATURE) {
watermarkAdvancedFeatureToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.VIDEOINFERENCESPEECHINPUT) {
videoInferenceSpeechInputToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.REGIONALPROMPTINGADVFEATUREREGIONS) {
regionalPromptingRegionsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.AUDIOINFERENCESPEECH) {
audioInferenceSpeechToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.AUDIOINFERENCESPEECHVOICES) {
audioInferenceSpeechVoicesToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.AUDIOSETTINGS) {
audioSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.TEXTINFERENCESETTINGS) {
textInferenceSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.VIDEOSETTINGS) {
videoSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.VIDEOINFERENCESETTINGSTTS) {
videoInferenceSettingsTtsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.VIDEOINFERENCESETTINGSACTIVESPEAKERDETECTION) {
videoInferenceSettingsActiveSpeakerDetectionToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.VIDEOINFERENCESETTINGSACTIVESPEAKERBOUNDINGBOXES) {
videoInferenceSettingsActiveSpeakerBoundingBoxesToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.VIDEOINFERENCESETTINGSSEGMENTS) {
videoInferenceSettingsSegmentsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.VIDEOINFERENCESETTINGSEDIT) {
videoInferenceSettingsControlToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.ACCELERATOROPTIONS) {
acceleratorOptionsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.BYTEDANCEPROVIDERSETTINGS) {
bytedanceProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.XAIPROVIDERSETTINGS) {
xaiProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.ULTRALYTICSINPUTS) {
ultralyticsProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.OPENAIPROVIDERSETTINGS) {
openaiProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.LIGHTRICKSPROVIDERSETTINGS) {
lightricksProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.KLINGPROVIDERSETTINGS) {
klingProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.LUMAPROVIDERSETTINGS) {
lumaProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.BRIAPROVIDERSETTINGS) {
briaProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.PIXVERSEPROVIDERSETTINGS) {
pixverseProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.ALIBABAPROVIDERSETTINGS) {
alibabaProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.MIRELOPROVIDERSETTINGS) {
mireloProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.GOOGLEPROVIDERSETTINGS) {
googleProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.SYNCPROVIDERSETTINGS) {
syncProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.SYNCSEGMENT) {
syncSegmentToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.VIDUPROVIDERSETTINGS) {
viduProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.SOURCEFULPROVIDERSETTINGS) {
sourcefulProviderSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.SOURCEFULPROVIDERSETTINGSFONTS) {
sourcefulProviderSettingsFontsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.SETTINGS) {
settingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.OUTPAINT) {
outpaintSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.SAFETYINPUTS) {
safetyInputsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.IMAGEINFERENCESETTINGSCOLORPALETTE) {
imageInferenceSettingsColorPaletteToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.IMAGEINFERENCESETTINGSMOODBOARDS) {
imageInferenceSettingsMoodboardsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.IMAGEINFERENCESETTINGSSTRUCTUREDPROMPT) {
imageInferenceSettingsStructuredPromptToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.IMAGEINFERENCESETTINGSPROMPTENHANCE) {
imageInferenceSettingsPromptEnhanceToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.IMAGEINFERENCESETTINGSSCORINGRUBRIC) {
imageInferenceSettingsScoringRubricToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.AUDIOINPUT) {
audioInputToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.SPEECHINPUT) {
speechInputToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.BRIAPROVIDERMASK) {
briaProviderMaskToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.WANANIMATEADVANCEDFEATURESETTINGS) {
wanAnimateAdvancedFeatureSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.VIDEOADVANCEDFEATUREINPUTS) {
videoAdvancedFeatureInputsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.AUDIOINFERENCEINPUTS) {
audioInferenceInputsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.AUDIOINFERENCEINPUTSREFERENCEAUDIO) {
audioInferenceReferenceVoiceToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.VIDEOINPUTSFRAMEIMAGES) {
videoInputsFrameToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.REFERENCEVIDEOS ||
nodeClass === RUNWARE_NODE_TYPES.REFERENCEVIDEOS_LEGACY) {
referenceVideosToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.THREEDINFERENCE) {
threeDInferenceToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.THREEDINFERENCESETTINGS) {
threeDInferenceSettingsToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.THREEDINFERENCESETTINGSSPARSESTRUCTURE ||
nodeClass === RUNWARE_NODE_TYPES.THREEDINFERENCESETTINGSSHAPESLAT ||
nodeClass === RUNWARE_NODE_TYPES.THREEDINFERENCESETTINGSTEXSLAT) {
threeDInferenceSettingsLatToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.THREEDINFERENCESETTINGSMESHCLUSTER) {
threeDInferenceSettingsMeshClusterToggleHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.THREEDINFERENCESETTINGSDRACOCOMPRESSION) {
threeDInferenceSettingsDracoCompressionToggleHandler(node);
}
if(crNodeProps.colorModeOnly === true) return;
if(nodeWidgets.length <= 0) return;
if(nodeClass === RUNWARE_NODE_TYPES.VIDEOMODELSEARCH) {
videoModelSearchFilterHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.THREEDMODELSEARCH) {
threeDModelSearchFilterHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.AUDIOMODELSEARCH) {
audioModelSearchFilterHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.TEXTMODELSEARCH) {
textModelSearchFilterHandler(node);
} else if(nodeClass === RUNWARE_NODE_TYPES.VECTORIZE) {
vectorizeModelSearchFilterHandler(node);
vectorizeToggleHandler(node);
}
for(const nodeWidget of nodeWidgets) {
const widgetName = nodeWidget.name;
const widgetType = nodeWidget.type;
if(crNodeProps.promptEnhancer === true && widgetType === "customtext") {
if (widgetName === "positivePrompt" || widgetName === "negativePrompt") {
nodeWidget.inputEl.addEventListener('keydown', promptEnhanceHandler);
}
} else if(crNodeProps.liveDimensions === true && widgetType === "combo" && widgetName === "dimensions") {
syncDimensionsNodeHandler(node, nodeWidget);
}
if(crNodeProps.liveSearch === true) {
if(widgetType === "text" && SEARCH_TERMS.includes(widgetName)) {
node.callback = function(){};
searchNodeHandler(node, nodeWidget);
nodeInitList.push(node);
}
}
}
},
loadedGraphNode(node) {
if(nodeInitList.includes(node)) node.callback();
}
})
-754
View File
@@ -1,754 +0,0 @@
const DEFAULT_BGCOLOR = "#5345bf";
const DEFAULT_DIMENSIONS_LIST = {
"Square (512x512)": "512x512",
"Square HD (1024x1024)": "1024x1024",
"Square 2K (2048x2048)": "2048x2048",
"Portrait 3:4 (768x1024)": "768x1024",
"Portrait 9:16 (576x1024)": "576x1024",
"Landscape 4:3 (1024x768)": "1024x768",
"Landscape 16:9 (1024x576)": "1024x576"
};
const DEFAULT_MODELS_ARCH_LIST = {
"All": "all",
"FLUX.1-Schnell": "flux1s",
"FLUX.1-Dev": "flux1d",
"FLUX.1-Krea": "flux1d",
"Pony": "pony",
"SD 1.5": "sd1x",
"SD 1.5 Hyper": "sdhyper",
"SD 1.5 LCM": "sd1xlcm",
"SD 3": "sd3",
"SDXL 1.0": "sdxl",
"SDXL 1.0 LCM": "sdxllcm",
"SDXL Distilled": "sdxldistilled",
"SDXL Hyper": "sdxlhyper",
"SDXL Lightning": "sdxllightning",
"SDXL Turbo": "sdxlturbo",
};
const DEFAULT_CONTROLNET_CONDITIONING_LIST = {
"All": "all",
"Canny": "canny",
"Depth": "depth",
"MLSD": "mlsd",
"Normal BAE": "normalbae",
"Open Pose": "openpose",
"Tile": "tile",
"Seg": "seg",
"Line Art": "lineart",
"Line Art Anime": "lineart_anime",
"Shuffle": "shuffle",
"Scribble": "scribble",
"Soft Edge": "softedge",
};
const SEARCH_TERMS = ["Model Search", "Lora Search", "ControlNet Search", "Embedding Search", "VAE Search"];
const MODEL_TYPES_TERMS = ["ModelType", "LoraType", "ControlNetType"];
const MODEL_LIST_TERMS = ["ModelList", "LoraList", "ControlNetList", "EmbeddingList", "VAEList"];
const RUNWARE_NODE_TYPES = {
IMAGEINFERENCE: "Runware Image Inference",
KONTEXTINFERENCE: "Runware Kontext Inference",
IMAGENINFERENCE: "Runware Imagen Inference",
OUTPAINT: "Runware Outpaint",
PHOTOMAKER: "Runware PhotoMaker V2",
MODELSEARCH: "Runware Model Search",
MULTIINFERENCE: "Runware Multi Inference",
TEACACHE: "Runware TeaCache",
DEEPCACHE: "Runware DeepCache",
LORASEARCH: "Runware Lora Search",
CONTROLNET: "Runware ControlNet",
BGREMOVAL: "Runware Background Removal",
UPSCALER: "Runware Image Upscaler",
IMAGEUPSCALERSETTINGS: "Runware Image Upscaler Settings",
REFINER: "Runware Refiner",
LORACOMBINE: "Runware Lora Combine",
CONTROLNETCOMBINE: "Runware ControlNet Combine",
IPADAPTER: "Runware IPAdapter",
IPADAPTERSCOMBINE: "Runware IPAdapters Combine",
IMAGEMASKING: "Runware Image Masking",
CONTROLNETPREPROCESSING: "Runware ControlNet PreProcessor",
APIMANAGER: "Runware API Manager",
IMAGECAPTION: "Runware Image Caption",
EMBEDDING: "Runware Embedding Search",
EMBEDDINGCOMBINE: "Runware Embedding Combine",
VAE: "Runware VAE Search",
REFERENCEIMAGES: "Runware Reference Images",
IMAGEINFERENCEINPUTS: "Runware Image Inference Inputs",
IMAGEINFERENCEINPUTSFONTS: "Runware Image Inference Inputs Fonts",
IMAGEINFERENCEADVANCEDFEATURES: "Runware Image Advanced Feature Input",
VIDEOINFERENCE: "Runware Video Inference",
VIDEOMODELSEARCH: "Runware Video Model Search",
FRAMEIMAGES: "Runware Frame Images",
AUDIOINFERENCE: "Runware Audio Inference",
AUDIOINFERENCESPEECH: "Runware Audio Inference Speech",
AUDIOINFERENCESPEECHVOICES: "Runware Audio Inference Speech Voices",
AUDIOMODELSEARCH: "Runware Audio Model Search",
TEXTMODELSEARCH: "Runware Text Model Search",
TEXTINFERENCE: "Runware Text Inference",
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",
AUDIOINFERENCEINPUTSREFERENCEAUDIO: "Runware Audio Inference Inputs Reference Audio",
AUDIOSETTINGS: "Runware Audio Inference Settings",
AUDIOSETTINGSVOICEMODIFY: "Runware Audio Inference Settings Voice Modify",
PIXVERSEPROVIDERSETTINGS: "Runware Pixverse Provider Settings",
OPENAIPROVIDERSETTINGS: "Runware OpenAI Provider Settings",
BYTEDANCEPROVIDERSETTINGS: "Runware Bytedance Provider Settings",
BRIAPROVIDERSETTINGS: "Runware Bria Provider Settings",
BRIAPROVIDERMASK: "Runware Bria Provider Mask",
ELEVENLABSPROVIDERSETTINGS: "Runware ElevenLabs Provider Settings",
ELEVENLABSPROVIDERSETTINGSSECTIONS: "Runware ElevenLabs Provider Settings Sections",
VIDUPROVIDERSETTINGS: "Runware Vidu Provider Settings",
LORAMIX: "Runware Lora Mix",
MEDIAUPLOAD: "Runware Media Upload",
INPUTAUDIOS: "Runware Input Audios",
AUDIOINPUT: "Runware Video Audio Input",
SPEECHINPUT: "Runware Video Speech Input",
REFERENCEVIDEOS: "Runware Video Inference Inputs Reference Videos",
REFERENCEVIDEOS_LEGACY: "Runware Reference Videos",
REFERENCEVOICES: "Runware Video Inference Inputs Reference Voices",
REFERENCEVOICES_LEGACY: "Runware Reference Voices",
REFERENCEAUDIOS: "Runware Video Inference Inputs Reference Audios",
VIDEOINPUTSREFERENCES_LEGACY: "Runware Video Inputs References",
VIDEOINPUTSREFERENCEMULTIIMAGES: "Runware Video Inference Inputs Reference Images Multiple Images Connector",
VIDEOINFERENCEINPUTS: "Runware Video Inference Inputs",
VIDEOINFERENCEELEMENTS: "Runware Video Inference Elements",
VIDEOINFERENCEELEMENTSCOMBINE: "Runware Video Inference Elements Combine",
VIDEOINFERENCESPEECHINPUT: "Runware Video Inference Speech Input",
VIDEOINFERENCELORA: "Runware Video Inference Lora",
VIDEOSETTINGS: "Runware Video Inference Settings",
VIDEOINFERENCESETTINGSTTS: "Runware Video Inference Settings TTS",
VIDEOINFERENCESETTINGSACTIVESPEAKERDETECTION: "Runware Video Inference Settings Active Speaker Detection",
VIDEOINFERENCESETTINGSACTIVESPEAKERBOUNDINGBOXES: "Runware Video Inference Settings Active Speaker Bounding Boxes",
VIDEOINFERENCESETTINGSSEGMENTS: "Runware Video Inference Settings Segments",
VIDEOINFERENCESETTINGSEDIT: "Runware Video Inference Settings Edit",
VIDEOINFERENCESETTINGSSOURCEPOSITION: "Runware Video Inference Settings Source Position",
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",
MIRELOPROVIDERSETTINGS: "Runware Mirelo Provider Settings",
ALIBABAPROVIDERSETTINGS: "Runware Alibaba Provider Settings",
BLACKFORESTPROVIDERSETTINGS: "Runware BlackForest Labs Provider Settings",
XAIPROVIDERSETTINGS: "Runware xAI Provider Settings",
RECRAFTPROVIDERSETTINGS: "Runware Recraft Provider Settings",
RECRAFTCOLOR: "Runware Provider Settings Recraft Color",
SOURCEFULPROVIDERSETTINGS: "Runware Sourceful Provider Settings",
SOURCEFULPROVIDERSETTINGSFONTS: "Runware Sourceful Provider Settings Fonts",
ULTRALYTICSINPUTS: "Runware Image Inference Ultralytics",
GOOGLEPROVIDERSETTINGS: "Runware Google Provider Settings",
SYNCSEGMENT: "Runware Sync Segment",
SYNCPROVIDERSETTINGS: "Runware Sync Provider Settings",
VECTORIZE: "Runware Vectorize",
VIDEOBGREMOVAL: "Runware Video Background Removal",
VIDEOUPSCALER: "Runware Video Upscaler",
VIDEOINPUTSREFERENCES: "Runware Video Inference Inputs Reference Images",
VIDEOINPUTSFRAMEIMAGES: "Runware Video Inputs Frame Images",
ACCELERATOROPTIONS: "Runware Accelerator Options",
VIDEOTRANSCRIPTION: "Runware Video Transcription",
MINIMAXPROVIDERSETTINGS: "Runware MiniMax Provider Settings",
SAFETYINPUTS: "Runware Safety Inputs",
SETTINGS: "Runware Settings",
IMAGEINFERENCESETTINGSCOLORPALETTE: "Runware Image Inference Settings Color Palette",
IMAGEINFERENCESETTINGSMOODBOARDS: "Runware Image Inference Settings Moodboards",
IMAGEINFERENCESETTINGSSTRUCTUREDPROMPT: "Runware Image Inference Settings Structured Prompt",
IMAGEINFERENCESETTINGSPROMPTENHANCE: "Runware Image Inference Settings Prompt Enhance",
IMAGEINFERENCESETTINGSSCORINGRUBRIC: "Runware Image Inference Settings Scoring Rubric",
IMAGEINFERENCESETTINGSSCORINGRUBRICCOMBINE: "Runware Image Inference Settings Scoring Rubric Combine",
VIDEOADVANCEDFEATUREINPUTS: "Runware Video Advanced Feature Inputs",
WANANIMATEADVANCEDFEATURESETTINGS: "Runware Wan Animate Advanced Feature Settings",
SAVEIMAGE: "Runware Save Image",
SAVETEXT: "Runware Save Text",
VIDEOINFERENCEOUTPUTS: "Runware Video Inference Outputs",
THREEDINFERENCE: "Runware 3D Inference",
THREEDMODELSEARCH: "Runware 3D Model Search",
THREEDINFERENCEINPUTS: "Runware 3D Inference Inputs",
THREEDINFERENCESETTINGS: "Runware 3D Inference Settings",
THREEDINFERENCESETTINGSSPARSESTRUCTURE: "Runware 3D Inference Settings Sparse Structure",
THREEDINFERENCESETTINGSSHAPESLAT: "Runware 3D Inference Settings Shape Slat",
THREEDINFERENCESETTINGSTEXSLAT: "Runware 3D Inference Settings Tex Slat",
THREEDINFERENCESETTINGSMESHCLUSTER: "Runware 3D Inference Settings Mesh Cluster",
THREEDINFERENCESETTINGSDRACOCOMPRESSION: "Runware 3D Inference Settings Draco Compression",
SAVE3D: "Runware Save 3D",
LOADMESH: "Runware Load Mesh",
WATERMARKADVANCEDFEATURE: "Runware Watermark Advanced Feature",
REGIONALPROMPTINGADVFEATURE: "Runware Regional Prompting Advanced Feature",
REGIONALPROMPTINGADVFEATUREREGIONS: "Runware Regional Prompting Advanced Feature Regions",
};
const RUNWARE_NODE_PROPS = {
[RUNWARE_NODE_TYPES.IMAGEINFERENCE]: {
bgColor: DEFAULT_BGCOLOR,
liveDimensions: true,
promptEnhancer: true,
},
[RUNWARE_NODE_TYPES.KONTEXTINFERENCE]: {
bgColor: DEFAULT_BGCOLOR,
promptEnhancer: true,
},
[RUNWARE_NODE_TYPES.IMAGENINFERENCE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.REFERENCEIMAGES]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.OUTPAINT]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.PHOTOMAKER]: {
bgColor: DEFAULT_BGCOLOR,
liveDimensions: true,
promptEnhancer: true,
},
[RUNWARE_NODE_TYPES.MODELSEARCH]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: true,
},
[RUNWARE_NODE_TYPES.MULTIINFERENCE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.TEACACHE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.DEEPCACHE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.LORASEARCH]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: true,
},
[RUNWARE_NODE_TYPES.EMBEDDING]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: true,
},
[RUNWARE_NODE_TYPES.CONTROLNET]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: true,
},
[RUNWARE_NODE_TYPES.VAE]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: true,
},
[RUNWARE_NODE_TYPES.EMBEDDINGCOMBINE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.CONTROLNETCONDITIONING]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: true,
},
[RUNWARE_NODE_TYPES.BGREMOVAL]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.UPSCALER]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IMAGEUPSCALERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.REFINER]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.LORACOMBINE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IPADAPTER]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IPADAPTERSCOMBINE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.CONTROLNETCOMBINE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IMAGEMASKING]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.CONTROLNETPREPROCESSING]: {
bgColor: DEFAULT_BGCOLOR,
liveDimensions: true,
},
[RUNWARE_NODE_TYPES.APIMANAGER]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IMAGECAPTION]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINFERENCE]: {
bgColor: DEFAULT_BGCOLOR,
liveDimensions: true,
promptEnhancer: true,
},
[RUNWARE_NODE_TYPES.VIDEOMODELSEARCH]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: true,
},
[RUNWARE_NODE_TYPES.FRAMEIMAGES]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.AUDIOINFERENCE]: {
bgColor: DEFAULT_BGCOLOR,
promptEnhancer: true,
},
[RUNWARE_NODE_TYPES.AUDIOINFERENCESPEECH]: {
bgColor: DEFAULT_BGCOLOR,
},
[RUNWARE_NODE_TYPES.AUDIOINFERENCESPEECHVOICES]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.AUDIOMODELSEARCH]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: true,
},
[RUNWARE_NODE_TYPES.TEXTMODELSEARCH]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: false,
},
[RUNWARE_NODE_TYPES.TEXTINFERENCE]: {
bgColor: DEFAULT_BGCOLOR,
},
[RUNWARE_NODE_TYPES.TEXTINFERENCEMESSAGES]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.TEXTINFERENCEMESSAGE]: {
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,
},
[RUNWARE_NODE_TYPES.AUDIOSECTIONS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IMAGEINFERENCEINPUTS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IMAGEINFERENCEINPUTSFONTS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IMAGEINFERENCEADVANCEDFEATURES]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.WATERMARKADVANCEDFEATURE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINFERENCESPEECHINPUT]: {
bgColor: DEFAULT_BGCOLOR,
},
[RUNWARE_NODE_TYPES.VIDEOINFERENCELORA]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.REGIONALPROMPTINGADVFEATURE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.REGIONALPROMPTINGADVFEATUREREGIONS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.PIXVERSEPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.OPENAIPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.BYTEDANCEPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.BRIAPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.BRIAPROVIDERMASK]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.ELEVENLABSPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.ELEVENLABSPROVIDERSETTINGSSECTIONS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDUPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.LORAMIX]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.MEDIAUPLOAD]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.INPUTAUDIOS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.AUDIOINPUT]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.SPEECHINPUT]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.REFERENCEVIDEOS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.REFERENCEVIDEOS_LEGACY]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.REFERENCEVOICES]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.REFERENCEVOICES_LEGACY]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.REFERENCEAUDIOS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINFERENCEINPUTS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINPUTSREFERENCEMULTIIMAGES]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINFERENCEELEMENTS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINFERENCEELEMENTSCOMBINE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.LIGHTRICKSPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.KLINGPROVIDERSETTINGS]: {
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,
},
[RUNWARE_NODE_TYPES.LUMAPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.MIDJOURNEYPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.MIRELOPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.ALIBABAPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.BLACKFORESTPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.XAIPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.RECRAFTPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
},
[RUNWARE_NODE_TYPES.RECRAFTCOLOR]: {
bgColor: DEFAULT_BGCOLOR,
},
[RUNWARE_NODE_TYPES.SOURCEFULPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.SOURCEFULPROVIDERSETTINGSFONTS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.ULTRALYTICSINPUTS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.GOOGLEPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.SYNCSEGMENT]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.SYNCPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VECTORIZE]: {
bgColor: DEFAULT_BGCOLOR,
liveDimensions: true,
},
[RUNWARE_NODE_TYPES.VIDEOBGREMOVAL]: {
bgColor: DEFAULT_BGCOLOR,
},
[RUNWARE_NODE_TYPES.VIDEOUPSCALER]: {
bgColor: DEFAULT_BGCOLOR,
},
[RUNWARE_NODE_TYPES.VIDEOINPUTSREFERENCES]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINPUTSREFERENCES_LEGACY]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINPUTSFRAMEIMAGES]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.AUDIOINFERENCEINPUTS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.AUDIOINFERENCEINPUTSREFERENCEAUDIO]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.AUDIOSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
},
[RUNWARE_NODE_TYPES.AUDIOSETTINGSVOICEMODIFY]: {
bgColor: DEFAULT_BGCOLOR,
},
[RUNWARE_NODE_TYPES.VIDEOSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
},
[RUNWARE_NODE_TYPES.VIDEOINFERENCESETTINGSTTS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINFERENCESETTINGSACTIVESPEAKERDETECTION]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINFERENCESETTINGSACTIVESPEAKERBOUNDINGBOXES]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINFERENCESETTINGSSEGMENTS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINFERENCESETTINGSEDIT]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINFERENCESETTINGSSOURCEPOSITION]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.ACCELERATOROPTIONS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOTRANSCRIPTION]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.MINIMAXPROVIDERSETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.SAFETYINPUTS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.SETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IMAGEINFERENCESETTINGSCOLORPALETTE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IMAGEINFERENCESETTINGSMOODBOARDS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IMAGEINFERENCESETTINGSSTRUCTUREDPROMPT]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IMAGEINFERENCESETTINGSPROMPTENHANCE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IMAGEINFERENCESETTINGSSCORINGRUBRIC]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IMAGEINFERENCESETTINGSSCORINGRUBRICCOMBINE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOADVANCEDFEATUREINPUTS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.WANANIMATEADVANCEDFEATURESETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.SAVEIMAGE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.SAVETEXT]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.VIDEOINFERENCEOUTPUTS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.THREEDINFERENCE]: {
bgColor: DEFAULT_BGCOLOR,
promptEnhancer: true,
},
[RUNWARE_NODE_TYPES.THREEDMODELSEARCH]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: false,
},
[RUNWARE_NODE_TYPES.THREEDINFERENCEINPUTS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.THREEDINFERENCESETTINGS]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.THREEDINFERENCESETTINGSSPARSESTRUCTURE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.THREEDINFERENCESETTINGSSHAPESLAT]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.THREEDINFERENCESETTINGSTEXSLAT]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.THREEDINFERENCESETTINGSMESHCLUSTER]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.THREEDINFERENCESETTINGSDRACOCOMPRESSION]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.SAVE3D]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.LOADMESH]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
};
export {
DEFAULT_BGCOLOR,
DEFAULT_DIMENSIONS_LIST,
DEFAULT_MODELS_ARCH_LIST,
DEFAULT_CONTROLNET_CONDITIONING_LIST,
SEARCH_TERMS,
MODEL_TYPES_TERMS,
MODEL_LIST_TERMS,
RUNWARE_NODE_TYPES,
RUNWARE_NODE_PROPS
}
-6243
View File
File diff suppressed because it is too large Load Diff
-172
View File
@@ -1,172 +0,0 @@
class acceleratorOptions:
"""Accelerator Options node for configuring caching options for faster generation"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"fbcache": ("BOOLEAN", {
"tooltip": "Enable Frame Buffer Cache for faster generation",
"default": None,
}),
"useCacheDistance": ("BOOLEAN", {
"tooltip": "Enable to include cacheDistance parameter in API request.",
"default": False,
}),
"cacheDistance": ("FLOAT", {
"tooltip": "Cache distance parameter for FBCache",
"default": 0.0,
"min": 0.0,
"max": 1.0,
"step": 0.01,
}),
"teaCache": ("BOOLEAN", {
"tooltip": "Enable TeaCache for transformer-based models (Flux, SD3). Does not work with UNet models like SDXL or SD1.5. Particularly effective for iterative editing workflows.",
"default": None,
}),
"useTeaCacheDistance": ("BOOLEAN", {
"tooltip": "Enable to include teaCacheDistance parameter in API request.",
"default": False,
}),
"teaCacheDistance": ("FLOAT", {
"tooltip": "Controls TeaCache aggressiveness: 0.0 (conservative/quality) to 1.0 (aggressive/speed). Lower values maintain quality, higher values prioritize speed.",
"default": 0.0,
"min": 0.0,
"max": 1.0,
"step": 0.01,
}),
"deepCache": ("BOOLEAN", {
"tooltip": "Enable DeepCache for UNet-based models (SDXL, SD1.5). Not applicable to transformer models. Provides performance improvements for high-throughput scenarios.",
"default": None,
}),
"useDeepCacheOptions": ("BOOLEAN", {
"tooltip": "Enable to include DeepCache options (deepCacheInterval and deepCacheBranchId) in API request.",
"default": False,
}),
"deepCacheInterval": ("INT", {
"tooltip": "Frequency of feature caching - steps between cache operations. Larger values = faster but may impact quality. Smaller values = better quality but slower.",
"default": 1,
"min": 1,
"max": 20,
}),
"deepCacheBranchId": ("INT", {
"tooltip": "Network branch for caching (0=shallowest, deeper=more conservative). Lower IDs = aggressive caching/faster, higher IDs = conservative caching/better quality.",
"default": 0,
"min": 0,
"max": 10,
}),
"useCacheSteps": ("BOOLEAN", {
"tooltip": "Enable to include cache step control parameters (step numbers) in API request.",
"default": False,
}),
"cacheStartStep": ("INT", {
"tooltip": "Inference step number where caching begins (0 to total steps). Only included when useCacheSteps is enabled and value > 0.",
"default": 0,
"min": 0,
"max": 200,
}),
"cacheEndStep": ("INT", {
"tooltip": "Inference step number where caching stops (must be > cacheStartStep). Only included when useCacheSteps is enabled.",
"default": 1,
"min": 1,
"max": 200,
}),
"useCachePercentageSteps": ("BOOLEAN", {
"tooltip": "Enable to include cache step control parameters (percentages) in API request.",
"default": False,
}),
"cacheStartStepPercentage": ("INT", {
"tooltip": "Percentage of total steps where caching begins (0-99). Only included when useCachePercentageSteps is enabled and value > 0.",
"default": 0,
"min": 0,
"max": 99,
}),
"cacheEndStepPercentage": ("INT", {
"tooltip": "Percentage of total steps where caching stops (must be > cacheStartStepPercentage). Only included when useCachePercentageSteps is enabled.",
"default": 1,
"min": 1,
"max": 100,
}),
"useCacheMaxConsecutiveSteps": ("BOOLEAN", {
"tooltip": "Enable to include cacheMaxConsecutiveSteps parameter in API request.",
"default": False,
}),
"cacheMaxConsecutiveSteps": ("INT", {
"tooltip": "Max consecutive steps using cached computations before forcing fresh computation. Prevents quality degradation from extended cache reuse.",
"default": 1,
"min": 1,
"max": 5,
}),
},
}
DESCRIPTION = "Configure Runware accelerator options for faster image generation. Supports TeaCache (transformer models like Flux/SD3) and DeepCache (UNet models like SDXL/SD1.5) with fine-grained control over caching parameters and step ranges. Note: These caching methods work best with deterministic schedulers (Euler, DDIM) rather than stochastic schedulers."
FUNCTION = "acceleratorOptions"
RETURN_TYPES = ("RUNWAREACCELERATOR",)
RETURN_NAMES = ("Runware Accelerator",)
CATEGORY = "Runware"
def acceleratorOptions(self, **kwargs):
"""Build accelerator options dictionary"""
params = self._extractParameters(kwargs)
acceleratorOptions = self._buildAcceleratorOptions(params)
return (acceleratorOptions,)
def _extractParameters(self, kwargs):
"""Extract all parameters from kwargs"""
return {
"fbcache": kwargs.get("fbcache"),
"useCacheDistance": kwargs.get("useCacheDistance", False),
"cacheDistance": kwargs.get("cacheDistance", 0.0),
"teaCache": kwargs.get("teaCache"),
"useTeaCacheDistance": kwargs.get("useTeaCacheDistance", False),
"teaCacheDistance": kwargs.get("teaCacheDistance", 0.0),
"deepCache": kwargs.get("deepCache"),
"useDeepCacheOptions": kwargs.get("useDeepCacheOptions", False),
"deepCacheInterval": kwargs.get("deepCacheInterval", 1),
"deepCacheBranchId": kwargs.get("deepCacheBranchId", 0),
"useCacheSteps": kwargs.get("useCacheSteps", False),
"cacheStartStep": kwargs.get("cacheStartStep", 0),
"cacheEndStep": kwargs.get("cacheEndStep", 1),
"useCachePercentageSteps": kwargs.get("useCachePercentageSteps", False),
"cacheStartStepPercentage": kwargs.get("cacheStartStepPercentage", 0),
"cacheEndStepPercentage": kwargs.get("cacheEndStepPercentage", 1),
"useCacheMaxConsecutiveSteps": kwargs.get("useCacheMaxConsecutiveSteps", False),
"cacheMaxConsecutiveSteps": kwargs.get("cacheMaxConsecutiveSteps", 1),
}
def _buildAcceleratorOptions(self, params):
"""Build accelerator options dictionary from parameters"""
acceleratorOptions = {}
if params["fbcache"] is True:
acceleratorOptions["fbcache"] = params["fbcache"]
if params["useCacheDistance"]:
acceleratorOptions["cacheDistance"] = params["cacheDistance"]
if params["teaCache"] is True:
acceleratorOptions["teaCache"] = params["teaCache"]
if params["useTeaCacheDistance"]:
acceleratorOptions["teaCacheDistance"] = params["teaCacheDistance"]
if params["deepCache"] is True:
acceleratorOptions["deepCache"] = params["deepCache"]
if params["useDeepCacheOptions"]:
acceleratorOptions["deepCacheInterval"] = params["deepCacheInterval"]
acceleratorOptions["deepCacheBranchId"] = params["deepCacheBranchId"]
if params["useCacheSteps"]:
if params["cacheStartStep"] > 0:
acceleratorOptions["cacheStartStep"] = params["cacheStartStep"]
acceleratorOptions["cacheEndStep"] = params["cacheEndStep"]
if params["useCachePercentageSteps"]:
if params["cacheStartStepPercentage"] > 0:
acceleratorOptions["cacheStartStepPercentage"] = params["cacheStartStepPercentage"]
acceleratorOptions["cacheEndStepPercentage"] = params["cacheEndStepPercentage"]
if params["useCacheMaxConsecutiveSteps"]:
acceleratorOptions["cacheMaxConsecutiveSteps"] = params["cacheMaxConsecutiveSteps"]
return acceleratorOptions
@@ -1,2 +0,0 @@
# Advanced Feature Settings module
@@ -1,99 +0,0 @@
"""
Runware Wan Animate Advanced Feature Settings Node
Provides Wan Animate-specific settings for video generation
"""
from typing import Dict, Any
class RunwareWanAnimateAdvancedFeatureSettings:
"""Runware Wan Animate Advanced Feature Settings Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useMode": ("BOOLEAN", {
"tooltip": "Enable to include mode parameter in API request.",
"default": True,
}),
"mode": (["animate", "replace"], {
"tooltip": "Animation mode: 'animate' uses pose detector and skeleton retargeting to animate a character from a reference image. 'replace' uses pose detector and segmentation model to replace a character in existing footage. Only used when 'Use Mode' is enabled.",
"default": "animate",
}),
"useRetargetPose": ("BOOLEAN", {
"tooltip": "Enable to include retargetPose parameter in API request. Only available for animate mode.",
"default": False,
}),
"retargetPose": ("BOOLEAN", {
"tooltip": "Retarget the pose of video image to match the reference image initial pose. That is achieved moving the hands' bones according to video. Means if the character from video moves his hands it will take the initial reference image pose and change the position of bones according to video movements. Supported only for animate mode. Only used when 'Use Retarget Pose' is enabled.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"usePrevSegCondFrames": ("BOOLEAN", {
"tooltip": "Enable to include prevSegCondFrames parameter in API request.",
"default": False,
}),
"prevSegCondFrames": ("INT", {
"tooltip": "How many frames to take from previous segment to keep temporal consistency. Less -> less consistent between the segments. More -> more time for inference. Only used when 'Use Prev Seg Cond Frames' is enabled.",
"default": 1,
"min": 1,
"max": 5,
}),
}
}
RETURN_TYPES = ("RUNWAREWANANIMATEADVANCEDFEATURESETTINGS",)
RETURN_NAMES = ("Wan Animate Advanced Feature Settings",)
FUNCTION = "create_wan_animate_settings"
CATEGORY = "Runware/Advanced Feature Settings"
DESCRIPTION = "Configure Wan Animate advanced feature settings including mode (animate/replace), pose retargeting, and previous segment conditioning frames for character animation and replacement."
def create_wan_animate_settings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create Wan Animate advanced feature settings"""
# Get use parameters
useMode = kwargs.get("useMode", True)
useRetargetPose = kwargs.get("useRetargetPose", False)
usePrevSegCondFrames = kwargs.get("usePrevSegCondFrames", False)
# Get actual parameters
mode = kwargs.get("mode", "animate")
retargetPose = kwargs.get("retargetPose", False)
prevSegCondFrames = kwargs.get("prevSegCondFrames", 1)
# Build settings dictionary
wanAnimateSettings = {}
# Add mode only if useMode is enabled
if useMode and mode is not None:
wanAnimateSettings["mode"] = mode
# Add retargetPose only if useRetargetPose is enabled
# Note: retargetPose is only supported for animate mode, but we'll let the API handle validation
if useRetargetPose:
wanAnimateSettings["retargetPose"] = retargetPose
# Add prevSegCondFrames only if usePrevSegCondFrames is enabled
if usePrevSegCondFrames:
wanAnimateSettings["prevSegCondFrames"] = prevSegCondFrames
# Clean up None values
wanAnimateSettings = {k: v for k, v in wanAnimateSettings.items() if v is not None}
# Return the full structure with the feature name as key (similar to providerSettings pattern)
if wanAnimateSettings:
return ({"wanAnimate": wanAnimateSettings},)
else:
return ({},)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareWanAnimateAdvancedFeatureSettings": RunwareWanAnimateAdvancedFeatureSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareWanAnimateAdvancedFeatureSettings": "Runware Wan Animate Advanced Feature Settings",
}
-84
View File
@@ -1,84 +0,0 @@
from .utils import runwareUtils as rwUtils
class apiManager:
"""API Manager node for configuring Runware API settings"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"API Key": (
"STRING",
{
"tooltip": "Define Or Change Your Runware API Key To Start Using Runware Image Inference Services.\n\n(e.g; BcVr1JVQM8FGzrRwb3GsbCq5ww1QwabV)\n\nIf You Don't Have One, You Can Get It From: https://my.runware.ai/keys",
},
),
"Max Timeout": (
"INT",
{
"tooltip": "Change Runware API Request Timeout In Seconds.\n\n(e.g; 90 Seconds).",
"min": 5,
"max": 99,
"default": int(rwUtils.getTimeout()),
},
),
"Image Output Quality": (
"INT",
{
"tooltip": "Sets the compression quality of the output image. Higher values preserve more quality but increase file size, lower values reduce file size but decrease quality.",
"min": 20,
"max": 99,
"default": int(rwUtils.getOutputQuality()),
},
),
"Image Output Format": (
["WEBP", "PNG", "JPEG"],
{
"tooltip": "Change the Default Image Output Format.",
"default": rwUtils.getOutputFormat(),
},
),
"Enable Images Caching": (
"BOOLEAN",
{
"label_on": "Enabled",
"label_off": "Disabled",
"tooltip": "Enable or disable image caching functionality.\n\nWhen enabled, images will be cached to improve performance and reduce redundant processing.",
"default": rwUtils.getEnableImagesCaching(),
},
),
"Min Image Cache Size": (
"INT",
{
"tooltip": "Set the minimum size (in KB) for images to be cached.",
"min": 30,
"max": 4096,
"step": 1,
"default": int(rwUtils.getMinImageCacheSize()),
},
),
},
}
DESCRIPTION = "API Managers is a Runware Utility That helps you define or change your API Key, Session Timeout, Image Output Format & Quality, and Image Caching settings From The ComfyUI Interface Without having to adjust the env file locally."
CATEGORY = "Runware"
RETURN_TYPES = ()
def manageApiSettings(self, **kwargs):
"""Manage API settings"""
apiKey = kwargs.get("API Key")
maxTimeout = kwargs.get("Max Timeout")
imageOutputQuality = kwargs.get("Image Output Quality")
imageOutputFormat = kwargs.get("Image Output Format")
enableImagesCaching = kwargs.get("Enable Images Caching")
minImageCacheSize = kwargs.get("Min Image Cache Size")
rwUtils.setApiKey(apiKey)
rwUtils.setTimeout(maxTimeout)
rwUtils.setOutputQuality(imageOutputQuality)
rwUtils.setOutputFormat(imageOutputFormat)
rwUtils.setEnableImagesCaching(enableImagesCaching)
rwUtils.setMinImageCacheSize(minImageCacheSize)
return ()
-577
View File
@@ -1,577 +0,0 @@
import uuid
import requests
import torch
import librosa
import tempfile
import os
from urllib.parse import urlparse
import comfy.model_management
from .utils import runwareUtils as rwUtils
class RunwareAudioInference:
"""Runware Audio Inference node for generating audio using Runware API"""
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"model": ("RUNWAREAUDIOMODEL", {
"tooltip": "AI model to use for audio generation"
}),
"positivePrompt": ("STRING", {
"multiline": True,
"default": "classical piano piece, gentle and melodic",
"tooltip": "Text description that guides the audio generation process"
}),
"negativePrompt": ("STRING", {
"multiline": True,
"default": "",
"tooltip": "Text description of what you don't want in the audio"
}),
"useDuration": ("BOOLEAN", {
"default": True,
"tooltip": "Enable/disable duration parameter in API request"
}),
"duration": ("INT", {
"default": 10,
"min": 10,
"max": 300,
"step": 1,
"tooltip": "Length of generated audio in seconds (10-300)"
}),
"useSampleRate": ("BOOLEAN", {
"default": False,
"tooltip": "Enable/disable sampleRate parameter in API request"
}),
"sampleRate": ([8000, 16000, 22050, 24000, 32000, 44100, 48000], {
"default": 32000,
"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"
}),
"bitrate": ([8, 16, 32, 64, 128, 256], {
"default": 128,
"tooltip": "Audio bitrate in kbps. Allowed: 8, 16, 32, 64, 128, 256. Applies to compressed formats (e.g. MP3, OGG).",
}),
"useChannels": ("BOOLEAN", {
"default": False,
"tooltip": "Enable/disable channels parameter in API request"
}),
"channels": ("INT", {
"default": 2,
"min": 1,
"max": 2,
"step": 1,
"tooltip": "Number of audio channels (1=mono, 2=stereo)"
}),
"outputFormat": (["MP3", "MP4", "WAV", "FLAC", "OGG"], {
"default": "MP3",
"tooltip": "Format of the output audio"
}),
"numberResults": ("INT", {
"default": 1,
"min": 1,
"max": 3,
"tooltip": "Number of audio files to generate"
}),
"useSeed": ("BOOLEAN", {
"tooltip": "Enable to include seed parameter in API request for reproducible generation.",
"default": False,
}),
"seed": ("INT", {
"tooltip": "Seed for reproducible audio generation. Only used when 'Use Seed' is enabled.",
"default": 1,
"min": 1,
"max": 2147483647,
}),
"useSteps": ("BOOLEAN", {
"tooltip": "Enable to include steps parameter in API request. Disable if your model doesn't support steps.",
"default": False,
}),
"steps": ("INT", {
"tooltip": "Number of inference steps for audio generation. More steps generally result in higher quality but longer generation time.",
"default": 20,
"min": 1,
"max": 100,
}),
"useStrength": ("BOOLEAN", {
"tooltip": "Enable to include strength parameter in API request. Used for audio-to-audio generation.",
"default": False,
}),
"strength": ("FLOAT", {
"tooltip": "Influence of the input audio in audio-to-audio generation. Lower = more original, higher = more variation. Only used when 'Use Strength' is enabled.",
"default": 0.8,
"min": 0.0,
"max": 1.0,
"step": 0.01,
}),
"useCFGScale": ("BOOLEAN", {
"tooltip": "Enable to include CFGScale parameter in API request. Disable if your model doesn't support CFG scale.",
"default": False,
}),
"CFGScale": ("FLOAT", {
"tooltip": "Classifier-free guidance scale. Higher values adhere more closely to the prompt. Only used when 'Use CFG Scale' is enabled.",
"default": 12.0,
"min": 1.0,
"max": 50.0,
"step": 0.5,
}),
},
"optional": {
"inputs": ("RUNWAREAUDIOINFERENCEINPUTS", {
"tooltip": "Custom inputs for audio generation (e.g., video URL for audio extraction)"
}),
"settings": ("RUNWAREAUDIOSETTINGS", {
"tooltip": "Connect Runware Audio Inference Settings for includePrefix, topK, temperature, audioTemperature, lyrics, guidanceType, languageBoost, turbo, textNormalization, xVectorOnly, maxNewTokens, transcript, etc."
}),
"speech": ("RUNWARESPEECH", {
"tooltip": "Connect Runware Audio Inference Speech for speech synthesis (e.g. Minimax)"
}),
"voiceModify": ("RUNWAREVOICEMODIFY", {
"tooltip": "Connect Runware Audio Inference Settings Voice Modify for pitch, intensity, timbre, sound effects"
}),
"providerSettings": ("RUNWAREPROVIDERSETTINGS", {
"tooltip": "Provider-specific configuration settings"
}),
}
}
RETURN_TYPES = ("AUDIO", "VIDEO")
RETURN_NAMES = ("audio", "video")
FUNCTION = "generateAudio"
CATEGORY = "Runware/Audio"
def generateAudio(self, **kwargs):
"""Main function to generate audio using Runware API"""
params = self._extractParameters(kwargs)
genConfig = self._buildGenConfig(params)
try:
# Debug: Print the request being sent
print(f"[DEBUG] Sending Audio Inference Request:")
print(f"[DEBUG] Request Payload: {rwUtils.safe_json_dumps(genConfig, indent=2)}")
genResult = rwUtils.inferenecRequest(genConfig)
# Debug: Print the response received
print(f"[DEBUG] Received Audio Inference Response:")
print(f"[DEBUG] Response: {rwUtils.safe_json_dumps(genResult, indent=2)}")
print(f"[Debugging] Generation config: {rwUtils.safe_json_dumps(genConfig, indent=2)}")
except Exception as e:
# Re-raise the original error without modification
raise e
# Extract task UUID for polling
taskUUID = genConfig[0]["taskUUID"]
# Poll for audio completion
while True:
# Check for interrupt before each poll
comfy.model_management.throw_exception_if_processing_interrupted()
# Poll for audio result
pollResult = rwUtils.pollVideoResult(taskUUID)
print(f"[Debugging] Poll result: {rwUtils.safe_json_dumps(pollResult, indent=2) if isinstance(pollResult, (dict, list)) else pollResult}")
# Check for errors first
if pollResult and "errors" in pollResult and len(pollResult["errors"]) > 0:
error_info = pollResult["errors"][0]
error_message = error_info.get("message", "Unknown error")
# Extract more detailed error info if available
if "responseContent" in error_info:
response_content = error_info["responseContent"]
# Handle both string and dict response content
if isinstance(response_content, str):
detailed_message = response_content
elif isinstance(response_content, dict):
detailed_message = response_content.get("message", str(response_content))
else:
detailed_message = str(response_content)
if detailed_message:
error_message = f"{error_message}\nProvider Error: {detailed_message}"
# Include taskUUID for debugging
task_uuid = error_info.get("taskUUID", "unknown")
raise Exception(f"Audio generation failed (Task: {task_uuid}): {error_message}")
if pollResult and "data" in pollResult and len(pollResult["data"]) > 0:
audioData = pollResult["data"][0]
# Check status directly
if "status" in audioData:
status = audioData["status"]
if status == "success":
audioUrls = self._extractAudioUrls(pollResult)
hasAudio = len(audioUrls) > 0
hasVideo = bool(audioData.get("videoURL") or audioData.get("videoBase64Data", False))
if hasAudio:
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)
if hasVideo:
videos = rwUtils.convertVideoB64List(pollResult, width=None, height=None)
print(f"[DEBUG] No audio URL in response, but video is present. Returning video.")
# Extract first video object from tuple (ComfyUI expects single VideoObject, not tuple)
if len(videos) > 0:
# Return empty audio object when only video is present (prevents errors in downstream nodes)
emptyAudioObj = {
"waveform": torch.zeros((1, 1, 1)), # [batch, channels, samples]
"sample_rate": params["sampleRate"]
}
return (emptyAudioObj, videos[0])
else:
raise Exception("No video object found in response")
raise Exception("No audio or video data received from API")
# If status is "processing", continue polling
# Check for interrupt before waiting
comfy.model_management.throw_exception_if_processing_interrupted()
# Wait before next poll (split into smaller chunks to allow more frequent interrupt checks)
for _ in range(10): # 10 x 0.1 second = 1 second total
comfy.model_management.throw_exception_if_processing_interrupted()
rwUtils.time.sleep(0.1)
def _extractParameters(self, kwargs):
"""Extract and validate parameters from kwargs"""
return {
"positivePrompt": kwargs.get("positivePrompt", ""),
"model": kwargs.get("model", ""),
"duration": kwargs.get("duration", 30),
"useDuration": kwargs.get("useDuration", True),
"sampleRate": int(kwargs.get("sampleRate", 32000)),
"useSampleRate": kwargs.get("useSampleRate", False),
"bitrate": kwargs.get("bitrate", 128),
"useBitrate": kwargs.get("useBitrate", False),
"channels": kwargs.get("channels", 2),
"useChannels": kwargs.get("useChannels", False),
"outputFormat": kwargs.get("outputFormat", "MP3"),
"negativePrompt": kwargs.get("negativePrompt", ""),
"numberResults": kwargs.get("numberResults", 1),
"seed": kwargs.get("seed", 1),
"useSeed": kwargs.get("useSeed", False),
"steps": kwargs.get("steps", 20),
"useSteps": kwargs.get("useSteps", False),
"strength": kwargs.get("strength", 0.8),
"useStrength": kwargs.get("useStrength", False),
"CFGScale": kwargs.get("CFGScale", 12.0),
"useCFGScale": kwargs.get("useCFGScale", False),
"inputs": kwargs.get("inputs", None),
"settings": kwargs.get("settings", None),
"speech": kwargs.get("speech", None),
"voiceModify": kwargs.get("voiceModify", None),
"providerSettings": kwargs.get("providerSettings", None),
}
def _hasSections(self, providerSettings):
"""Check if provider settings contain sections"""
if not providerSettings or not isinstance(providerSettings, dict):
return False
# Provider settings is now flat (e.g., {"music": {...}})
# Check if it has music.compositionPlan.sections
musicSettings = providerSettings.get("music", {})
compositionPlan = musicSettings.get("compositionPlan", {})
sections = compositionPlan.get("sections", [])
return len(sections) > 0
def _buildGenConfig(self, params):
"""Build the generation configuration for API request"""
taskUuid = str(uuid.uuid4())
genConfig = [{
"taskType": "audioInference",
"taskUUID": taskUuid,
"model": params["model"],
"outputType": "URL",
"outputFormat": params["outputFormat"],
"deliveryMethod": "async",
"includeCost": True,
"numberResults": params["numberResults"],
}]
# Build audioSettings conditionally based on use flags
audioSettings = {}
if params["useSampleRate"]:
audioSettings["sampleRate"] = int(params["sampleRate"])
if params["useBitrate"]:
_br = int(params["bitrate"])
_allowed_bitrates = (8, 16, 32, 64, 128, 256)
audioSettings["bitrate"] = _br if _br in _allowed_bitrates else min(_allowed_bitrates, key=lambda x: abs(x - _br))
if params["useChannels"]:
audioSettings["channels"] = params["channels"]
# Only add audioSettings if at least one setting is enabled
if audioSettings:
genConfig[0]["audioSettings"] = audioSettings
# Handle sections - disable duration if sections are provided
hasSections = self._hasSections(params["providerSettings"])
if hasSections:
params["useDuration"] = False
print(f"[DEBUG] Disabled duration because sections are provided")
# Add positivePrompt only when non-empty
if len(params["positivePrompt"]) > 0:
genConfig[0]["positivePrompt"] = params["positivePrompt"]
print(f"[DEBUG] Added positivePrompt: '{params['positivePrompt']}'")
else:
print(f"[DEBUG] Skipped positivePrompt")
if params["useDuration"]:
genConfig[0]["duration"] = params["duration"]
print(f"[DEBUG] Added duration: {params['duration']}")
else:
print(f"[DEBUG] Skipped duration")
# Add seed parameter only if enabled
if params["useSeed"]:
genConfig[0]["seed"] = params["seed"]
# Add steps parameter only if enabled
if params["useSteps"]:
genConfig[0]["steps"] = params["steps"]
# Add strength parameter only if enabled
if params["useStrength"]:
genConfig[0]["strength"] = params["strength"]
# Add CFGScale parameter only if enabled
if params["useCFGScale"]:
genConfig[0]["CFGScale"] = params["CFGScale"]
# Add optional parameters
if params["negativePrompt"]:
genConfig[0]["negativePrompt"] = params["negativePrompt"]
# Handle audio settings (lyrics, guidance_type)
if params["settings"] is not None and isinstance(params["settings"], dict) and len(params["settings"]) > 0:
genConfig[0]["settings"] = params["settings"]
print(f"[DEBUG] Audio settings merged: {rwUtils.sanitize_for_logging(params['settings'])}")
# Handle speech (from Runware Audio Inference Speech node)
if params["speech"] is not None and isinstance(params["speech"], dict) and len(params["speech"]) > 0:
genConfig[0]["speech"] = params["speech"]
print(f"[DEBUG] Speech merged: {rwUtils.sanitize_for_logging(params['speech'])}")
# Handle voiceModify (from Runware Audio Inference Settings Voice Modify node) -> settings.voiceModify
if params["voiceModify"] is not None and isinstance(params["voiceModify"], dict) and len(params["voiceModify"]) > 0:
if "settings" not in genConfig[0]:
genConfig[0]["settings"] = {}
vm = dict(params["voiceModify"])
genConfig[0]["settings"]["voiceModify"] = vm
print(f"[DEBUG] Voice modify merged: {rwUtils.sanitize_for_logging(vm)}")
# Handle inputs - merge custom inputs from Audio Inference Inputs node
if params["inputs"] is not None:
# Merge inputs from audio inference inputs node
if "inputs" not in genConfig[0]:
genConfig[0]["inputs"] = {}
# Merge each input from inputs
for key, value in params["inputs"].items():
genConfig[0]["inputs"][key] = value
print(f"[DEBUG] Audio inference inputs merged: {rwUtils.sanitize_for_logging(params['inputs'])}")
print(f"[DEBUG] Final genConfig inputs: {rwUtils.sanitize_for_logging(genConfig[0].get('inputs', {}))}")
# Handle providerSettings - extract provider name from model and wrap with provider name (same pattern as video inference)
if params["providerSettings"] is not None:
# Extract provider name from model (e.g., "klingai:8@1" -> "klingai", "elevenlabs:1@1" -> "elevenlabs")
provider_name = params["model"].split(":")[0] if ":" in params["model"] else params["model"]
# If providerSettings is a dictionary, create the correct API format
if isinstance(params["providerSettings"], dict):
# Create the providerSettings object with provider name as key
final_provider_settings = {
provider_name: params["providerSettings"]
}
genConfig[0]["providerSettings"] = final_provider_settings
print(f"[DEBUG] Provider settings wrapped with provider name: {rwUtils.sanitize_for_logging(final_provider_settings)}")
else:
# If it's just a string, use it directly
genConfig[0]["providerSettings"] = params["providerSettings"]
print(f"[DEBUG] Sending Audio Inference Request:")
print(f"[DEBUG] Request Payload: {rwUtils.safe_json_dumps(genConfig, indent=2)}")
return genConfig
def _logResponse(self, genResult):
"""Log the API response for debugging"""
print(f"[DEBUG] Received Audio Inference Response:")
print(f"[DEBUG] Response: {rwUtils.safe_json_dumps(genResult, indent=2)}")
def _validateResponse(self, genResult):
"""Validate API response and raise errors if needed"""
if "errors" in genResult:
errorMessage = genResult["errors"][0]["message"]
raise Exception(f"Audio generation failed: {errorMessage}")
if "data" not in genResult or len(genResult["data"]) == 0:
raise Exception("No data received from API")
audioData = genResult["data"][0]
hasAudio = bool(audioData.get("audioURL") or audioData.get("audioDataURI") or audioData.get("audioBase64Data"))
hasVideo = bool(audioData.get("videoURL") or audioData.get("videoBase64Data") or audioData.get("videoUUID"))
if not hasAudio and not hasVideo:
raise Exception("No audio or video data received from API")
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
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"
}
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()
tempSuffix = self._inferAudioSuffix(audioUrl, outputFormat)
with tempfile.NamedTemporaryFile(suffix=tempSuffix, delete=False) as tempFile:
tempFile.write(response.content)
tempFilePath = tempFile.name
try:
waveformNp, originalSampleRate = self._loadAudioFile(tempFilePath)
waveformNp = self._resampleAudio(waveformNp, originalSampleRate, targetSampleRate)
waveformTensor = self._convertToTensor(waveformNp)
return {
"waveform": waveformTensor,
"sample_rate": targetSampleRate,
"format": tempSuffix.lstrip(".")
}
finally:
if os.path.exists(tempFilePath):
os.unlink(tempFilePath)
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)
# Convert to [samples, channels] format
if waveformNp.ndim == 1:
waveformNp = waveformNp.reshape(-1, 1)
else:
waveformNp = waveformNp.T # [channels, samples] -> [samples, channels]
return waveformNp, originalSampleRate
def _resampleAudio(self, waveformNp, originalSampleRate, targetSampleRate):
"""Resample audio to target sample rate"""
if originalSampleRate == targetSampleRate:
return waveformNp
if waveformNp.ndim == 1:
return librosa.resample(waveformNp, orig_sr=originalSampleRate, target_sr=targetSampleRate)
else:
# Multi-channel: resample each channel
return librosa.resample(waveformNp.T, orig_sr=originalSampleRate, target_sr=targetSampleRate).T
def _convertToTensor(self, waveformNp):
"""Convert numpy array to PyTorch tensor with correct format"""
# Ensure 2D shape [samples, channels]
if waveformNp.ndim == 1:
waveformNp = waveformNp.reshape(-1, 1)
# Convert to [channels, samples] for ComfyUI
waveformTensor = torch.from_numpy(waveformNp.T).float()
# Add batch dimension if needed: [batch, channels, samples]
if waveformTensor.dim() == 2:
waveformTensor = waveformTensor.unsqueeze(0)
return waveformTensor
-171
View File
@@ -1,171 +0,0 @@
from .utils import runwareUtils as rwUtils
from typing import Dict, Any
class audioInferenceInputs:
"""Audio Inference Inputs node for configuring audio generation inputs"""
MAX_VIDEOS = 4
MAX_AUDIOS = 4
MAX_REFERENCE_IMAGES = 4
MAX_REFERENCE_AUDIOS = 4
@classmethod
def INPUT_TYPES(cls):
optionalInputs = {
"useAudio": ("BOOLEAN", {
"tooltip": "Enable to include single audio input in API request.",
"default": False,
}),
"Audio": ("STRING", {
"tooltip": "Audio URL or mediaUUID for audio generation. Can be a direct URL or a mediaUUID from Runware Media Upload node. Only used when 'Use Audio' is enabled.",
"default": "",
}),
"useVideo": ("BOOLEAN", {
"tooltip": "Enable to include single video input in API request.",
"default": False,
}),
"Video": ("STRING", {
"tooltip": "Video URL or mediaUUID for audio generation. Can be a direct URL or a mediaUUID from Runware Media Upload node. Only used when 'Use Video' is enabled.",
"default": "",
}),
"useVideos": ("BOOLEAN", {
"tooltip": "Enable to include multiple videos input in API request.",
"default": False,
}),
}
# Add Video1, Video2, Video3, Video4 inputs
for i in range(1, cls.MAX_VIDEOS + 1):
ordinal = rwUtils.getOrdinal(i)
optionalInputs[f"Video{i}"] = ("STRING", {
"tooltip": f"Video URL or mediaUUID for the {ordinal} video. Only used when 'Use Videos' is enabled.",
"default": "",
})
# Multiple audios last so the UI groups useAudios with Audio1…Audio4 (ComfyUI order = dict insertion order)
optionalInputs["useAudios"] = ("BOOLEAN", {
"tooltip": "Enable to include multiple audio inputs in API request (inputs.audios array).",
"default": False,
})
for i in range(1, cls.MAX_AUDIOS + 1):
ordinal = rwUtils.getOrdinal(i)
optionalInputs[f"Audio{i}"] = ("STRING", {
"tooltip": f"Audio URL or mediaUUID for the {ordinal} audio. Only used when 'Use Audios' is enabled.",
"default": "",
})
for i in range(1, cls.MAX_REFERENCE_IMAGES + 1):
ordinal = rwUtils.getOrdinal(i)
optionalInputs[f"Reference Image {i}"] = ("IMAGE", {
"tooltip": f"Optional {ordinal} reference image for audio inference (inputs.referenceImages).",
})
optionalInputs["useReferenceAudios"] = ("BOOLEAN", {
"tooltip": "Enable to include reference audios in API request (inputs.referenceAudios array).",
"default": False,
})
for i in range(1, cls.MAX_REFERENCE_AUDIOS + 1):
ordinal = rwUtils.getOrdinal(i)
optionalInputs[f"Reference Audio {i}"] = ("STRING", {
"tooltip": f"Audio URL or mediaUUID for the {ordinal} reference audio (inputs.referenceAudios).",
"default": "",
})
optionalInputs["Reference Voice"] = ("RUNWAREAUDIOINFERENCEREFERENCEVOICES", {
"tooltip": "Connect Runware Audio Inference Inputs Reference Audio for zero-shot voice cloning (inputs.referenceVoices).",
})
return {
"required": {},
"optional": optionalInputs
}
DESCRIPTION = (
"Configure custom inputs for Runware Audio Inference, including optional single or multiple audio URL/mediaUUID "
"(inputs.audio or inputs.audios), reference voice for cloning (inputs.referenceVoices), "
"single or multiple video inputs for audio extraction or generation, up to 4 reference images "
"(inputs.referenceImages), and up to 4 reference audios (inputs.referenceAudios)."
)
FUNCTION = "createInputs"
RETURN_TYPES = ("RUNWAREAUDIOINFERENCEINPUTS",)
RETURN_NAMES = ("Audio Inference Inputs",)
CATEGORY = "Runware"
def createInputs(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create audio inference inputs from provided parameters"""
useAudio = kwargs.get("useAudio", False)
audio = kwargs.get("Audio", None)
useAudios = kwargs.get("useAudios", False)
useVideo = kwargs.get("useVideo", False)
video = kwargs.get("Video", None)
useVideos = kwargs.get("useVideos", False)
useReferenceAudios = kwargs.get("useReferenceAudios", False)
referenceVoices = kwargs.get("Reference Voice", None)
inputs = {}
# Handle single audio (inputs.audio)
if useAudio and audio is not None and audio.strip() != "":
inputs["audio"] = audio.strip()
# Handle multiple audios (inputs.audios as array)
if useAudios:
audioList = []
for i in range(1, self.MAX_AUDIOS + 1):
audioKey = f"Audio{i}"
audioUrl = kwargs.get(audioKey)
if audioUrl and audioUrl.strip() != "":
audioList.append(audioUrl.strip())
if len(audioList) > 0:
inputs["audios"] = audioList
# Handle single video (inputs.video)
if useVideo and video is not None and video.strip() != "":
inputs["video"] = video.strip()
# Handle multiple videos (inputs.videos as array)
if useVideos:
videoList = []
for i in range(1, self.MAX_VIDEOS + 1):
videoKey = f"Video{i}"
videoUrl = kwargs.get(videoKey)
if videoUrl and videoUrl.strip() != "":
videoList.append(videoUrl.strip())
if len(videoList) > 0:
inputs["videos"] = videoList
referenceImages = []
for i in range(1, self.MAX_REFERENCE_IMAGES + 1):
image = kwargs.get(f"Reference Image {i}", None)
if image is not None:
referenceImages.append(rwUtils.convertTensor2IMG(image))
if len(referenceImages) > 0:
inputs["referenceImages"] = referenceImages
if useReferenceAudios:
referenceAudios = []
for i in range(1, self.MAX_REFERENCE_AUDIOS + 1):
audioUrl = kwargs.get(f"Reference Audio {i}", None)
if audioUrl and isinstance(audioUrl, str) and audioUrl.strip() != "":
referenceAudios.append(audioUrl.strip())
if len(referenceAudios) > 0:
inputs["referenceAudios"] = referenceAudios
if referenceVoices is not None and isinstance(referenceVoices, list) and len(referenceVoices) > 0:
inputs["referenceVoices"] = referenceVoices
return (inputs,)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareAudioInferenceInputs": audioInferenceInputs,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareAudioInferenceInputs": "Runware Audio Inference Inputs",
}
-74
View File
@@ -1,74 +0,0 @@
"""
Runware Audio Inference Inputs Reference Audio node.
Builds inputs.referenceVoices for zero-shot voice cloning (up to 4 entries).
"""
from typing import Any, Dict, List
from .utils import runwareUtils as rwUtils
class RunwareAudioInferenceReferenceVoices:
"""Build inputs.referenceVoices[] for Fish Audio and other TTS models."""
MAX_REFERENCE_VOICES = 4
@classmethod
def INPUT_TYPES(cls):
optional_inputs = {}
for i in range(1, cls.MAX_REFERENCE_VOICES + 1):
ordinal = rwUtils.getOrdinal(i)
optional_inputs[f"useReferenceVoice{i}"] = ("BOOLEAN", {
"default": False,
"tooltip": f"Enable to include the {ordinal} reference voice in inputs.referenceVoices.",
})
optional_inputs[f"audio{i}"] = ("STRING", {
"default": "",
"tooltip": f"Reference audio clip ({ordinal}) as media UUID, URL, or base64. Required when enabled.",
})
optional_inputs[f"text{i}"] = ("STRING", {
"multiline": True,
"default": "",
"tooltip": f"Transcript of the {ordinal} reference audio clip (1–1000 characters). Required when enabled.",
})
return {
"required": {},
"optional": optional_inputs,
}
RETURN_TYPES = ("RUNWAREAUDIOINFERENCEREFERENCEVOICES",)
RETURN_NAMES = ("referenceVoices",)
FUNCTION = "createReferenceVoices"
CATEGORY = "Runware/Audio"
DESCRIPTION = (
"Configure inputs.referenceVoices for zero-shot voice cloning (up to 4 entries). "
"Each entry: { \"audio\": \"<UUID/URL/base64>\", \"text\": \"<transcript>\" }. "
"Connect to Runware Audio Inference Inputs."
)
def createReferenceVoices(self, **kwargs) -> tuple[List[Dict[str, Any]]]:
reference_voices: List[Dict[str, Any]] = []
for i in range(1, self.MAX_REFERENCE_VOICES + 1):
if not kwargs.get(f"useReferenceVoice{i}", False):
continue
audio = (kwargs.get(f"audio{i}") or "").strip()
text = (kwargs.get(f"text{i}") or "").strip()
if not audio or not text:
continue
reference_voices.append({"audio": audio, "text": text})
return (reference_voices,)
NODE_CLASS_MAPPINGS = {
"RunwareAudioInferenceReferenceVoices": RunwareAudioInferenceReferenceVoices,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareAudioInferenceReferenceVoices": "Runware Audio Inference Inputs Reference Audio",
}
-145
View File
@@ -1,145 +0,0 @@
"""
Runware Audio Inference Speech node: builds the speech payload for Runware Audio Inference.
Connect its output to the "speech" input of Runware Audio Inference for speech synthesis (e.g. Minimax).
"""
from typing import Dict, Any
class RunwareAudioInferenceSpeech:
"""Builds speech payload (text, voice, speed, volume, pitch?, emotion?, tone?) for Runware Audio Inference."""
EMOTIONS = [
"happy", "sad", "angry", "fearful", "disgusted", "surprised",
"calm", "fluent", "whisper",
]
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"text": ("STRING", {
"multiline": True,
"default": "",
"tooltip": "Text to synthesize as speech (speech.text). Optional when positivePrompt on Runware Audio Inference carries the script (e.g. Seed Audio).",
}),
},
"optional": {
"useVoice": ("BOOLEAN", {
"default": False,
"tooltip": "Enable to include voice in speech synthesis (speech.voice).",
}),
"voice": ("STRING", {
"default": "",
"tooltip": "Voice identifier.",
}),
"useSpeed": ("BOOLEAN", {"default": False, "tooltip": "Include speed (speech.speed)"}),
"speed": ("FLOAT", {
"default": 0.0,
"min": 0.0,
"max": 2.0,
"step": 0.01,
"tooltip": "Speech rate adjustment (speech.speed). 0 = no change for Seed Audio.",
}),
"useVolume": ("BOOLEAN", {"default": False, "tooltip": "Include volume (speech.volume)"}),
"volume": ("FLOAT", {
"default": 0.0,
"min": 0.0,
"max": 10.0,
"step": 0.1,
"tooltip": "Volume adjustment (speech.volume). 0 = no change.",
}),
"usePitch": ("BOOLEAN", {"default": False, "tooltip": "Include pitch (speech.pitch)"}),
"pitch": ("FLOAT", {
"default": 0.0,
"min": -12.0,
"max": 12.0,
"step": 0.1,
"tooltip": "Pitch multiplier -12 to 12 (speech.pitch)",
}),
"useEmotion": ("BOOLEAN", {"default": False, "tooltip": "Include emotion (not available for speech.turbo)"}),
"emotion": (cls.EMOTIONS, {"default": "calm", "tooltip": "Emotion for speech (speech.emotion)"}),
"useTone": ("BOOLEAN", {"default": False, "tooltip": "Include pronunciation dictionary (speech.tone)"}),
"tone": ("STRING", {
"multiline": True,
"default": "",
"placeholder": "omg/oh my god\nother/other expansion",
"tooltip": "Pronunciation dictionary, one mapping per line (e.g. omg/oh my god)",
}),
"useLanguage": ("BOOLEAN", {
"default": False,
"tooltip": "Enable to include language (speech.language).",
}),
"language": ("STRING", {
"default": "Auto",
"tooltip": "Language of the input text (for example: en-US). Applies to all variants. Only used when 'Use Language' is enabled.",
}),
"voices": ("RUNWAREAUDIOINFERENCESPEECHVOICES", {
"tooltip": "Optional multi-speaker voices array (speech.voices[]). If provided, it takes precedence over speech.voice.",
}),
}
}
RETURN_TYPES = ("RUNWARESPEECH",)
RETURN_NAMES = ("speech",)
FUNCTION = "createSpeech"
CATEGORY = "Runware/Audio"
DESCRIPTION = (
"Build speech parameters for Runware Audio Inference (text, voice, speed, volume, pitch, emotion, tone, language, etc.). "
"Connect output to the 'speech' input of Runware Audio Inference."
)
def createSpeech(self, **kwargs) -> tuple:
"""Build the speech object for the API (connect to Runware Audio Inference 'speech' input)."""
text = (kwargs.get("text") or "").strip()
voice = (kwargs.get("voice") or "").strip()
use_voice = kwargs.get("useVoice", False)
use_speed = kwargs.get("useSpeed", False)
speed = float(kwargs.get("speed", 0.0))
use_volume = kwargs.get("useVolume", False)
volume = float(kwargs.get("volume", 0.0))
use_pitch = kwargs.get("usePitch", False)
pitch = float(kwargs.get("pitch", 0.0))
use_emotion = kwargs.get("useEmotion", False)
emotion = kwargs.get("emotion", "calm")
use_tone = kwargs.get("useTone", False)
tone_str = kwargs.get("tone", "") or ""
use_language = kwargs.get("useLanguage", False)
language = (kwargs.get("language") or "Auto").strip()
voices = kwargs.get("voices")
speech: Dict[str, Any] = {}
if text:
speech["text"] = text
if isinstance(voices, list) and len(voices) > 0:
speech["voices"] = voices
elif use_voice and voice:
speech["voice"] = voice
if use_speed:
speech["speed"] = speed
if use_volume:
speech["volume"] = volume
if use_pitch:
speech["pitch"] = pitch
if use_emotion:
speech["emotion"] = emotion
if use_tone and tone_str.strip():
tone_lines = [s.strip() for s in tone_str.replace(",", "\n").split("\n") if s.strip()]
if tone_lines:
speech["tone"] = tone_lines
if use_language:
speech["language"] = language
return (speech,)
NODE_CLASS_MAPPINGS = {
"RunwareAudioInferenceSpeech": RunwareAudioInferenceSpeech,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareAudioInferenceSpeech": "Runware Audio Inference Speech",
}
-86
View File
@@ -1,86 +0,0 @@
"""
Runware Audio Inference Speech Voices node:
Builds speech.voices[] for multi-speaker audio inference speech payloads.
"""
import re
class RunwareAudioInferenceSpeechVoices:
"""Build speech.voices[] list for Runware Audio Inference."""
MAX_VOICES = 4
_SPEAKER_ALIAS_PATTERN = re.compile(r"^[A-Za-z0-9]+$")
@classmethod
def INPUT_TYPES(cls):
optional_inputs = {}
for i in range(1, cls.MAX_VOICES + 1):
optional_inputs[f"useVoice{i}"] = ("BOOLEAN", {
"default": False,
"tooltip": f"Enable to include speaker/voice pair #{i} in speech.voices.",
})
optional_inputs[f"speaker{i}"] = ("STRING", {
"default": f"Speaker{i}",
"tooltip": "Unique alphanumeric speaker alias (speech.voices[].speaker).",
})
optional_inputs[f"voice{i}"] = ("STRING", {
"default": "",
"tooltip": "Voice name for this speaker (speech.voices[].voice).",
})
return {
"required": {},
"optional": optional_inputs,
}
RETURN_TYPES = ("RUNWAREAUDIOINFERENCESPEECHVOICES",)
RETURN_NAMES = ("voices",)
FUNCTION = "createVoices"
CATEGORY = "Runware/Audio"
DESCRIPTION = (
"Build speech.voices[] for multi-speaker synthesis. "
"Each item contains speaker (unique alphanumeric alias) and voice name."
)
def createVoices(self, **kwargs) -> tuple[list[dict[str, str]],]:
voices: list[dict[str, str]] = []
seen_speakers = set()
for i in range(1, self.MAX_VOICES + 1):
use_voice = bool(kwargs.get(f"useVoice{i}", False))
if not use_voice:
continue
speaker = (kwargs.get(f"speaker{i}") or "").strip()
voice = (kwargs.get(f"voice{i}") or "").strip()
if not speaker or not voice:
raise Exception(
f"Voice #{i} is enabled, but speaker{i} and voice{i} must both be provided."
)
if not self._SPEAKER_ALIAS_PATTERN.match(speaker):
raise Exception(
f"speaker{i} must be alphanumeric only (got: '{speaker}')."
)
if speaker in seen_speakers:
raise Exception(
f"Duplicate speaker alias '{speaker}'. Each speaker must be unique."
)
seen_speakers.add(speaker)
voices.append({
"speaker": speaker,
"voice": voice,
})
return (voices,)
NODE_CLASS_MAPPINGS = {
"RunwareAudioInferenceSpeechVoices": RunwareAudioInferenceSpeechVoices,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareAudioInferenceSpeechVoices": "Runware Audio Inference Speech Voices",
}
-68
View File
@@ -1,68 +0,0 @@
"""
Runware Audio Input Node
Creates an audio input configuration with id and source
"""
from typing import Dict, Any
class RunwareAudioInput:
"""Runware Audio Input Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"source": ("STRING", {
"tooltip": "URL, base64, data URI, or UUID for the audio file. Audio formats: WAV, MP3, OGG, M4A, M3A, AAC, WMA, FLAC, MP4. Recommended: WAV/MP3 at 44.1kHz or 48kHz sampling rate.",
"default": "",
}),
},
"optional": {
"useId": ("BOOLEAN", {
"tooltip": "Enable to include reference ID for mapping audio input. Required when using segments array.",
"default": False,
}),
"id": ("STRING", {
"tooltip": "Reference ID used to map audio input. Required when using segments array.",
"default": "",
}),
}
}
RETURN_TYPES = ("RUNWAREAUDIOINPUT",)
RETURN_NAMES = ("Audio Input",)
FUNCTION = "createAudioInput"
CATEGORY = "Runware"
DESCRIPTION = "Create an audio input configuration with reference ID and source for video generation."
def createAudioInput(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create audio input configuration"""
useId = kwargs.get("useId", False)
audio_id = kwargs.get("id", "").strip()
source = kwargs.get("source", "").strip()
if not source:
return ({},)
audioInput: Dict[str, Any] = {
"source": source,
}
# Only include id if useId is enabled and id is provided
if useId and audio_id:
audioInput["id"] = audio_id
return (audioInput,)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareAudioInput": RunwareAudioInput,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareAudioInput": "Runware Video Audio Input",
}
-130
View File
@@ -1,130 +0,0 @@
class RunwareAudioModelSearch:
"""Audio Model Search node for searching audio models"""
# Define the audio models by provider
AUDIO_MODELS = {
"ElevenLabs": [
"elevenlabs:1@1 (ElevenLabs Multilingual v2)",
"elevenlabs:2@1 (ElevenLabs Multilingual v2 Turbo)",
"elevenlabs:3@1 (ElevenLabs Monolingual v1)",
],
"KlingAI": [
"klingai:8@1 (KlingAI Audio)",
],
"Mirelo": [
"mirelo:1@1 (Mirelo SFX 1.5)",
],
"Ace": [
"runware:ace-step@0 (ACE Step v1 3.5B)",
"runware:ace-step@v1.5-base (ACE-Step v1.5 Base)",
"runware:ace-step@v1.5-turbo (ACE-Step v1.5 Turbo)",
"runware:ace-step@v1.5-xl-base (ACE-Step v1.5 XL Base)",
"runware:ace-step@v1.5-xl-turbo (ACE-Step v1.5 XL Turbo)",
"runware:ace-step@v1.5-xl-sft (ACE-Step v1.5 XL SFT)",
],
"Alibaba": [
"alibaba:qwen@3-tts-1.7b-voicedesign (Qwen3 TTS 1.7B Voice Design)",
"alibaba:qwen@3-tts-1.7b-customvoice (Qwen3 TTS 1.7B Custom Voice)",
"alibaba:qwen@3-tts-1.7b-base (Qwen3 TTS 1.7B Base)",
],
"Bytedance": [
"bytedance:seed-audio@1.0 (Seed Audio 1.0)",
],
"Dia": [
"runware:dia@v1.0 (Dia 1.6B)",
"runware:dia2@2b (Dia2 2B)",
],
"xAI": [
"xai:voice@tts (xAI TTS)",
],
"MiniMax": [
"minimax:speech@2.8 (MiniMax Speech 2.8)",
"minimax:music@cover (MiniMax Music Cover)",
"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)",
],
"Google": [
"google:gemini@3.1-flash-tts (Gemini 3.1 Flash TTS)",
],
"Fish": [
"fishaudio:s2.1@pro (Fish Audio S2.1 Pro)",
],
}
MODEL_PROVIDERS = [
"All",
"ElevenLabs",
"KlingAI",
"Mirelo",
"Ace",
"Alibaba",
"Bytedance",
"Dia",
"xAI",
"MiniMax",
"Inworld",
"Google",
"Fish",
]
@classmethod
def INPUT_TYPES(cls):
allModels = cls._getAllModels()
defaultModel = "elevenlabs:1@1 (ElevenLabs Multilingual v2)"
return {
"required": {
"Model Search": ("STRING", {
"tooltip": "Search For Specific Audio Model By Name Or Code (eg: elevenlabs, klingai).",
}),
"Model Provider": (cls.MODEL_PROVIDERS, {
"tooltip": "Choose Audio Model Provider To Filter Results.",
"default": "ElevenLabs",
}),
"AudioList": (allModels, {
"tooltip": "Audio Model Results Will Show UP Here So You Could Choose From.",
"default": defaultModel,
}),
"Use Search Value": ("BOOLEAN", {
"tooltip": "When Enabled, the value you've set in the search input will be used instead.\n\nThis is useful in case the model search API is down or you prefer to set the model manually.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
}
}
@classmethod
def _getAllModels(cls):
"""Get all models from all providers"""
allModels = []
for models in cls.AUDIO_MODELS.values():
allModels.extend(models)
return allModels
RETURN_TYPES = ("RUNWAREAUDIOMODEL",)
RETURN_NAMES = ("model",)
FUNCTION = "searchModels"
CATEGORY = "Runware"
DESCRIPTION = "Directly Search and Connect Audio Models to Runware Audio Inference Nodes In ComfyUI."
@classmethod
def VALIDATE_INPUTS(cls, AudioList):
return True
def searchModels(self, **kwargs):
"""Search and return audio model"""
enableSearchValue = kwargs.get("Use Search Value", False)
searchInput = kwargs.get("Model Search")
if enableSearchValue:
modelAirCode = searchInput
else:
currentModel = kwargs.get("AudioList")
modelAirCode = currentModel.split(" (")[0]
return (modelAirCode,)
-66
View File
@@ -1,66 +0,0 @@
class RunwareAudioSections:
"""Audio Sections node for creating audio composition sections"""
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"sectionName": ("STRING", {
"default": "intro",
"tooltip": "Name of the section"
}),
"positiveLocalStyles": ("STRING", {
"default": "guitar, buildup",
"tooltip": "Styles that should be present in this section (comma-separated)"
}),
"negativeLocalStyles": ("STRING", {
"default": "vocals",
"tooltip": "Styles that should not be present in this section (comma-separated)"
}),
"duration": ("INT", {
"default": 15,
"min": 3,
"max": 120,
"tooltip": "Duration of the section in seconds (3-120)"
}),
"lines": ("STRING", {
"multiline": True,
"default": "",
"tooltip": "Lyrics for the section (one line per line)"
}),
}
}
RETURN_TYPES = ("RUNWAREAUDIOSECTIONS",)
RETURN_NAMES = ("sections",)
FUNCTION = "createSections"
CATEGORY = "Runware/Audio"
def createSections(self, **kwargs):
"""Create audio section object from provided parameters"""
sectionName = kwargs.get("sectionName", "")
positiveLocalStyles = kwargs.get("positiveLocalStyles", "")
negativeLocalStyles = kwargs.get("negativeLocalStyles", "")
duration = kwargs.get("duration", 0)
lines = kwargs.get("lines", "")
section = {
"sectionName": sectionName,
"positiveLocalStyles": self._parseCommaSeparated(positiveLocalStyles),
"negativeLocalStyles": self._parseCommaSeparated(negativeLocalStyles),
"duration": duration,
"lines": self._parseLines(lines)
}
return (section,)
def _parseCommaSeparated(self, text):
"""Parse comma-separated string into list"""
return [item.strip() for item in text.split(",") if item.strip()]
def _parseLines(self, text):
"""Parse multiline text into list"""
return [line.strip() for line in text.split("\n") if line.strip()]
-552
View File
@@ -1,552 +0,0 @@
"""
Runware Audio Inference Settings Node
Provides lyrics, lyricsOptimizer, instrumental, guidanceType, languageBoost, turbo, temperature, textNormalization,
bpm, keyScale, timeSignature, vocalLanguage, coverConditioningScale, repaintingStart, repaintingEnd,
cfgIntervalStart, cfgIntervalEnd, xVectorOnly, maxNewTokens, maxTokens, transcript,
normalizeLoudness, topP, chunkLength, minChunkLength, normalize, latency, repetitionPenalty,
conditionOnPreviousChunks, earlyStopThreshold, and more for Runware Audio Inference.
"""
from typing import Dict, Any
_VOCAL_LANGUAGE_CODES = [
"ar", "az", "bg", "bn", "ca", "cs", "da", "de", "el", "en", "es", "fa", "fi", "fr",
"he", "hi", "hr", "ht", "hu", "id", "is", "it", "ja", "ko", "la", "lt", "ms", "ne",
"nl", "no", "pa", "pl", "pt", "ro", "ru", "sa", "sk", "sr", "sv", "sw", "ta", "te",
"th", "tl", "tr", "uk", "ur", "vi", "yue", "zh", "unknown",
]
class RunwareAudioSettings:
"""Runware Audio Inference Settings Node."""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useTemperature": ("BOOLEAN", {
"tooltip": "Enable to include temperature in audio generation settings",
"default": False,
}),
"temperature": ("FLOAT", {
"tooltip": "Sampling temperature for text token generation. Lower values produce more coherent, predictable speech content. Higher values increase variety but risk artifacts. Set 0 for greedy decoding. Only used when 'Use Temperature' is enabled.",
"default": 0.6,
"min": 0.0,
"max": 5.0,
"step": 0.1,
}),
"useAudioTemperature": ("BOOLEAN", {
"tooltip": "Enable to include audioTemperature in audio generation settings",
"default": False,
}),
"audioTemperature": ("FLOAT", {
"tooltip": "Sampling temperature for audio token generation. Lower values produce cleaner, more stable audio. Higher values add expressiveness but may introduce noise. Set 0 for greedy decoding. Only used when 'Use Audio Temperature' is enabled.",
"default": 0.8,
"min": 0.0,
"max": 5.0,
"step": 0.1,
}),
"useTopK": ("BOOLEAN", {
"tooltip": "Enable to include topK token sampling in audio generation settings",
"default": False,
}),
"topK": ("INT", {
"tooltip": "Top-K candidates for both text and audio token sampling. Limits sampling to the K most likely next tokens at each step. Only used when 'Use Top K' is enabled.",
"default": 50,
"min": 1,
"max": 500,
"step": 1,
}),
"useIncludePrefix": ("BOOLEAN", {
"tooltip": "Enable to include includePrefix in audio generation settings",
"default": False,
}),
"includePrefix": ("BOOLEAN", {
"tooltip": "When voice cloning, keep prefix audio in output (true) or trim to new speech (false). Only relevant when compatible prefix/reference audio is provided. Only used when 'Use Include Prefix' is enabled.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
"useLyrics": ("BOOLEAN", {
"tooltip": "Enable to include lyrics in audio generation settings",
"default": False,
}),
"lyrics": ("STRING", {
"multiline": True,
"default": "",
"placeholder": "[verse] ... [chorus] ... (format like genius.com)",
"tooltip": "Lyrics for the audio generation. Format like lyrics from genius.com with [verse], [chorus], [refrain], etc.",
}),
"useGuidanceType": ("BOOLEAN", {
"tooltip": "Enable to include guidanceType (controls how guidance value is used)",
"default": False,
}),
"guidanceType": (["apg", "cfg"], {
"tooltip": "Controls how the guidance value is used. See API GuidanceType: apg | cfg.",
"default": "apg",
}),
"useLanguageBoost": ("BOOLEAN", {
"tooltip": "Enable to include languageBoost (language detection setting) in audio generation settings",
"default": False,
}),
"languageBoost": ([
"auto", "zh", "yue", "en", "ar", "ru", "es", "fr", "pt", "de", "tr", "nl", "uk",
"vi", "id", "ja", "it", "ko", "th", "pl", "ro", "el", "cs", "fi", "hi", "bg", "da",
"he", "ms", "fa", "sk", "sv", "hr", "fil", "hu", "no", "sl", "ca", "nn", "ta", "af",
], {
"tooltip": "Language detection setting. Codes: zh, yue, en, ar, ru, es, fr, pt, de, tr, nl, uk, vi, id, ja, it, ko, th, pl, ro, el, cs, fi, hi, bg, da, he, ms, fa, sk, sv, hr, fil, hu, no, sl, ca, nn, ta, af, auto",
"default": "auto",
}),
"useTurbo": ("BOOLEAN", {
"tooltip": "Enable to include turbo (select turbo model or not) in audio generation settings",
"default": False,
}),
"turbo": ("BOOLEAN", {
"tooltip": "When enabled, use turbo model. Only used when 'Use Turbo' is enabled.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
"useTextNormalization": ("BOOLEAN", {
"tooltip": "Enable to include textNormalization (normalize text before synthesis) in audio generation settings",
"default": False,
}),
"textNormalization": ("BOOLEAN", {
"tooltip": "Normalize text (e.g. numbers, abbreviations) before synthesis to improve pronunciation. Only used when 'Use Text Normalization' is enabled.",
"default": True,
"label_on": "true",
"label_off": "false",
}),
"useBpm": ("BOOLEAN", {
"tooltip": "Enable to include bpm (tempo) in audio generation settings. If disabled, the model decides automatically.",
"default": False,
}),
"bpm": ("INT", {
"tooltip": "Beats per minute (30–300). If not set via API, the model decides. Common: 60–80 ballad, 90–110 pop, 120–130 house, 140–170 DnB. Only used when 'Use Bpm' is enabled.",
"default": 120,
"min": 30,
"max": 300,
"step": 1,
}),
"useKeyScale": ("BOOLEAN", {
"tooltip": "Enable to include keyScale (musical key and scale). Empty value lets the model decide.",
"default": False,
}),
"keyScale": ("STRING", {
"default": "",
"tooltip": "Musical key and scale. Empty string lets the model decide. Format: '{Note}{Accidental} {Mode}' (Note A–G, Accidental '' | '#' | 'b' | '♯' | '♭', Mode 'major' | 'minor'). Examples: 'C major', 'F# minor', 'B♭ major'.",
}),
"useTimeSignature": ("BOOLEAN", {
"tooltip": "Enable to include timeSignature (beats per measure). If disabled, the model decides automatically.",
"default": False,
}),
"timeSignature": ("INT", {
"default": 4,
"min": 1,
"max": 32,
"step": 1,
"tooltip": "Time signature as integer beats per bar (e.g. 2, 3, 4, 6). Only used when 'Use Time Signature' is enabled.",
}),
"useVocalLanguage": ("BOOLEAN", {
"tooltip": "Enable to include vocalLanguage (language code) for vocal generation. See dropdown for available codes.",
"default": False,
}),
"vocalLanguage": (_VOCAL_LANGUAGE_CODES, {
"tooltip": "Language code for vocals. Supports values from the dropdown list (including 'yue', 'zh', and 'unknown'). Use 'unknown' for instrumental or automatic detection. Only used when 'Use Vocal Language' is enabled.",
"default": "en",
}),
"useCoverConditioningScale": ("BOOLEAN", {
"tooltip": "Enable to include coverConditioningScale (source-audio conditioning fraction for cover tasks). Only effective when input audio is provided.",
"default": False,
}),
"coverConditioningScale": ("FLOAT", {
"tooltip": "Fraction of denoising steps using source-audio conditioning (cover task). 1.0 = all steps; 0.5 = first half source then text-only. Only used when 'Use Cover Conditioning Scale' is enabled.",
"default": 1.0,
"min": 0.0,
"max": 1.0,
"step": 0.01,
}),
"useRepaintingStart": ("BOOLEAN", {
"tooltip": "Enable to include repaintingStart (seconds). Requires input audio. Sets repaint region with repaintingEnd; negative values prepend (outpaint before start).",
"default": False,
}),
"repaintingStart": ("FLOAT", {
"tooltip": "Start time in seconds for repaint/inpaint/outpaint region. Only used when 'Use Repainting Start' is enabled.",
"default": 0.0,
"step": 0.01,
}),
"useRepaintingEnd": ("BOOLEAN", {
"tooltip": "Enable to include repaintingEnd (seconds). Requires input audio. Values beyond source duration append (outpaint).",
"default": False,
}),
"repaintingEnd": ("FLOAT", {
"tooltip": "End time in seconds for repaint region. Only used when 'Use Repainting End' is enabled.",
"default": 0.0,
"step": 0.01,
}),
"useXVectorOnly": ("BOOLEAN", {
"tooltip": "Enable to include xVectorOnly in audio generation settings.",
"default": False,
}),
"xVectorOnly": ("BOOLEAN", {
"tooltip": "true = speaker embedding only (no transcript needed, lower similarity). false = ICL mode (needs transcript, higher quality). Only used when 'Use X Vector Only' is enabled.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
"useMaxNewTokens": ("BOOLEAN", {
"tooltip": "Enable to include maxNewTokens (audio output token cap) in audio generation settings.",
"default": False,
}),
"maxNewTokens": ("INT", {
"tooltip": "Audio output token cap. Higher = longer audio, but risk of generation hangs. Applies to all variants. Only used when 'Use Max New Tokens' is enabled.",
"default": 4096,
"min": 1,
"max": 262144,
"step": 1,
}),
"useTranscript": ("BOOLEAN", {
"tooltip": "Enable to include transcript (reference audio transcript) in audio generation settings.",
"default": False,
}),
"transcript": ("STRING", {
"multiline": True,
"default": "",
"tooltip": "Transcript of the reference audio. Required for ICL mode; omit or leave empty for x-vector-only mode. Base variant only. Only used when 'Use Transcript' is enabled.",
}),
"useInstrumental": ("BOOLEAN", {
"tooltip": "Enable to include instrumental in audio generation settings.",
"default": False,
}),
"instrumental": ("BOOLEAN", {
"tooltip": "When true, generates instrumental audio. For Music 2.6, this mode rejects settings.lyrics and settings.lyricsOptimizer, and positivePrompt becomes required.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
"useLyricsOptimizer": ("BOOLEAN", {
"tooltip": "Enable to include lyricsOptimizer in audio generation settings.",
"default": False,
}),
"lyricsOptimizer": ("BOOLEAN", {
"tooltip": "Provider-side lyric generation from prompt when lyrics is empty/omitted. Supported for non-instrumental Music 2.6 requests only; rejected when instrumental=true.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
"useCfgIntervalStart": ("BOOLEAN", {
"tooltip": "Enable to include cfgIntervalStart (diffusion ratio where CFG begins). ACE-Step Base only — not supported on Turbo variants.",
"default": False,
}),
"cfgIntervalStart": ("FLOAT", {
"tooltip": "Diffusion ratio where CFG begins (0.0 = first step). Only used when 'Use Cfg Interval Start' is enabled.",
"default": 0.0,
"min": 0.0,
"max": 1.0,
"step": 0.01,
}),
"useCfgIntervalEnd": ("BOOLEAN", {
"tooltip": "Enable to include cfgIntervalEnd (diffusion ratio where CFG ends). ACE-Step Base only — not supported on Turbo variants.",
"default": False,
}),
"cfgIntervalEnd": ("FLOAT", {
"tooltip": "Diffusion ratio where CFG ends (1.0 = last step). Only used when 'Use Cfg Interval End' is enabled.",
"default": 1.0,
"min": 0.0,
"max": 1.0,
"step": 0.01,
}),
"useNormalizeLoudness": ("BOOLEAN", {
"tooltip": "Enable to include normalizeLoudness in settings.",
"default": False,
}),
"normalizeLoudness": ("BOOLEAN", {
"tooltip": "Normalize output loudness for consistent perceived volume. Only used when 'Use Normalize Loudness' is enabled.",
"default": True,
"label_on": "true",
"label_off": "false",
}),
"useTopP": ("BOOLEAN", {
"tooltip": "Enable to include topP in settings.",
"default": False,
}),
"topP": ("FLOAT", {
"tooltip": "Controls diversity via nucleus sampling. Must be between 0.0001 and 1. Only used when 'Use Top P' is enabled.",
"default": 0.7,
"min": 0.0001,
"max": 1.0,
"step": 0.0001,
}),
"useChunkLength": ("BOOLEAN", {
"tooltip": "Enable to include chunkLength in settings.",
"default": False,
}),
"chunkLength": ("INT", {
"tooltip": "Text segment size for processing. Only used when 'Use Chunk Length' is enabled.",
"default": 300,
"min": 100,
"max": 300,
"step": 1,
}),
"useMinChunkLength": ("BOOLEAN", {
"tooltip": "Enable to include minChunkLength in settings.",
"default": False,
}),
"minChunkLength": ("INT", {
"tooltip": "Minimum characters before splitting into a new chunk. Only used when 'Use Min Chunk Length' is enabled.",
"default": 50,
"min": 0,
"max": 100,
"step": 1,
}),
"useNormalize": ("BOOLEAN", {
"tooltip": "Enable to include normalize in settings (text normalization for English and Chinese).",
"default": False,
}),
"normalize": ("BOOLEAN", {
"tooltip": "Normalizes text for English and Chinese, improving stability for numbers. Only used when 'Use Normalize' is enabled.",
"default": True,
"label_on": "true",
"label_off": "false",
}),
"useLatency": ("BOOLEAN", {
"tooltip": "Enable to include latency in settings.",
"default": False,
}),
"latency": (["low", "normal", "balanced"], {
"tooltip": "Latency / quality trade-off. normal = best quality, balanced = reduced latency, low = lowest latency. Only used when 'Use Latency' is enabled.",
"default": "normal",
}),
"useMaxTokens": ("BOOLEAN", {
"tooltip": "Enable to include maxTokens in settings (e.g. Fish Audio TTS).",
"default": False,
}),
"maxTokens": ("INT", {
"tooltip": "Maximum audio tokens to generate per text chunk. Only used when 'Use Max Tokens' is enabled.",
"default": 1024,
"min": 1,
"max": 4294967295,
"step": 1,
}),
"useRepetitionPenalty": ("BOOLEAN", {
"tooltip": "Enable to include repetitionPenalty in settings.",
"default": False,
}),
"repetitionPenalty": ("FLOAT", {
"tooltip": "Penalty for repeating audio patterns. Values above 1.0 reduce repetition. Only used when 'Use Repetition Penalty' is enabled.",
"default": 1.2,
"min": 0.0,
"max": 2.0,
"step": 0.01,
}),
"useConditionOnPreviousChunks": ("BOOLEAN", {
"tooltip": "Enable to include conditionOnPreviousChunks in settings.",
"default": False,
}),
"conditionOnPreviousChunks": ("BOOLEAN", {
"tooltip": "Use previous audio as context for voice consistency across chunks. Only used when 'Use Condition On Previous Chunks' is enabled.",
"default": True,
"label_on": "true",
"label_off": "false",
}),
"useEarlyStopThreshold": ("BOOLEAN", {
"tooltip": "Enable to include earlyStopThreshold in settings.",
"default": False,
}),
"earlyStopThreshold": ("FLOAT", {
"tooltip": "Early stopping threshold for batch processing. Only used when 'Use Early Stop Threshold' is enabled.",
"default": 1.0,
"min": 0.0,
"max": 1.0,
"step": 0.01,
}),
}
}
RETURN_TYPES = ("RUNWAREAUDIOSETTINGS",)
RETURN_NAMES = ("settings",)
FUNCTION = "createSettings"
CATEGORY = "Runware/Audio"
DESCRIPTION = (
"Configure audio generation settings (lyrics, lyricsOptimizer, instrumental, guidanceType, languageBoost, turbo, temperature, textNormalization, "
"bpm, keyScale, timeSignature, vocalLanguage, coverConditioningScale, repaintingStart, repaintingEnd, "
"cfgIntervalStart, cfgIntervalEnd, xVectorOnly, maxNewTokens, maxTokens, transcript, "
"normalizeLoudness, topP, chunkLength, minChunkLength, normalize, latency, repetitionPenalty, "
"conditionOnPreviousChunks, earlyStopThreshold, etc.) "
"for Runware Audio Inference. Connect to Runware Audio Inference node."
)
def createSettings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create audio settings dict for API"""
use_temperature = kwargs.get("useTemperature", False)
temperature = kwargs.get("temperature", 0.6)
use_audio_temperature = kwargs.get("useAudioTemperature", False)
audio_temperature = kwargs.get("audioTemperature", 0.8)
use_top_k = kwargs.get("useTopK", False)
top_k = kwargs.get("topK", 50)
use_include_prefix = kwargs.get("useIncludePrefix", False)
include_prefix = kwargs.get("includePrefix", False)
use_lyrics = kwargs.get("useLyrics", False)
lyrics = kwargs.get("lyrics", "")
use_instrumental = kwargs.get("useInstrumental", False)
instrumental = kwargs.get("instrumental", False)
use_lyrics_optimizer = kwargs.get("useLyricsOptimizer", False)
lyrics_optimizer = kwargs.get("lyricsOptimizer", False)
use_guidance_type = kwargs.get("useGuidanceType", False)
guidance_type = kwargs.get("guidanceType", "apg")
use_language_boost = kwargs.get("useLanguageBoost", False)
language_boost = kwargs.get("languageBoost", "auto")
use_turbo = kwargs.get("useTurbo", False)
turbo = kwargs.get("turbo", False)
use_text_normalization = kwargs.get("useTextNormalization", False)
text_normalization = kwargs.get("textNormalization", True)
use_bpm = kwargs.get("useBpm", False)
bpm = kwargs.get("bpm", 120)
use_key_scale = kwargs.get("useKeyScale", False)
key_scale = kwargs.get("keyScale", "")
use_time_signature = kwargs.get("useTimeSignature", False)
time_signature = kwargs.get("timeSignature", 4)
use_vocal_language = kwargs.get("useVocalLanguage", False)
vocal_language = kwargs.get("vocalLanguage", "en")
use_cover_conditioning_scale = kwargs.get("useCoverConditioningScale", False)
cover_conditioning_scale = kwargs.get("coverConditioningScale", 1.0)
use_repainting_start = kwargs.get("useRepaintingStart", False)
repainting_start = kwargs.get("repaintingStart", 0.0)
use_repainting_end = kwargs.get("useRepaintingEnd", False)
repainting_end = kwargs.get("repaintingEnd", 0.0)
use_x_vector_only = kwargs.get("useXVectorOnly", False)
x_vector_only = kwargs.get("xVectorOnly", False)
use_max_new_tokens = kwargs.get("useMaxNewTokens", False)
max_new_tokens = kwargs.get("maxNewTokens", 4096)
use_transcript = kwargs.get("useTranscript", False)
transcript = kwargs.get("transcript", "") or ""
use_cfg_interval_start = kwargs.get("useCfgIntervalStart", False)
cfg_interval_start = kwargs.get("cfgIntervalStart", 0.0)
use_cfg_interval_end = kwargs.get("useCfgIntervalEnd", False)
cfg_interval_end = kwargs.get("cfgIntervalEnd", 1.0)
use_normalize_loudness = kwargs.get("useNormalizeLoudness", False)
use_top_p = kwargs.get("useTopP", False)
top_p = kwargs.get("topP", 0.7)
use_chunk_length = kwargs.get("useChunkLength", False)
chunk_length = kwargs.get("chunkLength", 300)
use_min_chunk_length = kwargs.get("useMinChunkLength", False)
min_chunk_length = kwargs.get("minChunkLength", 50)
use_normalize = kwargs.get("useNormalize", False)
normalize = kwargs.get("normalize", True)
use_latency = kwargs.get("useLatency", False)
latency = kwargs.get("latency", "normal")
use_max_tokens = kwargs.get("useMaxTokens", False)
max_tokens = kwargs.get("maxTokens", 1024)
use_repetition_penalty = kwargs.get("useRepetitionPenalty", False)
repetition_penalty = kwargs.get("repetitionPenalty", 1.2)
use_condition_on_previous_chunks = kwargs.get("useConditionOnPreviousChunks", False)
use_early_stop_threshold = kwargs.get("useEarlyStopThreshold", False)
early_stop_threshold = kwargs.get("earlyStopThreshold", 1.0)
settings: Dict[str, Any] = {}
if use_temperature:
settings["temperature"] = float(temperature)
if use_audio_temperature:
settings["audioTemperature"] = float(audio_temperature)
if use_top_k:
settings["topK"] = int(top_k)
if use_include_prefix:
settings["includePrefix"] = bool(include_prefix)
if use_lyrics and lyrics and lyrics.strip():
settings["lyrics"] = lyrics.strip()
if use_instrumental:
settings["instrumental"] = bool(instrumental)
if use_lyrics_optimizer:
settings["lyricsOptimizer"] = bool(lyrics_optimizer)
if use_guidance_type:
settings["guidanceType"] = guidance_type
if use_language_boost:
settings["languageBoost"] = language_boost
if use_turbo:
settings["turbo"] = bool(turbo) # API requires true/false
if use_text_normalization:
settings["textNormalization"] = bool(text_normalization)
if use_bpm:
settings["bpm"] = int(bpm)
if use_key_scale and str(key_scale).strip():
settings["keyScale"] = str(key_scale).strip()
if use_time_signature:
settings["timeSignature"] = int(time_signature)
if use_vocal_language:
settings["vocalLanguage"] = vocal_language
if use_cover_conditioning_scale:
settings["coverConditioningScale"] = float(cover_conditioning_scale)
if use_repainting_start:
settings["repaintingStart"] = float(repainting_start)
if use_repainting_end:
settings["repaintingEnd"] = float(repainting_end)
if use_x_vector_only:
settings["xVectorOnly"] = bool(x_vector_only)
if use_max_new_tokens:
settings["maxNewTokens"] = int(max_new_tokens)
if use_transcript and transcript.strip():
settings["transcript"] = transcript.strip()
if use_cfg_interval_start:
settings["cfgIntervalStart"] = float(cfg_interval_start)
if use_cfg_interval_end:
settings["cfgIntervalEnd"] = float(cfg_interval_end)
if use_normalize_loudness:
settings["normalizeLoudness"] = bool(kwargs.get("normalizeLoudness", True))
if use_top_p:
settings["topP"] = float(top_p)
if use_chunk_length:
settings["chunkLength"] = int(chunk_length)
if use_min_chunk_length:
settings["minChunkLength"] = int(min_chunk_length)
if use_normalize:
settings["normalize"] = bool(normalize)
if use_latency:
settings["latency"] = str(latency)
if use_max_tokens:
settings["maxTokens"] = int(max_tokens)
if use_repetition_penalty:
settings["repetitionPenalty"] = float(repetition_penalty)
if use_condition_on_previous_chunks:
settings["conditionOnPreviousChunks"] = bool(kwargs.get("conditionOnPreviousChunks", True))
if use_early_stop_threshold:
settings["earlyStopThreshold"] = float(early_stop_threshold)
return (settings,)
NODE_CLASS_MAPPINGS = {
"RunwareAudioSettings": RunwareAudioSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareAudioSettings": "Runware Audio Inference Settings",
}
-98
View File
@@ -1,98 +0,0 @@
"""
Runware Audio Inference Settings Voice Modify node.
Outputs settings.voiceModify (pitch, intensity, timbre, soundEffects) for Runware Audio Inference.
"""
from typing import Dict, Any
SOUND_EFFECTS = ["", "spacious_echo", "auditorium_echo", "lofi_telephone", "robotic"]
class RunwareAudioSettingsVoiceModify:
"""Runware Audio Inference Settings Voice Modify – pitch, intensity, timbre, sound effects."""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"usePitch": ("BOOLEAN", {
"default": False,
"tooltip": "Include pitch in voice modification",
}),
"pitch": ("INT", {
"default": 0,
"min": -100,
"max": 100,
"step": 1,
"tooltip": "Voice pitch modification (settings.voiceModify.pitch)",
}),
"useIntensity": ("BOOLEAN", {
"default": False,
"tooltip": "Include intensity in voice modification",
}),
"intensity": ("INT", {
"default": 0,
"min": -100,
"max": 100,
"step": 1,
"tooltip": "Voice intensity modification (settings.voiceModify.intensity)",
}),
"useTimbre": ("BOOLEAN", {
"default": False,
"tooltip": "Include timbre in voice modification",
}),
"timbre": ("INT", {
"default": 0,
"min": -100,
"max": 100,
"step": 1,
"tooltip": "Voice timbre modification (settings.voiceModify.timbre)",
}),
"useSoundEffects": ("BOOLEAN", {
"default": False,
"tooltip": "Include sound effect in voice modification",
}),
"soundEffects": (SOUND_EFFECTS, {
"default": "",
"tooltip": "Sound effect: spacious_echo, auditorium_echo, lofi_telephone, robotic",
}),
}
}
RETURN_TYPES = ("RUNWAREVOICEMODIFY",)
RETURN_NAMES = ("voiceModify",)
FUNCTION = "createVoiceModify"
CATEGORY = "Runware/Audio"
DESCRIPTION = "Voice modification settings (pitch, intensity, timbre, sound effects). Connect to Runware Audio Inference voiceModify input."
def createVoiceModify(self, **kwargs) -> tuple:
"""Build voiceModify dict for settings.voiceModify. Only includes keys when the corresponding use toggle is on."""
use_pitch = kwargs.get("usePitch", False)
pitch = kwargs.get("pitch", 0)
use_intensity = kwargs.get("useIntensity", False)
intensity = kwargs.get("intensity", 0)
use_timbre = kwargs.get("useTimbre", False)
timbre = kwargs.get("timbre", 0)
use_sound_effects = kwargs.get("useSoundEffects", False)
sound_effects = (kwargs.get("soundEffects") or "").strip()
voice_modify: Dict[str, Any] = {}
if use_pitch:
voice_modify["pitch"] = pitch
if use_intensity:
voice_modify["intensity"] = intensity
if use_timbre:
voice_modify["timbre"] = timbre
if use_sound_effects and sound_effects:
voice_modify["soundEffects"] = sound_effects
return (voice_modify,)
NODE_CLASS_MAPPINGS = {
"RunwareAudioSettingsVoiceModify": RunwareAudioSettingsVoiceModify,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareAudioSettingsVoiceModify": "Runware Audio Inference Settings Voice Modify",
}
-171
View File
@@ -1,171 +0,0 @@
from .utils import runwareUtils as rwUtils
class bgremoval:
RUNWARE_RMBG_MODELS = {
"RemBG 1.4": "runware:109@1",
"Bria RMBG 2.0": "runware:110@1",
"Bria RMBG v2.0": "bria:2@1",
"BiRefNet v1 Base": "runware:112@1",
"BiRefNet v1 Base - COD": "runware:112@2",
"BiRefNet Dis": "runware:112@3",
"BiRefNet General": "runware:112@5",
"BiRefNet General RES 512x512": "runware:112@6",
"BiRefNet HRSOD DHU": "runware:112@7",
"BiRefNet Massive TR DIS5K TES": "runware:112@8",
"BiRefNet Matting": "runware:112@9",
"BiRefNet Portrait": "runware:112@10"
}
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Image": ("IMAGE", {
"tooltip": "Specifies the input image to be processed."
}),
"Model": (list(cls.RUNWARE_RMBG_MODELS.keys()), {
"tooltip": "Select the background removal model to use. Different models have varying strengths depending on image content and complexity.",
"default": "RemBG 1.4",
}),
},
"optional": {
"Post Process Mask": ("BOOLEAN", {
"tooltip": "Controls whether the mask should undergo additional post-processing. This step can improve the accuracy and quality of the background removal mask.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"Return Only Mask": ("BOOLEAN", {
"tooltip": "Whether to return only the mask. The mask is the opposite of the image background removal.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"Alpha Matting": ("BOOLEAN", {
"tooltip": "Alpha matting is a post-processing technique that enhances the quality of the output by refining the edges of the foreground object.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"Alpha Matting Foreground Threshold": ("INT", {
"tooltip": "Threshold value used in alpha matting to distinguish the foreground from the background. Adjusting this parameter affects the sharpness and accuracy of the foreground object edges.",
"default": 240,
"min": 1,
"max": 255,
}),
"Alpha Matting Background Threshold": ("INT", {
"tooltip": "Threshold value used in alpha matting to refine the background areas. It influences how aggressively the algorithm removes the background while preserving image details. The higher the value, the more computation is needed and therefore the more expensive the operation is.",
"default": 10,
"min": 1,
"max": 255,
}),
"Alpha Matting Erode Size": ("INT", {
"tooltip": "Specifies the size of the erosion operation used in alpha matting. Erosion helps in smoothing the edges of the foreground object for a cleaner removal of the background.",
"default": 10,
"min": 1,
"max": 255,
}),
"safetyInputs": ("RUNWARESAFETYINPUTS", {
"tooltip": "Connect Runware Safety Inputs node to configure safety and content moderation settings.",
}),
"Accelerator": ("RUNWAREACCELERATOR", {
"tooltip": "Connect a Runware Accelerator Options Node to configure caching and acceleration settings.",
}),
"providerSettings": ("RUNWAREPROVIDERSETTINGS", {
"tooltip": "Connect a Runware Provider Settings node to configure provider-specific parameters.",
}),
**rwUtils.RUNWARE_REMBG_OUTPUT_FORMATS,
}
}
DESCRIPTION = "Remove backgrounds from images effortlessly using Runware's low-cost image editing Inference."
FUNCTION = "rembg"
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("IMAGE",)
CATEGORY = "Runware"
def rembg(self, **kwargs):
image = kwargs.get("Image")
modelName = kwargs.get("Model", "RemBG 1.4")
postProcessMask = kwargs.get("Post Process Mask", False)
returnOnlyMask = kwargs.get("Return Only Mask", False)
alphaMatting = kwargs.get("Alpha Matting", False)
alphaMattingForegroundThreshold = kwargs.get("Alpha Matting Foreground Threshold", 240)
alphaMattingBackgroundThreshold = kwargs.get("Alpha Matting Background Threshold", 10)
alphaMattingErodeSize = kwargs.get("Alpha Matting Erode Size", 10)
outputFormat = kwargs.get("outputFormat", "WEBP")
safetyInputs = kwargs.get("safetyInputs", None)
runwareAccelerator = kwargs.get("Accelerator", None)
providerSettings = kwargs.get("providerSettings", None)
modelAIR = self.RUNWARE_RMBG_MODELS.get(modelName, "runware:109@1")
includeExtraSettings = postProcessMask or returnOnlyMask or alphaMatting
if modelAIR != "runware:109@1" and includeExtraSettings:
raise ValueError(
f"Oops! The selected model '{modelName}' does not support additional settings!\n"
"Please switch to 'RemBG 1.4' if you wish to use these features."
)
genConfig = {
"taskType": "imageBackgroundRemoval",
"taskUUID": rwUtils.genRandUUID(),
"inputImage": rwUtils.convertTensor2IMG(image),
"model": modelAIR,
"outputFormat": outputFormat,
"outputQuality": rwUtils.OUTPUT_QUALITY,
"outputType": "URL",
}
if includeExtraSettings:
settings = {
"postProcessMask": postProcessMask,
"returnOnlyMask": returnOnlyMask,
"alphaMatting": alphaMatting,
}
if alphaMatting:
settings["alphaMattingForegroundThreshold"] = alphaMattingForegroundThreshold
settings["alphaMattingBackgroundThreshold"] = alphaMattingBackgroundThreshold
settings["alphaMattingErodeSize"] = alphaMattingErodeSize
genConfig["settings"] = settings
# Add safety inputs if provided
if safetyInputs is not None and isinstance(safetyInputs, dict) and len(safetyInputs) > 0:
genConfig["safety"] = safetyInputs
# Add accelerator options if provided
if runwareAccelerator is not None and isinstance(runwareAccelerator, dict) and len(runwareAccelerator) > 0:
genConfig["acceleratorOptions"] = runwareAccelerator
# Handle providerSettings - extract provider name from model and merge with custom settings
if providerSettings is not None:
# Extract provider name from model (e.g., "bria:2@1" -> "bria", "runware:109@1" -> "runware")
provider_name = modelAIR.split(":")[0] if ":" in modelAIR else "runware"
# If providerSettings is a dictionary, create the correct API format
if isinstance(providerSettings, dict):
# Create the providerSettings object with provider name as key
final_provider_settings = {
provider_name: providerSettings
}
genConfig["providerSettings"] = final_provider_settings
print(f"[Debugging] Provider settings: {final_provider_settings}")
else:
# If it's just a string, use it directly
genConfig["providerSettings"] = providerSettings
# Debug: Print the request being sent
print(f"[DEBUG] Sending Background Removal Request:", flush=True)
print(f"[DEBUG] Request Payload: {rwUtils.safe_json_dumps([genConfig], indent=2)}", flush=True)
try:
genResult = rwUtils.inferenecRequest([genConfig])
# Debug: Print the response received
print(f"[DEBUG] Received Background Removal Response:", flush=True)
print(f"[DEBUG] Response: {rwUtils.safe_json_dumps(genResult, indent=2)}", flush=True)
except Exception as e:
print(f"[DEBUG] Error in Background Removal Request: {str(e)}", flush=True)
raise
images = rwUtils.extractImageURLs(genResult)
return (images,)
-143
View File
@@ -1,143 +0,0 @@
from ..utils import runwareUtils as rwUtils
from server import PromptServer
from aiohttp import web
routes = PromptServer.instance.routes
@routes.post('/setAPIKey')
async def setAPIKey(reqPayload):
reqData = await reqPayload.json()
apiKey = reqData.get('apiKey', None)
if(apiKey is None or apiKey == "" or len(apiKey) < 30):
return web.json_response({'success': False, 'error': 'Invalid API Key!'})
try:
apiKey = apiKey.strip()
apiCheckResult = rwUtils.checkAPIKey(apiKey)
if(apiCheckResult == False):
return web.json_response({'success': False, 'error': 'Failed To Set Your API Key, Please Try Again!'})
elif(apiCheckResult != True):
return web.json_response({'success': False, 'error': apiCheckResult})
rwUtils.setAPIKey(apiKey)
except Exception as e:
return web.json_response({'success': False, 'error': str(e)})
return web.json_response({'success': True})
@routes.post('/setMaxTimeout')
async def setMaxTimeout(reqPayload):
reqData = await reqPayload.json()
maxTimeout = reqData.get('maxTimeout', 90)
if(maxTimeout < 5 or maxTimeout > 99):
return web.json_response({'success': False, 'error': 'Invalid Timeout Value!'})
try:
rwUtils.setTimeout(maxTimeout)
except Exception as e:
return web.json_response({'success': False, 'error': str(e)})
return web.json_response({'success': True})
@routes.post('/setOutputFormat')
async def setOutputFormat(reqPayload):
reqData = await reqPayload.json()
outputFormat = reqData.get('outputFormat', 'WEBP')
if outputFormat not in ['WEBP', 'PNG', 'JPEG', 'TIFF']:
return web.json_response({'success': False, 'error': 'Invalid Output Format!'})
try:
rwUtils.setOutputFormat(outputFormat)
except Exception as e:
return web.json_response({'success': False, 'error': str(e)})
return web.json_response({'success': True})
@routes.post('/setOutputQuality')
async def setOutputQuality(reqPayload):
reqData = await reqPayload.json()
outputQuality = reqData.get('outputQuality', 95)
if not isinstance(outputQuality, int) or outputQuality < 20 or outputQuality > 99:
return web.json_response({'success': False, 'error': 'Invalid Output Quality!'})
try:
rwUtils.setOutputQuality(outputQuality)
except Exception as e:
return web.json_response({'success': False, 'error': str(e)})
return web.json_response({'success': True})
@routes.post('/setEnableImagesCaching')
async def setEnableImagesCaching(reqPayload):
reqData = await reqPayload.json()
enableCaching = reqData.get('enableCaching', True)
if not isinstance(enableCaching, bool):
return web.json_response({'success': False, 'error': 'Invalid value for enable caching!'})
try:
rwUtils.setEnableImagesCaching(enableCaching)
except Exception as e:
return web.json_response({'success': False, 'error': str(e)})
return web.json_response({'success': True})
@routes.post('/setMinImageCacheSize')
async def setMinImageCacheSize(reqPayload):
reqData = await reqPayload.json()
minCacheSize = reqData.get('minCacheSize', 100)
if not isinstance(minCacheSize, int) or minCacheSize < 30 or minCacheSize > 4096:
return web.json_response({'success': False, 'error': 'Invalid minimum cache size!'})
try:
rwUtils.setMinImageCacheSize(minCacheSize)
except Exception as e:
return web.json_response({'success': False, 'error': str(e)})
return web.json_response({'success': True})
@routes.post('/promptEnhance')
async def promptEnhance(reqPayload):
reqData = await reqPayload.json()
userPrompt = reqData.get('userPrompt')
utilityConfig = [{
"taskType": "promptEnhance",
"taskUUID": rwUtils.genRandUUID(),
"prompt": userPrompt,
"promptMaxLength": 300,
"promptVersions": 1,
}]
try:
utilityResults = rwUtils.inferenecRequest(utilityConfig)
except Exception as e:
return web.json_response({'success': False, 'error': str(e)})
enhancedPrompt = utilityResults["data"][0]["text"]
return web.json_response({'success': True, 'enhancedPrompt': enhancedPrompt})
@routes.post('/modelSearch')
async def modelSearch(reqPayload):
reqData = await reqPayload.json()
modelQuery = reqData.get('modelQuery', "")
modelArch = reqData.get('modelArch', "all")
modelCategory = reqData.get('modelCat', "checkpoint")
modelType = reqData.get('modelType', "base")
controlNetConditioning = reqData.get('condtioning', "all")
utilityConfig = [{
"taskType": "modelSearch",
"taskUUID": rwUtils.genRandUUID(),
"category": modelCategory,
"limit": 25,
"sort": "-downloadCount",
}]
aclTypes = ["controlnet", "lora", "lycoris", "embeddings", "vae"]
if(modelCategory not in aclTypes):
utilityConfig[0]["type"] = modelType
elif(modelCategory == "controlnet" and controlNetConditioning != "all"):
utilityConfig[0]["conditioning"] = controlNetConditioning
if(modelArch != "all"):
utilityConfig[0]["architecture"] = modelArch
if(modelQuery != ""):
utilityConfig[0]["search"] = modelQuery
try:
utilityResults = rwUtils.inferenecRequest(utilityConfig)
except Exception as e:
return web.json_response({'success': False, 'error': str(e)})
totalResults = utilityResults["data"][0]["totalResults"]
if(totalResults < 1):
return web.json_response({'success': False, 'error': 'No Results Found!'})
results = utilityResults["data"][0].get("results", [])
if not results:
return web.json_response({'success': False, 'error': 'No Results Found!'})
modelList = results
return web.json_response({'success': True, 'modelList': modelList})
-169
View File
@@ -1,169 +0,0 @@
from .utils import runwareUtils as rwUtils
class controlNet:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Guide Image": ("IMAGE", {
"tooltip": "Specifies the preprocessed image to be used as guide to control the image generation process."
}),
"ControlNet Search": ("STRING", {
"tooltip": "Searchg For Specific ControlNet By Name Or Civit AIR Code (eg: Canny).",
}),
"Model Architecture": ([
"All",
"FLUX.1-Schnell",
"FLUX.1-Dev",
"FLUX.1 Krea",
"Pony",
"SD 1.5",
"SD 1.5 Hyper",
"SD 1.5 LCM",
"SD 3",
"SDXL 1.0",
"SDXL 1.0 LCM",
"SDXL Distilled",
"SDXL Hyper",
"SDXL Lightning",
"SDXL Turbo",
], {
"tooltip": "Choose ControlNet Model Architecture To Filter Results.",
"default": "All",
}),
"ControlNetType": ([
"All",
"Canny",
"Depth",
"MLSD",
"Normal BAE",
"Open Pose",
"Tile",
"Seg",
"Line Art",
"Line Art Anime",
"Shuffle",
"Scribble",
"Soft Edge",
], {
"tooltip": "Choose ControlNet Type To Filter Results.",
"default": "All",
}),
"ControlNetList": ([
"civitai:38784@44716 (SD1.5 Canny)",
"civitai:38784@44876 (SD1.5 Inpaint)",
"civitai:38784@44877 (SD1.5 Lineart)",
"civitai:38784@44795 (SD1.5 MLSD)",
"civitai:38784@44774 (SD1.5 NormalBAE)",
"runware:20@1 (SDXL Canny)",
"runware:25@1 (Flux Dev Canny)",
"runware:26@1 (Flux Dev Tile)",
"runware:28@1 (Flux Dev Blur)",
"runware:29@1 (Flux Dev OpenPose)",
"runware:30@1 (Flux Dev Gray)",
"runware:31@1 (Flux Dev Low Quality)",
], {
"tooltip": "ControlNet Results Will Show UP Here So You Could Choose From.",
"default": "civitai:38784@44716 (SD1.5 Canny)",
}),
"startStep": ("INT", {
"tooltip": "Represents the step number at which the ControlNet model starts to control the inference process. (Enter -1 To Disable)",
"min": -1,
"max": 99,
"default": -1,
}),
"startStepPercentage": ("INT", {
"tooltip": "Represents the percentage of steps at which the ControlNet model starts to control the inference process. (Enter -1 To Disable)",
"min": -1,
"max": 99,
"default": 0,
}),
"endStep": ("INT", {
"tooltip": "Represents the step number at which the ControlNet preprocessor ends to control the inference process. (Enter -1 To Disable)",
"min": -1,
"max": 100,
"default": -1,
}),
"endStepPercentage": ("INT", {
"tooltip": "Represents the percentage of steps at which the ControlNet model ends to control the inference process. (Enter -1 To Disable)",
"min": -1,
"max": 100,
"default": 80,
}),
"Control Mode": (["prompt", "controlnet", "balanced"], {
"tooltip": "Choose Control Mode To Control The Inference Process.",
"default": "balanced",
}),
"weight": ("FLOAT", {
"tooltip": "Represents the weight (strength) of the ControlNet model in the image.",
"default": 1.0,
"min": 0,
"max": 1.0,
"step": 0.1,
}),
"Use Search Value": ("BOOLEAN", {
"tooltip": "When Enabled, the value you've set in the search input will be used instead.\n\nThis is useful in case the model search API is down or you prefer to set the model manually.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
},
}
DESCRIPTION = "Directly Search and Configure ControlNet Guidance to Connect It With Runware Image Inference Nodes In ComfyUI."
FUNCTION = "controlNet"
RETURN_TYPES = ("RUNWARECONTROLNET",)
RETURN_NAMES = ("Runware ControlNet",)
CATEGORY = "Runware"
@classmethod
def VALIDATE_INPUTS(cls, ControlNetList, startStep, startStepPercentage, endStep, endStepPercentage):
if((startStep == -1 and startStepPercentage == -1) or (startStep != -1 and startStepPercentage != -1)):
raise Exception("Error: Please Provide Either Start Step or Start Step Percentage!")
if((endStep == -1 and endStepPercentage == -1) or (endStep != -1 and endStepPercentage != -1)):
raise Exception("Error: Please Provide Either End Step or End Step Percentage!")
if((startStep != -1 and endStep != -1) and (startStep > endStep)):
raise Exception("Error: Start Step Cannot Be Greater Than End Step!")
elif((startStep != -1 and endStep != -1) and (startStep == endStep)):
raise Exception("Error: Start Step Cannot Be Equal To End Step!")
if((startStepPercentage != -1 and endStepPercentage != -1) and (startStepPercentage > endStepPercentage)):
raise Exception("Error: Start Step Percentage Cannot Be Greater Than End Step Percentage!")
elif((startStepPercentage != -1 and endStepPercentage != -1) and (startStepPercentage == endStepPercentage)):
raise Exception("Error: Start Step Percentage Cannot Be Equal To End Step Percentage!")
return True
def controlNet(self, **kwargs):
guideImage = kwargs.get("Guide Image")
enableSearchValue = kwargs.get("Use Search Value", False)
searchInput = kwargs.get("ControlNet Search")
if enableSearchValue:
modelAIRCode = searchInput
else:
CRModel = kwargs.get("ControlNetList")
modelAIRCode = CRModel.split(" ")[0]
startStep = kwargs.get("startStep")
startStepPercentage = kwargs.get("startStepPercentage")
endStep = kwargs.get("endStep")
endStepPercentage = kwargs.get("endStepPercentage")
controlMode = kwargs.get("Control Mode")
weight = kwargs.get("weight")
controlNetGuideOBJ = [{
"model": modelAIRCode,
"guideImage": rwUtils.convertTensor2IMG(guideImage),
"weight": round(weight, 2),
"controlMode": controlMode,
}]
if(startStep != -1):
controlNetGuideOBJ[0]["startStep"] = startStep
elif(startStepPercentage != -1):
controlNetGuideOBJ[0]["startStepPercentage"] = startStepPercentage
if(endStep != -1):
controlNetGuideOBJ[0]["endStep"] = endStep
elif(endStepPercentage != -1):
controlNetGuideOBJ[0]["endStepPercentage"] = endStepPercentage
return (controlNetGuideOBJ,)
-37
View File
@@ -1,37 +0,0 @@
class controlNetCombine:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"ControlNet 1": ("RUNWARECONTROLNET", {
"tooltip": "Connect a Runware ControlNet Node.",
}),
},
"optional": {
"ControlNet 2": ("RUNWARECONTROLNET", {
"tooltip": "Connect a Runware ControlNet Node.",
}),
"ControlNet 3": ("RUNWARECONTROLNET", {
"tooltip": "Connect a Runware ControlNet Node.",
}),
},
}
DESCRIPTION = "Combine One or More ControlNet's To Guide Image Generation Process in Runware Image Inference."
FUNCTION = "controlNetCombine"
RETURN_TYPES = ("RUNWARECONTROLNET",)
RETURN_NAMES = ("Runware ControlNet's",)
CATEGORY = "Runware"
def controlNetCombine(self, **kwargs):
controlNet1 = kwargs.get("ControlNet 1")
controlNet2 = kwargs.get("ControlNet 2", None)
controlNet3 = kwargs.get("ControlNet 3", None)
controlNetObjectArray = controlNet1
if(controlNet2 is not None):
controlNetObjectArray = controlNetObjectArray + controlNet2
if(controlNet3 is not None):
controlNetObjectArray = controlNetObjectArray + controlNet3
print(controlNetObjectArray)
return (controlNetObjectArray,)
-119
View File
@@ -1,119 +0,0 @@
from .utils import runwareUtils as rwUtils
class controlNetPreprocessor:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Image": ("IMAGE", {
"tooltip": "Specifies the input image to be preprocessed to generate a guide image. This guide image will be used as a reference for image generation processes, guiding the AI to generate images that are more aligned with the input image."
}),
"PreProcessor Type": ([
"Canny",
"Depth",
"MLSD",
"NormalBAE",
"OpenPose",
"Tile",
"Seg",
"LineArt",
"LineArtAnime",
"Shuffle",
"Scribble",
"SoftEdge",
], {
"tooltip": "Choose Preprocessor Type To Generate The Guide Image.",
"default": "Canny",
}),
"Include Hands And Faces [ OpenPose ]": ("BOOLEAN", {
"default": False,
"tooltip": "Include the hands and face in the pose outline when using the OpenPose preprocessor.",
"label_on": "Enabled",
"label_off": "Disabled",
}),
"Canny Low Threshold": ("INT", {
"tooltip": "Defines the lower threshold when using the Canny edge detection preprocessor.",
"default": 100,
"min": 0,
"max": 255,
}),
"Canny High Threshold": ("INT", {
"tooltip": "Defines the high threshold when using the Canny edge detection preprocessor.",
"default": 200,
"min": 0,
"max": 255,
}),
"Image Resizing": ("BOOLEAN", {
"default": False,
"tooltip": "It Allows Image Resizing if the dimension of the image is higher than a specific value it will be resized.",
"label_on": "Enabled",
"label_off": "Disabled",
}),
"dimensions": ([
"Square (512x512)", "Square HD (1024x1024)", "Portrait 3:4 (768x1024)",
"Portrait 9:16 (576x1024)", "Landscape 4:3 (1024x768)",
"Landscape 16:9 (1024x576)", "Custom"
], {
"default": "Square HD (1024x1024)",
"tooltip": "Resize the dimensions of the generated Guide image by specifying its width and height in pixels, or select from the predefined options. Image dimensions must be multiples of 64 (e.g., 512x512, 1024x768).",
}),
"width": ("INT", {
"tooltip": "If the Image height dimension is larger than this value, the output image will be resized to the specified height.",
"default": 1024,
"min": 512,
"max": 2048,
"step": 64,
}),
"height": ("INT", {
"tooltip": "If the Image width dimension is larger than this value, the output image will be resized to the specified width.",
"default": 1024,
"min": 512,
"max": 2048,
"step": 64,
}),
},
}
DESCRIPTION = "ControlNet offers advanced capabilities for precise image processing through the use of guide images in specific formats, known as preprocessed images. This powerful tool enhances the control and customization of image generation, enabling users to achieve desired artistic styles and detailed adjustments effectively."
FUNCTION = "controlNetPreProcess"
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("GUIDE IMAGE",)
CATEGORY = "Runware"
def controlNetPreProcess(self, **kwargs):
image = kwargs.get("Image")
preProcessorType = kwargs.get("PreProcessor Type")
includeHandsAndFaces = kwargs.get("Include Hands And Faces [ OpenPose ]")
cannyLowThreshold = kwargs.get("Canny Low Threshold")
cannyHighThreshold = kwargs.get("Canny High Threshold")
imageResizing = kwargs.get("Image Resizing")
width = kwargs.get("width")
height = kwargs.get("height")
genConfig = [
{
"taskType": "imageControlNetPreProcess",
"taskUUID": rwUtils.genRandUUID(),
"inputImage": rwUtils.convertTensor2IMG(image),
"preProcessorType": preProcessorType,
"outputFormat": rwUtils.OUTPUT_FORMAT,
"outputQuality": rwUtils.OUTPUT_QUALITY,
"outputType": "URL",
}
]
# For Debugging Purposes Only
print(f"[Debugging] Task UUID: {genConfig[0]['taskUUID']}")
if(imageResizing):
genConfig[0]["width"] = width
genConfig[0]["height"] = height
if(preProcessorType == "OpenPose" and includeHandsAndFaces):
genConfig[0]["includeHandsAndFaceOpenPose"] = includeHandsAndFaces
elif(preProcessorType == "Canny"):
genConfig[0]["lowThresholdCanny"] = cannyLowThreshold
genConfig[0]["highThresholdCanny"] = cannyHighThreshold
genResult = rwUtils.inferenecRequest(genConfig)
images = rwUtils.extractImageURLs(genResult)
return (images,)
-80
View File
@@ -1,80 +0,0 @@
class embeddingSearch:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Embedding Search": ("STRING", {
"tooltip": "Search For A Specific Embedding By Name Or Civit AIR Code (eg: EasyNegative).",
}),
"Model Architecture": ([
"All",
"FLUX.1-Schnell",
"FLUX.1-Dev",
"FLUX.1 Krea",
"Pony",
"SD 1.5",
"SD 1.5 Hyper",
"SD 1.5 LCM",
"SD 3",
"SDXL 1.0",
"SDXL 1.0 LCM",
"SDXL Distilled",
"SDXL Hyper",
"SDXL Lightning",
"SDXL Turbo",
], {
"tooltip": "Choose Embedding Model Architecture To Filter Out The Results.",
"default": "SD 1.5",
}),
"EmbeddingList": ([
"civitai:7808@9208 (EasyNegative)",
"civitai:4629@5637 (Deep Negative V1.x)",
"civitai:56519@60938 (negative_hand Negative Embedding)",
"civitai:72437@77169 (BadDream + UnrealisticDream (Negative Embeddings))",
"civitai:11772@25820 (veryBadImageNegative)",
"civitai:71961@94057 (Fast Negative Embedding (+ FastNegativeV2))",
], {
"tooltip": "Embedding Results Will Show UP Here So You Could Choose From.",
"default": "civitai:7808@9208 (EasyNegative)",
}),
"weight": ("FLOAT", {
"tooltip": "Defines the strength or influence of the Embedding on the generation process.",
"default": 1.0,
"min": 0,
"max": 1,
"step": 0.1,
}),
"Use Search Value": ("BOOLEAN", {
"tooltip": "When Enabled, the value you've set in the search input will be used instead.\n\nThis is useful in case the model search API is down or you prefer to set the model manually.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
},
}
DESCRIPTION = "Directly Search and Connect Embeddings to Runware Inference Nodes In ComfyUI."
FUNCTION = "embeddingSearch"
RETURN_TYPES = ("RUNWAREEMBEDDING",)
RETURN_NAMES = ("Runware Embedding",)
CATEGORY = "Runware"
@classmethod
def VALIDATE_INPUTS(cls, EmbeddingList):
return True
def embeddingSearch(self, **kwargs):
enableSearchValue = kwargs.get("Use Search Value", False)
searchInput = kwargs.get("Embedding Search")
embedding_weight = kwargs.get("weight")
if enableSearchValue:
modelAIRCode = searchInput
else:
crModel = kwargs.get("EmbeddingList")
modelAIRCode = crModel.split(" ")[0]
return ({
"model": modelAIRCode,
"weight": round(embedding_weight, 2),
},)
-37
View File
@@ -1,37 +0,0 @@
class embeddingsCombine:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Embedding 1": ("RUNWAREEMBEDDING", {
"tooltip": "Connect a Runware Embedding From an Embedding Node.",
}),
},
"optional": {
"Embedding 2": ("RUNWAREEMBEDDING", {
"tooltip": "Connect a Runware Embedding From an Embedding Node.",
}),
"Embedding 3": ("RUNWAREEMBEDDING", {
"tooltip": "Connect a Runware Embedding From an Embedding Node.",
}),
},
}
DESCRIPTION = "Combine One or More Embeddings To Connect It With Runware Image Inference."
FUNCTION = "embeddingsCombine"
RETURN_TYPES = ("RUNWAREEMBEDDING",)
RETURN_NAMES = ("Runware Embeddings",)
CATEGORY = "Runware"
def embeddingsCombine(self, **kwargs):
embeddingV1 = kwargs.get("Embedding 1")
embeddingV2 = kwargs.get("Embedding 2", None)
embeddingV3 = kwargs.get("Embedding 3", None)
embeddingsObjArray = []
embeddingsObjArray.append(embeddingV1)
if(embeddingV2):
embeddingsObjArray.append(embeddingV2)
if(embeddingV3):
embeddingsObjArray.append(embeddingV3)
return (embeddingsObjArray,)
-72
View File
@@ -1,72 +0,0 @@
from .utils import runwareUtils as rwUtils
class RunwareFrameImages:
"""Frame Images node for defining keyframes that constrain video timeline"""
FRAME_POSITIONS = ["auto", "first", "last", "0", "12", "24", "36", "48", "60", "72", "84", "96", "108", "120"]
MAX_FRAMES = 4
@classmethod
def INPUT_TYPES(cls):
optionalInputs = {}
for i in range(1, cls.MAX_FRAMES + 1):
optionalInputs[f"image{i}"] = ("IMAGE", {
"tooltip": "Frame image that will constrain video content at a specific timeline position. Used for keyframe control.",
})
optionalInputs[f"frame{i}_position"] = (cls.FRAME_POSITIONS, {
"default": "auto",
"tooltip": "Frame position: 'auto' (automatic distribution), 'first' (beginning), 'last' (end), or specific frame number (0-120).",
})
return {
"required": {},
"optional": optionalInputs
}
DESCRIPTION = "Define keyframes that constrain specific frames within the video timeline. Different from reference images - these control WHEN specific visual content appears, not overall style consistency."
FUNCTION = "createFrameImages"
RETURN_TYPES = ("RUNWAREFRAMEIMAGES",)
RETURN_NAMES = ("Frame Images",)
CATEGORY = "Runware"
def createFrameImages(self, **kwargs):
"""Create frame images list from provided parameters"""
frameImages = []
for i in range(1, self.MAX_FRAMES + 1):
imageKey = f"image{i}"
positionKey = f"frame{i}_position"
image = kwargs.get(imageKey)
position = kwargs.get(positionKey, "auto")
if image is not None:
frameData = self._createFrameData(image, position)
frameImages.append(frameData)
return (frameImages,)
def _createFrameData(self, image, position):
"""Create frame data object from image and position"""
imageData = rwUtils.convertTensor2IMGBase64Only(image)
frameData = {"inputImage": imageData}
if position != "auto":
if position.isdigit():
frameData["frame"] = int(position)
else:
frameData["frame"] = position # "first" or "last"
return frameData
# Node class mappings for ComfyUI registration
NODE_CLASS_MAPPINGS = {
"RunwareFrameImages": RunwareFrameImages,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareFrameImages": "Runware Frame Images",
}
-134
View File
@@ -1,134 +0,0 @@
from .utils import runwareUtils as rwUtils
class imageCaptioning:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Always Recaption": ("BOOLEAN", {
"default": False,
"tooltip": "Enable this option to always recaption the image each time you run the workflow.",
"label_on": "Enabled",
"label_off": "Disabled",
}),
"prompt": ("STRING", {
"multiline": True,
"default": "Describe this image in detail",
"placeholder": "Enter the prompt for image captioning. You can provide multiple prompts separated by newlines.",
"tooltip": "The prompt(s) to guide the image captioning. Multiple prompts can be provided, each on a new line."
}),
},
"optional": {
"inputImages": ("IMAGE", {
"tooltip": "Connect multiple Load Image nodes here to caption multiple images at once."
}),
"inputImage": ("IMAGE", {
"tooltip": "Connect a single Load Image node here for convenience. If inputImages is provided, this will be ignored."
}),
"model": ("STRING", {
"default": "",
"placeholder": "Leave empty for default model (runware:150@1)",
"tooltip": "Optional: Specify the AIR model ID (e.g., runware:150@1, runware:150@2). Leave empty to use the default model."
}),
"template": ("STRING", {
"default": "",
"placeholder": "Optional template for structured output",
"tooltip": "Optional template to structure the caption output."
}),
"imageCaption": ("STRING", {
"multiline": True,
"placeholder": "Generated image caption will appear here automatically.",
"tooltip": "This field will be automatically populated with the generated image caption."
}),
},
"hidden": { "node_id": "UNIQUE_ID" }
}
DESCRIPTION = "Advanced image captioning with support for multiple images, custom prompts, and model selection. Generate descriptive text from single or multiple images using customizable prompts and optional AIR models. Perfect for batch processing and structured output generation."
FUNCTION = "imageCaptioning"
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("IMAGE PROMPT",)
CATEGORY = "Runware"
OUTPUT_NODE = True
@classmethod
def IS_CHANGED(s, **kwargs):
alwaysRecaption = kwargs.get("Always Recaption")
if(alwaysRecaption):
return float("NAN")
return True
def imageCaptioning(self, **kwargs):
inputImages = kwargs.get("inputImages", None)
inputImage = kwargs.get("inputImage", None)
prompt = kwargs.get("prompt", "Describe this image in detail")
model = kwargs.get("model", "")
template = kwargs.get("template", "")
# Handle multiple prompts - split by newlines
prompt_list = [p.strip() for p in prompt.split('\n') if p.strip()]
if not prompt_list:
prompt_list = ["Describe this image in detail"]
# Determine which images to use (following SDK logic)
if inputImages is not None:
images_to_process = inputImages
elif inputImage is not None:
# Single image provided via inputImage - convert to array
images_to_process = [inputImage]
else:
raise Exception("Either inputImages or inputImage must be provided")
# Set inputImage to inputImages[0] if not already provided
actual_input_image = inputImage
if actual_input_image is None and len(images_to_process) > 0:
actual_input_image = images_to_process[0]
# Convert images to base64 format
converted_images = []
for img in images_to_process:
converted_images.append(rwUtils.convertTensor2IMG(img))
# Create a dictionary with mandatory parameters
task_params = {
"taskType": "imageCaption",
"taskUUID": rwUtils.genRandUUID(),
}
# Add either inputImage or inputImages, but not both (API requirement)
if len(converted_images) == 1:
# Single image - use inputImage parameter
task_params["inputImage"] = converted_images[0]
else:
# Multiple images - use inputImages parameter
task_params["inputImages"] = converted_images
# Add model parameter only if specified - backend handles default
if model and model.strip():
task_params["model"] = model.strip()
# Add template parameter if specified
if template and template.strip():
task_params["template"] = template.strip()
# When using template, do NOT include prompt parameter
else:
# Use the provided prompt when no template
task_params["prompt"] = prompt_list
# Send the task with all applicable parameters
genConfig = [task_params]
genResult = rwUtils.inferenecRequest(genConfig)
print(genResult)
# Handle multiple results if multiple images were processed
if len(genResult["data"]) > 1:
# Multiple images - combine results
combined_text = "\n\n".join([item["text"] for item in genResult["data"]])
genText = combined_text
else:
# Single image
genText = genResult["data"][0]["text"]
rwUtils.sendImageCaption(genText, kwargs.get("node_id"))
return (genText, )
-536
View File
@@ -1,536 +0,0 @@
from .utils import runwareUtils as rwUtils
import comfy.model_management
class txt2img:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"positivePrompt": ("STRING", {
"multiline": True,
"placeholder": "Positive Prompt: a text instruction to guide the model on generating the image. It is usually a sentence or a paragraph that provides positive guidance for the task. This parameter is essential to shape the desired results.\n\nYou Can Press (Ctrl + Alt + E) To Enhance The Prompt!",
"tooltip": "Positive Prompt: a text instruction to guide the model on generating the image. You Can Also Press (Ctrl + Alt + E) To Enhance The Prompt!"
}),
"negativePrompt": ("STRING", {
"multiline": True,
"placeholder": "Negative Prompt: a text instruction to guide the model on generating the image. It is usually a sentence or a paragraph that provides negative guidance for the task. This parameter helps to avoid certain undesired results.",
"tooltip": "Negative Prompt: a text instruction to guide the model on generating the image."
}),
"Model": ("RUNWAREMODEL", {
"tooltip": "Connect a Runware Model From Runware Model Node.",
}),
"Multi Inference Mode": ("BOOLEAN", {
"tooltip": "If Enabled the node will skip the image generation process and will only return the Runware Task Object to be used in the Multi Inference Node.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"Prompt Weighting": (["Disabled", "sdEmbeds", "Compel"], {
"default": "Disabled",
"tooltip": "Prompt weighting allows you to adjust how strongly different parts of your prompt influence the generated image.\n\nChoose between \"compel\" notation with advanced weighting operations or \"sdEmbeds\" for simple emphasis adjustments.\n\nCompel Example: \"small+ dog, pixar style\"\n\nsdEmbeds Example: \"(small:2.5) dog, pixar style\"",
}),
"dimensions": ([
"None", "Square (512x512)", "Square HD (1024x1024)", "Square 2K (2048x2048)", "Portrait 3:4 (768x1024)",
"Portrait 9:16 (576x1024)", "Landscape 4:3 (1024x768)",
"Landscape 16:9 (1024x576)",
"Custom"
], {
"default": "Square (512x512)",
"tooltip": "Adjust the dimensions of the generated image by specifying its width and height in pixels, or select from the predefined options. Image dimensions must be multiples of 64 (e.g., 512x512, 1024x768). Select 'None' to let the model determine dimensions automatically.",
}),
"width": ("INT", {
"tooltip": "The Width of the image in pixels.",
"default": 512,
"min": 128,
"max": 6048,
"step": 1,
}),
"height": ("INT", {
"tooltip": "The Height of the image in pixels.",
"default": 512,
"min": 128,
"max": 6048,
"step": 1,
}),
"useResolution": ("BOOLEAN", {
"tooltip": "Enable to include resolution parameter in API request. Disable if your model doesn't support resolution.",
"default": False,
}),
"resolution": (["960p", "1080p", "1k", "2k", "4k"], {
"tooltip": "Select a standard resolution for Image Inference. Only used when 'Use Resolution' is enabled.",
"default": "1k",
}),
"useSteps": ("BOOLEAN", {
"tooltip": "Enable to include steps parameter in API request. Disable if your model doesn't support steps (like nano banana).",
"default": False,
}),
"steps": ("INT", {
"tooltip": "The number of steps is the number of iterations the model will perform to generate the image. Only used when 'Use Steps' is enabled.",
"default": 4,
"min": 1,
"max": 100,
}),
"useScheduler": ("BOOLEAN", {
"tooltip": "Enable to include scheduler parameter in API request. Disable if your model doesn't support scheduler.",
"default": False,
}),
"scheduler": (['Default', 'DDIM', 'DDIMScheduler', 'DDPMScheduler', 'DEISMultistepScheduler', 'DPMSolverSinglestepScheduler', 'DPMSolverMultistepScheduler', 'DPMSolverMultistepInverse', 'DPM++', 'DPM++ Karras', 'DPM++ 2M', 'DPM++ 2M Karras', 'DPM++ 2M SDE Karras', 'DPM++ 2M SDE', 'DPM++ 3M', 'DPM++ 3M Karras', 'DPM++ SDE Karras', 'DPM++ SDE', 'EDMEulerScheduler', 'EDMDPMSolverMultistepScheduler', 'Euler', 'EulerDiscreteScheduler', 'Euler Karras', 'Euler a', 'EulerAncestralDiscreteScheduler', 'FlowMatchEulerDiscreteScheduler', 'Heun', 'HeunDiscreteScheduler', 'Heun Karras', 'IPNDMScheduler', 'KDPM2DiscreteScheduler', 'KDPM2AncestralDiscreteScheduler', 'LCM', 'LCMScheduler', 'LMS', 'LMSDiscreteScheduler', 'LMS Karras', 'PNDMScheduler', 'TCDScheduler', 'UniPC', 'UniPCMultistepScheduler', 'UniPC Karras', 'UniPC 2M', 'UniPC 2M Karras', 'UniPC 3M', 'UniPC 3M Karras'], {
"tooltip": "An scheduler is a component that manages the inference process. Different schedulers can be used to achieve different results like more detailed images, faster inference, or more accurate results.",
"default": "Default",
}),
"useCFGScale": ("BOOLEAN", {
"tooltip": "Enable to include CFG scale parameter in API request. Disable if your model doesn't support CFG scale (like nano banana).",
"default": False,
}),
"cfgScale": ("FLOAT", {
"tooltip": "Guidance scale represents how closely the images will resemble the prompt or how much freedom the AI model has. Only used when 'Use CFG Scale' is enabled.",
"default": 6.5,
"min": 1.0,
"max": 50.0,
"step": 0.5,
}),
"useSeed": ("BOOLEAN", {
"tooltip": "Enable to include seed parameter in API request. Disable if your model doesn't support seed.",
"default": False,
}),
"seed": ("INT", {
"tooltip": "A value used to randomize the image generation. If you want to make images reproducible (generate the same image multiple times), you can use the same seed value.",
"default": 1,
"min": 1,
"max": 2147483647,
}),
"useClipSkip": ("BOOLEAN", {
"tooltip": "Enable to include clipSkip parameter in API request. Disable if your model doesn't support clipSkip.",
"default": False,
}),
"clipSkip": ("INT", {
"tooltip": "Enables skipping layers of the CLIP embedding process, leading to quicker and more varied image generation. Only used when 'Use Clip Skip' is enabled.",
"default": 0,
"min": 0,
"max": 2,
}),
"strength": ("FLOAT", {
"tooltip": "When doing Image-to-Image or Inpainting, this parameter is used to determine the influence of the seedImage image in the generated output. A lower value results in more influence from the original image, while a higher value allows more creative deviation.",
"default": 0.8,
"min": 0.0,
"max": 1.0,
"step": 0.01,
}),
"Mask Margin": ("BOOLEAN", {
"tooltip": "Enables Or Disables Mask Margin Feature.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"maskMargin": ("INT", {
"tooltip": "Adds extra context pixels around the masked region during inpainting. When this parameter is present, the model will zoom into the masked area, considering these additional pixels to create more coherent and well-integrated details.",
"default": 32,
"min": 32,
"max": 128,
}),
"outputFormat": (["WEBP", "PNG", "JPEG", "TIFF"], {
"tooltip": "Choose the output image format. This will be used for the API request. The actual saved format will match the format returned by the API (visible in imageURL).",
"default": rwUtils.getOutputFormat(),
}),
"batchSize": ("INT", {
"tooltip": "The number of images to generate in a single request.",
"default": 1,
"min": 1,
"max": 10,
}),
"acceleration": (["none", "low", "medium", "high"], {
"tooltip": "Applies optimized acceleration presets that automatically configure multiple generation parameters for the best speed and quality balance. This parameter serves as an abstraction layer that intelligently adjusts acceleratorOptions, steps, scheduler, and other underlying settings.\n\nAvailable values:\n- none: No acceleration applied, uses default parameter values.\n- low: Minimal acceleration with optimized settings for lowest quality loss.\n- medium: Balanced acceleration preset with moderate speed improvements.\n- high: Maximum acceleration with caching and aggressive optimizations for fastest generation.",
"default": "none",
}),
"outputQuality": ("INT", {
"tooltip": "Output quality passed to the API request (0-100).",
"default": rwUtils.OUTPUT_QUALITY,
"min": 0,
"max": 100,
"step": 1,
}),
"useUpscaleFactor": ("BOOLEAN", {
"tooltip": "Enable to include upscaleFactor parameter in API request.",
"default": False,
}),
"upscaleFactor": ([1, 2, 4], {
"tooltip": "Upscale the initially generated image by the given factor. Supported values: 1, 2, or 4.",
"default": 2,
}),
},
"optional": {
"Accelerator": ("RUNWAREACCELERATOR", {
"tooltip": "Connect a Runware Accelerator Options Node to configure caching and acceleration settings.",
}),
"Lora": ("RUNWARELORA", {
"tooltip": "Connect a Runware Lora From Lora Search Node Or Lora Combine For Multiple Lora's Together.",
}),
"Outpainting": ("RUNWAREOUTPAINT", {
"tooltip": "Connect a Runware Outpainting Node to extend the image boundaries in different directions.",
}),
"IPAdapters": ("RUNWAREIPAdapter", {
"tooltip": "Connect a Runware IP Adapter node or IP Adapter Combine node to use reference images for guiding the generation.",
}),
"ControlNet": ("RUNWARECONTROLNET", {
"tooltip": "Connect a Runware ControlNet Guidance Node to help the model generate images that align with the desired structure.",
}),
"Refiner": ("RUNWAREREFINER", {
"tooltip": "Connect a Runware Refiner Node to help create higher quality image outputs by incorporating specialized models designed to enhance image details and overall coherence.",
}),
"seedImage": ("IMAGE", {
"tooltip": "Specifies the seed image to be used for the diffusion process, when doing Image-to-Image, Inpainting or Outpainting, this parameter is required.",
}),
"maskImage": ("MASK", {
"tooltip": "Specifies the mask image to be used for the inpainting process, when doing Inpainting, this parameter is required.",
}),
"Embeddings": ("RUNWAREEMBEDDING", {
"tooltip": "Connect a Runware Embedding Node to help the model generate images that align with the desired structure.",
}),
"VAE": ("RUNWAREVAE", {
"tooltip": "Connect a Runware VAE Node to help the model generate images that align with the desired structure.",
}),
"referenceImages": ("RUNWAREREFERENCEIMAGES", {
"tooltip": "Connect a Runware Reference Images Node to provide visual guidance for image generation.",
}),
"inputs": ("RUNWAREIMAGEINFERENCEINPUTS", {
"tooltip": "Connect a Runware Image Inference Inputs Node to provide custom inputs like references for the inference process.",
}),
"providerSettings": ("RUNWAREPROVIDERSETTINGS", {
"tooltip": "Connect a Runware Provider Settings Node to configure provider-specific parameters.",
}),
"safetyInputs": ("RUNWARESAFETYINPUTS", {
"tooltip": "Connect Runware Safety Inputs node to configure safety and content moderation settings.",
}),
"settings": ("RUNWARESETTINGS", {
"tooltip": "Connect Runware Image Inference Settings for temperature, systemPrompt, topP, layers, quality, backgroundMode, backgroundColor, enhancePrompt, scoringPrompt, background, style, search, promptExtend, editRegions (JSON), thinking, thinkingLevel (low/medium/high/xhigh), sequential, colorPalette (via Color Palette node), etc.",
}),
"ultralytics": ("RUNWAREULTRALYTICSINPUTS", {
"tooltip": "Connect Runware Image Inference Ultralytics node to configure Ultralytics parameters (maskBlur, maskPadding, confidence, prompts, steps, CFGScale, strength).",
}),
"advancedFeatures": ("RUNWAREIMAGEINFERENCEADVANCEDFEATURES", {
"tooltip": "Connect Runware Image Advanced Feature Input to configure advancedFeatures: layerDiffuse (transparency), hiresFix (two-stage high-res), watermark, and regionalPrompting (per-region prompts and masks).",
}),
}
}
DESCRIPTION = "Generates Images Lightning Fast With Runware Image Inference Sonic Engine."
FUNCTION = "generateImage"
RETURN_TYPES = ("STRING", "RUNWARETASK")
RETURN_NAMES = ("IMAGE", "RW-Task")
CATEGORY = "Runware"
@classmethod
def VALIDATE_INPUTS(cls, **kwargs):
positivePrompt = kwargs.get("positivePrompt", "")
negativePrompt = kwargs.get("negativePrompt", "")
settings = kwargs.get("settings")
structured_prompt = None
if isinstance(settings, dict):
structured_prompt = settings.get("structuredPrompt")
has_structured_prompt = (
isinstance(structured_prompt, dict) and len(structured_prompt) > 0
)
has_positive_prompt = bool(
positivePrompt is not None and str(positivePrompt) != ""
)
if has_structured_prompt and has_positive_prompt:
return (
"settings.structuredPrompt and positivePrompt are mutually exclusive. "
"Clear positivePrompt or disconnect Structured Prompt from Settings."
)
# Only validate positivePrompt length if it's provided
if positivePrompt and len(positivePrompt) > 10000:
return "Positive Prompt is too long. Maximum length is 10000 characters."
if negativePrompt and len(negativePrompt) > 10000:
return "Negative Prompt is too long. Maximum length is 10000 characters."
return True
def generateImage(self, **kwargs):
runwareModel = kwargs.get("Model")
positivePrompt = kwargs.get("positivePrompt")
negativePrompt = kwargs.get("negativePrompt", None)
multiInferenceMode = kwargs.get("Multi Inference Mode", False)
promptWeighting = kwargs.get("Prompt Weighting", "Disabled")
runwareControlNet = kwargs.get("ControlNet", None)
runwareAccelerator = kwargs.get("Accelerator", None)
runwareLora = kwargs.get("Lora", None)
runwareOutpainting = kwargs.get("Outpainting", None)
runwareIPAdapters = kwargs.get("IPAdapters", None)
runwareRefiner = kwargs.get("Refiner", None)
runwareEmbedding = kwargs.get("Embeddings", None)
runwareVAE = kwargs.get("VAE", None)
referenceImages = kwargs.get("referenceImages", None)
inputs = kwargs.get("inputs", None)
providerSettings = kwargs.get("providerSettings", None)
safetyInputs = kwargs.get("safetyInputs", None)
settings = kwargs.get("settings", None)
ultralytics = kwargs.get("ultralytics", None)
advanced_features = kwargs.get("advancedFeatures", None)
seedImage = kwargs.get("seedImage", None)
seedImageStrength = kwargs.get("strength", 0.8)
maskImage = kwargs.get("maskImage", None)
enableMaskMargin = kwargs.get("Mask Margin", False)
maskImageMargin = kwargs.get("maskMargin", 32)
clipSkip = kwargs.get("clipSkip", 0)
height = kwargs.get("height", 512)
width = kwargs.get("width", 512)
steps = kwargs.get("steps", 4)
useSteps = kwargs.get("useSteps", False)
scheduler = kwargs.get("scheduler", "Default")
useScheduler = kwargs.get("useScheduler", False)
cfgScale = kwargs.get("cfgScale", 6.5)
useCFGScale = kwargs.get("useCFGScale", False)
seed = kwargs.get("seed")
useSeed = kwargs.get("useSeed", False)
useClipSkip = kwargs.get("useClipSkip", False)
dimensions = kwargs.get("dimensions", "Square (512x512)")
outputFormat = kwargs.get("outputFormat", rwUtils.getOutputFormat())
batchSize = kwargs.get("batchSize", 1)
outputQuality = kwargs.get("outputQuality", rwUtils.OUTPUT_QUALITY)
acceleration = kwargs.get("acceleration", "none")
useResolution = kwargs.get("useResolution", False)
resolution = kwargs.get("resolution", "1k")
useUpscaleFactor = kwargs.get("useUpscaleFactor", False)
upscaleFactor = kwargs.get("upscaleFactor", 2)
if (maskImage is not None and seedImage is None):
raise Exception("Mask Image Requires Seed Image To Be Provided!")
genConfig = [
{
"taskType": "imageInference",
"taskUUID": rwUtils.genRandUUID(),
"model": runwareModel,
"outputType": "URL",
"outputFormat": outputFormat,
"outputQuality": int(outputQuality),
"numberResults": batchSize,
"deliveryMethod": "async",
}
]
# Add positivePrompt only if provided
if positivePrompt is not None and positivePrompt != "":
genConfig[0]["positivePrompt"] = positivePrompt
# For Debugging Purposes Only
print(f"[Debugging] Task UUID: {genConfig[0]['taskUUID']}")
# Add steps, CFGScale, seed, scheduler, clipSkip, and dimensions only if enabled
if useSteps:
genConfig[0]["steps"] = steps
if useCFGScale:
# Extract provider name from model (e.g., "bria:20@1" -> "bria")
provider_name = runwareModel.split(":")[0] if ":" in runwareModel else runwareModel
# Cast to int for Bria models
if provider_name.lower() == "bria":
genConfig[0]["CFGScale"] = int(cfgScale)
else:
genConfig[0]["CFGScale"] = cfgScale
if useSeed:
genConfig[0]["seed"] = seed
if useScheduler:
genConfig[0]["scheduler"] = scheduler
if useClipSkip:
genConfig[0]["clipSkip"] = clipSkip
if dimensions != "None":
genConfig[0]["width"] = width
genConfig[0]["height"] = height
if (negativePrompt is not None and negativePrompt != ""):
genConfig[0]["negativePrompt"] = negativePrompt
if (promptWeighting != "Disabled"):
if (promptWeighting == "sdEmbeds"):
genConfig[0]["promptWeighting"] = "sdEmbeds"
else:
genConfig[0]["promptWeighting"] = "compel"
if (runwareAccelerator is not None):
genConfig[0]["acceleratorOptions"] = runwareAccelerator
# Add acceleration if not "none"
if acceleration and acceleration != "none":
genConfig[0]["acceleration"] = acceleration
# Add resolution if enabled
if useResolution:
genConfig[0]["resolution"] = resolution
if useUpscaleFactor:
upscaleFactor = int(upscaleFactor)
if upscaleFactor not in (1, 2, 4):
raise ValueError("upscaleFactor must be 1, 2, or 4 when useUpscaleFactor is enabled.")
genConfig[0]["upscaleFactor"] = upscaleFactor
if (runwareLora is not None):
if (isinstance(runwareLora, list)):
genConfig[0]["lora"] = runwareLora
elif (isinstance(runwareLora, dict)):
genConfig[0]["lora"] = [runwareLora]
if (runwareIPAdapters is not None):
if (isinstance(runwareIPAdapters, list)):
genConfig[0]["ipAdapters"] = runwareIPAdapters
elif (isinstance(runwareIPAdapters, dict)):
genConfig[0]["ipAdapters"] = [runwareIPAdapters]
if (runwareEmbedding is not None):
if (isinstance(runwareEmbedding, list)):
genConfig[0]["embeddings"] = runwareEmbedding
elif (isinstance(runwareEmbedding, dict)):
genConfig[0]["embeddings"] = [runwareEmbedding]
if runwareOutpainting:
genConfig[0]["outpaint"] = runwareOutpainting
if (runwareVAE is not None):
genConfig[0]["vae"] = runwareVAE
if (runwareControlNet is not None):
genConfig[0]["controlNet"] = runwareControlNet
if (runwareRefiner is not None):
genConfig[0]["refiner"] = runwareRefiner
if (seedImage is not None):
seedImage = rwUtils.convertTensor2IMG(seedImage)
genConfig[0]["seedImage"] = seedImage
genConfig[0]["strength"] = seedImageStrength
if (maskImage is not None):
maskImage = rwUtils.convertTensor2IMG(maskImage)
genConfig[0]["maskImage"] = maskImage
if (enableMaskMargin):
genConfig[0]["maskMargin"] = maskImageMargin
# Handle referenceImages
if referenceImages is not None:
genConfig[0]["referenceImages"] = referenceImages
# Handle inputs (custom inference inputs)
if inputs is not None:
genConfig[0]["inputs"] = inputs
# Handle providerSettings - extract provider name from model and merge with custom settings
if providerSettings is not None:
# Extract provider name from model (e.g., "bytedance:1@1" -> "bytedance")
provider_name = runwareModel.split(":")[0] if ":" in runwareModel else runwareModel
# Special case: model "runware:201@10" uses "alibaba" as provider
if runwareModel == "runware:201@10":
provider_name = "alibaba"
# If providerSettings is a dictionary, create the correct API format
if isinstance(providerSettings, dict):
# Check if providerSettings already has a provider key (e.g., {"alibaba": {...}})
if any(key in providerSettings for key in ["alibaba", "bytedance", "klingai", "openai", "pixverse",
"bria", "lightricks", "luma", "minimax", "runway", "vidu", "elevenlabs", "bfl", "xai", "recraft", "sourceful"]):
final_provider_settings = providerSettings
else:
# Otherwise, wrap with the extracted provider name
final_provider_settings = {
provider_name: providerSettings
}
genConfig[0]["providerSettings"] = final_provider_settings
else:
# If it's already in the correct format, use it directly
genConfig[0]["providerSettings"] = providerSettings
# Add safety inputs if provided
if safetyInputs is not None and isinstance(safetyInputs, dict) and len(safetyInputs) > 0:
genConfig[0]["safety"] = safetyInputs
# Add settings if provided
if settings is not None and isinstance(settings, dict) and len(settings) > 0:
genConfig[0]["settings"] = settings
# Add ultralytics parameters if provided
if ultralytics is not None and isinstance(ultralytics, dict) and len(ultralytics) > 0:
genConfig[0]["ultralytics"] = ultralytics
# Add advancedFeatures if provided (layerDiffuse, hiresFix, etc.)
if advanced_features is not None and isinstance(advanced_features, dict) and len(advanced_features) > 0:
genConfig[0]["advancedFeatures"] = advanced_features
if (multiInferenceMode):
return (None, genConfig)
else:
try:
# Debug: Print the request being sent
print(f"[DEBUG] Sending Image Inference Request:")
print(f"[DEBUG] Request Payload: {rwUtils.safe_json_dumps(genConfig, indent=2)}")
genResult = rwUtils.inferenecRequest(genConfig)
# Debug: Print the response received
print(f"[DEBUG] Received Image Inference Response:")
print(f"[DEBUG] Response: {rwUtils.safe_json_dumps(genResult, indent=2)}")
except Exception as e:
# Re-raise the original error without modification
raise e
# Extract task UUID for polling (async delivery)
taskUUID = genConfig[0]["taskUUID"]
# Poll for image completion
while True:
# Check for interrupt before each poll
comfy.model_management.throw_exception_if_processing_interrupted()
# Poll for image result
pollResult = rwUtils.pollVideoResult(taskUUID) # Reuse pollVideoResult as it's generic
print(f"[Debugging] Poll result: {rwUtils.safe_json_dumps(pollResult, indent=2) if isinstance(pollResult, (dict, list)) else pollResult}")
# Check for errors first
if pollResult and "errors" in pollResult and len(pollResult["errors"]) > 0:
error_info = pollResult["errors"][0]
error_message = error_info.get("message", "Unknown error")
# Extract more detailed error info if available
if "responseContent" in error_info:
response_content = error_info["responseContent"]
# Handle both string and dict response content
if isinstance(response_content, str):
detailed_message = response_content
elif isinstance(response_content, dict):
detailed_message = response_content.get("message", str(response_content))
else:
detailed_message = str(response_content)
if detailed_message:
error_message = f"{error_message}\nProvider Error: {detailed_message}"
# Include taskUUID for debugging
task_uuid = error_info.get("taskUUID", "unknown")
raise Exception(f"Image generation failed (Task: {task_uuid}): {error_message}")
if pollResult and "data" in pollResult and len(pollResult["data"]) > 0:
image_data = pollResult["data"][0]
# Check status directly
if "status" in image_data:
status = image_data["status"]
if status == "success":
# Check for image data (imageURL, imageBase64Data)
if "imageURL" in image_data or "imageBase64Data" in image_data:
# Extract URLs from all results
image_urls = []
for result in pollResult.get("data", []):
imageURL = result.get("imageURL")
if imageURL:
image_urls.append(imageURL)
# Return URLs as comma-separated string (or single URL)
image_url_str = ",".join(image_urls) if image_urls else ""
return (image_url_str, None)
# If status is "processing", continue polling
# Check for interrupt before waiting
comfy.model_management.throw_exception_if_processing_interrupted()
# Wait before next poll (split into smaller chunks to allow more frequent interrupt checks)
for _ in range(10): # 10 x 0.1 second = 1 second total
comfy.model_management.throw_exception_if_processing_interrupted()
rwUtils.time.sleep(0.1)
-80
View File
@@ -1,80 +0,0 @@
from typing import Dict, Any
class RunwareImageInferenceAdvancedFeatures:
"""Runware Image Advanced Feature Input Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useLayerDiffuse": ("BOOLEAN", {
"tooltip": "Enable to include layerDiffuse in advancedFeatures. When enabled, LayerDiffuse generates images with transparency (alpha) without post-processing background removal.",
"default": False,
}),
"layerDiffuse": ("BOOLEAN", {
"tooltip": "Enable LayerDiffuse technology for direct generation of images with transparency (alpha channels). Only used when 'Use Layer Diffuse' is enabled.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
"useHiresFix": ("BOOLEAN", {
"tooltip": "Enable to include hiresFix in advancedFeatures. When enabled, uses a two-stage process for higher-resolution, higher-detail images.",
"default": False,
}),
"hiresFix": ("BOOLEAN", {
"tooltip": "Enable a two-stage generation: first at native resolution, then upscale and refine in a second pass. Only used when 'Use Hires Fix' is enabled.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
"watermark": ("RUNWAREIMAGEWATERMARKADVFEATURE", {
"tooltip": "Connect Runware Watermark Advanced Feature to include watermark configuration in advancedFeatures.watermark.",
}),
"regionalPrompting": ("RUNWAREREGIONALPROMPTINGADVFEATURE", {
"tooltip": "Connect Runware Regional Prompting Advanced Feature to configure regionalPrompting in advancedFeatures.",
}),
},
}
RETURN_TYPES = ("RUNWAREIMAGEINFERENCEADVANCEDFEATURES",)
RETURN_NAMES = ("advancedFeatures",)
FUNCTION = "createAdvancedFeatures"
CATEGORY = "Runware"
DESCRIPTION = (
"Configure advanced features for Runware Image Inference: layerDiffuse (transparency), "
"hiresFix (two-stage high-res), watermark, and regional prompting. "
"Connect to Runware Image Inference advancedFeatures input."
)
def createAdvancedFeatures(self, watermark=None, regionalPrompting=None, **kwargs) -> tuple:
"""Build advancedFeatures dict for the API."""
use_layer_diffuse = kwargs.get("useLayerDiffuse", False)
layer_diffuse = kwargs.get("layerDiffuse", False)
use_hires_fix = kwargs.get("useHiresFix", False)
hires_fix = kwargs.get("hiresFix", False)
advanced_features: Dict[str, Any] = {}
if use_layer_diffuse:
advanced_features["layerDiffuse"] = bool(layer_diffuse)
if use_hires_fix:
advanced_features["hiresFix"] = bool(hires_fix)
if isinstance(watermark, dict) and watermark:
advanced_features["watermark"] = watermark
if isinstance(regionalPrompting, dict) and regionalPrompting:
advanced_features["regionalPrompting"] = regionalPrompting
return (advanced_features,)
NODE_CLASS_MAPPINGS = {
"RunwareImageInferenceAdvancedFeatures": RunwareImageInferenceAdvancedFeatures,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareImageInferenceAdvancedFeatures": "Runware Image Advanced Feature Input",
}
-160
View File
@@ -1,160 +0,0 @@
from .utils import runwareUtils as rwUtils
class imageInferenceInputs:
"""Image Inference Inputs node for configuring image generation inputs"""
MAX_REFERENCE_IMAGES = 14
MAX_SUPER_RESOLUTION_REFERENCE_IMAGES = 5
@classmethod
def INPUT_TYPES(cls):
optionalInputs = {
"image": ("IMAGE", {
"tooltip": "Specifies an image input for the inference process."
}),
"mask": ("IMAGE", {
"tooltip": "Optional mask image for the inference process."
}),
"seedImage": ("IMAGE", {
"tooltip": "Optional seed image for the inference process"
}),
}
for i in range(1, cls.MAX_REFERENCE_IMAGES + 1):
ordinal = rwUtils.getOrdinal(i)
optionalInputs[f"Reference Image {i}"] = ("IMAGE", {
"tooltip": f"Specifies {ordinal.capitalize()} Reference Image for the inputs. These reference images help guide the image generation process."
})
optionalInputs[f"Reference Tag {i}"] = ("STRING", {
"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": "",
})
optionalInputs[f"Reference Strength {i}"] = ("FLOAT", {
"tooltip": f"Strength (0-1) for sketch reference. Only used when Reference Type is 'sketch'.",
"default": 0.0,
"min": 0.0,
"max": 1.0,
"step": 0.1,
})
for i in range(1, cls.MAX_SUPER_RESOLUTION_REFERENCE_IMAGES + 1):
ordinal = rwUtils.getOrdinal(i)
optionalInputs[f"Super Resolution Reference Image {i}"] = ("IMAGE", {
"tooltip": f"Specifies {ordinal.capitalize()} Super Resolution Reference Image for the inputs.",
})
optionalInputs["Fonts"] = ("RUNWAREIMAGEINFERENCEFONTS", {
"tooltip": "Connect Runware Image Inference Inputs Fonts for inputs.fonts (up to 2 font URL + text references).",
})
return {
"required": {},
"optional": optionalInputs
}
DESCRIPTION = (
"Configure custom inputs for Runware Image Inference, including image/mask/seedImage, reference images "
"(with optional tag, role, type e.g. 'sketch' for illustrative style models, and strength 0-1 for sketch), "
"super resolution references, and font references (inputs.fonts)."
)
FUNCTION = "createInputs"
RETURN_TYPES = ("RUNWAREIMAGEINFERENCEINPUTS",)
RETURN_NAMES = ("Inference Inputs",)
CATEGORY = "Runware"
def createInputs(self, **kwargs):
"""Create image inference inputs from provided parameters"""
image = kwargs.get("image", None)
mask = kwargs.get("mask", None)
seedImage = kwargs.get("seedImage", None)
inputs = {}
if image is not None:
inputs["image"] = rwUtils.convertTensor2IMG(image)
if mask is not None:
inputs["mask"] = rwUtils.convertTensor2IMG(mask)
if seedImage is not None:
inputs["seedImage"] = rwUtils.convertTensor2IMG(seedImage)
references = self._collectReferences(kwargs)
if len(references) > 0:
inputs["referenceImages"] = references
super_resolution_refs = self._collectSuperResolutionReferences(kwargs)
if len(super_resolution_refs) > 0:
inputs["superResolutionReferenceImages"] = super_resolution_refs
fonts = kwargs.get("Fonts", None)
if fonts is not None and isinstance(fonts, list) and len(fonts) > 0:
inputs["fonts"] = fonts
return (inputs,)
def _collectReferences(self, kwargs):
"""Collect and convert reference images to list. Each entry may include type (e.g. 'sketch') and strength (0-1, only for sketch)."""
reference_slots = []
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, role, ref_type, strength))
if not reference_slots:
return []
has_tags = any(
isinstance(tag, str) and tag.strip() != ""
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
)
if not has_tags and not has_roles and not has_type:
return [
rwUtils.convertTensor2IMG(image)
for image, _, _, _, _ in reference_slots
]
references = []
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":
entry["strength"] = max(0.0, min(1.0, float(strength)))
references.append(entry)
return references
def _collectSuperResolutionReferences(self, kwargs):
"""Collect and convert super resolution reference images to list (same pattern as referenceImages)"""
images = []
for i in range(1, self.MAX_SUPER_RESOLUTION_REFERENCE_IMAGES + 1):
image = kwargs.get(f"Super Resolution Reference Image {i}", None)
if image is not None:
images.append(rwUtils.convertTensor2IMG(image))
return images
-60
View File
@@ -1,60 +0,0 @@
"""
Runware Image Inference Inputs Fonts node.
Builds inputs.fonts for image inference (up to 2 font references with url and text).
"""
from typing import Any, Dict, List, Tuple
class RunwareImageInferenceInputsFonts:
"""Build inputs.fonts[] for image inference requests."""
MAX_FONTS = 2
@classmethod
def INPUT_TYPES(cls):
optional_inputs = {}
for i in range(1, cls.MAX_FONTS + 1):
optional_inputs[f"fontUrl{i}"] = ("STRING", {
"default": "",
"tooltip": (
f"Font file URL for reference {i}. Must be ttf, otf, woff, or woff2. "
"Included when both fontUrl and text are non-empty."
),
})
optional_inputs[f"text{i}"] = ("STRING", {
"multiline": True,
"default": "",
"tooltip": f"Text to render with font reference {i}. Included when both fontUrl and text are non-empty.",
})
return {
"required": {},
"optional": optional_inputs,
}
RETURN_TYPES = ("RUNWAREIMAGEINFERENCEFONTS",)
RETURN_NAMES = ("fonts",)
FUNCTION = "create_fonts"
CATEGORY = "Runware"
DESCRIPTION = (
"Configure inputs.fonts for Runware Image Inference (up to 2 entries). "
"Each entry: { \"url\": \"<font URL>\", \"text\": \"<text to render>\" }. "
"A slot is included only when both fontUrl and text are non-empty. "
"Connect to Runware Image Inference Inputs."
)
def create_fonts(self, **kwargs) -> Tuple[List[Dict[str, Any]]]:
fonts: List[Dict[str, Any]] = []
for i in range(1, self.MAX_FONTS + 1):
font_url = (kwargs.get(f"fontUrl{i}") or "").strip()
text = (kwargs.get(f"text{i}") or "").strip()
if len(font_url) > 0 and len(text) > 0:
fonts.append({
"url": font_url,
"text": text,
})
return (fonts,)
@@ -1,59 +0,0 @@
"""
Runware Image Inference Settings Color Palette
Builds settings.colorPalette: list of { "hex": "#...", "ratio": "NN.NN%" } for Runware Image Inference Settings.
"""
from typing import Any, Dict, List, Tuple
class RunwareImageInferenceSettingsColorPalette:
"""Up to 8 hex/ratio pairs; each slot is included only when use_{i} is enabled."""
_SLOTS = 8
@classmethod
def INPUT_TYPES(cls):
optional: Dict[str, tuple] = {}
for i in range(1, cls._SLOTS + 1):
optional[f"use_{i}"] = ("BOOLEAN", {
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
"tooltip": f"Include swatch {i} in the output colorPalette.",
})
optional[f"hex_{i}"] = ("STRING", {
"default": "",
"tooltip": f"Swatch {i} hex, e.g. #C2D1E6. Required when use_{i} is enabled.",
})
optional[f"ratio_{i}"] = ("STRING", {
"default": "",
"tooltip": f"Swatch {i} ratio, e.g. 23.51%%. Optional.",
})
return {"required": {}, "optional": optional}
RETURN_TYPES = ("RUNWAREIMAGEINFERENCECOLORPALETTE",)
RETURN_NAMES = ("colorPalette",)
FUNCTION = "build_palette"
CATEGORY = "Runware"
DESCRIPTION = (
"Build a colorPalette list for image inference settings (hex + optional ratio per swatch). "
"Enable use_1…use_8 for each swatch to include; connect the output to Runware Image Inference Settings."
)
def build_palette(self, **kwargs) -> Tuple[List[Dict[str, Any]]]:
out: List[Dict[str, Any]] = []
for i in range(1, self._SLOTS + 1):
if not kwargs.get(f"use_{i}", False):
continue
hx = str(kwargs.get(f"hex_{i}") or "").strip()
if not hx:
raise ValueError(
f"use_{i} is enabled but hex_{i} is empty or invalid. "
"Provide a hex color (e.g. #C2D1E6) or disable this swatch."
)
ratio = str(kwargs.get(f"ratio_{i}") or "").strip()
entry: Dict[str, Any] = {"hex": hx}
if ratio:
entry["ratio"] = ratio
out.append(entry)
return (out,)
@@ -1,56 +0,0 @@
"""
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],)
@@ -1,103 +0,0 @@
"""
Runware Image Inference Settings Prompt Enhance
Builds settings.promptEnhance for image inference (e.g. Ernie).
Connect the output to Runware Image Inference Settings → promptEnhance.
"""
from typing import Any, Dict, Tuple
class RunwareImageInferenceSettingsPromptEnhance:
"""Build settings.promptEnhance object (enabled, temperature, topP)."""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useTemperature": ("BOOLEAN", {
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
"tooltip": "Include temperature in settings.promptEnhance.",
}),
"temperature": ("FLOAT", {
"default": 1.2,
"min": 0.1,
"max": 5.0,
"step": 0.1,
"tooltip": (
"settings.promptEnhance.temperature (0.1–5). "
"Ernie-specific API default when omitted: 0.6."
),
}),
"useTopP": ("BOOLEAN", {
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
"tooltip": "Include topP in settings.promptEnhance.",
}),
"topP": ("FLOAT", {
"default": 0.95,
"min": 0.1,
"max": 1.0,
"step": 0.01,
"tooltip": (
"settings.promptEnhance.topP (0.1–1). "
"Ernie-specific API default when omitted: 0.95."
),
}),
"useEnabled": ("BOOLEAN", {
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
"tooltip": "Include enabled in settings.promptEnhance.",
}),
"enabled": ("BOOLEAN", {
"default": True,
"label_on": "true",
"label_off": "false",
"tooltip": (
"settings.promptEnhance.enabled. Required when using temperature or topP. "
"Only used when 'Use Enabled' is on."
),
}),
},
}
RETURN_TYPES = ("RUNWAREIMAGEINFERENCEPROMPTENHANCE",)
RETURN_NAMES = ("promptEnhance",)
FUNCTION = "build_prompt_enhance"
CATEGORY = "Runware"
DESCRIPTION = (
"Build settings.promptEnhance (enabled, temperature, topP) for image inference. "
"Connect to Runware Image Inference Settings."
)
def build_prompt_enhance(self, **kwargs) -> Tuple[Dict[str, Any]]:
prompt_enhance: Dict[str, Any] = {}
if kwargs.get("useEnabled", False):
prompt_enhance["enabled"] = bool(kwargs.get("enabled", True))
if kwargs.get("useTemperature", False):
prompt_enhance["temperature"] = float(kwargs.get("temperature", 1.2))
if kwargs.get("useTopP", False):
prompt_enhance["topP"] = float(kwargs.get("topP", 0.95))
if prompt_enhance and "enabled" not in prompt_enhance and (
"temperature" in prompt_enhance or "topP" in prompt_enhance
):
prompt_enhance["enabled"] = True
return (prompt_enhance,)
NODE_CLASS_MAPPINGS = {
"RunwareImageInferenceSettingsPromptEnhance": RunwareImageInferenceSettingsPromptEnhance,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareImageInferenceSettingsPromptEnhance": "Runware Image Inference Settings Prompt Enhance",
}
@@ -1,171 +0,0 @@
"""
Runware Image Inference Settings Scoring Rubric
Builds settings.scoringRubric[] for Riverflow 2.5 (1–8 dimensions).
"""
from typing import Any, Dict, List, Tuple
_MAX_SCORE_GUIDANCE = 5
_MAX_RUBRIC_COMBINE = 4
def _build_score_guidance(kwargs: Dict[str, Any]) -> List[Dict[str, Any]]:
guidance: List[Dict[str, Any]] = []
for i in range(1, _MAX_SCORE_GUIDANCE + 1):
if not kwargs.get(f"useScoreGuidance{i}", False):
continue
score = kwargs.get(f"score{i}")
description = (kwargs.get(f"guidanceDescription{i}") or "").strip()
if score is None or not description:
continue
guidance.append({
"score": float(score),
"description": description,
})
return guidance
def _build_rubric_entry(kwargs: Dict[str, Any]) -> Dict[str, Any]:
entry: Dict[str, Any] = {
"key": (kwargs.get("key") or "").strip(),
"label": (kwargs.get("label") or "").strip(),
"description": (kwargs.get("description") or "").strip(),
"weight": float(kwargs.get("weight", 1.0)),
}
if kwargs.get("usePassingScore", False):
entry["passingScore"] = float(kwargs.get("passingScore", 0.0))
guidance = _build_score_guidance(kwargs)
if guidance:
entry["scoreGuidance"] = guidance
return entry
class RunwareImageInferenceSettingsScoringRubric:
"""Build one scoringRubric dimension for settings.scoringRubric."""
@classmethod
def INPUT_TYPES(cls):
optional: Dict[str, tuple] = {
"usePassingScore": ("BOOLEAN", {
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
"tooltip": "Include passingScore for this scoring dimension.",
}),
"passingScore": ("FLOAT", {
"default": 0.5,
"min": 0.0,
"max": 1.0,
"step": 0.01,
"tooltip": "Optional dimension floor from 0 to 1. Only used when usePassingScore is enabled.",
}),
}
for i in range(1, _MAX_SCORE_GUIDANCE + 1):
optional[f"useScoreGuidance{i}"] = ("BOOLEAN", {
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
"tooltip": f"Include scoreGuidance anchor {i} (up to {_MAX_SCORE_GUIDANCE} per dimension).",
})
optional[f"score{i}"] = ("FLOAT", {
"default": 0.0,
"min": 0.0,
"max": 1.0,
"step": 0.01,
"tooltip": f"Normalized score anchor (0–1) for scoreGuidance entry {i}.",
})
optional[f"guidanceDescription{i}"] = ("STRING", {
"multiline": True,
"default": "",
"tooltip": f"What score anchor {i} means for the judge.",
})
return {
"required": {
"key": ("STRING", {
"default": "",
"tooltip": "Unique machine-readable dimension key (e.g. composition, lighting).",
}),
"label": ("STRING", {
"default": "",
"tooltip": "Short human-readable dimension label.",
}),
"description": ("STRING", {
"multiline": True,
"default": "",
"tooltip": "Judging criteria for this score dimension.",
}),
"weight": ("FLOAT", {
"default": 1.0,
"min": 0.01,
"max": 100.0,
"step": 0.01,
"tooltip": "Positive scoring weight. Weights are normalized internally.",
}),
},
"optional": optional,
}
RETURN_TYPES = ("RUNWAREIMAGEINFERENCESCORINGRUBRIC",)
RETURN_NAMES = ("scoringRubric",)
FUNCTION = "build_scoring_rubric"
CATEGORY = "Runware"
DESCRIPTION = (
"Define one settings.scoringRubric[] dimension for Riverflow 2.5 (key, label, description, weight, "
"optional passingScore and up to 5 scoreGuidance anchors). Connect to Runware Image Inference Settings, "
"or use Runware Image Inference Settings Scoring Rubric Combine for up to 4 dimensions."
)
def build_scoring_rubric(self, **kwargs) -> Tuple[List[Dict[str, Any]]]:
return ([_build_rubric_entry(kwargs)],)
class RunwareImageInferenceSettingsScoringRubricCombine:
"""Combine up to 4 Scoring Rubric node outputs into settings.scoringRubric."""
@classmethod
def INPUT_TYPES(cls):
optional: Dict[str, tuple] = {}
for i in range(2, _MAX_RUBRIC_COMBINE + 1):
optional[f"Scoring Rubric {i}"] = ("RUNWAREIMAGEINFERENCESCORINGRUBRIC", {
"tooltip": f"Optional scoring rubric dimension {i} (connect Runware Image Inference Settings Scoring Rubric).",
})
return {
"required": {
"Scoring Rubric 1": ("RUNWAREIMAGEINFERENCESCORINGRUBRIC", {
"tooltip": "First scoring rubric dimension (connect Runware Image Inference Settings Scoring Rubric).",
}),
},
"optional": optional,
}
RETURN_TYPES = ("RUNWAREIMAGEINFERENCESCORINGRUBRIC",)
RETURN_NAMES = ("scoringRubric",)
FUNCTION = "combine_scoring_rubric"
CATEGORY = "Runware"
DESCRIPTION = (
"Combine up to 4 Runware Image Inference Settings Scoring Rubric outputs into settings.scoringRubric. "
"Connect scoringRubric to Runware Image Inference Settings."
)
def combine_scoring_rubric(self, **kwargs) -> Tuple[List[Dict[str, Any]]]:
combined: List[Dict[str, Any]] = []
for i in range(1, _MAX_RUBRIC_COMBINE + 1):
chunk = kwargs.get(f"Scoring Rubric {i}")
if chunk is not None and isinstance(chunk, list):
combined.extend(
entry for entry in chunk
if isinstance(entry, dict) and len(entry) > 0
)
return (combined,)
@@ -1,216 +0,0 @@
"""
Runware Image Inference Settings Structured Prompt
Builds settings.structuredPrompt for Ideogram 4.0 and compatible models.
Connect the output to Runware Image Inference Settings → structuredPrompt.
Mutually exclusive with positivePrompt at the API level.
"""
import json
from typing import Any, Dict, List, Tuple
class RunwareImageInferenceSettingsStructuredPrompt:
"""Ideogram-style structured JSON prompt for image inference settings."""
_ELEMENT_SLOTS = 8
@classmethod
def INPUT_TYPES(cls):
optional: Dict[str, tuple] = {
"background": ("STRING", {
"multiline": True,
"default": "",
"tooltip": "compositional_deconstruction.background — setting/surface/atmosphere, not discrete subjects.",
}),
"useStyleDescription": ("BOOLEAN", {
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
"tooltip": "Include style_description from JSON below.",
}),
"style_description": ("STRING", {
"multiline": True,
"default": "",
"tooltip": "style_description object as JSON (aesthetics, lighting, photo OR art_style, medium, color_palette). Only when 'Use Style Description' is enabled.",
}),
"useElementsJson": ("BOOLEAN", {
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
"tooltip": "Set elements from a JSON array instead of element slots 1–8.",
}),
"elementsJson": ("STRING", {
"multiline": True,
"default": "",
"tooltip": "JSON array of element objects ({type, desc, text?, bbox?, color_palette?}). Only when 'Use Elements JSON' is enabled.",
}),
}
for i in range(1, cls._ELEMENT_SLOTS + 1):
optional[f"use_element_{i}"] = ("BOOLEAN", {
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
"tooltip": f"Include element slot {i} in compositional_deconstruction.elements.",
})
optional[f"element_type_{i}"] = (["obj", "text"], {
"default": "obj",
"tooltip": f"Element {i} type: obj (desc interpreted) or text (text rendered literally).",
})
optional[f"element_desc_{i}"] = ("STRING", {
"multiline": True,
"default": "",
"tooltip": f"Element {i} desc — natural language treatment/position notes.",
})
optional[f"element_text_{i}"] = ("STRING", {
"multiline": True,
"default": "",
"tooltip": f"Element {i} text — literal copy for type text only.",
})
optional[f"element_bbox_{i}"] = ("STRING", {
"default": "",
"tooltip": f"Element {i} optional bbox JSON: [y_min, x_min, y_max, x_max] in 0–1000 coords.",
})
optional[f"element_color_palette_{i}"] = ("STRING", {
"default": "",
"tooltip": f"Element {i} optional hex colours, comma-separated (e.g. #1F3B26,#7A5A2F).",
})
required = {
"high_level_description": ("STRING", {
"multiline": True,
"default": "",
"tooltip": "Global scene summary (required).",
}),
}
return {"required": required, "optional": optional}
RETURN_TYPES = ("RUNWAREIMAGEINFERENCESTRUCTUREDPROMPT",)
RETURN_NAMES = ("structuredPrompt",)
FUNCTION = "build_structured_prompt"
CATEGORY = "Runware"
DESCRIPTION = (
"Build settings.structuredPrompt for Ideogram 4.0 (snake_case schema) from "
"high_level_description, background, style_description, and elements. "
"Connect to Runware Image Inference Settings. Do not send positivePrompt in the same request."
)
@staticmethod
def _parse_json_object(raw: str, field_name: str) -> Dict[str, Any]:
text = (raw or "").strip()
if not text:
raise ValueError(f"{field_name} is empty.")
try:
parsed = json.loads(text)
except json.JSONDecodeError as e:
raise ValueError(f"{field_name} must be valid JSON: {e}") from e
if not isinstance(parsed, dict):
raise ValueError(f"{field_name} must be a JSON object.")
return parsed
@staticmethod
def _parse_json_array(raw: str, field_name: str) -> List[Any]:
text = (raw or "").strip()
if not text:
raise ValueError(f"{field_name} is empty.")
try:
parsed = json.loads(text)
except json.JSONDecodeError as e:
raise ValueError(f"{field_name} must be valid JSON: {e}") from e
if not isinstance(parsed, list):
raise ValueError(f"{field_name} must be a JSON array.")
return parsed
@classmethod
def _build_element_from_slot(cls, kwargs: Dict[str, Any], index: int) -> Dict[str, Any]:
el_type = str(kwargs.get(f"element_type_{index}", "obj")).strip() or "obj"
if el_type not in ("obj", "text"):
raise ValueError(f"element_type_{index} must be obj or text.")
element: Dict[str, Any] = {"type": el_type}
desc = str(kwargs.get(f"element_desc_{index}") or "").strip()
if desc:
element["desc"] = desc
if el_type == "text":
text = str(kwargs.get(f"element_text_{index}") or "").strip()
if text:
element["text"] = text
bbox_raw = str(kwargs.get(f"element_bbox_{index}") or "").strip()
if bbox_raw:
try:
bbox = json.loads(bbox_raw)
except json.JSONDecodeError as e:
raise ValueError(f"element_bbox_{index} must be valid JSON: {e}") from e
if not isinstance(bbox, list):
raise ValueError(f"element_bbox_{index} must be a JSON array.")
element["bbox"] = bbox
palette_raw = str(kwargs.get(f"element_color_palette_{index}") or "").strip()
if palette_raw:
palette = [h.strip() for h in palette_raw.split(",") if h.strip()]
if palette:
element["color_palette"] = palette
if el_type == "text" and "text" not in element and not desc:
raise ValueError(
f"Element slot {index}: type text requires element_text_{index} or element_desc_{index}."
)
if el_type == "obj" and not desc:
raise ValueError(f"Element slot {index}: type obj requires element_desc_{index}.")
return element
@classmethod
def _collect_elements(cls, kwargs: Dict[str, Any]) -> List[Dict[str, Any]]:
if kwargs.get("useElementsJson", False):
return cls._parse_json_array(
kwargs.get("elementsJson", ""),
"elementsJson",
)
elements: List[Dict[str, Any]] = []
for i in range(1, cls._ELEMENT_SLOTS + 1):
if not kwargs.get(f"use_element_{i}", False):
continue
elements.append(cls._build_element_from_slot(kwargs, i))
return elements
def build_structured_prompt(self, **kwargs) -> Tuple[Dict[str, Any]]:
hld = str(kwargs.get("high_level_description") or "").strip()
if not hld:
raise ValueError("high_level_description is required.")
structured: Dict[str, Any] = {"high_level_description": hld}
if kwargs.get("useStyleDescription", False):
style_raw = kwargs.get("style_description", "")
structured["style_description"] = self._parse_json_object(
style_raw,
"style_description",
)
comp: Dict[str, Any] = {}
background = str(kwargs.get("background") or "").strip()
if background:
comp["background"] = background
elements = self._collect_elements(kwargs)
if elements:
comp["elements"] = elements
if comp:
structured["compositional_deconstruction"] = comp
return (structured,)
NODE_CLASS_MAPPINGS = {
"RunwareImageInferenceSettingsStructuredPrompt": RunwareImageInferenceSettingsStructuredPrompt,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareImageInferenceSettingsStructuredPrompt": "Runware Image Inference Settings Structured Prompt",
}
-101
View File
@@ -1,101 +0,0 @@
from .utils import runwareUtils as rwUtils
class imageMasking:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Image": ("IMAGE", {
"tooltip": "Specifies the input image to be processed for mask generation."
}),
"Detection Model": ([
"face_yolov8n", "face_yolov8s", "mediapipe_face_full", "mediapipe_face_short", "mediapipe_face_mesh",
"mediapipe_face_mesh_eyes_only", "eyes_mesh_mediapipe", "nose_mesh_mediapipe", "lips_mesh_mediapipe",
"eyes_lips_mesh", "nose_eyes_mesh", "nose_lips_mesh", "hand_yolov8n", "person_yolov8n-seg", "person_yolov8s-seg"
], {
"tooltip": "Specifies the specialized detection model to use for mask generation.",
"default": "face_yolov8n",
}),
"Confidence": ("FLOAT", {
"tooltip": "Confidence threshold for detections. Only detections with confidence scores above this threshold will be included in the mask.",
"default": 0.25,
"min": 0,
"max": 1,
"step": 0.01,
}),
"Max Detections": ("INT", {
"tooltip": "Limits the maximum number of elements (faces, hands, or people) that will be detected and masked in the image. If there are more elements than this value, only the ones with highest confidence scores will be included.",
"default": 6,
"min": 1,
"max": 20,
}),
"Mask Padding": ("INT", {
"tooltip": "Extends or reduces the detected mask area by the specified number of pixels. Positive values create a larger masked region (useful when you want to ensure complete coverage of the element), while negative values shrink the mask (useful for tighter, more focused areas).",
"default": 4,
"min": -40,
"max": 40,
}),
"Mask Blur": ("INT", {
"tooltip": "Extends the mask by the specified number of pixels with a gradual fade-out effect, creating smooth transitions between masked and unmasked regions in the final result.",
"default": 4,
"min": 0,
"max": 20,
}),
},
}
DESCRIPTION = "Image Masking provides intelligent detection and mask generation for specific elements in images, particularly optimized for faces, hands, and people. Built on advanced detection models, this feature enhances the inpainting workflow by automatically creating precise masks around detected elements, enabling targeted enhancement and detailing."
FUNCTION = "imageMasking"
RETURN_TYPES = ("IMAGE", "STRING", "STRING")
RETURN_NAMES = ("Image", "Mask Preview", "Mask")
CATEGORY = "Runware"
def imageMasking(self, **kwargs):
image = kwargs.get("Image")
detectionModel = kwargs.get("Detection Model")
confidence = kwargs.get("Confidence")
maxDetections = kwargs.get("Max Detections")
maskPadding = kwargs.get("Mask Padding")
maskBlur = kwargs.get("Mask Blur")
model_mapping = {
"face_yolov8n": "runware:35@1",
"face_yolov8s": "runware:35@2",
"mediapipe_face_full": "runware:35@6",
"mediapipe_face_short": "runware:35@7",
"mediapipe_face_mesh": "runware:35@8",
"mediapipe_face_mesh_eyes_only": "runware:35@9",
"eyes_mesh_mediapipe": "runware:35@15",
"nose_mesh_mediapipe": "runware:35@13",
"lips_mesh_mediapipe": "runware:35@14",
"eyes_lips_mesh": "runware:35@10",
"nose_eyes_mesh": "runware:35@11",
"nose_lips_mesh": "runware:35@12",
"hand_yolov8n": "runware:35@3",
"person_yolov8n-seg": "runware:35@4",
"person_yolov8s-seg": "runware:35@5"
}
detectionModel = model_mapping.get(detectionModel, detectionModel)
genConfig = [
{
"taskType": "imageMasking",
"taskUUID": rwUtils.genRandUUID(),
"inputImage": rwUtils.convertTensor2IMG(image),
"model": detectionModel,
"confidence": confidence,
"maxDetections": maxDetections,
"outputType": "URL",
"outputFormat": rwUtils.OUTPUT_FORMAT,
"outputQuality": rwUtils.OUTPUT_QUALITY,
}
]
genConfig[0]["maskPadding"] = maskPadding
genConfig[0]["maskBlur"] = maskBlur
genResult = rwUtils.inferenecRequest(genConfig)
image_url = rwUtils.extractImageURLs(genResult)
return (image, image_url, image_url)
@@ -1,80 +0,0 @@
from typing import Dict, Any, Tuple
class RunwareRegionalPromptingAdvancedFeature:
"""
Runware Regional Prompting Advanced Feature Node
Builds the advancedFeatures.regionalPrompting configuration object:
- injectSteps
- backgroundPrompt
- baseRatio
- regions (connected from the Regions node)
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"injectSteps": ("INT", {
"tooltip": "Steps to inject regional masks (1-100).",
"default": 15,
"min": 1,
"max": 100,
}),
"backgroundPrompt": ("STRING", {
"tooltip": "Background prompt applied to uncovered areas.",
"default": "",
}),
"baseRatio": ("FLOAT", {
"tooltip": "Base prompt attention weight (0.0 - 1.0).",
"default": 0.2,
"min": 0.0,
"max": 1.0,
"step": 0.01,
}),
"regions": ("RUNWAREREGIONALPROMPTINGREGIONS", {
"tooltip": "Connect Runware Regional Prompting Advanced Feature Regions node to define regional prompts and masks.",
}),
},
}
RETURN_TYPES: Tuple[str] = ("RUNWAREREGIONALPROMPTINGADVFEATURE",)
RETURN_NAMES = ("regionalPrompting",)
FUNCTION = "createRegionalPrompting"
CATEGORY = "Runware"
DESCRIPTION = (
"Configure regional prompting advanced feature (injectSteps, backgroundPrompt, "
"baseRatio, and regions). Connect output to Runware Image Advanced Feature Input "
"regionalPrompting input."
)
def createRegionalPrompting(self, regions=None, **kwargs) -> tuple:
"""Build regionalPrompting dict for advancedFeatures.regionalPrompting."""
inject_steps = int(kwargs.get("injectSteps", 15))
base_ratio = float(kwargs.get("baseRatio", 0.2))
background_prompt = (kwargs.get("backgroundPrompt", "") or "").strip()
config: Dict[str, Any] = {
"injectSteps": inject_steps,
"baseRatio": base_ratio,
}
if background_prompt:
config["backgroundPrompt"] = background_prompt
if isinstance(regions, list) and regions:
config["regions"] = regions
return (config,)
NODE_CLASS_MAPPINGS = {
"RunwareRegionalPromptingAdvancedFeature": RunwareRegionalPromptingAdvancedFeature,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareRegionalPromptingAdvancedFeature": "Runware Regional Prompting Advanced Feature",
}
@@ -1,196 +0,0 @@
from typing import Dict, Any, List, Tuple
class RunwareRegionalPromptingAdvancedFeatureRegions:
"""
Runware Regional Prompting Advanced Feature Regions Node
Builds the advancedFeatures.regionalPrompting.regions array, with up to 4 regions.
Each region has a prompt and a rectangular mask [x0, y0, x1, y1].
"""
@classmethod
def INPUT_TYPES(cls):
coords_tooltip = "Mask rectangle as [x0, y0, x1, y1] in image pixel coordinates."
return {
"required": {},
"optional": {
# Region 1
"useRegion1": ("BOOLEAN", {
"tooltip": "Enable first regional prompt mask.",
"default": False,
}),
"region1Prompt": ("STRING", {
"tooltip": "Prompt for region 1.",
"default": "",
}),
"region1_x0": ("INT", {
"tooltip": coords_tooltip,
"default": 0,
"min": 0,
"max": 8192,
}),
"region1_y0": ("INT", {
"tooltip": coords_tooltip,
"default": 0,
"min": 0,
"max": 8192,
}),
"region1_x1": ("INT", {
"tooltip": coords_tooltip,
"default": 512,
"min": 0,
"max": 8192,
}),
"region1_y1": ("INT", {
"tooltip": coords_tooltip,
"default": 512,
"min": 0,
"max": 8192,
}),
# Region 2
"useRegion2": ("BOOLEAN", {
"tooltip": "Enable second regional prompt mask.",
"default": False,
}),
"region2Prompt": ("STRING", {
"tooltip": "Prompt for region 2.",
"default": "",
}),
"region2_x0": ("INT", {
"tooltip": coords_tooltip,
"default": 512,
"min": 0,
"max": 8192,
}),
"region2_y0": ("INT", {
"tooltip": coords_tooltip,
"default": 0,
"min": 0,
"max": 8192,
}),
"region2_x1": ("INT", {
"tooltip": coords_tooltip,
"default": 1024,
"min": 0,
"max": 8192,
}),
"region2_y1": ("INT", {
"tooltip": coords_tooltip,
"default": 512,
"min": 0,
"max": 8192,
}),
# Region 3
"useRegion3": ("BOOLEAN", {
"tooltip": "Enable third regional prompt mask.",
"default": False,
}),
"region3Prompt": ("STRING", {
"tooltip": "Prompt for region 3.",
"default": "",
}),
"region3_x0": ("INT", {
"tooltip": coords_tooltip,
"default": 0,
"min": 0,
"max": 8192,
}),
"region3_y0": ("INT", {
"tooltip": coords_tooltip,
"default": 512,
"min": 0,
"max": 8192,
}),
"region3_x1": ("INT", {
"tooltip": coords_tooltip,
"default": 512,
"min": 0,
"max": 8192,
}),
"region3_y1": ("INT", {
"tooltip": coords_tooltip,
"default": 1024,
"min": 0,
"max": 8192,
}),
# Region 4
"useRegion4": ("BOOLEAN", {
"tooltip": "Enable fourth regional prompt mask.",
"default": False,
}),
"region4Prompt": ("STRING", {
"tooltip": "Prompt for region 4.",
"default": "",
}),
"region4_x0": ("INT", {
"tooltip": coords_tooltip,
"default": 512,
"min": 0,
"max": 8192,
}),
"region4_y0": ("INT", {
"tooltip": coords_tooltip,
"default": 512,
"min": 0,
"max": 8192,
}),
"region4_x1": ("INT", {
"tooltip": coords_tooltip,
"default": 1024,
"min": 0,
"max": 8192,
}),
"region4_y1": ("INT", {
"tooltip": coords_tooltip,
"default": 1024,
"min": 0,
"max": 8192,
}),
},
}
RETURN_TYPES: Tuple[str] = ("RUNWAREREGIONALPROMPTINGREGIONS",)
RETURN_NAMES = ("regions",)
FUNCTION = "createRegions"
CATEGORY = "Runware"
DESCRIPTION = (
"Configure regions array for regional prompting. Each enabled region provides "
"a prompt and a rectangular mask [x0, y0, x1, y1]. Connect to Runware Regional "
"Prompting Advanced Feature regions input."
)
def createRegions(self, **kwargs) -> tuple:
"""Build list of regional prompting regions."""
regions: List[Dict[str, Any]] = []
for idx in range(1, 5):
use_region = kwargs.get(f"useRegion{idx}", False)
prompt = (kwargs.get(f"region{idx}Prompt", "") or "").strip()
if not use_region or not prompt:
continue
x0 = int(kwargs.get(f"region{idx}_x0", 0))
y0 = int(kwargs.get(f"region{idx}_y0", 0))
x1 = int(kwargs.get(f"region{idx}_x1", 0))
y1 = int(kwargs.get(f"region{idx}_y1", 0))
mask = [x0, y0, x1, y1]
regions.append(
{
"prompt": prompt,
"mask": mask,
}
)
return (regions,)
NODE_CLASS_MAPPINGS = {
"RunwareRegionalPromptingAdvancedFeatureRegions": RunwareRegionalPromptingAdvancedFeatureRegions,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareRegionalPromptingAdvancedFeatureRegions": "Runware Regional Prompting Advanced Feature Regions",
}
-61
View File
@@ -1,61 +0,0 @@
"""
Runware Image Upscaler Settings — builds settings (enhanceDetails, realism). Connect to Runware Image Upscaler.
"""
from typing import Any, Dict, Tuple
class RunwareImageUpscalerSettings:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useEnhanceDetails": ("BOOLEAN", {
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
"tooltip": "Include settings.enhanceDetails in the upscale request.",
}),
"enhanceDetails": ("BOOLEAN", {
"default": False,
"label_on": "true",
"label_off": "false",
"tooltip": "Enhance fine textures and small details. May increase contrast and introduce minor deviations from the original.",
}),
"useRealism": ("BOOLEAN", {
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
"tooltip": "Include settings.realism in the upscale request.",
}),
"realism": ("BOOLEAN", {
"default": True,
"label_on": "true",
"label_off": "false",
"tooltip": "Improve realism. May deviate more from the original. Recommended for AI-generated images.",
}),
},
}
RETURN_TYPES = ("RUNWAREIMAGEUPSCALERSETTINGS",)
RETURN_NAMES = ("Upscaler Settings",)
FUNCTION = "build_settings"
CATEGORY = "Runware"
DESCRIPTION = (
"Optional settings.enhanceDetails and settings.realism for image upscale. "
"Connect to Runware Image Upscaler."
)
def build_settings(self, **kwargs) -> Tuple[Dict[str, Any]]:
out: Dict[str, Any] = {}
settings: Dict[str, Any] = {}
if kwargs.get("useEnhanceDetails", False):
settings["enhanceDetails"] = bool(kwargs.get("enhanceDetails", False))
if kwargs.get("useRealism", False):
settings["realism"] = bool(kwargs.get("realism", True))
if settings:
out["settings"] = settings
return (out,)
-162
View File
@@ -1,162 +0,0 @@
from typing import Dict, Any, Tuple
from .utils.runwareUtils import convertTensor2IMG
class RunwareWatermarkAdvancedFeature:
"""Runware Watermark Advanced Feature Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useText": ("BOOLEAN", {
"tooltip": "Enable to set text content for the watermark (2-32 characters).",
"default": False,
}),
"text": ("STRING", {
"tooltip": "Text content for the watermark (2-32 characters). Only used when 'Use Text' is enabled.",
"default": "",
}),
"useImage": ("BOOLEAN", {
"tooltip": "Enable to set image content for the watermark (connect an IMAGE, UUID, URL, data URI, or base64).",
"default": False,
}),
"image": ("IMAGE", {
"tooltip": "Image to use as watermark. Can be connected from a Load Image node; internally converted to a data URI/UUID when used.",
}),
"useDisplayPosition": ("BOOLEAN", {
"tooltip": "Enable to set watermark display position.",
"default": False,
}),
"displayPosition": ([
"top-left", "top-center", "top-right",
"center-left", "center-center", "center-right",
"bottom-left", "bottom-center", "bottom-right",
], {
"tooltip": "Watermark position on the image.",
"default": "bottom-right",
}),
"useTiled": ("BOOLEAN", {
"tooltip": "Enable to tile the watermark across the image at -45 degrees.",
"default": False,
}),
"tiled": ("BOOLEAN", {
"tooltip": "When enabled, distribute watermark across the image at -45 degrees. Only used when 'Use Tiled' is enabled.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
"useOpacity": ("BOOLEAN", {
"tooltip": "Enable to set watermark opacity (0.1 - 1.0).",
"default": False,
}),
"opacity": ("FLOAT", {
"tooltip": "Watermark opacity (0.1 - 1.0). Only used when 'Use Opacity' is enabled.",
"default": 0.6,
"min": 0.1,
"max": 1.0,
"step": 0.05,
}),
"useFontColor": ("BOOLEAN", {
"tooltip": "Enable to set watermark font color (hex).",
"default": False,
}),
"fontColor": ("STRING", {
"tooltip": "Text color in hex format (e.g. #ffffff). Only used when 'Use Font Color' is enabled.",
"default": "#ffffff",
}),
"useBgColor": ("BOOLEAN", {
"tooltip": "Enable to set watermark background color (hex).",
"default": False,
}),
"bgColor": ("STRING", {
"tooltip": "Background color in hex format (e.g. #000000 or transparent). Only used when 'Use Bg Color' is enabled.",
"default": "transparent",
}),
},
}
RETURN_TYPES: Tuple[str] = ("RUNWAREIMAGEWATERMARKADVFEATURE",)
RETURN_NAMES = ("watermark",)
FUNCTION = "createWatermark"
CATEGORY = "Runware"
DESCRIPTION = (
"Configure watermark advanced feature for Runware Image Inference. "
"You must provide exactly one of text or image. Connect output to "
"Runware Image Advanced Feature Input watermark connection."
)
def createWatermark(self, **kwargs) -> tuple:
"""Build watermark dict for advancedFeatures.watermark."""
use_text = kwargs.get("useText", False)
text = (kwargs.get("text", "") or "").strip()
use_image = kwargs.get("useImage", False)
raw_image = kwargs.get("image", None)
has_text = use_text and bool(text)
image_str: str = ""
if use_image:
if raw_image is None:
raise Exception(
"Watermark 'useImage' is enabled but no IMAGE input was provided. "
"Connect an IMAGE (e.g., from a Load Image node) to the watermark node."
)
# If an IMAGE tensor is connected, convert it to a data URI / UUID.
try:
# convertTensor2IMG handles caching and returns either UUID or data URI.
image_str = convertTensor2IMG(raw_image)
except Exception as e:
raise Exception(
f"Failed to convert watermark IMAGE input. "
f"Ensure the connected image is valid. Details: {e}"
) from e
has_image = use_image and bool(image_str)
if has_text == has_image:
raise Exception(
"Watermark configuration requires exactly one of text or image (but not both or neither)."
)
use_pos = kwargs.get("useDisplayPosition", False)
pos = kwargs.get("displayPosition", "bottom-right")
use_tiled = kwargs.get("useTiled", False)
tiled = bool(kwargs.get("tiled", False))
use_opacity = kwargs.get("useOpacity", False)
opacity = float(kwargs.get("opacity", 0.6))
use_font = kwargs.get("useFontColor", False)
font_color = (kwargs.get("fontColor", "#ffffff") or "").strip()
use_bg = kwargs.get("useBgColor", False)
bg_color = (kwargs.get("bgColor", "transparent") or "").strip()
watermark: Dict[str, Any] = {}
if has_text:
watermark["text"] = text
if has_image:
watermark["image"] = image_str
if use_pos:
watermark["displayPosition"] = pos
if use_tiled:
watermark["tiled"] = tiled
if use_opacity:
watermark["opacity"] = opacity
if use_font and font_color:
watermark["fontColor"] = font_color
if use_bg and bg_color:
watermark["bgColor"] = bg_color
return (watermark,)
NODE_CLASS_MAPPINGS = {
"RunwareWatermarkAdvancedFeature": RunwareWatermarkAdvancedFeature,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareWatermarkAdvancedFeature": "Runware Watermark Advanced Feature",
}
-44
View File
@@ -1,44 +0,0 @@
from typing import List
class inputAudios:
"""Input Audios node for configuring multiple audio inputs"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"Audio1": ("STRING", {
"tooltip": "MediaUUID for the first audio file from Runware Media Upload node.",
}),
"Audio2": ("STRING", {
"tooltip": "MediaUUID for the second audio file from Runware Media Upload node.",
}),
"Audio3": ("STRING", {
"tooltip": "MediaUUID for the third audio file from Runware Media Upload node.",
}),
"Audio4": ("STRING", {
"tooltip": "MediaUUID for the fourth audio file from Runware Media Upload node.",
}),
}
}
DESCRIPTION = "Configure multiple audio inputs (mediaUUIDs) for video generation with audio synchronization."
FUNCTION = "createInputAudios"
RETURN_TYPES = ("RUNWAREINPUTAUDIOS",)
RETURN_NAMES = ("Input Audios",)
CATEGORY = "Runware"
def createInputAudios(self, **kwargs) -> tuple[List[str]]:
"""Create list of audio UUIDs from provided parameters"""
audioList = []
for i in range(1, 5):
audioKey = f"Audio{i}"
audioUuid = kwargs.get(audioKey)
if audioUuid and audioUuid.strip() != "":
audioList.append(audioUuid.strip())
return (audioList,)
-54
View File
@@ -1,54 +0,0 @@
from .utils import runwareUtils as rwUtils
class ipAdapter:
RUNWARE_IPADAPTER_MODELS = {
"FLUX.1 Dev Redux": "runware:105@1",
"IP Adapter SDXL": "runware:55@1",
"IP Adapter SDXL Plus": "runware:55@2",
"IP Adapter SDXL Plus Face": "runware:55@3",
"IP Adapter SDXL Vit-H": "runware:55@4",
"IP Adapter SD 1.5": "runware:55@5",
"IP Adapter SD 1.5 Plus": "runware:55@6",
"IP Adapter SD 1.5 Light": "runware:55@7",
"IP Adapter SD 1.5 Plus Face": "runware:55@8",
"IP Adapter SD 1.5 Full Face": "runware:55@9",
"IP Adapter SD 1.5 Vit-G": "runware:55@10",
"Bria IP Adapter": "bria:10@21"
}
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Reference Image": ("IMAGE",),
"Model": (list(cls.RUNWARE_IPADAPTER_MODELS.keys()), {
"tooltip": "Choose IP Adapter model to use for reference-based image generation.",
"default": "IP Adapter SDXL",
}),
"weight": ("FLOAT", {
"default": 1.0,
"min": 0.0,
"max": 1.0,
"step": 0.01,
"tooltip": "Represents the strength or influence of this IP-Adapter in the generation process.\n\nA value of 0 means no influence, while 1 means maximum influence.\n\nNote: This Value Is Ignored For Flux Redux.",
}),
},
}
RETURN_TYPES = ("RUNWAREIPAdapter",)
RETURN_NAMES = ("IPAdapter",)
FUNCTION = "ipAdapter"
CATEGORY = "Runware"
DESCRIPTION = "IP-Adapters enable image-prompted generation, allowing you to use reference images to guide the style and content of your generations. Multiple IP Adapters can be used simultaneously."
def ipAdapter(self, **kwargs):
refImage = kwargs.get("Reference Image")
modelName = kwargs.get("Model")
weight = kwargs.get("weight")
modelAirCode = self.RUNWARE_IPADAPTER_MODELS.get(modelName)
guideImage = rwUtils.convertTensor2IMG(refImage)
return ({
"model": modelAirCode,
"guideImage": guideImage,
"weight": round(weight, 2),
},)
-37
View File
@@ -1,37 +0,0 @@
class ipAdapterCombine:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"IPAdapter 1": ("RUNWAREIPAdapter", {
"tooltip": "Connect an IPAdapter from Runware IPAdapter node.",
}),
},
"optional": {
"IPAdapter 2": ("RUNWAREIPAdapter", {
"tooltip": "Connect an IPAdapter from Runware IPAdapter node.",
}),
"IPAdapter 3": ("RUNWAREIPAdapter", {
"tooltip": "Connect an IPAdapter from Runware IPAdapter node.",
}),
},
}
DESCRIPTION = "Combine multiple IPAdapters to connect with Runware Image Inference."
FUNCTION = "ipAdapterCombine"
RETURN_TYPES = ("RUNWAREIPAdapter",)
RETURN_NAMES = ("IPAdapters",)
CATEGORY = "Runware"
def ipAdapterCombine(self, **kwargs):
ipAdapter1 = kwargs.get("IPAdapter 1")
ipAdapter2 = kwargs.get("IPAdapter 2", None)
ipAdapter3 = kwargs.get("IPAdapter 3", None)
ipAdapterArray = []
ipAdapterArray.append(ipAdapter1)
if(ipAdapter2):
ipAdapterArray.append(ipAdapter2)
if(ipAdapter3):
ipAdapterArray.append(ipAdapter3)
return (ipAdapterArray,)
-55
View File
@@ -1,55 +0,0 @@
import base64
import os
class RunwareLoadMesh:
"""Runware Load Mesh node for loading 3D model files (uses same logic as Load Image)"""
MESH_EXTENSIONS = {".glb", ".ply"}
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"file_path": ("STRING", {
"default": "",
"multiline": False,
"placeholder": "/path/to/model.glb or .ply",
"tooltip": "Full path to .glb or .ply file. Use this to load from any directory.",
}),
},
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("Base64",)
FUNCTION = "load_mesh"
CATEGORY = "Runware"
DESCRIPTION = "Load a 3D model file from a path or entering a path. Base64 encodes and outputs as data URI. Connect to Runware 3D Inference Inputs meshFile."
def load_mesh(self, file_path):
path = (file_path or "").strip() if isinstance(file_path, str) else ""
if not path:
return ("",)
full_path = os.path.expanduser(path)
ext = os.path.splitext(full_path)[1].lower()
if ext not in self.MESH_EXTENSIONS:
raise ValueError(
f"Runware Load Mesh: unsupported extension '{ext}'. Expected .glb or .ply."
)
if not os.path.isfile(full_path):
raise FileNotFoundError(
f"Runware Load Mesh: file not found: {full_path}"
)
with open(full_path, 'rb') as f:
mesh_bytes = f.read()
mesh_base64 = base64.b64encode(mesh_bytes).decode('utf-8')
return (f"data:application/octet-stream;base64,{mesh_base64}",)
NODE_CLASS_MAPPINGS = {
"RunwareLoadMesh": RunwareLoadMesh,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareLoadMesh": "Runware Load Mesh",
}
-37
View File
@@ -1,37 +0,0 @@
class loraCombine:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Lora 1": ("RUNWARELORA", {
"tooltip": "Connect a Runware Lora From Lora Search Node.",
}),
},
"optional": {
"Lora 2": ("RUNWARELORA", {
"tooltip": "Connect a Runware Lora From Lora Search Node.",
}),
"Lora 3": ("RUNWARELORA", {
"tooltip": "Connect a Runware Lora From Lora Search Node.",
}),
},
}
DESCRIPTION = "Combine One or More Lora's To Connect It With Runware Image Inference."
FUNCTION = "loraCombine"
RETURN_TYPES = ("RUNWARELORA",)
RETURN_NAMES = ("Runware Lora's",)
CATEGORY = "Runware"
def loraCombine(self, **kwargs):
runwareLora1 = kwargs.get("Lora 1")
runwareLora2 = kwargs.get("Lora 2", None)
runwareLora3 = kwargs.get("Lora 3", None)
loraObjectArray = []
loraObjectArray.append(runwareLora1)
if(runwareLora2):
loraObjectArray.append(runwareLora2)
if(runwareLora3):
loraObjectArray.append(runwareLora3)
return (loraObjectArray,)
-87
View File
@@ -1,87 +0,0 @@
class loraSearch:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Lora Search": ("STRING", {
"tooltip": "Search For Specific Lora By Name Or Civit AIR Code (eg: Cyberpunk).",
}),
"Model Architecture": ([
"All",
"FLUX.1-Schnell",
"FLUX.1-Dev",
"FLUX.1 Krea",
"Pony",
"SD 1.5",
"SD 1.5 Hyper",
"SD 1.5 LCM",
"SD 3",
"SDXL 1.0",
"SDXL 1.0 LCM",
"SDXL Distilled",
"SDXL Hyper",
"SDXL Lightning",
"SDXL Turbo",
], {
"tooltip": "Choose Lora Model Architecture To Filter Results.",
"default": "All",
}),
"LoraType": ([
"Lora",
"LyCORIS"
], {
"tooltip": "Choose Lora Type To Filter Out The Results.",
"default": "Lora",
}),
"LoraList": ([
"civitai:58390@62833 (Detail Tweaker LoRA (细节调整LoRA))",
"civitai:82098@87153 (Add More Details - Detail Enhancer / Tweaker (细节调整) LoRA)",
"civitai:122359@135867 (Detail Tweaker XL)",
"civitai:14171@16677 (Cute_girl_mix4)",
"civitai:13941@16576 (epi_noiseoffset)",
"civitai:25995@32988 (blindbox/大概是盲盒)",
], {
"tooltip": "Lora Results Will Show UP Here So You Could Choose From.",
"default": "civitai:58390@62833 (Detail Tweaker LoRA (细节调整LoRA))",
}),
"weight": ("FLOAT", {
"tooltip": "Defines the strength or influence of the LoRA model in the generation process.",
"default": 1.0,
"min": -4.0,
"max": 4.0,
"step": 0.1,
}),
"Use Search Value": ("BOOLEAN", {
"tooltip": "When Enabled, the value you've set in the search input will be used instead.\n\nThis is useful in case the model search API is down or you prefer to set the model manually.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
},
}
DESCRIPTION = "Directly Search and Connect Lora's to Runware Inference Nodes In ComfyUI."
FUNCTION = "loraSearch"
RETURN_TYPES = ("RUNWARELORA",)
RETURN_NAMES = ("Runware Lora",)
CATEGORY = "Runware"
@classmethod
def VALIDATE_INPUTS(cls, LoraList):
return True
def loraSearch(self, **kwargs):
enableSearchValue = kwargs.get("Use Search Value", False)
searchInput = kwargs.get("Lora Search")
lora_weight = kwargs.get("weight")
if enableSearchValue:
modelAIRCode = searchInput
else:
crModel = kwargs.get("LoraList")
modelAIRCode = crModel.split(" ")[0]
return ({
"model": modelAIRCode,
"weight": round(lora_weight, 2),
},)
-296
View File
@@ -1,296 +0,0 @@
import base64
import os
import time
import torch
import soundfile as sf
import numpy as np
from io import BytesIO
from .utils.runwareUtils import (
RUNWARE_API_BASE_URL,
genRandUUID,
inferenecRequest,
pollVideoResult,
sanitize_for_logging,
safe_json_dumps,
sendMediaUUID,
)
class RunwareMediaUpload:
"""Runware Media Upload node for uploading audio/video to Runware storage"""
# MIME type mapping for video files
VIDEO_MIME_TYPES = {
'.mp4': 'video/mp4',
'.avi': 'video/avi',
'.mov': 'video/quicktime',
'.webm': 'video/webm',
}
DEFAULT_SAMPLE_RATE = 22050
DEFAULT_VIDEO_MIME = 'video/mp4'
MAX_POLL_ATTEMPTS = 100
POLL_INTERVAL = 2
REQUEST_TIMEOUT = 30
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"media": ("VIDEO,AUDIO", {
"tooltip": "Media tensor from ComfyUI load video or audio node",
}),
"mediaUUID": ("STRING", {
"placeholder": "Generated media UUID will appear here automatically.",
"tooltip": "This field will be automatically populated with the generated media UUID after upload.",
}),
},
"hidden": {"node_id": "UNIQUE_ID"}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("mediaUUID",)
FUNCTION = "uploadMedia"
CATEGORY = "Runware"
DESCRIPTION = "Upload audio media to Runware's media storage and get mediaUUID"
OUTPUT_NODE = True
@classmethod
def IS_CHANGED(cls, **kwargs):
return float("NAN")
def uploadMedia(self, media=None, mediaUuid=None, **kwargs):
"""Main function to upload media and return mediaUUID"""
if media is None:
raise ValueError("Media input is required")
print(f"[Debug] uploadMedia called with media: {type(media)}, mediaUuid: {mediaUuid}")
try:
mediaBase64 = self._convertMediaToBase64(media)
print(f"[Debug] Media converted to base64, length: {len(mediaBase64)}")
mediaUuid = self._uploadToRunware(mediaBase64)
print(f"[Debug] Upload completed! MediaUUID: {mediaUuid}")
print(f"[Debug] ===== RESULT: {mediaUuid} =====")
sendMediaUUID(mediaUuid, kwargs.get("node_id"))
return (mediaUuid,)
except Exception as e:
print(f"[Debug] Media upload failed: {str(e)}")
raise e
def _convertMediaToBase64(self, mediaTensor):
"""Convert media tensor to base64 data URI"""
print(f"[Debug] Media type: {type(mediaTensor)}")
if self._isVideoFile(mediaTensor):
return self._convertVideoToBase64(mediaTensor)
elif isinstance(mediaTensor, dict) and 'waveform' in mediaTensor:
return self._convertAudioDictToBase64(mediaTensor)
elif isinstance(mediaTensor, torch.Tensor):
return self._convertTensorToBase64(mediaTensor)
else:
raise ValueError(f"Unsupported media type: {type(mediaTensor)}")
def _isVideoFile(self, mediaTensor):
"""Check if media tensor is a VideoFromFile object"""
return (hasattr(mediaTensor, 'video_path') or
str(type(mediaTensor)).find('VideoFromFile') != -1)
def _convertVideoToBase64(self, videoTensor):
"""Convert video file to base64 data URI"""
print(f"[Debug] VideoFromFile object detected")
videoFile = self._getVideoFilePath(videoTensor)
videoPath = self._extractVideoPath(videoFile)
print(f"[Debug] Processing video file: {videoPath}")
with open(videoPath, 'rb') as f:
videoBytes = f.read()
mimeType = self._getMimeType(videoPath)
videoBase64 = base64.b64encode(videoBytes).decode('utf-8')
return f"data:{mimeType};base64,{videoBase64}"
def _getVideoFilePath(self, videoTensor):
"""Extract video file path from VideoFromFile object"""
videoFile = getattr(videoTensor, '_VideoFromFile__file', None)
if videoFile is None:
for attr in ['video_path', 'path', 'file_path', 'filename', '_file']:
if hasattr(videoTensor, attr):
videoFile = getattr(videoTensor, attr)
print(f"[Debug] Found video file via {attr}: {videoFile}")
break
if videoFile is None:
raise ValueError("Could not find video file in VideoFromFile object")
return videoFile
def _extractVideoPath(self, videoFile):
"""Extract file path from file object or string"""
if hasattr(videoFile, 'name'):
return videoFile.name
elif isinstance(videoFile, str):
return videoFile
else:
raise ValueError(f"Unexpected video file type: {type(videoFile)}")
def _getMimeType(self, filePath):
"""Get MIME type based on file extension"""
ext = os.path.splitext(filePath.lower())[1]
return self.VIDEO_MIME_TYPES.get(ext, self.DEFAULT_VIDEO_MIME)
def _convertAudioDictToBase64(self, audioDict):
"""Convert audio dictionary to base64 WAV data URI"""
waveform = audioDict['waveform']
sampleRate = audioDict.get('sample_rate', self.DEFAULT_SAMPLE_RATE)
waveformNp = self._prepareWaveformNumpy(waveform)
wavBytes = self._convertWaveformToWav(waveformNp, sampleRate)
wavBase64 = base64.b64encode(wavBytes).decode('utf-8')
return f"data:audio/wav;base64,{wavBase64}"
def _convertTensorToBase64(self, mediaTensor):
"""Convert direct tensor input to base64 WAV data URI"""
waveformNp = self._prepareWaveformNumpy(mediaTensor)
wavBytes = self._convertWaveformToWav(waveformNp, self.DEFAULT_SAMPLE_RATE)
wavBase64 = base64.b64encode(wavBytes).decode('utf-8')
return f"data:audio/wav;base64,{wavBase64}"
def _prepareWaveformNumpy(self, waveform):
"""Prepare waveform tensor/array for soundfile (returns [channels, samples])"""
if isinstance(waveform, torch.Tensor):
# Normalize dimensions: [batch, channels, samples] -> [channels, samples]
if waveform.dim() == 3:
waveform = waveform.squeeze(0)
elif waveform.dim() == 1:
waveform = waveform.unsqueeze(0)
waveformNp = waveform.cpu().numpy()
else:
waveformNp = np.array(waveform)
if waveformNp.ndim == 1:
waveformNp = waveformNp.reshape(1, -1)
return waveformNp
def _convertWaveformToWav(self, waveformNp, sampleRate):
"""Convert numpy waveform to WAV bytes using soundfile"""
# soundfile expects [samples, channels]
waveformNp = waveformNp.T
buffer = BytesIO()
sf.write(buffer, waveformNp, sampleRate, format='WAV')
buffer.seek(0)
return buffer.getvalue()
def _uploadToRunware(self, mediaDataUri):
"""Upload media to Runware and return mediaUUID"""
taskUuid = genRandUUID()
print(f"[Debug] Generated task UUID: {taskUuid}")
mediaData = self._stripDataUriPrefix(mediaDataUri)
uploadConfig = self._buildUploadConfig(taskUuid, mediaData)
print(f"[Debug] Upload config: {sanitize_for_logging(uploadConfig[0])}")
print(f"[Debug] API URL: {RUNWARE_API_BASE_URL}")
uploadResult = self._makeUploadRequest(uploadConfig)
self._validateUploadResponse(uploadResult)
uploadData = uploadResult.get("data", [])
if uploadData and len(uploadData) > 0:
mediaUuid = uploadData[0].get("mediaUUID", "")
if mediaUuid and mediaUuid != "1":
print(f"[Debug] Got immediate mediaUUID: {mediaUuid}")
return mediaUuid
print(f"[Debug] Starting polling for result...")
return self._pollForResult(taskUuid)
def _stripDataUriPrefix(self, mediaDataUri):
"""Strip data URI prefix if present"""
if mediaDataUri.startswith("data:"):
mediaData = mediaDataUri.split(",", 1)[1]
print(f"[Debug] Stripped data URI prefix, media data length: {len(mediaData)}")
return mediaData
return mediaDataUri
def _buildUploadConfig(self, taskUuid, mediaData):
"""Build upload configuration for API request"""
return [{
"taskType": "mediaStorage",
"taskUUID": taskUuid,
"operation": "upload",
"media": mediaData,
}]
def _makeUploadRequest(self, uploadConfig):
"""Make upload request to Runware API"""
print(f"[Debug] Making upload request to Runware API...")
return inferenecRequest(uploadConfig)
def _validateUploadResponse(self, uploadResult):
"""Validate upload response"""
print(f"[Debug] Upload response: {safe_json_dumps(uploadResult, indent=2) if isinstance(uploadResult, (dict, list)) else uploadResult}")
if "errors" in uploadResult:
print(f"[Debug] Upload error: {safe_json_dumps(uploadResult, indent=2) if isinstance(uploadResult, (dict, list)) else uploadResult}")
raise Exception(f"Upload failed: {uploadResult}")
def _pollForResult(self, taskUuid):
"""Poll for media upload result"""
for attempt in range(self.MAX_POLL_ATTEMPTS):
try:
print(f"[Debug] Poll attempt {attempt + 1}/{self.MAX_POLL_ATTEMPTS}")
pollResult = pollVideoResult(taskUuid)
pollData = self._parsePollResponse(pollResult)
if pollData:
mediaUuid = pollData.get("mediaUUID", "")
if mediaUuid == "1":
print(f"[Debug] Still processing... attempt {attempt + 1}")
time.sleep(self.POLL_INTERVAL)
continue
elif mediaUuid and mediaUuid != "1":
print(f"[Debug] Upload completed! MediaUUID: {mediaUuid}")
return mediaUuid
print(f"[Debug] Attempt {attempt + 1}: No data in response, continuing...")
time.sleep(self.POLL_INTERVAL)
except Exception as e:
print(f"[Debug] Poll attempt {attempt + 1} failed: {str(e)}")
time.sleep(self.POLL_INTERVAL)
continue
raise Exception("Polling timeout - upload did not complete")
def _parsePollResponse(self, pollResult):
"""Parse polling response"""
if pollResult is None:
print(f"[Debug] Poll returned empty response")
return None
print(f"[Debug] Poll response: {safe_json_dumps(pollResult, indent=2) if isinstance(pollResult, (dict, list)) else pollResult}")
if "errors" in pollResult:
print(f"[Debug] Poll error: {safe_json_dumps(pollResult, indent=2) if isinstance(pollResult, (dict, list)) else pollResult}")
return None
if "data" in pollResult and len(pollResult["data"]) > 0:
print(f"[Debug] Poll data: {sanitize_for_logging(pollResult['data'][0])}")
return pollResult["data"][0]
return None
runwareMediaUpload = RunwareMediaUpload
-89
View File
@@ -1,89 +0,0 @@
class modelSearch:
"""Model Search node for searching and selecting image models"""
MODEL_ARCHITECTURES = [
"All",
"FLUX.1-Schnell",
"FLUX.1-Dev",
"FLUX.1-Krea",
"Pony",
"SD 1.5",
"SD 1.5 Hyper",
"SD 1.5 LCM",
"SD 3",
"SDXL 1.0",
"SDXL 1.0 LCM",
"SDXL Distilled",
"SDXL Hyper",
"SDXL Lightning",
"SDXL Turbo",
]
MODEL_TYPES = [
"Base Model",
"Inpainting Model",
]
FEATURED_MODELS = [
"rundiffusion:110@101 (Juggernaut Lightning Flux by RunDiffusion)",
"rundiffusion:130@100 (Juggernaut Pro Flux by RunDiffusion)",
"runware:100@1 (Flux Schnell)",
"runware:101@1 (Flux Dev)",
"runware:107@1 (FLUX.1 Krea)",
"runware:5@1 (SD3)",
"civitai:4384@128713 (SDXL 1.5 DreamShaper)",
"civitai:43331@176425 (SD 1.5 majicMIX realistic 麦橘写实)",
"civitai:101055@128078 (SDXL v1.0 VAE fix)",
"civitai:133005@288982 (SDXL Juggernaut XL V8)",
]
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Model Search": ("STRING", {
"tooltip": "Search For Specific Model By Name Or Civit AIR Code (eg: Juggernaut).",
}),
"Model Architecture": (cls.MODEL_ARCHITECTURES, {
"tooltip": "Choose Model Architecture To Filter Results.",
"default": "All",
}),
"ModelType": (cls.MODEL_TYPES, {
"tooltip": "Choose Model Type To Filter Results.",
"default": "Base Model",
}),
"ModelList": (cls.FEATURED_MODELS, {
"tooltip": "Model Results Will Show UP Here So You Could Choose From. If You didn't Search For Anything this will show featured Model List.",
"default": "runware:100@1 (Flux Schnell)",
}),
"Use Search Value": ("BOOLEAN", {
"tooltip": "When Enabled, the value you've set in the search input will be used instead.\n\nThis is useful in case the model search API is down or you prefer to set the model manually.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
},
}
DESCRIPTION = "Directly Search and Connect Model Checkpoints to Runware Inference Nodes In ComfyUI."
FUNCTION = "modelSearch"
RETURN_TYPES = ("RUNWAREMODEL",)
RETURN_NAMES = ("Runware Model",)
CATEGORY = "Runware"
@classmethod
def VALIDATE_INPUTS(cls, ModelList):
return True
def modelSearch(self, **kwargs):
"""Search and return model AIR code"""
enableSearchValue = kwargs.get("Use Search Value", False)
searchInput = kwargs.get("Model Search")
if enableSearchValue:
modelAirCode = searchInput
else:
currentModel = kwargs.get("ModelList")
modelAirCode = currentModel.split(" ")[0]
return (modelAirCode,)
-109
View File
@@ -1,109 +0,0 @@
import asyncio
from .utils import runwareUtils as rwUtils
from comfy_execution.graph import ExecutionBlocker
class multiInference:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Task 1": (
"RUNWARETASK",
{
"tooltip": "Connect a Runware Task From Any Inference Node.",
},
),
},
"optional": {
"Task 2": (
"RUNWARETASK",
{
"tooltip": "Connect a Runware Task From Any Inference Node.",
},
),
"Task 3": (
"RUNWARETASK",
{
"tooltip": "Connect a Runware Task From Any Inference Node.",
},
),
"Task 4": (
"RUNWARETASK",
{
"tooltip": "Connect a Runware Task From Any Inference Node.",
},
),
},
}
DESCRIPTION = "Allows you to Run Multiple Inference Tasks in Parallel."
FUNCTION = "multiInference"
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING")
RETURN_NAMES = ("Result 1", "Result 2", "Result 3", "Result 4")
CATEGORY = "Runware"
def multiInference(self, **kwargs):
tasksData = [kwargs.get(f"Task {i}", None) for i in range(1, 5)]
validTasks = [task for task in tasksData if task is not None]
if not validTasks:
raise Exception("Error: No valid tasks provided for Multi Inference!")
taskIncidents = {}
async def runInference(taskData, taskIndex, oindex):
try:
print(f"[Debugging] Task {oindex + 1} UUID => {taskData[0]['taskUUID']}")
loop = asyncio.get_running_loop()
result = await loop.run_in_executor(
None, rwUtils.inferenecRequest, taskData
)
image_url = rwUtils.extractImageURLs(result)
return image_url
except Exception as e:
print(f"\n---- Runware Multi Inference Task {oindex + 1} Failed ----")
print(f"Task-Type: {taskData[0]['taskType']}")
print(f"Task-UUID: {taskData[0]['taskUUID']}")
print(f"{e}")
print(f"-------------------\n")
taskIncidents[taskIndex] = e
return ExecutionBlocker(None)
async def main():
coroutines = []
valid_indices = [idx for idx, t in enumerate(tasksData) if t is not None]
for i, config in enumerate(validTasks):
original_index = valid_indices[i]
coroutines.append(runInference(config, i, original_index))
results = await asyncio.gather(*coroutines, return_exceptions=False)
return results
try:
loop = asyncio.get_event_loop()
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
finalResults = loop.run_until_complete(main())
optResult = [ExecutionBlocker(None)] * 4
rindex = 0
workingTasks = 0
for i, task_config in enumerate(tasksData):
if task_config is not None:
result = finalResults[rindex]
if result is not None and not isinstance(result, ExecutionBlocker):
workingTasks += 1
optResult[i] = result
rindex += 1
if workingTasks == 0 and len(validTasks) > 0:
firstError = min(taskIncidents.keys())
raise Exception(
f"Error in Task {firstError + 1} => {taskIncidents[firstError]}"
)
return tuple(optResult)
-99
View File
@@ -1,99 +0,0 @@
class outpaintSettings:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"useTop": ("BOOLEAN", {
"tooltip": "Enable to include top extension in outpaint settings.",
"default": False,
}),
"Top": (
"INT",
{
"tooltip": "Number of pixels to extend at the top of the image. Only used when 'Use Top' is enabled.",
"default": 64,
"min": 0,
"max": 2048,
"step": 1,
},
),
"useRight": ("BOOLEAN", {
"tooltip": "Enable to include right extension in outpaint settings.",
"default": False,
}),
"Right": (
"INT",
{
"tooltip": "Number of pixels to extend at the right of the image. Only used when 'Use Right' is enabled.",
"default": 64,
"min": 0,
"max": 2048,
"step": 1,
},
),
"useBottom": ("BOOLEAN", {
"tooltip": "Enable to include bottom extension in outpaint settings.",
"default": False,
}),
"Bottom": (
"INT",
{
"tooltip": "Number of pixels to extend at the bottom of the image. Only used when 'Use Bottom' is enabled.",
"default": 64,
"min": 0,
"max": 2048,
"step": 1,
},
),
"useLeft": ("BOOLEAN", {
"tooltip": "Enable to include left extension in outpaint settings.",
"default": False,
}),
"Left": (
"INT",
{
"tooltip": "Number of pixels to extend at the left of the image. Only used when 'Use Left' is enabled.",
"default": 64,
"min": 0,
"max": 2048,
"step": 1,
},
),
"useBlur": ("BOOLEAN", {
"tooltip": "Enable to include blur in outpaint settings.",
"default": False,
}),
"Blur": (
"INT",
{
"tooltip": "The amount of blur to apply at the boundaries between the original image and the extended areas, measured in pixels. Only used when 'Use Blur' is enabled.",
"default": 0,
"min": 0,
"max": 32,
"step": 1,
},
),
},
}
DESCRIPTION = "Can Be Added To Image Inference Nodes To Extend the image boundaries in specified directions. \n\nYou must provide the final dimensions using width and height parameters, which should account for the original image size plus the total extension (seedImage dimensions + top + bottom, left + right)."
FUNCTION = "outpaintSettings"
RETURN_TYPES = ("RUNWAREOUTPAINT",)
RETURN_NAMES = ("Outpaint Settings",)
CATEGORY = "Runware"
def outpaintSettings(self, **kwargs):
outpaint = {}
if kwargs.get("useTop", False):
outpaint["top"] = int(kwargs.get("Top", 0))
if kwargs.get("useRight", False):
outpaint["right"] = int(kwargs.get("Right", 0))
if kwargs.get("useBottom", False):
outpaint["bottom"] = int(kwargs.get("Bottom", 0))
if kwargs.get("useLeft", False):
outpaint["left"] = int(kwargs.get("Left", 0))
if kwargs.get("useBlur", False):
outpaint["blur"] = int(kwargs.get("Blur", 0))
return (outpaint if outpaint else None,)
-191
View File
@@ -1,191 +0,0 @@
from .utils import runwareUtils as rwUtils
class photoMaker:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Model": ("RUNWAREMODEL", {
"tooltip": "Connect a Runware Model From Runware Model Node.",
}),
"Image 1": ("IMAGE", {
"tooltip": "Specifies Input Image 1 that will be used as reference for the subject. These reference images help the AI to maintain identity consistency during the generation process."
}),
"positivePrompt": ("STRING", {
"multiline": True,
"placeholder": "Positive Prompt: a text instruction to guide the model on generating the image. It is usually a sentence or a paragraph that provides positive guidance for the task. This parameter is essential to shape the desired results.\n\nThe PhotoMaker positive prompt must follow a specific format: the class word (like \"man\", \"woman\", \"girl\") must be followed by the trigger word \"img\".\nExample: man img, wearing a suit",
"tooltip": "Positive Prompt: a text instruction to guide the model on generating the image.\nDo not Forget To Add trigger word \"img\" after the class word (like \"man\", \"woman\", \"girl\")",
}),
"negativePrompt": ("STRING", {
"multiline": True,
"placeholder": "Negative Prompt: a text instruction to guide the model on generating the image. It is usually a sentence or a paragraph that provides negative guidance for the task. This parameter helps to avoid certain undesired results.",
"tooltip": "Negative Prompt: a text instruction to guide the model on generating the image."
}),
"Prompt Weighting": (["Disabled", "Compel"], {
"default": "Disabled",
"tooltip": "Prompt weighting allows you to adjust how strongly different parts of your prompt influence the generated image.",
}),
"style": ([
"No style", "Cinematic", "Disney Character", "Digital Art", "Photographic", "Fantasy art",
"Neonpunk", "Enhance", "Comic book", "Lowpoly", "Line art"
], {
"default": "No style",
"tooltip": "Specifies the artistic style to be applied to the generated images."
}),
"dimensions": ([
"Square (512x512)", "Square HD (1024x1024)", "Portrait 3:4 (768x1024)",
"Portrait 9:16 (576x1024)", "Landscape 4:3 (1024x768)",
"Landscape 16:9 (1024x576)", "Custom"
], {
"default": "Square HD (1024x1024)",
"tooltip": "Adjust the dimensions of the generated image by specifying its width and height in pixels, or select from the predefined options. Image dimensions must be multiples of 64 (e.g., 512x512, 1024x768).",
}),
"width": ("INT", {
"tooltip": "The Width of the image in pixels.",
"default": 1024,
"min": 512,
"max": 2048,
"step": 64,
}),
"height": ("INT", {
"tooltip": "The Height of the image in pixels.",
"default": 1024,
"min": 512,
"max": 2048,
"step": 64,
}),
"steps": ("INT", {
"tooltip": "The number of steps is the number of iterations the model will perform to generate the image. The higher the number of steps, the more detailed the image will be. However, increasing the number of steps will also increase the time it takes to generate the image and may not always result in a better image.",
"default": 25,
"min": 1,
"max": 100,
}),
"scheduler": (['Default', 'DDIM', 'DDIMScheduler', 'DDPMScheduler', 'DEISMultistepScheduler', 'DPMSolverSinglestepScheduler', 'DPMSolverMultistepScheduler', 'DPMSolverMultistepInverse', 'DPM++', 'DPM++ Karras', 'DPM++ 2M', 'DPM++ 2M Karras', 'DPM++ 2M SDE Karras', 'DPM++ 2M SDE', 'DPM++ 3M', 'DPM++ 3M Karras', 'DPM++ SDE Karras', 'DPM++ SDE', 'EDMEulerScheduler', 'EDMDPMSolverMultistepScheduler', 'Euler', 'EulerDiscreteScheduler', 'Euler Karras', 'Euler a', 'EulerAncestralDiscreteScheduler', 'FlowMatchEulerDiscreteScheduler', 'Heun', 'HeunDiscreteScheduler', 'Heun Karras', 'IPNDMScheduler', 'KDPM2DiscreteScheduler', 'KDPM2AncestralDiscreteScheduler', 'LCM', 'LCMScheduler', 'LMS', 'LMSDiscreteScheduler', 'LMS Karras', 'PNDMScheduler', 'TCDScheduler', 'UniPC', 'UniPCMultistepScheduler', 'UniPC Karras', 'UniPC 2M', 'UniPC 2M Karras', 'UniPC 3M', 'UniPC 3M Karras'], {
"tooltip": "An scheduler is a component that manages the inference process. Different schedulers can be used to achieve different results like more detailed images, faster inference, or more accurate results.",
"default": "Default",
}),
"cfgScale": ("FLOAT", {
"tooltip": "Guidance scale represents how closely the images will resemble the prompt or how much freedom the AI model has. Higher values are closer to the prompt. Low values may reduce the quality of the results.",
"default": 6.5,
"min": 1.0,
"max": 30.0,
"step": 0.5,
}),
"strength": ("INT", {
"default": 15,
"min": 15,
"max": 50,
"tooltip": "Controls the balance between preserving the subject's original features and the creative transformation specified in the prompt.\n- Lower values provide stronger subject fidelity.\n- Higher values allow more creative freedom in the transformation."
}),
"clipSkip": ("INT", {
"tooltip": "Enables skipping layers of the CLIP embedding process, leading to quicker and more varied image generation.",
"default": 0,
"min": 0,
"max": 2,
}),
"seed": ("INT", {
"tooltip": "A value used to randomize the image generation. If you want to make images reproducible (generate the same image multiple times), you can use the same seed value. Set to 0 to auto-generate a random seed.",
"default": 0,
"min": 0,
"max": 4294967295,
}),
"batchSize": ("INT", {
"tooltip": "The number of images to generate in a single request.",
"default": 1,
"min": 1,
"max": 10,
}),
},
"optional": {
"Image 2": ("IMAGE", {
"tooltip": "Specifies Input Image 2 that will be used as reference for the subject. These reference images help the AI to maintain identity consistency during the generation process."
}),
"Image 3": ("IMAGE", {
"tooltip": "Specifies Input Image 3 that will be used as reference for the subject. These reference images help the AI to maintain identity consistency during the generation process."
}),
"Image 4": ("IMAGE", {
"tooltip": "Specifies Input Image 4 that will be used as reference for the subject. These reference images help the AI to maintain identity consistency during the generation process."
}),
}
}
DESCRIPTION = "Transform and style images using PhotoMaker's advanced personalization technology. Create consistent, high-quality image variations with precise subject fidelity and style control."
FUNCTION = "photoMaker"
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("IMAGE",)
CATEGORY = "Runware"
@classmethod
def VALIDATE_INPUTS(cls, positivePrompt, negativePrompt):
if (positivePrompt is not None and (positivePrompt == "" or len(positivePrompt) < 3 or len(positivePrompt) > 2000)):
raise Exception(
"Positive Prompt Must Be Between 3 And 2000 characters!")
if (negativePrompt is not None and negativePrompt != "" and (len(negativePrompt) < 3 or len(negativePrompt) > 2000)):
raise Exception(
"Negative Prompt Must Be Between 3 And 2000 characters!")
return True
def photoMaker(self, **kwargs):
runwareModel = kwargs.get("Model")
image1 = kwargs.get("Image 1")
image2 = kwargs.get("Image 2", None)
image3 = kwargs.get("Image 3", None)
image4 = kwargs.get("Image 4", None)
positivePrompt = kwargs.get("positivePrompt")
negativePrompt = kwargs.get("negativePrompt", None)
promptWeighting = kwargs.get("Prompt Weighting", "Disabled")
style = kwargs.get("style", "No style")
dimensions = kwargs.get("dimensions", "Square HD (1024x1024)")
width = kwargs.get("width", 1024)
height = kwargs.get("height", 1024)
steps = kwargs.get("steps", 25)
scheduler = kwargs.get("scheduler", "Default")
cfgScale = kwargs.get("cfgScale", 6.5)
strength = kwargs.get("strength", 15)
clipSkip = kwargs.get("clipSkip", 0)
seed = kwargs.get("seed")
batchSize = kwargs.get("batchSize", 1)
imageList = [rwUtils.convertTensor2IMG(image1)]
if (image2 is not None):
imageList.append(rwUtils.convertTensor2IMG(image2))
if (image3 is not None):
imageList.append(rwUtils.convertTensor2IMG(image3))
if (image4 is not None):
imageList.append(rwUtils.convertTensor2IMG(image4))
genConfig = [
{
"taskType": "photoMaker",
"taskUUID": rwUtils.genRandUUID(),
"model": runwareModel,
"inputImages": imageList,
"positivePrompt": positivePrompt,
"style": style,
"strength": strength,
"height": height,
"width": width,
"steps": steps,
"scheduler": scheduler,
"seed": seed,
"CFGScale": cfgScale,
"clipSkip": clipSkip,
"numberResults": batchSize,
"outputType": "URL",
"outputFormat": rwUtils.OUTPUT_FORMAT,
"outputQuality": rwUtils.OUTPUT_QUALITY,
}
]
if (negativePrompt is not None and negativePrompt != ""):
genConfig[0]["negativePrompt"] = negativePrompt
if (promptWeighting != "Disabled"):
if (promptWeighting == "sdEmbeds"):
genConfig[0]["promptWeighting"] = "sdEmbeds"
else:
genConfig[0]["promptWeighting"] = "compel"
genResult = rwUtils.inferenecRequest(genConfig)
images = rwUtils.extractImageURLs(genResult)
return (images,)
-5
View File
@@ -1,5 +0,0 @@
"""
Runware Provider Settings Module
Contains all provider-specific settings nodes for Runware ComfyUI integration.
"""
@@ -1,114 +0,0 @@
"""
Runware Alibaba Provider Settings Node
Provides Alibaba-specific settings for image/video generation
"""
from typing import Dict, Any
class RunwareAlibabaProviderSettings:
"""Runware Alibaba Provider Settings Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"usePromptEnhancer": ("BOOLEAN", {
"tooltip": "Enable to include promptEnhancer parameter in provider settings",
"default": False,
}),
"promptEnhancer": ("BOOLEAN", {
"tooltip": "Enable prompt enhancement for Alibaba generation. Only used when 'Use Prompt Enhancer' is enabled.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"usePromptExtend": ("BOOLEAN", {
"tooltip": "Enable to include promptExtend parameter in provider settings",
"default": False,
}),
"promptExtend": ("BOOLEAN", {
"tooltip": "Enable LLM prompt rewriting. If enabled, a large language model (LLM) rewrites the input prompt. This can significantly improve the generation quality for shorter prompts but increases the time required. Only used when 'Use Prompt Extend' is enabled.",
"default": True,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useAudio": ("BOOLEAN", {
"tooltip": "Enable to include audio parameter in provider settings",
"default": False,
}),
"audio": ("BOOLEAN", {
"tooltip": "Automatically add audio (ignored if audio_url is set). Only used when 'Use Audio' is enabled.",
"default": True,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useShotType": ("BOOLEAN", {
"tooltip": "Enable to include shotType parameter in provider settings. Only applicable if promptExtend is enabled.",
"default": False,
}),
"shotType": (["single", "multi"], {
"tooltip": "Shot type for generation. Only applicable if promptExtend is true. Only used when 'Use Shot Type' is enabled.",
"default": "single",
}),
}
}
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("Provider Settings",)
FUNCTION = "createProviderSettings"
CATEGORY = "Runware/Provider Settings"
DESCRIPTION = "Configure Alibaba-specific provider settings for image/video generation including prompt enhancement, prompt extension, audio, and shot type."
def createProviderSettings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create Alibaba provider settings"""
# Get control parameters
usePromptEnhancer = kwargs.get("usePromptEnhancer", False)
usePromptExtend = kwargs.get("usePromptExtend", False)
useAudio = kwargs.get("useAudio", False)
useShotType = kwargs.get("useShotType", False)
# Get value parameters
promptEnhancer = kwargs.get("promptEnhancer", False)
promptExtend = kwargs.get("promptExtend", True)
audio = kwargs.get("audio", True)
shotType = kwargs.get("shotType", "single")
# Build settings dictionary - only include what is enabled
alibabaSettings: Dict[str, Any] = {}
# Add promptEnhancer only if usePromptEnhancer is enabled
if usePromptEnhancer:
alibabaSettings["promptEnhancer"] = promptEnhancer
# Add promptExtend only if usePromptExtend is enabled
if usePromptExtend:
alibabaSettings["promptExtend"] = promptExtend
# Add audio only if useAudio is enabled
if useAudio:
alibabaSettings["audio"] = audio
# Add shotType only if useShotType is enabled and promptExtend is enabled
if useShotType and usePromptExtend and promptExtend:
if shotType is not None and shotType != "None":
alibabaSettings["shotType"] = shotType
# Clean up None values
alibabaSettings = {k: v for k, v in alibabaSettings.items() if v is not None}
# Return flat dictionary - will be wrapped by inference node with provider name
return (alibabaSettings,)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareAlibabaProviderSettings": RunwareAlibabaProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareAlibabaProviderSettings": "Runware Alibaba Provider Settings",
}
@@ -1,66 +0,0 @@
"""
Runware BlackForest Labs Provider Settings Node
Provides BlackForest Labs-specific settings for image generation
"""
from typing import Dict, Any
class RunwareBlackForestProviderSettings:
"""Runware BlackForest Labs Provider Settings Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useSafetyTolerance": ("BOOLEAN", {
"tooltip": "Enable to include safetyTolerance parameter in API request",
"default": False,
}),
"safetyTolerance": ("INT", {
"tooltip": "Safety tolerance value for generation. Only used when 'Use Safety Tolerance' is enabled.",
"default": 0,
"min": 0,
"max": 100,
}),
}
}
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("Provider Settings",)
FUNCTION = "createProviderSettings"
CATEGORY = "Runware/Provider Settings"
DESCRIPTION = "Configure BlackForest Labs-specific provider settings for image generation including safety tolerance."
def createProviderSettings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create BlackForest Labs provider settings"""
# Get control parameters
useSafetyTolerance = kwargs.get("useSafetyTolerance", False)
# Get value parameters
safetyTolerance = kwargs.get("safetyTolerance", 0)
# Build settings dictionary - only include what is enabled
blackForestSettings: Dict[str, Any] = {}
# Add safetyTolerance only if useSafetyTolerance is enabled
if useSafetyTolerance:
blackForestSettings["safetyTolerance"] = safetyTolerance
# Clean up None values
blackForestSettings = {k: v for k, v in blackForestSettings.items() if v is not None}
# Return flat dictionary - will be wrapped by inference node with provider name
return (blackForestSettings,)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareBlackForestProviderSettings": RunwareBlackForestProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareBlackForestProviderSettings": "Runware BlackForest Labs Provider Settings",
}
@@ -1,228 +0,0 @@
"""
Runware Bria Provider Mask Node
Creates mask configuration for Bria video eraser settings
"""
from typing import Dict, Any, List
class RunwareBriaProviderMask:
"""Runware Bria Provider Mask Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useForeground": ("BOOLEAN", {
"tooltip": "Enable to include mask.foreground parameter in API request.",
"default": False,
}),
"foreground": ("BOOLEAN", {
"tooltip": "Triggers the foreground mask endpoint. Only used when 'Use Foreground' is enabled.",
"default": True,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"usePrompt": ("BOOLEAN", {
"tooltip": "Enable to include mask.prompt parameter in API request.",
"default": False,
}),
"prompt": ("STRING", {
"tooltip": "Text instruction describing the object to be masked. Only used when 'Use Prompt' is enabled.",
"default": "",
"multiline": True,
}),
"useFrameIndex": ("BOOLEAN", {
"tooltip": "Enable to include mask.frameIndex parameter in API request.",
"default": False,
}),
"frameIndex": ("INT", {
"tooltip": "The frame number to apply key points to. Specifies which frame in the video to use for masking. Only used when 'Use Frame Index' is enabled.",
"default": 0,
"min": 0,
}),
"use_1": ("BOOLEAN", {
"tooltip": "Enable to include the first key point in the mask configuration.",
"default": False,
}),
"X1": ("INT", {
"tooltip": "X-coordinate of the first key point on the frame",
"default": 0,
"min": 0,
}),
"Y1": ("INT", {
"tooltip": "Y-coordinate of the first key point on the frame",
"default": 0,
"min": 0,
}),
"Type1": (["positive", "negative"], {
"tooltip": "Type of mask hint for first key point. 'positive' includes area, 'negative' excludes area.",
"default": "positive",
}),
"use_2": ("BOOLEAN", {
"tooltip": "Enable to include the second key point in the mask configuration.",
"default": False,
}),
"X2": ("INT", {
"tooltip": "X-coordinate of the second key point on the frame",
"default": 0,
"min": 0,
}),
"Y2": ("INT", {
"tooltip": "Y-coordinate of the second key point on the frame",
"default": 0,
"min": 0,
}),
"Type2": (["positive", "negative"], {
"tooltip": "Type of mask hint for second key point. 'positive' includes area, 'negative' excludes area.",
"default": "positive",
}),
"use_3": ("BOOLEAN", {
"tooltip": "Enable to include the third key point in the mask configuration.",
"default": False,
}),
"X3": ("INT", {
"tooltip": "X-coordinate of the third key point on the frame",
"default": 0,
"min": 0,
}),
"Y3": ("INT", {
"tooltip": "Y-coordinate of the third key point on the frame",
"default": 0,
"min": 0,
}),
"Type3": (["positive", "negative"], {
"tooltip": "Type of mask hint for third key point. 'positive' includes area, 'negative' excludes area.",
"default": "positive",
}),
"use_4": ("BOOLEAN", {
"tooltip": "Enable to include the fourth key point in the mask configuration.",
"default": False,
}),
"X4": ("INT", {
"tooltip": "X-coordinate of the fourth key point on the frame",
"default": 0,
"min": 0,
}),
"Y4": ("INT", {
"tooltip": "Y-coordinate of the fourth key point on the frame",
"default": 0,
"min": 0,
}),
"Type4": (["positive", "negative"], {
"tooltip": "Type of mask hint for fourth key point. 'positive' includes area, 'negative' excludes area.",
"default": "positive",
}),
"use_5": ("BOOLEAN", {
"tooltip": "Enable to include the fifth key point in the mask configuration.",
"default": False,
}),
"X5": ("INT", {
"tooltip": "X-coordinate of the fifth key point on the frame",
"default": 0,
"min": 0,
}),
"Y5": ("INT", {
"tooltip": "Y-coordinate of the fifth key point on the frame",
"default": 0,
"min": 0,
}),
"Type5": (["positive", "negative"], {
"tooltip": "Type of mask hint for fifth key point. 'positive' includes area, 'negative' excludes area.",
"default": "positive",
}),
"use_6": ("BOOLEAN", {
"tooltip": "Enable to include the sixth key point in the mask configuration.",
"default": False,
}),
"X6": ("INT", {
"tooltip": "X-coordinate of the sixth key point on the frame",
"default": 0,
"min": 0,
}),
"Y6": ("INT", {
"tooltip": "Y-coordinate of the sixth key point on the frame",
"default": 0,
"min": 0,
}),
"Type6": (["positive", "negative"], {
"tooltip": "Type of mask hint for sixth key point. 'positive' includes area, 'negative' excludes area.",
"default": "positive",
}),
}
}
RETURN_TYPES = ("RUNWAREBRIAPROVIDERMASK",)
RETURN_NAMES = ("Bria Provider Setting Mask",)
FUNCTION = "createMask"
CATEGORY = "Runware/Provider Settings"
DESCRIPTION = "Create mask configuration for Bria video eraser settings. Supports foreground mask, text-based masking, and coordinate-based key points for precise object removal."
def createMask(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create mask configuration from provided parameters"""
mask: Dict[str, Any] = {}
maskEnabled = False
# Handle foreground
useForeground = kwargs.get("useForeground", False)
if useForeground:
foreground = kwargs.get("foreground", True)
mask["foreground"] = foreground
maskEnabled = True
# Handle prompt
usePrompt = kwargs.get("usePrompt", False)
if usePrompt:
prompt = kwargs.get("prompt", "").strip()
if prompt:
mask["prompt"] = prompt
maskEnabled = True
# Handle frameIndex
useFrameIndex = kwargs.get("useFrameIndex", False)
if useFrameIndex:
frameIndex = kwargs.get("frameIndex", 0)
mask["frameIndex"] = frameIndex
maskEnabled = True
# Process key points
keyPoints: List[Dict[str, Any]] = []
for i in range(1, 7):
use_key = kwargs.get(f"use_{i}", False)
# Only process key point if use toggle is enabled
if use_key:
x = kwargs.get(f"X{i}", 0)
y = kwargs.get(f"Y{i}", 0)
point_type = kwargs.get(f"Type{i}", "positive")
# Only add key point if both x and y are provided and at least one is non-zero
if x > 0 or y > 0:
keyPoints.append({
"x": x,
"y": y,
"type": point_type
})
# Add keyPoints if any were provided
if len(keyPoints) > 0:
mask["keyPoints"] = keyPoints
maskEnabled = True
# Only return mask if it has at least one property
if maskEnabled:
return (mask,)
else:
return ({},)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareBriaProviderMask": RunwareBriaProviderMask,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareBriaProviderMask": "Runware Bria Provider Mask",
}
@@ -1,349 +0,0 @@
"""
Runware Bria Provider Settings Node
Provides Bria-specific settings for image/video generation
"""
from typing import Dict, Any
class RunwareBriaProviderSettings:
"""Runware Bria Provider Settings Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useMedium": ("BOOLEAN", {
"tooltip": "Enable to include medium parameter in API request. Disable if not needed.",
"default": False,
}),
"medium": (["None", "photography", "art"], {
"tooltip": "Select the medium type for image generation. Only used when 'Use Medium' is enabled.",
"default": "photography",
}),
"usePromptEnhancement": ("BOOLEAN", {
"tooltip": "Enable to include promptEnhancement parameter in API request.",
"default": False,
}),
"promptEnhancement": ("BOOLEAN", {
"tooltip": "When set to true, enhances the provided prompt by generating additional, more descriptive variations, resulting in more diverse and creative output images. Note that turning this flag on may result in a few additional seconds to the inference time. Only used when 'Use Prompt Enhancement' is enabled.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useEnhanceImage": ("BOOLEAN", {
"tooltip": "Enable to include enhanceImage parameter in API request.",
"default": False,
}),
"enhanceImage": ("BOOLEAN", {
"tooltip": "When set to true, generates images with richer details, sharper textures, and enhanced clarity. Slightly increases generation time per image. Only used when 'Use Enhance Image' is enabled.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"usePromptContentModeration": ("BOOLEAN", {
"tooltip": "Enable to include promptContentModeration parameter in API request.",
"default": False,
}),
"promptContentModeration": ("BOOLEAN", {
"tooltip": "When enabled (default: true), the input prompt is scanned for NSFW or ethically restricted terms before image generation. If the prompt violates Bria's ethical guidelines, the request will be rejected with a 408 error. Only used when 'Use Prompt Content Moderation' is enabled.",
"default": True,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useContentModeration": ("BOOLEAN", {
"tooltip": "Enable to include contentModeration parameter in API request.",
"default": False,
}),
"contentModeration": ("BOOLEAN", {
"tooltip": "When enabled, applies content moderation to both input visuals and generated outputs. Only used when 'Use Content Moderation' is enabled.",
"default": True,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useIpSignal": ("BOOLEAN", {
"tooltip": "Enable to include ipSignal parameter in API request.",
"default": False,
}),
"ipSignal": ("BOOLEAN", {
"tooltip": "Flags prompts with potential IP content. If detected, a warning will be included in the response. Only used when 'Use IP Signal' is enabled.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"usePreserveAlpha": ("BOOLEAN", {
"tooltip": "Enable to include preserveAlpha parameter in API request.",
"default": False,
}),
"preserveAlpha": ("BOOLEAN", {
"tooltip": "Preserve alpha channel in the image. Only used when 'Use Preserve Alpha' is enabled.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useMode": ("BOOLEAN", {
"tooltip": "Enable to include mode parameter in API request for Background Replace.",
"default": False,
}),
"mode": (["None", "base", "high_control", "fast"], {
"tooltip": "Selects the background-generation mode for Background Replace. Base: clean, high quality backgrounds. High_control: stronger prompt adherence and scene context, finer control over layout and details. Fast: same core capabilities as base, optimal speed and quality balance. Only used when 'Use Mode' is enabled.",
"default": "base",
}),
"useEnhanceReferenceImages": ("BOOLEAN", {
"tooltip": "Enable to include enhanceReferenceImages parameter in API request.",
"default": False,
}),
"enhanceReferenceImages": ("BOOLEAN", {
"tooltip": "When set to true, additional logic processes the included reference image to make adjustments for optimal results. Only used when 'Use Enhance Reference Images' is enabled.",
"default": True,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useRefinePrompt": ("BOOLEAN", {
"tooltip": "Enable to include refinePrompt parameter in API request.",
"default": False,
}),
"refinePrompt": ("BOOLEAN", {
"tooltip": "When true, an additional logic takes the prompt that was included and adjusts it to achieve optimal results. Only used when 'Use Refine Prompt' is enabled.",
"default": True,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useOriginalQuality": ("BOOLEAN", {
"tooltip": "Enable to include originalQuality parameter in API request.",
"default": False,
}),
"originalQuality": ("BOOLEAN", {
"tooltip": "When true, the output image retains the original input image's size; otherwise, the image is scaled to 1 megapixel (1MP) while preserving its aspect ratio. Only used when 'Use Original Quality' is enabled.",
"default": True,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useForceBackgroundDetection": ("BOOLEAN", {
"tooltip": "Enable to include forceBackgroundDetection parameter in API request.",
"default": False,
}),
"forceBackgroundDetection": ("BOOLEAN", {
"tooltip": "When true, forces background detection and removal, even if the original image already contains an alpha channel. Useful for refining existing foreground/background separation or ignoring unnecessary alpha channels. Only used when 'Use Force Background Detection' is enabled.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"usePreserveAudio": ("BOOLEAN", {
"tooltip": "Enable to include preserveAudio parameter in API request.",
"default": False,
}),
"preserveAudio": ("BOOLEAN", {
"tooltip": "Whether to keep the original audio track. Only used when 'Use Preserve Audio' is enabled.",
"default": True,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useAutoTrim": ("BOOLEAN", {
"tooltip": "Enable to include autoTrim parameter in API request.",
"default": False,
}),
"autoTrim": ("BOOLEAN", {
"tooltip": "If true, videos longer than 5 seconds are trimmed to the first 5 seconds. If false, videos longer than 5 seconds are rejected. Only used when 'Use Auto Trim' is enabled.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useSeason": ("BOOLEAN", {
"tooltip": "Enable to include season parameter (maps to /reseason).",
"default": False,
}),
"season": (["spring", "summer", "autumn", "winter"], {
"tooltip": "Desired season. Maps to /reseason.",
"default": "spring",
}),
"useEdit": ("BOOLEAN", {
"tooltip": "Enable to include edit parameter.",
"default": False,
}),
"edit": (["colorize", "relight", "restore", "blend", "reseason", "sketch_to_image"], {
"tooltip": "Edit mode: colorize, relight, restore, blend, reseason, sketch_to_image.",
"default": "restore",
}),
"useColor": ("BOOLEAN", {
"tooltip": "Enable to include color parameter (maps to /colorize).",
"default": False,
}),
"color": (["contemporary color", "vivid color", "black and white colors", "sepia vintage"], {
"tooltip": "Restoration/color style. Maps to /colorize.",
"default": "contemporary color",
}),
"useLightDirection": ("BOOLEAN", {
"tooltip": "Enable to include lightDirection parameter (maps to /relight).",
"default": False,
}),
"lightDirection": (["front", "side", "bottom", "top-down"], {
"tooltip": "Direction of light. Maps to /relight.",
"default": "front",
}),
"useLightType": ("BOOLEAN", {
"tooltip": "Enable to include lightType parameter (maps to /relight).",
"default": False,
}),
"lightType": ([
"midday", "blue hour light", "low-angle sunlight", "sunrise light",
"spotlight on subject, keep background settings", "overcast light",
"soft overcast daylight lighting", "cloud-filtered lighting", "fog-diffused lighting",
"moonlight lighting", "starlight lighting nighttime", "soft bokeh lighting",
"harsh studio lighting keep background setting",
], {
"tooltip": "Type of lighting. Maps to /relight.",
"default": "midday",
}),
"Bria Provider Setting Mask": ("RUNWAREBRIAPROVIDERMASK", {
"tooltip": "Connect Runware Bria Provider Mask node to provide mask configuration for video eraser operations.",
}),
}
}
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("providerSettings",)
FUNCTION = "create_provider_settings"
CATEGORY = "Runware/Provider Settings"
DESCRIPTION = "Configure Bria-specific provider settings for image generation, background replacement, and video operations including medium type, prompt enhancement, content moderation, and background replace mode."
def create_provider_settings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create Bria provider settings"""
# Get use parameters
useMedium = kwargs.get("useMedium", False)
usePromptEnhancement = kwargs.get("usePromptEnhancement", False)
useEnhanceImage = kwargs.get("useEnhanceImage", False)
usePromptContentModeration = kwargs.get("usePromptContentModeration", False)
useContentModeration = kwargs.get("useContentModeration", False)
useIpSignal = kwargs.get("useIpSignal", False)
usePreserveAlpha = kwargs.get("usePreserveAlpha", False)
useMode = kwargs.get("useMode", False)
useEnhanceReferenceImages = kwargs.get("useEnhanceReferenceImages", False)
useRefinePrompt = kwargs.get("useRefinePrompt", False)
useOriginalQuality = kwargs.get("useOriginalQuality", False)
useForceBackgroundDetection = kwargs.get("useForceBackgroundDetection", False)
usePreserveAudio = kwargs.get("usePreserveAudio", False)
useAutoTrim = kwargs.get("useAutoTrim", False)
useSeason = kwargs.get("useSeason", False)
useEdit = kwargs.get("useEdit", False)
useColor = kwargs.get("useColor", False)
useLightDirection = kwargs.get("useLightDirection", False)
useLightType = kwargs.get("useLightType", False)
# Get actual parameters
medium = kwargs.get("medium", "photography")
promptEnhancement = kwargs.get("promptEnhancement", False)
enhanceImage = kwargs.get("enhanceImage", False)
promptContentModeration = kwargs.get("promptContentModeration", True)
contentModeration = kwargs.get("contentModeration", True)
ipSignal = kwargs.get("ipSignal", False)
preserveAlpha = kwargs.get("preserveAlpha", False)
mode = kwargs.get("mode", "base")
enhanceReferenceImages = kwargs.get("enhanceReferenceImages", True)
refinePrompt = kwargs.get("refinePrompt", True)
originalQuality = kwargs.get("originalQuality", True)
forceBackgroundDetection = kwargs.get("forceBackgroundDetection", False)
preserveAudio = kwargs.get("preserveAudio", True)
autoTrim = kwargs.get("autoTrim", False)
season = kwargs.get("season", "spring")
edit = kwargs.get("edit", "restore")
color = kwargs.get("color", "contemporary color")
lightDirection = kwargs.get("lightDirection", "front")
lightType = kwargs.get("lightType", "midday")
mask = kwargs.get("Bria Provider Setting Mask", None)
# Build settings dictionary
settings = {}
# Add medium only if useMedium is enabled
if useMedium and medium is not None and medium != "None":
settings["medium"] = medium
# Add promptEnhancement only if usePromptEnhancement is enabled
if usePromptEnhancement:
settings["promptEnhancement"] = promptEnhancement
# Add enhanceImage only if useEnhanceImage is enabled
if useEnhanceImage:
settings["enhanceImage"] = enhanceImage
# Add promptContentModeration only if usePromptContentModeration is enabled
if usePromptContentModeration:
settings["promptContentModeration"] = promptContentModeration
# Add contentModeration only if useContentModeration is enabled
if useContentModeration:
settings["contentModeration"] = contentModeration
# Add ipSignal only if useIpSignal is enabled
if useIpSignal:
settings["ipSignal"] = ipSignal
# Add preserveAlpha only if usePreserveAlpha is enabled
if usePreserveAlpha:
settings["preserveAlpha"] = preserveAlpha
# Add mode only if useMode is enabled (for Background Replace)
if useMode and mode is not None and mode != "None":
settings["mode"] = mode
# Add enhanceReferenceImages only if useEnhanceReferenceImages is enabled
if useEnhanceReferenceImages:
settings["enhanceReferenceImages"] = enhanceReferenceImages
# Add refinePrompt only if useRefinePrompt is enabled
if useRefinePrompt:
settings["refinePrompt"] = refinePrompt
# Add originalQuality only if useOriginalQuality is enabled
if useOriginalQuality:
settings["originalQuality"] = originalQuality
# Add forceBackgroundDetection only if useForceBackgroundDetection is enabled
if useForceBackgroundDetection:
settings["forceBackgroundDetection"] = forceBackgroundDetection
# Add preserveAudio only if usePreserveAudio is enabled
if usePreserveAudio:
settings["preserveAudio"] = preserveAudio
# Add autoTrim only if useAutoTrim is enabled
if useAutoTrim:
settings["autoTrim"] = autoTrim
# Add season only if useSeason is enabled (maps to /reseason)
if useSeason:
settings["season"] = season
# Add edit only if useEdit is enabled
if useEdit:
settings["edit"] = edit
# Add color only if useColor is enabled (maps to /colorize)
if useColor:
settings["color"] = color
# Add relight params (Bria API expects camelCase)
if useLightDirection:
settings["lightDirection"] = lightDirection
if useLightType:
settings["lightType"] = lightType
# Add mask if provided from Mask node
if mask is not None and isinstance(mask, dict) and len(mask) > 0:
settings["mask"] = mask
# Clean up None values
settings = {k: v for k, v in settings.items() if v is not None}
return (settings,)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareBriaProviderSettings": RunwareBriaProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareBriaProviderSettings": "Runware Bria Provider Settings",
}
@@ -1,132 +0,0 @@
"""
Runware Bytedance Provider Settings Node
Provides Bytedance-specific settings for image/video generation
"""
from typing import Dict, Any
class RunwareBytedanceProviderSettings:
"""Runware Bytedance Provider Settings Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useCameraFixed": ("BOOLEAN", {
"tooltip": "Enable to include cameraFixed parameter in provider settings",
"default": False,
}),
"cameraFixed": ("BOOLEAN", {
"tooltip": "Enable to fix camera position during video generation",
"default": False,
}),
"useMaxSequentialImages": ("BOOLEAN", {
"tooltip": "Enable to include maxSequentialImages parameter in provider settings",
"default": False,
}),
"maxSequentialImages": ("INT", {
"tooltip": "Maximum number of sequential images to generate (1-15)",
"default": 1,
"min": 1,
"max": 15,
}),
"useFastMode": ("BOOLEAN", {
"tooltip": "Enable to include fastMode parameter in provider settings",
"default": False,
}),
"fastMode": ("BOOLEAN", {
"tooltip": "When enabled, speeds up generation by sacrificing some effects. Default: false. RTF: 25-28 (fast) vs 35 (normal). Supported by OmniHuman 1.5.",
"default": False,
}),
"useAudio": ("BOOLEAN", {
"tooltip": "Enable to include audio parameter in provider settings.",
"default": False,
}),
"audio": ("BOOLEAN", {
"tooltip": "Enable audio generation support when available.",
"default": False,
}),
"useDraft": ("BOOLEAN", {
"tooltip": "Enable to include draft parameter in provider settings",
"default": False,
}),
"draft": ("BOOLEAN", {
"tooltip": "Enable draft mode for Bytedance provider",
"default": False,
}),
"useOptimizePromptMode": ("BOOLEAN", {
"tooltip": "Enable to include optimizePromptMode parameter in provider settings",
"default": False,
}),
"optimizePromptMode": (["standard", "fast"], {
"tooltip": "Prompt optimization mode. standard: higher quality, longer time. fast: faster generation, average quality.",
"default": "standard",
}),
}
}
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("providerSettings",)
FUNCTION = "create_provider_settings"
CATEGORY = "Runware/Provider Settings"
def create_provider_settings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create Bytedance provider settings"""
# Get control parameters
useCameraFixed = kwargs.get("useCameraFixed", False)
useMaxSequentialImages = kwargs.get("useMaxSequentialImages", False)
useFastMode = kwargs.get("useFastMode", False)
useAudio = kwargs.get("useAudio", False)
useDraft = kwargs.get("useDraft", False)
useOptimizePromptMode = kwargs.get("useOptimizePromptMode", False)
# Get value parameters
cameraFixed = kwargs.get("cameraFixed", False)
maxSequentialImages = kwargs.get("maxSequentialImages", 1)
fastMode = kwargs.get("fastMode", False)
audio = kwargs.get("audio", False)
draft = kwargs.get("draft", False)
optimizePromptMode = kwargs.get("optimizePromptMode", "standard")
# Build settings dictionary - only include what is enabled
settings = {}
# Add cameraFixed only if useCameraFixed is enabled
if useCameraFixed:
settings["cameraFixed"] = cameraFixed
# Add maxSequentialImages only if useMaxSequentialImages is enabled
if useMaxSequentialImages:
settings["maxSequentialImages"] = maxSequentialImages
# Add fastMode only if useFastMode is enabled
if useFastMode:
settings["fastMode"] = fastMode
# Add audio only if useAudio is enabled
if useAudio:
settings["audio"] = audio
# Add draft only if useDraft is enabled
if useDraft:
settings["draft"] = draft
# Add optimizePromptMode only if useOptimizePromptMode is enabled
if useOptimizePromptMode:
settings["optimizePromptMode"] = optimizePromptMode
# Clean up None values
settings = {k: v for k, v in settings.items() if v is not None}
return (settings,)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareBytedanceProviderSettings": RunwareBytedanceProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareBytedanceProviderSettings": "Runware Bytedance Provider Settings",
}
@@ -1,65 +0,0 @@
class RunwareElevenLabsProviderSettings:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"positiveGlobalStyles": ("STRING", {
"multiline": True,
"default": "rock, energetic",
"tooltip": "Global positive styles for the entire song (comma-separated): positiveGlobalStyles"
}),
"negativeGlobalStyles": ("STRING", {
"multiline": True,
"default": "slow, quiet",
"tooltip": "Global negative styles for the entire song (comma-separated): negativeGlobalStyles"
}),
},
"optional": {
"sections": ("RUNWAREAUDIOSECTIONS", {
"tooltip": "Connect multiple Runware Audio Sections nodes to define song structure"
}),
}
}
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("providerSettings",)
FUNCTION = "create_provider_settings"
CATEGORY = "Runware/Audio"
def create_provider_settings(self, **kwargs):
positiveGlobalStyles = kwargs.get("positiveGlobalStyles", "")
negativeGlobalStyles = kwargs.get("negativeGlobalStyles", "")
sections = kwargs.get("sections", [])
# Parse styles from comma-separated strings
positiveStyles = [style.strip() for style in positiveGlobalStyles.split(",") if style.strip()]
negativeStyles = [style.strip() for style in negativeGlobalStyles.split(",") if style.strip()]
# Handle sections - can be single section or list of sections
sections_list = []
if sections:
if isinstance(sections, list):
sections_list = sections
else:
sections_list = [sections]
# Build the music composition plan
music = {
"compositionPlan": {
"positiveGlobalStyles": positiveStyles,
"negativeGlobalStyles": negativeStyles,
"sections": sections_list
}
}
# Return flat dictionary - will be wrapped by inference node with provider name (same pattern as video provider settings)
providerSettings = {
"music": music
}
return (providerSettings,)
@@ -1,56 +0,0 @@
class RunwareElevenLabsProviderSettingsSections:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"sectionName": ("STRING", {
"default": "intro",
"tooltip": "Name of the section"
}),
"positiveLocalStyles": ("STRING", {
"default": "guitar, buildup",
"tooltip": "Local positive styles for this section (comma-separated): positiveLocalStyles"
}),
"negativeLocalStyles": ("STRING", {
"default": "vocals",
"tooltip": "Local negative styles for this section (comma-separated): negativeLocalStyles"
}),
"duration": ("INT", {
"default": 15,
"min": 3,
"max": 120,
"tooltip": "Duration of the section in seconds (3-120)"
}),
"lines": ("STRING", {
"multiline": True,
"default": "",
"tooltip": "Lyrics for the section (one line per line)"
}),
}
}
RETURN_TYPES = ("RUNWAREAUDIOSECTIONS",)
RETURN_NAMES = ("sections",)
FUNCTION = "create_sections"
CATEGORY = "Runware/Audio"
def create_sections(self, **kwargs):
sectionName = kwargs.get("sectionName", "")
positiveLocalStyles = kwargs.get("positiveLocalStyles", "")
negativeLocalStyles = kwargs.get("negativeLocalStyles", "")
duration = kwargs.get("duration", 0)
lines = kwargs.get("lines", "")
# Create section object
section = {
"sectionName": sectionName,
"positiveLocalStyles": [style.strip() for style in positiveLocalStyles.split(",") if style.strip()],
"negativeLocalStyles": [style.strip() for style in negativeLocalStyles.split(",") if style.strip()],
"duration": duration,
"lines": [line.strip() for line in lines.split("\n") if line.strip()]
}
return (section,)
@@ -1,108 +0,0 @@
"""
Runware Google Provider Settings Node
Provides Google-specific settings for image/video generation
"""
from typing import Dict, Any
class RunwareGoogleProviderSettings:
"""Runware Google Provider Settings Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useGenerateAudio": ("BOOLEAN", {
"tooltip": "Enable to include generateAudio parameter in provider settings",
"default": False,
}),
"generateAudio": ("BOOLEAN", {
"tooltip": "Enable audio generation. Only used when 'Use Generate Audio' is enabled.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useEnhancePrompt": ("BOOLEAN", {
"tooltip": "Enable to include enhancePrompt parameter in provider settings",
"default": False,
}),
"enhancePrompt": ("BOOLEAN", {
"tooltip": "Enable prompt enhancement for Google generation. Only used when 'Use Enhance Prompt' is enabled.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useSearch": ("BOOLEAN", {
"tooltip": "Enable to include search parameter in provider settings",
"default": False,
}),
"search": ("BOOLEAN", {
"tooltip": "Enable search functionality. Only used when 'Use Search' is enabled.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useResizeMode": ("BOOLEAN", {
"tooltip": "Enable to include resizeMode in providerSettings.google (image resize behavior).",
"default": False,
}),
"resizeMode": (["crop", "pad"], {
"tooltip": "Value for providerSettings.google.resizeMode. Must be 'crop' or 'pad'. Only used when 'Use Resize Mode' is enabled.",
"default": "crop",
}),
}
}
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("Provider Settings",)
FUNCTION = "createProviderSettings"
CATEGORY = "Runware/Provider Settings"
DESCRIPTION = "Configure Google-specific provider settings for image/video generation including audio generation, prompt enhancement, search, and resizeMode (crop/pad)."
def createProviderSettings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create Google provider settings"""
# Get control parameters
useGenerateAudio = kwargs.get("useGenerateAudio", False)
useEnhancePrompt = kwargs.get("useEnhancePrompt", False)
useSearch = kwargs.get("useSearch", False)
useResizeMode = kwargs.get("useResizeMode", False)
# Get value parameters
generateAudio = kwargs.get("generateAudio", False)
enhancePrompt = kwargs.get("enhancePrompt", False)
search = kwargs.get("search", False)
resizeMode = kwargs.get("resizeMode", "crop")
# Build settings dictionary - only include what is enabled
googleSettings: Dict[str, Any] = {}
# Add parameters only if their use toggles are enabled
if useGenerateAudio:
googleSettings["generateAudio"] = generateAudio
if useEnhancePrompt:
googleSettings["enhancePrompt"] = enhancePrompt
if useSearch:
googleSettings["search"] = search
if useResizeMode:
mode = str(resizeMode).strip().lower() if resizeMode is not None else "crop"
googleSettings["resizeMode"] = mode if mode in ("crop", "pad") else "crop"
# Clean up None values
googleSettings = {k: v for k, v in googleSettings.items() if v is not None}
# Return flat dictionary - will be wrapped by inference node with provider name
return (googleSettings,)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareGoogleProviderSettings": RunwareGoogleProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareGoogleProviderSettings": "Runware Google Provider Settings",
}
@@ -1,47 +0,0 @@
"""
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",
}
@@ -1,230 +0,0 @@
"""
Runware KlingAI Provider Settings Node
Provides KlingAI-specific settings for video generation
"""
from typing import Dict, Any, List
class RunwareKlingProviderSettings:
"""Runware KlingAI Provider Settings Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useCameraControl": ("BOOLEAN", {
"tooltip": "Enable to include cameraControl parameter in provider settings",
"default": False,
}),
"cameraControl": (["none", "static", "dynamic", "zoom_in", "zoom_out", "pan_left", "pan_right", "tilt_up", "tilt_down", "smooth"], {
"tooltip": "Camera control mode for KlingAI video generation",
"default": "none",
}),
"useSoundVolume": ("BOOLEAN", {
"tooltip": "Enable to include soundVolume parameter in provider settings",
"default": False,
}),
"soundVolume": ("FLOAT", {
"tooltip": "Background sound volume to mix with generated audio (provider key: soundVolume).",
"default": 1.0,
"min": 0.0,
"max": 2.0,
"step": 0.1,
}),
"useOriginalAudioVolume": ("BOOLEAN", {
"tooltip": "Enable to include originalAudioVolume parameter in provider settings",
"default": False,
}),
"originalAudioVolume": ("FLOAT", {
"tooltip": "Original video volume mix level (provider key: originalAudioVolume). No effect if source has no audio.",
"default": 1.0,
"min": 0.0,
"max": 2.0,
"step": 0.1,
}),
"useSoundEffectPrompt": ("BOOLEAN", {
"tooltip": "Enable to include soundEffectPrompt parameter in provider settings",
"default": False,
}),
"soundEffectPrompt": ("STRING", {
"multiline": True,
"tooltip": "Prompt describing the sound effect to generate",
"default": "",
}),
"useBgmPrompt": ("BOOLEAN", {
"tooltip": "Enable to include bgmPrompt parameter in provider settings",
"default": False,
}),
"bgmPrompt": ("STRING", {
"multiline": True,
"tooltip": "Prompt describing the background music to generate",
"default": "",
}),
"useAsmrMode": ("BOOLEAN", {
"tooltip": "Enable to include asmrMode parameter in provider settings",
"default": False,
}),
"asmrMode": ("BOOLEAN", {
"tooltip": "Enable ASMR mode for audio generation",
"default": False,
}),
"useKeepOriginalSound": ("BOOLEAN", {
"tooltip": "Enable to include keepOriginalSound parameter in provider settings",
"default": False,
}),
"keepOriginalSound": ("BOOLEAN", {
"tooltip": "Keep the original sound from the source video. Only used when 'Use Keep Original Sound' is enabled.",
"default": True,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useSound": ("BOOLEAN", {
"tooltip": "Enable to include sound parameter in provider settings",
"default": False,
}),
"sound": ("BOOLEAN", {
"tooltip": "Whether sound is generated simultaneously when generating a video",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useCharacterOrientation": ("BOOLEAN", {
"tooltip": "Enable to include characterOrientation parameter in provider settings",
"default": False,
}),
"characterOrientation": (["image", "video"], {
"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.",
}),
"MultiPrompt Segment 6": ("RUNWAREKLINGMULTIPROMPTSEGMENT", {
"tooltip": "Connect a Runware Kling MultiPrompt Segment. Only used when 'Use Multiprompt' is enabled.",
}),
}
}
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("Provider Settings",)
FUNCTION = "createProviderSettings"
CATEGORY = "Runware/Provider Settings"
DESCRIPTION = "Configure KlingAI-specific provider settings for video generation including camera control, audio volume, sound effects, ASMR mode, and sound generation."
def createProviderSettings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create KlingAI provider settings"""
# Get control parameters
useCameraControl = kwargs.get("useCameraControl", False)
useSoundVolume = kwargs.get("useSoundVolume", False)
useOriginalAudioVolume = kwargs.get("useOriginalAudioVolume", False)
useSoundEffectPrompt = kwargs.get("useSoundEffectPrompt", False)
useBgmPrompt = kwargs.get("useBgmPrompt", False)
useAsmrMode = kwargs.get("useAsmrMode", False)
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")
soundVolume = kwargs.get("soundVolume", 1.0)
originalAudioVolume = kwargs.get("originalAudioVolume", 1.0)
soundEffectPrompt = kwargs.get("soundEffectPrompt", "")
bgmPrompt = kwargs.get("bgmPrompt", "")
asmrMode = kwargs.get("asmrMode", False)
keepOriginalSound = kwargs.get("keepOriginalSound", True)
sound = kwargs.get("sound", False)
characterOrientation = kwargs.get("characterOrientation", "image")
# Build settings dictionary - only include what is enabled
klingSettings: Dict[str, Any] = {}
# Add cameraControl only if useCameraControl is enabled and not "none"
if useCameraControl and cameraControl != "none":
klingSettings["cameraControl"] = cameraControl
# Add soundVolume only if useSoundVolume is enabled
if useSoundVolume:
klingSettings["soundVolume"] = float(soundVolume)
# Add originalAudioVolume only if useOriginalAudioVolume is enabled
if useOriginalAudioVolume:
klingSettings["originalAudioVolume"] = float(originalAudioVolume)
# Add soundEffectPrompt only if useSoundEffectPrompt is enabled and not empty
if useSoundEffectPrompt and soundEffectPrompt and soundEffectPrompt.strip():
klingSettings["soundEffectPrompt"] = soundEffectPrompt.strip()
# Add bgmPrompt only if useBgmPrompt is enabled and not empty
if useBgmPrompt and bgmPrompt and bgmPrompt.strip():
klingSettings["bgmPrompt"] = bgmPrompt.strip()
# Add asmrMode only if useAsmrMode is enabled
if useAsmrMode:
klingSettings["asmrMode"] = asmrMode
# Add keepOriginalSound only if useKeepOriginalSound is enabled
if useKeepOriginalSound:
klingSettings["keepOriginalSound"] = keepOriginalSound
# Add sound only if useSound is enabled
if useSound:
klingSettings["sound"] = sound
# Add characterOrientation only if useCharacterOrientation is enabled
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, 7):
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}
# Return flat dictionary - will be wrapped by inference node with provider name (same pattern as video provider settings)
return (klingSettings,)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareKlingProviderSettings": RunwareKlingProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareKlingProviderSettings": "Runware KlingAI Provider Settings",
}
@@ -1,91 +0,0 @@
"""
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",
}),
"MultiPrompt Segment 6": ("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, 7):
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",
}
@@ -1,102 +0,0 @@
"""
Runware Lightricks Provider Settings Node
Provides Lightricks-specific settings for video generation
"""
from typing import Dict, Any
class RunwareLightricksProviderSettings:
"""Runware Lightricks Provider Settings Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useGenerateAudio": ("BOOLEAN", {
"tooltip": "Enable to override the audio generation flag. Disable to keep the default behavior.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"generateAudio": ("BOOLEAN", {
"tooltip": "Enable to generate audio for the video. Disable to generate video without audio. Default: false.",
"default": False,
}),
"useStartTime": ("BOOLEAN", {
"tooltip": "Enable to include a custom start time for the replacement segment.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"startTime": ("INT", {
"tooltip": "Start time (seconds) where the replacement should begin.",
"default": 0,
"min": 0,
}),
"useDuration": ("BOOLEAN", {
"tooltip": "Enable to include a custom duration for the replacement segment.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"duration": ("INT", {
"tooltip": "Duration (seconds) for the replacement segment.",
"default": 5,
"min": 1,
}),
"useMode": ("BOOLEAN", {
"tooltip": "Enable to select what parts of the video should be replaced.",
"default": False,
"label_on": "Enabled",
"label_off": "Default",
}),
"mode": (["replace_audio", "replace_video", "replace_audio_and_video"], {
"tooltip": "Choose what to replace in the video task.",
"default": "replace_audio",
}),
}
}
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("providerSettings",)
FUNCTION = "create_provider_settings"
CATEGORY = "Runware/Provider Settings"
def create_provider_settings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create Lightricks provider settings"""
# Get value parameters
generateAudio = kwargs.get("generateAudio", False)
useStartTime = kwargs.get("useStartTime", False)
useDuration = kwargs.get("useDuration", False)
useMode = kwargs.get("useMode", False)
useGenerateAudio = kwargs.get("useGenerateAudio", False)
startTime = kwargs.get("startTime", 0)
duration = kwargs.get("duration", 5)
mode = kwargs.get("mode", "replace_audio")
# Build settings dictionary
# Always include generateAudio to ensure it's explicitly set
settings = {}
if useGenerateAudio:
settings["generateAudio"] = generateAudio
if useStartTime:
settings["startTime"] = startTime
if useDuration:
settings["duration"] = duration
if useMode:
settings["mode"] = mode
return (settings,)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareLightricksProviderSettings": RunwareLightricksProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareLightricksProviderSettings": "Runware Lightricks Provider Settings",
}
@@ -1,146 +0,0 @@
from typing import Any, Dict, List
class RunwareLumaProviderSettings:
"""Provider settings node for LumaAI video generation parameters."""
CONCEPT_OPTIONS = [
"",
"aerial",
"aerial_drone",
"bolt_cam",
"crane_down",
"crane_up",
"dolly_zoom",
"elevator_doors",
"eye_level",
"ground_level",
"handheld",
"high_angle",
"low_angle",
"orbit_left",
"orbit_right",
"over_the_shoulder",
"overhead",
"pan_left",
"pan_right",
"pedestal_down",
"pedestal_up",
"pov",
"pull_out",
"push_in",
"roll_left",
"roll_right",
"selfie",
"static",
"tilt_down",
"tilt_up",
"tiny_planet",
"truck_left",
"truck_right",
"zoom_in",
"zoom_out",
]
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useLoop": ("BOOLEAN", {
"tooltip": "Enable to include the loop parameter in provider settings.",
"default": False,
}),
"loop": ("BOOLEAN", {
"tooltip": "When enabled, generated video will loop seamlessly.",
"default": False,
}),
"useConcept1": ("BOOLEAN", {
"tooltip": "Enable to include Concept 1 in the concepts list.",
"default": False,
}),
"concept1": (cls.CONCEPT_OPTIONS, {
"tooltip": "Concept key to guide the video generation (e.g., aerial, bolt_cam).",
"default": "",
}),
"useConcept2": ("BOOLEAN", {
"tooltip": "Enable to include Concept 2 in the concepts list.",
"default": False,
}),
"concept2": (cls.CONCEPT_OPTIONS, {
"tooltip": "Concept key to guide the video generation.",
"default": "",
}),
"useConcept3": ("BOOLEAN", {
"tooltip": "Enable to include Concept 3 in the concepts list.",
"default": False,
}),
"concept3": (cls.CONCEPT_OPTIONS, {
"tooltip": "Concept key to guide the video generation.",
"default": "",
}),
"useConcept4": ("BOOLEAN", {
"tooltip": "Enable to include Concept 4 in the concepts list.",
"default": False,
}),
"concept4": (cls.CONCEPT_OPTIONS, {
"tooltip": "Concept key to guide the video generation.",
"default": "",
}),
}
}
DESCRIPTION = "Configure LumaAI provider settings including loop and concept guidance."
FUNCTION = "createProviderSettings"
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("Provider Settings",)
CATEGORY = "Runware/Provider Settings"
def createProviderSettings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Build LumaAI provider settings payload."""
useLoop = kwargs.get("useLoop", False)
loop = kwargs.get("loop", False)
concepts = self._collectConcepts(**kwargs)
lumaSettings: Dict[str, Any] = {}
if useLoop:
lumaSettings["loop"] = bool(loop)
if concepts:
lumaSettings["concepts"] = concepts
result = {}
if lumaSettings:
result["lumaai"] = lumaSettings
return (result if result else None,)
def _collectConcepts(self, **kwargs) -> List[Dict[str, str]]:
"""Collect concept entries from kwargs respecting use toggles."""
concepts: List[Dict[str, str]] = []
for index in range(1, 5):
use_key = f"useConcept{index}"
concept_key = f"concept{index}"
if kwargs.get(use_key, False):
concept_value = kwargs.get(concept_key, "")
if isinstance(concept_value, str):
normalized = concept_value.strip().lower()
if normalized != "":
concepts.append({"key": normalized})
return concepts
NODE_CLASS_MAPPINGS = {
"RunwareLumaProviderSettings": RunwareLumaProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareLumaProviderSettings": "Runware Luma Provider Settings",
}
@@ -1,120 +0,0 @@
"""
Runware Midjourney Provider Settings Node
Provides Midjourney-specific settings for image generation workflows.
"""
from typing import Any, Dict, Optional, Tuple
class RunwareMidjourneyProviderSettings:
"""Runware Midjourney Provider Settings Node"""
QUALITY_STEP = 0.25
QUALITY_MIN = 0.25
QUALITY_MAX = 2.0
DEFAULT_QUALITY = 1.0
DEFAULT_STYLIZE = 0
STYLIZE_MIN = 0
STYLIZE_MAX = 1000
DEFAULT_CHAOS = 0
CHAOS_MIN = 0
CHAOS_MAX = 100
DEFAULT_WEIRD = 0
WEIRD_MIN = 0
WEIRD_MAX = 3000
NIJI_OPTIONS = [
"", # Allow provider default when omitted
"close",
"0",
"5",
"6",
]
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"quality": ("FLOAT", {
"tooltip": "Midjourney quality multiplier (provider key: providerSettings.midjourney.quality).",
"default": cls.DEFAULT_QUALITY,
"min": cls.QUALITY_MIN,
"max": cls.QUALITY_MAX,
"step": cls.QUALITY_STEP,
}),
"stylize": ("INT", {
"tooltip": "Controls how strongly Midjourney's artistic style is applied (provider key: providerSettings.midjourney.stylize).",
"default": cls.DEFAULT_STYLIZE,
"min": cls.STYLIZE_MIN,
"max": cls.STYLIZE_MAX,
}),
"chaos": ("INT", {
"tooltip": "Controls variation and unpredictability (provider key: providerSettings.midjourney.chaos).",
"default": cls.DEFAULT_CHAOS,
"min": cls.CHAOS_MIN,
"max": cls.CHAOS_MAX,
}),
"weird": ("INT", {
"tooltip": "Adds surreal, experimental characteristics (provider key: providerSettings.midjourney.weird).",
"default": cls.DEFAULT_WEIRD,
"min": cls.WEIRD_MIN,
"max": cls.WEIRD_MAX,
}),
"niji": (cls.NIJI_OPTIONS, {
"tooltip": "Select the Midjourney anime-focused rendering engine (provider key: providerSettings.midjourney.niji).",
"default": "close",
}),
}
}
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("Provider Settings",)
FUNCTION = "create_provider_settings"
CATEGORY = "Runware/Provider Settings"
DESCRIPTION = "Configure Midjourney-specific provider settings such as quality, stylize, chaos, weird, and niji options for image generation."
def create_provider_settings(self, **kwargs) -> Tuple[Optional[Dict[str, Any]],]:
"""Build Midjourney provider settings payload."""
quality = kwargs.get("quality", self.DEFAULT_QUALITY)
stylize = kwargs.get("stylize", self.DEFAULT_STYLIZE)
chaos = kwargs.get("chaos", self.DEFAULT_CHAOS)
weird = kwargs.get("weird", self.DEFAULT_WEIRD)
niji = kwargs.get("niji", "close")
settings: Dict[str, Any] = {}
if quality is not None:
settings["quality"] = float(quality)
if stylize is not None:
settings["stylize"] = int(stylize)
if chaos is not None:
settings["chaos"] = int(chaos)
if weird is not None:
settings["weird"] = int(weird)
if isinstance(niji, str) and niji.strip():
settings["niji"] = niji.strip()
# Clean None values so the inference request only includes populated options.
settings = {k: v for k, v in settings.items() if v is not None}
return (settings if settings else None,)
NODE_CLASS_MAPPINGS = {
"RunwareMidjourneyProviderSettings": RunwareMidjourneyProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareMidjourneyProviderSettings": "Runware Midjourney Provider Settings",
}
@@ -1,50 +0,0 @@
"""
Runware MiniMax Provider Settings Node
Provides MiniMax-specific settings for video generation
"""
from typing import Dict, Any
class RunwareMiniMaxProviderSettings:
"""Runware MiniMax Provider Settings Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"promptOptimizer": ("BOOLEAN", {
"tooltip": "Enable/disable prompt optimization.",
"default": False,
}),
}
}
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("providerSettings",)
FUNCTION = "create_provider_settings"
CATEGORY = "Runware/Provider Settings"
def create_provider_settings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create MiniMax provider settings"""
# Get value parameters
promptOptimizer = kwargs.get("promptOptimizer", False)
# Build settings dictionary
# Always include promptOptimizer to ensure it's explicitly set
settings = {
"promptOptimizer": promptOptimizer
}
return (settings,)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareMiniMaxProviderSettings": RunwareMiniMaxProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareMiniMaxProviderSettings": "Runware MiniMax Provider Settings",
}
@@ -1,67 +0,0 @@
"""
Runware Mirelo Provider Settings Node
Provides Mirelo-specific settings for audio generation
"""
from typing import Dict, Any
class RunwareMireloProviderSettings:
"""Runware Mirelo Provider Settings Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useStartOffset": ("BOOLEAN", {
"tooltip": "Enable to include startOffset parameter in provider settings",
"default": False,
}),
"startOffset": ("INT", {
"tooltip": "Start offset in seconds for audio generation. Specifies the starting point in the video (0-10 seconds). Only used when 'Use Start Offset' is enabled.",
"default": 0,
"min": 0,
"max": 10,
}),
}
}
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("Provider Settings",)
FUNCTION = "createProviderSettings"
CATEGORY = "Runware/Provider Settings"
DESCRIPTION = "Configure Mirelo-specific provider settings for audio generation including start offset for video-to-audio conversion."
def createProviderSettings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create Mirelo provider settings"""
# Get control parameters
useStartOffset = kwargs.get("useStartOffset", False)
# Get value parameters
startOffset = kwargs.get("startOffset", 0)
# Build settings dictionary - only include what is enabled
mireloSettings: Dict[str, Any] = {}
# Add startOffset only if useStartOffset is enabled
if useStartOffset:
mireloSettings["startOffset"] = startOffset
# Clean up None values
mireloSettings = {k: v for k, v in mireloSettings.items() if v is not None}
# Return flat dictionary - will be wrapped by inference node with provider name
return (mireloSettings,)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareMireloProviderSettings": RunwareMireloProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareMireloProviderSettings": "Runware Mirelo Provider Settings",
}
@@ -1,68 +0,0 @@
class RunwareOpenAIProviderSettings:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"quality": (["auto", "high", "medium", "low"], {
"default": "auto",
"tooltip": "Quality setting for OpenAI DALL-E models.",
}),
"useBackground": (["enable", "disabled"], {
"default": "disabled",
"tooltip": "Enable to include background parameter in API request.",
}),
"background": (["transparent", "opaque"], {
"default": "opaque",
"tooltip": "Background setting for OpenAI image generation. Only used when 'Use Background' is enabled.",
}),
"useStyle": (["enable", "disabled"], {
"default": "disabled",
"tooltip": "Enable to include style parameter in API request.",
}),
"style": (["vivid", "natural"], {
"default": "vivid",
"tooltip": "Style setting for OpenAI DALL-E models. Only used when 'Use Style' is enabled.",
}),
}
}
DESCRIPTION = "Configure OpenAI-specific settings for image generation with quality, background, and style options."
FUNCTION = "create_openai_settings"
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("Provider Settings",)
CATEGORY = "Runware"
def create_openai_settings(self, **kwargs):
quality = kwargs.get("quality", "auto")
useBackground = kwargs.get("useBackground", "disabled")
background = kwargs.get("background", "opaque")
useStyle = kwargs.get("useStyle", "disabled")
style = kwargs.get("style", "vivid")
# Create OpenAI settings dictionary
openai_settings = {
"quality": quality,
}
# Add background only if enabled
if useBackground == "enable":
openai_settings["background"] = background
# Add style only if enabled
if useStyle == "enable":
openai_settings["style"] = style
print(f"[OpenAI Provider Settings] Created settings: {openai_settings}")
return (openai_settings,)
# Node class mappings for ComfyUI registration
NODE_CLASS_MAPPINGS = {
"RunwareOpenAIProviderSettings": RunwareOpenAIProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareOpenAIProviderSettings": "Runware OpenAI Provider Settings",
}
@@ -1,148 +0,0 @@
class RunwarePixverseProviderSettings:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"useEffect": ("BOOLEAN", {
"tooltip": "Enable to include effect parameter in API request.",
"default": False,
}),
"effect": (["none", "boom drop", "zoom in", "zoom out", "pan left", "pan right", "tilt up", "tilt down"], {
"default": "none",
"tooltip": "Camera effect for Pixverse videos. Only used when 'Use Effect' is enabled.",
}),
"useCameraMovement": ("BOOLEAN", {
"tooltip": "Enable to include cameraMovement parameter in API request.",
"default": False,
}),
"cameraMovement": (["none", "static", "dynamic", "smooth"], {
"default": "none",
"tooltip": "Camera movement style for Pixverse videos. Only used when 'Use Camera Movement' is enabled.",
}),
"useStyle": ("BOOLEAN", {
"tooltip": "Enable to include style parameter in API request.",
"default": False,
}),
"style": (["anime", "realistic", "cinematic", "artistic", "vintage", "modern"], {
"default": "realistic",
"tooltip": "Visual style for Pixverse videos. Only used when 'Use Style' is enabled.",
}),
"useMotionMode": ("BOOLEAN", {
"tooltip": "Enable to include motionMode parameter in API request.",
"default": False,
}),
"motionMode": (["normal", "slow", "fast", "smooth", "dynamic"], {
"default": "normal",
"tooltip": "Motion mode for Pixverse videos. Only used when 'Use Motion Mode' is enabled.",
}),
"useSoundEffectSwitch": ("BOOLEAN", {
"tooltip": "Enable to include soundEffectSwitch parameter in API request.",
"default": False,
}),
"soundEffectSwitch": ("BOOLEAN", {
"default": False,
"tooltip": "Enable or disable sound effects for Pixverse videos. Only used when 'Use Sound Effect Switch' is enabled.",
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useSoundEffectContent": ("BOOLEAN", {
"tooltip": "Enable to include soundEffectContent parameter in API request.",
"default": False,
}),
"soundEffectContent": ("STRING", {
"multiline": True,
"placeholder": "Sound effect description or content",
"tooltip": "Sound effect content for Pixverse videos. Only used when 'Use Sound Effect Content' is enabled.",
}),
"useAudio": ("BOOLEAN", {
"tooltip": "Enable to include audio parameter in API request.",
"default": False,
}),
"audio": ("BOOLEAN", {
"tooltip": "Controls audio generation for Pixverse videos. Only used when 'Use Audio' is enabled.",
"default": False,
}),
"useMultiClip": ("BOOLEAN", {
"tooltip": "Enable to include multiClip parameter in API request.",
"default": False,
}),
"multiClip": ("BOOLEAN", {
"tooltip": "Multi-clip (Multi-shot) for Pixverse videos. Only used when 'Use Multi Clip' is enabled.",
"default": False,
}),
"useThinking": ("BOOLEAN", {
"tooltip": "Enable to include thinking parameter in API request.",
"default": False,
}),
"thinking": (["enabled", "disabled", "auto"], {
"tooltip": "Thinking option for Pixverse videos. Only used when 'Use Thinking' is enabled.",
"default": "auto",
}),
}
}
DESCRIPTION = "Configure Pixverse-specific settings for video generation with effect, style, motion, and sound options."
FUNCTION = "create_pixverse_settings"
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("Provider Settings",)
CATEGORY = "Runware"
def create_pixverse_settings(self, **kwargs):
useEffect = kwargs.get("useEffect", False)
effect = kwargs.get("effect", "none")
useCameraMovement = kwargs.get("useCameraMovement", False)
cameraMovement = kwargs.get("cameraMovement", "none")
useStyle = kwargs.get("useStyle", False)
style = kwargs.get("style", "realistic")
useMotionMode = kwargs.get("useMotionMode", False)
motionMode = kwargs.get("motionMode", "normal")
useSoundEffectSwitch = kwargs.get("useSoundEffectSwitch", False)
soundEffectSwitch = kwargs.get("soundEffectSwitch", False)
useSoundEffectContent = kwargs.get("useSoundEffectContent", False)
soundEffectContent = kwargs.get("soundEffectContent", "")
useAudio = kwargs.get("useAudio", False)
audio = kwargs.get("audio", False)
useMultiClip = kwargs.get("useMultiClip", False)
multiClip = kwargs.get("multiClip", False)
useThinking = kwargs.get("useThinking", False)
thinking = kwargs.get("thinking", "auto")
# Create Pixverse settings dictionary - only include parameters if their use flags are enabled
pixverse_settings = {}
if useEffect:
pixverse_settings["effect"] = effect
if useCameraMovement:
pixverse_settings["cameraMovement"] = cameraMovement
if useStyle:
pixverse_settings["style"] = style
if useMotionMode:
pixverse_settings["motionMode"] = motionMode
if useSoundEffectSwitch:
pixverse_settings["soundEffectSwitch"] = soundEffectSwitch
if useSoundEffectContent and soundEffectContent and soundEffectContent.strip():
pixverse_settings["soundEffectContent"] = soundEffectContent
if useAudio:
pixverse_settings["audio"] = audio
if useMultiClip:
pixverse_settings["multiClip"] = multiClip
if useThinking:
pixverse_settings["thinking"] = thinking
# Remove "none" values to keep the settings clean
pixverse_settings = {k: v for k, v in pixverse_settings.items() if v != "none" and v is not None}
print(f"[Pixverse Provider Settings] Created settings: {pixverse_settings}")
return (pixverse_settings,)
# Node class mappings for ComfyUI registration
NODE_CLASS_MAPPINGS = {
"RunwarePixverseProviderSettings": RunwarePixverseProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwarePixverseProviderSettings": "Runware Pixverse Provider Settings",
}
@@ -1,68 +0,0 @@
"""
Runware Recraft Provider Settings Node
IRecraftProviderSettings: styleId, colors (List[IRecraftRGB]), backgroundColor (IRecraftRGB)
"""
from typing import Dict, Any, List
class RunwareRecraftProviderSettings:
"""Runware Recraft Provider Settings Node"""
@classmethod
def INPUT_TYPES(cls):
color_input = ("RUNWARERECRAFTCOLOR", {"tooltip": "IRecraftRGB - connect Runware Recraft Color"})
return {
"required": {},
"optional": {
"useStyleId": ("BOOLEAN", {
"tooltip": "Enable to include styleId parameter",
"default": False,
}),
"styleId": ("STRING", {
"default": "",
"tooltip": "Style ID for Recraft image generation.",
}),
"color1": color_input,
"color2": color_input,
"color3": color_input,
"color4": color_input,
"color5": color_input,
"backgroundColor": color_input,
}
}
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("Provider Settings",)
FUNCTION = "createProviderSettings"
CATEGORY = "Runware/Provider Settings"
DESCRIPTION = "IRecraftProviderSettings. Connect Runware Recraft Color nodes for colors (palette) and backgroundColor."
def createProviderSettings(self, **kwargs) -> tuple[Dict[str, Any]]:
settings: Dict[str, Any] = {}
if kwargs.get("useStyleId") and (sid := kwargs.get("styleId", "").strip()):
settings["styleId"] = sid
colors: List[Dict[str, List[int]]] = []
for i in range(1, 6):
c = kwargs.get(f"color{i}")
if c and isinstance(c, dict) and "rgb" in c:
colors.append(c)
if colors:
settings["colors"] = colors
bg = kwargs.get("backgroundColor")
if bg and isinstance(bg, dict) and "rgb" in bg:
settings["backgroundColor"] = bg
return (settings,)
NODE_CLASS_MAPPINGS = {
"RunwareRecraftProviderSettings": RunwareRecraftProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareRecraftProviderSettings": "Runware Recraft Provider Settings",
}
@@ -1,40 +0,0 @@
"""
Runware Recraft Color Node - IRecraftRGB
Outputs {rgb: [r, g, b]} for use as palette color or backgroundColor in Recraft Provider Settings
"""
from typing import Dict, List
def _clamp_rgb(value: int) -> int:
return max(0, min(255, int(value)))
class RunwareRecraftColor:
"""Outputs IRecraftRGB {rgb: [r,g,b]} - use for colors palette or backgroundColor"""
@classmethod
def INPUT_TYPES(cls):
rgb = ("INT", {"default": 128, "min": 0, "max": 255})
return {
"required": {},
"optional": {"r": rgb, "g": rgb, "b": rgb}
}
RETURN_TYPES = ("RUNWARERECRAFTCOLOR",)
RETURN_NAMES = ("Color",)
FUNCTION = "create"
CATEGORY = "Runware/Provider Settings"
DESCRIPTION = "IRecraftRGB - use for palette (colors) or backgroundColor. Connect to Runware Recraft Provider Settings."
def create(self, r: int = 128, g: int = 128, b: int = 128) -> tuple[Dict[str, List[int]]]:
return ({"rgb": [_clamp_rgb(r), _clamp_rgb(g), _clamp_rgb(b)]},)
NODE_CLASS_MAPPINGS = {
"RunwareRecraftColor": RunwareRecraftColor,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareRecraftColor": "Runware Provider Settings Recraft Color",
}
@@ -1,56 +0,0 @@
"""
Runware Runway Provider Settings Node
Provides Runway-specific settings for video generation
"""
from typing import Dict, Any
class RunwareRunwayProviderSettings:
"""Runware Runway Provider Settings Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"publicFigureThreshold": (["auto", "low"], {
"tooltip": "Select the public figure detection threshold (provider key: contentModeration.publicFigureThreshold).",
"default": "auto",
}),
}
}
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("Provider Settings",)
FUNCTION = "createProviderSettings"
CATEGORY = "Runware"
DESCRIPTION = "Configure Runway-specific provider settings for video generation including content moderation options."
def createProviderSettings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create Runway provider settings"""
publicFigureThreshold = kwargs.get("publicFigureThreshold")
runwaySettings: Dict[str, Any] = {}
if isinstance(publicFigureThreshold, str) and publicFigureThreshold.strip() != "":
contentModeration: Dict[str, Any] = {}
contentModeration["publicFigureThreshold"] = publicFigureThreshold.strip()
runwaySettings["contentModeration"] = contentModeration
# Remove unset values
runwaySettings = {k: v for k, v in runwaySettings.items() if v is not None}
return (runwaySettings,)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareRunwayProviderSettings": RunwareRunwayProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareRunwayProviderSettings": "Runware Runway Provider Settings",
}
@@ -1,72 +0,0 @@
"""
Runware Sourceful Provider Settings Node
Provides Sourceful-specific settings for image generation (providerSettings.sourceful.*)
"""
from typing import Dict, Any
class RunwareSourcefulProviderSettings:
"""Runware Sourceful Provider Settings Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useTransparency": ("BOOLEAN", {
"tooltip": "Enable to include transparency parameter in API request",
"default": False,
}),
"transparency": ("BOOLEAN", {
"tooltip": "Transparency setting for image generation. Only used when 'Use Transparency' is enabled.",
"default": False,
}),
"useEnhancePrompt": ("BOOLEAN", {
"tooltip": "Enable to include enhancePrompt parameter in API request",
"default": False,
}),
"enhancePrompt": ("BOOLEAN", {
"tooltip": "When true, enhances the provided prompt. Only used when 'Use Enhance Prompt' is enabled.",
"default": True,
}),
"fontInputs": ("RUNWARESOURCEFULFONTINPUTS", {
"tooltip": "Connect Runware Sourceful Provider Settings Fonts node to provide font references (fontUrl and text). Up to 2 fonts.",
}),
}
}
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("providerSettings",)
FUNCTION = "create_provider_settings"
CATEGORY = "Runware/Provider Settings"
DESCRIPTION = "Configure Sourceful-specific provider settings for image generation: transparency, enhancePrompt, and font inputs."
def create_provider_settings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create Sourceful provider settings"""
useTransparency = kwargs.get("useTransparency", False)
transparency = kwargs.get("transparency", False)
useEnhancePrompt = kwargs.get("useEnhancePrompt", False)
enhancePrompt = kwargs.get("enhancePrompt", True)
font_inputs = kwargs.get("fontInputs", None)
settings: Dict[str, Any] = {}
if useTransparency:
settings["transparency"] = transparency
if useEnhancePrompt:
settings["enhancePrompt"] = enhancePrompt
if font_inputs is not None and isinstance(font_inputs, list) and len(font_inputs) > 0:
settings["fontInputs"] = font_inputs
return (settings,)
NODE_CLASS_MAPPINGS = {
"RunwareSourcefulProviderSettings": RunwareSourcefulProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareSourcefulProviderSettings": "Runware Sourceful Provider Settings",
}
@@ -1,76 +0,0 @@
"""
Runware Sourceful Provider Settings Fonts Node
Creates font inputs array for Sourceful provider (up to 2 font references with fontUrl and text).
"""
from typing import Dict, List
class RunwareSourcefulProviderSettingsFonts:
"""Runware Sourceful Provider Settings Fonts Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useFont1": ("BOOLEAN", {
"tooltip": "Enable to include first font reference in fontInputs array",
"default": False,
}),
"fontUrl1": ("STRING", {
"tooltip": "URL of the font file (e.g. .woff2). Only used when 'Use Font 1' is enabled.",
"default": "",
}),
"text1": ("STRING", {
"multiline": True,
"tooltip": "Text to render with the first font. Only used when 'Use Font 1' is enabled.",
"default": "",
}),
"useFont2": ("BOOLEAN", {
"tooltip": "Enable to include second font reference in fontInputs array",
"default": False,
}),
"fontUrl2": ("STRING", {
"tooltip": "URL of the font file (e.g. .woff2). Only used when 'Use Font 2' is enabled.",
"default": "",
}),
"text2": ("STRING", {
"multiline": True,
"tooltip": "Text to render with the second font. Only used when 'Use Font 2' is enabled.",
"default": "",
}),
}
}
RETURN_TYPES = ("RUNWARESOURCEFULFONTINPUTS",)
RETURN_NAMES = ("Font Inputs",)
FUNCTION = "create_font_inputs"
CATEGORY = "Runware/Provider Settings"
DESCRIPTION = "Create font inputs for Sourceful provider. Up to 2 font references, each with font URL and text to render."
def create_font_inputs(self, **kwargs) -> tuple[List[Dict[str, str]]]:
"""Create font inputs array"""
font_inputs: List[Dict[str, str]] = []
for i in range(1, 3):
use_font = kwargs.get(f"useFont{i}", False)
if use_font:
font_url = (kwargs.get(f"fontUrl{i}", "") or "").strip()
text = (kwargs.get(f"text{i}", "") or "").strip()
if font_url and text:
font_inputs.append({
"fontUrl": font_url,
"text": text,
})
return (font_inputs,)
NODE_CLASS_MAPPINGS = {
"RunwareSourcefulProviderSettingsFonts": RunwareSourcefulProviderSettingsFonts,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareSourcefulProviderSettingsFonts": "Runware Sourceful Provider Settings Fonts",
}
@@ -1,157 +0,0 @@
"""
Runware Sync Provider Settings Node
Provides Sync-specific settings for video generation with lip-sync capabilities
"""
from typing import Dict, Any, List
class RunwareSyncProviderSettings:
"""Runware Sync Provider Settings Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"syncMode": (["bounce", "loop", "cut_off", "silence", "remap"], {
"tooltip": "Synchronization strategy. Enum: bounce, loop, cut_off, silence, remap",
"default": "bounce",
}),
},
"optional": {
"useEditRegion": ("BOOLEAN", {
"tooltip": "Enable to include editRegion parameter",
"default": False,
}),
"editRegion": (["lips", "face", "head"], {
"tooltip": "Edit region for the model. Only works with react-1. Defaults to face, which affects lipsync + emotions in the face region. When head is selected, model generates natural talking head movements along with emotions + lipsync. Only used when 'Use Edit Region' is enabled.",
"default": "face",
}),
"useEmotionPrompt": ("BOOLEAN", {
"tooltip": "Enable to include emotionPrompt parameter",
"default": False,
}),
"emotionPrompt": (["happy", "sad", "angry", "disgusted", "surprised", "neutral"], {
"tooltip": "Emotion prompt for the generation. Only works for react-1 model. Only single word emotions are supported. Only used when 'Use Emotion Prompt' is enabled.",
"default": "happy",
}),
"useTemperature": ("BOOLEAN", {
"tooltip": "Enable to include temperature parameter",
"default": False,
}),
"temperature": ("FLOAT", {
"tooltip": "Sampling temperature. 0 -> least expressive, 1 -> most expressive. Only used when 'Use Temperature' is enabled.",
"default": 0.5,
"min": 0.0,
"max": 1.0,
"step": 0.1,
}),
"useActiveSpeakerDetection": ("BOOLEAN", {
"tooltip": "Enable to include activeSpeakerDetection parameter",
"default": False,
}),
"activeSpeakerDetection": ("BOOLEAN", {
"tooltip": "Enable active speaker detection. Only used when 'Use Active Speaker Detection' is enabled.",
"default": True,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useOcclusionDetectionEnabled": ("BOOLEAN", {
"tooltip": "Enable to include occlusionDetectionEnabled parameter",
"default": False,
}),
"occlusionDetectionEnabled": ("BOOLEAN", {
"tooltip": "Enable occlusion detection. Only used when 'Use Occlusion Detection Enabled' is enabled.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"Sync Segment 1": ("RUNWARESYNCSEGMENT", {
"tooltip": "Connect a Runware Sync Segment node (optional)",
}),
"Sync Segment 2": ("RUNWARESYNCSEGMENT", {
"tooltip": "Connect a Runware Sync Segment node (optional)",
}),
"Sync Segment 3": ("RUNWARESYNCSEGMENT", {
"tooltip": "Connect a Runware Sync Segment node (optional)",
}),
"Sync Segment 4": ("RUNWARESYNCSEGMENT", {
"tooltip": "Connect a Runware Sync Segment node (optional)",
}),
}
}
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("Provider Settings",)
FUNCTION = "createProviderSettings"
CATEGORY = "Runware/Provider Settings"
DESCRIPTION = "Configure Sync-specific provider settings for video generation including lip-sync, emotion prompts, and segment configurations."
def createProviderSettings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create Sync provider settings"""
# Get required parameters
syncMode = kwargs.get("syncMode", "bounce")
# Get control parameters
useEditRegion = kwargs.get("useEditRegion", False)
useEmotionPrompt = kwargs.get("useEmotionPrompt", False)
useTemperature = kwargs.get("useTemperature", False)
useActiveSpeakerDetection = kwargs.get("useActiveSpeakerDetection", False)
useOcclusionDetectionEnabled = kwargs.get("useOcclusionDetectionEnabled", False)
# Get value parameters
editRegion = kwargs.get("editRegion", "face")
emotionPrompt = kwargs.get("emotionPrompt", "happy")
temperature = kwargs.get("temperature", 0.5)
activeSpeakerDetection = kwargs.get("activeSpeakerDetection", True)
occlusionDetectionEnabled = kwargs.get("occlusionDetectionEnabled", False)
# Get sync segments (up to 4)
segments: List[Dict[str, Any]] = []
for i in range(1, 5):
segmentKey = f"Sync Segment {i}"
segment = kwargs.get(segmentKey)
if segment is not None and isinstance(segment, dict):
segments.append(segment)
# Build settings dictionary
syncSettings: Dict[str, Any] = {
"syncMode": syncMode,
}
# Add optional parameters only if enabled
if useEditRegion:
syncSettings["editRegion"] = editRegion
if useEmotionPrompt:
syncSettings["emotionPrompt"] = emotionPrompt
if useTemperature:
syncSettings["temperature"] = float(temperature)
if useActiveSpeakerDetection:
syncSettings["activeSpeakerDetection"] = activeSpeakerDetection
if useOcclusionDetectionEnabled:
syncSettings["occlusionDetectionEnabled"] = occlusionDetectionEnabled
# Add segments if any are provided
if segments:
syncSettings["segments"] = segments
# Clean up None values
syncSettings = {k: v for k, v in syncSettings.items() if v is not None}
# Return settings wrapped with provider key "sync"
if syncSettings:
return ({"sync": syncSettings},)
else:
return ({},)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareSyncProviderSettings": RunwareSyncProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareSyncProviderSettings": "Runware Sync Provider Settings",
}
-104
View File
@@ -1,104 +0,0 @@
"""
Runware Sync Segment Node
Creates a sync segment configuration for sync provider settings
"""
from typing import Dict, Any
class RunwareSyncSegment:
"""Runware Sync Segment Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"startTime": ("FLOAT", {
"tooltip": "Start time of the segment in seconds",
"default": 0.0,
"min": 0.0,
"step": 0.1,
}),
"endTime": ("FLOAT", {
"tooltip": "End time of the segment in seconds",
"default": 1.0,
"min": 0.0,
"step": 0.1,
}),
"ref": ("STRING", {
"tooltip": "Reference identifier for the audio track (e.g., 'audio-track-1', 'speech-track-1')",
"default": "audio-track-1",
}),
},
"optional": {
"useAudioStartTime": ("BOOLEAN", {
"tooltip": "Enable to include audioStartTime parameter",
"default": False,
}),
"audioStartTime": ("FLOAT", {
"tooltip": "Start time in the audio track in seconds. Only used when 'Use Audio Start Time' is enabled.",
"default": 0.0,
"min": 0.0,
"step": 0.1,
}),
"useAudioEndTime": ("BOOLEAN", {
"tooltip": "Enable to include audioEndTime parameter",
"default": False,
}),
"audioEndTime": ("FLOAT", {
"tooltip": "End time in the audio track in seconds. Only used when 'Use Audio End Time' is enabled.",
"default": 1.0,
"min": 0.0,
"step": 0.1,
}),
}
}
RETURN_TYPES = ("RUNWARESYNCSEGMENT",)
RETURN_NAMES = ("Sync Segment",)
FUNCTION = "createSyncSegment"
CATEGORY = "Runware/Provider Settings"
DESCRIPTION = "Create a sync segment configuration for sync provider settings."
def createSyncSegment(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create sync segment configuration"""
# Get required parameters
startTime = kwargs.get("startTime", 0.0)
endTime = kwargs.get("endTime", 1.0)
ref = kwargs.get("ref", "audio-track-1")
# Get optional parameters
useAudioStartTime = kwargs.get("useAudioStartTime", False)
audioStartTime = kwargs.get("audioStartTime", 0.0)
useAudioEndTime = kwargs.get("useAudioEndTime", False)
audioEndTime = kwargs.get("audioEndTime", 1.0)
# Build segment dictionary
segment: Dict[str, Any] = {
"startTime": float(startTime),
"endTime": float(endTime),
"ref": str(ref),
}
# Add optional audio timing parameters
if useAudioStartTime:
segment["audioStartTime"] = float(audioStartTime)
if useAudioEndTime:
segment["audioEndTime"] = float(audioEndTime)
# Clean up None values
segment = {k: v for k, v in segment.items() if v is not None}
return (segment,)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareSyncSegment": RunwareSyncSegment,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareSyncSegment": "Runware Sync Segment",
}
@@ -1,142 +0,0 @@
"""
Runware Image Inference Ultralytics Node
Provides Ultralytics-specific settings for image generation (providerSettings.ultralytics.*)
"""
from typing import Dict, Any
class RunwareUltralyticsProviderSettings:
"""Runware Image Inference Ultralytics - Ultralytics provider settings"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useMaskBlur": ("BOOLEAN", {
"tooltip": "Enable to include maskBlur parameter.",
"default": False,
}),
"maskBlur": ("INT", {
"default": 5,
"min": 0,
"max": 100,
"tooltip": "Mask feathering amount (0-100).",
}),
"useMaskPadding": ("BOOLEAN", {
"tooltip": "Enable to include maskPadding parameter.",
"default": False,
}),
"maskPadding": ("INT", {
"default": 5,
"min": 0,
"max": 20,
"tooltip": "Padding around face region (0-20).",
}),
"useConfidence": ("BOOLEAN", {
"tooltip": "Enable to include confidence parameter.",
"default": False,
}),
"confidence": ("FLOAT", {
"default": 0.5,
"min": 0.0,
"max": 1.0,
"step": 0.01,
"tooltip": "Detection confidence threshold (0-1).",
}),
"usePositivePrompt": ("BOOLEAN", {
"tooltip": "Enable to include positivePrompt parameter.",
"default": False,
}),
"positivePrompt": ("STRING", {
"default": "",
"multiline": True,
"tooltip": "Prompt for diffusion model.",
}),
"useNegativePrompt": ("BOOLEAN", {
"tooltip": "Enable to include negativePrompt parameter.",
"default": False,
}),
"negativePrompt": ("STRING", {
"default": "",
"multiline": True,
"tooltip": "Negative prompt for model.",
}),
"useSteps": ("BOOLEAN", {
"tooltip": "Enable to include steps parameter.",
"default": False,
}),
"steps": ("INT", {
"default": 20,
"min": 1,
"max": 100,
"tooltip": "Number of diffusion steps (1-100).",
}),
"useCFGScale": ("BOOLEAN", {
"tooltip": "Enable to include CFGScale parameter.",
"default": False,
}),
"CFGScale": ("FLOAT", {
"default": 8.0,
"min": 0.0,
"max": 50.0,
"step": 0.1,
"tooltip": "Classifier-free guidance scale (0-50).",
}),
"useStrength": ("BOOLEAN", {
"tooltip": "Enable to include strength parameter.",
"default": False,
}),
"strength": ("FLOAT", {
"default": 0.3,
"min": 0.0001,
"max": 1.0,
"step": 0.01,
"tooltip": "Denoising strength (0.0001-1).",
}),
}
}
DESCRIPTION = "Configure Ultralytics-specific settings for Runware Image Inference. providerSettings.ultralytics.*"
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("providerSettings",)
FUNCTION = "create_provider_settings"
CATEGORY = "Runware/Provider Settings"
def create_provider_settings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create Ultralytics provider settings"""
settings = {}
if kwargs.get("useMaskBlur", False):
settings["maskBlur"] = kwargs.get("maskBlur", 5)
if kwargs.get("useMaskPadding", False):
settings["maskPadding"] = kwargs.get("maskPadding", 5)
if kwargs.get("useConfidence", False):
settings["confidence"] = kwargs.get("confidence", 0.5)
if kwargs.get("usePositivePrompt", False):
val = kwargs.get("positivePrompt", "")
if val is not None and str(val).strip():
settings["positivePrompt"] = str(val).strip()
if kwargs.get("useNegativePrompt", False):
val = kwargs.get("negativePrompt", "")
if val is not None and str(val).strip():
settings["negativePrompt"] = str(val).strip()
if kwargs.get("useSteps", False):
settings["steps"] = kwargs.get("steps", 20)
if kwargs.get("useCFGScale", False):
settings["CFGScale"] = kwargs.get("CFGScale", 8.0)
if kwargs.get("useStrength", False):
settings["strength"] = kwargs.get("strength", 0.3)
return (settings,)
NODE_CLASS_MAPPINGS = {
"RunwareUltralyticsProviderSettings": RunwareUltralyticsProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareUltralyticsProviderSettings": "Runware Image Inference Ultralytics",
}
@@ -1,118 +0,0 @@
class RunwareViduProviderSettings:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
# Audio parameter
"useAudio": ("BOOLEAN", {
"tooltip": "Enable to include audio parameter in API request",
"default": False,
}),
"audio": ("BOOLEAN", {
"tooltip": "Enable audio for video generation. Only used when 'Use Audio' is enabled.",
"default": True,
}),
# BGM parameter
"useBgm": ("BOOLEAN", {
"tooltip": "Enable to include bgm parameter in API request",
"default": False,
}),
"bgm": ("BOOLEAN", {
"tooltip": "Enable background music for video generation. Only used when 'Use BGM' is enabled.",
"default": False,
}),
# Style parameter
"useStyle": ("BOOLEAN", {
"tooltip": "Enable to include style parameter in API request",
"default": False,
}),
"style": ("STRING", {
"tooltip": "Video style configuration for Vidu models. Only used when 'Use Style' is enabled.",
"default": "",
}),
# Movement amplitude parameter
"useMovementAmplitude": ("BOOLEAN", {
"tooltip": "Enable to include movementAmplitude parameter in API request",
"default": False,
}),
"movementAmplitude": ("STRING", {
"tooltip": "Controls the amplitude/intensity of movement in the generated video. Only used when 'Use Movement Amplitude' is enabled.",
"default": "",
}),
# Template parameters
"useTemplateName": ("BOOLEAN", {
"tooltip": "Enable to include template name in API request",
"default": False,
}),
"templateName": ("STRING", {
"tooltip": "Template name for video generation. Only used when 'Use Template Name' is enabled.",
"default": "",
}),
"useTemplateArea": ("BOOLEAN", {
"tooltip": "Enable to include template area in API request",
"default": False,
}),
"templateArea": ("STRING", {
"tooltip": "Template area configuration. Only used when 'Use Template Area' is enabled.",
"default": "",
}),
"useTemplateBeast": ("BOOLEAN", {
"tooltip": "Enable to include template beast in API request",
"default": False,
}),
"templateBeast": ("STRING", {
"tooltip": "Template beast configuration. Only used when 'Use Template Beast' is enabled.",
"default": "",
}),
},
}
DESCRIPTION = "Configure Vidu-specific provider settings for video generation. Includes background music, style, movement amplitude, and template configurations."
FUNCTION = "viduProviderSettings"
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("Vidu Provider Settings",)
CATEGORY = "Runware"
def viduProviderSettings(self, **kwargs):
# Get all optional parameters
useAudio = kwargs.get("useAudio", False)
audio = kwargs.get("audio", True)
useBgm = kwargs.get("useBgm", False)
bgm = kwargs.get("bgm", False)
useStyle = kwargs.get("useStyle", False)
style = kwargs.get("style", "")
useMovementAmplitude = kwargs.get("useMovementAmplitude", False)
movementAmplitude = kwargs.get("movementAmplitude", "")
useTemplateName = kwargs.get("useTemplateName", False)
templateName = kwargs.get("templateName", "")
useTemplateArea = kwargs.get("useTemplateArea", False)
templateArea = kwargs.get("templateArea", "")
useTemplateBeast = kwargs.get("useTemplateBeast", False)
templateBeast = kwargs.get("templateBeast", "")
# Build provider settings dictionary
providerSettings = {}
if useAudio:
providerSettings["audio"] = audio
if useBgm:
providerSettings["bgm"] = bgm
if useStyle and style and style.strip():
providerSettings["style"] = style.strip()
if useMovementAmplitude and movementAmplitude and movementAmplitude.strip():
providerSettings["movementAmplitude"] = movementAmplitude.strip()
# Build template object if any template parameters are enabled and provided
template = {}
if useTemplateName and templateName and templateName.strip():
template["name"] = templateName.strip()
if useTemplateArea and templateArea and templateArea.strip():
template["area"] = templateArea.strip()
if useTemplateBeast and templateBeast and templateBeast.strip():
template["beast"] = templateBeast.strip()
if template:
providerSettings["template"] = template
return (providerSettings,)
@@ -1,54 +0,0 @@
"""
Runware xAI Provider Settings Node
Provides xAI-specific settings for image generation (providerSettings.xai.quality)
"""
from typing import Dict, Any
class RunwareXAIProviderSettings:
"""Runware xAI Provider Settings Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useQuality": ("BOOLEAN", {
"tooltip": "Enable to include quality parameter in API request.",
"default": False,
}),
"quality": (["low", "medium", "high"], {
"default": "medium",
"tooltip": "Quality of the output image. Can be low, medium, or high. Currently a no-op, reserved for future use.",
}),
}
}
DESCRIPTION = "Configure xAI-specific settings for image generation. providerSettings.xai.quality"
RETURN_TYPES = ("RUNWAREPROVIDERSETTINGS",)
RETURN_NAMES = ("providerSettings",)
FUNCTION = "create_provider_settings"
CATEGORY = "Runware/Provider Settings"
def create_provider_settings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create xAI provider settings"""
useQuality = kwargs.get("useQuality", False)
quality = kwargs.get("quality", "medium")
settings = {}
if useQuality:
settings["quality"] = quality
return (settings,)
NODE_CLASS_MAPPINGS = {
"RunwareXAIProviderSettings": RunwareXAIProviderSettings,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareXAIProviderSettings": "Runware xAI Provider Settings",
}
-48
View File
@@ -1,48 +0,0 @@
from typing import List
class referenceAudios:
"""Reference Audios node for configuring reference audio inputs"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"Audio1": ("STRING", {
"default": "",
"tooltip": "MediaUUID for the first reference audio from Runware Media Upload node.",
}),
"Audio2": ("STRING", {
"default": "",
"tooltip": "MediaUUID for the second reference audio from Runware Media Upload node.",
}),
"Audio3": ("STRING", {
"default": "",
"tooltip": "MediaUUID for the third reference audio from Runware Media Upload node.",
}),
"Audio4": ("STRING", {
"default": "",
"tooltip": "MediaUUID for the fourth reference audio from Runware Media Upload node.",
}),
}
}
DESCRIPTION = "Configure multiple reference audios (mediaUUIDs) for video inference inputs."
FUNCTION = "createReferenceAudios"
RETURN_TYPES = ("RUNWAREINPUTAUDIOS",)
RETURN_NAMES = ("Reference Audios",)
CATEGORY = "Runware"
def createReferenceAudios(self, **kwargs) -> tuple[List[str]]:
"""Create list of reference audio UUIDs from provided parameters"""
audioList = []
for i in range(1, 5):
audioKey = f"Audio{i}"
audioUuid = kwargs.get(audioKey)
if audioUuid and audioUuid.strip() != "":
audioList.append(audioUuid.strip())
return (audioList,)
-50
View File
@@ -1,50 +0,0 @@
from .utils import runwareUtils as rwUtils
class referenceImages:
"""Reference Images node for providing reference images for identity consistency"""
MAX_IMAGES = 14
@classmethod
def INPUT_TYPES(cls):
requiredInputs = {
"Image 1": ("IMAGE", {
"tooltip": "Specifies Reference Image 1 that will be used as reference for the subject. These reference images help the AI to maintain identity consistency during the generation process."
}),
}
optionalInputs = {}
for i in range(2, cls.MAX_IMAGES + 1):
ordinal = rwUtils.getOrdinal(i)
optionalInputs[f"Image {i}"] = ("IMAGE", {
"tooltip": f"Specifies {ordinal.capitalize()} Reference Image that will be used as reference for the subject. These reference images help the AI to maintain identity consistency during the generation process."
})
return {
"required": requiredInputs,
"optional": optionalInputs
}
DESCRIPTION = "Used With Runware Tasks To Provide Reference Images For The Subject. These Reference Images Help The AI To Maintain Identity Consistency During The Generation Process."
FUNCTION = "referenceImages"
RETURN_TYPES = ("RUNWAREREFERENCEIMAGES",)
RETURN_NAMES = ("Reference Images",)
CATEGORY = "Runware"
def referenceImages(self, **kwargs):
"""Collect and convert reference images to list"""
imageList = []
# Always include Image 1 (required)
image1 = kwargs.get("Image 1")
if image1 is not None:
imageList.append(rwUtils.convertTensor2IMG(image1))
# Add optional images 2 through MAX_IMAGES
for i in range(2, self.MAX_IMAGES + 1):
image = kwargs.get(f"Image {i}", None)
if image is not None:
imageList.append(rwUtils.convertTensor2IMG(image))
return (imageList,)
-79
View File
@@ -1,79 +0,0 @@
from typing import Any, Dict, List
from .utils import runwareUtils as rwUtils
class referenceVideos:
"""Reference Videos node for configuring reference video inputs"""
MAX_VIDEOS = 4
@classmethod
def INPUT_TYPES(cls):
optionalInputs = {}
for i in range(1, cls.MAX_VIDEOS + 1):
ordinal = rwUtils.getOrdinal(i)
optionalInputs[f"useVideo{i}"] = ("BOOLEAN", {
"default": False,
"tooltip": f"Enable to include the {ordinal} reference video.",
})
optionalInputs[f"Video{i}"] = ("STRING", {
"tooltip": f"MediaUUID for the {ordinal} reference video from Runware Media Upload node.",
})
optionalInputs[f"Tag{i}"] = ("STRING", {
"default": "",
"tooltip": f"Optional tag for the {ordinal} reference video (e.g., @video{i}). Must be used in the prompt.",
})
optionalInputs[f"Type{i}"] = ("STRING", {
"default": "reference",
"tooltip": (
f"Optional type for the {ordinal} reference video. "
"For example: reference or extend."
),
})
return {
"required": {},
"optional": optionalInputs
}
DESCRIPTION = (
"Configure multiple reference video inputs (mediaUUIDs) for video generation, "
"with optional tag and type per reference."
)
FUNCTION = "createReferenceVideos"
RETURN_TYPES = ("RUNWAREREFERENCEVIDEOS",)
RETURN_NAMES = ("Reference Videos",)
CATEGORY = "Runware"
def createReferenceVideos(self, **kwargs) -> tuple[List[Any]]:
"""Create list of reference video entries (string UUIDs or objects with tag/type)."""
videoList: List[Any] = []
for i in range(1, self.MAX_VIDEOS + 1):
useKey = f"useVideo{i}"
videoKey = f"Video{i}"
tagKey = f"Tag{i}"
typeKey = f"Type{i}"
useVideo = bool(kwargs.get(useKey, False))
videoUuid = kwargs.get(videoKey)
tagValue = (kwargs.get(tagKey) or "").strip()
typeValue = (kwargs.get(typeKey) or "reference").strip()
if useVideo and videoUuid and videoUuid.strip() != "":
normalized_video = videoUuid.strip()
# Backward compatible payload:
# - If tag/type are not provided, keep legacy string entry.
# - If tag or non-default type is provided, emit object entry.
if tagValue or typeValue != "reference":
entry: Dict[str, str] = {"video": normalized_video}
if tagValue:
entry["tag"] = tagValue
if typeValue:
entry["type"] = typeValue
videoList.append(entry)
else:
videoList.append(normalized_video)
return (videoList,)
-43
View File
@@ -1,43 +0,0 @@
from typing import List
from .utils import runwareUtils as rwUtils
class referenceVoices:
"""Reference Voices node for configuring reference voice inputs"""
MAX_VOICES = 4
@classmethod
def INPUT_TYPES(cls):
optionalInputs = {}
for i in range(1, cls.MAX_VOICES + 1):
ordinal = rwUtils.getOrdinal(i)
optionalInputs[f"Voice{i}"] = ("STRING", {
"tooltip": f"URL or mediaUUID for the {ordinal} reference voice from Runware Media Upload node or external URL.",
})
return {
"required": {},
"optional": optionalInputs
}
DESCRIPTION = "Configure multiple reference voice inputs (URLs or mediaUUIDs) for video generation."
FUNCTION = "createReferenceVoices"
RETURN_TYPES = ("RUNWAREREFERENCEVOICES",)
RETURN_NAMES = ("Reference Voices",)
CATEGORY = "Runware"
def createReferenceVoices(self, **kwargs) -> tuple[List[str]]:
"""Create list of reference voice URLs/UUIDs"""
voiceList = []
for i in range(1, self.MAX_VOICES + 1):
voiceKey = f"Voice{i}"
voiceUrl = kwargs.get(voiceKey)
if voiceUrl and voiceUrl.strip() != "":
voiceList.append(voiceUrl.strip())
return (voiceList,)
-56
View File
@@ -1,56 +0,0 @@
class refiner:
"""Refiner node for enhancing image quality using refiner models"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Model": ("RUNWAREMODEL", {
"tooltip": "Connect a Runware Model From Model Search Node.",
}),
"startStep": ("INT", {
"tooltip": "Represents the step number at which the refinement process begins. The initial model will generate the image up to this step, after which the refiner model takes over to enhance the result.",
"min": 0,
"max": 99,
"default": 0,
}),
"startStepPercentage": ("INT", {
"tooltip": "Represents the percentage of total steps at which the refinement process begins. The initial model will generate the image up to this percentage of steps before the refiner takes over.",
"min": 0,
"max": 99,
"default": 0
}),
},
}
DESCRIPTION = "Refiner models help create higher quality image outputs by incorporating specialized models designed to enhance image details and overall coherence. This can be particularly useful when you need results with superior quality, photorealism, or specific aesthetic refinements. (Note that refiner models are only SDXL based)"
FUNCTION = "refiner"
RETURN_TYPES = ("RUNWAREREFINER",)
CATEGORY = "Runware"
@classmethod
def VALIDATE_INPUTS(cls, startStep, startStepPercentage):
"""Validate that exactly one of startStep or startStepPercentage is provided"""
if startStep != 0 and startStepPercentage != 0:
raise Exception("Please provide either startStep or startStepPercentage, not both.")
elif startStep == 0 and startStepPercentage == 0:
raise Exception("Please provide either startStep or startStepPercentage.")
else:
return True
def refiner(self, **kwargs):
"""Create refiner configuration"""
runwareModel = kwargs.get("Model")
startStep = kwargs.get("startStep")
startStepPercentage = kwargs.get("startStepPercentage")
refinerResult = {
"model": runwareModel,
}
if startStep != 0:
refinerResult["startStep"] = startStep
else:
refinerResult["startStepPercentage"] = startStepPercentage
return (refinerResult,)
-139
View File
@@ -1,139 +0,0 @@
from .utils import runwareUtils as rwUtils
class runwareKontext:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Model": ([
"bfl:3@1 (FLUX.1 Kontext Pro)",
"bfl:4@1 (FLUX.1 Kontext Max)",
], {
"tooltip": "Select The Model You Want For Image Generation or Image Editing.",
"default": "bfl:3@1 (FLUX.1 Kontext Pro)",
}),
"positivePrompt": ("STRING", {
"multiline": True,
"placeholder": "Text instruction to guide the model on generating the image. It is usually a sentence or a paragraph that provides positive guidance for the task. This parameter is essential to shape the desired results.\n\nYou Can Press (Ctrl + Alt + E) To Enhance The Prompt!",
"tooltip": "Text instruction to guide the model on generating the image. You Can Also Press (Ctrl + Alt + E) To Enhance The Prompt!"
}),
"Prompt Upsampling": ("BOOLEAN", {
"tooltip": "If Enabled it Will automatically modify the prompt at generation time for more creative results.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"Aspect Ratio": ([
"21:9 (Ultra-Wide / Landscape)",
"16:9 (Wide / Landscape)",
"4:3 (Standard / Landscape)",
"3:2 (Classic Photo / Landscape)",
"1:1 (Square)",
"2:3 (Classic Photo / Portrait)",
"3:4 (Standard / Portrait)",
"9:16 (Vertical Video / Portrait)",
"9:21 (Ultra-Tall / Portrait)"
], {
"default": "16:9 (Wide / Landscape)",
"tooltip": "Adjust the dimensions of the generated image. This setting allows you to choose from various aspect ratios, such as 16:9 for wide images or 1:1 for square images. The width and height will be automatically adjusted based on the selected aspect ratio.",
}),
"seed": ("INT", {
"tooltip": "A value used to randomize the image generation. If you want to make images reproducible (generate the same image multiple times), you can use the same seed value. Set to 0 to auto-generate a random seed.",
"default": 0,
"min": 0,
"max": 4294967295,
}),
"Multi Inference Mode": ("BOOLEAN", {
"tooltip": "If Enabled the node will skip the image generation process and will only return the Runware Task Object to be used in the Multi Inference Node.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"batchSize": ("INT", {
"tooltip": "The number of images to generate in a single request.",
"default": 1,
"min": 1,
"max": 10,
}),
},
"optional": {
"Reference Images": ("RUNWAREREFERENCEIMAGES", {
"tooltip": "Connect a Runware Reference Images Node to use reference images for image editing or generation.",
}),
}
}
@classmethod
def VALIDATE_INPUTS(cls, positivePrompt):
if (positivePrompt is not None and (positivePrompt == "" or len(positivePrompt) < 3 or len(positivePrompt) > 2000)):
raise Exception(
"Positive Prompt Must Be Between 3 And 2000 characters!")
return True
DESCRIPTION = "Generate And Edit Images Using Runware BFL Kontext Models."
FUNCTION = "kontextInference"
RETURN_TYPES = ("IMAGE", "RUNWARETASK")
RETURN_NAMES = ("IMAGE", "RW-Task")
CATEGORY = "Runware"
def kontextInference(self, **kwargs):
runwareModel = kwargs.get("Model")
runwareModel = runwareModel.split(" ")[0]
positivePrompt = kwargs.get("positivePrompt")
promptUpsampling = kwargs.get("Prompt Upsampling")
aspectRatio = kwargs.get("Aspect Ratio")
multiInferenceMode = kwargs.get("Multi Inference Mode", False)
referenceImages = kwargs.get("Reference Images", None)
seed = kwargs.get("seed")
batchSize = kwargs.get("batchSize", 1)
aspectRatioMap = {
"21:9": (1568, 672),
"16:9": (1392, 752),
"4:3": (1184, 880),
"3:2": (1248, 832),
"1:1": (1024, 1024),
"2:3": (832, 1248),
"3:4": (880, 1184),
"9:16": (752, 1392),
"9:21": (672, 1568),
}
secAspectRatio = aspectRatio.split(" ")[0]
width, height = aspectRatioMap.get(secAspectRatio, (1024, 1024))
genConfig = [
{
"taskType": "imageInference",
"taskUUID": rwUtils.genRandUUID(),
"positivePrompt": positivePrompt,
"height": height,
"width": width,
"model": runwareModel,
"seed": seed,
"outputType": "base64Data",
"outputFormat": rwUtils.OUTPUT_FORMAT,
"outputQuality": rwUtils.OUTPUT_QUALITY,
"numberResults": batchSize,
}
]
if (referenceImages is not None):
genConfig[0]["referenceImages"] = referenceImages
if (promptUpsampling):
genConfig[0]["providerSettings"] = {
"bfl": {
"promptUpsampling": True
}
}
# For Debugging Purposes Only
print(f"[Debugging] Task UUID: {genConfig[0]['taskUUID']}")
if (multiInferenceMode):
return (None, genConfig)
else:
genResult = rwUtils.inferenecRequest(genConfig)
images = rwUtils.convertImageB64List(genResult)
return (images, None)
-111
View File
@@ -1,111 +0,0 @@
from .utils import runwareUtils as rwUtils
class runwareImagen:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Model": ([
"google:1@1 (Imagen 3)",
"google:1@2 (Imagen 3 Fast)",
"google:2@1 (Imagen 4 Preview)",
"google:2@2 (Imagen 4 Ultra)",
"google:2@3 (Imagen 4 Fast)",
], {
"tooltip": "Select The Google Imagen Model For Image Generation.",
"default": "google:1@1 (Imagen 3)",
}),
"positivePrompt": ("STRING", {
"multiline": True,
"placeholder": "Text instruction to guide the model on generating the image. It is usually a sentence or a paragraph that provides positive guidance for the task. This parameter is essential to shape the desired results.",
"tooltip": "Text instruction to guide the model on generating the image."
}),
"Aspect Ratio": ([
"1:1 (Square)",
"9:16 (Vertical)",
"16:9 (Wide)",
"3:4 (Portrait)",
"4:3 (Landscape)"
], {
"default": "1:1 (Square)",
"tooltip": "Adjust the dimensions of the generated image. The width and height will be automatically adjusted based on the selected aspect ratio.",
}),
"seed": ("INT", {
"tooltip": "A value used to randomize the image generation. If you want to make images reproducible (generate the same image multiple times), you can use the same seed value.",
"default": 0,
"min": 1,
"max": 9223372036854776000,
}),
"Multi Inference Mode": ("BOOLEAN", {
"tooltip": "If Enabled the node will skip the image generation process and will only return the Runware Task Object to be used in the Multi Inference Node.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"batchSize": ("INT", {
"tooltip": "The number of images to generate in a single request.",
"default": 1,
"min": 1,
"max": 10,
}),
}
}
@classmethod
def VALIDATE_INPUTS(cls, positivePrompt):
if (positivePrompt is not None and (positivePrompt == "" or len(positivePrompt) < 3 or len(positivePrompt) > 2000)):
raise Exception(
"Positive Prompt Must Be Between 3 And 2000 characters!")
return True
DESCRIPTION = "Generate Images Using Google Imagen Models."
FUNCTION = "imagenInference"
RETURN_TYPES = ("IMAGE", "RUNWARETASK")
RETURN_NAMES = ("IMAGE", "RW-Task")
CATEGORY = "Runware"
def imagenInference(self, **kwargs):
runwareModel = kwargs.get("Model")
runwareModel = runwareModel.split(" ")[0]
positivePrompt = kwargs.get("positivePrompt")
aspectRatio = kwargs.get("Aspect Ratio")
multiInferenceMode = kwargs.get("Multi Inference Mode", False)
seed = kwargs.get("seed")
batchSize = kwargs.get("batchSize", 1)
aspectRatioMap = {
"1:1": (1024, 1024),
"9:16": (768, 1408),
"16:9": (1408, 768),
"3:4": (896, 1280),
"4:3": (1280, 896),
}
secAspectRatio = aspectRatio.split(" ")[0]
width, height = aspectRatioMap.get(secAspectRatio, (1024, 1024))
genConfig = [
{
"taskType": "imageInference",
"taskUUID": rwUtils.genRandUUID(),
"positivePrompt": positivePrompt,
"height": height,
"width": width,
"model": runwareModel,
"seed": seed,
"outputType": "base64Data",
"outputFormat": rwUtils.OUTPUT_FORMAT,
"outputQuality": rwUtils.OUTPUT_QUALITY,
"numberResults": batchSize
}
]
# For Debugging Purposes Only
print(f"[Debugging] Task UUID: {genConfig[0]['taskUUID']}")
if (multiInferenceMode):
return (None, genConfig)
else:
genResult = rwUtils.inferenecRequest(genConfig)
images = rwUtils.convertImageB64List(genResult)
return (images, None)
-91
View File
@@ -1,91 +0,0 @@
from typing import Dict, Any
class safetyInputs:
"""Runware Safety Inputs Node for configuring safety and content moderation settings"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useMode": ("BOOLEAN", {
"tooltip": "Enable to include mode in payload.",
"default": False,
}),
"mode": (["none", "fast", "full"], {
"tooltip": "Safety moderation mode: none (disable), fast (quick check), or full (comprehensive check).",
"default": "none",
}),
"useTolerance": ("BOOLEAN", {
"tooltip": "Enable to include tolerance in payload.",
"default": False,
}),
"tolerance": ("BOOLEAN", {
"tooltip": "Safety tolerance setting for content moderation.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useCheckInputs": ("BOOLEAN", {
"tooltip": "Enable to include checkInputs in payload.",
"default": False,
}),
"checkInputs": ("BOOLEAN", {
"tooltip": "Enable checking of input content for safety violations.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"useCheckContent": ("BOOLEAN", {
"tooltip": "Enable to include checkContent in payload.",
"default": False,
}),
"checkContent": ("BOOLEAN", {
"tooltip": "Enable checking of generated content for safety violations.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
}
}
RETURN_TYPES = ("RUNWARESAFETYINPUTS",)
RETURN_NAMES = ("Safety Inputs",)
FUNCTION = "createSafetyInputs"
CATEGORY = "Runware"
DESCRIPTION = "Configure safety and content moderation settings for Runware tasks."
def createSafetyInputs(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create safety inputs dictionary"""
useMode = kwargs.get("useMode", False)
mode = kwargs.get("mode", None)
useTolerance = kwargs.get("useTolerance", False)
tolerance = kwargs.get("tolerance", None)
useCheckInputs = kwargs.get("useCheckInputs", False)
checkInputs = kwargs.get("checkInputs", None)
useCheckContent = kwargs.get("useCheckContent", False)
checkContent = kwargs.get("checkContent", None)
safetyInputs = {}
if useMode and mode is not None:
safetyInputs["mode"] = mode
if useTolerance and tolerance is not None:
safetyInputs["tolerance"] = tolerance
if useCheckInputs and checkInputs is not None:
safetyInputs["checkInputs"] = checkInputs
if useCheckContent and checkContent is not None:
safetyInputs["checkContent"] = checkContent
return (safetyInputs,)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareSafetyInputs": safetyInputs,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareSafetyInputs": "Runware Safety Inputs",
}
-124
View File
@@ -1,124 +0,0 @@
from .utils import runwareUtils as rwUtils
import folder_paths
import os
import re
import requests
from datetime import datetime
class RunwareSave3D:
"""Runware Save 3D node for saving 3D model files from URL"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"3dObject": ("STRING", {
"tooltip": "3D file URL from Runware 3D Inference node."
}),
"filenamePrefix": ("STRING", {
"default": "ComfyUI",
"tooltip": "Prefix for the filename."
}),
},
"optional": {
"filepath": ("STRING", {
"placeholder": "Saved file path will appear here after execution.",
"tooltip": "This field is automatically populated with the saved file path after save.",
}),
},
"hidden": {"node_id": "UNIQUE_ID"},
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("filepath",)
FUNCTION = "save_3d"
OUTPUT_NODE = True
CATEGORY = "Runware"
DESCRIPTION = "Save 3D model files (GLB/PLY/FBX) from Runware 3D Inference to the output folder."
def save_3d(self, filenamePrefix="ComfyUI", **kwargs):
"""Save 3D file from URL"""
file_url = kwargs.get("3dObject", "")
if not file_url or not file_url.strip():
raise Exception("No 3D URL provided. Please connect the '3dObject' output from Runware 3D Inference node.")
file_url = file_url.strip()
# Get output directory
output_dir = folder_paths.get_output_directory()
threed_output_dir = os.path.join(output_dir, "3d_models")
os.makedirs(threed_output_dir, exist_ok=True)
# Get extension from URL (handle query parameters)
url_without_params = file_url.split('?')[0]
extension = url_without_params.split('.')[-1].lower()
if extension not in ["glb", "ply", "fbx"]:
raise Exception(f"Unsupported 3D file extension '{extension}' from URL. Expected .glb, .ply, or .fbx.")
# Sanitize filenamePrefix to prevent path traversal or arbitrary subdir creation
prefix_basename = os.path.basename(
filenamePrefix.replace(os.sep, "_").replace(os.altsep or os.sep, "_")
)
safe_prefix = re.sub(r"[^\w\-]", "", prefix_basename) or "ComfyUI"
# Generate filename with timestamp
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{safe_prefix}_{timestamp}.{extension}"
filepath = os.path.join(threed_output_dir, filename)
# Download with retry logic
max_retries = 10
retry_delays = [2, 5, 10, 15, 20]
for attempt in range(max_retries):
try:
response = requests.get(file_url, stream=True, timeout=60)
print(f"[Runware Save 3D] Download attempt {attempt + 1}: {response.status_code}")
if response.status_code == 422:
print(f"[Runware Save 3D] Server still processing, waiting...")
rwUtils.time.sleep(retry_delays[min(attempt, len(retry_delays) - 1)])
continue
if response.status_code == 502:
print(f"[Runware Save 3D] Server error (502), retrying...")
rwUtils.time.sleep(retry_delays[min(attempt, len(retry_delays) - 1)])
continue
response.raise_for_status()
with open(filepath, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
print(f"[Runware Save 3D] Successfully saved to: {filepath}")
# Send filepath to UI (like media upload mediaUUID)
rwUtils.sendSave3DFilepath(filepath, kwargs.get("node_id"))
return {"result": (filepath,), "ui": {"text": (f"Saved: {filename}",)}}
except requests.exceptions.RequestException as e:
print(f"[Runware Save 3D] Attempt {attempt + 1} failed: {e}")
if attempt < max_retries - 1:
rwUtils.time.sleep(retry_delays[min(attempt, len(retry_delays) - 1)])
continue
else:
raise Exception(f"Failed to download 3D file after {max_retries} attempts: {e}")
except Exception as e:
print(f"[Runware Save 3D] Unexpected error: {e}")
raise Exception(f"Failed to save 3D file: {e}")
raise Exception("Failed to download 3D file: max retries exceeded")
NODE_CLASS_MAPPINGS = {
"RunwareSave3D": RunwareSave3D,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareSave3D": "Runware Save 3D",
}
-110
View File
@@ -1,110 +0,0 @@
import folder_paths
import os
import io
import requests
import torch
import numpy as np
from PIL import Image
from datetime import datetime
def _is_svg_url(url: str) -> bool:
"""True if URL points to SVG (e.g. from Runware Vectorize)."""
u = (url or "").split("?")[0].lower()
return u.endswith(".svg")
def _bytes_to_image_tensor(img_data: bytes):
"""Decode image bytes to ComfyUI IMAGE tensor [1, H, W, C] float32 0-1. Fails on SVG (use placeholder for SVG)."""
pil = Image.open(io.BytesIO(img_data)).convert("RGB")
arr = np.array(pil).astype(np.float32) / 255.0
return torch.from_numpy(arr).unsqueeze(0)
def _placeholder_image_tensor():
"""Return a 1x1 RGB placeholder tensor [1, 1, 1, 3] for non-raster outputs (e.g. SVG from Vectorize)."""
arr = np.ones((1, 1, 3), dtype=np.float32) * 0.5 # grey
return torch.from_numpy(arr).unsqueeze(0)
class RunwareSaveImage:
"""Runware Save Image: download from URL(s), save/preview to disk, and forward as IMAGE for chaining."""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Images": ("STRING", {
"tooltip": "Image URL(s) from Runware Image Inference or Runware Vectorize node. For multiple images, URLs should be comma-separated. SVG URLs (e.g. from Vectorize) are saved as .svg files; a placeholder tensor is passed for the IMAGE output."
}),
"filenamePrefix": ("STRING", {
"default": "ComfyUI",
"tooltip": "Prefix for the filename."
}),
"saveImage": ("BOOLEAN", {
"default": True,
"label_on": "Enabled",
"label_off": "Disabled",
"tooltip": "If true, save to output folder. If false, download and preview only (purged when ComfyUI restarts)."
}),
}
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("IMAGE",)
FUNCTION = "save_images"
OUTPUT_NODE = True
CATEGORY = "Runware"
def save_images(self, Images, filenamePrefix="ComfyUI", saveImage=True):
"""Download image(s) from URL(s), save/preview to disk, update gallery, and return IMAGE for downstream nodes."""
image_urls = [url.strip() for url in Images.split(",") if url.strip()]
if not image_urls:
raise Exception("No images provided. Connect the 'IMAGE' output from Runware Image Inference node.")
if saveImage:
output_dir = folder_paths.get_output_directory()
os.makedirs(output_dir, exist_ok=True)
file_type = "output"
else:
output_dir = folder_paths.get_temp_directory()
file_type = "temp"
saved_files = []
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
tensors = []
for i, image_url in enumerate(image_urls):
response = requests.get(image_url, timeout=30)
response.raise_for_status()
img_data = response.content
# SVG (e.g. from Runware Vectorize) cannot be opened by PIL; save as file and use placeholder tensor
if _is_svg_url(image_url):
tensors.append(_placeholder_image_tensor())
else:
tensors.append(_bytes_to_image_tensor(img_data))
url_without_params = image_url.split("?")[0]
ext = url_without_params.split(".")[-1].lstrip(".").lower() or "png"
extension = "." + ext
if len(image_urls) > 1:
filename = f"{filenamePrefix}_{timestamp}_{i+1:03}{extension}"
else:
filename = f"{filenamePrefix}_{timestamp}{extension}"
filepath = os.path.join(output_dir, filename)
with open(filepath, "wb") as f:
f.write(img_data)
print(f"[Runware] {'Saved' if saveImage else 'Preview'}: {filepath}")
saved_files.append({
"filename": filename,
"subfolder": "",
"type": file_type
})
image_batch = torch.cat(tensors, dim=0)
return {"result": (image_batch,), "ui": {"images": saved_files}}
-37
View File
@@ -1,37 +0,0 @@
from .utils import runwareUtils as rwUtils
class RunwareSaveText:
"""Show text in the node (e.g. from Runware Text Inference) and forward it for downstream nodes."""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"text": ("STRING", {
"multiline": True,
"default": "",
"tooltip": "Connect the 'text' output from Runware Text Inference. Text is mirrored here after run.",
}),
},
"hidden": {"node_id": "UNIQUE_ID"},
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("text",)
FUNCTION = "forward_text"
OUTPUT_NODE = True
CATEGORY = "Runware"
DESCRIPTION = "Display text in the node and forward the same string to other nodes."
def forward_text(self, text, **kwargs):
if text is None:
text = ""
elif not isinstance(text, str):
text = str(text)
node_id = kwargs.get("node_id")
if node_id is not None:
rwUtils.sendSaveText(text, node_id)
return (text,)
-486
View File
@@ -1,486 +0,0 @@
"""
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 (low/medium/high/xhigh), backgroundMode (original/transparent/solid), backgroundColor,
enhancePrompt, scoringPrompt, sequential, renderingSpeed (TURBO/DEFAULT/QUALITY),
magicPrompt (AUTO/ON/OFF), autoCrop, dilatePixels, creativity (raw/low/medium/high), colorPalette,
moodboards (from Runware Image Inference Settings Moodboards),
structuredPrompt (from Runware Image Inference Settings Structured Prompt; Ideogram 4.0),
and promptEnhance (from Runware Image Inference Settings Prompt Enhance),
preserveInputSize (return output at original input resolution),
and turbo (faster generation for slightly lower quality).
"""
import json
from typing import Any, Dict, List, Optional
class RunwareSettings:
"""Runware Image Inference Settings node (display name); API registration key remains Runware Settings."""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {},
"optional": {
"useTemperature": ("BOOLEAN", {
"tooltip": "Enable to include temperature parameter in API request",
"default": False,
}),
"temperature": ("FLOAT", {
"tooltip": "Temperature value for generation. Only used when 'Use Temperature' is enabled.",
"default": 1.0,
"min": 0.0,
"max": 2.0,
"step": 0.1,
}),
"useSystemPrompt": ("BOOLEAN", {
"tooltip": "Enable to include systemPrompt parameter in API request",
"default": False,
}),
"systemPrompt": ("STRING", {
"multiline": True,
"tooltip": "System prompt for generation. Only used when 'Use System Prompt' is enabled.",
"default": "",
}),
"useTopP": ("BOOLEAN", {
"tooltip": "Enable to include topP parameter in API request",
"default": False,
}),
"topP": ("FLOAT", {
"tooltip": "Top-p (nucleus) sampling parameter. Only used when 'Use Top P' is enabled.",
"default": 1.0,
"min": 0.0,
"max": 1.0,
"step": 0.01,
}),
"useLayers": ("BOOLEAN", {
"tooltip": "Enable to include layers parameter in API request",
"default": False,
}),
"layers": ("INT", {
"tooltip": "The number of layers to generate. Only used when 'Use Layers' is enabled.",
"default": 4,
"min": 1,
"max": 10,
"step": 1,
}),
"useTrueCFGScale": ("BOOLEAN", {
"tooltip": "Enable to include true_cfg_scale parameter in API request",
"default": False,
}),
"trueCFGScale": ("FLOAT", {
"tooltip": "Guidance scale as defined in Classifier-Free Diffusion Guidance. Classifier-free guidance is enabled by setting true_cfg_scale > 1 and a provided negative_prompt. Higher guidance scale encourages to generate images that are closely linked to the text prompt, usually at the expense of lower image quality. Only used when 'Use True CFG Scale' is enabled.",
"default": 1.0,
"min": 1.0,
"step": 0.1,
}),
"useQuality": ("BOOLEAN", {
"tooltip": "Enable to include quality parameter in API request",
"default": False,
}),
"quality": (["low", "medium", "high"], {
"default": "medium",
"tooltip": "Quality of the output image. Only used when 'Use Quality' is enabled.",
}),
"usePromptExtend": ("BOOLEAN", {
"tooltip": "Enable to include promptExtend (automatic prompt rewriting/expansion) in API request.",
"default": False,
}),
"promptExtend": ("BOOLEAN", {
"tooltip": "Enables automatic prompt rewriting/expansion to improve quality. Adds 3–5s latency. Disable for detailed prompts or latency-sensitive use cases. Only used when 'Use Prompt Extend' is enabled.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
"useEditRegions": ("BOOLEAN", {
"tooltip": "Enable to include editRegions in settings (per-input-image bounding boxes for edit flows).",
"default": False,
}),
"editRegions": ("STRING", {
"multiline": True,
"tooltip": "JSON array: one entry per input image; each entry is a list of boxes [x1,y1,x2,y2]. Example: [[[0,0,12,12],[25,25,100,100]],[],[[10,10,50,50]]]",
"default": "",
}),
"useThinking": ("BOOLEAN", {
"tooltip": "Enable to include thinking (boolean) in settings.",
"default": False,
}),
"thinking": ("BOOLEAN", {
"tooltip": "Thinking / reasoning flag passed in settings. Only used when 'Use Thinking' is enabled.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
"useSequential": ("BOOLEAN", {
"tooltip": "Enable to include sequential (boolean) in settings.",
"default": False,
}),
"sequential": ("BOOLEAN", {
"tooltip": "Sequential / image-set mode flag. Only used when 'Use Sequential' is enabled.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
"colorPalette": ("RUNWAREIMAGEINFERENCECOLORPALETTE", {
"tooltip": "Connect Runware Image Inference Color Palette. When the palette node outputs at least one swatch, it is merged into settings.colorPalette.",
}),
"useThinkingLevel": ("BOOLEAN", {
"tooltip": "Enable to include thinkingLevel (string) in settings.",
"default": False,
}),
"thinkingLevel": (["high", "medium", "low", "xhigh"], {
"default": "high",
"tooltip": (
"Reasoning level for visual understanding. Riverflow 2.5 also supports xhigh (Pro only). "
"Only used when 'Use Thinking Level' is enabled."
),
}),
"useBackground": ("BOOLEAN", {
"tooltip": "Enable to include background style in API request",
"default": False,
}),
"background": (["auto", "transparent", "opaque"], {
"default": "auto",
"tooltip": "Background style for the generated image. Only used when 'Use Background' is enabled.",
}),
"useStyle": ("BOOLEAN", {
"tooltip": "Enable to include style preset in API request",
"default": False,
}),
"style": ("STRING", {
"default": "auto",
"multiline": False,
"tooltip": "Style preset for generation. Only used when 'Use Style' is enabled.",
}),
"useSearch": ("BOOLEAN", {
"tooltip": "Enable to include search grounding in API request",
"default": False,
}),
"search": ("BOOLEAN", {
"tooltip": "Enables web search grounding for reference. Only used when 'Use Search' is enabled.",
"default": False,
"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.",
}),
"useAutoCrop": ("BOOLEAN", {
"tooltip": "Enable to include autoCrop in settings.",
"default": False,
}),
"autoCrop": ("BOOLEAN", {
"tooltip": "If true, crop the reference image to the canvas bounds when it extends beyond the edges. Defaults to false. Only used when 'Use Auto Crop' is enabled.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
"useDilatePixels": ("BOOLEAN", {
"tooltip": "Enable to include dilatePixels in settings.",
"default": False,
}),
"dilatePixels": ("INT", {
"tooltip": "Expands mask edges for cleaner removal. Only used when 'Use Dilate Pixels' is enabled.",
"default": 10,
"min": 0,
"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.",
}),
"structuredPrompt": ("RUNWAREIMAGEINFERENCESTRUCTUREDPROMPT", {
"tooltip": "Connect Runware Image Inference Settings Structured Prompt (Ideogram 4.0). Merged into settings.structuredPrompt. Mutually exclusive with positivePrompt on the same API request.",
}),
"promptEnhance": ("RUNWAREIMAGEINFERENCEPROMPTENHANCE", {
"tooltip": "Connect Runware Image Inference Settings Prompt Enhance. Merged into settings.promptEnhance (temperature, topP). Omit from API when not connected or empty.",
}),
"useBackgroundMode": ("BOOLEAN", {
"tooltip": "Enable to include backgroundMode in settings (Riverflow 2.5 output background handling).",
"default": False,
}),
"backgroundMode": (["original", "transparent", "solid"], {
"default": "original",
"tooltip": (
"settings.backgroundMode: original, transparent, or solid. "
"Only used when 'Use Background Mode' is enabled."
),
}),
"useBackgroundColor": ("BOOLEAN", {
"tooltip": "Enable to include backgroundColor in settings (required when backgroundMode is solid).",
"default": False,
}),
"backgroundColor": ("STRING", {
"default": "#FFFFFF",
"tooltip": (
"Hex color (#RRGGBB) composited when backgroundMode is solid. "
"Only used when 'Use Background Color' is enabled."
),
}),
"useEnhancePrompt": ("BOOLEAN", {
"tooltip": "Enable to include enhancePrompt (boolean) in settings.",
"default": False,
}),
"enhancePrompt": ("BOOLEAN", {
"default": False,
"label_on": "true",
"label_off": "false",
"tooltip": (
"Enhance the prompt with use-case context before generation (Riverflow 2.5). "
"Distinct from settings.promptEnhance (nested object). "
"Only used when 'Use Enhance Prompt' is enabled."
),
}),
"useScoringPrompt": ("BOOLEAN", {
"tooltip": "Enable to include scoringPrompt in settings.",
"default": False,
}),
"scoringPrompt": ("STRING", {
"multiline": True,
"default": "",
"tooltip": (
"Optional quality rubric used when RF 2.5 scores candidate outputs. "
"Only used when 'Use Scoring Prompt' is enabled."
),
}),
"scoringRubric": ("RUNWAREIMAGEINFERENCESCORINGRUBRIC", {
"tooltip": (
"Connect Runware Image Inference Settings Scoring Rubric or Scoring Rubric Combine "
"for settings.scoringRubric (up to 4 dimensions via Combine)."
),
}),
"useCopyrightDetection": ("BOOLEAN", {
"tooltip": "Enable to include copyrightDetection in settings.",
"default": False,
}),
"copyrightDetection": ("BOOLEAN", {
"default": False,
"label_on": "true",
"label_off": "false",
"tooltip": (
"Opt into post-generation copyright detection (Hive likeness + logo checks). "
"Only used when 'Use Copyright Detection' is enabled."
),
}),
"usePreserveInputSize": ("BOOLEAN", {
"tooltip": "Enable to include preserveInputSize in settings.",
"default": False,
}),
"preserveInputSize": ("BOOLEAN", {
"default": False,
"label_on": "true",
"label_off": "false",
"tooltip": (
"Return the output at the original input resolution. "
"Only used when 'Use Preserve Input Size' is enabled."
),
}),
"useTurbo": ("BOOLEAN", {
"tooltip": "Enable to include turbo in settings.",
"default": False,
}),
"turbo": ("BOOLEAN", {
"tooltip": "Turbo mode: faster generation for slightly lower quality. Only used when 'Use Turbo' is enabled.",
"default": False,
"label_on": "true",
"label_off": "false",
}),
}
}
RETURN_TYPES = ("RUNWARESETTINGS",)
RETURN_NAMES = ("Settings",)
FUNCTION = "createSettings"
CATEGORY = "Runware"
DESCRIPTION = (
"Configure general settings for image generation: temperature, system prompt, top-p, layers, quality, "
"backgroundMode (original/transparent/solid), backgroundColor, enhancePrompt, scoringPrompt, background, style, search, "
"promptExtend, editRegions (JSON), thinking (boolean), thinkingLevel (low/medium/high/xhigh), sequential, "
"renderingSpeed (TURBO/DEFAULT/QUALITY), magicPrompt (AUTO/ON/OFF), autoCrop, dilatePixels, "
"creativity (raw/low/medium/high), preserveInputSize, turbo (faster generation for slightly lower quality), and optional colorPalette, moodboards, structuredPrompt, promptEnhance, "
"and scoringRubric from dedicated settings nodes."
)
def createSettings(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create settings configuration"""
# Get control parameters
useTemperature = kwargs.get("useTemperature", False)
useSystemPrompt = kwargs.get("useSystemPrompt", False)
useTopP = kwargs.get("useTopP", False)
useLayers = kwargs.get("useLayers", False)
useTrueCFGScale = kwargs.get("useTrueCFGScale", False)
useQuality = kwargs.get("useQuality", False)
useBackground = kwargs.get("useBackground", False)
useStyle = kwargs.get("useStyle", False)
useSearch = kwargs.get("useSearch", False)
usePromptExtend = kwargs.get("usePromptExtend", False)
promptExtend = kwargs.get("promptExtend", False)
useEditRegions = kwargs.get("useEditRegions", False)
useThinking = kwargs.get("useThinking", False)
useThinkingLevel = kwargs.get("useThinkingLevel", False)
useBackgroundMode = kwargs.get("useBackgroundMode", False)
useBackgroundColor = kwargs.get("useBackgroundColor", False)
useEnhancePrompt = kwargs.get("useEnhancePrompt", False)
useScoringPrompt = kwargs.get("useScoringPrompt", False)
useCopyrightDetection = kwargs.get("useCopyrightDetection", False)
usePreserveInputSize = kwargs.get("usePreserveInputSize", False)
useSequential = kwargs.get("useSequential", False)
useRenderingSpeed = kwargs.get("useRenderingSpeed", False)
useMagicPrompt = kwargs.get("useMagicPrompt", False)
useAutoCrop = kwargs.get("useAutoCrop", False)
useDilatePixels = kwargs.get("useDilatePixels", False)
useCreativity = kwargs.get("useCreativity", False)
useTurbo = kwargs.get("useTurbo", False)
# Get value parameters
temperature = kwargs.get("temperature", 1.0)
systemPrompt = kwargs.get("systemPrompt", "")
topP = kwargs.get("topP", 1.0)
layers = kwargs.get("layers")
trueCFGScale = kwargs.get("trueCFGScale")
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")
creativity = kwargs.get("creativity", "medium")
# Build settings dictionary - only include what is enabled
settings: Dict[str, Any] = {}
# Add optional parameters only if enabled
if useTemperature:
settings["temperature"] = float(temperature)
if useSystemPrompt and systemPrompt and systemPrompt.strip():
settings["systemPrompt"] = systemPrompt.strip()
if useTopP:
settings["topP"] = float(topP)
if useLayers:
settings["layers"] = int(layers)
if useTrueCFGScale:
settings["true_cfg_scale"] = float(trueCFGScale)
if useQuality:
settings["quality"] = quality
if useBackground:
settings["background"] = background
if useStyle:
settings["style"] = style
if useSearch:
settings["search"] = bool(kwargs.get("search", False))
if usePromptExtend:
settings["promptExtend"] = bool(promptExtend)
if useEditRegions:
raw_er = (kwargs.get("editRegions") or "").strip()
if raw_er:
try:
parsed = json.loads(raw_er)
except json.JSONDecodeError as e:
raise ValueError(f"editRegions must be valid JSON: {e}") from e
if not isinstance(parsed, list):
raise ValueError("editRegions JSON must be an array (one element per input image).")
settings["editRegions"] = parsed
if useThinking:
settings["thinking"] = bool(kwargs.get("thinking", False))
if useThinkingLevel:
settings["thinkingLevel"] = str(kwargs.get("thinkingLevel", "high"))
if useBackgroundMode:
settings["backgroundMode"] = str(kwargs.get("backgroundMode", "original"))
if useBackgroundColor:
background_color = (kwargs.get("backgroundColor") or "").strip()
if background_color:
settings["backgroundColor"] = background_color
if useEnhancePrompt:
settings["enhancePrompt"] = bool(kwargs.get("enhancePrompt", False))
if useScoringPrompt:
scoring_prompt = (kwargs.get("scoringPrompt") or "").strip()
if scoring_prompt:
settings["scoringPrompt"] = scoring_prompt
if useCopyrightDetection:
settings["copyrightDetection"] = bool(kwargs.get("copyrightDetection", False))
if usePreserveInputSize:
settings["preserveInputSize"] = bool(kwargs.get("preserveInputSize", False))
if useSequential:
settings["sequential"] = bool(kwargs.get("sequential", False))
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
structured_prompt: Optional[Dict[str, Any]] = kwargs.get("structuredPrompt")
if (
structured_prompt is not None
and isinstance(structured_prompt, dict)
and len(structured_prompt) > 0
):
settings["structuredPrompt"] = structured_prompt
prompt_enhance: Optional[Dict[str, Any]] = kwargs.get("promptEnhance")
if (
prompt_enhance is not None
and isinstance(prompt_enhance, dict)
and len(prompt_enhance) > 0
):
settings["promptEnhance"] = prompt_enhance
scoring_rubric: Optional[List[Dict[str, Any]]] = kwargs.get("scoringRubric")
if scoring_rubric is not None and isinstance(scoring_rubric, list) and len(scoring_rubric) > 0:
settings["scoringRubric"] = scoring_rubric
if useRenderingSpeed:
settings["renderingSpeed"] = str(renderingSpeed)
if useMagicPrompt:
settings["magicPrompt"] = str(magicPrompt)
if useAutoCrop:
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
if useTurbo:
settings["turbo"] = bool(kwargs.get("turbo", False))
# Clean up None values
settings = {k: v for k, v in settings.items() if v is not None}
return (settings,)
-81
View File
@@ -1,81 +0,0 @@
"""
Runware Speech Input Node
Creates a speech input configuration with TTS provider settings
"""
from typing import Dict, Any
class RunwareSpeechInput:
"""Runware Speech Input Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"provider": ("STRING", {
"tooltip": "TTS provider name. Currently supports: elevenlabs",
"default": "elevenlabs",
}),
"voiceId": ("STRING", {
"tooltip": "ElevenLabs voice ID for text-to-speech generation",
"default": "",
}),
"text": ("STRING", {
"multiline": True,
"tooltip": "The text script to be spoken",
"default": "",
}),
},
"optional": {
"useId": ("BOOLEAN", {
"tooltip": "Enable to include reference ID for mapping speech input. Required when using segments array.",
"default": False,
}),
"id": ("STRING", {
"tooltip": "Reference ID used to map speech input. Required when using segments array.",
"default": "",
}),
}
}
RETURN_TYPES = ("RUNWARESPEECHINPUT",)
RETURN_NAMES = ("Speech Input",)
FUNCTION = "createSpeechInput"
CATEGORY = "Runware"
DESCRIPTION = "Create a speech input configuration with TTS provider settings for video generation."
def createSpeechInput(self, **kwargs) -> tuple[Dict[str, Any]]:
"""Create speech input configuration"""
useId = kwargs.get("useId", False)
speech_id = kwargs.get("id", "").strip()
provider = kwargs.get("provider", "elevenlabs").strip()
voiceId = kwargs.get("voiceId", "").strip()
text = kwargs.get("text", "").strip()
if not provider or not voiceId or not text:
return ({},)
speechInput: Dict[str, Any] = {
"provider": provider,
"voiceId": voiceId,
"text": text,
}
# Only include id if useId is enabled and id is provided
if useId and speech_id:
speechInput["id"] = speech_id
return (speechInput,)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareSpeechInput": RunwareSpeechInput,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareSpeechInput": "Runware Video Speech Input",
}
-59
View File
@@ -1,59 +0,0 @@
"""
Runware Speech Input Combine Node
Combines multiple speech inputs into a list
"""
class RunwareSpeechInputCombine:
"""Runware Speech Input Combine Node"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Speech Input 1": ("RUNWARESPEECHINPUT", {
"tooltip": "Connect a Runware Speech Input node.",
}),
},
"optional": {
"Speech Input 2": ("RUNWARESPEECHINPUT", {
"tooltip": "Connect a Runware Speech Input node.",
}),
"Speech Input 3": ("RUNWARESPEECHINPUT", {
"tooltip": "Connect a Runware Speech Input node.",
}),
"Speech Input 4": ("RUNWARESPEECHINPUT", {
"tooltip": "Connect a Runware Speech Input node.",
}),
},
}
DESCRIPTION = "Combine multiple speech inputs to connect with Runware Video Inference Inputs."
FUNCTION = "speechInputCombine"
RETURN_TYPES = ("RUNWARESPEECHINPUT",)
RETURN_NAMES = ("Speech Inputs",)
CATEGORY = "Runware"
def speechInputCombine(self, **kwargs):
"""Combine speech inputs into a list"""
speechInputs = []
for i in range(1, 5):
speechKey = f"Speech Input {i}"
speechInput = kwargs.get(speechKey)
if speechInput is not None and isinstance(speechInput, dict) and len(speechInput) > 0:
speechInputs.append(speechInput)
return (speechInputs,)
# Node class mappings
NODE_CLASS_MAPPINGS = {
"RunwareSpeechInputCombine": RunwareSpeechInputCombine,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"RunwareSpeechInputCombine": "Runware Speech Inputs Combine",
}

Some files were not shown because too many files have changed in this diff Show More