griptape-nodes 0.52.1__py3-none-any.whl → 0.54.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 (71) hide show
  1. griptape_nodes/__init__.py +8 -942
  2. griptape_nodes/__main__.py +6 -0
  3. griptape_nodes/app/app.py +48 -86
  4. griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +35 -5
  5. griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +15 -1
  6. griptape_nodes/cli/__init__.py +1 -0
  7. griptape_nodes/cli/commands/__init__.py +1 -0
  8. griptape_nodes/cli/commands/config.py +74 -0
  9. griptape_nodes/cli/commands/engine.py +80 -0
  10. griptape_nodes/cli/commands/init.py +550 -0
  11. griptape_nodes/cli/commands/libraries.py +96 -0
  12. griptape_nodes/cli/commands/models.py +504 -0
  13. griptape_nodes/cli/commands/self.py +120 -0
  14. griptape_nodes/cli/main.py +56 -0
  15. griptape_nodes/cli/shared.py +75 -0
  16. griptape_nodes/common/__init__.py +1 -0
  17. griptape_nodes/common/directed_graph.py +71 -0
  18. griptape_nodes/drivers/storage/base_storage_driver.py +40 -20
  19. griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +24 -29
  20. griptape_nodes/drivers/storage/local_storage_driver.py +23 -14
  21. griptape_nodes/exe_types/core_types.py +60 -2
  22. griptape_nodes/exe_types/node_types.py +257 -38
  23. griptape_nodes/exe_types/param_components/__init__.py +1 -0
  24. griptape_nodes/exe_types/param_components/execution_status_component.py +138 -0
  25. griptape_nodes/machines/control_flow.py +195 -94
  26. griptape_nodes/machines/dag_builder.py +207 -0
  27. griptape_nodes/machines/fsm.py +10 -1
  28. griptape_nodes/machines/parallel_resolution.py +558 -0
  29. griptape_nodes/machines/{node_resolution.py → sequential_resolution.py} +30 -57
  30. griptape_nodes/node_library/library_registry.py +34 -1
  31. griptape_nodes/retained_mode/events/app_events.py +5 -1
  32. griptape_nodes/retained_mode/events/base_events.py +9 -9
  33. griptape_nodes/retained_mode/events/config_events.py +30 -0
  34. griptape_nodes/retained_mode/events/execution_events.py +2 -2
  35. griptape_nodes/retained_mode/events/model_events.py +296 -0
  36. griptape_nodes/retained_mode/events/node_events.py +4 -3
  37. griptape_nodes/retained_mode/griptape_nodes.py +34 -12
  38. griptape_nodes/retained_mode/managers/agent_manager.py +23 -5
  39. griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +3 -1
  40. griptape_nodes/retained_mode/managers/config_manager.py +44 -3
  41. griptape_nodes/retained_mode/managers/context_manager.py +6 -5
  42. griptape_nodes/retained_mode/managers/event_manager.py +8 -2
  43. griptape_nodes/retained_mode/managers/flow_manager.py +150 -206
  44. griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +1 -1
  45. griptape_nodes/retained_mode/managers/library_manager.py +35 -25
  46. griptape_nodes/retained_mode/managers/model_manager.py +1107 -0
  47. griptape_nodes/retained_mode/managers/node_manager.py +102 -220
  48. griptape_nodes/retained_mode/managers/object_manager.py +11 -5
  49. griptape_nodes/retained_mode/managers/os_manager.py +28 -13
  50. griptape_nodes/retained_mode/managers/secrets_manager.py +8 -4
  51. griptape_nodes/retained_mode/managers/settings.py +116 -7
  52. griptape_nodes/retained_mode/managers/static_files_manager.py +85 -12
  53. griptape_nodes/retained_mode/managers/sync_manager.py +17 -9
  54. griptape_nodes/retained_mode/managers/workflow_manager.py +186 -192
  55. griptape_nodes/retained_mode/retained_mode.py +19 -0
  56. griptape_nodes/servers/__init__.py +1 -0
  57. griptape_nodes/{mcp_server/server.py → servers/mcp.py} +1 -1
  58. griptape_nodes/{app/api.py → servers/static.py} +43 -40
  59. griptape_nodes/traits/add_param_button.py +1 -1
  60. griptape_nodes/traits/button.py +334 -6
  61. griptape_nodes/traits/color_picker.py +66 -0
  62. griptape_nodes/traits/multi_options.py +188 -0
  63. griptape_nodes/traits/numbers_selector.py +77 -0
  64. griptape_nodes/traits/options.py +93 -2
  65. griptape_nodes/traits/traits.json +4 -0
  66. griptape_nodes/utils/async_utils.py +31 -0
  67. {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.54.0.dist-info}/METADATA +4 -1
  68. {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.54.0.dist-info}/RECORD +71 -48
  69. {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.54.0.dist-info}/WHEEL +1 -1
  70. /griptape_nodes/{mcp_server → servers}/ws_request_manager.py +0 -0
  71. {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.54.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)
@@ -366,8 +362,6 @@ class NodeManager:
366
362
  log_level = logging.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(
@@ -448,35 +445,32 @@ class NodeManager:
448
445
  # get the current node executing / resolving
449
446
  # if it's in connected nodes, cancel flow.
450
447
  # otherwise, leave it.
451
- control_node_name, resolving_node_name = GriptapeNodes.FlowManager().flow_state(parent_flow)
448
+ control_node_names, resolving_node_names = GriptapeNodes.FlowManager().flow_state(parent_flow)
452
449
  connected_nodes = parent_flow.get_all_connected_nodes(node)
453
450
  cancelled = False
454
- if control_node_name is not None:
455
- control_node = GriptapeNodes.ObjectManager().get_object_by_name(control_node_name)
456
- if control_node in connected_nodes:
457
- result = GriptapeNodes.handle_request(CancelFlowRequest(flow_name=parent_flow_name))
458
- cancelled = True
459
- if not result.succeeded():
460
- details = (
461
- f"Attempted to delete a Node '{node.name}'. Failed because running flow could not cancel."
462
- )
463
- logger.error(details)
464
- return DeleteNodeResultFailure(result_details=details)
465
- if resolving_node_name is not None and not cancelled:
466
- resolving_node = GriptapeNodes.ObjectManager().get_object_by_name(resolving_node_name)
467
- if resolving_node in connected_nodes:
468
- result = GriptapeNodes.handle_request(CancelFlowRequest(flow_name=parent_flow_name))
469
- if not result.succeeded():
470
- details = (
471
- f"Attempted to delete a Node '{node.name}'. Failed because running flow could not cancel."
472
- )
473
- logger.error(details)
474
- return DeleteNodeResultFailure(result_details=details)
451
+ if control_node_names is not None:
452
+ for control_node_name in control_node_names:
453
+ control_node = GriptapeNodes.ObjectManager().get_object_by_name(control_node_name)
454
+ if control_node in connected_nodes:
455
+ result = GriptapeNodes.handle_request(CancelFlowRequest(flow_name=parent_flow_name))
456
+ cancelled = True
457
+ if not result.succeeded():
458
+ details = f"Attempted to delete a Node '{node.name}'. Failed because running flow could not cancel."
459
+ return DeleteNodeResultFailure(result_details=details)
460
+ if resolving_node_names is not None and not cancelled:
461
+ for resolving_node_name in resolving_node_names:
462
+ resolving_node = GriptapeNodes.ObjectManager().get_object_by_name(resolving_node_name)
463
+ if resolving_node in connected_nodes:
464
+ result = GriptapeNodes.handle_request(CancelFlowRequest(flow_name=parent_flow_name))
465
+ if not result.succeeded():
466
+ details = f"Attempted to delete a Node '{node.name}'. Failed because running flow could not cancel."
467
+ return DeleteNodeResultFailure(result_details=details)
468
+ break # Only need to cancel once
475
469
  # Clear the execution queue, because we don't want to hit this node eventually.
476
470
  parent_flow.clear_execution_queue()
477
471
  return None
478
472
 
479
- def on_delete_node_request(self, request: DeleteNodeRequest) -> ResultPayload: # noqa: C901, PLR0911, PLR0912, PLR0915 (complex logic, lots of edge cases)
473
+ def on_delete_node_request(self, request: DeleteNodeRequest) -> ResultPayload: # noqa: C901, PLR0911, PLR0912 (complex logic, lots of edge cases)
480
474
  node_name = request.node_name
481
475
  node = None
482
476
  if node_name is None:
@@ -485,7 +479,6 @@ class NodeManager:
485
479
  details = (
486
480
  "Attempted to delete a Node from the Current Context. Failed because the Current Context is empty."
487
481
  )
488
- logger.error(details)
489
482
  return DeleteNodeResultFailure(result_details=details)
490
483
 
491
484
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -494,7 +487,6 @@ class NodeManager:
494
487
  node = GriptapeNodes.ObjectManager().attempt_get_object_by_name_as_type(node_name, BaseNode)
495
488
  if node is None:
496
489
  details = f"Attempted to delete a Node '{node_name}', but no such Node was found."
497
- logger.error(details)
498
490
  return DeleteNodeResultFailure(result_details=details)
499
491
 
500
492
  with GriptapeNodes.ContextManager().node(node=node):
@@ -503,7 +495,6 @@ class NodeManager:
503
495
  parent_flow = GriptapeNodes.FlowManager().get_flow_by_name(parent_flow_name)
504
496
  except KeyError as err:
505
497
  details = f"Attempted to delete a Node '{node_name}'. Error: {err}"
506
- logger.error(details)
507
498
  return DeleteNodeResultFailure(result_details=details)
508
499
 
509
500
  cancel_result = self.cancel_conditionally(parent_flow, parent_flow_name, node)
@@ -519,7 +510,6 @@ class NodeManager:
519
510
  list_connections_result = GriptapeNodes.handle_request(request=list_node_connections_request)
520
511
  if not isinstance(list_connections_result, ListConnectionsForNodeResultSuccess):
521
512
  details = f"Attempted to delete a Node '{node_name}'. Failed because it could not gather Connections to the Node."
522
- logger.error(details)
523
513
  return DeleteNodeResultFailure(result_details=details)
524
514
 
525
515
  # Check incoming connections
@@ -537,7 +527,6 @@ class NodeManager:
537
527
  details = (
538
528
  f"Attempted to delete a Node '{node_name}'. Failed when attempting to delete Connection."
539
529
  )
540
- logger.error(details)
541
530
  return DeleteNodeResultFailure(result_details=details)
542
531
  continue # Refresh connection list after cascading deletions
543
532
 
@@ -556,7 +545,6 @@ class NodeManager:
556
545
  details = (
557
546
  f"Attempted to delete a Node '{node_name}'. Failed when attempting to delete Connection."
558
547
  )
559
- logger.error(details)
560
548
  return DeleteNodeResultFailure(result_details=details)
561
549
 
562
550
  # Remove from the owning Flow
@@ -571,9 +559,7 @@ class NodeManager:
571
559
  GriptapeNodes.ContextManager().pop_node()
572
560
 
573
561
  details = f"Successfully deleted Node '{node_name}'."
574
- logger.debug(details)
575
-
576
- return DeleteNodeResultSuccess()
562
+ return DeleteNodeResultSuccess(result_details=details)
577
563
 
578
564
  def on_get_node_resolution_state_request(self, request: GetNodeResolutionStateRequest) -> ResultPayload:
579
565
  node_name = request.node_name
@@ -582,7 +568,6 @@ class NodeManager:
582
568
  # Get from the current context.
583
569
  if not GriptapeNodes.ContextManager().has_current_node():
584
570
  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
571
  return GetNodeResolutionStateResultFailure(result_details=details)
587
572
 
588
573
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -594,18 +579,13 @@ class NodeManager:
594
579
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
595
580
  if node is None:
596
581
  details = f"Attempted to get resolution state for a Node '{node_name}', but no such Node was found."
597
- logger.error(details)
598
582
  result = GetNodeResolutionStateResultFailure(result_details=details)
599
583
  return result
600
584
 
601
585
  node_state = node.state
602
586
 
603
587
  details = f"Successfully got resolution state for Node '{node_name}'."
604
- logger.debug(details)
605
-
606
- result = GetNodeResolutionStateResultSuccess(
607
- state=node_state.name,
608
- )
588
+ result = GetNodeResolutionStateResultSuccess(state=node_state.name, result_details=details)
609
589
  return result
610
590
 
611
591
  def on_get_node_metadata_request(self, request: GetNodeMetadataRequest) -> ResultPayload:
@@ -615,7 +595,6 @@ class NodeManager:
615
595
  # Get from the current context.
616
596
  if not GriptapeNodes.ContextManager().has_current_node():
617
597
  details = "Attempted to get metadata for a Node from the Current Context. Failed because the Current Context is empty."
618
- logger.error(details)
619
598
  return GetNodeMetadataResultFailure(result_details=details)
620
599
 
621
600
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -628,18 +607,13 @@ class NodeManager:
628
607
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
629
608
  if node is None:
630
609
  details = f"Attempted to get metadata for a Node '{node_name}', but no such Node was found."
631
- logger.error(details)
632
610
 
633
611
  result = GetNodeMetadataResultFailure(result_details=details)
634
612
  return result
635
613
 
636
614
  metadata = node.metadata
637
615
  details = f"Successfully retrieved metadata for a Node '{node_name}'."
638
- logger.debug(details)
639
-
640
- result = GetNodeMetadataResultSuccess(
641
- metadata=metadata,
642
- )
616
+ result = GetNodeMetadataResultSuccess(metadata=metadata, result_details=details)
643
617
  return result
644
618
 
645
619
  def on_set_node_metadata_request(self, request: SetNodeMetadataRequest) -> ResultPayload:
@@ -649,7 +623,6 @@ class NodeManager:
649
623
  # Get from the current context.
650
624
  if not GriptapeNodes.ContextManager().has_current_node():
651
625
  details = "Attempted to set metadata for a Node from the Current Context. Failed because the Current Context is empty."
652
- logger.error(details)
653
626
  return SetNodeMetadataResultFailure(result_details=details)
654
627
 
655
628
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -662,7 +635,6 @@ class NodeManager:
662
635
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
663
636
  if node is None:
664
637
  details = f"Attempted to set metadata for a Node '{node_name}', but no such Node was found."
665
- logger.error(details)
666
638
 
667
639
  result = SetNodeMetadataResultFailure(result_details=details)
668
640
  return result
@@ -671,9 +643,7 @@ class NodeManager:
671
643
  for key, value in request.metadata.items():
672
644
  node.metadata[key] = value
673
645
  details = f"Successfully set metadata for a Node '{node_name}'."
674
- logger.debug(details)
675
-
676
- result = SetNodeMetadataResultSuccess()
646
+ result = SetNodeMetadataResultSuccess(result_details=details)
677
647
  return result
678
648
 
679
649
  def on_batch_set_node_metadata_request(self, request: BatchSetNodeMetadataRequest) -> ResultPayload:
@@ -714,7 +684,11 @@ class NodeManager:
714
684
  result_details=f"Failed to update any nodes. Failed nodes: {failed_nodes}"
715
685
  )
716
686
 
717
- return BatchSetNodeMetadataResultSuccess(updated_nodes=updated_nodes, failed_nodes=failed_nodes)
687
+ return BatchSetNodeMetadataResultSuccess(
688
+ updated_nodes=updated_nodes,
689
+ failed_nodes=failed_nodes,
690
+ result_details=f"Successfully updated metadata for {len(updated_nodes)} nodes.",
691
+ )
718
692
 
719
693
  def on_list_connections_for_node_request(self, request: ListConnectionsForNodeRequest) -> ResultPayload:
720
694
  node_name = request.node_name
@@ -723,7 +697,6 @@ class NodeManager:
723
697
  # Get from the current context.
724
698
  if not GriptapeNodes.ContextManager().has_current_node():
725
699
  details = "Attempted to list Connections for a Node from the Current Context. Failed because the Current Context is empty."
726
- logger.error(details)
727
700
  return ListConnectionsForNodeResultFailure(result_details=details)
728
701
 
729
702
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -736,7 +709,6 @@ class NodeManager:
736
709
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
737
710
  if node is None:
738
711
  details = f"Attempted to list Connections for a Node '{node_name}', but no such Node was found."
739
- logger.error(details)
740
712
 
741
713
  result = ListConnectionsForNodeResultFailure(result_details=details)
742
714
  return result
@@ -746,7 +718,6 @@ class NodeManager:
746
718
  GriptapeNodes.FlowManager().get_flow_by_name(parent_flow_name)
747
719
  except KeyError as err:
748
720
  details = f"Attempted to list Connections for a Node '{node_name}'. Error: {err}"
749
- logger.error(details)
750
721
 
751
722
  result = ListConnectionsForNodeResultFailure(result_details=details)
752
723
  return result
@@ -783,11 +754,10 @@ class NodeManager:
783
754
  ]
784
755
 
785
756
  details = f"Successfully listed all Connections to and from Node '{node_name}'."
786
- logger.debug(details)
787
-
788
757
  result = ListConnectionsForNodeResultSuccess(
789
758
  incoming_connections=incoming_connections_list,
790
759
  outgoing_connections=outgoing_connections_list,
760
+ result_details=details,
791
761
  )
792
762
  return result
793
763
 
@@ -799,7 +769,6 @@ class NodeManager:
799
769
  # Get from the current context.
800
770
  if not GriptapeNodes.ContextManager().has_current_node():
801
771
  details = "Attempted to list Parameters for a Node from the Current Context. Failed because the Current Context is empty."
802
- logger.error(details)
803
772
  return ListParametersOnNodeResultFailure(result_details=details)
804
773
 
805
774
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -811,7 +780,6 @@ class NodeManager:
811
780
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
812
781
  if node is None:
813
782
  details = f"Attempted to list Parameters for a Node '{node_name}', but no such Node was found."
814
- logger.error(details)
815
783
 
816
784
  result = ListParametersOnNodeResultFailure(result_details=details)
817
785
  return result
@@ -819,11 +787,7 @@ class NodeManager:
819
787
  ret_list = [param.name for param in node.parameters]
820
788
 
821
789
  details = f"Successfully listed Parameters for Node '{node_name}'."
822
- logger.debug(details)
823
-
824
- result = ListParametersOnNodeResultSuccess(
825
- parameter_names=ret_list,
826
- )
790
+ result = ListParametersOnNodeResultSuccess(parameter_names=ret_list, result_details=details)
827
791
  return result
828
792
 
829
793
  def generate_unique_parameter_name(self, node: BaseNode, base_name: str) -> str:
@@ -852,7 +816,6 @@ class NodeManager:
852
816
  # Get from the current context.
853
817
  if not GriptapeNodes.ContextManager().has_current_node():
854
818
  details = "Attempted to add Parameter to a Node from the Current Context. Failed because the Current Context is empty."
855
- logger.error(details)
856
819
  return AddParameterToNodeResultFailure(result_details=details)
857
820
 
858
821
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -864,7 +827,6 @@ class NodeManager:
864
827
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
865
828
  if node is None:
866
829
  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
830
 
869
831
  result = AddParameterToNodeResultFailure(result_details=details)
870
832
  return result
@@ -872,7 +834,6 @@ class NodeManager:
872
834
  # Check if node is locked
873
835
  if node.lock:
874
836
  details = f"Attempted to add Parameter '{request.parameter_name}' to Node '{node_name}'. Failed because the Node was locked."
875
- logger.error(details)
876
837
  result = AddParameterToNodeResultFailure(result_details=details)
877
838
  return result
878
839
 
@@ -880,12 +841,10 @@ class NodeManager:
880
841
  parameter = node.get_parameter_by_name(request.parent_container_name)
881
842
  if parameter is None:
882
843
  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
844
  result = AddParameterToNodeResultFailure(result_details=details)
885
845
  return result
886
846
  if not isinstance(parameter, ParameterContainer):
887
847
  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
848
  result = AddParameterToNodeResultFailure(result_details=details)
890
849
  return result
891
850
  try:
@@ -897,11 +856,13 @@ class NodeManager:
897
856
  return result
898
857
 
899
858
  return AddParameterToNodeResultSuccess(
900
- parameter_name=new_param.name, type=new_param.type, node_name=node_name
859
+ parameter_name=new_param.name,
860
+ type=new_param.type,
861
+ node_name=node_name,
862
+ result_details=f"Successfully added parameter '{new_param.name}' to container parameter '{request.parent_container_name}' in node '{node_name}'.",
901
863
  )
902
864
  if request.parameter_name is None or request.tooltip is None:
903
865
  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
866
  result = AddParameterToNodeResultFailure(result_details=details)
906
867
  return result
907
868
 
@@ -937,7 +898,6 @@ class NodeManager:
937
898
 
938
899
  if has_control_type and has_non_control_types:
939
900
  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
901
 
942
902
  result = AddParameterToNodeResultFailure(result_details=details)
943
903
  return result
@@ -976,7 +936,6 @@ class NodeManager:
976
936
  node.add_parameter(new_param)
977
937
  except Exception as e:
978
938
  details = f"Couldn't add parameter with name {request.parameter_name} to Node '{node_name}'. Error: {e}"
979
- logger.error(details)
980
939
  return AddParameterToNodeResultFailure(result_details=details)
981
940
 
982
941
  details = f"Successfully added Parameter '{final_param_name}' to Node '{node_name}'."
@@ -988,7 +947,7 @@ class NodeManager:
988
947
  logger.log(level=log_level, msg=details)
989
948
 
990
949
  result = AddParameterToNodeResultSuccess(
991
- parameter_name=new_param.name, type=new_param.type, node_name=node_name
950
+ parameter_name=new_param.name, type=new_param.type, node_name=node_name, result_details=details
992
951
  )
993
952
  return result
994
953
 
@@ -1000,7 +959,6 @@ class NodeManager:
1000
959
  # Get the Current Context
1001
960
  if not GriptapeNodes.ContextManager().has_current_node():
1002
961
  details = f"Attempted to remove Parameter '{request.parameter_name}' from a Node, but no Current Context was found."
1003
- logger.error(details)
1004
962
 
1005
963
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1006
964
  return result
@@ -1014,14 +972,12 @@ class NodeManager:
1014
972
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
1015
973
  if node is None:
1016
974
  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
975
 
1019
976
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1020
977
  return result
1021
978
  # Check if the node is locked
1022
979
  if node.lock:
1023
980
  details = f"Attempted to remove Element '{request.parameter_name}' from Node '{node_name}'. Failed because the Node was locked."
1024
- logger.error(details)
1025
981
 
1026
982
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1027
983
  return result
@@ -1029,7 +985,6 @@ class NodeManager:
1029
985
  element = node.get_element_by_name_and_type(request.parameter_name)
1030
986
  if element is None:
1031
987
  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
988
 
1034
989
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1035
990
  return result
@@ -1040,13 +995,14 @@ class NodeManager:
1040
995
  GriptapeNodes.handle_request(RemoveParameterFromNodeRequest(child.name, node_name))
1041
996
  node.remove_parameter_element_by_name(request.parameter_name)
1042
997
 
1043
- return RemoveParameterFromNodeResultSuccess()
998
+ return RemoveParameterFromNodeResultSuccess(
999
+ result_details=f"Successfully removed parameter group '{request.parameter_name}' and all its children from node '{node_name}'."
1000
+ )
1044
1001
 
1045
1002
  # No tricky stuff, users!
1046
1003
  # if user_defined doesn't exist, or is false, then it's not user-defined
1047
1004
  if not getattr(element, "user_defined", False):
1048
1005
  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
1006
 
1051
1007
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1052
1008
  return result
@@ -1057,7 +1013,6 @@ class NodeManager:
1057
1013
  list_connections_result = GriptapeNodes.handle_request(request=list_node_connections_request)
1058
1014
  if not isinstance(list_connections_result, ListConnectionsForNodeResultSuccess):
1059
1015
  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
1016
 
1062
1017
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1063
1018
  return result
@@ -1076,7 +1031,6 @@ class NodeManager:
1076
1031
  delete_result = GriptapeNodes.handle_request(delete_request)
1077
1032
  if isinstance(delete_result, DeleteConnectionResultFailure):
1078
1033
  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
1034
 
1081
1035
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1082
1036
 
@@ -1092,7 +1046,6 @@ class NodeManager:
1092
1046
  delete_result = GriptapeNodes.handle_request(delete_request)
1093
1047
  if isinstance(delete_result, DeleteConnectionResultFailure):
1094
1048
  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
1049
 
1097
1050
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1098
1051
 
@@ -1101,14 +1054,11 @@ class NodeManager:
1101
1054
  node.remove_parameter_element(element)
1102
1055
  else:
1103
1056
  details = f"Attempted to remove Element '{request.parameter_name}' from Node '{node_name}'. Failed because element didn't exist."
1104
- logger.error(details)
1105
1057
 
1106
1058
  result = RemoveParameterFromNodeResultFailure(result_details=details)
1107
1059
 
1108
1060
  details = f"Successfully removed Element '{request.parameter_name}' from Node '{node_name}'."
1109
- logger.debug(details)
1110
-
1111
- result = RemoveParameterFromNodeResultSuccess()
1061
+ result = RemoveParameterFromNodeResultSuccess(result_details=details)
1112
1062
  return result
1113
1063
 
1114
1064
  def on_get_parameter_details_request(self, request: GetParameterDetailsRequest) -> ResultPayload:
@@ -1118,7 +1068,6 @@ class NodeManager:
1118
1068
  if node_name is None:
1119
1069
  if not GriptapeNodes.ContextManager().has_current_node():
1120
1070
  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
1071
 
1123
1072
  result = GetParameterDetailsResultFailure(result_details=details)
1124
1073
  return result
@@ -1131,7 +1080,6 @@ class NodeManager:
1131
1080
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
1132
1081
  if node is None:
1133
1082
  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
1083
 
1136
1084
  result = GetParameterDetailsResultFailure(result_details=details)
1137
1085
  return result
@@ -1141,7 +1089,6 @@ class NodeManager:
1141
1089
 
1142
1090
  if element is None:
1143
1091
  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
1092
  return GetParameterDetailsResultFailure(result_details=details)
1146
1093
 
1147
1094
  # Let's bundle up the details.
@@ -1155,9 +1102,7 @@ class NodeManager:
1155
1102
  allows_property = ParameterMode.PROPERTY in modes_allowed
1156
1103
  allows_output = ParameterMode.OUTPUT in modes_allowed
1157
1104
 
1158
- details = f"Successfully got details for Element '{request.parameter_name}' from Node '{node_name}'."
1159
- logger.debug(details)
1160
-
1105
+ details = f"Successfully got details for Element '{request.parameter_name}' from Node '{node_name}'."
1161
1106
  result = GetParameterDetailsResultSuccess(
1162
1107
  element_id=element.element_id,
1163
1108
  type=getattr(element, "type", ""),
@@ -1174,6 +1119,7 @@ class NodeManager:
1174
1119
  is_user_defined=getattr(element, "user_defined", False),
1175
1120
  settable=getattr(element, "settable", None),
1176
1121
  ui_options=getattr(element, "ui_options", None),
1122
+ result_details=details,
1177
1123
  )
1178
1124
  return result
1179
1125
 
@@ -1184,7 +1130,6 @@ class NodeManager:
1184
1130
  if node_name is None:
1185
1131
  if not GriptapeNodes.ContextManager().has_current_node():
1186
1132
  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
1133
 
1189
1134
  return GetNodeElementDetailsResultFailure(result_details=details)
1190
1135
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -1196,7 +1141,6 @@ class NodeManager:
1196
1141
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
1197
1142
  if node is None:
1198
1143
  details = f"Attempted to get element details for Node '{node_name}', but no such Node was found."
1199
- logger.error(details)
1200
1144
 
1201
1145
  return GetNodeElementDetailsResultFailure(result_details=details)
1202
1146
 
@@ -1208,7 +1152,6 @@ class NodeManager:
1208
1152
  element = node.root_ui_element.find_element_by_id(request.specific_element_id)
1209
1153
  if element is None:
1210
1154
  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
1155
 
1213
1156
  return GetNodeElementDetailsResultFailure(result_details=details)
1214
1157
 
@@ -1219,8 +1162,7 @@ class NodeManager:
1219
1162
  if param_to_value:
1220
1163
  element_details["element_id_to_value"] = param_to_value
1221
1164
  details = f"Successfully got element details for Node '{node_name}'."
1222
- logger.debug(details)
1223
- result = GetNodeElementDetailsResultSuccess(element_details=element_details)
1165
+ result = GetNodeElementDetailsResultSuccess(element_details=element_details, result_details=details)
1224
1166
  return result
1225
1167
 
1226
1168
  def _set_param_to_value(self, node: BaseNode, element: BaseNodeElement, param_to_value: dict) -> None:
@@ -1326,7 +1268,6 @@ class NodeManager:
1326
1268
  )
1327
1269
  if isinstance(delete_result, ResultPayloadFailure):
1328
1270
  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
1271
  return AlterParameterDetailsResultFailure(result_details=details)
1331
1272
 
1332
1273
  # Check and break invalid outgoing connections
@@ -1345,7 +1286,6 @@ class NodeManager:
1345
1286
  )
1346
1287
  if isinstance(delete_result, ResultPayloadFailure):
1347
1288
  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
1289
  return AlterParameterDetailsResultFailure(result_details=details)
1350
1290
 
1351
1291
  return None
@@ -1357,7 +1297,6 @@ class NodeManager:
1357
1297
  if node_name is None:
1358
1298
  if not GriptapeNodes.ContextManager().has_current_node():
1359
1299
  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
1300
 
1362
1301
  return AlterParameterDetailsResultFailure(result_details=details)
1363
1302
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -1369,14 +1308,12 @@ class NodeManager:
1369
1308
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
1370
1309
  if node is None:
1371
1310
  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
1311
 
1374
1312
  return AlterParameterDetailsResultFailure(result_details=details)
1375
1313
 
1376
1314
  # Is the node locked?
1377
1315
  if node.lock:
1378
1316
  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
1317
  return AlterParameterDetailsResultFailure(result_details=details)
1381
1318
 
1382
1319
  # Handle ErrorProxyNode parameter alteration requests
@@ -1387,21 +1324,18 @@ class NodeManager:
1387
1324
 
1388
1325
  # Early return with warning - we're just preserving the original changes
1389
1326
  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
1327
 
1392
- result_details = ResultDetails(message=details, level="WARNING")
1328
+ result_details = ResultDetails(message=details, level=logging.WARNING)
1393
1329
  return AlterParameterDetailsResultSuccess(result_details=result_details)
1394
1330
 
1395
1331
  # Reject runtime parameter alterations on ErrorProxy
1396
1332
  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
1333
  return AlterParameterDetailsResultFailure(result_details=details)
1399
1334
 
1400
1335
  # Does the Element actually exist on the Node?
1401
1336
  element = node.get_element_by_name_and_type(request.parameter_name)
1402
1337
  if element is None:
1403
1338
  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
1339
  return AlterParameterDetailsResultFailure(result_details=details)
1406
1340
  if request.ui_options is not None:
1407
1341
  element.ui_options = request.ui_options # type: ignore[attr-defined]
@@ -1423,8 +1357,9 @@ class NodeManager:
1423
1357
  if hasattr(element, "user_defined") and element.user_defined is False and request.request_id: # type: ignore[attr-defined]
1424
1358
  # TODO: https://github.com/griptape-ai/griptape-nodes/issues/826
1425
1359
  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()
1360
+ return AlterParameterDetailsResultSuccess(
1361
+ result_details=ResultDetails(message=details, level=logging.WARNING)
1362
+ )
1428
1363
  self.modify_key_parameter_fields(request, element)
1429
1364
 
1430
1365
  # This field requires the node as well
@@ -1433,9 +1368,7 @@ class NodeManager:
1433
1368
  node.parameter_values[request.parameter_name] = request.default_value
1434
1369
 
1435
1370
  details = f"Successfully altered details for Element '{request.parameter_name}' from Node '{node_name}'."
1436
- logger.debug(details)
1437
-
1438
- result = AlterParameterDetailsResultSuccess()
1371
+ result = AlterParameterDetailsResultSuccess(result_details=details)
1439
1372
  return result
1440
1373
 
1441
1374
  # For C901 (too complex): Need to give customers explicit reasons for failure on each case.
@@ -1446,7 +1379,6 @@ class NodeManager:
1446
1379
  if node_name is None:
1447
1380
  if not GriptapeNodes.ContextManager().has_current_node():
1448
1381
  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
1382
 
1451
1383
  return GetParameterValueResultFailure(result_details=details)
1452
1384
  node = GriptapeNodes.ContextManager().get_current_node()
@@ -1461,14 +1393,12 @@ class NodeManager:
1461
1393
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
1462
1394
  if node is None:
1463
1395
  details = f'"{node_name}" not found'
1464
- logger.error(details)
1465
1396
  return GetParameterValueResultFailure(result_details=details)
1466
1397
 
1467
1398
  # Does the Parameter actually exist on the Node?
1468
1399
  parameter = node.get_parameter_by_name(param_name)
1469
1400
  if parameter is None:
1470
1401
  details = f'"{node_name}.{param_name}" not found'
1471
- logger.error(details)
1472
1402
  return GetParameterValueResultFailure(result_details=details)
1473
1403
 
1474
1404
  # Values are actually stored on the NODE, so let's ask them.
@@ -1484,13 +1414,12 @@ class NodeManager:
1484
1414
 
1485
1415
  # Cool.
1486
1416
  details = f"{node_name}.{request.parameter_name} = {data_value}"
1487
- logger.debug(details)
1488
-
1489
1417
  result = GetParameterValueResultSuccess(
1490
1418
  input_types=parameter.input_types,
1491
1419
  type=parameter.type,
1492
1420
  output_type=parameter.output_type,
1493
1421
  value=TypeValidator.safe_serialize(data_value),
1422
+ result_details=details,
1494
1423
  )
1495
1424
  return result
1496
1425
 
@@ -1508,7 +1437,6 @@ class NodeManager:
1508
1437
  if node_name is None:
1509
1438
  if not GriptapeNodes.ContextManager().has_current_node():
1510
1439
  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
1440
  return SetParameterValueResultFailure(result_details=details)
1513
1441
  node = GriptapeNodes.ContextManager().get_current_node()
1514
1442
  node_name = node.name
@@ -1522,13 +1450,11 @@ class NodeManager:
1522
1450
  node = obj_mgr.attempt_get_object_by_name_as_type(node_name, BaseNode)
1523
1451
  if node is None:
1524
1452
  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
1453
  return SetParameterValueResultFailure(result_details=details)
1527
1454
 
1528
1455
  # Is the node locked?
1529
1456
  if node.lock:
1530
1457
  details = f"Attempted to set parameter '{param_name}' value on node '{node_name}'. Failed because the Node was locked."
1531
- logger.error(details)
1532
1458
  return SetParameterValueResultFailure(result_details=details)
1533
1459
 
1534
1460
  # Handle ErrorProxyNode parameter value requests
@@ -1544,14 +1470,12 @@ class NodeManager:
1544
1470
  else:
1545
1471
  # Reject runtime parameter value changes on ErrorProxy
1546
1472
  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
1473
  return SetParameterValueResultFailure(result_details=details)
1549
1474
 
1550
1475
  # Does the Parameter actually exist on the Node?
1551
1476
  parameter = node.get_parameter_by_name(param_name)
1552
1477
  if parameter is None:
1553
1478
  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
1479
 
1556
1480
  result = SetParameterValueResultFailure(result_details=details)
1557
1481
  return result
@@ -1561,7 +1485,6 @@ class NodeManager:
1561
1485
  incoming_param_set = request.incoming_connection_source_parameter_name is not None
1562
1486
  if incoming_node_set != incoming_param_set:
1563
1487
  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
1488
  result = SetParameterValueResultFailure(result_details=details)
1566
1489
  return result
1567
1490
 
@@ -1583,7 +1506,6 @@ class NodeManager:
1583
1506
  if param_connections: # Has incoming connections
1584
1507
  # TODO: https://github.com/griptape-ai/griptape-nodes/issues/1965 Consider emitting UI events when parameters become settable/unsettable due to connection changes
1585
1508
  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
1509
  result = SetParameterValueResultFailure(result_details=details)
1588
1510
  return result
1589
1511
 
@@ -1594,15 +1516,14 @@ class NodeManager:
1594
1516
  request.value = modified_value
1595
1517
  except Exception as err:
1596
1518
  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
1519
  result = SetParameterValueResultFailure(result_details=details)
1599
1520
  return result
1600
1521
 
1601
1522
  # Validate that parameters can be set at all (note: we want the value to be set during initial setup, but not after)
1602
- # This check comes after before_value_set to allow nodes to temporarily modify settable state
1603
- if not parameter.settable and not request.initial_setup:
1523
+ # We skip this if it's a passthru from a connection or if we're on initial setup; those always trump settable.
1524
+ # This check comes *AFTER* before_value_set() to allow nodes to temporarily modify settable state
1525
+ if not parameter.settable and not incoming_node_set 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,