griptape-nodes 0.67.0__tar.gz → 0.68.0__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.67.0 → griptape_nodes-0.68.0}/PKG-INFO +1 -1
  2. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/pyproject.toml +1 -1
  3. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/bootstrap/utils/python_subprocess_executor.py +17 -4
  4. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/node_executor.py +218 -18
  5. griptape_nodes-0.68.0/src/griptape_nodes/exe_types/node_groups/__init__.py +7 -0
  6. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/node_groups/base_iterative_node_group.py +54 -2
  7. griptape_nodes-0.68.0/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_variant_parameter.py +152 -0
  8. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/flow_manager.py +2 -1
  9. griptape_nodes-0.67.0/src/griptape_nodes/exe_types/node_groups/__init__.py +0 -7
  10. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/README.md +0 -0
  11. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/__init__.py +0 -0
  12. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/__main__.py +0 -0
  13. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/api_client/__init__.py +0 -0
  14. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/api_client/client.py +0 -0
  15. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/api_client/request_client.py +0 -0
  16. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/app/.python-version +0 -0
  17. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/app/__init__.py +0 -0
  18. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/app/app.py +0 -0
  19. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/app/watch.py +0 -0
  20. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/bootstrap/__init__.py +0 -0
  21. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/bootstrap/utils/__init__.py +0 -0
  22. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/bootstrap/workflow_executors/__init__.py +0 -0
  23. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/bootstrap/workflow_executors/local_session_workflow_executor.py +0 -0
  24. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +0 -0
  25. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/bootstrap/workflow_executors/subprocess_workflow_executor.py +0 -0
  26. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/bootstrap/workflow_executors/utils/__init__.py +0 -0
  27. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/bootstrap/workflow_executors/utils/subprocess_script.py +0 -0
  28. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +0 -0
  29. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/bootstrap/workflow_publishers/__init__.py +0 -0
  30. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/bootstrap/workflow_publishers/local_workflow_publisher.py +0 -0
  31. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/bootstrap/workflow_publishers/subprocess_workflow_publisher.py +0 -0
  32. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/bootstrap/workflow_publishers/utils/__init__.py +0 -0
  33. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/bootstrap/workflow_publishers/utils/subprocess_script.py +0 -0
  34. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/cli/__init__.py +0 -0
  35. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/cli/commands/__init__.py +0 -0
  36. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/cli/commands/config.py +0 -0
  37. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/cli/commands/engine.py +0 -0
  38. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/cli/commands/init.py +0 -0
  39. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/cli/commands/libraries.py +0 -0
  40. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/cli/commands/models.py +0 -0
  41. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/cli/commands/self.py +0 -0
  42. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/cli/main.py +0 -0
  43. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/cli/shared.py +0 -0
  44. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/__init__.py +0 -0
  45. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/directed_graph.py +0 -0
  46. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/macro_parser/__init__.py +0 -0
  47. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/macro_parser/core.py +0 -0
  48. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/macro_parser/exceptions.py +0 -0
  49. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/macro_parser/formats.py +0 -0
  50. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/macro_parser/matching.py +0 -0
  51. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/macro_parser/parsing.py +0 -0
  52. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/macro_parser/resolution.py +0 -0
  53. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/macro_parser/segments.py +0 -0
  54. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/project_templates/__init__.py +0 -0
  55. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/project_templates/default_project_template.py +0 -0
  56. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/project_templates/defaults/README.md +0 -0
  57. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/project_templates/directory.py +0 -0
  58. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/project_templates/loader.py +0 -0
  59. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/project_templates/project.py +0 -0
  60. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/project_templates/situation.py +0 -0
  61. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/common/project_templates/validation.py +0 -0
  62. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/drivers/__init__.py +0 -0
  63. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/drivers/storage/__init__.py +0 -0
  64. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/drivers/storage/base_storage_driver.py +0 -0
  65. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +0 -0
  66. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/drivers/storage/local_storage_driver.py +0 -0
  67. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/drivers/storage/storage_backend.py +0 -0
  68. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/drivers/thread_storage/__init__.py +0 -0
  69. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/drivers/thread_storage/base_thread_storage_driver.py +0 -0
  70. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/drivers/thread_storage/griptape_cloud_thread_storage_driver.py +0 -0
  71. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/drivers/thread_storage/local_thread_storage_driver.py +0 -0
  72. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/drivers/thread_storage/thread_storage_backend.py +0 -0
  73. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/__init__.py +0 -0
  74. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/base_iterative_nodes.py +0 -0
  75. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/connections.py +0 -0
  76. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/core_types.py +0 -0
  77. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/flow.py +0 -0
  78. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/node_groups/base_node_group.py +0 -0
  79. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/node_groups/subflow_node_group.py +0 -0
  80. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/node_types.py +0 -0
  81. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_components/README.md +0 -0
  82. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_components/__init__.py +0 -0
  83. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_components/api_key_provider_parameter.py +0 -0
  84. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_components/artifact_url/__init__.py +0 -0
  85. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_components/artifact_url/public_artifact_url_parameter.py +0 -0
  86. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_components/execution_status_component.py +0 -0
  87. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_components/huggingface/__init__.py +0 -0
  88. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_model_parameter.py +0 -0
  89. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_file_parameter.py +0 -0
  90. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_parameter.py +0 -0
  91. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_utils.py +0 -0
  92. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_components/log_parameter.py +0 -0
  93. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_components/progress_bar_component.py +0 -0
  94. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_components/seed_parameter.py +0 -0
  95. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_types/__init__.py +0 -0
  96. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_types/parameter_audio.py +0 -0
  97. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_types/parameter_bool.py +0 -0
  98. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_types/parameter_button.py +0 -0
  99. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_types/parameter_dict.py +0 -0
  100. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_types/parameter_float.py +0 -0
  101. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_types/parameter_image.py +0 -0
  102. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_types/parameter_int.py +0 -0
  103. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_types/parameter_json.py +0 -0
  104. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_types/parameter_number.py +0 -0
  105. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_types/parameter_range.py +0 -0
  106. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_types/parameter_string.py +0 -0
  107. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_types/parameter_three_d.py +0 -0
  108. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/param_types/parameter_video.py +0 -0
  109. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/exe_types/type_validator.py +0 -0
  110. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/machines/__init__.py +0 -0
  111. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/machines/control_flow.py +0 -0
  112. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/machines/dag_builder.py +0 -0
  113. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/machines/fsm.py +0 -0
  114. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/machines/parallel_resolution.py +0 -0
  115. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/machines/sequential_resolution.py +0 -0
  116. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/mcp_server/__init__.py +0 -0
  117. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/node_library/__init__.py +0 -0
  118. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/node_library/advanced_node_library.py +0 -0
  119. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/node_library/library_registry.py +0 -0
  120. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/node_library/workflow_registry.py +0 -0
  121. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/py.typed +0 -0
  122. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/__init__.py +0 -0
  123. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/__init__.py +0 -0
  124. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/agent_events.py +0 -0
  125. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/app_events.py +0 -0
  126. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/arbitrary_python_events.py +0 -0
  127. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/base_events.py +0 -0
  128. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/config_events.py +0 -0
  129. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/connection_events.py +0 -0
  130. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/context_events.py +0 -0
  131. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/execution_events.py +0 -0
  132. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/flow_events.py +0 -0
  133. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/generate_request_payload_schemas.py +0 -0
  134. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/library_events.py +0 -0
  135. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/logger_events.py +0 -0
  136. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/mcp_events.py +0 -0
  137. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/model_events.py +0 -0
  138. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/node_events.py +0 -0
  139. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/object_events.py +0 -0
  140. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/os_events.py +0 -0
  141. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/parameter_events.py +0 -0
  142. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/payload_registry.py +0 -0
  143. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/project_events.py +0 -0
  144. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/resource_events.py +0 -0
  145. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/secrets_events.py +0 -0
  146. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/static_file_events.py +0 -0
  147. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/sync_events.py +0 -0
  148. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/validation_events.py +0 -0
  149. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/variable_events.py +0 -0
  150. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/events/workflow_events.py +0 -0
  151. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/griptape_nodes.py +0 -0
  152. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/__init__.py +0 -0
  153. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/agent_manager.py +0 -0
  154. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +0 -0
  155. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/config_manager.py +0 -0
  156. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/context_manager.py +0 -0
  157. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/engine_identity_manager.py +0 -0
  158. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/event_manager.py +0 -0
  159. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/__init__.py +0 -0
  160. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/__init__.py +0 -0
  161. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/advanced_library_load_failure_problem.py +0 -0
  162. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/after_library_callback_problem.py +0 -0
  163. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/before_library_callback_problem.py +0 -0
  164. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/create_config_category_problem.py +0 -0
  165. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/dependency_installation_failed_problem.py +0 -0
  166. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/deprecated_node_warning_problem.py +0 -0
  167. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/duplicate_library_problem.py +0 -0
  168. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/duplicate_node_registration_problem.py +0 -0
  169. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/engine_version_error_problem.py +0 -0
  170. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/incompatible_requirements_problem.py +0 -0
  171. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/insufficient_disk_space_problem.py +0 -0
  172. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/invalid_version_string_problem.py +0 -0
  173. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_json_decode_problem.py +0 -0
  174. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_load_exception_problem.py +0 -0
  175. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_not_found_problem.py +0 -0
  176. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_problem.py +0 -0
  177. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_schema_exception_problem.py +0 -0
  178. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_schema_validation_problem.py +0 -0
  179. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/modified_parameters_set_deprecation_warning_problem.py +0 -0
  180. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/modified_parameters_set_removed_problem.py +0 -0
  181. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/node_class_not_base_node_problem.py +0 -0
  182. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/node_class_not_found_problem.py +0 -0
  183. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/node_module_import_problem.py +0 -0
  184. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/old_xdg_location_warning_problem.py +0 -0
  185. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/sandbox_directory_missing_problem.py +0 -0
  186. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/ui_options_field_modified_incompatible_problem.py +0 -0
  187. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/ui_options_field_modified_warning_problem.py +0 -0
  188. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/update_config_category_problem.py +0 -0
  189. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/venv_creation_failed_problem.py +0 -0
  190. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/__init__.py +0 -0
  191. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/deprecated_node_in_workflow_problem.py +0 -0
  192. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_dependency_version_string_problem.py +0 -0
  193. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_library_version_string_problem.py +0 -0
  194. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_metadata_schema_problem.py +0 -0
  195. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_metadata_section_count_problem.py +0 -0
  196. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_toml_format_problem.py +0 -0
  197. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_not_registered_problem.py +0 -0
  198. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_version_below_required_problem.py +0 -0
  199. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_version_large_difference_problem.py +0 -0
  200. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_version_major_mismatch_problem.py +0 -0
  201. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_version_minor_difference_problem.py +0 -0
  202. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/missing_creation_date_problem.py +0 -0
  203. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/missing_last_modified_date_problem.py +0 -0
  204. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/missing_toml_section_problem.py +0 -0
  205. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/node_type_not_found_problem.py +0 -0
  206. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/workflow_not_found_problem.py +0 -0
  207. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/workflow_problem.py +0 -0
  208. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/workflow_schema_version_problem.py +0 -0
  209. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/library_manager.py +0 -0
  210. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/mcp_manager.py +0 -0
  211. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/model_manager.py +0 -0
  212. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/node_manager.py +0 -0
  213. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/object_manager.py +0 -0
  214. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/operation_manager.py +0 -0
  215. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/os_manager.py +0 -0
  216. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/project_manager.py +0 -0
  217. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/resource_components/__init__.py +0 -0
  218. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/resource_components/capability_field.py +0 -0
  219. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/resource_components/comparator.py +0 -0
  220. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/resource_components/resource_instance.py +0 -0
  221. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/resource_components/resource_type.py +0 -0
  222. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/resource_manager.py +0 -0
  223. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/resource_types/__init__.py +0 -0
  224. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/resource_types/compute_resource.py +0 -0
  225. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/resource_types/cpu_resource.py +0 -0
  226. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/resource_types/os_resource.py +0 -0
  227. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/secrets_manager.py +0 -0
  228. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/session_manager.py +0 -0
  229. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/settings.py +0 -0
  230. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/static_files_manager.py +0 -0
  231. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/sync_manager.py +0 -0
  232. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/user_manager.py +0 -0
  233. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/variable_manager.py +0 -0
  234. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/version_compatibility_manager.py +0 -0
  235. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/managers/workflow_manager.py +0 -0
  236. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/retained_mode.py +0 -0
  237. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/utils/__init__.py +0 -0
  238. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/utils/name_generator.py +0 -0
  239. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/retained_mode/variable_types.py +0 -0
  240. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/servers/__init__.py +0 -0
  241. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/servers/mcp.py +0 -0
  242. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/servers/static.py +0 -0
  243. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/traits/__init__.py +0 -0
  244. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/traits/add_param_button.py +0 -0
  245. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/traits/button.py +0 -0
  246. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/traits/clamp.py +0 -0
  247. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/traits/color_picker.py +0 -0
  248. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/traits/compare.py +0 -0
  249. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/traits/compare_images.py +0 -0
  250. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/traits/file_system_picker.py +0 -0
  251. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/traits/minmax.py +0 -0
  252. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/traits/multi_options.py +0 -0
  253. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/traits/numbers_selector.py +0 -0
  254. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/traits/options.py +0 -0
  255. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/traits/slider.py +0 -0
  256. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/traits/trait_registry.py +0 -0
  257. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/traits/traits.json +0 -0
  258. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/updater/__init__.py +0 -0
  259. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/updater/__main__.py +0 -0
  260. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/utils/__init__.py +0 -0
  261. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/utils/async_utils.py +0 -0
  262. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/utils/dict_utils.py +0 -0
  263. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/utils/file_utils.py +0 -0
  264. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/utils/git_utils.py +0 -0
  265. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/utils/huggingface_utils.py +0 -0
  266. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/utils/image_preview.py +0 -0
  267. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/utils/library_utils.py +0 -0
  268. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/utils/metaclasses.py +0 -0
  269. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/utils/uv_utils.py +0 -0
  270. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/utils/version_utils.py +0 -0
  271. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/version_compatibility/__init__.py +0 -0
  272. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/version_compatibility/versions/__init__.py +0 -0
  273. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/version_compatibility/versions/v0_39_0/__init__.py +0 -0
  274. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/version_compatibility/versions/v0_39_0/modified_parameters_set_removal.py +0 -0
  275. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/version_compatibility/versions/v0_63_8/__init__.py +0 -0
  276. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/version_compatibility/versions/v0_63_8/deprecated_nodegroup_parameters.py +0 -0
  277. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/version_compatibility/versions/v0_65_4/__init__.py +0 -0
  278. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/version_compatibility/versions/v0_65_4/run_in_parallel_to_run_in_order.py +0 -0
  279. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/version_compatibility/versions/v0_65_5/__init__.py +0 -0
  280. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/version_compatibility/versions/v0_65_5/flux_2_removed_parameters.py +0 -0
  281. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/src/griptape_nodes/version_compatibility/versions/v0_7_0/__init__.py +0 -0
  282. {griptape_nodes-0.67.0 → griptape_nodes-0.68.0}/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.67.0
3
+ Version: 0.68.0
4
4
  Summary: Add your description here
5
5
  Requires-Dist: griptape>=1.8.12
6
6
  Requires-Dist: pydantic>=2.10.6
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "griptape-nodes"
3
- version = "0.67.0"
3
+ version = "0.68.0"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12.0, <3.13"
@@ -2,16 +2,24 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  import logging
5
+ import os
5
6
  import sys
6
7
  from typing import TYPE_CHECKING, Any
7
8
 
8
9
  if TYPE_CHECKING:
9
10
  from pathlib import Path
10
- from subprocess import _ENV
11
11
 
12
12
  logger = logging.getLogger(__name__)
13
13
 
14
14
 
15
+ def _create_subprocess_env(extra_env: dict[str, str] | None = None) -> dict[str, str]:
16
+ """Create environment for subprocess, inheriting parent env with optional overrides."""
17
+ env = os.environ.copy()
18
+ if extra_env:
19
+ env.update(extra_env)
20
+ return env
21
+
22
+
15
23
  class PythonSubprocessExecutorError(Exception):
16
24
  """Exception raised during Python subprocess execution."""
17
25
 
@@ -22,7 +30,11 @@ class PythonSubprocessExecutor:
22
30
  self._is_running = False
23
31
 
24
32
  async def execute_python_script(
25
- self, script_path: Path, args: list[str] | None = None, cwd: Path | None = None, env: _ENV | None = None
33
+ self,
34
+ script_path: Path,
35
+ args: list[str] | None = None,
36
+ cwd: Path | None = None,
37
+ env: dict[str, str] | None = None,
26
38
  ) -> None:
27
39
  """Execute a Python script in a subprocess and wait for completion.
28
40
 
@@ -30,7 +42,7 @@ class PythonSubprocessExecutor:
30
42
  script_path: Path to the Python script to execute
31
43
  args: Additional command line arguments
32
44
  cwd: Working directory for the subprocess
33
- env: Environment variables for the subprocess
45
+ env: Extra environment variables to add or override in the subprocess
34
46
  """
35
47
  if self.is_running():
36
48
  logger.warning("Another subprocess is already running. Terminating it first.")
@@ -38,6 +50,7 @@ class PythonSubprocessExecutor:
38
50
 
39
51
  args = args or []
40
52
  command = [sys.executable, str(script_path), *args]
53
+ subprocess_env = _create_subprocess_env(env)
41
54
 
42
55
  try:
43
56
  logger.info("Starting subprocess: %s", " ".join(command))
@@ -46,7 +59,7 @@ class PythonSubprocessExecutor:
46
59
  self._process = await asyncio.create_subprocess_exec(
47
60
  *command,
48
61
  cwd=cwd,
49
- env=env,
62
+ env=subprocess_env,
50
63
  stdout=asyncio.subprocess.PIPE,
51
64
  stderr=asyncio.subprocess.PIPE,
52
65
  )
@@ -5,6 +5,7 @@ import asyncio
5
5
  import logging
6
6
  import pickle
7
7
  from dataclasses import dataclass
8
+ from enum import StrEnum
8
9
  from pathlib import Path
9
10
  from typing import TYPE_CHECKING, Any, NamedTuple
10
11
 
@@ -16,7 +17,7 @@ from griptape_nodes.exe_types.base_iterative_nodes import (
16
17
  BaseIterativeStartNode,
17
18
  )
18
19
  from griptape_nodes.exe_types.core_types import ParameterTypeBuiltin
19
- from griptape_nodes.exe_types.node_groups import BaseIterativeNodeGroup, SubflowNodeGroup
20
+ from griptape_nodes.exe_types.node_groups import BaseIterativeNodeGroup, IterationControlParam, SubflowNodeGroup
20
21
  from griptape_nodes.exe_types.node_types import (
21
22
  CONTROL_INPUT_PARAMETER,
22
23
  LOCAL_EXECUTION,
@@ -106,6 +107,15 @@ if TYPE_CHECKING:
106
107
 
107
108
  logger = logging.getLogger("griptape_nodes")
108
109
 
110
+
111
+ class IterationControlAction(StrEnum):
112
+ """Enum for iterative group control actions."""
113
+
114
+ ADD = "add" # Normal path - add result to list
115
+ SKIP = "skip" # Skip this iteration, don't add result
116
+ BREAK = "break" # Break out of loop immediately
117
+
118
+
109
119
  LOOP_EVENTS_TO_SUPPRESS = {
110
120
  CreateFlowResultSuccess,
111
121
  CreateFlowResultFailure,
@@ -816,13 +826,153 @@ class NodeExecutor:
816
826
  # Check if it's the break signal
817
827
  return next_control_output == deserialized_end_node.break_loop_signal_output
818
828
 
819
- async def _execute_loop_iterations_sequentially( # noqa: PLR0915
829
+ def _get_iteration_control_action_for_group(
830
+ self,
831
+ end_loop_node: BaseIterativeNodeGroup,
832
+ node_name_mappings: dict[str, str],
833
+ ) -> IterationControlAction:
834
+ """Determine which control action was taken during iteration for an iterative group.
835
+
836
+ Checks if any internal nodes have executed their control outputs that connect to the
837
+ group's control parameters (loop_complete, skip_iteration, break_loop). This is done by
838
+ checking the source node's parameter_output_values for CONTROL_INPUT_PARAMETER.
839
+
840
+ Args:
841
+ end_loop_node: The BaseIterativeNodeGroup being executed
842
+ node_name_mappings: Mapping from original to deserialized node names
843
+
844
+ Returns:
845
+ IterationControlAction indicating which control path was taken
846
+ """
847
+ # Get incoming connections to the end_loop_node (the iterative group)
848
+ list_connections_request = ListConnectionsForNodeRequest(node_name=end_loop_node.name)
849
+ list_connections_result = GriptapeNodes.handle_request(list_connections_request)
850
+ if not isinstance(list_connections_result, ListConnectionsForNodeResultSuccess):
851
+ logger.warning("Failed to list connections for node %s", end_loop_node.name)
852
+ return IterationControlAction.ADD
853
+
854
+ incoming_connections = list_connections_result.incoming_connections
855
+
856
+ # Check each control parameter to see if its source node has fired
857
+ # Priority: BREAK > SKIP > LOOP_COMPLETE > default ADD
858
+ break_source = self._find_source_for_control_param(incoming_connections, IterationControlParam.BREAK_LOOP)
859
+ skip_source = self._find_source_for_control_param(incoming_connections, IterationControlParam.SKIP_ITERATION)
860
+ loop_complete_source = self._find_source_for_control_param(
861
+ incoming_connections, IterationControlParam.LOOP_COMPLETE
862
+ )
863
+
864
+ # Check if break was triggered
865
+ if self._check_control_source_fired(break_source, node_name_mappings):
866
+ return IterationControlAction.BREAK
867
+
868
+ # Check if skip was triggered
869
+ if self._check_control_source_fired(skip_source, node_name_mappings):
870
+ return IterationControlAction.SKIP
871
+
872
+ # Check if loop_complete was triggered
873
+ if self._check_control_source_fired(loop_complete_source, node_name_mappings):
874
+ return IterationControlAction.ADD
875
+
876
+ # If none of the control parameters were triggered, default to ADD
877
+ # This preserves backward compatibility for workflows without explicit loop_complete connections
878
+ return IterationControlAction.ADD
879
+
880
+ def _check_control_source_fired(
881
+ self,
882
+ source: tuple[str, str] | None,
883
+ node_name_mappings: dict[str, str],
884
+ ) -> bool:
885
+ """Check if a control source node has fired its control output.
886
+
887
+ Args:
888
+ source: Tuple of (source_node_name, source_parameter_name) or None
889
+ node_name_mappings: Mapping from original to deserialized node names
890
+
891
+ Returns:
892
+ True if the source node's next control output matches the specified parameter
893
+ """
894
+ if source is None:
895
+ return False
896
+
897
+ source_node_name, source_param_name = source
898
+ deserialized_source_name = node_name_mappings.get(source_node_name)
899
+ if deserialized_source_name is None:
900
+ return False
901
+
902
+ node_manager = GriptapeNodes.NodeManager()
903
+ try:
904
+ deserialized_source_node = node_manager.get_node_by_name(deserialized_source_name)
905
+ except ValueError:
906
+ return False
907
+
908
+ if deserialized_source_node is None:
909
+ return False
910
+
911
+ # Check if the node's next control output matches the source parameter
912
+ next_control_output = deserialized_source_node.get_next_control_output()
913
+ if next_control_output is None:
914
+ return False
915
+
916
+ # Get the parameter object to compare
917
+ source_param = deserialized_source_node.get_parameter_by_name(source_param_name)
918
+ return next_control_output == source_param
919
+
920
+ def _find_source_for_control_param(
921
+ self,
922
+ incoming_connections: list,
923
+ control_param_name: str,
924
+ ) -> tuple[str, str] | None:
925
+ """Find the source node and parameter that connects to a control parameter on the iterative group.
926
+
927
+ Args:
928
+ incoming_connections: List of incoming connections to the iterative group
929
+ control_param_name: Name of the control parameter to find (e.g., "break_loop")
930
+
931
+ Returns:
932
+ Tuple of (source_node_name, source_parameter_name), or None if not found
933
+ """
934
+ flow_manager = GriptapeNodes.FlowManager()
935
+ connections = flow_manager.get_connections()
936
+
937
+ for conn in incoming_connections:
938
+ if conn.target_parameter_name != control_param_name:
939
+ continue
940
+
941
+ source_node_name = conn.source_node_name
942
+ source_param_name = conn.source_parameter_name
943
+
944
+ # If source is a SubflowNodeGroup, follow the internal connection to get the actual source
945
+ node_manager = GriptapeNodes.NodeManager()
946
+ try:
947
+ source_node = node_manager.get_node_by_name(source_node_name)
948
+ except ValueError:
949
+ continue
950
+
951
+ if isinstance(source_node, SubflowNodeGroup):
952
+ # Get connections to this proxy parameter to find the actual internal source
953
+ proxy_param = source_node.get_parameter_by_name(source_param_name)
954
+ if proxy_param:
955
+ internal_connections = connections.get_all_incoming_connections(source_node)
956
+ for internal_conn in internal_connections:
957
+ if (
958
+ internal_conn.target_parameter.name == source_param_name
959
+ and internal_conn.is_node_group_internal
960
+ ):
961
+ source_node_name = internal_conn.source_node.name
962
+ source_param_name = internal_conn.source_parameter.name
963
+ break
964
+
965
+ return (source_node_name, source_param_name)
966
+
967
+ return None
968
+
969
+ async def _execute_loop_iterations_sequentially( # noqa: PLR0915, C901, PLR0912
820
970
  self,
821
971
  package_result: PackageNodesAsSerializedFlowResultSuccess,
822
972
  total_iterations: int,
823
973
  parameter_values_per_iteration: dict[int, dict[str, Any]],
824
974
  end_loop_node: BaseIterativeEndNode | BaseIterativeNodeGroup,
825
- ) -> tuple[dict[int, Any], list[int], dict[str, Any]]:
975
+ ) -> tuple[dict[int, Any], list[int], dict[str, Any], int, bool]:
826
976
  """Execute loop iterations sequentially by running one flow instance N times.
827
977
 
828
978
  Args:
@@ -834,8 +984,10 @@ class NodeExecutor:
834
984
  Returns:
835
985
  Tuple of:
836
986
  - iteration_results: Dict mapping iteration_index -> result value
837
- - successful_iterations: List of iteration indices that succeeded
987
+ - successful_iterations: List of iteration indices that executed without error
838
988
  - last_iteration_values: Dict mapping parameter names -> values from last iteration
989
+ - skipped_count: Number of iterations that were skipped via skip control signal
990
+ - break_occurred: True if the loop exited early due to a break signal
839
991
  """
840
992
  # Deserialize flow once
841
993
  context_manager = GriptapeNodes.ContextManager()
@@ -868,6 +1020,8 @@ class NodeExecutor:
868
1020
 
869
1021
  iteration_results: dict[int, Any] = {}
870
1022
  successful_iterations: list[int] = []
1023
+ skipped_count = 0
1024
+ break_occurred = False
871
1025
 
872
1026
  # Build reverse mapping: packaged_name → original_name for event translation
873
1027
  reverse_node_mapping = {
@@ -927,6 +1081,39 @@ class NodeExecutor:
927
1081
 
928
1082
  successful_iterations.append(iteration_index)
929
1083
 
1084
+ # For BaseIterativeNodeGroup, check control action to handle skip/break
1085
+ if isinstance(end_loop_node, BaseIterativeNodeGroup):
1086
+ control_action = self._get_iteration_control_action_for_group(end_loop_node, node_name_mappings)
1087
+
1088
+ if control_action == IterationControlAction.SKIP:
1089
+ logger.info(
1090
+ "Skip detected at iteration %d/%d - skipping result collection",
1091
+ iteration_index + 1,
1092
+ total_iterations,
1093
+ )
1094
+ skipped_count += 1
1095
+ continue
1096
+
1097
+ if control_action == IterationControlAction.BREAK:
1098
+ logger.info(
1099
+ "Break detected at iteration %d/%d - collecting result then stopping",
1100
+ iteration_index + 1,
1101
+ total_iterations,
1102
+ )
1103
+ # Extract result from this iteration before breaking
1104
+ # (the work was done, so we should collect the result)
1105
+ deserialized_flows = [(iteration_index, flow_name, node_name_mappings)]
1106
+ single_iteration_results = self.get_parameter_values_from_iterations(
1107
+ end_loop_node=end_loop_node,
1108
+ deserialized_flows=deserialized_flows,
1109
+ package_flow_result_success=package_result,
1110
+ )
1111
+ iteration_results.update(single_iteration_results)
1112
+ break_occurred = True
1113
+ break
1114
+
1115
+ # control_action == ADD: fall through to extract result
1116
+
930
1117
  # Extract result from this iteration
931
1118
  deserialized_flows = [(iteration_index, flow_name, node_name_mappings)]
932
1119
  single_iteration_results = self.get_parameter_values_from_iterations(
@@ -938,13 +1125,14 @@ class NodeExecutor:
938
1125
 
939
1126
  logger.info("Completed sequential iteration %d/%d", iteration_index + 1, total_iterations)
940
1127
 
941
- # Check if the end node signaled a break
1128
+ # Check if the end node signaled a break (for BaseIterativeEndNode)
942
1129
  if self._should_break_loop(node_name_mappings, package_result):
943
1130
  logger.info(
944
1131
  "Loop break detected at iteration %d/%d - stopping execution early",
945
1132
  iteration_index + 1,
946
1133
  total_iterations,
947
1134
  )
1135
+ break_occurred = True
948
1136
  break
949
1137
 
950
1138
  # Extract last iteration values from the last successful iteration
@@ -956,7 +1144,7 @@ class NodeExecutor:
956
1144
  total_iterations=len(successful_iterations),
957
1145
  )
958
1146
 
959
- return iteration_results, successful_iterations, last_iteration_values
1147
+ return iteration_results, successful_iterations, last_iteration_values, skipped_count, break_occurred
960
1148
 
961
1149
  finally:
962
1150
  # Cleanup - delete the flow
@@ -1018,6 +1206,8 @@ class NodeExecutor:
1018
1206
  iteration_results,
1019
1207
  successful_iterations,
1020
1208
  last_iteration_values,
1209
+ _skipped_count,
1210
+ _break_occurred,
1021
1211
  ) = await self._execute_loop_iterations_sequentially(
1022
1212
  package_result=package_result,
1023
1213
  total_iterations=total_iterations,
@@ -1383,12 +1573,16 @@ class NodeExecutor:
1383
1573
  parameter_values_per_iteration = self._get_merged_parameter_values_for_iterative_group(node, package_result)
1384
1574
 
1385
1575
  # Execute iterations sequentially based on execution environment
1576
+ skipped_count = 0
1577
+ break_occurred = False
1386
1578
  match execution_type:
1387
1579
  case node_types.LOCAL_EXECUTION:
1388
1580
  (
1389
1581
  iteration_results,
1390
1582
  successful_iterations,
1391
1583
  last_iteration_values,
1584
+ skipped_count,
1585
+ break_occurred,
1392
1586
  ) = await self._execute_loop_iterations_sequentially(
1393
1587
  package_result=package_result,
1394
1588
  total_iterations=total_iterations,
@@ -1420,19 +1614,25 @@ class NodeExecutor:
1420
1614
  execution_type=execution_type,
1421
1615
  )
1422
1616
 
1423
- # Check if execution stopped early due to break (not failure)
1617
+ # Check if execution stopped early
1424
1618
  if len(successful_iterations) < total_iterations:
1425
- expected_count = len(successful_iterations)
1426
- actual_count = len(iteration_results)
1427
- if expected_count != actual_count:
1428
- failed_count = expected_count - actual_count
1429
- msg = f"Iterative group execution failed: {failed_count} of {expected_count} iterations failed"
1430
- raise RuntimeError(msg)
1431
- logger.info(
1432
- "Iterative group execution stopped early at %d of %d iterations (break signal)",
1433
- len(successful_iterations),
1434
- total_iterations,
1435
- )
1619
+ if break_occurred:
1620
+ # Early exit due to break signal - this is expected behavior
1621
+ logger.info(
1622
+ "Iterative group execution stopped early at %d of %d iterations (break signal)",
1623
+ len(successful_iterations),
1624
+ total_iterations,
1625
+ )
1626
+ else:
1627
+ # Early exit not due to break - check for failures
1628
+ # successful_iterations includes skipped iterations, but iteration_results does not
1629
+ # So we need to account for skipped iterations when checking for failures
1630
+ expected_result_count = len(successful_iterations) - skipped_count
1631
+ actual_result_count = len(iteration_results)
1632
+ if expected_result_count != actual_result_count:
1633
+ failed_count = expected_result_count - actual_result_count
1634
+ msg = f"Iterative group execution failed: {failed_count} of {len(successful_iterations)} iterations failed"
1635
+ raise RuntimeError(msg)
1436
1636
 
1437
1637
  # Build results list in iteration order
1438
1638
  node._results_list = []
@@ -0,0 +1,7 @@
1
+ """Node group implementations for managing collections of nodes."""
2
+
3
+ from .base_iterative_node_group import BaseIterativeNodeGroup, IterationControlParam
4
+ from .base_node_group import BaseNodeGroup
5
+ from .subflow_node_group import SubflowNodeGroup
6
+
7
+ __all__ = ["BaseIterativeNodeGroup", "BaseNodeGroup", "IterationControlParam", "SubflowNodeGroup"]
@@ -3,9 +3,11 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from abc import abstractmethod
6
+ from enum import StrEnum
6
7
  from typing import Any
7
8
 
8
9
  from griptape_nodes.exe_types.core_types import (
10
+ ControlParameterInput,
9
11
  Parameter,
10
12
  ParameterMode,
11
13
  ParameterTypeBuiltin,
@@ -23,6 +25,14 @@ EXECUTION_MODE_VALUE_LOOKUP = {
23
25
  }
24
26
 
25
27
 
28
+ class IterationControlParam(StrEnum):
29
+ """Parameter names for iteration control on iterative node groups."""
30
+
31
+ LOOP_COMPLETE = "loop_complete"
32
+ SKIP_ITERATION = "skip_iteration"
33
+ BREAK_LOOP = "break_loop"
34
+
35
+
26
36
  class BaseIterativeNodeGroup(SubflowNodeGroup):
27
37
  """Base class for iterative node groups (ForEach, ForLoop, etc.).
28
38
 
@@ -100,7 +110,15 @@ class BaseIterativeNodeGroup(SubflowNodeGroup):
100
110
  self.metadata["left_parameters"] = []
101
111
  self.metadata["left_parameters"].append("index")
102
112
 
103
- # Results collection parameters (right side - collects from group)
113
+ # Control input for loop completion (right side - primary loop completion path)
114
+ self.loop_complete = ControlParameterInput(
115
+ tooltip="Signal that this iteration is complete and continue to next iteration",
116
+ name=IterationControlParam.LOOP_COMPLETE.value,
117
+ )
118
+ self.loop_complete.ui_options = {"display_name": "Loop Complete"}
119
+ self.add_parameter(self.loop_complete)
120
+
121
+ # Data parameter for the item to add (right side - collects from group)
104
122
  self.new_item_to_add = Parameter(
105
123
  name="new_item_to_add",
106
124
  tooltip="Item to add to results list for each iteration",
@@ -109,6 +127,21 @@ class BaseIterativeNodeGroup(SubflowNodeGroup):
109
127
  )
110
128
  self.add_parameter(self.new_item_to_add)
111
129
 
130
+ # Skip and Break control inputs (right side - for loop control)
131
+ self.skip_iteration = ControlParameterInput(
132
+ tooltip="Skip current item and continue to next iteration",
133
+ name=IterationControlParam.SKIP_ITERATION.value,
134
+ )
135
+ self.skip_iteration.ui_options = {"display_name": "Skip to Next Iteration"}
136
+ self.add_parameter(self.skip_iteration)
137
+
138
+ self.break_loop = ControlParameterInput(
139
+ tooltip="Break out of loop immediately",
140
+ name=IterationControlParam.BREAK_LOOP.value,
141
+ )
142
+ self.break_loop.ui_options = {"display_name": "Break Out of Loop"}
143
+ self.add_parameter(self.break_loop)
144
+
112
145
  self.results = Parameter(
113
146
  name="results",
114
147
  tooltip="Collected results from all iterations",
@@ -120,7 +153,15 @@ class BaseIterativeNodeGroup(SubflowNodeGroup):
120
153
  # Track right parameters for UI layout
121
154
  if "right_parameters" not in self.metadata:
122
155
  self.metadata["right_parameters"] = []
123
- self.metadata["right_parameters"].extend(["new_item_to_add", "results"])
156
+ self.metadata["right_parameters"].extend(
157
+ [
158
+ IterationControlParam.LOOP_COMPLETE.value,
159
+ "new_item_to_add",
160
+ IterationControlParam.SKIP_ITERATION.value,
161
+ IterationControlParam.BREAK_LOOP.value,
162
+ "results",
163
+ ]
164
+ )
124
165
 
125
166
  def after_value_set(self, parameter: Parameter, value: Any) -> None:
126
167
  """Handle parameter value changes."""
@@ -130,6 +171,17 @@ class BaseIterativeNodeGroup(SubflowNodeGroup):
130
171
  run_in_order = EXECUTION_MODE_VALUE_LOOKUP.get(value, True)
131
172
  self.set_parameter_value("run_in_order", run_in_order)
132
173
 
174
+ # Hide or show skip/break controls based on execution mode
175
+ # Skip and Break are only supported in sequential mode (run_in_order=True)
176
+ if run_in_order:
177
+ # Show controls when running sequentially
178
+ self.show_parameter_by_name(self.skip_iteration.name)
179
+ self.show_parameter_by_name(self.break_loop.name)
180
+ else:
181
+ # Hide controls when running in parallel (not supported)
182
+ self.hide_parameter_by_name(self.skip_iteration.name)
183
+ self.hide_parameter_by_name(self.break_loop.name)
184
+
133
185
  @abstractmethod
134
186
  def _get_iteration_items(self) -> list[Any]:
135
187
  """Get the list of items to iterate over.
@@ -0,0 +1,152 @@
1
+ """HuggingFace parameter class that supports repo + variant/subfolder selection."""
2
+
3
+ import logging
4
+ from pathlib import Path
5
+
6
+ from huggingface_hub.constants import HF_HUB_CACHE
7
+
8
+ from griptape_nodes.exe_types.node_types import BaseNode
9
+ from griptape_nodes.exe_types.param_components.huggingface.huggingface_model_parameter import HuggingFaceModelParameter
10
+
11
+ logger = logging.getLogger("griptape_nodes")
12
+
13
+
14
+ def _get_repo_cache_path(repo_id: str) -> Path | None:
15
+ """Get the cache path for a repo if it exists."""
16
+ cache_path = Path(HF_HUB_CACHE)
17
+ if not cache_path.exists():
18
+ return None
19
+
20
+ # Convert repo_id to cache folder format: owner/repo -> models--owner--repo
21
+ folder_name = f"models--{repo_id.replace('/', '--')}"
22
+ repo_path = cache_path / folder_name
23
+
24
+ if repo_path.exists():
25
+ return repo_path
26
+ return None
27
+
28
+
29
+ def _get_snapshot_path(repo_path: Path) -> Path | None:
30
+ """Get the latest snapshot path for a repo."""
31
+ snapshots_dir = repo_path / "snapshots"
32
+ if not snapshots_dir.exists():
33
+ return None
34
+
35
+ snapshots = [p for p in snapshots_dir.iterdir() if p.is_dir()]
36
+ if not snapshots:
37
+ return None
38
+
39
+ # Return the most recent snapshot
40
+ return max(snapshots, key=lambda p: p.stat().st_mtime)
41
+
42
+
43
+ def _list_variants_in_cache(repo_id: str, variants: list[str]) -> list[tuple[str, str, str]]:
44
+ """List (repo_id, variant, revision) tuples for variants that exist in cache.
45
+
46
+ Args:
47
+ repo_id: The HuggingFace repo ID (e.g., "Lightricks/LTX-2")
48
+ variants: List of variant/subfolder names to check for (e.g., ["ltx-2-19b-dev", "ltx-2-19b-dev-fp8"])
49
+
50
+ Returns:
51
+ List of (repo_id, variant, revision) tuples for variants found in cache
52
+ """
53
+ repo_path = _get_repo_cache_path(repo_id)
54
+ if repo_path is None:
55
+ return []
56
+
57
+ snapshot_path = _get_snapshot_path(repo_path)
58
+ if snapshot_path is None:
59
+ return []
60
+
61
+ revision = snapshot_path.name
62
+ results = []
63
+
64
+ for variant in variants:
65
+ # Check for variant as a directory (subfolder model structure)
66
+ variant_dir_path = snapshot_path / variant
67
+ if variant_dir_path.exists() and variant_dir_path.is_dir():
68
+ results.append((repo_id, variant, revision))
69
+ continue
70
+
71
+ # Check for variant as a .safetensors file (single-file model structure)
72
+ variant_file_path = snapshot_path / f"{variant}.safetensors"
73
+ if variant_file_path.exists() and variant_file_path.is_file():
74
+ results.append((repo_id, variant, revision))
75
+
76
+ return results
77
+
78
+
79
+ class HuggingFaceRepoVariantParameter(HuggingFaceModelParameter):
80
+ """Parameter class for selecting a variant/subfolder within a HuggingFace repo.
81
+
82
+ Use this when a single repo contains multiple model variants as subfolders.
83
+ For example, Lightricks/LTX-2 contains:
84
+ - ltx-2-19b-dev
85
+ - ltx-2-19b-dev-fp8
86
+ - ltx-2-19b-dev-fp4
87
+ """
88
+
89
+ def __init__(
90
+ self,
91
+ node: BaseNode,
92
+ repo_id: str,
93
+ variants: list[str],
94
+ parameter_name: str = "model",
95
+ ):
96
+ """Initialize the parameter.
97
+
98
+ Args:
99
+ node: The node this parameter belongs to
100
+ repo_id: The HuggingFace repo ID (e.g., "Lightricks/LTX-2")
101
+ variants: List of variant/subfolder names (e.g., ["ltx-2-19b-dev", "ltx-2-19b-dev-fp8"])
102
+ parameter_name: Name of the parameter (default: "model")
103
+ """
104
+ super().__init__(node, parameter_name)
105
+ self._repo_id = repo_id
106
+ self._variants = variants
107
+ self.refresh_parameters()
108
+
109
+ @classmethod
110
+ def _repo_variant_to_key(cls, repo_id: str, variant: str) -> str:
111
+ """Convert repo_id and variant to a display key."""
112
+ return f"{repo_id}/{variant}"
113
+
114
+ @classmethod
115
+ def _key_to_repo_variant(cls, key: str) -> tuple[str, str]:
116
+ """Parse a display key back to repo_id and variant.
117
+
118
+ Key format: "owner/repo/variant"
119
+ """
120
+ parts = key.rsplit("/", 1)
121
+ if len(parts) == 2: # noqa: PLR2004
122
+ return parts[0], parts[1]
123
+ # Fallback: treat entire key as repo_id with empty variant
124
+ return key, ""
125
+
126
+ def fetch_repo_revisions(self) -> list[tuple[str, str]]:
127
+ """Fetch available variants from cache.
128
+
129
+ Returns list of (display_key, revision) tuples where display_key is "repo_id/variant".
130
+ """
131
+ variant_revisions = _list_variants_in_cache(self._repo_id, self._variants)
132
+ return [
133
+ (self._repo_variant_to_key(repo_id, variant), revision) for repo_id, variant, revision in variant_revisions
134
+ ]
135
+
136
+ def get_download_commands(self) -> list[str]:
137
+ """Return download commands for the repo."""
138
+ return [f'huggingface-cli download "{self._repo_id}"']
139
+
140
+ def get_download_models(self) -> list[str]:
141
+ """Return list of model names for download."""
142
+ return [self._repo_id]
143
+
144
+ def get_repo_variant_revision(self) -> tuple[str, str, str]:
145
+ """Get the selected repo_id, variant, and revision.
146
+
147
+ Returns:
148
+ Tuple of (repo_id, variant, revision)
149
+ """
150
+ repo_key, revision = self.get_repo_revision()
151
+ repo_id, variant = self._key_to_repo_variant(repo_key)
152
+ return repo_id, variant, revision