Parameters
Parameters define the inputs, outputs, and properties of a node. This page covers every Parameter attribute, the traits system, the parameter helper classes, containers, and advanced patterns for dynamic parameter surfaces.
For the mapping from parameter types to editor widgets, supported ui_options keys, and traits, see the Parameter UI Reference.
Parameter Attributes
All Parameter attributes:
- name: str, unique identifier, no whitespace
- tooltip: str or list[dict] for UI help text
- default_value: Any default value
- type: str (e.g., "str", "list[str]", ParameterTypeBuiltin.STR.value)
- input_types: list[str] for incoming connection types
- output_type: str for outgoing connection type
- allowed_modes: set[ParameterMode] {INPUT, OUTPUT, PROPERTY}
- ui_options: dict for UI customization
- converters: list[Callable[[Any], Any]] for value transformation
- validators: list[Callable[[Parameter, Any], None]] for validation
- hide/hide_label/hide_property: common UI flags (also available via
ui_options;ui_optionswins on conflict) - allow_input/allow_property/allow_output: convenience flags for configuring modes (ignored if
allowed_modesis explicitly set) - settable: bool (default True) - False for computed/output parameters
- serializable: bool (default True) - set False for non-serializable values (drivers, file handles, etc.)
- user_defined: bool (default False)
- private: bool (default False) - hide from general user editing (library/internal use)
- parent_container_name: str|None — assigns this parameter as a child of a
ParameterContainer(i.e. aParameterListorParameterDictionary). Used for list-like ownership. - parent_element_name: str|None — nests this parameter under a
ParameterGroup(a UI grouping element). Used for visual grouping in the node UI.
Traits
Add functionality via add_trait():
- Options:
Options(choices=list[str] | list[tuple[str, Any]], show_search: bool = True, search_filter: str = "") - Slider:
Slider(min_val: float, max_val: float) - Button:
Button(label: str = "", variant=..., size=..., button_link=... | on_click=..., get_button_state=...) - ColorPicker:
ColorPicker(format="hex") - FileSystemPicker:
FileSystemPicker(...)(file/directory selection UI)
For the full list of traits, the widgets they render, and the ui_options keys they manage, see the Parameter UI Reference.
Parameter helper constructs (ParameterString, ParameterInt, ...)
Griptape Nodes includes a set of convenience Parameter subclasses under griptape_nodes.exe_types.param_types.*.
They exist to make common parameter patterns simple, consistent, and runtime-mutable (many expose UI options as Python properties).
Quick reference table
| Helper | Enforced type / output_type |
Default input_types behavior |
Key UI convenience args | Notes |
|---|---|---|---|---|
ParameterString |
"str" / "str" |
accept_any=True → ["any"] with converter to str |
markdown, multiline, placeholder_text, is_full_width |
type, output_type, and input_types constructor args are ignored |
ParameterBool |
"bool" / "bool" |
accept_any=True → ["any"] with converter to bool |
on_label, off_label |
Converts common strings like "true"/"false", "yes"/"no" |
ParameterInt |
"int" / "int" |
accept_any=True → ["any"] with converter to int |
step, slider, min_val, max_val, validate_min_max |
Adds constraint traits (Clamp/MinMax/Slider) based on args |
ParameterFloat |
"float" / "float" |
accept_any=True → ["any"] with converter to float |
step, slider, min_val, max_val, validate_min_max |
Adds constraint traits (Clamp/MinMax/Slider) based on args |
ParameterDict |
"dict" / "dict" |
accept_any=True → ["any"] with converter to dict |
(none) | Uses griptape_nodes.utils.dict_utils.to_dict() for conversion |
ParameterJson |
"json" / "json" |
accept_any=True → ["any"] with converter to JSON |
button, button_label, button_icon |
Uses json_repair.repair_json() for robust string → JSON |
ParameterRange |
"list" / "list" |
accept_any=True → ["any"] with converter to list |
range_slider + min_val/max_val/step, labels |
Range slider is only meaningful when the value is a 2-number list |
ParameterImage |
"ImageUrlArtifact" / "ImageUrlArtifact" |
accept_any=True → ["any"] (no conversion) |
clickable_file_browser, webcam_capture_image, edit_mask, pulse_on_run |
Mostly UI convenience; add converters if you need type coercion |
ParameterAudio |
"AudioUrlArtifact" / "AudioUrlArtifact" |
accept_any=True → ["any"] (no conversion) |
clickable_file_browser, microphone_capture_audio, edit_audio, pulse_on_run |
Mostly UI convenience; add converters if you need type coercion |
ParameterVideo |
"VideoUrlArtifact" / "VideoUrlArtifact" |
accept_any=True → ["any"] (no conversion) |
clickable_file_browser, webcam_capture_video, edit_video, pulse_on_run |
Mostly UI convenience; add converters if you need type coercion |
Parameter3D |
"ThreeDUrlArtifact" / "ThreeDUrlArtifact" |
accept_any=True → ["any"] (no conversion) |
clickable_file_browser, expander, pulse_on_run |
Mostly UI convenience; add converters if you need type coercion |
ParameterButton |
"button" / "str" |
["str", "any"] |
label, variant, size, icon, state, href / on_click |
Label is display text; default_value is stored value |
Shared behavior across helpers
- All helpers forward the standard
Parameterconstructor knobs (allowed_modesorallow_input/allow_property/allow_output,hide/hide_label/hide_property,settable,serializable, etc.). - Many helpers default
accept_any=True. When enabled, the helper typically setsinput_types=["any"]and prepends a converter (e.g.ParameterStringconverts any input tostr). Turn this off if you want strict typing. - If you provide both an explicit convenience parameter (e.g.
hide=True) and the same key inui_options(e.g.ui_options={"hide": False}),ui_optionswins and Griptape Nodes will warn about the conflict.
Detailed helper notes
ParameterString
- Enforces
type="str"andoutput_type="str". accept_any=TrueconvertsNone→""and otherwise usesstr(value).- UI convenience:
markdown,multiline,placeholder_text,is_full_width(all are runtime-settable properties).
ParameterBool
- Enforces
type="bool"andoutput_type="bool". accept_any=Trueconverts common string representations (e.g."true","yes","on","1") toTrueand ("false","no","off","0") toFalse.- UI convenience:
on_label,off_label(runtime-settable properties).
ParameterInt / ParameterFloat (via ParameterNumber)
- Enforces numeric
type/output_typeand can prepend a converter whenaccept_any=True. step: stored inui_options["step"]and validated (value must be a multiple of the current step).slider,min_val,max_val,validate_min_max: adds one of these constraint traits based on priority:Slider(min_val, max_val)ifslider=TrueMinMax(min_val, max_val)ifvalidate_min_max=TrueClamp(min_val, max_val)ifmin_valandmax_valare provided
ParameterJson
- Enforces
type="json"andoutput_type="json". accept_any=Trueattempts to repair/parse JSON strings usingjson_repair.repair_json()(and will also attempt to stringify non-string inputs).- UI convenience: optional editor button (
button,button_label,button_icon).
ParameterDict
- Enforces
type="dict"andoutput_type="dict". accept_any=Trueusesto_dict(...)to coerce common inputs into a dict.
ParameterRange
- Enforces
type="list"andoutput_type="list". accept_any=TruecoercesNone→[], list → list, and any other value →[value].- UI convenience:
range_slider(a nestedui_options["range_slider"]object) withmin_val/max_val/stepand label visibility options. - The range slider UI is only applicable when the value is a list of exactly two numeric values.
ParameterImage (Recommended for Image Parameters)
Always use ParameterImage instead of generic Parameter for image inputs/outputs. It provides:
- Automatic
type="ImageUrlArtifact"andoutput_type="ImageUrlArtifact" - Built-in UI options for file browser, webcam capture, and mask editing
- Consistent behavior across all image-handling nodes
Basic Usage:
from griptape_nodes.exe_types.param_types.parameter_image import ParameterImage
# Input image parameter
self.add_parameter(
ParameterImage(
name="input_image",
tooltip="Input image for processing",
allow_output=False, # Input only
)
)
# Output image parameter
self.add_parameter(
ParameterImage(
name="output_image",
tooltip="Generated image result",
allow_input=False, # Output only
allow_property=False,
)
)
Available UI Options:
clickable_file_browser: Enable file browser for image selectionwebcam_capture_image: Enable webcam captureedit_mask: Enable mask editing overlaypulse_on_run: Visual feedback when image updates
Dynamic Visibility Pattern:
For parameters that should only appear for certain model types:
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
# Add image parameter (hidden by default)
self.add_parameter(
ParameterImage(
name="input_image",
tooltip="Input image for image-to-image generation",
allow_output=False,
)
)
# Initialize visibility based on default model
self._initialize_parameter_visibility()
def _initialize_parameter_visibility(self) -> None:
"""Initialize parameter visibility based on default model."""
model = self.get_parameter_value("model") or "default"
if model in ["model-with-image-support", "another-model"]:
self.show_parameter_by_name("input_image")
else:
self.hide_parameter_by_name("input_image")
def after_value_set(self, parameter: Parameter, value: Any) -> None:
"""Update visibility when model changes."""
if parameter.name == "model":
if value in ["model-with-image-support", "another-model"]:
self.show_parameter_by_name("input_image")
else:
self.hide_parameter_by_name("input_image")
self.set_parameter_value("input_image", None) # Clear when hiding
return super().after_value_set(parameter, value)
Why Use ParameterImage Over Generic Parameter:
| Aspect | Generic Parameter |
ParameterImage |
|---|---|---|
| Type safety | Manual type/input_types setup |
Automatic artifact types |
| UI features | Manual ui_options configuration |
Built-in file browser, webcam, mask editing |
| Consistency | Varies by implementation | Standardized across nodes |
| Maintenance | More boilerplate code | Less code, cleaner |
Legacy Pattern (Avoid):
# ❌ Don't do this - use ParameterImage instead
self.add_parameter(
Parameter(
name="input_image",
input_types=["ImageArtifact", "ImageUrlArtifact", "str"],
type="ImageArtifact",
default_value=None,
tooltip="Input image",
allowed_modes={ParameterMode.INPUT, ParameterMode.PROPERTY},
ui_options={"display_name": "Input Image"},
)
)
Why ParameterImage is better:
- Standardized type conversion logic: Handles ImageArtifact, ImageUrlArtifact, and string inputs consistently
- Built-in UI features: File browser, webcam capture, mask editing
- Less boilerplate: Automatically configures types and options
- Robust error handling: Gracefully handles various input formats (URLs, file paths, data URIs)
Recommended Pattern:
# ✅ Use ParameterImage for cleaner, more maintainable code
self.add_parameter(
ParameterImage(
name="input_image",
tooltip="Input image",
allow_output=False,
)
)
ParameterImage standardizes how your node handles different image input types, reducing conversion errors and improving reliability.
ParameterAudio / ParameterVideo / Parameter3D
- Enforce their corresponding
*UrlArtifacttype/output_type(e.g.,AudioUrlArtifact,VideoUrlArtifact,ThreeDUrlArtifact). - These helpers primarily provide UI options (file browser / capture / editing / expanders). If you need coercion from e.g.
str→ artifact, supplyconvertersand/or handle it in your node'sbefore_value_set()/process()logic. - Follow the same patterns as
ParameterImagefor these media types.
ParameterButton
Buttons provide interactive UI elements that trigger actions when clicked, such as updating parameters, performing calculations, or navigating between states.
Basic Properties:
- Enforces
type="button"andoutput_type="str" - By default, it's a property-only UI element (
allow_property=True,allow_input=False,allow_output=False) - Accepts either
href="..."(simple link) oron_click=...(custom callback) parameters labelis the display text shown on the buttoniconadds a visual icon (optional)icon_positioncontrols icon placement ("left" or "right", defaults to "left")
Important: The on_click handler and href are passed directly to ParameterButton as parameters, not via the Button trait.
Implementation Pattern:
Buttons must be wrapped in a ParameterButtonGroup container:
from griptape_nodes.exe_types.core_types import ParameterButtonGroup
from griptape_nodes.exe_types.param_types.parameter_button import ParameterButton
from griptape_nodes.traits.button import Button, ButtonDetailsMessagePayload
class MyNode(DataNode):
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
# Create button group with context manager
with ParameterButtonGroup(name="my_button_group") as button_group:
ParameterButton(
name="update_button",
label="Update Date/Time",
icon="calendar",
on_click=self._handle_button_click, # Pass on_click directly
)
self.add_node_element(button_group)
# Add parameter that will be updated by button
self.add_parameter(
Parameter(
name="display_value",
tooltip="Value updated by button",
type=ParameterTypeBuiltin.STR.value,
allowed_modes={ParameterMode.PROPERTY},
ui_options={
"display_name": "Display Value",
"readonly": True, # Prevent manual editing
},
default_value="Click button to update",
)
)
def _handle_button_click(
self,
button: Button,
button_payload: ButtonDetailsMessagePayload,
) -> None:
"""Button click handler.
Args:
button: The Button trait instance
button_payload: Contains click event details
"""
# Update parameter value
new_value = "Updated at " + datetime.now().strftime("%H:%M:%S")
self.set_parameter_value("display_value", new_value)
Multiple Buttons in a Group:
with ParameterButtonGroup(name="navigation_buttons") as nav_buttons:
ParameterButton(
name="previous",
label="Previous",
icon="arrow-left",
on_click=self._previous_item, # Pass on_click directly
)
ParameterButton(
name="next",
label="Next",
icon="arrow-right",
icon_position="right",
on_click=self._next_item, # Pass on_click directly
)
self.add_node_element(nav_buttons)
Common Use Cases:
-
Update Display Values
def _update_datetime(self, button: Button, button_payload: ButtonDetailsMessagePayload) -> None: """Update datetime display when button is clicked.""" current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") self.set_parameter_value("datetime_display", current_time) -
Navigate Through Items
def _next_image(self, button: Button, button_payload: ButtonDetailsMessagePayload) -> None: """Increment index and update display.""" current_index = self.get_parameter_value("image_index") self.set_parameter_value("image_index", current_index + 1) self._update_display() -
Trigger Calculations
def _calculate(self, button: Button, button_payload: ButtonDetailsMessagePayload) -> None: """Perform calculation and update result parameter.""" input_value = self.get_parameter_value("input") result = self._perform_complex_calculation(input_value) self.set_parameter_value("result", result) -
Reset to Defaults
def _reset(self, button: Button, button_payload: ButtonDetailsMessagePayload) -> None: """Reset parameters to default values.""" self.set_parameter_value("counter", 0) self.set_parameter_value("display", "")
Link Buttons (Alternative to on_click):
For simple navigation to external URLs:
ParameterButton(
name="docs_link",
label="View Documentation",
icon="external-link",
href="https://docs.griptape.ai", # Pass href directly
)
Best Practices:
- Use descriptive button labels that clearly indicate the action
- Choose appropriate icons that match the action (e.g., "calendar" for date/time, "arrow-left"/"arrow-right" for navigation)
- Keep button handlers simple and focused on a single action
- Use read-only parameters for values that should only be updated by buttons
- Avoid triggering expensive operations directly in button handlers (consider using flags that
process()checks instead) - Group related buttons together in a single
ParameterButtonGroup
Common Patterns:
| Pattern | Button Action | Updated Parameter Type | Use Case |
|---|---|---|---|
| Update Display | Updates a read-only text parameter | PROPERTY (readonly) |
Show current time, status, count |
| Navigation | Increments/decrements an index | Hidden PROPERTY parameter |
Image carousel, list browsing |
| Toggle State | Switches between states | PROPERTY parameter |
Enable/disable features |
| Trigger Action | Sets a flag checked by process() |
Hidden PROPERTY parameter |
Refresh data, recalculate |
Complete Example:
See example_control_node.py and image_carousel.py for working implementations that demonstrate:
- Button creation with icons
- Button group usage
- Updating read-only parameters
- Handler method signatures
- Navigation patterns
- Locale-appropriate datetime formatting
Containers
- ParameterList: A container parameter that owns multiple child
Parameteritems (useget_parameter_list_value()to flatten values) - ParameterDictionary: A container parameter that owns ordered key/value pairs (distinct from
ParameterDict, which is adict-typed value parameter helper) - ParameterGroup: For UI grouping
Container semantics (important):
- Container parameters are represented as
ParameterContainerobjects in the engine. They are always truthy, even when empty (they override__bool__()to avoid bugs with stale cached values). ParameterListsupports several UI convenience options (e.g.collapsed, grid display, and column count) that are merged intoui_optionsat runtime.ParameterDictionaryis an ordered collection of key/value pair children (internally represented as a list to preserve order).
parent_container_name vs parent_element_name — critical distinction:
Parameters have two separate parent-pointer attributes that serve different purposes:
| Attribute | Points to | Purpose |
|---|---|---|
parent_container_name |
ParameterContainer (ParameterList, ParameterDictionary) |
Ownership. The parameter is a child of a list/dictionary container. The engine uses this for add_parameter(), child cleanup, value aggregation, and serialization/reload. |
parent_element_name |
ParameterGroup |
UI grouping. The parameter is visually nested under a collapsible group in the node UI. The engine uses this for add_parameter() placement, _remove_existing_*() lookups, and serialization/reload. |
Do NOT confuse them. If you use parent_container_name when you should be using parent_element_name (or vice versa), the parameter will:
- Appear at the node root instead of inside the intended group/container
- Not be cleaned up between runs (e.g. stale outputs persist)
- Fail to restore after save/reload — the reload handler looks for a
ParameterContainerorParameterGroupby the name you specified, and if the type doesn't match, the parameter is silently dropped before its saved values are applied
Rule of thumb:
- Putting a parameter inside a
ParameterListorParameterDictionary? → useparent_container_name - Putting a parameter inside a
ParameterGroupfor visual organization? → useparent_element_name
# ✅ CORRECT: Nesting under a ParameterGroup for UI grouping
param = ParameterImage(
name="cell_0_0",
parent_element_name=self._grid_cells_group.name, # ParameterGroup
...
)
# ❌ WRONG: Using parent_container_name for a ParameterGroup
param = ParameterImage(
name="cell_0_0",
parent_container_name=self._grid_cells_group.name, # BUG: this is a ParameterGroup, not a ParameterContainer
...
)
ParameterList Pattern
For parameters accepting multiple inputs of the same type:
self.add_parameter(
ParameterList(
name="tools",
input_types=["Tool", "list[Tool]"],
default_value=[],
tooltip="Connect individual tools or a list of tools",
allowed_modes={ParameterMode.INPUT},
)
)
# Retrieve in process method
tools = self.get_parameter_list_value("tools") # Always returns a list
for tool in tools:
# Process each tool
Benefits:
- Multiple connection points in UI
- Automatic aggregation of inputs
- Flexible workflow design
- Follows Griptape design patterns
Important behavior note: get_parameter_list_value() flattens nested iterables and drops falsey items
(e.g. 0, False, "", empty dicts/lists). If you need to preserve falsey values, use get_parameter_value() and handle flattening yourself.
Common Parameter Patterns
Search Input with Placeholder
Parameter(
name="search_query",
input_types=["str"],
type="str",
tooltip="Search term to find models",
allowed_modes={ParameterMode.INPUT, ParameterMode.PROPERTY},
ui_options={"placeholder_text": "e.g., llama, bert, stable-diffusion"}
)
Full-Width List Output
Parameter(
name="results",
output_type="list[dict]",
type="list[dict]",
tooltip="Search results with full information",
allowed_modes={ParameterMode.OUTPUT},
ui_options={"is_full_width": True}
)
Multiline Text Input
Parameter(
name="prompt",
input_types=["str"],
type="str",
tooltip="Description of desired output",
allowed_modes={ParameterMode.INPUT, ParameterMode.PROPERTY},
ui_options={"multiline": True, "placeholder_text": "Describe what you want..."}
)
File Upload with Browser
Parameter(
name="image",
input_types=["ImageArtifact", "ImageUrlArtifact", "str"],
type="ImageArtifact",
tooltip="Input image file",
allowed_modes={ParameterMode.INPUT, ParameterMode.PROPERTY},
ui_options={"clickable_file_browser": True}
)
Advanced Parameter Patterns
Dynamic Parameter Visibility
Use after_value_set() callback to create context-aware UIs:
def after_value_set(self, parameter: Parameter, value: Any) -> None:
"""Update parameter visibility based on model selection."""
if parameter.name == "model":
if value == "text-to-image":
self.hide_parameter_by_name("input_image")
self.show_parameter_by_name("prompt")
elif value == "image-to-image":
self.show_parameter_by_name("input_image")
self.show_parameter_by_name("prompt")
return super().after_value_set(parameter, value)
Dynamic Options Updates
Update parameter choices at runtime:
from griptape_nodes.traits.options import Options
def _update_option_choices(self, param_name: str, choices: list, default_value: str):
"""Update Options trait choices dynamically."""
param = self.get_parameter_by_name(param_name)
if not param:
return
# Traits are stored as child elements on the Parameter
# (most commonly, you'll be updating an Options trait)
for trait in param.find_elements_by_type(Options):
trait.choices = choices
break
self.set_parameter_value(param_name, default_value)
Dynamic Parameter Schemas with ParameterTransitionComponent
Some nodes expose a dropdown — a model picker, a mode selector, an operation chooser — where each choice implies a different set of input and output parameters. The naive approach (clear every parameter, rebuild from scratch) destroys every connection the user set up. ParameterTransitionComponent solves this by computing the diff between the current parameter surface and the one the new choice needs, then acting one of four ways per name:
- Preserve — the name exists on both sides with identical signatures. Nothing is touched.
- Replace — the name exists on both sides but the signature changed (types, modes, or both). The component captures the existing incoming and outgoing connections, removes the old parameter, adds a new one with the new signature, and then re-dispatches each captured connection through
CreateConnectionRequest. The platform's connection handler validates type compatibility, so connections that are still valid under the new signature come back; connections that are no longer valid are dropped. Because connection re-creation goes through the same path as a fresh connection, values re-flow through incoming edges the normal way. - Remove — the name no longer exists in the desired schema. Removed via
RemoveParameterFromNodeRequest, which cleans up all connections. - Add — the name is new. Added via the caller-supplied factory.
One consequence of always-replace: a replaced parameter's stored property value is lost (the new Parameter starts empty). Connections and their re-flowed values are what survive. For nodes that don't want this behavior for certain same-name-same-signature cases, declaring identical signatures keeps the parameter in the preserve bucket.
Walkthrough — Multi-model image generation (hypothetical)
Note on this example. The
MultiModelImageGeneratornode used below is hypothetical — it does not exist in Griptape today, and this walkthrough is not a description of how any shipped node works. We use a hypothetical consolidated node here because it's the kind of node an author might build on top ofParameterTransitionComponent, and it makes the "switching between shapes" story concrete for an artist audience.
The hypothetical node. MultiModelImageGenerator has a Model dropdown offering FLUX, SDXL, and Stable Diffusion 3, plus a "— none —" option for an unconfigured starting state. Every model shares some parameters; each has its own extras. Here's the parameter surface per model:
| Parameter | FLUX | SDXL | SD3 |
|---|---|---|---|
prompt |
str | str | str |
width |
int | int | int |
height |
int | int | int |
seed |
int | int | int |
guidance |
float | — | — |
steps |
— | int | — |
cfg_scale |
— | float | — |
reference_image |
— | str (URL) | ImageArtifact |
negative_prompt |
— | — | str |
Notice reference_image appears on both SDXL and SD3 but with different types — SDXL's driver accepts a URL string, SD3's accepts a Griptape-native ImageArtifact. Real image-to-image APIs diverge exactly this way: some want a URL, some want uploaded bytes, some want a native artifact object.
The user session. An artist opens a workflow. The MultiModelImageGenerator node starts with no model selected — its parameter surface is empty apart from the Model dropdown itself.
Step 1 — First selection (FLUX). The artist picks FLUX. Every FLUX parameter (prompt, width, height, seed, guidance) appears. They connect a TextPrompt node to prompt, connect a ResolutionPicker node to width and height, type 42 into seed, leave guidance at its default. They render.
Under the hood: the component sees an empty "current" set and a full "desired" set for FLUX. Every FLUX parameter lands in to_add. Nothing in to_remove or to_preserve.
Step 2 — Comparing models (FLUX → SDXL). The artist wants to see how SDXL handles the same prompt. They switch the dropdown to SDXL.
prompt,width,height→to_preserve. The connections toTextPromptandResolutionPickerare untouched.seed→to_preserve. Same name, bothint. Value42survives.guidance→to_remove. SDXL doesn't have it.steps,cfg_scale,reference_image→to_add. The SDXL-specific parameters appear, ready for the artist to tune.
The artist tweaks steps, connects an ImageURL node to reference_image, and renders. Every connection they set up in Step 1 is still live. They didn't have to redo anything.
Step 3 — Signature change (SDXL → SD3). Curious about SD3, they switch the dropdown again.
prompt,width,height,seed→to_preserve. Signatures unchanged, so nothing is touched.reference_image→to_replace. Both models use the namereference_image, but SDXL's accepts a URL string and SD3's accepts anImageArtifact. The component captures the existing connection fromImageURL, removes the old parameter, adds the newImageArtifact-typed one, and then re-dispatches the captured connection throughCreateConnectionRequest. The connection handler sees the source providesstrbut the new parameter accepts onlyImageArtifactand rejects it. Net result:reference_imageexists with the new signature, no connection, ready for the artist to connect an artifact source (e.g., aLoadImagenode).steps,cfg_scale→to_remove. SD3 doesn't use them.negative_prompt→to_add. SD3's distinguishing parameter appears, empty and ready for input.
Type narrowing behaves exactly as an artist would expect. If reference_image had previously accepted [ImageArtifact, str] with a connection providing ImageArtifact, and the new schema narrowed it to [ImageArtifact], the replace path captures the connection, rebuilds the parameter, and re-dispatches the connection — the platform's connection handler sees ImageArtifact is still valid and the edge comes back automatically. Only connections that are no longer type-compatible get dropped.
Step 4 — Clearing the node (SD3 → no selection). The artist decides this node isn't working out and selects "— none —" from the dropdown. Everything the node was managing goes to to_remove. prompt, width, height, seed, reference_image, negative_prompt — all gone. The parameter surface returns to just the Model dropdown. The artist can start fresh or pick a different approach.
What the node author writes
from griptape_nodes.exe_types.param_components.parameter_transition_component import (
ParameterTransitionComponent,
TransitionParameter,
)
# In MultiModelImageGenerator.__init__:
self._model_params = ParameterTransitionComponent(
self,
manages_parameter=lambda p: p.name in self._MODEL_PARAM_NAMES,
)
# In the model-dropdown change handler:
desired = self._build_transition_parameters_for_model(selected_model) # author's domain logic
self._model_params.transition_to(desired)
_build_transition_parameters_for_model is the author's — the component doesn't care how the list is computed (from a dataclass, a config file, a registry, hand-rolled). It only cares about the resulting list[TransitionParameter]. The author also decides how manages_parameter scopes the component's view of the node's parameters (here: a known set of model-parameter names, excluding the Model dropdown itself and any other ambient parameters the node owns).
API reference
Constructor:
ParameterTransitionComponent(
node: BaseNode,
*,
manages_parameter: Callable[[Parameter], bool],
)
node— the node instance that owns the parameter surface.manages_parameter— predicate identifying which of the node's parameters this component manages. Everything outside the predicate is left alone.
TransitionParameter fields:
name: str— parameter name, must be unique within a singletransition_tocall.allowed_modes: frozenset[ParameterMode]— which modes the parameter supports (INPUT,PROPERTY,OUTPUT). Must match the existing Parameter'sallowed_modesfor preservation.input_types: frozenset[str]— the set of types the parameter accepts as input, matching whatParameter.input_typeswill return after creation. The property applies fallbacks when no input types are declared on the underlying request, so populate this to reflect the effective signature (e.g., for an output-only parameter built withoutput_type="X", usefrozenset({"X"})becauseParameter.input_typesfalls back to[output_type]).output_type: str— the type the parameter emits, matching whatParameter.output_typewill return after creation. For an input-only parameter built withinput_types=[X, Y, Z], useXbecause the property falls back to the first input type.add_request_factory: Callable[[], AddParameterToNodeRequest]— a zero-arg callable the component invokes only for parameters that actually need adding. Typically afunctools.partialwrapping the author's builder method.
TransitionPlan fields:
to_preserve: frozenset[str]— parameters left completely untouched (name + signature both match). Connections and stored values are unaffected because nothing was dispatched.to_replace: frozenset[str]— same-named parameters whose signature differed. The component captured their existing connections, removed the old Parameter, added a new one, and re-dispatched each connection throughCreateConnectionRequest(the platform validates type compatibility and rejects ones that are no longer valid). Stored property values on replaced parameters are lost; connection values re-flow normally.to_remove: frozenset[str]— parameters removed viaRemoveParameterFromNodeRequest.to_add: frozenset[str]— parameters added viaAddParameterToNodeRequest.
Signature-match check. Two same-named parameters are considered a match when all three of allowed_modes, input_types, and output_type match exactly. The component reads these off the existing Parameter's public properties (the same accessors used elsewhere for connection type checks). Any difference — a widened input-type list, a dropped mode, a changed output type — puts the parameter in the replace bucket. This is identity equality over the effective schema, not directional type-compatibility: two parameters that happen to be assignment-compatible but were declared differently represent different schema intent.
When to reach for this
- A single node's parameter surface depends on a discrete user choice (model picker, mode selector, operation chooser).
- Users are expected to switch between choices during normal workflow authoring and should not lose their connections each time.
When NOT to reach for this
- You want to hide or show parameters based on a value — use the Dynamic Parameter Visibility pattern with
hide_parameter_by_name/show_parameter_by_name. - You only need to update a dropdown's options — use the Dynamic Options Updates pattern.
- The parameter set is fixed; only values change.
Working with ParameterGroups
The component manages individual parameters by name. If your dynamic surface uses groups, pass parent_element_name in your add_request_factory so newly-added parameters land in the right group; group creation and cleanup are handled by the caller.
Advanced ParameterList Usage
Include both individual and list types for maximum flexibility:
self.add_parameter(
ParameterList(
name="images",
input_types=[
"ImageArtifact",
"ImageUrlArtifact",
"str",
"list",
"list[ImageArtifact]",
"list[ImageUrlArtifact]",
],
default_value=[],
tooltip="Input images (up to 10 images total)",
allowed_modes={ParameterMode.INPUT},
ui_options={"expander": True, "display_name": "Input Images"},
)
)
Controlling Parameter Order in the UI
Parameters appear in the UI in the order they are added via add_parameter(). This matters for user experience - related parameters should be grouped logically.
Problem: Base classes like BaseImageProcessor automatically add parameters (e.g., input_image) in their __init__, which may not be the order you want.
Solution: Extend SuccessFailureNode directly instead of BaseImageProcessor to gain full control over parameter ordering:
from griptape_nodes.exe_types.node_types import SuccessFailureNode
from griptape_nodes.exe_types.param_types.parameter_image import ParameterImage
class ColorMatch(SuccessFailureNode):
"""Transfer colors from a reference image to a target image."""
def __init__(self, name: str, metadata: dict[Any, Any] | None = None) -> None:
super().__init__(name, metadata)
# Reference image FIRST - the source of the color palette
self.add_parameter(
ParameterImage(
name="reference_image",
tooltip="Reference image - the source of the color palette to transfer",
ui_options={"clickable_file_browser": True, "expander": True},
)
)
# Target image SECOND - the image to modify
self.add_parameter(
ParameterImage(
name="target_image",
tooltip="Target image - the image to apply the color transfer to",
ui_options={"clickable_file_browser": True, "expander": True},
)
)
# Additional parameters in desired order...
When to Use This Pattern:
- Two-image nodes where the semantic order matters (reference → target)
- Nodes requiring specific parameter groupings not provided by base classes
- When base class parameter order conflicts with your UX goals
Trade-off: You lose helper methods from specialized base classes, but gain complete control over the node's UI structure.
Two-Image Processing Node Pattern
For nodes that process two images together (blending, color matching, compositing), use this pattern:
from typing import Any, ClassVar
from PIL import Image
from griptape.artifacts import ImageUrlArtifact
from griptape_nodes.exe_types.core_types import Parameter
from griptape_nodes.exe_types.node_types import SuccessFailureNode
from griptape_nodes.exe_types.param_types.parameter_image import ParameterImage
from griptape_nodes_library.utils.image_utils import (
dict_to_image_url_artifact,
load_pil_from_url,
save_pil_image_with_named_filename,
)
from griptape_nodes_library.utils.file_utils import generate_filename
class TwoImageProcessor(SuccessFailureNode):
"""Base pattern for nodes processing two images."""
CATEGORY: ClassVar[str] = "image"
def __init__(self, name: str, metadata: dict[Any, Any] | None = None) -> None:
super().__init__(name, metadata)
# First image input
self.add_parameter(
ParameterImage(
name="image_a",
tooltip="First input image",
ui_options={"clickable_file_browser": True, "expander": True},
)
)
# Second image input
self.add_parameter(
ParameterImage(
name="image_b",
tooltip="Second input image",
ui_options={"clickable_file_browser": True, "expander": True},
)
)
# Output image
self.add_parameter(
ParameterImage(
name="output_image",
tooltip="Processed result",
allowed_modes={ParameterMode.OUTPUT},
)
)
def _get_image_artifact(self, param_name: str) -> ImageUrlArtifact | None:
"""Convert parameter value to ImageUrlArtifact."""
value = self.get_parameter_value(param_name)
if value is None:
return None
if isinstance(value, dict):
return dict_to_image_url_artifact(value)
return value
def _process_images(self) -> None:
"""Process both images and set output."""
image_a_artifact = self._get_image_artifact("image_a")
image_b_artifact = self._get_image_artifact("image_b")
if not image_a_artifact or not image_b_artifact:
return
# Load as PIL images
pil_a = load_pil_from_url(image_a_artifact.value)
pil_b = load_pil_from_url(image_b_artifact.value)
# Process images (override in subclass)
result_pil = self._do_processing(pil_a, pil_b)
# Save result
filename = generate_filename(self.name, suffix="processed")
output_artifact = save_pil_image_with_named_filename(result_pil, filename)
self.parameter_output_values["output_image"] = output_artifact
def _do_processing(self, image_a: Image.Image, image_b: Image.Image) -> Image.Image:
"""Override this method with actual processing logic."""
raise NotImplementedError
def after_value_set(self, parameter: Parameter, value: Any) -> None:
"""Trigger live preview when both images are available."""
if parameter.name in ("image_a", "image_b"):
image_a = self.get_parameter_value("image_a")
image_b = self.get_parameter_value("image_b")
if image_a and image_b:
self._process_images()
return super().after_value_set(parameter, value)
def process(self) -> None:
"""Main processing entry point."""
self._process_images()
Key Utilities Used:
dict_to_image_url_artifact(): Converts dict representation to artifactload_pil_from_url(): Loads PIL Image from URL (including localhost)save_pil_image_with_named_filename(): Saves PIL Image and returns artifactgenerate_filename(): Creates consistent filenames with node name