griptape-nodes 0.53.0__py3-none-any.whl → 0.54.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. griptape_nodes/__init__.py +5 -2
  2. griptape_nodes/app/app.py +4 -26
  3. griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +35 -5
  4. griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +15 -1
  5. griptape_nodes/cli/commands/config.py +4 -1
  6. griptape_nodes/cli/commands/init.py +5 -3
  7. griptape_nodes/cli/commands/libraries.py +14 -8
  8. griptape_nodes/cli/commands/models.py +504 -0
  9. griptape_nodes/cli/commands/self.py +5 -2
  10. griptape_nodes/cli/main.py +11 -1
  11. griptape_nodes/cli/shared.py +0 -9
  12. griptape_nodes/common/directed_graph.py +17 -1
  13. griptape_nodes/drivers/storage/base_storage_driver.py +40 -20
  14. griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +24 -29
  15. griptape_nodes/drivers/storage/local_storage_driver.py +17 -13
  16. griptape_nodes/exe_types/node_types.py +219 -14
  17. griptape_nodes/exe_types/param_components/__init__.py +1 -0
  18. griptape_nodes/exe_types/param_components/execution_status_component.py +138 -0
  19. griptape_nodes/machines/control_flow.py +129 -92
  20. griptape_nodes/machines/dag_builder.py +207 -0
  21. griptape_nodes/machines/parallel_resolution.py +264 -276
  22. griptape_nodes/machines/sequential_resolution.py +9 -7
  23. griptape_nodes/node_library/library_registry.py +34 -1
  24. griptape_nodes/retained_mode/events/app_events.py +5 -1
  25. griptape_nodes/retained_mode/events/base_events.py +7 -7
  26. griptape_nodes/retained_mode/events/config_events.py +30 -0
  27. griptape_nodes/retained_mode/events/execution_events.py +2 -2
  28. griptape_nodes/retained_mode/events/model_events.py +296 -0
  29. griptape_nodes/retained_mode/griptape_nodes.py +10 -1
  30. griptape_nodes/retained_mode/managers/agent_manager.py +14 -0
  31. griptape_nodes/retained_mode/managers/config_manager.py +44 -3
  32. griptape_nodes/retained_mode/managers/event_manager.py +8 -2
  33. griptape_nodes/retained_mode/managers/flow_manager.py +45 -14
  34. griptape_nodes/retained_mode/managers/library_manager.py +3 -3
  35. griptape_nodes/retained_mode/managers/model_manager.py +1107 -0
  36. griptape_nodes/retained_mode/managers/node_manager.py +26 -26
  37. griptape_nodes/retained_mode/managers/object_manager.py +1 -1
  38. griptape_nodes/retained_mode/managers/os_manager.py +6 -6
  39. griptape_nodes/retained_mode/managers/settings.py +87 -9
  40. griptape_nodes/retained_mode/managers/static_files_manager.py +77 -9
  41. griptape_nodes/retained_mode/managers/sync_manager.py +10 -5
  42. griptape_nodes/retained_mode/managers/workflow_manager.py +101 -92
  43. griptape_nodes/retained_mode/retained_mode.py +19 -0
  44. griptape_nodes/servers/__init__.py +1 -0
  45. griptape_nodes/{mcp_server/server.py → servers/mcp.py} +1 -1
  46. griptape_nodes/{app/api.py → servers/static.py} +43 -40
  47. griptape_nodes/traits/button.py +124 -6
  48. griptape_nodes/traits/multi_options.py +188 -0
  49. griptape_nodes/traits/numbers_selector.py +77 -0
  50. griptape_nodes/traits/options.py +93 -2
  51. griptape_nodes/utils/async_utils.py +31 -0
  52. {griptape_nodes-0.53.0.dist-info → griptape_nodes-0.54.1.dist-info}/METADATA +3 -1
  53. {griptape_nodes-0.53.0.dist-info → griptape_nodes-0.54.1.dist-info}/RECORD +56 -47
  54. {griptape_nodes-0.53.0.dist-info → griptape_nodes-0.54.1.dist-info}/WHEEL +1 -1
  55. /griptape_nodes/{mcp_server → servers}/ws_request_manager.py +0 -0
  56. {griptape_nodes-0.53.0.dist-info → griptape_nodes-0.54.1.dist-info}/entry_points.txt +0 -0
@@ -294,5 +294,11 @@ class EventManager:
294
294
  app_event_type = type(app_event)
295
295
  if app_event_type in self._app_event_listeners:
296
296
  listener_set = self._app_event_listeners[app_event_type]
297
- for listener_callback in listener_set:
298
- await call_function(listener_callback, app_event)
297
+
298
+ await asyncio.gather(
299
+ *[
300
+ asyncio.create_task(call_function(listener_callback, app_event))
301
+ for listener_callback in listener_set
302
+ ],
303
+ return_exceptions=True,
304
+ )
@@ -16,6 +16,9 @@ from griptape_nodes.exe_types.core_types import (
16
16
  from griptape_nodes.exe_types.flow import ControlFlow
17
17
  from griptape_nodes.exe_types.node_types import BaseNode, ErrorProxyNode, NodeResolutionState, StartLoopNode, StartNode
18
18
  from griptape_nodes.machines.control_flow import CompleteState, ControlFlowMachine
19
+ from griptape_nodes.machines.dag_builder import DagBuilder
20
+ from griptape_nodes.machines.parallel_resolution import ParallelResolutionMachine
21
+ from griptape_nodes.machines.sequential_resolution import SequentialResolutionMachine
19
22
  from griptape_nodes.retained_mode.events.base_events import (
20
23
  ExecutionEvent,
21
24
  ExecutionGriptapeNodeEvent,
@@ -144,6 +147,7 @@ class FlowManager:
144
147
  _global_flow_queue: Queue[QueueItem]
145
148
  _global_control_flow_machine: ControlFlowMachine | None
146
149
  _global_single_node_resolution: bool
150
+ _global_dag_builder: DagBuilder
147
151
 
148
152
  def __init__(self, event_manager: EventManager) -> None:
149
153
  event_manager.assign_manager_to_request_type(CreateFlowRequest, self.on_create_flow_request)
@@ -187,11 +191,20 @@ class FlowManager:
187
191
  self._global_flow_queue = Queue[QueueItem]()
188
192
  self._global_control_flow_machine = None # Track the current control flow machine
189
193
  self._global_single_node_resolution = False
194
+ self._global_dag_builder = DagBuilder()
195
+
196
+ @property
197
+ def global_single_node_resolution(self) -> bool:
198
+ return self._global_single_node_resolution
190
199
 
191
200
  @property
192
201
  def global_flow_queue(self) -> Queue[QueueItem]:
193
202
  return self._global_flow_queue
194
203
 
204
+ @property
205
+ def global_dag_builder(self) -> DagBuilder:
206
+ return self._global_dag_builder
207
+
195
208
  def get_connections(self) -> Connections:
196
209
  """Get the connections instance."""
197
210
  return self._connections
@@ -416,10 +429,10 @@ class FlowManager:
416
429
 
417
430
  # Success
418
431
  details = f"Successfully created Flow '{final_flow_name}'."
419
- log_level = "DEBUG"
432
+ log_level = logging.DEBUG
420
433
  if (request.flow_name is not None) and (final_flow_name != request.flow_name):
421
434
  details = f"{details} WARNING: Had to rename from original Flow requested '{request.flow_name}' as an object with this name already existed."
422
- log_level = "WARNING"
435
+ log_level = logging.WARNING
423
436
 
424
437
  result = CreateFlowResultSuccess(
425
438
  flow_name=final_flow_name, result_details=ResultDetails(message=details, level=log_level)
@@ -513,6 +526,7 @@ class FlowManager:
513
526
 
514
527
  # Clean up ControlFlowMachine and DAG orchestrator for this flow
515
528
  self._global_control_flow_machine = None
529
+ self._global_dag_builder.clear()
516
530
 
517
531
  details = f"Successfully deleted Flow '{flow_name}'."
518
532
  result = DeleteFlowResultSuccess(result_details=details)
@@ -1076,14 +1090,14 @@ class FlowManager:
1076
1090
  details = f"Could not get flow state. Error: {err}"
1077
1091
  return GetFlowStateResultFailure(result_details=details)
1078
1092
  try:
1079
- control_node, resolving_node = self.flow_state(flow)
1093
+ control_nodes, resolving_nodes = self.flow_state(flow)
1080
1094
  except Exception as e:
1081
1095
  details = f"Failed to get flow state of flow with name {flow_name}. Exception occurred: {e} "
1082
1096
  logger.exception(details)
1083
1097
  return GetFlowStateResultFailure(result_details=details)
1084
1098
  details = f"Successfully got flow state for flow with name {flow_name}."
1085
1099
  return GetFlowStateResultSuccess(
1086
- control_node=control_node, resolving_node=resolving_node, result_details=details
1100
+ control_nodes=control_nodes, resolving_node=resolving_nodes, result_details=details
1087
1101
  )
1088
1102
 
1089
1103
  def on_cancel_flow_request(self, request: CancelFlowRequest) -> ResultPayload:
@@ -1588,7 +1602,8 @@ class FlowManager:
1588
1602
  start_node = queue_item.node
1589
1603
  self._global_flow_queue.task_done()
1590
1604
 
1591
- # Initialize global control flow machine if needed
1605
+ # Initialize global control flow machine and DAG builder
1606
+
1592
1607
  self._global_control_flow_machine = ControlFlowMachine(flow.name)
1593
1608
  try:
1594
1609
  await self._global_control_flow_machine.start_flow(start_node, debug_mode)
@@ -1618,6 +1633,7 @@ class FlowManager:
1618
1633
  if self._global_control_flow_machine is not None:
1619
1634
  self._global_control_flow_machine.reset_machine(cancel=True)
1620
1635
  self._global_single_node_resolution = False
1636
+ self._global_dag_builder.clear()
1621
1637
  logger.debug("Cancelling flow run")
1622
1638
 
1623
1639
  GriptapeNodes.EventManager().put_event(
@@ -1702,19 +1718,23 @@ class FlowManager:
1702
1718
  # If flow already exists, throw an error
1703
1719
  errormsg = f"This workflow is already in progress. Please wait for the current process to finish before starting {node.name} again."
1704
1720
  raise RuntimeError(errormsg)
1721
+
1705
1722
  self._global_single_node_resolution = True
1706
1723
 
1707
1724
  # Get or create machine
1708
1725
  self._global_control_flow_machine = ControlFlowMachine(flow.name)
1709
- self._global_control_flow_machine.context.current_node = node
1726
+ self._global_control_flow_machine.context.current_nodes = [node]
1710
1727
  resolution_machine = self._global_control_flow_machine.resolution_machine
1711
1728
  resolution_machine.change_debug_mode(debug_mode=debug_mode)
1712
1729
  node.state = NodeResolutionState.UNRESOLVED
1713
- # Resolve the node
1730
+ # Build the DAG for the node
1731
+ if isinstance(resolution_machine, ParallelResolutionMachine):
1732
+ self._global_dag_builder.add_node_with_dependencies(node)
1733
+ resolution_machine.context.dag_builder = self._global_dag_builder
1714
1734
  await resolution_machine.resolve_node(node)
1715
1735
  if resolution_machine.is_complete():
1716
1736
  self._global_single_node_resolution = False
1717
- self._global_control_flow_machine.context.current_node = None
1737
+ self._global_control_flow_machine.context.current_nodes = []
1718
1738
 
1719
1739
  async def single_execution_step(self, flow: ControlFlow, change_debug_mode: bool) -> None: # noqa: FBT001
1720
1740
  # do a granular step
@@ -1772,19 +1792,30 @@ class FlowManager:
1772
1792
  # Clear entry control parameter for new execution
1773
1793
  node.set_entry_control_parameter(None)
1774
1794
 
1775
- def flow_state(self, flow: ControlFlow) -> tuple[str | None, str | None]: # noqa: ARG002
1795
+ def flow_state(self, flow: ControlFlow) -> tuple[list[str] | None, list[str] | None]: # noqa: ARG002
1776
1796
  if not self.check_for_existing_running_flow():
1777
1797
  msg = "Flow hasn't started."
1778
1798
  raise RuntimeError(msg)
1779
1799
  if self._global_control_flow_machine is None:
1780
1800
  return None, None
1781
1801
  control_flow_context = self._global_control_flow_machine.context
1782
- current_control_node = (
1783
- control_flow_context.current_node.name if control_flow_context.current_node is not None else None
1802
+ current_control_nodes = (
1803
+ [control_flow_node.name for control_flow_node in control_flow_context.current_nodes]
1804
+ if control_flow_context.current_nodes is not None
1805
+ else None
1784
1806
  )
1785
- focus_stack_for_node = self._global_control_flow_machine.resolution_machine.context.focus_stack
1786
- current_resolving_node = focus_stack_for_node[-1].node.name if len(focus_stack_for_node) else None
1787
- return current_control_node, current_resolving_node
1807
+ # focus_stack is no longer available in the new architecture
1808
+ if isinstance(control_flow_context.resolution_machine, ParallelResolutionMachine):
1809
+ current_resolving_nodes = [
1810
+ node.node_reference.name
1811
+ for node in control_flow_context.resolution_machine.context.task_to_node.values()
1812
+ ]
1813
+ return current_control_nodes, current_resolving_nodes
1814
+ if isinstance(control_flow_context.resolution_machine, SequentialResolutionMachine):
1815
+ focus_stack_for_node = control_flow_context.resolution_machine.context.focus_stack
1816
+ current_resolving_node = focus_stack_for_node[-1].node.name if len(focus_stack_for_node) else None
1817
+ return current_control_nodes, [current_resolving_node] if current_resolving_node else None
1818
+ return current_control_nodes, None
1788
1819
 
1789
1820
  def get_start_node_from_node(self, flow: ControlFlow, node: BaseNode) -> BaseNode | None:
1790
1821
  # backwards chain in control outputs.
@@ -914,12 +914,12 @@ class LibraryManager:
914
914
  case LibraryStatus.GOOD:
915
915
  details = f"Successfully loaded Library '{library_data.name}' from JSON file at {json_path}"
916
916
  return RegisterLibraryFromFileResultSuccess(
917
- library_name=library_data.name, result_details=ResultDetails(message=details, level="INFO")
917
+ library_name=library_data.name, result_details=ResultDetails(message=details, level=logging.INFO)
918
918
  )
919
919
  case LibraryStatus.FLAWED:
920
920
  details = f"Successfully loaded Library JSON file from '{json_path}', but one or more nodes failed to load. Check the log for more details."
921
921
  return RegisterLibraryFromFileResultSuccess(
922
- library_name=library_data.name, result_details=ResultDetails(message=details, level="WARNING")
922
+ library_name=library_data.name, result_details=ResultDetails(message=details, level=logging.WARNING)
923
923
  )
924
924
  case LibraryStatus.UNUSABLE:
925
925
  details = f"Attempted to load Library JSON file from '{json_path}'. Failed because no nodes were loaded. Check the log for more details."
@@ -2040,4 +2040,4 @@ class LibraryManager:
2040
2040
  details = (
2041
2041
  "Successfully reloaded all libraries. All object state was cleared and previous libraries were unloaded."
2042
2042
  )
2043
- return ReloadAllLibrariesResultSuccess(result_details=ResultDetails(message=details, level="INFO"))
2043
+ return ReloadAllLibrariesResultSuccess(result_details=ResultDetails(message=details, level=logging.INFO))