Files
christian-byrne-claude-code…/knowledge/custom-node-dev/02-node-interface.md
T
2025-06-15 16:32:07 -07:00

380 lines
8.4 KiB
Markdown

# Node Interface Reference
## Complete Node API
This reference covers all attributes and methods available when creating custom nodes in ComfyUI.
## Required Components
### Class Definition
Every custom node must be a Python class. The class name will be used internally, while the display name can be customized.
```python
class YourNodeClassName:
"""Docstring describing what your node does"""
```
### INPUT_TYPES (Required)
A class method that returns a dictionary defining the node's inputs.
```python
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
# Required inputs must be connected
"input_name": ("TYPE", options_dict),
},
"optional": {
# Optional inputs can be left unconnected
"optional_name": ("TYPE", options_dict),
},
"hidden": {
# Hidden inputs not shown in UI
"unique_id": "UNIQUE_ID",
"prompt": "PROMPT",
"extra_pnginfo": "EXTRA_PNGINFO",
}
}
```
### RETURN_TYPES (Required)
A tuple defining the types of outputs the node produces.
```python
RETURN_TYPES = ("IMAGE", "MASK")
```
### FUNCTION (Required)
The name of the method to call when the node executes.
```python
FUNCTION = "process"
```
### Execution Method (Required)
The method that performs the node's computation.
```python
def process(self, **kwargs):
# All inputs from INPUT_TYPES are passed as keyword arguments
# Must return a tuple matching RETURN_TYPES
return (output1, output2)
```
## Optional Components
### CATEGORY
Where the node appears in the add node menu. Use "/" for submenu organization.
```python
CATEGORY = "image/filters"
```
### RETURN_NAMES
Human-readable names for outputs (must match RETURN_TYPES length).
```python
RETURN_NAMES = ("processed_image", "generated_mask")
```
### OUTPUT_NODE
Set to `True` if this node produces final outputs (like Save Image).
```python
OUTPUT_NODE = True
```
### OUTPUT_TOOLTIPS
Tooltips for each output socket.
```python
OUTPUT_TOOLTIPS = ("The processed image", "The generated mask")
```
### DESCRIPTION
A description shown in the UI when hovering over the node.
```python
DESCRIPTION = "Applies advanced filtering to images"
```
## Input Type Options
### Basic Input Options
All input types support these options:
```python
{
"default": value, # Default value
"forceInput": True, # Force connection (no widget)
"lazy": True, # Enable lazy evaluation
"tooltip": "Help text", # Tooltip on hover
}
```
### Numeric Inputs (INT, FLOAT)
```python
"number": ("FLOAT", {
"default": 1.0,
"min": 0.0,
"max": 10.0,
"step": 0.01,
"round": 0.001, # Round to this precision
"display": "slider", # or "number"
})
```
### String Inputs
```python
"text": ("STRING", {
"default": "Enter text",
"multiline": True, # Multi-line text box
"dynamicPrompts": True, # Enable dynamic prompts
"placeholder": "Type here..."
})
```
### Boolean Inputs
```python
"enable": ("BOOLEAN", {
"default": True,
"label_on": "Enabled", # Label when True
"label_off": "Disabled" # Label when False
})
```
### Combo Box (Dropdown)
```python
"mode": (["option1", "option2", "option3"], {
"default": "option1"
})
```
### Image Upload
```python
"image": ("IMAGE", {
"image_upload": True, # Enable upload button
})
```
## Advanced Features
### IS_CHANGED Method
Control when the node is re-executed:
```python
@classmethod
def IS_CHANGED(cls, **kwargs):
# Return a value that changes when node should re-execute
# Can return hash, timestamp, random value, etc.
return float("nan") # Always re-execute
```
### VALIDATE_INPUTS Method
Custom validation before execution:
```python
@classmethod
def VALIDATE_INPUTS(cls, **kwargs):
# Return True if inputs are valid
# Return error message string if invalid
if kwargs.get("value", 0) < 0:
return "Value must be non-negative"
return True
```
### Hidden Inputs
Access special system information:
```python
"hidden": {
"unique_id": "UNIQUE_ID", # Node's unique ID
"prompt": "PROMPT", # Full workflow prompt
"extra_pnginfo": "EXTRA_PNGINFO", # PNG metadata dict
"dynprompt": "DYNPROMPT" # Dynamic prompt object
}
```
### Lazy Evaluation
Enable on-demand execution:
```python
"input": ("TYPE", {"lazy": True})
# In execution method:
def process(self, input):
# Input might be a lazy object
if hasattr(input, '__call__'):
actual_value = input() # Evaluate lazy input
```
## Type System
### Built-in Types
```python
# Basic types
"STRING" # Text strings
"INT" # Integers
"FLOAT" # Floating point numbers
"BOOLEAN" # True/False values
# Image types
"IMAGE" # Image tensor [B,H,W,C]
"MASK" # Mask tensor [H,W] or [B,H,W]
"LATENT" # Latent representation
# Model types
"MODEL" # Diffusion model
"CLIP" # CLIP text encoder
"VAE" # Variational autoencoder
"CONDITIONING" # Text conditioning
"CONTROL_NET" # ControlNet model
# Advanced types
"SAMPLER" # Sampling method
"SIGMAS" # Sigma schedule
"GUIDER" # Guidance model
"NOISE" # Noise tensor
# Special types
"*" # ANY type (matches anything)
"STRING,FLOAT,INT" # Union types
```
### Custom Types
You can define custom types:
```python
class MY_CUSTOM_TYPE:
def __init__(self, data):
self.data = data
# Use in node:
RETURN_TYPES = ("MY_CUSTOM_TYPE",)
```
## Complete Example
```python
class AdvancedImageProcessor:
"""
Processes images with multiple effects and options.
Demonstrates most node interface features.
"""
def __init__(self):
self.type = "AdvancedImageProcessor"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image": ("IMAGE", {
"tooltip": "Input image to process"
}),
"mode": (["blur", "sharpen", "edge_detect"], {
"default": "blur"
}),
"strength": ("FLOAT", {
"default": 1.0,
"min": 0.0,
"max": 10.0,
"step": 0.1,
"display": "slider"
}),
},
"optional": {
"mask": ("MASK", {
"tooltip": "Optional mask for selective processing"
}),
"seed": ("INT", {
"default": 0,
"min": 0,
"max": 0xffffffffffffffff
}),
},
"hidden": {
"unique_id": "UNIQUE_ID"
}
}
RETURN_TYPES = ("IMAGE", "MASK")
RETURN_NAMES = ("processed", "effect_mask")
OUTPUT_TOOLTIPS = ("Processed image", "Mask showing affected areas")
FUNCTION = "process_image"
CATEGORY = "image/processing"
DESCRIPTION = "Advanced image processing with multiple effects"
def process_image(self, image, mode, strength, mask=None, seed=0, unique_id=None):
# Processing implementation
processed = apply_effect(image, mode, strength)
effect_mask = generate_effect_mask(image, processed)
return (processed, effect_mask)
@classmethod
def IS_CHANGED(cls, **kwargs):
# Re-execute if seed changes
return kwargs.get("seed", 0)
@classmethod
def VALIDATE_INPUTS(cls, **kwargs):
if kwargs.get("strength", 0) < 0:
return "Strength must be non-negative"
return True
# Register the node
NODE_CLASS_MAPPINGS = {
"AdvancedImageProcessor": AdvancedImageProcessor
}
NODE_DISPLAY_NAME_MAPPINGS = {
"AdvancedImageProcessor": "Advanced Image Processor"
}
```
## Server Communication
For advanced use cases, communicate with the frontend:
```python
from server import PromptServer
def process_image(self, image):
# Send message to frontend
PromptServer.instance.send_sync(
"my_node.message",
{"text": "Processing complete", "node_id": unique_id}
)
return (image,)
```
## Next Steps
- Learn about [Data Types and Type Conversion](./03-data-types.md)
- Explore [Advanced Features](./04-advanced-features.md)
- See [Example Implementations](./05-examples.md)