griptape-nodes 0.59.3__py3-none-any.whl → 0.60.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. griptape_nodes/cli/commands/libraries.py +21 -1
  2. griptape_nodes/common/macro_parser/__init__.py +28 -0
  3. griptape_nodes/common/macro_parser/core.py +230 -0
  4. griptape_nodes/common/macro_parser/exceptions.py +23 -0
  5. griptape_nodes/common/macro_parser/formats.py +170 -0
  6. griptape_nodes/common/macro_parser/matching.py +134 -0
  7. griptape_nodes/common/macro_parser/parsing.py +172 -0
  8. griptape_nodes/common/macro_parser/resolution.py +168 -0
  9. griptape_nodes/common/macro_parser/segments.py +42 -0
  10. griptape_nodes/exe_types/core_types.py +241 -4
  11. griptape_nodes/exe_types/node_types.py +7 -1
  12. griptape_nodes/exe_types/param_components/huggingface/__init__.py +1 -0
  13. griptape_nodes/exe_types/param_components/huggingface/huggingface_model_parameter.py +168 -0
  14. griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_file_parameter.py +38 -0
  15. griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_parameter.py +33 -0
  16. griptape_nodes/exe_types/param_components/huggingface/huggingface_utils.py +136 -0
  17. griptape_nodes/exe_types/param_components/log_parameter.py +136 -0
  18. griptape_nodes/exe_types/param_components/seed_parameter.py +59 -0
  19. griptape_nodes/exe_types/param_types/__init__.py +1 -0
  20. griptape_nodes/exe_types/param_types/parameter_bool.py +221 -0
  21. griptape_nodes/exe_types/param_types/parameter_float.py +179 -0
  22. griptape_nodes/exe_types/param_types/parameter_int.py +183 -0
  23. griptape_nodes/exe_types/param_types/parameter_number.py +380 -0
  24. griptape_nodes/exe_types/param_types/parameter_string.py +232 -0
  25. griptape_nodes/node_library/library_registry.py +2 -1
  26. griptape_nodes/retained_mode/events/app_events.py +21 -0
  27. griptape_nodes/retained_mode/events/os_events.py +142 -6
  28. griptape_nodes/retained_mode/events/parameter_events.py +2 -0
  29. griptape_nodes/retained_mode/griptape_nodes.py +14 -0
  30. griptape_nodes/retained_mode/managers/agent_manager.py +5 -3
  31. griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +19 -1
  32. griptape_nodes/retained_mode/managers/library_manager.py +8 -1
  33. griptape_nodes/retained_mode/managers/node_manager.py +14 -1
  34. griptape_nodes/retained_mode/managers/os_manager.py +403 -124
  35. griptape_nodes/retained_mode/managers/user_manager.py +120 -0
  36. griptape_nodes/retained_mode/managers/workflow_manager.py +44 -34
  37. griptape_nodes/traits/multi_options.py +26 -2
  38. griptape_nodes/utils/huggingface_utils.py +136 -0
  39. {griptape_nodes-0.59.3.dist-info → griptape_nodes-0.60.1.dist-info}/METADATA +1 -1
  40. {griptape_nodes-0.59.3.dist-info → griptape_nodes-0.60.1.dist-info}/RECORD +42 -19
  41. {griptape_nodes-0.59.3.dist-info → griptape_nodes-0.60.1.dist-info}/WHEEL +1 -1
  42. {griptape_nodes-0.59.3.dist-info → griptape_nodes-0.60.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,380 @@
1
+ """ParameterNumber base class for numeric parameters with step validation support."""
2
+
3
+ import math
4
+ from collections.abc import Callable
5
+ from typing import Any
6
+
7
+ from griptape_nodes.exe_types.core_types import Parameter, ParameterMode, Trait
8
+ from griptape_nodes.traits.clamp import Clamp
9
+ from griptape_nodes.traits.minmax import MinMax
10
+ from griptape_nodes.traits.slider import Slider
11
+
12
+
13
+ class ParameterNumber(Parameter):
14
+ """Base class for numeric parameters with step validation support.
15
+
16
+ This class provides common functionality for numeric parameters including
17
+ step validation, UI options, and type conversion. Subclasses should set
18
+ the appropriate type and conversion methods.
19
+ """
20
+
21
+ def __init__( # noqa: PLR0913
22
+ self,
23
+ name: str,
24
+ tooltip: str | None = None,
25
+ *,
26
+ type: str, # noqa: A002
27
+ input_types: list[str] | None = None, # noqa: ARG002
28
+ output_type: str,
29
+ default_value: Any = None,
30
+ tooltip_as_input: str | None = None,
31
+ tooltip_as_property: str | None = None,
32
+ tooltip_as_output: str | None = None,
33
+ allowed_modes: set[ParameterMode] | None = None,
34
+ traits: set[type[Trait] | Trait] | None = None,
35
+ converters: list[Callable[[Any], Any]] | None = None,
36
+ validators: list[Callable[[Parameter, Any], None]] | None = None,
37
+ ui_options: dict | None = None,
38
+ step: float | None = None,
39
+ slider: bool = False,
40
+ min_val: float = 0,
41
+ max_val: float = 100,
42
+ validate_min_max: bool = False,
43
+ accept_any: bool = True,
44
+ hide: bool = False,
45
+ hide_label: bool = False,
46
+ hide_property: bool = False,
47
+ allow_input: bool = True,
48
+ allow_property: bool = True,
49
+ allow_output: bool = True,
50
+ settable: bool = True,
51
+ serializable: bool = True,
52
+ user_defined: bool = False,
53
+ element_id: str | None = None,
54
+ element_type: str | None = None,
55
+ parent_container_name: str | None = None,
56
+ ) -> None:
57
+ """Initialize a numeric parameter with step validation.
58
+
59
+ Args:
60
+ name: Parameter name
61
+ tooltip: Parameter tooltip
62
+ type: Parameter type (should be "int" or "float")
63
+ input_types: Allowed input types
64
+ output_type: Output type (should be "int" or "float")
65
+ default_value: Default parameter value
66
+ tooltip_as_input: Tooltip for input mode
67
+ tooltip_as_property: Tooltip for property mode
68
+ tooltip_as_output: Tooltip for output mode
69
+ allowed_modes: Allowed parameter modes
70
+ traits: Parameter traits
71
+ converters: Parameter converters
72
+ validators: Parameter validators
73
+ ui_options: Dictionary of UI options
74
+ step: Step size for numeric input controls
75
+ slider: Whether to use slider trait
76
+ min_val: Minimum value for constraints
77
+ max_val: Maximum value for constraints
78
+ validate_min_max: Whether to validate min/max with error
79
+ accept_any: Whether to accept any input type and convert to number (default: True)
80
+ hide: Whether to hide the entire parameter
81
+ hide_label: Whether to hide the parameter label
82
+ hide_property: Whether to hide the parameter in property mode
83
+ allow_input: Whether to allow input mode
84
+ allow_property: Whether to allow property mode
85
+ allow_output: Whether to allow output mode
86
+ settable: Whether the parameter is settable
87
+ serializable: Whether the parameter is serializable
88
+ user_defined: Whether the parameter is user-defined
89
+ element_id: Element ID
90
+ element_type: Element type
91
+ parent_container_name: Name of parent container
92
+ """
93
+ # Build ui_options dictionary from the provided UI-specific parameters
94
+ if ui_options is None:
95
+ ui_options = {}
96
+ else:
97
+ ui_options = ui_options.copy()
98
+
99
+ # Add numeric-specific UI options if they have values
100
+ if step is not None:
101
+ ui_options["step"] = step
102
+
103
+ # Set up numeric conversion based on accept_any setting
104
+ if converters is None:
105
+ existing_converters = []
106
+ else:
107
+ existing_converters = converters
108
+
109
+ if accept_any:
110
+ final_input_types = ["any"]
111
+ final_converters = [self._convert_to_number, *existing_converters]
112
+ else:
113
+ final_input_types = [type]
114
+ final_converters = existing_converters
115
+
116
+ # Set up validators
117
+ if validators is None:
118
+ existing_validators = []
119
+ else:
120
+ existing_validators = validators
121
+
122
+ # Add step validator if step is specified
123
+ final_validators = existing_validators.copy()
124
+ if step is not None:
125
+ final_validators.append(self._create_step_validator(step))
126
+
127
+ # Set up constraint traits based on parameters
128
+ self._setup_constraint_traits(
129
+ name=name, traits=traits, slider=slider, min_val=min_val, max_val=max_val, validate_min_max=validate_min_max
130
+ )
131
+
132
+ # Call parent with explicit parameters, following ControlParameter pattern
133
+ super().__init__(
134
+ name=name,
135
+ tooltip=tooltip,
136
+ type=type,
137
+ input_types=final_input_types,
138
+ output_type=output_type,
139
+ default_value=default_value,
140
+ tooltip_as_input=tooltip_as_input,
141
+ tooltip_as_property=tooltip_as_property,
142
+ tooltip_as_output=tooltip_as_output,
143
+ allowed_modes=allowed_modes,
144
+ traits=self._constraint_traits,
145
+ converters=final_converters,
146
+ validators=final_validators,
147
+ ui_options=ui_options,
148
+ hide=hide,
149
+ hide_label=hide_label,
150
+ hide_property=hide_property,
151
+ allow_input=allow_input,
152
+ allow_property=allow_property,
153
+ allow_output=allow_output,
154
+ settable=settable,
155
+ serializable=serializable,
156
+ user_defined=user_defined,
157
+ element_id=element_id,
158
+ element_type=element_type,
159
+ parent_container_name=parent_container_name,
160
+ )
161
+
162
+ def _create_step_validator(self, step_value: float) -> Callable[[Parameter, Any], None]: # noqa: ARG002
163
+ """Create a validator function that enforces step constraints for numbers.
164
+
165
+ Args:
166
+ step_value: The step size to enforce
167
+
168
+ Returns:
169
+ A validator function that raises ValueError if the value is not a multiple of step
170
+ """
171
+
172
+ def validate_step(param: Parameter, value: Any) -> None:
173
+ if value is None:
174
+ return
175
+
176
+ if not isinstance(value, (int, float)):
177
+ return # Let other validators handle type issues
178
+
179
+ # Get the current step value from the parameter's UI options
180
+ current_step = param.ui_options.get("step")
181
+ if current_step is None:
182
+ return # No step constraint
183
+
184
+ # For numbers, we need to check if the value is approximately a multiple of step
185
+ # due to floating point precision issues
186
+ remainder = value % current_step
187
+ # Use math.isclose() for proper floating-point comparison
188
+ if not (
189
+ math.isclose(remainder, 0.0, abs_tol=1e-10) or math.isclose(remainder, current_step, abs_tol=1e-10)
190
+ ):
191
+ msg = f"Value {value} is not a multiple of step {current_step}"
192
+ raise ValueError(msg)
193
+
194
+ return validate_step
195
+
196
+ def _convert_to_number(self, value: Any) -> int | float:
197
+ """Convert any input value to a number.
198
+
199
+ This is an abstract method that subclasses must implement to provide
200
+ the appropriate type conversion (int or float).
201
+
202
+ Args:
203
+ value: The value to convert
204
+
205
+ Returns:
206
+ The converted number
207
+
208
+ Raises:
209
+ NotImplementedError: If not implemented by subclass
210
+ """
211
+ msg = f"{self.name}: Subclasses must implement _convert_to_number"
212
+ raise NotImplementedError(msg)
213
+
214
+ def _setup_constraint_traits( # noqa: PLR0913
215
+ self,
216
+ name: str,
217
+ traits: set[type[Trait] | Trait] | None,
218
+ *,
219
+ slider: bool,
220
+ min_val: float,
221
+ max_val: float,
222
+ validate_min_max: bool,
223
+ ) -> None:
224
+ """Set up constraint traits based on parameters.
225
+
226
+ Args:
227
+ name: Parameter name for error messages
228
+ traits: Existing traits set
229
+ slider: Whether to use slider trait
230
+ min_val: Minimum value
231
+ max_val: Maximum value
232
+ validate_min_max: Whether to validate min/max with error
233
+ """
234
+ # Validation rules
235
+ if min_val is not None and max_val is None:
236
+ msg = f"{name}: If min_val is provided, max_val must also be provided"
237
+ raise ValueError(msg)
238
+ if max_val is not None and min_val is None:
239
+ msg = f"{name}: If max_val is provided, min_val must also be provided"
240
+ raise ValueError(msg)
241
+ if slider and (min_val is None or max_val is None):
242
+ msg = f"{name}: If slider is True, both min_val and max_val must be provided"
243
+ raise ValueError(msg)
244
+ if validate_min_max and (min_val is None or max_val is None):
245
+ msg = f"{name}: If validate_min_max is True, both min_val and max_val must be provided"
246
+ raise ValueError(msg)
247
+
248
+ # Set up traits based on parameters
249
+ if traits is None:
250
+ traits = set()
251
+ else:
252
+ traits = set(traits)
253
+
254
+ # Add constraint trait based on priority: Slider > MinMax > Clamp
255
+ if slider and min_val is not None and max_val is not None:
256
+ traits.add(Slider(min_val=min_val, max_val=max_val))
257
+ elif validate_min_max and min_val is not None and max_val is not None:
258
+ traits.add(MinMax(min_val=min_val, max_val=max_val))
259
+ elif min_val is not None and max_val is not None:
260
+ traits.add(Clamp(min_val=min_val, max_val=max_val))
261
+
262
+ # Store traits for later use
263
+ self._constraint_traits = traits
264
+
265
+ # Store min/max values for runtime property access
266
+ self._min_val = min_val
267
+ self._max_val = max_val
268
+
269
+ @property
270
+ def slider(self) -> bool:
271
+ """Whether slider trait is active."""
272
+ return any(isinstance(trait, Slider) for trait in self.find_elements_by_type(Trait))
273
+
274
+ @slider.setter
275
+ def slider(self, value: bool) -> None:
276
+ """Set slider trait."""
277
+ if value:
278
+ if not hasattr(self, "_constraint_traits") or not self._constraint_traits:
279
+ msg = f"{self.name}: Cannot enable slider without min_val and max_val"
280
+ raise ValueError(msg)
281
+ # Find existing constraint traits and replace with slider
282
+ self._remove_constraint_traits()
283
+ # Get min/max from existing traits or use defaults
284
+ min_val = getattr(self, "_min_val", 0)
285
+ max_val = getattr(self, "_max_val", 100)
286
+ self.add_trait(Slider(min_val=min_val, max_val=max_val))
287
+ else:
288
+ # Remove slider trait
289
+ slider_traits = self.find_elements_by_type(Slider)
290
+ for trait in slider_traits:
291
+ self.remove_trait(trait)
292
+
293
+ @property
294
+ def min_val(self) -> float | None:
295
+ """Get minimum value from constraint traits."""
296
+ for trait in self.find_elements_by_type(Trait):
297
+ if hasattr(trait, "min"):
298
+ return getattr(trait, "min", None)
299
+ return None
300
+
301
+ @min_val.setter
302
+ def min_val(self, value: float | None) -> None:
303
+ """Set minimum value and update constraint traits."""
304
+ if value is not None and self.max_val is None:
305
+ msg = f"{self.name}: Cannot set min_val without max_val"
306
+ raise ValueError(msg)
307
+ self._min_val = value
308
+ self._update_constraint_traits()
309
+
310
+ @property
311
+ def max_val(self) -> float | None:
312
+ """Get maximum value from constraint traits."""
313
+ for trait in self.find_elements_by_type(Trait):
314
+ if hasattr(trait, "max"):
315
+ return getattr(trait, "max", None)
316
+ return None
317
+
318
+ @max_val.setter
319
+ def max_val(self, value: float | None) -> None:
320
+ """Set maximum value and update constraint traits."""
321
+ if value is not None and self.min_val is None:
322
+ msg = f"{self.name}: Cannot set max_val without min_val"
323
+ raise ValueError(msg)
324
+ self._max_val = value
325
+ self._update_constraint_traits()
326
+
327
+ @property
328
+ def validate_min_max(self) -> bool:
329
+ """Whether MinMax trait is active."""
330
+ return any(isinstance(trait, MinMax) for trait in self.find_elements_by_type(Trait))
331
+
332
+ @validate_min_max.setter
333
+ def validate_min_max(self, value: bool) -> None:
334
+ """Set MinMax validation."""
335
+ if value:
336
+ # Check if we have stored min/max values
337
+ min_val = getattr(self, "_min_val", None)
338
+ max_val = getattr(self, "_max_val", None)
339
+ if min_val is None or max_val is None:
340
+ msg = f"{self.name}: Cannot enable validate_min_max without min_val and max_val"
341
+ raise ValueError(msg)
342
+ # Replace existing constraint traits with MinMax
343
+ self._remove_constraint_traits()
344
+ self.add_trait(MinMax(min_val=min_val, max_val=max_val))
345
+ else:
346
+ # Remove MinMax trait and replace with Clamp if we have min/max
347
+ min_max_traits = self.find_elements_by_type(MinMax)
348
+ for trait in min_max_traits:
349
+ self.remove_trait(trait)
350
+ min_val = getattr(self, "_min_val", None)
351
+ max_val = getattr(self, "_max_val", None)
352
+ if min_val is not None and max_val is not None:
353
+ self.add_trait(Clamp(min_val=min_val, max_val=max_val))
354
+
355
+ def _remove_constraint_traits(self) -> None:
356
+ """Remove all constraint traits."""
357
+ for trait_type in [Slider, MinMax, Clamp]:
358
+ traits = self.find_elements_by_type(trait_type)
359
+ for trait in traits:
360
+ self.remove_trait(trait)
361
+
362
+ def _update_constraint_traits(self) -> None:
363
+ """Update constraint traits based on current min/max values."""
364
+ min_val = getattr(self, "_min_val", None)
365
+ max_val = getattr(self, "_max_val", None)
366
+
367
+ if min_val is None or max_val is None:
368
+ self._remove_constraint_traits()
369
+ return
370
+
371
+ # Determine which trait to use based on current state
372
+ if self.slider:
373
+ self._remove_constraint_traits()
374
+ self.add_trait(Slider(min_val=min_val, max_val=max_val))
375
+ elif self.validate_min_max:
376
+ self._remove_constraint_traits()
377
+ self.add_trait(MinMax(min_val=min_val, max_val=max_val))
378
+ else:
379
+ self._remove_constraint_traits()
380
+ self.add_trait(Clamp(min_val=min_val, max_val=max_val))
@@ -0,0 +1,232 @@
1
+ """ParameterString component for string inputs with enhanced UI options."""
2
+
3
+ from collections.abc import Callable
4
+ from typing import Any
5
+
6
+ from griptape_nodes.exe_types.core_types import Parameter, ParameterMode, Trait
7
+
8
+
9
+ class ParameterString(Parameter):
10
+ """A specialized Parameter class for string inputs with enhanced UI options.
11
+
12
+ This class provides a convenient way to create string parameters with common
13
+ UI customizations like markdown support, multiline input, and placeholder text.
14
+ It exposes these UI options as direct properties for easy runtime modification.
15
+
16
+ Example:
17
+ param = ParameterString(
18
+ name="description",
19
+ tooltip="Enter a description",
20
+ markdown=True,
21
+ multiline=True,
22
+ placeholder_text="Type your description here..."
23
+ )
24
+ param.multiline = False # Change UI options at runtime
25
+ """
26
+
27
+ def __init__( # noqa: PLR0913
28
+ self,
29
+ name: str,
30
+ tooltip: str | None = None,
31
+ *,
32
+ type: str = "str", # noqa: A002, ARG002
33
+ input_types: list[str] | None = None, # noqa: ARG002
34
+ output_type: str = "str", # noqa: ARG002
35
+ default_value: Any = None,
36
+ tooltip_as_input: str | None = None,
37
+ tooltip_as_property: str | None = None,
38
+ tooltip_as_output: str | None = None,
39
+ allowed_modes: set[ParameterMode] | None = None,
40
+ traits: set[type[Trait] | Trait] | None = None,
41
+ converters: list[Callable[[Any], Any]] | None = None,
42
+ validators: list[Callable[[Parameter, Any], None]] | None = None,
43
+ ui_options: dict | None = None,
44
+ markdown: bool = False,
45
+ multiline: bool = False,
46
+ placeholder_text: str | None = None,
47
+ accept_any: bool = True,
48
+ hide: bool = False,
49
+ hide_label: bool = False,
50
+ hide_property: bool = False,
51
+ allow_input: bool = True,
52
+ allow_property: bool = True,
53
+ allow_output: bool = True,
54
+ settable: bool = True,
55
+ serializable: bool = True,
56
+ user_defined: bool = False,
57
+ element_id: str | None = None,
58
+ element_type: str | None = None,
59
+ parent_container_name: str | None = None,
60
+ ) -> None:
61
+ """Initialize a string parameter with enhanced UI options.
62
+
63
+ Args:
64
+ name: Parameter name
65
+ tooltip: Parameter tooltip
66
+ type: Parameter type (ignored, always "str" for ParameterString)
67
+ input_types: Allowed input types (ignored, set based on accept_any)
68
+ output_type: Output type (ignored, always "str" for ParameterString)
69
+ default_value: Default parameter value
70
+ tooltip_as_input: Tooltip for input mode
71
+ tooltip_as_property: Tooltip for property mode
72
+ tooltip_as_output: Tooltip for output mode
73
+ allowed_modes: Allowed parameter modes
74
+ traits: Parameter traits
75
+ converters: Parameter converters
76
+ validators: Parameter validators
77
+ ui_options: Dictionary of UI options
78
+ markdown: Whether to enable markdown rendering
79
+ multiline: Whether to use multiline input
80
+ placeholder_text: Placeholder text for the input field
81
+ accept_any: Whether to accept any input type and convert to string (default: True)
82
+ hide: Whether to hide the entire parameter
83
+ hide_label: Whether to hide the parameter label
84
+ hide_property: Whether to hide the parameter in property mode
85
+ allow_input: Whether to allow input mode
86
+ allow_property: Whether to allow property mode
87
+ allow_output: Whether to allow output mode
88
+ settable: Whether the parameter is settable
89
+ serializable: Whether the parameter is serializable
90
+ user_defined: Whether the parameter is user-defined
91
+ element_id: Element ID
92
+ element_type: Element type
93
+ parent_container_name: Name of parent container
94
+ """
95
+ # Build ui_options dictionary from the provided UI-specific parameters
96
+ if ui_options is None:
97
+ ui_options = {}
98
+ else:
99
+ ui_options = ui_options.copy()
100
+
101
+ # Add string-specific UI options if they have values
102
+ if markdown:
103
+ ui_options["markdown"] = markdown
104
+ if multiline:
105
+ ui_options["multiline"] = multiline
106
+ if placeholder_text is not None:
107
+ ui_options["placeholder_text"] = placeholder_text
108
+
109
+ # Set up string conversion based on accept_any setting
110
+ if converters is None:
111
+ existing_converters = []
112
+ else:
113
+ existing_converters = converters
114
+
115
+ if accept_any:
116
+ final_input_types = ["any"]
117
+ final_converters = [self._accept_any, *existing_converters]
118
+ else:
119
+ final_input_types = ["str"]
120
+ final_converters = existing_converters
121
+
122
+ # Call parent with explicit parameters, following ControlParameter pattern
123
+ super().__init__(
124
+ name=name,
125
+ tooltip=tooltip,
126
+ type="str", # Always a string type for ParameterString
127
+ input_types=final_input_types,
128
+ output_type="str", # Always output as string
129
+ default_value=default_value,
130
+ tooltip_as_input=tooltip_as_input,
131
+ tooltip_as_property=tooltip_as_property,
132
+ tooltip_as_output=tooltip_as_output,
133
+ allowed_modes=allowed_modes,
134
+ traits=traits,
135
+ converters=final_converters,
136
+ validators=validators,
137
+ ui_options=ui_options,
138
+ hide=hide,
139
+ hide_label=hide_label,
140
+ hide_property=hide_property,
141
+ allow_input=allow_input,
142
+ allow_property=allow_property,
143
+ allow_output=allow_output,
144
+ settable=settable,
145
+ serializable=serializable,
146
+ user_defined=user_defined,
147
+ element_id=element_id,
148
+ element_type=element_type,
149
+ parent_container_name=parent_container_name,
150
+ )
151
+
152
+ def _accept_any(self, value: Any) -> str:
153
+ """Convert any input value to a string.
154
+
155
+ Args:
156
+ value: The value to convert to string
157
+
158
+ Returns:
159
+ String representation of the value
160
+ """
161
+ if value is None:
162
+ return ""
163
+ return str(value)
164
+
165
+ @property
166
+ def markdown(self) -> bool:
167
+ """Get whether markdown rendering is enabled.
168
+
169
+ Returns:
170
+ True if markdown is enabled, False otherwise
171
+ """
172
+ return self.ui_options.get("markdown", False)
173
+
174
+ @markdown.setter
175
+ def markdown(self, value: bool) -> None:
176
+ """Set whether markdown rendering is enabled.
177
+
178
+ Args:
179
+ value: Whether to enable markdown rendering
180
+ """
181
+ if value:
182
+ self.update_ui_options_key("markdown", value)
183
+ else:
184
+ ui_options = self.ui_options.copy()
185
+ ui_options.pop("markdown", None)
186
+ self.ui_options = ui_options
187
+
188
+ @property
189
+ def multiline(self) -> bool:
190
+ """Get whether multiline input is enabled.
191
+
192
+ Returns:
193
+ True if multiline is enabled, False otherwise
194
+ """
195
+ return self.ui_options.get("multiline", False)
196
+
197
+ @multiline.setter
198
+ def multiline(self, value: bool) -> None:
199
+ """Set whether multiline input is enabled.
200
+
201
+ Args:
202
+ value: Whether to enable multiline input
203
+ """
204
+ if value:
205
+ self.update_ui_options_key("multiline", value)
206
+ else:
207
+ ui_options = self.ui_options.copy()
208
+ ui_options.pop("multiline", None)
209
+ self.ui_options = ui_options
210
+
211
+ @property
212
+ def placeholder_text(self) -> str | None:
213
+ """Get the placeholder text for the input field.
214
+
215
+ Returns:
216
+ The placeholder text if set, None otherwise
217
+ """
218
+ return self.ui_options.get("placeholder_text")
219
+
220
+ @placeholder_text.setter
221
+ def placeholder_text(self, value: str | None) -> None:
222
+ """Set the placeholder text for the input field.
223
+
224
+ Args:
225
+ value: The placeholder text to use, or None to remove it
226
+ """
227
+ if value is None:
228
+ ui_options = self.ui_options.copy()
229
+ ui_options.pop("placeholder_text", None)
230
+ self.ui_options = ui_options
231
+ else:
232
+ self.update_ui_options_key("placeholder_text", value)
@@ -327,7 +327,8 @@ class Library:
327
327
  """Create a new node instance of the specified type."""
328
328
  node_class = self._node_types.get(node_type)
329
329
  if not node_class:
330
- raise KeyError(self._library_data.name, node_type)
330
+ msg = f"Node type '{node_type}' not found in library '{self._library_data.name}'"
331
+ raise KeyError(msg)
331
332
  # Inject the metadata ABOUT the node from the Library
332
333
  # into the node's metadata blob.
333
334
  if metadata is None:
@@ -11,6 +11,23 @@ from griptape_nodes.retained_mode.events.base_events import (
11
11
  from griptape_nodes.retained_mode.events.payload_registry import PayloadRegistry
12
12
 
13
13
 
14
+ @dataclass
15
+ class OrganizationInfo:
16
+ """Organization information from Griptape Cloud."""
17
+
18
+ id: str
19
+ name: str
20
+
21
+
22
+ @dataclass
23
+ class UserInfo:
24
+ """User information from Griptape Cloud."""
25
+
26
+ id: str
27
+ email: str
28
+ name: str | None = None
29
+
30
+
14
31
  @dataclass
15
32
  @PayloadRegistry.register
16
33
  class AppStartSessionRequest(RequestPayload):
@@ -201,6 +218,8 @@ class EngineHeartbeatResultSuccess(ResultPayloadSuccess):
201
218
  workflow_file_path: Path to workflow file (None if none)
202
219
  has_active_flow: Whether there's an active flow running
203
220
  engine_name: Human-readable engine name
221
+ user: User information including ID, email, and name (None if not logged in)
222
+ user_organization: User's organization information including ID and name (None if not logged in)
204
223
  """
205
224
 
206
225
  heartbeat_id: str
@@ -216,6 +235,8 @@ class EngineHeartbeatResultSuccess(ResultPayloadSuccess):
216
235
  workflow_file_path: str | None
217
236
  has_active_flow: bool
218
237
  engine_name: str
238
+ user: UserInfo | None
239
+ user_organization: OrganizationInfo | None
219
240
 
220
241
 
221
242
  @dataclass