griptape-nodes 0.66.2__py3-none-any.whl → 0.68.0__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.
- griptape_nodes/bootstrap/utils/python_subprocess_executor.py +17 -4
- griptape_nodes/common/node_executor.py +295 -18
- griptape_nodes/exe_types/core_types.py +28 -1
- griptape_nodes/exe_types/node_groups/__init__.py +2 -2
- griptape_nodes/exe_types/node_groups/base_iterative_node_group.py +81 -10
- griptape_nodes/exe_types/node_groups/base_node_group.py +64 -1
- griptape_nodes/exe_types/node_groups/subflow_node_group.py +0 -34
- griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_variant_parameter.py +152 -0
- griptape_nodes/exe_types/param_components/seed_parameter.py +3 -2
- griptape_nodes/exe_types/param_types/parameter_audio.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_bool.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_button.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_dict.py +151 -0
- griptape_nodes/exe_types/param_types/parameter_float.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_image.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_int.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_json.py +268 -0
- griptape_nodes/exe_types/param_types/parameter_number.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_range.py +393 -0
- griptape_nodes/exe_types/param_types/parameter_string.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_three_d.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_video.py +3 -0
- griptape_nodes/retained_mode/events/library_events.py +2 -0
- griptape_nodes/retained_mode/events/parameter_events.py +89 -1
- griptape_nodes/retained_mode/managers/event_manager.py +176 -10
- griptape_nodes/retained_mode/managers/flow_manager.py +2 -1
- griptape_nodes/retained_mode/managers/library_manager.py +14 -4
- griptape_nodes/retained_mode/managers/node_manager.py +187 -7
- griptape_nodes/retained_mode/managers/workflow_manager.py +58 -16
- griptape_nodes/utils/file_utils.py +58 -0
- {griptape_nodes-0.66.2.dist-info → griptape_nodes-0.68.0.dist-info}/METADATA +1 -1
- {griptape_nodes-0.66.2.dist-info → griptape_nodes-0.68.0.dist-info}/RECORD +34 -30
- {griptape_nodes-0.66.2.dist-info → griptape_nodes-0.68.0.dist-info}/WHEEL +1 -1
- {griptape_nodes-0.66.2.dist-info → griptape_nodes-0.68.0.dist-info}/entry_points.txt +0 -0
|
@@ -67,9 +67,28 @@ class EventManager:
|
|
|
67
67
|
return self._event_queue
|
|
68
68
|
|
|
69
69
|
def should_suppress_event(self, event: BaseEvent | ProgressEvent) -> bool:
|
|
70
|
-
"""Check if events should be suppressed from being sent to websockets.
|
|
70
|
+
"""Check if events should be suppressed from being sent to websockets.
|
|
71
|
+
|
|
72
|
+
This method checks both the wrapper event type and the payload type for wrapped events.
|
|
73
|
+
For example, if InvolvedNodesEvent is in the suppression set, an ExecutionGriptapeNodeEvent
|
|
74
|
+
that wraps an InvolvedNodesEvent will be suppressed.
|
|
75
|
+
"""
|
|
71
76
|
event_type = type(event)
|
|
72
|
-
|
|
77
|
+
|
|
78
|
+
# Check wrapper type first
|
|
79
|
+
if self._event_suppression_counts.get(event_type, 0) > 0:
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
# For wrapped events (like ExecutionGriptapeNodeEvent), also check the payload type
|
|
83
|
+
wrapped_event = getattr(event, "wrapped_event", None)
|
|
84
|
+
if wrapped_event is not None:
|
|
85
|
+
payload = getattr(wrapped_event, "payload", None)
|
|
86
|
+
if payload is not None:
|
|
87
|
+
payload_type = type(payload)
|
|
88
|
+
if self._event_suppression_counts.get(payload_type, 0) > 0:
|
|
89
|
+
return True
|
|
90
|
+
|
|
91
|
+
return False
|
|
73
92
|
|
|
74
93
|
def clear_event_suppression(self) -> None:
|
|
75
94
|
"""Clear all event suppression counts."""
|
|
@@ -433,11 +452,14 @@ class EventTranslationContext:
|
|
|
433
452
|
self.manager = manager
|
|
434
453
|
self.node_name_mapping = node_name_mapping
|
|
435
454
|
self.original_put_event: Any = None
|
|
455
|
+
self.original_aput_event: Any = None
|
|
436
456
|
|
|
437
457
|
def __enter__(self) -> None:
|
|
438
458
|
"""Enter the context and start translating events."""
|
|
439
459
|
self.original_put_event = self.manager.put_event
|
|
460
|
+
self.original_aput_event = self.manager.aput_event
|
|
440
461
|
self.manager.put_event = self._translate_and_put # type: ignore[method-assign]
|
|
462
|
+
self.manager.aput_event = self._translate_and_aput # type: ignore[method-assign]
|
|
441
463
|
|
|
442
464
|
def __exit__(
|
|
443
465
|
self,
|
|
@@ -447,24 +469,168 @@ class EventTranslationContext:
|
|
|
447
469
|
) -> None:
|
|
448
470
|
"""Exit the context and restore original event sending."""
|
|
449
471
|
self.manager.put_event = self.original_put_event # type: ignore[method-assign]
|
|
472
|
+
self.manager.aput_event = self.original_aput_event # type: ignore[method-assign]
|
|
450
473
|
|
|
451
|
-
def
|
|
452
|
-
"""Translate node names in
|
|
474
|
+
def _translate_event(self, event: Any) -> Any:
|
|
475
|
+
"""Translate node names in an event.
|
|
453
476
|
|
|
454
477
|
Args:
|
|
455
|
-
event: The event to potentially translate
|
|
478
|
+
event: The event to potentially translate
|
|
479
|
+
|
|
480
|
+
Returns:
|
|
481
|
+
The translated event, or the original if no translation needed
|
|
456
482
|
"""
|
|
483
|
+
# Handle wrapped events (like ExecutionGriptapeNodeEvent)
|
|
484
|
+
wrapped_event = getattr(event, "wrapped_event", None)
|
|
485
|
+
if wrapped_event is not None:
|
|
486
|
+
payload = getattr(wrapped_event, "payload", None)
|
|
487
|
+
if payload is not None:
|
|
488
|
+
translated_payload = self._translate_payload(payload)
|
|
489
|
+
if translated_payload is not payload:
|
|
490
|
+
# Create a new wrapped event with the translated payload
|
|
491
|
+
translated_event = self._create_translated_wrapped_event(event, translated_payload)
|
|
492
|
+
if translated_event is not None:
|
|
493
|
+
return translated_event
|
|
494
|
+
|
|
457
495
|
# Check if event has node_name attribute and needs translation
|
|
458
496
|
if hasattr(event, "node_name"):
|
|
459
497
|
node_name = event.node_name
|
|
460
498
|
if node_name in self.node_name_mapping:
|
|
461
499
|
# Create a copy of the event with the translated node name
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
500
|
+
return self._copy_event_with_translated_name(event)
|
|
501
|
+
|
|
502
|
+
return event
|
|
503
|
+
|
|
504
|
+
def _translate_and_put(self, event: Any) -> None:
|
|
505
|
+
"""Translate node names in events and put them in the queue (sync version).
|
|
506
|
+
|
|
507
|
+
Args:
|
|
508
|
+
event: The event to potentially translate and send
|
|
509
|
+
"""
|
|
510
|
+
translated_event = self._translate_event(event)
|
|
511
|
+
self.original_put_event(translated_event)
|
|
512
|
+
|
|
513
|
+
async def _translate_and_aput(self, event: Any) -> None:
|
|
514
|
+
"""Translate node names in events and put them in the queue (async version).
|
|
515
|
+
|
|
516
|
+
Args:
|
|
517
|
+
event: The event to potentially translate and send
|
|
518
|
+
"""
|
|
519
|
+
translated_event = self._translate_event(event)
|
|
520
|
+
await self.original_aput_event(translated_event)
|
|
521
|
+
|
|
522
|
+
def _translate_payload(self, payload: Any) -> Any:
|
|
523
|
+
"""Translate node names in a payload.
|
|
524
|
+
|
|
525
|
+
Handles both single node_name and involved_nodes list.
|
|
526
|
+
|
|
527
|
+
Args:
|
|
528
|
+
payload: The payload to translate
|
|
529
|
+
|
|
530
|
+
Returns:
|
|
531
|
+
A new payload with translated names, or the original if no translation needed
|
|
532
|
+
"""
|
|
533
|
+
# Handle involved_nodes list (e.g., InvolvedNodesEvent)
|
|
534
|
+
involved_nodes = getattr(payload, "involved_nodes", None)
|
|
535
|
+
if involved_nodes is not None and isinstance(involved_nodes, list):
|
|
536
|
+
translated_nodes: list[str] = []
|
|
537
|
+
any_translated = False
|
|
538
|
+
for node_name in involved_nodes:
|
|
539
|
+
if node_name in self.node_name_mapping:
|
|
540
|
+
translated_nodes.append(self.node_name_mapping[node_name])
|
|
541
|
+
any_translated = True
|
|
542
|
+
else:
|
|
543
|
+
translated_nodes.append(node_name)
|
|
544
|
+
# Only create new payload if something was translated
|
|
545
|
+
if any_translated:
|
|
546
|
+
return self._copy_payload_with_translated_involved_nodes(payload, translated_nodes)
|
|
547
|
+
|
|
548
|
+
# Handle single node_name
|
|
549
|
+
node_name = getattr(payload, "node_name", None)
|
|
550
|
+
if node_name is not None and node_name in self.node_name_mapping:
|
|
551
|
+
return self._copy_payload_with_translated_node_name(payload, self.node_name_mapping[node_name])
|
|
552
|
+
|
|
553
|
+
return payload
|
|
554
|
+
|
|
555
|
+
def _copy_payload_with_translated_involved_nodes(self, payload: Any, translated_nodes: list[str]) -> Any:
|
|
556
|
+
"""Create a copy of a payload with translated involved_nodes.
|
|
557
|
+
|
|
558
|
+
Args:
|
|
559
|
+
payload: The payload to copy
|
|
560
|
+
translated_nodes: The translated list of node names
|
|
561
|
+
|
|
562
|
+
Returns:
|
|
563
|
+
A new payload instance with translated involved_nodes
|
|
564
|
+
"""
|
|
565
|
+
payload_class = type(payload)
|
|
566
|
+
|
|
567
|
+
if hasattr(payload, "model_dump"):
|
|
568
|
+
payload_dict = payload.model_dump()
|
|
569
|
+
elif hasattr(payload, "__dict__"):
|
|
570
|
+
payload_dict = payload.__dict__.copy()
|
|
571
|
+
else:
|
|
572
|
+
return payload
|
|
573
|
+
|
|
574
|
+
payload_dict["involved_nodes"] = translated_nodes
|
|
575
|
+
|
|
576
|
+
try:
|
|
577
|
+
return payload_class(**payload_dict)
|
|
578
|
+
except Exception:
|
|
579
|
+
return payload
|
|
580
|
+
|
|
581
|
+
def _copy_payload_with_translated_node_name(self, payload: Any, translated_name: str) -> Any:
|
|
582
|
+
"""Create a copy of a payload with a translated node_name.
|
|
583
|
+
|
|
584
|
+
Args:
|
|
585
|
+
payload: The payload to copy
|
|
586
|
+
translated_name: The translated node name
|
|
587
|
+
|
|
588
|
+
Returns:
|
|
589
|
+
A new payload instance with translated node_name
|
|
590
|
+
"""
|
|
591
|
+
payload_class = type(payload)
|
|
592
|
+
|
|
593
|
+
if hasattr(payload, "model_dump"):
|
|
594
|
+
payload_dict = payload.model_dump()
|
|
595
|
+
elif hasattr(payload, "__dict__"):
|
|
596
|
+
payload_dict = payload.__dict__.copy()
|
|
597
|
+
else:
|
|
598
|
+
return payload
|
|
465
599
|
|
|
466
|
-
|
|
467
|
-
|
|
600
|
+
payload_dict["node_name"] = translated_name
|
|
601
|
+
|
|
602
|
+
try:
|
|
603
|
+
return payload_class(**payload_dict)
|
|
604
|
+
except Exception:
|
|
605
|
+
return payload
|
|
606
|
+
|
|
607
|
+
def _create_translated_wrapped_event(self, event: Any, translated_payload: Any) -> Any | None:
|
|
608
|
+
"""Create a new wrapped event with a translated payload.
|
|
609
|
+
|
|
610
|
+
Args:
|
|
611
|
+
event: The original wrapped event (e.g., ExecutionGriptapeNodeEvent)
|
|
612
|
+
translated_payload: The translated payload
|
|
613
|
+
|
|
614
|
+
Returns:
|
|
615
|
+
A new wrapped event with the translated payload, or None if creation fails
|
|
616
|
+
"""
|
|
617
|
+
wrapped_event = getattr(event, "wrapped_event", None)
|
|
618
|
+
if wrapped_event is None:
|
|
619
|
+
return None
|
|
620
|
+
|
|
621
|
+
# Create new wrapped_event with translated payload
|
|
622
|
+
wrapped_class = type(wrapped_event)
|
|
623
|
+
try:
|
|
624
|
+
new_wrapped = wrapped_class(payload=translated_payload)
|
|
625
|
+
except Exception:
|
|
626
|
+
return None
|
|
627
|
+
|
|
628
|
+
# Create new outer event with new wrapped_event
|
|
629
|
+
event_class = type(event)
|
|
630
|
+
try:
|
|
631
|
+
return event_class(wrapped_event=new_wrapped)
|
|
632
|
+
except Exception:
|
|
633
|
+
return None
|
|
468
634
|
|
|
469
635
|
def _copy_event_with_translated_name(self, event: Any) -> Any:
|
|
470
636
|
"""Create a copy of an event with the node name translated to the original name.
|
|
@@ -2319,11 +2319,12 @@ class FlowManager:
|
|
|
2319
2319
|
start_node_parameter_value_commands.append(param_value_command)
|
|
2320
2320
|
|
|
2321
2321
|
# Create parameter command for start node (following single-node pattern exactly)
|
|
2322
|
+
# Use source parameter's default value to ensure type-safe propagation during connection creation
|
|
2322
2323
|
add_param_request = AddParameterToNodeRequest(
|
|
2323
2324
|
node_name=start_node_name,
|
|
2324
2325
|
parameter_name=param_name,
|
|
2325
2326
|
type=source_param.output_type,
|
|
2326
|
-
default_value=
|
|
2327
|
+
default_value=source_param.default_value,
|
|
2327
2328
|
tooltip=f"Parameter {target_parameter_name} from node {target_node_name} in packaged flow",
|
|
2328
2329
|
initial_setup=True,
|
|
2329
2330
|
)
|
|
@@ -170,7 +170,7 @@ from griptape_nodes.retained_mode.managers.os_manager import OSManager
|
|
|
170
170
|
from griptape_nodes.retained_mode.managers.settings import LIBRARIES_TO_DOWNLOAD_KEY, LIBRARIES_TO_REGISTER_KEY
|
|
171
171
|
from griptape_nodes.utils.async_utils import subprocess_run
|
|
172
172
|
from griptape_nodes.utils.dict_utils import merge_dicts
|
|
173
|
-
from griptape_nodes.utils.file_utils import find_file_in_directory
|
|
173
|
+
from griptape_nodes.utils.file_utils import find_file_in_directory, find_files_recursive
|
|
174
174
|
from griptape_nodes.utils.git_utils import (
|
|
175
175
|
GitCloneError,
|
|
176
176
|
GitPullError,
|
|
@@ -1244,6 +1244,7 @@ class LibraryManager:
|
|
|
1244
1244
|
LibraryRegistry.get_library(name=library_name)
|
|
1245
1245
|
return RegisterLibraryFromFileResultSuccess(
|
|
1246
1246
|
library_name=library_name,
|
|
1247
|
+
was_already_loaded=True,
|
|
1247
1248
|
result_details=f"Library '{library_name}' already loaded",
|
|
1248
1249
|
)
|
|
1249
1250
|
except KeyError:
|
|
@@ -1278,6 +1279,7 @@ class LibraryManager:
|
|
|
1278
1279
|
# Already loaded and good to go
|
|
1279
1280
|
return RegisterLibraryFromFileResultSuccess(
|
|
1280
1281
|
library_name=library_info.library_name,
|
|
1282
|
+
was_already_loaded=True,
|
|
1281
1283
|
result_details=f"Library '{library_info.library_name}' already loaded",
|
|
1282
1284
|
)
|
|
1283
1285
|
|
|
@@ -3107,7 +3109,7 @@ class LibraryManager:
|
|
|
3107
3109
|
problems=problems,
|
|
3108
3110
|
)
|
|
3109
3111
|
|
|
3110
|
-
async def load_libraries_request(self, request: LoadLibrariesRequest) -> ResultPayload: # noqa: ARG002, C901
|
|
3112
|
+
async def load_libraries_request(self, request: LoadLibrariesRequest) -> ResultPayload: # noqa: ARG002, C901, PLR0912
|
|
3111
3113
|
"""Load all libraries from configuration (backward compatibility wrapper).
|
|
3112
3114
|
|
|
3113
3115
|
This is the legacy entry point that loads all configured libraries.
|
|
@@ -3154,6 +3156,14 @@ class LibraryManager:
|
|
|
3154
3156
|
else:
|
|
3155
3157
|
library_name = lib_path
|
|
3156
3158
|
|
|
3159
|
+
# Check if library was already loaded (skip event emission if so)
|
|
3160
|
+
if isinstance(load_result, RegisterLibraryFromFileResultSuccess) and load_result.was_already_loaded:
|
|
3161
|
+
# Library was already loaded - skip events and continue
|
|
3162
|
+
loaded_count += 1
|
|
3163
|
+
continue
|
|
3164
|
+
|
|
3165
|
+
# Library was actually loaded or failed - emit appropriate events
|
|
3166
|
+
|
|
3157
3167
|
# Emit loading event
|
|
3158
3168
|
GriptapeNodes.EventManager().put_event(
|
|
3159
3169
|
AppEvent(
|
|
@@ -3230,8 +3240,8 @@ class LibraryManager:
|
|
|
3230
3240
|
def process_path(path: Path) -> None:
|
|
3231
3241
|
"""Process a path, handling both files and directories."""
|
|
3232
3242
|
if path.is_dir():
|
|
3233
|
-
#
|
|
3234
|
-
discovered_libraries.update(path
|
|
3243
|
+
# Recursively find library files, skipping hidden directories
|
|
3244
|
+
discovered_libraries.update(find_files_recursive(path, LibraryManager.LIBRARY_CONFIG_GLOB_PATTERN))
|
|
3235
3245
|
elif path.suffix == ".json":
|
|
3236
3246
|
discovered_libraries.add(path)
|
|
3237
3247
|
|
|
@@ -20,6 +20,7 @@ from griptape_nodes.exe_types.core_types import (
|
|
|
20
20
|
)
|
|
21
21
|
from griptape_nodes.exe_types.flow import ControlFlow
|
|
22
22
|
from griptape_nodes.exe_types.node_groups import SubflowNodeGroup
|
|
23
|
+
from griptape_nodes.exe_types.node_groups.base_node_group import BaseNodeGroup
|
|
23
24
|
from griptape_nodes.exe_types.node_types import (
|
|
24
25
|
LOCAL_EXECUTION,
|
|
25
26
|
PRIVATE_EXECUTION,
|
|
@@ -135,12 +136,18 @@ from griptape_nodes.retained_mode.events.object_events import (
|
|
|
135
136
|
RenameObjectResultSuccess,
|
|
136
137
|
)
|
|
137
138
|
from griptape_nodes.retained_mode.events.parameter_events import (
|
|
139
|
+
AddParameterGroupToNodeRequest,
|
|
140
|
+
AddParameterGroupToNodeResultFailure,
|
|
141
|
+
AddParameterGroupToNodeResultSuccess,
|
|
138
142
|
AddParameterToNodeRequest,
|
|
139
143
|
AddParameterToNodeResultFailure,
|
|
140
144
|
AddParameterToNodeResultSuccess,
|
|
141
145
|
AlterParameterDetailsRequest,
|
|
142
146
|
AlterParameterDetailsResultFailure,
|
|
143
147
|
AlterParameterDetailsResultSuccess,
|
|
148
|
+
AlterParameterGroupDetailsRequest,
|
|
149
|
+
AlterParameterGroupDetailsResultFailure,
|
|
150
|
+
AlterParameterGroupDetailsResultSuccess,
|
|
144
151
|
GetCompatibleParametersRequest,
|
|
145
152
|
GetCompatibleParametersResultFailure,
|
|
146
153
|
GetCompatibleParametersResultSuccess,
|
|
@@ -239,6 +246,12 @@ class NodeManager:
|
|
|
239
246
|
ListParametersOnNodeRequest, self.on_list_parameters_on_node_request
|
|
240
247
|
)
|
|
241
248
|
event_manager.assign_manager_to_request_type(AddParameterToNodeRequest, self.on_add_parameter_to_node_request)
|
|
249
|
+
event_manager.assign_manager_to_request_type(
|
|
250
|
+
AddParameterGroupToNodeRequest, self.on_add_parameter_group_to_node_request
|
|
251
|
+
)
|
|
252
|
+
event_manager.assign_manager_to_request_type(
|
|
253
|
+
AlterParameterGroupDetailsRequest, self.on_alter_parameter_group_details_request
|
|
254
|
+
)
|
|
242
255
|
event_manager.assign_manager_to_request_type(
|
|
243
256
|
RemoveParameterFromNodeRequest, self.on_remove_parameter_from_node_request
|
|
244
257
|
)
|
|
@@ -487,9 +500,9 @@ class NodeManager:
|
|
|
487
500
|
node.end_node = end_node
|
|
488
501
|
end_node.start_node = node
|
|
489
502
|
|
|
490
|
-
# Handle node_names_to_add for
|
|
503
|
+
# Handle node_names_to_add for BaseNodeGroup nodes
|
|
491
504
|
if request.node_names_to_add:
|
|
492
|
-
if isinstance(node,
|
|
505
|
+
if isinstance(node, BaseNodeGroup):
|
|
493
506
|
nodes_to_add = []
|
|
494
507
|
for node_name in request.node_names_to_add:
|
|
495
508
|
try:
|
|
@@ -510,7 +523,7 @@ class NodeManager:
|
|
|
510
523
|
else:
|
|
511
524
|
warning_details = (
|
|
512
525
|
f"Attempted to add nodes '{request.node_names_to_add}' to Node '{node.name}'. "
|
|
513
|
-
f"Failed because node is not a
|
|
526
|
+
f"Failed because node is not a BaseNodeGroup."
|
|
514
527
|
)
|
|
515
528
|
logger.warning(warning_details)
|
|
516
529
|
|
|
@@ -570,7 +583,7 @@ class NodeManager:
|
|
|
570
583
|
|
|
571
584
|
def _get_node_group(
|
|
572
585
|
self, node_group_name: str, node_names: list[str]
|
|
573
|
-
) ->
|
|
586
|
+
) -> BaseNodeGroup | AddNodesToNodeGroupResultFailure:
|
|
574
587
|
"""Get the NodeGroup node."""
|
|
575
588
|
try:
|
|
576
589
|
node_group = GriptapeNodes.ObjectManager().get_object_by_name(node_group_name)
|
|
@@ -578,7 +591,7 @@ class NodeManager:
|
|
|
578
591
|
details = f"Attempted to add nodes '{node_names}' to NodeGroup '{node_group_name}'. Failed because NodeGroup was not found."
|
|
579
592
|
return AddNodesToNodeGroupResultFailure(result_details=details)
|
|
580
593
|
|
|
581
|
-
if not isinstance(node_group,
|
|
594
|
+
if not isinstance(node_group, BaseNodeGroup):
|
|
582
595
|
details = f"Attempted to add nodes '{node_names}' to '{node_group_name}'. Failed because '{node_group_name}' is not a NodeGroup."
|
|
583
596
|
return AddNodesToNodeGroupResultFailure(result_details=details)
|
|
584
597
|
|
|
@@ -657,7 +670,7 @@ class NodeManager:
|
|
|
657
670
|
|
|
658
671
|
def _get_node_group_for_remove(
|
|
659
672
|
self, node_group_name: str, node_names: list[str]
|
|
660
|
-
) ->
|
|
673
|
+
) -> BaseNodeGroup | RemoveNodeFromNodeGroupResultFailure:
|
|
661
674
|
"""Get the NodeGroup node for remove operation."""
|
|
662
675
|
try:
|
|
663
676
|
node_group = GriptapeNodes.ObjectManager().get_object_by_name(node_group_name)
|
|
@@ -665,7 +678,7 @@ class NodeManager:
|
|
|
665
678
|
details = f"Attempted to remove nodes '{node_names}' from NodeGroup '{node_group_name}'. Failed because NodeGroup was not found."
|
|
666
679
|
return RemoveNodeFromNodeGroupResultFailure(result_details=details)
|
|
667
680
|
|
|
668
|
-
if not isinstance(node_group,
|
|
681
|
+
if not isinstance(node_group, BaseNodeGroup):
|
|
669
682
|
details = f"Attempted to remove nodes '{node_names}' from '{node_group_name}'. Failed because '{node_group_name}' is not a NodeGroup."
|
|
670
683
|
return RemoveNodeFromNodeGroupResultFailure(result_details=details)
|
|
671
684
|
|
|
@@ -1418,6 +1431,72 @@ class NodeManager:
|
|
|
1418
1431
|
)
|
|
1419
1432
|
return result
|
|
1420
1433
|
|
|
1434
|
+
def on_add_parameter_group_to_node_request( # noqa: C901, PLR0911
|
|
1435
|
+
self, request: AddParameterGroupToNodeRequest
|
|
1436
|
+
) -> ResultPayload:
|
|
1437
|
+
"""Handle request to add a ParameterGroup to a node."""
|
|
1438
|
+
node_name = request.node_name
|
|
1439
|
+
node = None
|
|
1440
|
+
parent_group: ParameterGroup | None = None
|
|
1441
|
+
|
|
1442
|
+
if node_name is None:
|
|
1443
|
+
if not GriptapeNodes.ContextManager().has_current_node():
|
|
1444
|
+
details = "Attempted to add ParameterGroup to a Node from the Current Context. Failed because the Current Context is empty."
|
|
1445
|
+
return AddParameterGroupToNodeResultFailure(result_details=details)
|
|
1446
|
+
|
|
1447
|
+
node = GriptapeNodes.ContextManager().get_current_node()
|
|
1448
|
+
node_name = node.name
|
|
1449
|
+
|
|
1450
|
+
if node is None:
|
|
1451
|
+
obj_mgr = GriptapeNodes.ObjectManager()
|
|
1452
|
+
node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
|
|
1453
|
+
if node is None:
|
|
1454
|
+
details = f"Attempted to add ParameterGroup '{request.group_name}' to a Node '{node_name}', but no such Node was found."
|
|
1455
|
+
return AddParameterGroupToNodeResultFailure(result_details=details)
|
|
1456
|
+
|
|
1457
|
+
if node.lock:
|
|
1458
|
+
details = f"Attempted to add ParameterGroup '{request.group_name}' to Node '{node_name}'. Failed because the Node was locked."
|
|
1459
|
+
return AddParameterGroupToNodeResultFailure(result_details=details)
|
|
1460
|
+
|
|
1461
|
+
if not request.group_name:
|
|
1462
|
+
details = (
|
|
1463
|
+
f"Attempted to add ParameterGroup to node '{node_name}'. Failed because group_name was not defined."
|
|
1464
|
+
)
|
|
1465
|
+
return AddParameterGroupToNodeResultFailure(result_details=details)
|
|
1466
|
+
|
|
1467
|
+
existing_element = node.get_element_by_name_and_type(request.group_name)
|
|
1468
|
+
if existing_element is not None:
|
|
1469
|
+
details = f"Attempted to add ParameterGroup '{request.group_name}' to node '{node_name}'. Failed because an element with that name already exists."
|
|
1470
|
+
return AddParameterGroupToNodeResultFailure(result_details=details)
|
|
1471
|
+
|
|
1472
|
+
if request.parent_element_name is not None:
|
|
1473
|
+
parent_element = node.get_element_by_name_and_type(request.parent_element_name)
|
|
1474
|
+
if parent_element is None:
|
|
1475
|
+
details = f"Attempted to add ParameterGroup '{request.group_name}' to Parent Element '{request.parent_element_name}' in node '{node_name}'. Failed because parent element didn't exist."
|
|
1476
|
+
return AddParameterGroupToNodeResultFailure(result_details=details)
|
|
1477
|
+
|
|
1478
|
+
if isinstance(parent_element, ParameterGroup):
|
|
1479
|
+
parent_group = parent_element
|
|
1480
|
+
|
|
1481
|
+
new_group = ParameterGroup(
|
|
1482
|
+
name=request.group_name,
|
|
1483
|
+
ui_options=request.ui_options if request.ui_options else {},
|
|
1484
|
+
parent_group_name=parent_group.name if parent_group is not None else None,
|
|
1485
|
+
user_defined=request.is_user_defined,
|
|
1486
|
+
)
|
|
1487
|
+
|
|
1488
|
+
if parent_group is not None:
|
|
1489
|
+
parent_group.add_child(new_group)
|
|
1490
|
+
else:
|
|
1491
|
+
node.add_node_element(new_group)
|
|
1492
|
+
|
|
1493
|
+
details = f"Successfully added ParameterGroup '{request.group_name}' to Node '{node_name}'."
|
|
1494
|
+
logger.debug(details)
|
|
1495
|
+
|
|
1496
|
+
return AddParameterGroupToNodeResultSuccess(
|
|
1497
|
+
group_name=new_group.name, node_name=node_name, result_details=details
|
|
1498
|
+
)
|
|
1499
|
+
|
|
1421
1500
|
def on_remove_parameter_from_node_request(self, request: RemoveParameterFromNodeRequest) -> ResultPayload: # noqa: C901, PLR0911, PLR0912, PLR0915
|
|
1422
1501
|
node_name = request.node_name
|
|
1423
1502
|
node = None
|
|
@@ -1592,6 +1671,7 @@ class NodeManager:
|
|
|
1592
1671
|
mode_allowed_output=allows_output,
|
|
1593
1672
|
is_user_defined=getattr(element, "user_defined", False),
|
|
1594
1673
|
settable=getattr(element, "settable", None),
|
|
1674
|
+
private=getattr(element, "private", False),
|
|
1595
1675
|
ui_options=getattr(element, "ui_options", None),
|
|
1596
1676
|
result_details=details,
|
|
1597
1677
|
)
|
|
@@ -1845,6 +1925,55 @@ class NodeManager:
|
|
|
1845
1925
|
result = AlterParameterDetailsResultSuccess(result_details=details)
|
|
1846
1926
|
return result
|
|
1847
1927
|
|
|
1928
|
+
def on_alter_parameter_group_details_request( # noqa: PLR0911
|
|
1929
|
+
self, request: AlterParameterGroupDetailsRequest
|
|
1930
|
+
) -> ResultPayload:
|
|
1931
|
+
"""Handle requests to alter ParameterGroup details (primarily ui_options)."""
|
|
1932
|
+
node_name = request.node_name
|
|
1933
|
+
node = None
|
|
1934
|
+
|
|
1935
|
+
if node_name is None:
|
|
1936
|
+
if not GriptapeNodes.ContextManager().has_current_node():
|
|
1937
|
+
details = f"Attempted to alter details for ParameterGroup '{request.group_name}' from node in the Current Context. Failed because there was no such Node."
|
|
1938
|
+
return AlterParameterGroupDetailsResultFailure(result_details=details)
|
|
1939
|
+
node = GriptapeNodes.ContextManager().get_current_node()
|
|
1940
|
+
node_name = node.name
|
|
1941
|
+
|
|
1942
|
+
if node is None:
|
|
1943
|
+
obj_mgr = GriptapeNodes.ObjectManager()
|
|
1944
|
+
node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
|
|
1945
|
+
if node is None:
|
|
1946
|
+
details = f"Attempted to alter details for ParameterGroup '{request.group_name}' from Node '{node_name}', but no such Node was found."
|
|
1947
|
+
return AlterParameterGroupDetailsResultFailure(result_details=details)
|
|
1948
|
+
|
|
1949
|
+
if node.lock:
|
|
1950
|
+
details = f"Attempted to alter details for ParameterGroup '{request.group_name}' from Node '{node_name}'. Failed because the Node was locked."
|
|
1951
|
+
return AlterParameterGroupDetailsResultFailure(result_details=details)
|
|
1952
|
+
|
|
1953
|
+
# Handle ErrorProxyNode parameter group alteration requests
|
|
1954
|
+
if isinstance(node, ErrorProxyNode):
|
|
1955
|
+
if request.initial_setup:
|
|
1956
|
+
node.record_initialization_request(request)
|
|
1957
|
+
details = f"ParameterGroup '{request.group_name}' alteration recorded for ErrorProxyNode '{node_name}'. Original node '{node.original_node_type}' had loading errors - preserving changes for correct recreation when dependency '{node.original_library_name}' is resolved."
|
|
1958
|
+
result_details = ResultDetails(message=details, level=logging.WARNING)
|
|
1959
|
+
return AlterParameterGroupDetailsResultSuccess(result_details=result_details)
|
|
1960
|
+
|
|
1961
|
+
details = f"Cannot modify ParameterGroup '{request.group_name}' on placeholder node '{node_name}'. This placeholder preserves your workflow structure but doesn't allow modifications."
|
|
1962
|
+
return AlterParameterGroupDetailsResultFailure(result_details=details)
|
|
1963
|
+
|
|
1964
|
+
# Find the ParameterGroup
|
|
1965
|
+
group = node.get_element_by_name_and_type(request.group_name, ParameterGroup)
|
|
1966
|
+
if group is None or not isinstance(group, ParameterGroup):
|
|
1967
|
+
details = f"Attempted to alter details for ParameterGroup '{request.group_name}' from Node '{node_name}'. Failed because no such ParameterGroup was found."
|
|
1968
|
+
return AlterParameterGroupDetailsResultFailure(result_details=details)
|
|
1969
|
+
|
|
1970
|
+
# Update ui_options if provided
|
|
1971
|
+
if request.ui_options is not None:
|
|
1972
|
+
group.ui_options = request.ui_options
|
|
1973
|
+
|
|
1974
|
+
details = f"Successfully altered details for ParameterGroup '{request.group_name}' from Node '{node_name}'."
|
|
1975
|
+
return AlterParameterGroupDetailsResultSuccess(result_details=details)
|
|
1976
|
+
|
|
1848
1977
|
# For C901 (too complex): Need to give customers explicit reasons for failure on each case.
|
|
1849
1978
|
def on_get_parameter_value_request(self, request: GetParameterValueRequest) -> ResultPayload:
|
|
1850
1979
|
node_name = request.node_name
|
|
@@ -2555,6 +2684,22 @@ class NodeManager:
|
|
|
2555
2684
|
|
|
2556
2685
|
# Now creation or alteration of all of the elements.
|
|
2557
2686
|
element_modification_commands = []
|
|
2687
|
+
|
|
2688
|
+
# Serialize only user-defined ParameterGroups (like parameters)
|
|
2689
|
+
all_groups = node.root_ui_element.find_elements_by_type(ParameterGroup)
|
|
2690
|
+
for group in all_groups:
|
|
2691
|
+
if group.user_defined:
|
|
2692
|
+
add_group_request = AddParameterGroupToNodeRequest(
|
|
2693
|
+
node_name=node_name,
|
|
2694
|
+
group_name=group.name,
|
|
2695
|
+
parent_element_name=group.parent_group_name,
|
|
2696
|
+
ui_options=group.ui_options if group.ui_options else {},
|
|
2697
|
+
is_user_defined=True,
|
|
2698
|
+
initial_setup=True,
|
|
2699
|
+
)
|
|
2700
|
+
element_modification_commands.append(add_group_request)
|
|
2701
|
+
|
|
2702
|
+
# Then serialize parameters
|
|
2558
2703
|
for parameter in node.parameters:
|
|
2559
2704
|
# Create the parameter, or alter it on the existing node
|
|
2560
2705
|
if parameter.user_defined:
|
|
@@ -2595,6 +2740,23 @@ class NodeManager:
|
|
|
2595
2740
|
alter_param_request = AlterParameterDetailsRequest.create(**diff)
|
|
2596
2741
|
element_modification_commands.append(alter_param_request)
|
|
2597
2742
|
|
|
2743
|
+
# Check for ParameterGroup alterations (ui_options changes like collapsed state)
|
|
2744
|
+
if reference_node is not None and not isinstance(node, ErrorProxyNode):
|
|
2745
|
+
# Compare ALL groups against the reference node (not just user-defined)
|
|
2746
|
+
# This matches the pattern used for parameter alterations
|
|
2747
|
+
for group in all_groups:
|
|
2748
|
+
diff = NodeManager._manage_alter_group_details(group, reference_node)
|
|
2749
|
+
relevant = False
|
|
2750
|
+
for key in diff:
|
|
2751
|
+
if key in AlterParameterGroupDetailsRequest.relevant_parameters():
|
|
2752
|
+
relevant = True
|
|
2753
|
+
break
|
|
2754
|
+
if relevant:
|
|
2755
|
+
diff["group_name"] = group.name
|
|
2756
|
+
diff["initial_setup"] = True
|
|
2757
|
+
alter_group_request = AlterParameterGroupDetailsRequest(**diff)
|
|
2758
|
+
element_modification_commands.append(alter_group_request)
|
|
2759
|
+
|
|
2598
2760
|
# Now assignment of values to all of the parameters.
|
|
2599
2761
|
set_value_commands = []
|
|
2600
2762
|
|
|
@@ -2977,6 +3139,24 @@ class NodeManager:
|
|
|
2977
3139
|
return vars(parameter)
|
|
2978
3140
|
return diff
|
|
2979
3141
|
|
|
3142
|
+
@staticmethod
|
|
3143
|
+
def _manage_alter_group_details(group: ParameterGroup, base_node_obj: BaseNode) -> dict:
|
|
3144
|
+
"""Compare a ParameterGroup against its base version and return differences.
|
|
3145
|
+
|
|
3146
|
+
Args:
|
|
3147
|
+
group: The current ParameterGroup to compare
|
|
3148
|
+
base_node_obj: The reference node containing the base version
|
|
3149
|
+
|
|
3150
|
+
Returns:
|
|
3151
|
+
Dictionary of differences, or empty dict if no changes
|
|
3152
|
+
"""
|
|
3153
|
+
base_group = base_node_obj.get_element_by_name_and_type(group.name, ParameterGroup)
|
|
3154
|
+
if base_group and isinstance(base_group, ParameterGroup):
|
|
3155
|
+
diff = base_group.equals(group)
|
|
3156
|
+
else:
|
|
3157
|
+
return {"ui_options": group.ui_options}
|
|
3158
|
+
return diff
|
|
3159
|
+
|
|
2980
3160
|
@staticmethod
|
|
2981
3161
|
def _handle_value_hashing( # noqa: PLR0913
|
|
2982
3162
|
value: Any,
|