griptape-nodes 0.52.1__py3-none-any.whl → 0.53.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.
Files changed (46) hide show
  1. griptape_nodes/__init__.py +6 -943
  2. griptape_nodes/__main__.py +6 -0
  3. griptape_nodes/app/app.py +45 -61
  4. griptape_nodes/cli/__init__.py +1 -0
  5. griptape_nodes/cli/commands/__init__.py +1 -0
  6. griptape_nodes/cli/commands/config.py +71 -0
  7. griptape_nodes/cli/commands/engine.py +80 -0
  8. griptape_nodes/cli/commands/init.py +548 -0
  9. griptape_nodes/cli/commands/libraries.py +90 -0
  10. griptape_nodes/cli/commands/self.py +117 -0
  11. griptape_nodes/cli/main.py +46 -0
  12. griptape_nodes/cli/shared.py +84 -0
  13. griptape_nodes/common/__init__.py +1 -0
  14. griptape_nodes/common/directed_graph.py +55 -0
  15. griptape_nodes/drivers/storage/local_storage_driver.py +7 -2
  16. griptape_nodes/exe_types/core_types.py +60 -2
  17. griptape_nodes/exe_types/node_types.py +38 -24
  18. griptape_nodes/machines/control_flow.py +86 -22
  19. griptape_nodes/machines/fsm.py +10 -1
  20. griptape_nodes/machines/parallel_resolution.py +570 -0
  21. griptape_nodes/machines/{node_resolution.py → sequential_resolution.py} +22 -51
  22. griptape_nodes/retained_mode/events/base_events.py +2 -2
  23. griptape_nodes/retained_mode/events/node_events.py +4 -3
  24. griptape_nodes/retained_mode/griptape_nodes.py +25 -12
  25. griptape_nodes/retained_mode/managers/agent_manager.py +9 -5
  26. griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +3 -1
  27. griptape_nodes/retained_mode/managers/context_manager.py +6 -5
  28. griptape_nodes/retained_mode/managers/flow_manager.py +117 -204
  29. griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +1 -1
  30. griptape_nodes/retained_mode/managers/library_manager.py +35 -25
  31. griptape_nodes/retained_mode/managers/node_manager.py +81 -199
  32. griptape_nodes/retained_mode/managers/object_manager.py +11 -5
  33. griptape_nodes/retained_mode/managers/os_manager.py +24 -9
  34. griptape_nodes/retained_mode/managers/secrets_manager.py +8 -4
  35. griptape_nodes/retained_mode/managers/settings.py +32 -1
  36. griptape_nodes/retained_mode/managers/static_files_manager.py +8 -3
  37. griptape_nodes/retained_mode/managers/sync_manager.py +8 -5
  38. griptape_nodes/retained_mode/managers/workflow_manager.py +110 -122
  39. griptape_nodes/traits/add_param_button.py +1 -1
  40. griptape_nodes/traits/button.py +216 -6
  41. griptape_nodes/traits/color_picker.py +66 -0
  42. griptape_nodes/traits/traits.json +4 -0
  43. {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.53.0.dist-info}/METADATA +2 -1
  44. {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.53.0.dist-info}/RECORD +46 -32
  45. {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.53.0.dist-info}/WHEEL +0 -0
  46. {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.53.0.dist-info}/entry_points.txt +0 -0
@@ -203,6 +203,7 @@ class NodeManager:
203
203
  event_manager.assign_manager_to_request_type(DuplicateSelectedNodesRequest, self.on_duplicate_selected_nodes)
204
204
  event_manager.assign_manager_to_request_type(SetLockNodeStateRequest, self.on_toggle_lock_node_request)
205
205
  event_manager.assign_manager_to_request_type(GetFlowForNodeRequest, self.on_get_flow_for_node_request)
206
+ event_manager.assign_manager_to_request_type(SendNodeMessageRequest, self.on_send_node_message_request)
206
207
 
207
208
  def handle_node_rename(self, old_name: str, new_name: str) -> None:
208
209
  # Get the node itself
@@ -253,7 +254,6 @@ class NodeManager:
253
254
  details = (
254
255
  "Attempted to create Node in the Current Context. Failed because the Current Context was empty."
255
256
  )
256
- logger.error(details)
257
257
  return CreateNodeResultFailure(result_details=details)
258
258
  parent_flow = GriptapeNodes.ContextManager().get_current_flow()
259
259
  parent_flow_name = parent_flow.name
@@ -265,7 +265,6 @@ class NodeManager:
265
265
  parent_flow = flow_mgr.get_flow_by_name(parent_flow_name)
266
266
  except KeyError as err:
267
267
  details = f"Attempted to create Node of type '{request.node_type}'. Failed when attempting to find the parent Flow. Error: {err}"
268
- logger.error(details)
269
268
  return CreateNodeResultFailure(result_details=details)
270
269
 
271
270
  # Now ensure that we're giving a valid name.
@@ -278,7 +277,6 @@ class NodeManager:
278
277
  )
279
278
  except KeyError as err:
280
279
  details = f"Attempted to create Node of type '{request.node_type}'. Failed when attempting to find the library this node type was in. Error: {err}"
281
- logger.error(details)
282
280
  return CreateNodeResultFailure(result_details=details)
283
281
 
284
282
  node_metadata = dest_library.get_node_metadata(request.node_type)
@@ -308,7 +306,6 @@ class NodeManager:
308
306
 
309
307
  traceback.print_exc()
310
308
  details = f"Could not create Node '{final_node_name}' of type '{request.node_type}': {err}"
311
- logger.error(details)
312
309
 
313
310
  # Check if we should create an Error Proxy node instead of failing
314
311
  if request.create_error_proxy_on_failure:
@@ -329,7 +326,6 @@ class NodeManager:
329
326
  )
330
327
  except Exception as proxy_err:
331
328
  details = f"Failed to create Error Proxy (placeholder) node: {proxy_err}"
332
- logger.error(details)
333
329
  return CreateNodeResultFailure(result_details=details)
334
330
  else:
335
331
  return CreateNodeResultFailure(result_details=details)
@@ -361,13 +357,11 @@ class NodeManager:
361
357
  else:
362
358
  details = f"Successfully created Node '{final_node_name}' in Flow '{parent_flow_name}'"
363
359
 
364
- log_level = logging.DEBUG
360
+ log_level = "DEBUG"
365
361
  if remapped_requested_node_name:
366
- log_level = logging.WARNING
362
+ log_level = "WARNING"
367
363
  details = f"{details}. WARNING: Had to rename from original node name requested '{request.node_name}' as an object with this name already existed."
368
364
 
369
- logger.log(level=log_level, msg=details)
370
-
371
365
  # Special handling for paired classes (e.g., create a Start node and it automatically creates a corresponding End node already connected).
372
366
  if isinstance(node, StartLoopNode) and not request.initial_setup:
373
367
  # If it's StartLoop, create an EndLoop and connect it to the StartLoop.
@@ -381,7 +375,7 @@ class NodeManager:
381
375
  # Check and see if the class exists
382
376
  libraries_with_node_type = LibraryRegistry.get_libraries_with_node_type(end_class_name)
383
377
  if not libraries_with_node_type:
384
- msg = f"Attempted to create a paried set of nodes for Node '{final_node_name}'. Failed because paired class '{end_class_name}' does not exist for start class '{node_class_name}'. The corresponding node will have to be created by hand and attached manually."
378
+ msg = f"Attempted to create a paired set of nodes for Node '{final_node_name}'. Failed because paired class '{end_class_name}' does not exist for start class '{node_class_name}'. The corresponding node will have to be created by hand and attached manually."
385
379
  logger.error(msg) # while this is bad, it's not unsalvageable, so we'll consider this a success.
386
380
  else:
387
381
  # Create the EndNode
@@ -419,7 +413,10 @@ class NodeManager:
419
413
  end_node.start_node = node
420
414
 
421
415
  return CreateNodeResultSuccess(
422
- node_name=node.name, node_type=node.__class__.__name__, specific_library_name=request.specific_library_name
416
+ node_name=node.name,
417
+ node_type=node.__class__.__name__,
418
+ specific_library_name=request.specific_library_name,
419
+ result_details=ResultDetails(message=details, level=log_level),
423
420
  )
424
421
 
425
422
  def cancel_conditionally(
@@ -460,7 +457,6 @@ class NodeManager:
460
457
  details = (
461
458
  f"Attempted to delete a Node '{node.name}'. Failed because running flow could not cancel."
462
459
  )
463
- logger.error(details)
464
460
  return DeleteNodeResultFailure(result_details=details)
465
461
  if resolving_node_name is not None and not cancelled:
466
462
  resolving_node = GriptapeNodes.ObjectManager().get_object_by_name(resolving_node_name)
@@ -470,13 +466,12 @@ class NodeManager:
470
466
  details = (
471
467
  f"Attempted to delete a Node '{node.name}'. Failed because running flow could not cancel."
472
468
  )
473
- logger.error(details)
474
469
  return DeleteNodeResultFailure(result_details=details)
475
470
  # Clear the execution queue, because we don't want to hit this node eventually.
476
471
  parent_flow.clear_execution_queue()
477
472
  return None
478
473
 
479
- def on_delete_node_request(self, request: DeleteNodeRequest) -> ResultPayload: # noqa: C901, PLR0911, PLR0912, PLR0915 (complex logic, lots of edge cases)
474
+ def on_delete_node_request(self, request: DeleteNodeRequest) -> ResultPayload: # noqa: C901, PLR0911, PLR0912 (complex logic, lots of edge cases)
480
475
  node_name = request.node_name
481
476
  node = None
482
477
  if node_name is None:
@@ -485,7 +480,6 @@ class NodeManager:
485
480
  details = (
486
481
  "Attempted to delete a Node from the Current Context. Failed because the Current Context is empty."
487
482
  )
488
- logger.error(details)
489
483
  return DeleteNodeResultFailure(result_details=details)
490
484
 
491
485
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -494,7 +488,6 @@ class NodeManager:
494
488
  node = GriptapeNodes.ObjectManager().attempt_get_object_by_name_as_type(node_name, BaseNode)
495
489
  if node is None:
496
490
  details = f"Attempted to delete a Node '{node_name}', but no such Node was found."
497
- logger.error(details)
498
491
  return DeleteNodeResultFailure(result_details=details)
499
492
 
500
493
  with GriptapeNodes.ContextManager().node(node=node):
@@ -503,7 +496,6 @@ class NodeManager:
503
496
  parent_flow = GriptapeNodes.FlowManager().get_flow_by_name(parent_flow_name)
504
497
  except KeyError as err:
505
498
  details = f"Attempted to delete a Node '{node_name}'. Error: {err}"
506
- logger.error(details)
507
499
  return DeleteNodeResultFailure(result_details=details)
508
500
 
509
501
  cancel_result = self.cancel_conditionally(parent_flow, parent_flow_name, node)
@@ -519,7 +511,6 @@ class NodeManager:
519
511
  list_connections_result = GriptapeNodes.handle_request(request=list_node_connections_request)
520
512
  if not isinstance(list_connections_result, ListConnectionsForNodeResultSuccess):
521
513
  details = f"Attempted to delete a Node '{node_name}'. Failed because it could not gather Connections to the Node."
522
- logger.error(details)
523
514
  return DeleteNodeResultFailure(result_details=details)
524
515
 
525
516
  # Check incoming connections
@@ -537,7 +528,6 @@ class NodeManager:
537
528
  details = (
538
529
  f"Attempted to delete a Node '{node_name}'. Failed when attempting to delete Connection."
539
530
  )
540
- logger.error(details)
541
531
  return DeleteNodeResultFailure(result_details=details)
542
532
  continue # Refresh connection list after cascading deletions
543
533
 
@@ -556,7 +546,6 @@ class NodeManager:
556
546
  details = (
557
547
  f"Attempted to delete a Node '{node_name}'. Failed when attempting to delete Connection."
558
548
  )
559
- logger.error(details)
560
549
  return DeleteNodeResultFailure(result_details=details)
561
550
 
562
551
  # Remove from the owning Flow
@@ -571,9 +560,7 @@ class NodeManager:
571
560
  GriptapeNodes.ContextManager().pop_node()
572
561
 
573
562
  details = f"Successfully deleted Node '{node_name}'."
574
- logger.debug(details)
575
-
576
- return DeleteNodeResultSuccess()
563
+ return DeleteNodeResultSuccess(result_details=details)
577
564
 
578
565
  def on_get_node_resolution_state_request(self, request: GetNodeResolutionStateRequest) -> ResultPayload:
579
566
  node_name = request.node_name
@@ -582,7 +569,6 @@ class NodeManager:
582
569
  # Get from the current context.
583
570
  if not GriptapeNodes.ContextManager().has_current_node():
584
571
  details = "Attempted to get resolution state for a Node from the Current Context. Failed because the Current Context is empty."
585
- logger.error(details)
586
572
  return GetNodeResolutionStateResultFailure(result_details=details)
587
573
 
588
574
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -594,18 +580,13 @@ class NodeManager:
594
580
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
595
581
  if node is None:
596
582
  details = f"Attempted to get resolution state for a Node '{node_name}', but no such Node was found."
597
- logger.error(details)
598
583
  result = GetNodeResolutionStateResultFailure(result_details=details)
599
584
  return result
600
585
 
601
586
  node_state = node.state
602
587
 
603
588
  details = f"Successfully got resolution state for Node '{node_name}'."
604
- logger.debug(details)
605
-
606
- result = GetNodeResolutionStateResultSuccess(
607
- state=node_state.name,
608
- )
589
+ result = GetNodeResolutionStateResultSuccess(state=node_state.name, result_details=details)
609
590
  return result
610
591
 
611
592
  def on_get_node_metadata_request(self, request: GetNodeMetadataRequest) -> ResultPayload:
@@ -615,7 +596,6 @@ class NodeManager:
615
596
  # Get from the current context.
616
597
  if not GriptapeNodes.ContextManager().has_current_node():
617
598
  details = "Attempted to get metadata for a Node from the Current Context. Failed because the Current Context is empty."
618
- logger.error(details)
619
599
  return GetNodeMetadataResultFailure(result_details=details)
620
600
 
621
601
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -628,18 +608,13 @@ class NodeManager:
628
608
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
629
609
  if node is None:
630
610
  details = f"Attempted to get metadata for a Node '{node_name}', but no such Node was found."
631
- logger.error(details)
632
611
 
633
612
  result = GetNodeMetadataResultFailure(result_details=details)
634
613
  return result
635
614
 
636
615
  metadata = node.metadata
637
616
  details = f"Successfully retrieved metadata for a Node '{node_name}'."
638
- logger.debug(details)
639
-
640
- result = GetNodeMetadataResultSuccess(
641
- metadata=metadata,
642
- )
617
+ result = GetNodeMetadataResultSuccess(metadata=metadata, result_details=details)
643
618
  return result
644
619
 
645
620
  def on_set_node_metadata_request(self, request: SetNodeMetadataRequest) -> ResultPayload:
@@ -649,7 +624,6 @@ class NodeManager:
649
624
  # Get from the current context.
650
625
  if not GriptapeNodes.ContextManager().has_current_node():
651
626
  details = "Attempted to set metadata for a Node from the Current Context. Failed because the Current Context is empty."
652
- logger.error(details)
653
627
  return SetNodeMetadataResultFailure(result_details=details)
654
628
 
655
629
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -662,7 +636,6 @@ class NodeManager:
662
636
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
663
637
  if node is None:
664
638
  details = f"Attempted to set metadata for a Node '{node_name}', but no such Node was found."
665
- logger.error(details)
666
639
 
667
640
  result = SetNodeMetadataResultFailure(result_details=details)
668
641
  return result
@@ -671,9 +644,7 @@ class NodeManager:
671
644
  for key, value in request.metadata.items():
672
645
  node.metadata[key] = value
673
646
  details = f"Successfully set metadata for a Node '{node_name}'."
674
- logger.debug(details)
675
-
676
- result = SetNodeMetadataResultSuccess()
647
+ result = SetNodeMetadataResultSuccess(result_details=details)
677
648
  return result
678
649
 
679
650
  def on_batch_set_node_metadata_request(self, request: BatchSetNodeMetadataRequest) -> ResultPayload:
@@ -714,7 +685,11 @@ class NodeManager:
714
685
  result_details=f"Failed to update any nodes. Failed nodes: {failed_nodes}"
715
686
  )
716
687
 
717
- return BatchSetNodeMetadataResultSuccess(updated_nodes=updated_nodes, failed_nodes=failed_nodes)
688
+ return BatchSetNodeMetadataResultSuccess(
689
+ updated_nodes=updated_nodes,
690
+ failed_nodes=failed_nodes,
691
+ result_details=f"Successfully updated metadata for {len(updated_nodes)} nodes.",
692
+ )
718
693
 
719
694
  def on_list_connections_for_node_request(self, request: ListConnectionsForNodeRequest) -> ResultPayload:
720
695
  node_name = request.node_name
@@ -723,7 +698,6 @@ class NodeManager:
723
698
  # Get from the current context.
724
699
  if not GriptapeNodes.ContextManager().has_current_node():
725
700
  details = "Attempted to list Connections for a Node from the Current Context. Failed because the Current Context is empty."
726
- logger.error(details)
727
701
  return ListConnectionsForNodeResultFailure(result_details=details)
728
702
 
729
703
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -736,7 +710,6 @@ class NodeManager:
736
710
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
737
711
  if node is None:
738
712
  details = f"Attempted to list Connections for a Node '{node_name}', but no such Node was found."
739
- logger.error(details)
740
713
 
741
714
  result = ListConnectionsForNodeResultFailure(result_details=details)
742
715
  return result
@@ -746,7 +719,6 @@ class NodeManager:
746
719
  GriptapeNodes.FlowManager().get_flow_by_name(parent_flow_name)
747
720
  except KeyError as err:
748
721
  details = f"Attempted to list Connections for a Node '{node_name}'. Error: {err}"
749
- logger.error(details)
750
722
 
751
723
  result = ListConnectionsForNodeResultFailure(result_details=details)
752
724
  return result
@@ -783,11 +755,10 @@ class NodeManager:
783
755
  ]
784
756
 
785
757
  details = f"Successfully listed all Connections to and from Node '{node_name}'."
786
- logger.debug(details)
787
-
788
758
  result = ListConnectionsForNodeResultSuccess(
789
759
  incoming_connections=incoming_connections_list,
790
760
  outgoing_connections=outgoing_connections_list,
761
+ result_details=details,
791
762
  )
792
763
  return result
793
764
 
@@ -799,7 +770,6 @@ class NodeManager:
799
770
  # Get from the current context.
800
771
  if not GriptapeNodes.ContextManager().has_current_node():
801
772
  details = "Attempted to list Parameters for a Node from the Current Context. Failed because the Current Context is empty."
802
- logger.error(details)
803
773
  return ListParametersOnNodeResultFailure(result_details=details)
804
774
 
805
775
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -811,7 +781,6 @@ class NodeManager:
811
781
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
812
782
  if node is None:
813
783
  details = f"Attempted to list Parameters for a Node '{node_name}', but no such Node was found."
814
- logger.error(details)
815
784
 
816
785
  result = ListParametersOnNodeResultFailure(result_details=details)
817
786
  return result
@@ -819,11 +788,7 @@ class NodeManager:
819
788
  ret_list = [param.name for param in node.parameters]
820
789
 
821
790
  details = f"Successfully listed Parameters for Node '{node_name}'."
822
- logger.debug(details)
823
-
824
- result = ListParametersOnNodeResultSuccess(
825
- parameter_names=ret_list,
826
- )
791
+ result = ListParametersOnNodeResultSuccess(parameter_names=ret_list, result_details=details)
827
792
  return result
828
793
 
829
794
  def generate_unique_parameter_name(self, node: BaseNode, base_name: str) -> str:
@@ -852,7 +817,6 @@ class NodeManager:
852
817
  # Get from the current context.
853
818
  if not GriptapeNodes.ContextManager().has_current_node():
854
819
  details = "Attempted to add Parameter to a Node from the Current Context. Failed because the Current Context is empty."
855
- logger.error(details)
856
820
  return AddParameterToNodeResultFailure(result_details=details)
857
821
 
858
822
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -864,7 +828,6 @@ class NodeManager:
864
828
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
865
829
  if node is None:
866
830
  details = f"Attempted to add Parameter '{request.parameter_name}' to a Node '{node_name}', but no such Node was found."
867
- logger.error(details)
868
831
 
869
832
  result = AddParameterToNodeResultFailure(result_details=details)
870
833
  return result
@@ -872,7 +835,6 @@ class NodeManager:
872
835
  # Check if node is locked
873
836
  if node.lock:
874
837
  details = f"Attempted to add Parameter '{request.parameter_name}' to Node '{node_name}'. Failed because the Node was locked."
875
- logger.error(details)
876
838
  result = AddParameterToNodeResultFailure(result_details=details)
877
839
  return result
878
840
 
@@ -880,12 +842,10 @@ class NodeManager:
880
842
  parameter = node.get_parameter_by_name(request.parent_container_name)
881
843
  if parameter is None:
882
844
  details = f"Attempted to add Parameter to Container Parameter '{request.parent_container_name}' in node '{node_name}'. Failed because parameter didn't exist."
883
- logger.error(details)
884
845
  result = AddParameterToNodeResultFailure(result_details=details)
885
846
  return result
886
847
  if not isinstance(parameter, ParameterContainer):
887
848
  details = f"Attempted to add Parameter to Container Parameter '{request.parent_container_name}' in node '{node_name}'. Failed because parameter wasn't a container."
888
- logger.error(details)
889
849
  result = AddParameterToNodeResultFailure(result_details=details)
890
850
  return result
891
851
  try:
@@ -897,11 +857,13 @@ class NodeManager:
897
857
  return result
898
858
 
899
859
  return AddParameterToNodeResultSuccess(
900
- parameter_name=new_param.name, type=new_param.type, node_name=node_name
860
+ parameter_name=new_param.name,
861
+ type=new_param.type,
862
+ node_name=node_name,
863
+ result_details=f"Successfully added parameter '{new_param.name}' to container parameter '{request.parent_container_name}' in node '{node_name}'.",
901
864
  )
902
865
  if request.parameter_name is None or request.tooltip is None:
903
866
  details = f"Attempted to add Parameter to node '{node_name}'. Failed because default_value, tooltip, or parameter_name was not defined."
904
- logger.error(details)
905
867
  result = AddParameterToNodeResultFailure(result_details=details)
906
868
  return result
907
869
 
@@ -937,7 +899,6 @@ class NodeManager:
937
899
 
938
900
  if has_control_type and has_non_control_types:
939
901
  details = f"Attempted to add Parameter '{request.parameter_name}' to Node '{node_name}'. Failed because it had 'ParameterControlType' AND at least one other non-control type. If a Parameter is intended for control, it must only accept that type."
940
- logger.error(details)
941
902
 
942
903
  result = AddParameterToNodeResultFailure(result_details=details)
943
904
  return result
@@ -976,7 +937,6 @@ class NodeManager:
976
937
  node.add_parameter(new_param)
977
938
  except Exception as e:
978
939
  details = f"Couldn't add parameter with name {request.parameter_name} to Node '{node_name}'. Error: {e}"
979
- logger.error(details)
980
940
  return AddParameterToNodeResultFailure(result_details=details)
981
941
 
982
942
  details = f"Successfully added Parameter '{final_param_name}' to Node '{node_name}'."
@@ -988,7 +948,7 @@ class NodeManager:
988
948
  logger.log(level=log_level, msg=details)
989
949
 
990
950
  result = AddParameterToNodeResultSuccess(
991
- parameter_name=new_param.name, type=new_param.type, node_name=node_name
951
+ parameter_name=new_param.name, type=new_param.type, node_name=node_name, result_details=details
992
952
  )
993
953
  return result
994
954
 
@@ -1000,7 +960,6 @@ class NodeManager:
1000
960
  # Get the Current Context
1001
961
  if not GriptapeNodes.ContextManager().has_current_node():
1002
962
  details = f"Attempted to remove Parameter '{request.parameter_name}' from a Node, but no Current Context was found."
1003
- logger.error(details)
1004
963
 
1005
964
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1006
965
  return result
@@ -1014,14 +973,12 @@ class NodeManager:
1014
973
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
1015
974
  if node is None:
1016
975
  details = f"Attempted to remove Parameter '{request.parameter_name}' from a Node '{node_name}', but no such Node was found."
1017
- logger.error(details)
1018
976
 
1019
977
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1020
978
  return result
1021
979
  # Check if the node is locked
1022
980
  if node.lock:
1023
981
  details = f"Attempted to remove Element '{request.parameter_name}' from Node '{node_name}'. Failed because the Node was locked."
1024
- logger.error(details)
1025
982
 
1026
983
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1027
984
  return result
@@ -1029,7 +986,6 @@ class NodeManager:
1029
986
  element = node.get_element_by_name_and_type(request.parameter_name)
1030
987
  if element is None:
1031
988
  details = f"Attempted to remove Element '{request.parameter_name}' from Node '{node_name}'. Failed because it didn't have an Element with that name on it."
1032
- logger.error(details)
1033
989
 
1034
990
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1035
991
  return result
@@ -1040,13 +996,14 @@ class NodeManager:
1040
996
  GriptapeNodes.handle_request(RemoveParameterFromNodeRequest(child.name, node_name))
1041
997
  node.remove_parameter_element_by_name(request.parameter_name)
1042
998
 
1043
- return RemoveParameterFromNodeResultSuccess()
999
+ return RemoveParameterFromNodeResultSuccess(
1000
+ result_details=f"Successfully removed parameter group '{request.parameter_name}' and all its children from node '{node_name}'."
1001
+ )
1044
1002
 
1045
1003
  # No tricky stuff, users!
1046
1004
  # if user_defined doesn't exist, or is false, then it's not user-defined
1047
1005
  if not getattr(element, "user_defined", False):
1048
1006
  details = f"Attempted to remove Element '{request.parameter_name}' from Node '{node_name}'. Failed because the Element was not user-defined (i.e., critical to the Node implementation). Only user-defined Elements can be removed from a Node."
1049
- logger.error(details)
1050
1007
 
1051
1008
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1052
1009
  return result
@@ -1057,7 +1014,6 @@ class NodeManager:
1057
1014
  list_connections_result = GriptapeNodes.handle_request(request=list_node_connections_request)
1058
1015
  if not isinstance(list_connections_result, ListConnectionsForNodeResultSuccess):
1059
1016
  details = f"Attempted to remove Parameter '{request.parameter_name}' from Node '{node_name}'. Failed because we were unable to get a list of Connections for the Parameter's Node."
1060
- logger.error(details)
1061
1017
 
1062
1018
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1063
1019
  return result
@@ -1076,7 +1032,6 @@ class NodeManager:
1076
1032
  delete_result = GriptapeNodes.handle_request(delete_request)
1077
1033
  if isinstance(delete_result, DeleteConnectionResultFailure):
1078
1034
  details = f"Attempted to remove Parameter '{request.parameter_name}' from Node '{node_name}'. Failed because we were unable to delete a Connection for that Parameter."
1079
- logger.error(details)
1080
1035
 
1081
1036
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1082
1037
 
@@ -1092,7 +1047,6 @@ class NodeManager:
1092
1047
  delete_result = GriptapeNodes.handle_request(delete_request)
1093
1048
  if isinstance(delete_result, DeleteConnectionResultFailure):
1094
1049
  details = f"Attempted to remove Parameter '{request.parameter_name}' from Node '{node_name}'. Failed because we were unable to delete a Connection for that Parameter."
1095
- logger.error(details)
1096
1050
 
1097
1051
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1098
1052
 
@@ -1101,14 +1055,11 @@ class NodeManager:
1101
1055
  node.remove_parameter_element(element)
1102
1056
  else:
1103
1057
  details = f"Attempted to remove Element '{request.parameter_name}' from Node '{node_name}'. Failed because element didn't exist."
1104
- logger.error(details)
1105
1058
 
1106
1059
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1107
1060
 
1108
1061
  details = f"Successfully removed Element '{request.parameter_name}' from Node '{node_name}'."
1109
- logger.debug(details)
1110
-
1111
- result = RemoveParameterFromNodeResultSuccess()
1062
+ result = RemoveParameterFromNodeResultSuccess(result_details=details)
1112
1063
  return result
1113
1064
 
1114
1065
  def on_get_parameter_details_request(self, request: GetParameterDetailsRequest) -> ResultPayload:
@@ -1118,7 +1069,6 @@ class NodeManager:
1118
1069
  if node_name is None:
1119
1070
  if not GriptapeNodes.ContextManager().has_current_node():
1120
1071
  details = f"Attempted to get details for Parameter '{request.parameter_name}' from a Node, but no Current Context was found."
1121
- logger.error(details)
1122
1072
 
1123
1073
  result = GetParameterDetailsResultFailure(result_details=details)
1124
1074
  return result
@@ -1131,7 +1081,6 @@ class NodeManager:
1131
1081
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
1132
1082
  if node is None:
1133
1083
  details = f"Attempted to get details for Parameter '{request.parameter_name}' from a Node '{node_name}', but no such Node was found."
1134
- logger.error(details)
1135
1084
 
1136
1085
  result = GetParameterDetailsResultFailure(result_details=details)
1137
1086
  return result
@@ -1141,7 +1090,6 @@ class NodeManager:
1141
1090
 
1142
1091
  if element is None:
1143
1092
  details = f"Attempted to get details for Element '{request.parameter_name}' from Node '{node_name}'. Failed because it didn't have an Element with that name on it."
1144
- logger.error(details)
1145
1093
  return GetParameterDetailsResultFailure(result_details=details)
1146
1094
 
1147
1095
  # Let's bundle up the details.
@@ -1155,9 +1103,7 @@ class NodeManager:
1155
1103
  allows_property = ParameterMode.PROPERTY in modes_allowed
1156
1104
  allows_output = ParameterMode.OUTPUT in modes_allowed
1157
1105
 
1158
- details = f"Successfully got details for Element '{request.parameter_name}' from Node '{node_name}'."
1159
- logger.debug(details)
1160
-
1106
+ details = f"Successfully got details for Element '{request.parameter_name}' from Node '{node_name}'."
1161
1107
  result = GetParameterDetailsResultSuccess(
1162
1108
  element_id=element.element_id,
1163
1109
  type=getattr(element, "type", ""),
@@ -1174,6 +1120,7 @@ class NodeManager:
1174
1120
  is_user_defined=getattr(element, "user_defined", False),
1175
1121
  settable=getattr(element, "settable", None),
1176
1122
  ui_options=getattr(element, "ui_options", None),
1123
+ result_details=details,
1177
1124
  )
1178
1125
  return result
1179
1126
 
@@ -1184,7 +1131,6 @@ class NodeManager:
1184
1131
  if node_name is None:
1185
1132
  if not GriptapeNodes.ContextManager().has_current_node():
1186
1133
  details = f"Attempted to get element details for element '{request.specific_element_id}` from a Node, but no Current Context was found."
1187
- logger.error(details)
1188
1134
 
1189
1135
  return GetNodeElementDetailsResultFailure(result_details=details)
1190
1136
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -1196,7 +1142,6 @@ class NodeManager:
1196
1142
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
1197
1143
  if node is None:
1198
1144
  details = f"Attempted to get element details for Node '{node_name}', but no such Node was found."
1199
- logger.error(details)
1200
1145
 
1201
1146
  return GetNodeElementDetailsResultFailure(result_details=details)
1202
1147
 
@@ -1208,7 +1153,6 @@ class NodeManager:
1208
1153
  element = node.root_ui_element.find_element_by_id(request.specific_element_id)
1209
1154
  if element is None:
1210
1155
  details = f"Attempted to get element details for element '{request.specific_element_id}' from Node '{node_name}'. Failed because it didn't have an element with that ID on it."
1211
- logger.error(details)
1212
1156
 
1213
1157
  return GetNodeElementDetailsResultFailure(result_details=details)
1214
1158
 
@@ -1219,8 +1163,7 @@ class NodeManager:
1219
1163
  if param_to_value:
1220
1164
  element_details["element_id_to_value"] = param_to_value
1221
1165
  details = f"Successfully got element details for Node '{node_name}'."
1222
- logger.debug(details)
1223
- result = GetNodeElementDetailsResultSuccess(element_details=element_details)
1166
+ result = GetNodeElementDetailsResultSuccess(element_details=element_details, result_details=details)
1224
1167
  return result
1225
1168
 
1226
1169
  def _set_param_to_value(self, node: BaseNode, element: BaseNodeElement, param_to_value: dict) -> None:
@@ -1326,7 +1269,6 @@ class NodeManager:
1326
1269
  )
1327
1270
  if isinstance(delete_result, ResultPayloadFailure):
1328
1271
  details = f"Failed to delete incompatible incoming connection from {conn.source_node_name}.{conn.source_parameter_name} to {node_name}.{request.parameter_name}: {delete_result}"
1329
- logger.error(details)
1330
1272
  return AlterParameterDetailsResultFailure(result_details=details)
1331
1273
 
1332
1274
  # Check and break invalid outgoing connections
@@ -1345,7 +1287,6 @@ class NodeManager:
1345
1287
  )
1346
1288
  if isinstance(delete_result, ResultPayloadFailure):
1347
1289
  details = f"Failed to delete incompatible outgoing connection from {node_name}.{request.parameter_name} to {conn.target_node_name}.{conn.target_parameter_name}: {delete_result}"
1348
- logger.error(details)
1349
1290
  return AlterParameterDetailsResultFailure(result_details=details)
1350
1291
 
1351
1292
  return None
@@ -1357,7 +1298,6 @@ class NodeManager:
1357
1298
  if node_name is None:
1358
1299
  if not GriptapeNodes.ContextManager().has_current_node():
1359
1300
  details = f"Attempted to alter details for Parameter '{request.parameter_name}' from node in the Current Context. Failed because there was no such Node."
1360
- logger.error(details)
1361
1301
 
1362
1302
  return AlterParameterDetailsResultFailure(result_details=details)
1363
1303
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -1369,14 +1309,12 @@ class NodeManager:
1369
1309
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
1370
1310
  if node is None:
1371
1311
  details = f"Attempted to alter details for Parameter '{request.parameter_name}' from Node '{node_name}', but no such Node was found."
1372
- logger.error(details)
1373
1312
 
1374
1313
  return AlterParameterDetailsResultFailure(result_details=details)
1375
1314
 
1376
1315
  # Is the node locked?
1377
1316
  if node.lock:
1378
1317
  details = f"Attempted to alter details for Parameter '{request.parameter_name}' from Node '{node_name}'. Failed because the Node was locked."
1379
- logger.error(details)
1380
1318
  return AlterParameterDetailsResultFailure(result_details=details)
1381
1319
 
1382
1320
  # Handle ErrorProxyNode parameter alteration requests
@@ -1387,21 +1325,18 @@ class NodeManager:
1387
1325
 
1388
1326
  # Early return with warning - we're just preserving the original changes
1389
1327
  details = f"Parameter '{request.parameter_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."
1390
- logger.warning(details)
1391
1328
 
1392
1329
  result_details = ResultDetails(message=details, level="WARNING")
1393
1330
  return AlterParameterDetailsResultSuccess(result_details=result_details)
1394
1331
 
1395
1332
  # Reject runtime parameter alterations on ErrorProxy
1396
1333
  details = f"Cannot modify parameter '{request.parameter_name}' on placeholder node '{node_name}'. This placeholder preserves your workflow structure but doesn't allow parameter modifications, as they could cause issues when the original node is restored."
1397
- logger.error(details)
1398
1334
  return AlterParameterDetailsResultFailure(result_details=details)
1399
1335
 
1400
1336
  # Does the Element actually exist on the Node?
1401
1337
  element = node.get_element_by_name_and_type(request.parameter_name)
1402
1338
  if element is None:
1403
1339
  details = f"Attempted to alter details for Element '{request.parameter_name}' from Node '{node_name}'. Failed because it didn't have an Element with that name on it."
1404
- logger.error(details)
1405
1340
  return AlterParameterDetailsResultFailure(result_details=details)
1406
1341
  if request.ui_options is not None:
1407
1342
  element.ui_options = request.ui_options # type: ignore[attr-defined]
@@ -1423,8 +1358,9 @@ class NodeManager:
1423
1358
  if hasattr(element, "user_defined") and element.user_defined is False and request.request_id: # type: ignore[attr-defined]
1424
1359
  # TODO: https://github.com/griptape-ai/griptape-nodes/issues/826
1425
1360
  details = f"Attempted to alter details for Element '{request.parameter_name}' from Node '{node_name}'. Could only alter some values because the Element was not user-defined (i.e., critical to the Node implementation). Only user-defined Elements can be totally modified from a Node."
1426
- logger.warning(details)
1427
- return AlterParameterDetailsResultSuccess()
1361
+ return AlterParameterDetailsResultSuccess(
1362
+ result_details=ResultDetails(message=details, level="WARNING")
1363
+ )
1428
1364
  self.modify_key_parameter_fields(request, element)
1429
1365
 
1430
1366
  # This field requires the node as well
@@ -1433,9 +1369,7 @@ class NodeManager:
1433
1369
  node.parameter_values[request.parameter_name] = request.default_value
1434
1370
 
1435
1371
  details = f"Successfully altered details for Element '{request.parameter_name}' from Node '{node_name}'."
1436
- logger.debug(details)
1437
-
1438
- result = AlterParameterDetailsResultSuccess()
1372
+ result = AlterParameterDetailsResultSuccess(result_details=details)
1439
1373
  return result
1440
1374
 
1441
1375
  # For C901 (too complex): Need to give customers explicit reasons for failure on each case.
@@ -1446,7 +1380,6 @@ class NodeManager:
1446
1380
  if node_name is None:
1447
1381
  if not GriptapeNodes.ContextManager().has_current_node():
1448
1382
  details = f"Attempted to get value for Parameter '{request.parameter_name}' from node in the Current Context. Failed because there was no such Node."
1449
- logger.error(details)
1450
1383
 
1451
1384
  return GetParameterValueResultFailure(result_details=details)
1452
1385
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -1461,14 +1394,12 @@ class NodeManager:
1461
1394
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
1462
1395
  if node is None:
1463
1396
  details = f'"{node_name}" not found'
1464
- logger.error(details)
1465
1397
  return GetParameterValueResultFailure(result_details=details)
1466
1398
 
1467
1399
  # Does the Parameter actually exist on the Node?
1468
1400
  parameter = node.get_parameter_by_name(param_name)
1469
1401
  if parameter is None:
1470
1402
  details = f'"{node_name}.{param_name}" not found'
1471
- logger.error(details)
1472
1403
  return GetParameterValueResultFailure(result_details=details)
1473
1404
 
1474
1405
  # Values are actually stored on the NODE, so let's ask them.
@@ -1484,13 +1415,12 @@ class NodeManager:
1484
1415
 
1485
1416
  # Cool.
1486
1417
  details = f"{node_name}.{request.parameter_name} = {data_value}"
1487
- logger.debug(details)
1488
-
1489
1418
  result = GetParameterValueResultSuccess(
1490
1419
  input_types=parameter.input_types,
1491
1420
  type=parameter.type,
1492
1421
  output_type=parameter.output_type,
1493
1422
  value=TypeValidator.safe_serialize(data_value),
1423
+ result_details=details,
1494
1424
  )
1495
1425
  return result
1496
1426
 
@@ -1508,7 +1438,6 @@ class NodeManager:
1508
1438
  if node_name is None:
1509
1439
  if not GriptapeNodes.ContextManager().has_current_node():
1510
1440
  details = f"Attempted to set parameter '{request.parameter_name}' value. Failed because no Node was found in the Current Context."
1511
- logger.error(details)
1512
1441
  return SetParameterValueResultFailure(result_details=details)
1513
1442
  node = GriptapeNodes.ContextManager().get_current_node()
1514
1443
  node_name = node.name
@@ -1522,13 +1451,11 @@ class NodeManager:
1522
1451
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
1523
1452
  if node is None:
1524
1453
  details = f"Attempted to set parameter '{param_name}' value on node '{node_name}'. Failed because no such Node could be found."
1525
- logger.error(details)
1526
1454
  return SetParameterValueResultFailure(result_details=details)
1527
1455
 
1528
1456
  # Is the node locked?
1529
1457
  if node.lock:
1530
1458
  details = f"Attempted to set parameter '{param_name}' value on node '{node_name}'. Failed because the Node was locked."
1531
- logger.error(details)
1532
1459
  return SetParameterValueResultFailure(result_details=details)
1533
1460
 
1534
1461
  # Handle ErrorProxyNode parameter value requests
@@ -1544,14 +1471,12 @@ class NodeManager:
1544
1471
  else:
1545
1472
  # Reject runtime parameter value changes on ErrorProxy
1546
1473
  details = f"Cannot set parameter '{param_name}' on placeholder node '{node_name}'. This placeholder preserves your workflow structure but doesn't allow parameter changes, as they could cause issues when the original node is restored."
1547
- logger.error(details)
1548
1474
  return SetParameterValueResultFailure(result_details=details)
1549
1475
 
1550
1476
  # Does the Parameter actually exist on the Node?
1551
1477
  parameter = node.get_parameter_by_name(param_name)
1552
1478
  if parameter is None:
1553
1479
  details = f"Attempted to set parameter value for '{node_name}.{param_name}'. Failed because no parameter with that name could be found."
1554
- logger.error(details)
1555
1480
 
1556
1481
  result = SetParameterValueResultFailure(result_details=details)
1557
1482
  return result
@@ -1561,7 +1486,6 @@ class NodeManager:
1561
1486
  incoming_param_set = request.incoming_connection_source_parameter_name is not None
1562
1487
  if incoming_node_set != incoming_param_set:
1563
1488
  details = f"Attempted to set parameter value for '{node_name}.{request.parameter_name}'. Failed because incoming connection source fields must both be None or both be set. Got incoming_connection_source_node_name={request.incoming_connection_source_node_name}, incoming_connection_source_parameter_name={request.incoming_connection_source_parameter_name}."
1564
- logger.error(details)
1565
1489
  result = SetParameterValueResultFailure(result_details=details)
1566
1490
  return result
1567
1491
 
@@ -1583,7 +1507,6 @@ class NodeManager:
1583
1507
  if param_connections: # Has incoming connections
1584
1508
  # TODO: https://github.com/griptape-ai/griptape-nodes/issues/1965 Consider emitting UI events when parameters become settable/unsettable due to connection changes
1585
1509
  details = f"Attempted to set parameter value for '{node_name}.{request.parameter_name}'. Failed because this parameter has incoming connections and cannot be set as a property while connected."
1586
- logger.error(details)
1587
1510
  result = SetParameterValueResultFailure(result_details=details)
1588
1511
  return result
1589
1512
 
@@ -1594,7 +1517,6 @@ class NodeManager:
1594
1517
  request.value = modified_value
1595
1518
  except Exception as err:
1596
1519
  details = f"Attempted to set parameter value for '{node_name}.{request.parameter_name}'. Failed because before_value_set hook raised exception: {err}"
1597
- logger.error(details)
1598
1520
  result = SetParameterValueResultFailure(result_details=details)
1599
1521
  return result
1600
1522
 
@@ -1602,7 +1524,6 @@ class NodeManager:
1602
1524
  # This check comes after before_value_set to allow nodes to temporarily modify settable state
1603
1525
  if not parameter.settable and not request.initial_setup:
1604
1526
  details = f"Attempted to set parameter value for '{node_name}.{request.parameter_name}'. Failed because that Parameter was flagged as not settable."
1605
- logger.error(details)
1606
1527
  result = SetParameterValueResultFailure(result_details=details)
1607
1528
  return result
1608
1529
 
@@ -1611,7 +1532,6 @@ class NodeManager:
1611
1532
  # Is this value kosher for the types allowed?
1612
1533
  if not parameter.is_incoming_type_allowed(object_type):
1613
1534
  details = f"Attempted to set parameter value for '{node_name}.{request.parameter_name}'. Failed because the value's type of '{object_type}' was not in the Parameter's list of allowed types: {parameter.input_types}."
1614
- logger.error(details)
1615
1535
 
1616
1536
  result = SetParameterValueResultFailure(result_details=details)
1617
1537
  return result
@@ -1620,27 +1540,23 @@ class NodeManager:
1620
1540
  parent_flow_name = self.get_node_parent_flow_by_name(node.name)
1621
1541
  except KeyError:
1622
1542
  details = f"Attempted to set parameter value for '{node_name}.{request.parameter_name}'. Failed because the node's parent flow does not exist. Could not unresolve future nodes."
1623
- logger.error(details)
1624
1543
  return SetParameterValueResultFailure(result_details=details)
1625
1544
 
1626
1545
  obj_mgr = GriptapeNodes.ObjectManager()
1627
1546
  parent_flow = obj_mgr.attempt_get_object_by_name_as_type(parent_flow_name, ControlFlow)
1628
1547
  if not parent_flow:
1629
1548
  details = f"Attempted to set parameter value for '{node_name}.{request.parameter_name}'. Failed because the node's parent flow does not exist. Could not unresolve future nodes."
1630
- logger.error(details)
1631
1549
  return SetParameterValueResultFailure(result_details=details)
1632
1550
  try:
1633
1551
  finalized_value, modified = self._set_and_pass_through_values(request, node)
1634
1552
  except Exception as err:
1635
1553
  details = f"Attempted to set parameter value for '{node_name}.{request.parameter_name}'. Failed because Exception: {err}"
1636
- logger.error(details)
1637
1554
  return SetParameterValueResultFailure(result_details=details)
1638
1555
  if not request.initial_setup and modified:
1639
1556
  try:
1640
1557
  GriptapeNodes.FlowManager().get_connections().unresolve_future_nodes(node)
1641
1558
  except Exception as err:
1642
1559
  details = f"Attempted to set parameter value for '{node_name}.{request.parameter_name}'. Failed because Exception: {err}"
1643
- logger.error(details)
1644
1560
  return SetParameterValueResultFailure(result_details=details)
1645
1561
  if request.initial_setup is False and not request.is_output and modified:
1646
1562
  # Mark node as unresolved, broadcast an event
@@ -1666,9 +1582,9 @@ class NodeManager:
1666
1582
 
1667
1583
  # Cool.
1668
1584
  details = f"Successfully set value on Node '{node_name}' Parameter '{request.parameter_name}'."
1669
- logger.debug(details)
1670
-
1671
- result = SetParameterValueResultSuccess(finalized_value=finalized_value, data_type=parameter.type)
1585
+ result = SetParameterValueResultSuccess(
1586
+ finalized_value=finalized_value, data_type=parameter.type, result_details=details
1587
+ )
1672
1588
  return result
1673
1589
 
1674
1590
  def _set_and_pass_through_values(self, request: SetParameterValueRequest, node: BaseNode) -> ModifiedReturnValue:
@@ -1703,7 +1619,7 @@ class NodeManager:
1703
1619
  # want to give clear reasoning for each failure.
1704
1620
  # For PLR0915 (too many statements): very little reusable code here, want to be explicit and
1705
1621
  # make debugger use friendly.
1706
- def on_get_all_node_info_request(self, request: GetAllNodeInfoRequest) -> ResultPayload: # noqa: C901, PLR0911, PLR0915
1622
+ def on_get_all_node_info_request(self, request: GetAllNodeInfoRequest) -> ResultPayload: # noqa: C901, PLR0911
1707
1623
  node_name = request.node_name
1708
1624
  node = None
1709
1625
 
@@ -1711,7 +1627,6 @@ class NodeManager:
1711
1627
  if node_name is None:
1712
1628
  if not GriptapeNodes.ContextManager().has_current_node():
1713
1629
  details = "Attempted to get all info for a Node from the Current Context. Failed because the Current Context is empty."
1714
- logger.error(details)
1715
1630
  return GetAllNodeInfoResultFailure(result_details=details)
1716
1631
 
1717
1632
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -1723,14 +1638,12 @@ class NodeManager:
1723
1638
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
1724
1639
  if node is None:
1725
1640
  details = f"Attempted to get all info for Node named '{node_name}', but no such Node was found."
1726
- logger.error(details)
1727
1641
  return GetAllNodeInfoResultFailure(result_details=details)
1728
1642
 
1729
1643
  get_metadata_request = GetNodeMetadataRequest(node_name=node_name)
1730
1644
  get_metadata_result = self.on_get_node_metadata_request(get_metadata_request)
1731
1645
  if not get_metadata_result.succeeded():
1732
1646
  details = f"Attempted to get all info for Node named '{node_name}', but failed getting the metadata."
1733
- logger.error(details)
1734
1647
  return GetAllNodeInfoResultFailure(result_details=details)
1735
1648
 
1736
1649
  get_resolution_state_request = GetNodeResolutionStateRequest(node_name=node_name)
@@ -1739,7 +1652,6 @@ class NodeManager:
1739
1652
  details = (
1740
1653
  f"Attempted to get all info for Node named '{node_name}', but failed getting the resolution state."
1741
1654
  )
1742
- logger.error(details)
1743
1655
  return GetAllNodeInfoResultFailure(result_details=details)
1744
1656
 
1745
1657
  list_connections_request = ListConnectionsForNodeRequest(node_name=node_name)
@@ -1748,7 +1660,6 @@ class NodeManager:
1748
1660
  details = (
1749
1661
  f"Attempted to get all info for Node named '{node_name}', but failed listing all connections for it."
1750
1662
  )
1751
- logger.error(details)
1752
1663
 
1753
1664
  return GetAllNodeInfoResultFailure(result_details=details)
1754
1665
  # Cast everything to get the linter off our back.
@@ -1758,7 +1669,6 @@ class NodeManager:
1758
1669
  list_connections_success = cast("ListConnectionsForNodeResultSuccess", list_connections_result)
1759
1670
  except Exception as err:
1760
1671
  details = f"Attempted to get all info for Node named '{node_name}'. Failed due to error: {err}."
1761
- logger.error(details)
1762
1672
 
1763
1673
  return GetAllNodeInfoResultFailure(result_details=details)
1764
1674
  get_node_elements_request = GetNodeElementDetailsRequest(node_name=node_name)
@@ -1767,13 +1677,11 @@ class NodeManager:
1767
1677
  details = (
1768
1678
  f"Attempted to get all info for Node named '{node_name}', but failed getting details for elements."
1769
1679
  )
1770
- logger.error(details)
1771
1680
  return GetAllNodeInfoResultFailure(result_details=details)
1772
1681
  try:
1773
1682
  get_element_details_success = cast("GetNodeElementDetailsResultSuccess", get_node_elements_result)
1774
1683
  except Exception as err:
1775
1684
  details = f"Attempted to get all info for Node named '{node_name}'. Failed due to error: {err}."
1776
- logger.error(details)
1777
1685
  return GetAllNodeInfoResultFailure(result_details=details)
1778
1686
 
1779
1687
  # this will return the node element and the value
@@ -1784,7 +1692,6 @@ class NodeManager:
1784
1692
  else:
1785
1693
  element_id_to_value = {}
1786
1694
  details = f"Successfully got all node info for node '{node_name}'."
1787
- logger.debug(details)
1788
1695
  result = GetAllNodeInfoResultSuccess(
1789
1696
  metadata=get_metadata_success.metadata,
1790
1697
  node_resolution_state=get_resolution_state_success.state,
@@ -1792,6 +1699,7 @@ class NodeManager:
1792
1699
  connections=list_connections_success,
1793
1700
  element_id_to_value=element_id_to_value,
1794
1701
  root_node_element=element_details,
1702
+ result_details=details,
1795
1703
  )
1796
1704
  return result
1797
1705
 
@@ -1802,7 +1710,6 @@ class NodeManager:
1802
1710
  if node_name is None:
1803
1711
  if not GriptapeNodes.ContextManager().has_current_node():
1804
1712
  details = "Attempted to get compatible parameters for node, but no current node was found."
1805
- logger.error(details)
1806
1713
  return GetCompatibleParametersResultFailure(result_details=details)
1807
1714
  node = GriptapeNodes.ContextManager().get_current_node()
1808
1715
  node_name = node.name
@@ -1815,14 +1722,12 @@ class NodeManager:
1815
1722
  details = (
1816
1723
  f"Attempted to get compatible parameters for node '{node_name}', but that node does not exist."
1817
1724
  )
1818
- logger.error(details)
1819
1725
  return GetCompatibleParametersResultFailure(result_details=details)
1820
1726
 
1821
1727
  # Vet the parameter.
1822
1728
  request_param = node.get_parameter_by_name(request.parameter_name)
1823
1729
  if request_param is None:
1824
1730
  details = f"Attempted to get compatible parameters for '{node_name}.{request.parameter_name}', but that no Parameter with that name could not be found."
1825
- logger.error(details)
1826
1731
  return GetCompatibleParametersResultFailure(result_details=details)
1827
1732
 
1828
1733
  # Figure out the mode we're going for, and if this parameter supports the mode.
@@ -1830,7 +1735,6 @@ class NodeManager:
1830
1735
  # Does this parameter support that?
1831
1736
  if request_mode not in request_param.allowed_modes:
1832
1737
  details = f"Attempted to get compatible parameters for '{node_name}.{request.parameter_name}' as '{request_mode}', but the Parameter didn't support that type of input/output."
1833
- logger.error(details)
1834
1738
  return GetCompatibleParametersResultFailure(result_details=details)
1835
1739
 
1836
1740
  # Get the parent flows.
@@ -1838,7 +1742,6 @@ class NodeManager:
1838
1742
  flow_name = self.get_node_parent_flow_by_name(node_name)
1839
1743
  except KeyError as err:
1840
1744
  details = f"Attempted to get compatible parameters for '{node_name}.{request.parameter_name}', but the node's parent flow could not be found: {err}"
1841
- logger.error(details)
1842
1745
  return GetCompatibleParametersResultFailure(result_details=details)
1843
1746
 
1844
1747
  # Iterate through all nodes in this Flow (yes, this restriction still sucks)
@@ -1848,14 +1751,12 @@ class NodeManager:
1848
1751
  )
1849
1752
  if not list_nodes_in_flow_result.succeeded():
1850
1753
  details = f"Attempted to get compatible parameters for '{node_name}.{request.parameter_name}'. Failed due to inability to list nodes in parent flow '{flow_name}'."
1851
- logger.error(details)
1852
1754
  return GetCompatibleParametersResultFailure(result_details=details)
1853
1755
 
1854
1756
  try:
1855
1757
  list_nodes_in_flow_success = cast("ListNodesInFlowResultSuccess", list_nodes_in_flow_result)
1856
1758
  except Exception as err:
1857
1759
  details = f"Attempted to get compatible parameters for '{node_name}.{request.parameter_name}'. Failed due to {err}"
1858
- logger.error(details)
1859
1760
  return GetCompatibleParametersResultFailure(result_details=details)
1860
1761
 
1861
1762
  # Walk through all nodes that are NOT us to find compatible Parameters.
@@ -1867,7 +1768,6 @@ class NodeManager:
1867
1768
  test_node = self.get_node_by_name(test_node_name)
1868
1769
  except ValueError as err:
1869
1770
  details = f"Attempted to get compatible parameters for node '{node_name}', and sought to test against {test_node_name}, but that node does not exist. Error: {err}."
1870
- logger.error(details)
1871
1771
  return GetCompatibleParametersResultFailure(result_details=details)
1872
1772
 
1873
1773
  # Get Parameters from Node
@@ -1904,8 +1804,9 @@ class NodeManager:
1904
1804
  valid_parameters_by_node[test_node_name] = compatible_list
1905
1805
 
1906
1806
  details = f"Successfully got compatible parameters for '{node_name}.{request.parameter_name}'."
1907
- logger.debug(details)
1908
- return GetCompatibleParametersResultSuccess(valid_parameters_by_node=valid_parameters_by_node)
1807
+ return GetCompatibleParametersResultSuccess(
1808
+ valid_parameters_by_node=valid_parameters_by_node, result_details=details
1809
+ )
1909
1810
 
1910
1811
  def get_node_by_name(self, name: str) -> BaseNode:
1911
1812
  obj_mgr = GriptapeNodes.ObjectManager()
@@ -1923,20 +1824,18 @@ class NodeManager:
1923
1824
  raise KeyError(msg)
1924
1825
  return self._name_to_parent_flow_name[node_name]
1925
1826
 
1926
- async def on_resolve_from_node_request(self, request: ResolveNodeRequest) -> ResultPayload: # noqa: C901, PLR0911, PLR0915, PLR0912
1827
+ async def on_resolve_from_node_request(self, request: ResolveNodeRequest) -> ResultPayload: # noqa: C901, PLR0911, PLR0912
1927
1828
  node_name = request.node_name
1928
1829
  debug_mode = request.debug_mode
1929
1830
 
1930
1831
  if node_name is None:
1931
1832
  details = "No Node name was provided. Failed to resolve node."
1932
- logger.error(details)
1933
1833
 
1934
1834
  return ResolveNodeResultFailure(validation_exceptions=[], result_details=details)
1935
1835
  try:
1936
1836
  node = self.get_node_by_name(node_name)
1937
1837
  except ValueError as e:
1938
1838
  details = f'Resolve failure. "{node_name}" does not exist. {e}'
1939
- logger.error(details)
1940
1839
 
1941
1840
  return ResolveNodeResultFailure(validation_exceptions=[e], result_details=details)
1942
1841
  # try to get the flow parent of this node
@@ -1944,7 +1843,6 @@ class NodeManager:
1944
1843
  flow_name = self._name_to_parent_flow_name[node_name]
1945
1844
  except KeyError as e:
1946
1845
  details = f'Failed to fetch parent flow for "{node_name}": {e}'
1947
- logger.error(details)
1948
1846
 
1949
1847
  return ResolveNodeResultFailure(validation_exceptions=[e], result_details=details)
1950
1848
  try:
@@ -1952,30 +1850,25 @@ class NodeManager:
1952
1850
  flow = obj_mgr.attempt_get_object_by_name_as_type(flow_name, ControlFlow)
1953
1851
  except KeyError as e:
1954
1852
  details = f'Failed to fetch parent flow for "{node_name}": {e}'
1955
- logger.error(details)
1956
1853
 
1957
1854
  return ResolveNodeResultFailure(validation_exceptions=[e], result_details=details)
1958
1855
 
1959
1856
  if flow is None:
1960
1857
  details = f'Failed to fetch parent flow for "{node_name}"'
1961
- logger.error(details)
1962
1858
  return ResolveNodeResultFailure(validation_exceptions=[], result_details=details)
1963
1859
  if GriptapeNodes.FlowManager().check_for_existing_running_flow():
1964
1860
  details = f"Failed to resolve from node '{node_name}'. Flow is already running."
1965
- logger.error(details)
1966
1861
  return ResolveNodeResultFailure(validation_exceptions=[], result_details=details)
1967
1862
  try:
1968
1863
  GriptapeNodes.FlowManager().get_connections().unresolve_future_nodes(node)
1969
1864
  except Exception as e:
1970
1865
  details = f'Failed to mark future nodes dirty. Unable to kick off flow from "{node_name}": {e}'
1971
- logger.error(details)
1972
1866
  return ResolveNodeResultFailure(validation_exceptions=[e], result_details=details)
1973
1867
  # Validate here.
1974
1868
  result = self.on_validate_node_dependencies_request(ValidateNodeDependenciesRequest(node_name=node_name))
1975
1869
  try:
1976
1870
  if not result.succeeded():
1977
1871
  details = f"Failed to resolve node '{node_name}'. Flow Validation Failed"
1978
- logger.error(details)
1979
1872
  return StartFlowResultFailure(validation_exceptions=[], result_details=details)
1980
1873
  result = cast("ValidateNodeDependenciesResultSuccess", result)
1981
1874
 
@@ -1984,21 +1877,17 @@ class NodeManager:
1984
1877
  if len(result.exceptions) > 0:
1985
1878
  for exception in result.exceptions:
1986
1879
  details = f"{details}\n\t{exception}"
1987
- logger.error(details)
1988
1880
  return StartFlowResultFailure(validation_exceptions=result.exceptions, result_details=details)
1989
1881
  except Exception as e:
1990
1882
  details = f"Failed to resolve node '{node_name}'. Flow Validation Failed. Error: {e}"
1991
- logger.error(details)
1992
1883
  return StartFlowResultFailure(validation_exceptions=[e], result_details=details)
1993
1884
  try:
1994
- await GriptapeNodes.FlowManager().resolve_singular_node(flow, node, debug_mode)
1885
+ await GriptapeNodes.FlowManager().resolve_singular_node(flow, node, debug_mode=debug_mode)
1995
1886
  except Exception as e:
1996
1887
  details = f'Failed to resolve "{node_name}". Error: {e}'
1997
- logger.error(details)
1998
1888
  return ResolveNodeResultFailure(validation_exceptions=[e], result_details=details)
1999
1889
  details = f'Starting to resolve "{node_name}" in "{flow_name}"'
2000
- logger.debug(details)
2001
- return ResolveNodeResultSuccess()
1890
+ return ResolveNodeResultSuccess(result_details=details)
2002
1891
 
2003
1892
  def on_validate_node_dependencies_request(self, request: ValidateNodeDependenciesRequest) -> ResultPayload:
2004
1893
  node_name = request.node_name
@@ -2006,18 +1895,15 @@ class NodeManager:
2006
1895
  node = obj_manager.attempt_get_object_by_name_as_type(node_name, BaseNode)
2007
1896
  if node is None:
2008
1897
  details = f'Failed to validate node dependencies. Node with "{node_name}" does not exist.'
2009
- logger.error(details)
2010
1898
  return ValidateNodeDependenciesResultFailure(result_details=details)
2011
1899
  try:
2012
1900
  flow_name = self.get_node_parent_flow_by_name(node_name)
2013
1901
  except Exception as e:
2014
1902
  details = f'Failed to validate node dependencies. Node with "{node_name}" has no parent flow. Error: {e}'
2015
- logger.error(details)
2016
1903
  return ValidateNodeDependenciesResultFailure(result_details=details)
2017
1904
  flow = GriptapeNodes.ObjectManager().attempt_get_object_by_name_as_type(flow_name, ControlFlow)
2018
1905
  if not flow:
2019
1906
  details = f'Failed to validate node dependencies. Flow with "{flow_name}" does not exist.'
2020
- logger.error(details)
2021
1907
  return ValidateNodeDependenciesResultFailure(result_details=details)
2022
1908
  # Gets all dependent nodes
2023
1909
  nodes = flow.get_node_dependencies(node)
@@ -2027,7 +1913,9 @@ class NodeManager:
2027
1913
  if exceptions:
2028
1914
  all_exceptions = all_exceptions + exceptions
2029
1915
  return ValidateNodeDependenciesResultSuccess(
2030
- validation_succeeded=(len(all_exceptions) == 0), exceptions=all_exceptions
1916
+ validation_succeeded=(len(all_exceptions) == 0),
1917
+ exceptions=all_exceptions,
1918
+ result_details=f"Successfully validated dependencies for node '{node_name}'. Found {len(all_exceptions)} validation issues.",
2031
1919
  )
2032
1920
 
2033
1921
  def on_serialize_node_to_commands(self, request: SerializeNodeToCommandsRequest) -> ResultPayload: # noqa: C901, PLR0912, PLR0915
@@ -2037,7 +1925,6 @@ class NodeManager:
2037
1925
  if node_name is None:
2038
1926
  if not GriptapeNodes.ContextManager().has_current_node():
2039
1927
  details = "Attempted to serialize a Node to commands from the Current Context. Failed because the Current Context is empty."
2040
- logger.error(details)
2041
1928
  return SerializeNodeToCommandsResultFailure(result_details=details)
2042
1929
  node = GriptapeNodes.ContextManager().get_current_node()
2043
1930
  node_name = node.name
@@ -2048,7 +1935,6 @@ class NodeManager:
2048
1935
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
2049
1936
  if node is None:
2050
1937
  details = f"Attempted to serialize Node '{node_name}' to commands. Failed because no Node with that name could be found."
2051
- logger.error(details)
2052
1938
  return SerializeNodeToCommandsResultFailure(result_details=details)
2053
1939
 
2054
1940
  # This is our current dude.
@@ -2073,7 +1959,6 @@ class NodeManager:
2073
1959
  else:
2074
1960
  # For regular nodes, this is still an error
2075
1961
  details = f"Attempted to serialize Node '{node_name}' to commands. Failed to get metadata for library '{library_used}'."
2076
- logger.error(details)
2077
1962
  return SerializeNodeToCommandsResultFailure(result_details=details)
2078
1963
  else:
2079
1964
  library_version = library_metadata_result.metadata.library_version
@@ -2161,11 +2046,10 @@ class NodeManager:
2161
2046
  node=node,
2162
2047
  unique_parameter_uuid_to_values=request.unique_parameter_uuid_to_values,
2163
2048
  serialized_parameter_value_tracker=request.serialized_parameter_value_tracker,
2049
+ create_node_request=create_node_request,
2164
2050
  )
2165
2051
  if set_param_value_requests is not None:
2166
2052
  set_value_commands.extend(set_param_value_requests)
2167
- else:
2168
- create_node_request.resolution = NodeResolutionState.UNRESOLVED.value
2169
2053
  # now check if locked
2170
2054
  if node.lock:
2171
2055
  lock_command = SetLockNodeStateRequest(node_name=None, lock=True)
@@ -2179,10 +2063,10 @@ class NodeManager:
2179
2063
  lock_node_command=lock_command,
2180
2064
  )
2181
2065
  details = f"Successfully serialized node '{node_name}' into commands."
2182
- logger.debug(details)
2183
2066
  result = SerializeNodeToCommandsResultSuccess(
2184
2067
  serialized_node_commands=serialized_node_commands, # How to serialize this node
2185
2068
  set_parameter_value_commands=set_value_commands, # The commands to serialize it with
2069
+ result_details=details,
2186
2070
  )
2187
2071
  return result
2188
2072
 
@@ -2300,7 +2184,6 @@ class NodeManager:
2300
2184
  create_node_result = GriptapeNodes().handle_request(create_node_request)
2301
2185
  if not isinstance(create_node_result, CreateNodeResultSuccess):
2302
2186
  details = f"Attempted to deserialize a serialized set of Node Creation commands. Failed to create node '{create_node_request.node_name}'."
2303
- logger.error(details)
2304
2187
  return DeserializeNodeFromCommandsResultFailure(result_details=details)
2305
2188
 
2306
2189
  # Adopt the newly-created node as our current context.
@@ -2308,7 +2191,6 @@ class NodeManager:
2308
2191
  node = GriptapeNodes.ObjectManager().attempt_get_object_by_name_as_type(node_name, BaseNode)
2309
2192
  if node is None:
2310
2193
  details = f"Attempted to deserialize a serialized set of Node Creation commands. Failed to get node '{node_name}'."
2311
- logger.error(details)
2312
2194
  return DeserializeNodeFromCommandsResultFailure(result_details=details)
2313
2195
  with GriptapeNodes.ContextManager().node(node=node):
2314
2196
  for element_command in request.serialized_node_commands.element_modification_commands:
@@ -2319,11 +2201,9 @@ class NodeManager:
2319
2201
  element_result = GriptapeNodes().handle_request(element_command)
2320
2202
  if element_result.failed():
2321
2203
  details = f"Attempted to deserialize a serialized set of Node Creation commands. Failed to execute an element command for node '{node_name}'."
2322
- logger.error(details)
2323
2204
  return DeserializeNodeFromCommandsResultFailure(result_details=details)
2324
2205
  details = f"Successfully deserialized a serialized set of Node Creation commands for node '{node_name}'."
2325
- logger.debug(details)
2326
- return DeserializeNodeFromCommandsResultSuccess(node_name=node_name)
2206
+ return DeserializeNodeFromCommandsResultSuccess(node_name=node_name, result_details=details)
2327
2207
 
2328
2208
  def on_serialize_selected_nodes_to_commands(
2329
2209
  self, request: SerializeSelectedNodesToCommandsRequest
@@ -2355,7 +2235,6 @@ class NodeManager:
2355
2235
  )
2356
2236
  if not isinstance(result, SerializeNodeToCommandsResultSuccess):
2357
2237
  details = f"Attempted to serialize a selection of Nodes. Failed to serialize {node_name}."
2358
- logger.error(details)
2359
2238
  return SerializeNodeToCommandsResultFailure(result_details=details)
2360
2239
  node_commands[node_name] = result.serialized_node_commands
2361
2240
  node_name_to_uuid[node_name] = result.serialized_node_commands.node_uuid
@@ -2401,7 +2280,10 @@ class NodeManager:
2401
2280
  # Set everything in the clipboard!
2402
2281
  GriptapeNodes.ContextManager()._clipboard.node_commands = final_result
2403
2282
  GriptapeNodes.ContextManager()._clipboard.parameter_uuid_to_values = unique_uuid_to_values
2404
- return SerializeSelectedNodesToCommandsResultSuccess(final_result)
2283
+ return SerializeSelectedNodesToCommandsResultSuccess(
2284
+ final_result,
2285
+ result_details=f"Successfully serialized {len(request.nodes_to_serialize)} selected nodes to commands.",
2286
+ )
2405
2287
 
2406
2288
  def on_deserialize_selected_nodes_from_commands( # noqa: C901, PLR0912
2407
2289
  self,
@@ -2431,13 +2313,11 @@ class NodeManager:
2431
2313
  )
2432
2314
  if not isinstance(result, DeserializeNodeFromCommandsResultSuccess):
2433
2315
  details = "Attempted to deserialize node but ran into an error on node serialization."
2434
- logger.error(details)
2435
2316
  return DeserializeSelectedNodesFromCommandsResultFailure(result_details=details)
2436
2317
  node_uuid_to_name[node_command.node_uuid] = result.node_name
2437
2318
  node = GriptapeNodes.ObjectManager().attempt_get_object_by_name_as_type(result.node_name, BaseNode)
2438
2319
  if node is None:
2439
2320
  details = "Attempted to deserialize node but ran into an error on node serialization."
2440
- logger.error(details)
2441
2321
  return DeserializeSelectedNodesFromCommandsResultFailure(result_details=details)
2442
2322
  with GriptapeNodes.ContextManager().node(node=node):
2443
2323
  parameter_commands = commands.set_parameter_value_commands[node_command.node_uuid]
@@ -2480,7 +2360,10 @@ class NodeManager:
2480
2360
  if not result.succeeded():
2481
2361
  details = f"Failed to create a connection between {connection_request.source_node_name} and {connection_request.target_node_name}"
2482
2362
  logger.warning(details)
2483
- return DeserializeSelectedNodesFromCommandsResultSuccess(node_names=list(node_uuid_to_name.values()))
2363
+ return DeserializeSelectedNodesFromCommandsResultSuccess(
2364
+ node_names=list(node_uuid_to_name.values()),
2365
+ result_details=f"Successfully deserialized {len(node_uuid_to_name)} nodes from commands.",
2366
+ )
2484
2367
 
2485
2368
  def on_duplicate_selected_nodes(self, request: DuplicateSelectedNodesRequest) -> ResultPayload:
2486
2369
  result = GriptapeNodes.handle_request(
@@ -2488,12 +2371,10 @@ class NodeManager:
2488
2371
  )
2489
2372
  if not result.succeeded():
2490
2373
  details = "Failed to serialized selected nodes."
2491
- logger.error(details)
2492
2374
  return DuplicateSelectedNodesResultFailure(result_details=details)
2493
2375
  result = GriptapeNodes.handle_request(DeserializeSelectedNodesFromCommandsRequest(positions=request.positions))
2494
2376
  if not isinstance(result, DeserializeSelectedNodesFromCommandsResultSuccess):
2495
2377
  details = "Failed to deserialize selected nodes."
2496
- logger.error(details)
2497
2378
  return DuplicateSelectedNodesResultFailure(result_details=details)
2498
2379
 
2499
2380
  # Remake duplicate connections of node
@@ -2502,7 +2383,9 @@ class NodeManager:
2502
2383
  initial_nodes = [sublist[0] for sublist in request.nodes_to_duplicate]
2503
2384
 
2504
2385
  NodeManager.remake_connections(self, new_node_names=result.node_names, old_node_names=initial_nodes)
2505
- return DuplicateSelectedNodesResultSuccess(result.node_names)
2386
+ return DuplicateSelectedNodesResultSuccess(
2387
+ result.node_names, result_details=f"Successfully duplicated {len(initial_nodes)} nodes."
2388
+ )
2506
2389
 
2507
2390
  @staticmethod
2508
2391
  def _manage_alter_details(parameter: Parameter, base_node_obj: BaseNode) -> dict:
@@ -2584,6 +2467,7 @@ class NodeManager:
2584
2467
  node: BaseNode,
2585
2468
  unique_parameter_uuid_to_values: dict[SerializedNodeCommands.UniqueParameterValueUUID, Any],
2586
2469
  serialized_parameter_value_tracker: SerializedParameterValueTracker,
2470
+ create_node_request: CreateNodeRequest,
2587
2471
  ) -> list[SerializedNodeCommands.IndirectSetParameterValueCommand] | None:
2588
2472
  """Generates code to save a parameter value for a node in a Griptape workflow.
2589
2473
 
@@ -2600,6 +2484,7 @@ class NodeManager:
2600
2484
  node (BaseNode): The node object that contains the parameter
2601
2485
  unique_parameter_uuid_to_values (dict[SerializedNodeCommands.UniqueParameterValueUUID, Any]): Dictionary mapping unique value UUIDs to values
2602
2486
  serialized_parameter_value_tracker (SerializedParameterValueTracker): Object mapping maintaining value hashes to unique value UUIDs, and non-serializable values
2487
+ create_node_request (CreateNodeRequest): The node creation request that will be modified if serialization fails
2603
2488
 
2604
2489
  Returns:
2605
2490
  None (if no value to be serialized) or an IndirectSetParameterValueCommand linking the value to the unique value map
@@ -2612,7 +2497,7 @@ class NodeManager:
2612
2497
  """
2613
2498
  output_value = None
2614
2499
  internal_value = None
2615
- if parameter.name in node.parameter_output_values and node.state == NodeResolutionState.RESOLVED:
2500
+ if parameter.name in node.parameter_output_values:
2616
2501
  # Output values are more important.
2617
2502
  output_value = node.parameter_output_values[parameter.name]
2618
2503
  if parameter.name in node.parameter_values:
@@ -2634,6 +2519,8 @@ class NodeManager:
2634
2519
  if internal_command is None:
2635
2520
  details = f"Attempted to serialize set value for parameter '{parameter.name}' on node '{node.name}'. The set value will not be restored in anything that attempts to deserialize or save this node. The value for this parameter was not serialized because it did not match Griptape Nodes' criteria for serializability. To remedy, either update the value's type to support serializability or mark the parameter as not serializable by setting serializable=False when creating the parameter."
2636
2521
  logger.warning(details)
2522
+ # Set node to unresolved when serialization fails
2523
+ create_node_request.resolution = NodeResolutionState.UNRESOLVED.value
2637
2524
  else:
2638
2525
  commands.append(internal_command)
2639
2526
  if output_value is not None:
@@ -2649,6 +2536,8 @@ class NodeManager:
2649
2536
  if output_command is None:
2650
2537
  details = f"Attempted to serialize output value for parameter '{parameter.name}' on node '{node.name}'. The output value will not be restored in anything that attempts to deserialize or save this node. The value for this parameter was not serialized because it did not match Griptape Nodes' criteria for serializability. To remedy, either update the value's type to support serializability or mark the parameter as not serializable by setting serializable=False when creating the parameter."
2651
2538
  logger.warning(details)
2539
+ # Set node to unresolved when serialization fails
2540
+ create_node_request.resolution = NodeResolutionState.UNRESOLVED.value
2652
2541
  else:
2653
2542
  commands.append(output_command)
2654
2543
  return commands if commands else None
@@ -2667,7 +2556,6 @@ class NodeManager:
2667
2556
  if node_name is None:
2668
2557
  if not GriptapeNodes.ContextManager().has_current_node():
2669
2558
  details = "Attempted to rename Parameter in the Current Context. Failed because the Current Context was empty."
2670
- logger.error(details)
2671
2559
  return RenameParameterResultFailure(result_details=details)
2672
2560
  node = GriptapeNodes.ContextManager().get_current_node()
2673
2561
  node_name = node.name
@@ -2676,38 +2564,32 @@ class NodeManager:
2676
2564
  node = self.get_node_by_name(node_name)
2677
2565
  except KeyError as err:
2678
2566
  details = f"Attempted to rename Parameter '{request.parameter_name}' on Node '{node_name}'. Failed because the Node could not be found. Error: {err}"
2679
- logger.error(details)
2680
2567
  return RenameParameterResultFailure(result_details=details)
2681
2568
 
2682
2569
  # Is the node locked?
2683
2570
  if node.lock:
2684
2571
  details = f"Attempted to rename Parameter '{request.parameter_name}' on Node '{node_name}'. Failed because the Node is locked."
2685
- logger.error(details)
2686
2572
  return RenameParameterResultFailure(result_details=details)
2687
2573
 
2688
2574
  # Get the parameter
2689
2575
  parameter = node.get_parameter_by_name(request.parameter_name)
2690
2576
  if parameter is None:
2691
2577
  details = f"Attempted to rename Parameter '{request.parameter_name}' on Node '{node_name}'. Failed because the Parameter could not be found."
2692
- logger.error(details)
2693
2578
  return RenameParameterResultFailure(result_details=details)
2694
2579
 
2695
2580
  # Only allow parameter rename for user-defined params
2696
2581
  if not parameter.user_defined:
2697
2582
  details = f"Attempted to rename Parameter '{request.parameter_name}' on Node '{node_name}'. Failed because the Parameter is not user-defined."
2698
- logger.error(details)
2699
2583
  return RenameParameterResultFailure(result_details=details)
2700
2584
 
2701
2585
  # Validate the new parameter name
2702
2586
  if any(char.isspace() for char in request.new_parameter_name):
2703
2587
  details = f"Failed to rename Parameter '{request.parameter_name}' to '{request.new_parameter_name}'. Parameter names cannot contain any whitespace characters."
2704
- logger.error(details)
2705
2588
  return RenameParameterResultFailure(result_details=details)
2706
2589
 
2707
2590
  # Check for duplicate names
2708
2591
  if node.does_name_exist(request.new_parameter_name):
2709
2592
  details = f"Failed to rename Parameter '{request.parameter_name}' to '{request.new_parameter_name}'. A Parameter with that name already exists."
2710
- logger.error(details)
2711
2593
  return RenameParameterResultFailure(result_details=details)
2712
2594
 
2713
2595
  # Get all connections for this node
@@ -2743,7 +2625,10 @@ class NodeManager:
2743
2625
  node.parameter_output_values[request.new_parameter_name] = node.parameter_output_values.pop(old_name)
2744
2626
 
2745
2627
  return RenameParameterResultSuccess(
2746
- old_parameter_name=old_name, new_parameter_name=request.new_parameter_name, node_name=node_name
2628
+ old_parameter_name=old_name,
2629
+ new_parameter_name=request.new_parameter_name,
2630
+ node_name=node_name,
2631
+ result_details=f"Successfully renamed parameter '{old_name}' to '{request.new_parameter_name}' on node '{node_name}'.",
2747
2632
  )
2748
2633
 
2749
2634
  def on_toggle_lock_node_request(self, request: SetLockNodeStateRequest) -> ResultPayload:
@@ -2751,7 +2636,6 @@ class NodeManager:
2751
2636
  if node_name is None:
2752
2637
  if not GriptapeNodes.ContextManager().has_current_node():
2753
2638
  details = "Attempted to lock node in the Current Context. Failed because the Current Context was empty."
2754
- logger.error(details)
2755
2639
  return SetLockNodeStateResultFailure(result_details=details)
2756
2640
  node = GriptapeNodes.ContextManager().get_current_node()
2757
2641
  node_name = node.name
@@ -2760,10 +2644,13 @@ class NodeManager:
2760
2644
  node = self.get_node_by_name(node_name)
2761
2645
  except ValueError as err:
2762
2646
  details = f"Attempted to lock node '{request.node_name}'. Failed because the Node could not be found. Error: {err}"
2763
- logger.error(details)
2764
2647
  return SetLockNodeStateResultFailure(result_details=details)
2765
2648
  node.lock = request.lock
2766
- return SetLockNodeStateResultSuccess(node_name=node_name, locked=node.lock)
2649
+ return SetLockNodeStateResultSuccess(
2650
+ node_name=node_name,
2651
+ locked=node.lock,
2652
+ result_details=f"Successfully set lock state to {node.lock} for node '{node_name}'.",
2653
+ )
2767
2654
 
2768
2655
  def on_send_node_message_request(self, request: SendNodeMessageRequest) -> ResultPayload:
2769
2656
  """Handle a SendNodeMessageRequest by calling the node's message callback.
@@ -2781,7 +2668,6 @@ class NodeManager:
2781
2668
  # Get from the current context
2782
2669
  if not GriptapeNodes.ContextManager().has_current_node():
2783
2670
  details = "Attempted to send message to Node from Current Context. Failed because the Current Context is empty."
2784
- logger.error(details)
2785
2671
  return SendNodeMessageResultFailure(result_details=details)
2786
2672
 
2787
2673
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -2793,7 +2679,6 @@ class NodeManager:
2793
2679
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
2794
2680
  if node is None:
2795
2681
  details = f"Attempted to send message to Node '{node_name}', but no such Node was found."
2796
- logger.error(details)
2797
2682
  return SendNodeMessageResultFailure(result_details=details)
2798
2683
 
2799
2684
  # Validate optional_element_name if specified
@@ -2801,7 +2686,6 @@ class NodeManager:
2801
2686
  element = node.root_ui_element.find_element_by_name(request.optional_element_name)
2802
2687
  if element is None:
2803
2688
  details = f"Attempted to send message to Node '{node_name}' with element '{request.optional_element_name}', but no such element was found."
2804
- logger.error(details)
2805
2689
  return SendNodeMessageResultFailure(result_details=details, altered_workflow_state=False)
2806
2690
 
2807
2691
  # Call the node's message callback
@@ -2813,7 +2697,6 @@ class NodeManager:
2813
2697
 
2814
2698
  if not callback_result.success:
2815
2699
  details = f"Failed to handle message for Node '{node_name}': {callback_result.details}"
2816
- logger.warning(details)
2817
2700
  return SendNodeMessageResultFailure(
2818
2701
  result_details=callback_result.details,
2819
2702
  response=callback_result.response,
@@ -2821,7 +2704,6 @@ class NodeManager:
2821
2704
  )
2822
2705
 
2823
2706
  details = f"Successfully sent message to Node '{node_name}': {callback_result.details}"
2824
- logger.debug(details)
2825
2707
  return SendNodeMessageResultSuccess(
2826
2708
  result_details=callback_result.details,
2827
2709
  response=callback_result.response,