griptape-nodes 0.69.0__tar.gz → 0.70.1__tar.gz

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 (282) hide show
  1. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/PKG-INFO +1 -1
  2. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/pyproject.toml +1 -1
  3. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/api_client/client.py +5 -1
  4. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/app/app.py +51 -0
  5. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_components/execution_status_component.py +10 -2
  6. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_components/log_parameter.py +5 -1
  7. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_types/parameter_number.py +22 -16
  8. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/node_events.py +5 -1
  9. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/context_manager.py +1 -27
  10. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/node_manager.py +78 -23
  11. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/object_manager.py +0 -1
  12. griptape_nodes-0.70.1/src/griptape_nodes/traits/clamp.py +79 -0
  13. griptape_nodes-0.69.0/src/griptape_nodes/traits/clamp.py +0 -36
  14. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/README.md +0 -0
  15. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/__init__.py +0 -0
  16. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/__main__.py +0 -0
  17. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/api_client/__init__.py +0 -0
  18. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/api_client/request_client.py +0 -0
  19. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/app/.python-version +0 -0
  20. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/app/__init__.py +0 -0
  21. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/app/watch.py +0 -0
  22. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/bootstrap/__init__.py +0 -0
  23. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/bootstrap/utils/__init__.py +0 -0
  24. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/bootstrap/utils/python_subprocess_executor.py +0 -0
  25. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/bootstrap/workflow_executors/__init__.py +0 -0
  26. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/bootstrap/workflow_executors/local_session_workflow_executor.py +0 -0
  27. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +0 -0
  28. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/bootstrap/workflow_executors/subprocess_workflow_executor.py +0 -0
  29. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/bootstrap/workflow_executors/utils/__init__.py +0 -0
  30. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/bootstrap/workflow_executors/utils/subprocess_script.py +0 -0
  31. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +0 -0
  32. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/bootstrap/workflow_publishers/__init__.py +0 -0
  33. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/bootstrap/workflow_publishers/local_workflow_publisher.py +0 -0
  34. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/bootstrap/workflow_publishers/subprocess_workflow_publisher.py +0 -0
  35. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/bootstrap/workflow_publishers/utils/__init__.py +0 -0
  36. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/bootstrap/workflow_publishers/utils/subprocess_script.py +0 -0
  37. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/cli/__init__.py +0 -0
  38. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/cli/commands/__init__.py +0 -0
  39. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/cli/commands/config.py +0 -0
  40. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/cli/commands/engine.py +0 -0
  41. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/cli/commands/init.py +0 -0
  42. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/cli/commands/libraries.py +0 -0
  43. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/cli/commands/models.py +0 -0
  44. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/cli/commands/self.py +0 -0
  45. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/cli/main.py +0 -0
  46. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/cli/shared.py +0 -0
  47. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/__init__.py +0 -0
  48. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/directed_graph.py +0 -0
  49. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/macro_parser/__init__.py +0 -0
  50. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/macro_parser/core.py +0 -0
  51. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/macro_parser/exceptions.py +0 -0
  52. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/macro_parser/formats.py +0 -0
  53. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/macro_parser/matching.py +0 -0
  54. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/macro_parser/parsing.py +0 -0
  55. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/macro_parser/resolution.py +0 -0
  56. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/macro_parser/segments.py +0 -0
  57. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/node_executor.py +0 -0
  58. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/project_templates/__init__.py +0 -0
  59. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/project_templates/default_project_template.py +0 -0
  60. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/project_templates/defaults/README.md +0 -0
  61. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/project_templates/directory.py +0 -0
  62. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/project_templates/loader.py +0 -0
  63. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/project_templates/project.py +0 -0
  64. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/project_templates/situation.py +0 -0
  65. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/common/project_templates/validation.py +0 -0
  66. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/drivers/__init__.py +0 -0
  67. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/drivers/storage/__init__.py +0 -0
  68. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/drivers/storage/base_storage_driver.py +0 -0
  69. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +0 -0
  70. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/drivers/storage/local_storage_driver.py +0 -0
  71. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/drivers/storage/storage_backend.py +0 -0
  72. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/drivers/thread_storage/__init__.py +0 -0
  73. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/drivers/thread_storage/base_thread_storage_driver.py +0 -0
  74. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/drivers/thread_storage/griptape_cloud_thread_storage_driver.py +0 -0
  75. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/drivers/thread_storage/local_thread_storage_driver.py +0 -0
  76. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/drivers/thread_storage/thread_storage_backend.py +0 -0
  77. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/__init__.py +0 -0
  78. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/base_iterative_nodes.py +0 -0
  79. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/connections.py +0 -0
  80. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/core_types.py +0 -0
  81. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/flow.py +0 -0
  82. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/node_groups/__init__.py +0 -0
  83. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/node_groups/base_iterative_node_group.py +0 -0
  84. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/node_groups/base_node_group.py +0 -0
  85. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/node_groups/subflow_node_group.py +0 -0
  86. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/node_types.py +0 -0
  87. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_components/README.md +0 -0
  88. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_components/__init__.py +0 -0
  89. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_components/api_key_provider_parameter.py +0 -0
  90. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_components/artifact_url/__init__.py +0 -0
  91. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_components/artifact_url/public_artifact_url_parameter.py +0 -0
  92. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_components/huggingface/__init__.py +0 -0
  93. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_model_parameter.py +0 -0
  94. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_file_parameter.py +0 -0
  95. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_parameter.py +0 -0
  96. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_variant_parameter.py +0 -0
  97. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_utils.py +0 -0
  98. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_components/progress_bar_component.py +0 -0
  99. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_components/seed_parameter.py +0 -0
  100. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_types/__init__.py +0 -0
  101. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_types/parameter_audio.py +0 -0
  102. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_types/parameter_bool.py +0 -0
  103. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_types/parameter_button.py +0 -0
  104. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_types/parameter_dict.py +0 -0
  105. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_types/parameter_float.py +0 -0
  106. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_types/parameter_image.py +0 -0
  107. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_types/parameter_int.py +0 -0
  108. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_types/parameter_json.py +0 -0
  109. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_types/parameter_range.py +0 -0
  110. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_types/parameter_string.py +0 -0
  111. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_types/parameter_three_d.py +0 -0
  112. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/param_types/parameter_video.py +0 -0
  113. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/exe_types/type_validator.py +0 -0
  114. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/machines/__init__.py +0 -0
  115. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/machines/control_flow.py +0 -0
  116. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/machines/dag_builder.py +0 -0
  117. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/machines/fsm.py +0 -0
  118. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/machines/parallel_resolution.py +0 -0
  119. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/machines/sequential_resolution.py +0 -0
  120. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/mcp_server/__init__.py +0 -0
  121. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/node_library/__init__.py +0 -0
  122. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/node_library/advanced_node_library.py +0 -0
  123. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/node_library/library_registry.py +0 -0
  124. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/node_library/workflow_registry.py +0 -0
  125. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/py.typed +0 -0
  126. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/__init__.py +0 -0
  127. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/__init__.py +0 -0
  128. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/agent_events.py +0 -0
  129. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/app_events.py +0 -0
  130. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/arbitrary_python_events.py +0 -0
  131. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/base_events.py +0 -0
  132. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/config_events.py +0 -0
  133. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/connection_events.py +0 -0
  134. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/context_events.py +0 -0
  135. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/execution_events.py +0 -0
  136. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/flow_events.py +0 -0
  137. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/generate_request_payload_schemas.py +0 -0
  138. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/library_events.py +0 -0
  139. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/logger_events.py +0 -0
  140. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/mcp_events.py +0 -0
  141. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/model_events.py +0 -0
  142. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/object_events.py +0 -0
  143. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/os_events.py +0 -0
  144. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/parameter_events.py +0 -0
  145. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/payload_registry.py +0 -0
  146. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/project_events.py +0 -0
  147. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/resource_events.py +0 -0
  148. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/secrets_events.py +0 -0
  149. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/static_file_events.py +0 -0
  150. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/sync_events.py +0 -0
  151. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/validation_events.py +0 -0
  152. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/variable_events.py +0 -0
  153. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/events/workflow_events.py +0 -0
  154. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/griptape_nodes.py +0 -0
  155. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/__init__.py +0 -0
  156. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/agent_manager.py +0 -0
  157. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +0 -0
  158. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/config_manager.py +0 -0
  159. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/engine_identity_manager.py +0 -0
  160. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/event_manager.py +0 -0
  161. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/__init__.py +0 -0
  162. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/__init__.py +0 -0
  163. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/advanced_library_load_failure_problem.py +0 -0
  164. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/after_library_callback_problem.py +0 -0
  165. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/before_library_callback_problem.py +0 -0
  166. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/create_config_category_problem.py +0 -0
  167. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/dependency_installation_failed_problem.py +0 -0
  168. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/deprecated_node_warning_problem.py +0 -0
  169. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/duplicate_library_problem.py +0 -0
  170. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/duplicate_node_registration_problem.py +0 -0
  171. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/engine_version_error_problem.py +0 -0
  172. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/incompatible_requirements_problem.py +0 -0
  173. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/insufficient_disk_space_problem.py +0 -0
  174. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/invalid_version_string_problem.py +0 -0
  175. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_json_decode_problem.py +0 -0
  176. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_load_exception_problem.py +0 -0
  177. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_not_found_problem.py +0 -0
  178. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_problem.py +0 -0
  179. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_schema_exception_problem.py +0 -0
  180. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_schema_validation_problem.py +0 -0
  181. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/modified_parameters_set_deprecation_warning_problem.py +0 -0
  182. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/modified_parameters_set_removed_problem.py +0 -0
  183. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/node_class_not_base_node_problem.py +0 -0
  184. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/node_class_not_found_problem.py +0 -0
  185. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/node_module_import_problem.py +0 -0
  186. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/old_xdg_location_warning_problem.py +0 -0
  187. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/sandbox_directory_missing_problem.py +0 -0
  188. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/ui_options_field_modified_incompatible_problem.py +0 -0
  189. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/ui_options_field_modified_warning_problem.py +0 -0
  190. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/update_config_category_problem.py +0 -0
  191. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/venv_creation_failed_problem.py +0 -0
  192. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/__init__.py +0 -0
  193. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/deprecated_node_in_workflow_problem.py +0 -0
  194. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_dependency_version_string_problem.py +0 -0
  195. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_library_version_string_problem.py +0 -0
  196. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_metadata_schema_problem.py +0 -0
  197. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_metadata_section_count_problem.py +0 -0
  198. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_toml_format_problem.py +0 -0
  199. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_not_registered_problem.py +0 -0
  200. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_version_below_required_problem.py +0 -0
  201. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_version_large_difference_problem.py +0 -0
  202. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_version_major_mismatch_problem.py +0 -0
  203. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_version_minor_difference_problem.py +0 -0
  204. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/missing_creation_date_problem.py +0 -0
  205. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/missing_last_modified_date_problem.py +0 -0
  206. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/missing_toml_section_problem.py +0 -0
  207. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/node_type_not_found_problem.py +0 -0
  208. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/workflow_not_found_problem.py +0 -0
  209. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/workflow_problem.py +0 -0
  210. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/workflow_schema_version_problem.py +0 -0
  211. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/flow_manager.py +0 -0
  212. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/library_manager.py +0 -0
  213. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/mcp_manager.py +0 -0
  214. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/model_manager.py +0 -0
  215. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/operation_manager.py +0 -0
  216. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/os_manager.py +0 -0
  217. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/project_manager.py +0 -0
  218. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/resource_components/__init__.py +0 -0
  219. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/resource_components/capability_field.py +0 -0
  220. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/resource_components/comparator.py +0 -0
  221. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/resource_components/resource_instance.py +0 -0
  222. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/resource_components/resource_type.py +0 -0
  223. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/resource_manager.py +0 -0
  224. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/resource_types/__init__.py +0 -0
  225. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/resource_types/compute_resource.py +0 -0
  226. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/resource_types/cpu_resource.py +0 -0
  227. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/resource_types/os_resource.py +0 -0
  228. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/secrets_manager.py +0 -0
  229. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/session_manager.py +0 -0
  230. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/settings.py +0 -0
  231. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/static_files_manager.py +0 -0
  232. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/sync_manager.py +0 -0
  233. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/user_manager.py +0 -0
  234. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/variable_manager.py +0 -0
  235. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/version_compatibility_manager.py +0 -0
  236. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/managers/workflow_manager.py +0 -0
  237. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/retained_mode.py +0 -0
  238. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/utils/__init__.py +0 -0
  239. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/utils/name_generator.py +0 -0
  240. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/retained_mode/variable_types.py +0 -0
  241. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/servers/__init__.py +0 -0
  242. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/servers/mcp.py +0 -0
  243. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/servers/static.py +0 -0
  244. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/traits/__init__.py +0 -0
  245. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/traits/add_param_button.py +0 -0
  246. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/traits/button.py +0 -0
  247. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/traits/color_picker.py +0 -0
  248. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/traits/compare.py +0 -0
  249. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/traits/compare_images.py +0 -0
  250. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/traits/file_system_picker.py +0 -0
  251. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/traits/minmax.py +0 -0
  252. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/traits/multi_options.py +0 -0
  253. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/traits/numbers_selector.py +0 -0
  254. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/traits/options.py +0 -0
  255. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/traits/slider.py +0 -0
  256. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/traits/trait_registry.py +0 -0
  257. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/traits/traits.json +0 -0
  258. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/updater/__init__.py +0 -0
  259. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/updater/__main__.py +0 -0
  260. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/utils/__init__.py +0 -0
  261. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/utils/async_utils.py +0 -0
  262. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/utils/dict_utils.py +0 -0
  263. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/utils/file_utils.py +0 -0
  264. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/utils/git_utils.py +0 -0
  265. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/utils/huggingface_utils.py +0 -0
  266. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/utils/image_preview.py +0 -0
  267. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/utils/library_utils.py +0 -0
  268. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/utils/metaclasses.py +0 -0
  269. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/utils/uv_utils.py +0 -0
  270. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/utils/version_utils.py +0 -0
  271. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/version_compatibility/__init__.py +0 -0
  272. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/version_compatibility/versions/__init__.py +0 -0
  273. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/version_compatibility/versions/v0_39_0/__init__.py +0 -0
  274. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/version_compatibility/versions/v0_39_0/modified_parameters_set_removal.py +0 -0
  275. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/version_compatibility/versions/v0_63_8/__init__.py +0 -0
  276. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/version_compatibility/versions/v0_63_8/deprecated_nodegroup_parameters.py +0 -0
  277. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/version_compatibility/versions/v0_65_4/__init__.py +0 -0
  278. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/version_compatibility/versions/v0_65_4/run_in_parallel_to_run_in_order.py +0 -0
  279. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/version_compatibility/versions/v0_65_5/__init__.py +0 -0
  280. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/version_compatibility/versions/v0_65_5/flux_2_removed_parameters.py +0 -0
  281. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/version_compatibility/versions/v0_7_0/__init__.py +0 -0
  282. {griptape_nodes-0.69.0 → griptape_nodes-0.70.1}/src/griptape_nodes/version_compatibility/versions/v0_7_0/local_executor_argument_addition.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: griptape-nodes
3
- Version: 0.69.0
3
+ Version: 0.70.1
4
4
  Summary: Add your description here
5
5
  Requires-Dist: griptape>=1.9.0
6
6
  Requires-Dist: pydantic>=2.10.6
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "griptape-nodes"
3
- version = "0.69.0"
3
+ version = "0.70.1"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12.0, <3.13"
@@ -168,7 +168,11 @@ class Client:
168
168
  logger.debug("WebSocket client connected")
169
169
  except TimeoutError as e:
170
170
  logger.error("Failed to connect WebSocket client: timeout")
171
- msg = "Connection timeout"
171
+ msg = (
172
+ "Connection timeout - failed to connect to Nodes API. "
173
+ "This usually indicates an invalid or missing GT_CLOUD_API_KEY. "
174
+ "Please verify your API key is correct."
175
+ )
172
176
  raise ConnectionError(msg) from e
173
177
 
174
178
  async def _disconnect(self) -> None:
@@ -3,11 +3,14 @@ from __future__ import annotations
3
3
  import asyncio
4
4
  import json
5
5
  import logging
6
+ import sys
6
7
  import threading
7
8
  from dataclasses import dataclass
8
9
 
10
+ from rich.align import Align
9
11
  from rich.console import Console
10
12
  from rich.logging import RichHandler
13
+ from rich.panel import Panel
11
14
 
12
15
  from griptape_nodes.api_client import Client
13
16
  from griptape_nodes.retained_mode.events import app_events, execution_events
@@ -113,6 +116,35 @@ logging.basicConfig(
113
116
  console = Console()
114
117
 
115
118
 
119
+ def _ensure_api_key() -> str:
120
+ """Verify that GT_CLOUD_API_KEY is set, exit with clear error message if not.
121
+
122
+ Returns:
123
+ The API key value
124
+
125
+ Raises:
126
+ SystemExit: If API key is missing or empty
127
+ """
128
+ secrets_manager = griptape_nodes.SecretsManager()
129
+ api_key = secrets_manager.get_secret("GT_CLOUD_API_KEY", should_error_on_not_found=False)
130
+
131
+ if not api_key:
132
+ message = Panel(
133
+ Align.center(
134
+ "[bold red]Nodes API key is not set, please run [code]gtn init[/code] with a valid key:[/bold red]\n"
135
+ "[code]gtn init --api-key <your key>[/code]\n\n"
136
+ "[bold red]You can generate a new key from [/bold red][bold blue][link=https://nodes.griptape.ai]https://nodes.griptape.ai[/link][/bold blue]",
137
+ ),
138
+ title="[red]X[/red] Missing Nodes API Key",
139
+ border_style="red",
140
+ padding=(1, 4),
141
+ )
142
+ console.print(message)
143
+ sys.exit(1)
144
+
145
+ return api_key
146
+
147
+
116
148
  def start_app() -> None:
117
149
  """Legacy sync entry point - runs async app."""
118
150
  try:
@@ -125,6 +157,9 @@ def start_app() -> None:
125
157
 
126
158
  async def astart_app() -> None:
127
159
  """New async app entry point."""
160
+ # Verify API key is set before starting
161
+ _ensure_api_key()
162
+
128
163
  # Initialize event queue in main thread
129
164
  griptape_nodes.EventManager().initialize_queue()
130
165
 
@@ -154,6 +189,22 @@ def _start_websocket_connection() -> None:
154
189
 
155
190
  # Run the async WebSocket tasks
156
191
  loop.run_until_complete(_run_websocket_tasks())
192
+ except ConnectionError:
193
+ # Connection failed - likely due to invalid/missing API key
194
+ message = Panel(
195
+ Align.center(
196
+ "[bold red]Failed to connect to Nodes API.[/bold red]\n\n"
197
+ "This usually indicates an invalid or missing [code]GT_CLOUD_API_KEY[/code].\n\n"
198
+ "[bold red]Please verify your API key:[/bold red]\n"
199
+ "[code]gtn init --api-key <your key>[/code]\n\n"
200
+ "[bold red]You can generate a new key from [/bold red][bold blue][link=https://nodes.griptape.ai]https://nodes.griptape.ai[/link][/bold blue]",
201
+ ),
202
+ title="[red]X[/red] Connection Failed",
203
+ border_style="red",
204
+ padding=(1, 4),
205
+ )
206
+ console.print(message)
207
+ sys.exit(1)
157
208
  except Exception as e:
158
209
  logger.error("WebSocket thread error: %s", e)
159
210
  raise
@@ -103,8 +103,16 @@ class ExecutionStatusComponent:
103
103
  """
104
104
  self._update_parameter_value(self._was_successful, was_successful)
105
105
  self._update_parameter_value(self._result_details, result_details)
106
- # Update display label: show warning emoji on failure
107
- self._update_status_group_display_label(show_warning=not was_successful)
106
+ # Update display label: show warning emoji only on actual failure (not placeholder/initialization messages)
107
+ # While string matching is not the best practice, it's currently the only way to check for placeholder messages.
108
+ # TODO: use a better approach to check for placeholder messages. https://github.com/griptape-ai/griptape-nodes/issues/3686
109
+ is_placeholder = result_details in (
110
+ "<Results will appear when the node executes>",
111
+ "Beginning execution...",
112
+ "",
113
+ )
114
+ show_warning = not was_successful and not is_placeholder
115
+ self._update_status_group_display_label(show_warning=show_warning)
108
116
 
109
117
  def clear_execution_status(self, initial_message: str | None = None) -> None:
110
118
  """Clear execution status and reset parameters.
@@ -20,7 +20,7 @@ class LogParameter:
20
20
  output_type="str",
21
21
  allowed_modes={ParameterMode.OUTPUT},
22
22
  tooltip="logs",
23
- ui_options={"multiline": True},
23
+ ui_options={"multiline": True, "placeholder_text": ""},
24
24
  )
25
25
  )
26
26
 
@@ -68,6 +68,10 @@ class StdoutCapture:
68
68
  def flush(self) -> None:
69
69
  self._original_stdout.flush()
70
70
 
71
+ def isatty(self) -> bool:
72
+ # Return False to prevent libraries from outputting ANSI color codes
73
+ return False
74
+
71
75
  def __enter__(self) -> "StdoutCapture":
72
76
  sys.stdout = self
73
77
  return self
@@ -235,12 +235,6 @@ class ParameterNumber(Parameter):
235
235
  validate_min_max: Whether to validate min/max with error
236
236
  """
237
237
  # Validation rules
238
- if min_val is not None and max_val is None:
239
- msg = f"{name}: If min_val is provided, max_val must also be provided"
240
- raise ValueError(msg)
241
- if max_val is not None and min_val is None:
242
- msg = f"{name}: If max_val is provided, min_val must also be provided"
243
- raise ValueError(msg)
244
238
  if slider and (min_val is None or max_val is None):
245
239
  msg = f"{name}: If slider is True, both min_val and max_val must be provided"
246
240
  raise ValueError(msg)
@@ -259,7 +253,7 @@ class ParameterNumber(Parameter):
259
253
  traits.add(Slider(min_val=min_val, max_val=max_val))
260
254
  elif validate_min_max and min_val is not None and max_val is not None:
261
255
  traits.add(MinMax(min_val=min_val, max_val=max_val))
262
- elif min_val is not None and max_val is not None:
256
+ elif min_val is not None or max_val is not None:
263
257
  traits.add(Clamp(min_val=min_val, max_val=max_val))
264
258
 
265
259
  # Store traits for later use
@@ -304,9 +298,6 @@ class ParameterNumber(Parameter):
304
298
  @min_val.setter
305
299
  def min_val(self, value: float | None) -> None:
306
300
  """Set minimum value and update constraint traits."""
307
- if value is not None and self.max_val is None:
308
- msg = f"{self.name}: Cannot set min_val without max_val"
309
- raise ValueError(msg)
310
301
  self._min_val = value
311
302
  self._update_constraint_traits()
312
303
 
@@ -321,9 +312,6 @@ class ParameterNumber(Parameter):
321
312
  @max_val.setter
322
313
  def max_val(self, value: float | None) -> None:
323
314
  """Set maximum value and update constraint traits."""
324
- if value is not None and self.min_val is None:
325
- msg = f"{self.name}: Cannot set max_val without min_val"
326
- raise ValueError(msg)
327
315
  self._max_val = value
328
316
  self._update_constraint_traits()
329
317
 
@@ -367,17 +355,35 @@ class ParameterNumber(Parameter):
367
355
  min_val = getattr(self, "_min_val", None)
368
356
  max_val = getattr(self, "_max_val", None)
369
357
 
370
- if min_val is None or max_val is None:
358
+ if min_val is None and max_val is None:
371
359
  self._remove_constraint_traits()
372
360
  return
373
361
 
362
+ if self.slider and (min_val is None or max_val is None):
363
+ msg = f"{self.name}: Cannot enable slider without min_val and max_val"
364
+ raise ValueError(msg)
365
+
366
+ if self.validate_min_max and (min_val is None or max_val is None):
367
+ msg = f"{self.name}: Cannot enable validate_min_max without min_val and max_val"
368
+ raise ValueError(msg)
369
+
374
370
  # Determine which trait to use based on current state
375
371
  if self.slider:
372
+ # Checked above: both min_val and max_val are available when slider is enabled.
373
+ # Type narrowing: assign to variables after None check so type checker understands
374
+ min_val_float: float = min_val # type: ignore[assignment]
375
+ max_val_float: float = max_val # type: ignore[assignment]
376
+ # Python will naturally coerce int to float if needed (no precision loss for reasonable values)
376
377
  self._remove_constraint_traits()
377
- self.add_trait(Slider(min_val=min_val, max_val=max_val))
378
+ self.add_trait(Slider(min_val=min_val_float, max_val=max_val_float))
378
379
  elif self.validate_min_max:
380
+ # Checked above: both min_val and max_val are available when validate_min_max is enabled.
381
+ # Type narrowing: assign to variables after None check so type checker understands
382
+ min_val_float: float = min_val # type: ignore[assignment]
383
+ max_val_float: float = max_val # type: ignore[assignment]
384
+ # Python will naturally coerce int to float if needed (no precision loss for reasonable values)
379
385
  self._remove_constraint_traits()
380
- self.add_trait(MinMax(min_val=min_val, max_val=max_val))
386
+ self.add_trait(MinMax(min_val=min_val_float, max_val=max_val_float))
381
387
  else:
382
388
  self._remove_constraint_traits()
383
389
  self.add_trait(Clamp(min_val=min_val, max_val=max_val))
@@ -564,6 +564,7 @@ class SerializeNodeToCommandsRequest(RequestPayload):
564
564
  serialized_parameter_value_tracker: SerializedParameterValueTracker = field(
565
565
  default_factory=SerializedParameterValueTracker
566
566
  )
567
+ use_pickling: bool = False
567
568
 
568
569
 
569
570
  @dataclass
@@ -648,7 +649,8 @@ class SerializeSelectedNodesToCommandsResultSuccess(WorkflowNotAlteredMixin, Res
648
649
 
649
650
  # They will be passed with node_name, timestamp
650
651
  # Could be a flow command if it's all nodes in a flow.
651
- serialized_selected_node_commands: SerializedSelectedNodesCommands
652
+ serialized_selected_node_commands: str
653
+ pickled_values: dict[str, str]
652
654
 
653
655
 
654
656
  @dataclass
@@ -674,6 +676,8 @@ class DeserializeSelectedNodesFromCommandsRequest(WorkflowNotAlteredMixin, Reque
674
676
  Results: DeserializeSelectedNodesFromCommandsResultSuccess (with node names) | DeserializeSelectedNodesFromCommandsResultFailure (deserialization error)
675
677
  """
676
678
 
679
+ deserialize_commands: str
680
+ pickled_values: dict[str, str]
677
681
  positions: list[NewPosition] | None = None
678
682
 
679
683
 
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- from typing import TYPE_CHECKING, Any
4
+ from typing import TYPE_CHECKING
5
5
 
6
6
  from griptape_nodes.exe_types.flow import ControlFlow
7
7
  from griptape_nodes.retained_mode.events.context_events import (
@@ -18,8 +18,6 @@ if TYPE_CHECKING:
18
18
  from griptape_nodes.exe_types.core_types import BaseNodeElement
19
19
  from griptape_nodes.exe_types.node_types import BaseNode
20
20
  from griptape_nodes.retained_mode.events.base_events import ResultPayload
21
- from griptape_nodes.retained_mode.events.flow_events import SerializedFlowCommands
22
- from griptape_nodes.retained_mode.events.node_events import SerializedSelectedNodesCommands
23
21
  from griptape_nodes.retained_mode.managers.event_manager import EventManager
24
22
 
25
23
  logger = logging.getLogger("griptape_nodes")
@@ -34,7 +32,6 @@ class ContextManager:
34
32
  """
35
33
 
36
34
  _workflow_stack: list[ContextManager.WorkflowContextState]
37
- _clipboard: ClipBoard
38
35
 
39
36
  class WorkflowContextError(Exception):
40
37
  """Base exception for workflow context errors."""
@@ -242,32 +239,9 @@ class ContextManager:
242
239
  ) -> None:
243
240
  self._manager.pop_element()
244
241
 
245
- class ClipBoard:
246
- """Keeps Commands for Copying or Pasting."""
247
-
248
- # Contains flow, node, parameter, connections
249
- flow_commands: SerializedFlowCommands | None
250
- # Contains node and Parameter and relevant connections
251
- node_commands: SerializedSelectedNodesCommands | None
252
- parameter_uuid_to_values: dict[str, Any] | None
253
-
254
- def __init__(self) -> None:
255
- self.flow_commands = None
256
- self.node_commands = None
257
- self.parameter_uuid_to_values = None
258
-
259
- def clear(self) -> None:
260
- del self.flow_commands
261
- self.flow_commands = None
262
- del self.node_commands
263
- self.node_commands = None
264
- if self.parameter_uuid_to_values is not None:
265
- self.parameter_uuid_to_values.clear()
266
-
267
242
  def __init__(self, event_manager: EventManager) -> None:
268
243
  """Initialize the context manager with empty workflow and flow stacks."""
269
244
  self._workflow_stack = []
270
- self._clipboard = self.ClipBoard()
271
245
  event_manager.assign_manager_to_request_type(
272
246
  request_type=SetWorkflowContextRequest, callback=self.on_set_workflow_context_request
273
247
  )
@@ -2781,6 +2781,7 @@ class NodeManager:
2781
2781
  unique_parameter_uuid_to_values=request.unique_parameter_uuid_to_values,
2782
2782
  serialized_parameter_value_tracker=request.serialized_parameter_value_tracker,
2783
2783
  create_node_request=create_node_request,
2784
+ use_pickling=request.use_pickling,
2784
2785
  )
2785
2786
  if set_param_value_requests is not None:
2786
2787
  set_value_commands.extend(set_param_value_requests)
@@ -2983,6 +2984,7 @@ class NodeManager:
2983
2984
  node_name=node_name,
2984
2985
  unique_parameter_uuid_to_values=unique_uuid_to_values,
2985
2986
  serialized_parameter_value_tracker=serialized_parameter_value_tracker,
2987
+ use_pickling=True,
2986
2988
  )
2987
2989
  )
2988
2990
  if not isinstance(result, SerializeNodeToCommandsResultSuccess):
@@ -3029,22 +3031,55 @@ class NodeManager:
3029
3031
  set_parameter_value_commands=parameter_commands,
3030
3032
  set_lock_commands_per_node=lock_commands,
3031
3033
  )
3032
- # Set everything in the clipboard if requested
3033
- if request.copy_to_clipboard:
3034
- GriptapeNodes.ContextManager()._clipboard.node_commands = final_result
3035
- GriptapeNodes.ContextManager()._clipboard.parameter_uuid_to_values = unique_uuid_to_values
3034
+
3035
+ # Encode pickled bytes to latin-1 strings for JSON serialization
3036
+ encoded_values = {}
3037
+ for uuid, value in unique_uuid_to_values.items():
3038
+ if isinstance(value, bytes):
3039
+ # Pickled bytes - encode as latin-1 string for transport
3040
+ encoded_values[uuid] = value.decode("latin1")
3041
+ else:
3042
+ # Non-pickled value - keep as-is (for backward compatibility)
3043
+ encoded_values[uuid] = value
3044
+
3045
+ # Pickle the commands object and encode as latin-1 string for transport
3046
+ pickled_commands_bytes = pickle.dumps(final_result)
3047
+ pickled_commands_string = pickled_commands_bytes.decode("latin1")
3036
3048
  return SerializeSelectedNodesToCommandsResultSuccess(
3037
- final_result,
3049
+ pickled_commands_string, # Send pickled string instead of object
3050
+ pickled_values=encoded_values,
3038
3051
  result_details=f"Successfully serialized {len(request.nodes_to_serialize)} selected nodes to commands.",
3039
3052
  )
3040
3053
 
3041
- def on_deserialize_selected_nodes_from_commands( # noqa: C901, PLR0912
3054
+ def on_deserialize_selected_nodes_from_commands( # noqa: C901, PLR0912, PLR0915
3042
3055
  self,
3043
3056
  request: DeserializeSelectedNodesFromCommandsRequest,
3044
3057
  ) -> ResultPayload:
3045
- commands = GriptapeNodes.ContextManager()._clipboard.node_commands
3046
- if commands is None:
3047
- return DeserializeSelectedNodesFromCommandsResultFailure(result_details="No Node Commands Found")
3058
+ # Decode latin-1 encoded pickled strings back to Python objects
3059
+ decoded_values = {}
3060
+ if request.pickled_values:
3061
+ for uuid, latin1_string in request.pickled_values.items():
3062
+ if isinstance(latin1_string, str):
3063
+ try:
3064
+ # Decode: latin-1 string → bytes → unpickled object
3065
+ pickled_bytes = latin1_string.encode("latin1")
3066
+ decoded_values[uuid] = pickle.loads(pickled_bytes) # noqa: S301 Expecting this from the GUI.
3067
+ except Exception:
3068
+ details = f"Failed to unpickle parameter value for UUID {uuid}"
3069
+ logger.warning(details)
3070
+ # Keep original value if unpickling fails
3071
+ decoded_values[uuid] = latin1_string
3072
+ else:
3073
+ # Not a string, keep as-is
3074
+ decoded_values[uuid] = latin1_string
3075
+
3076
+ # Unpickle the commands string into SerializedSelectedNodesCommands
3077
+ try:
3078
+ pickled_commands_bytes = request.deserialize_commands.encode("latin1")
3079
+ commands = pickle.loads(pickled_commands_bytes) # noqa: S301 Expecting this from the GUI.
3080
+ except Exception as e:
3081
+ details = f"Failed to unpickle commands: {e}"
3082
+ return DeserializeSelectedNodesFromCommandsResultFailure(result_details=details)
3048
3083
  connections = commands.serialized_connection_commands
3049
3084
  node_uuid_to_name = {}
3050
3085
  # Enumerate because positions is in the same order as the node commands.
@@ -3078,10 +3113,9 @@ class NodeManager:
3078
3113
  param_request = parameter_command.set_parameter_value_command
3079
3114
  # Set the Node name
3080
3115
  param_request.node_name = result.node_name
3081
- # Set the new value
3082
- table = GriptapeNodes.ContextManager()._clipboard.parameter_uuid_to_values
3083
- if table and parameter_command.unique_value_uuid in table:
3084
- value = table[parameter_command.unique_value_uuid]
3116
+ # Set the new value from decoded_values
3117
+ if decoded_values and parameter_command.unique_value_uuid in decoded_values:
3118
+ value = decoded_values[parameter_command.unique_value_uuid]
3085
3119
  # Using try-except-pass instead of contextlib.suppress because it's clearer.
3086
3120
  try: # noqa: SIM105
3087
3121
  # If we're pasting multiple times - we need to create a new copy for each paste so they don't all have the same reference.
@@ -3119,13 +3153,20 @@ class NodeManager:
3119
3153
  )
3120
3154
 
3121
3155
  def on_duplicate_selected_nodes(self, request: DuplicateSelectedNodesRequest) -> ResultPayload:
3122
- result = GriptapeNodes.handle_request(
3156
+ serialize_result = GriptapeNodes.handle_request(
3123
3157
  SerializeSelectedNodesToCommandsRequest(nodes_to_serialize=request.nodes_to_duplicate)
3124
3158
  )
3125
- if result.failed():
3159
+ if not isinstance(serialize_result, SerializeSelectedNodesToCommandsResultSuccess):
3126
3160
  details = "Failed to serialized selected nodes."
3127
3161
  return DuplicateSelectedNodesResultFailure(result_details=details)
3128
- result = GriptapeNodes.handle_request(DeserializeSelectedNodesFromCommandsRequest(positions=request.positions))
3162
+
3163
+ # Pass the pickled commands and values to deserialization
3164
+ deserialize_request = DeserializeSelectedNodesFromCommandsRequest(
3165
+ deserialize_commands=serialize_result.serialized_selected_node_commands,
3166
+ pickled_values=serialize_result.pickled_values,
3167
+ positions=request.positions,
3168
+ )
3169
+ result = GriptapeNodes.handle_request(deserialize_request)
3129
3170
  if not isinstance(result, DeserializeSelectedNodesFromCommandsResultSuccess):
3130
3171
  details = "Failed to deserialize selected nodes."
3131
3172
  return DuplicateSelectedNodesResultFailure(result_details=details)
@@ -3177,6 +3218,7 @@ class NodeManager:
3177
3218
  node_name: str,
3178
3219
  *,
3179
3220
  is_output: bool,
3221
+ use_pickling: bool = False,
3180
3222
  ) -> SerializedNodeCommands.IndirectSetParameterValueCommand | None:
3181
3223
  try:
3182
3224
  hash(value)
@@ -3211,12 +3253,20 @@ class NodeManager:
3211
3253
  return None
3212
3254
  # The value should be serialized. Add it to the map of uniques.
3213
3255
  unique_uuid = SerializedNodeCommands.UniqueParameterValueUUID(str(uuid4()))
3214
- try:
3215
- unique_parameter_uuid_to_values[unique_uuid] = copy.deepcopy(value)
3216
- except Exception:
3217
- details = f"Attempted to serialize parameter '{parameter_name}` on node '{node_name}'. The parameter value could not be copied. It will be serialized by value. If problems arise from this, ensure the type '{type(value)}' works with copy.deepcopy()."
3218
- logger.warning(details)
3219
- unique_parameter_uuid_to_values[unique_uuid] = value
3256
+
3257
+ if use_pickling:
3258
+ # Use pickle serialization via WorkflowManager
3259
+ workflow_manager = GriptapeNodes.WorkflowManager()
3260
+ pickled_bytes = workflow_manager._patch_and_pickle_object(value)
3261
+ unique_parameter_uuid_to_values[unique_uuid] = pickled_bytes
3262
+ else:
3263
+ # Use existing deep copy approach
3264
+ try:
3265
+ unique_parameter_uuid_to_values[unique_uuid] = copy.deepcopy(value)
3266
+ except Exception:
3267
+ details = f"Attempted to serialize parameter '{parameter_name}` on node '{node_name}'. The parameter value could not be copied. It will be serialized by value. If problems arise from this, ensure the type '{type(value)}' works with copy.deepcopy()."
3268
+ logger.warning(details)
3269
+ unique_parameter_uuid_to_values[unique_uuid] = value
3220
3270
  serialized_parameter_value_tracker.add_as_serializable(value_id, unique_uuid)
3221
3271
 
3222
3272
  # Serialize it
@@ -3233,12 +3283,14 @@ class NodeManager:
3233
3283
  return indirect_set_value_command
3234
3284
 
3235
3285
  @staticmethod
3236
- def handle_parameter_value_saving(
3286
+ def handle_parameter_value_saving( # noqa: PLR0913
3237
3287
  parameter: Parameter,
3238
3288
  node: BaseNode,
3239
3289
  unique_parameter_uuid_to_values: dict[SerializedNodeCommands.UniqueParameterValueUUID, Any],
3240
3290
  serialized_parameter_value_tracker: SerializedParameterValueTracker,
3241
3291
  create_node_request: CreateNodeRequest,
3292
+ *,
3293
+ use_pickling: bool = False,
3242
3294
  ) -> list[SerializedNodeCommands.IndirectSetParameterValueCommand] | None:
3243
3295
  """Generates code to save a parameter value for a node in a Griptape workflow.
3244
3296
 
@@ -3256,6 +3308,7 @@ class NodeManager:
3256
3308
  unique_parameter_uuid_to_values (dict[SerializedNodeCommands.UniqueParameterValueUUID, Any]): Dictionary mapping unique value UUIDs to values
3257
3309
  serialized_parameter_value_tracker (SerializedParameterValueTracker): Object mapping maintaining value hashes to unique value UUIDs, and non-serializable values
3258
3310
  create_node_request (CreateNodeRequest): The node creation request that will be modified if serialization fails
3311
+ use_pickling (bool): If True, use pickle-based serialization; if False, use deep copy
3259
3312
 
3260
3313
  Returns:
3261
3314
  None (if no value to be serialized) or an IndirectSetParameterValueCommand linking the value to the unique value map
@@ -3292,6 +3345,7 @@ class NodeManager:
3292
3345
  is_output=False,
3293
3346
  parameter_name=parameter.name,
3294
3347
  node_name=node.name,
3348
+ use_pickling=use_pickling,
3295
3349
  )
3296
3350
  if internal_command is None:
3297
3351
  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."
@@ -3310,6 +3364,7 @@ class NodeManager:
3310
3364
  is_output=True,
3311
3365
  parameter_name=parameter.name,
3312
3366
  node_name=node.name,
3367
+ use_pickling=use_pickling,
3313
3368
  )
3314
3369
  if output_command is None:
3315
3370
  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."
@@ -149,7 +149,6 @@ class ObjectManager:
149
149
  context_mgr.pop_node()
150
150
  context_mgr.pop_flow()
151
151
  context_mgr.pop_workflow()
152
- context_mgr._clipboard.clear()
153
152
 
154
153
  # Clear all local workflow variables
155
154
  GriptapeNodes.VariablesManager().on_clear_object_state()
@@ -0,0 +1,79 @@
1
+ from collections.abc import Callable
2
+ from dataclasses import dataclass, field
3
+ from typing import Any
4
+
5
+ from griptape_nodes.exe_types.core_types import Trait
6
+
7
+
8
+ @dataclass(eq=False)
9
+ class Clamp(Trait):
10
+ min: Any = 0
11
+ max: Any = 10
12
+ element_id: str = field(default_factory=lambda: "ClampTrait")
13
+
14
+ def __init__(self, min_val: float | None = None, max_val: float | None = None) -> None:
15
+ super().__init__()
16
+ self.min = min_val
17
+ self.max = max_val
18
+
19
+ @classmethod
20
+ def get_trait_keys(cls) -> list[str]:
21
+ return ["clamp"]
22
+
23
+ def _clamp_number(self, value: float) -> float:
24
+ # Keep this as a tiny helper so the converter stays readable and so we can
25
+ # consistently apply one-sided bounds (min-only or max-only) everywhere.
26
+ if self.max is not None and value > self.max:
27
+ value = self.max
28
+ if self.min is not None and value < self.min:
29
+ value = self.min
30
+ return value
31
+
32
+ def _clamp_sequence(self, value: list[Any]) -> list[Any]:
33
+ # Clamp historically applied to strings/lists by max length; we keep list handling
34
+ # explicit here so bounds semantics remain obvious.
35
+ if self.max is None:
36
+ return value
37
+ if len(value) <= self.max:
38
+ return value
39
+ return value[: self.max]
40
+
41
+ def _try_parse_numeric_string(self, value: str) -> float | None:
42
+ # Trait converters run BEFORE parameter-level converters (e.g. ParameterInt/Float
43
+ # parsing). That means UI inputs often arrive as strings here; to make min/max
44
+ # clamping actually work with typical UI inputs, we parse numeric strings when
45
+ # bounds are configured.
46
+ if self.min is None and self.max is None:
47
+ return None
48
+
49
+ stripped = value.strip()
50
+ if not stripped:
51
+ return None
52
+
53
+ try:
54
+ return float(stripped)
55
+ except ValueError:
56
+ return None
57
+
58
+ def converters_for_trait(self) -> list[Callable]:
59
+ def clamp(value: Any) -> Any:
60
+ if isinstance(value, list):
61
+ return self._clamp_sequence(value)
62
+
63
+ if isinstance(value, str):
64
+ parsed = self._try_parse_numeric_string(value)
65
+ if parsed is not None:
66
+ return self._clamp_number(parsed)
67
+
68
+ # If it's not a numeric string (or no bounds configured), preserve the
69
+ # historical string behavior: max length clamping only.
70
+ if self.max is None or len(value) <= self.max:
71
+ return value
72
+ return value[: self.max]
73
+
74
+ if isinstance(value, (int, float)):
75
+ return self._clamp_number(float(value))
76
+
77
+ return value
78
+
79
+ return [clamp]
@@ -1,36 +0,0 @@
1
- from collections.abc import Callable
2
- from dataclasses import dataclass, field
3
- from typing import Any
4
-
5
- from griptape_nodes.exe_types.core_types import Trait
6
-
7
-
8
- @dataclass(eq=False)
9
- class Clamp(Trait):
10
- min: Any = 0
11
- max: Any = 10
12
- element_id: str = field(default_factory=lambda: "ClampTrait")
13
-
14
- def __init__(self, min_val: float, max_val: float) -> None:
15
- super().__init__()
16
- self.min = min_val
17
- self.max = max_val
18
-
19
- @classmethod
20
- def get_trait_keys(cls) -> list[str]:
21
- return ["clamp"]
22
-
23
- def converters_for_trait(self) -> list[Callable]:
24
- def clamp(value: Any) -> Any:
25
- if isinstance(value, (str, list)):
26
- if len(value) > self.max:
27
- return value[: self.max]
28
- return value
29
- if isinstance(value, (int, float)):
30
- if value > self.max:
31
- return self.max
32
- if value < self.min:
33
- return self.min
34
- return value
35
-
36
- return [clamp]