griptape-nodes 0.66.2__py3-none-any.whl → 0.68.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- griptape_nodes/bootstrap/utils/python_subprocess_executor.py +17 -4
- griptape_nodes/common/node_executor.py +295 -18
- griptape_nodes/exe_types/core_types.py +28 -1
- griptape_nodes/exe_types/node_groups/__init__.py +2 -2
- griptape_nodes/exe_types/node_groups/base_iterative_node_group.py +81 -10
- griptape_nodes/exe_types/node_groups/base_node_group.py +64 -1
- griptape_nodes/exe_types/node_groups/subflow_node_group.py +0 -34
- griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_variant_parameter.py +152 -0
- griptape_nodes/exe_types/param_components/seed_parameter.py +3 -2
- griptape_nodes/exe_types/param_types/parameter_audio.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_bool.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_button.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_dict.py +151 -0
- griptape_nodes/exe_types/param_types/parameter_float.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_image.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_int.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_json.py +268 -0
- griptape_nodes/exe_types/param_types/parameter_number.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_range.py +393 -0
- griptape_nodes/exe_types/param_types/parameter_string.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_three_d.py +3 -0
- griptape_nodes/exe_types/param_types/parameter_video.py +3 -0
- griptape_nodes/retained_mode/events/library_events.py +2 -0
- griptape_nodes/retained_mode/events/parameter_events.py +89 -1
- griptape_nodes/retained_mode/managers/event_manager.py +176 -10
- griptape_nodes/retained_mode/managers/flow_manager.py +2 -1
- griptape_nodes/retained_mode/managers/library_manager.py +14 -4
- griptape_nodes/retained_mode/managers/node_manager.py +187 -7
- griptape_nodes/retained_mode/managers/workflow_manager.py +58 -16
- griptape_nodes/utils/file_utils.py +58 -0
- {griptape_nodes-0.66.2.dist-info → griptape_nodes-0.68.0.dist-info}/METADATA +1 -1
- {griptape_nodes-0.66.2.dist-info → griptape_nodes-0.68.0.dist-info}/RECORD +34 -30
- {griptape_nodes-0.66.2.dist-info → griptape_nodes-0.68.0.dist-info}/WHEEL +1 -1
- {griptape_nodes-0.66.2.dist-info → griptape_nodes-0.68.0.dist-info}/entry_points.txt +0 -0
|
@@ -2,18 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import logging
|
|
6
5
|
from abc import abstractmethod
|
|
6
|
+
from enum import StrEnum
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
9
|
from griptape_nodes.exe_types.core_types import (
|
|
10
|
+
ControlParameterInput,
|
|
10
11
|
Parameter,
|
|
11
12
|
ParameterMode,
|
|
12
13
|
ParameterTypeBuiltin,
|
|
13
14
|
)
|
|
14
15
|
from griptape_nodes.exe_types.node_groups.subflow_node_group import SubflowNodeGroup
|
|
16
|
+
from griptape_nodes.traits.options import Options
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
# Execution mode choices and their corresponding boolean values (True = run in order)
|
|
19
|
+
EXECUTION_MODE_ONE_AT_A_TIME = "Run Group Items One at a Time"
|
|
20
|
+
EXECUTION_MODE_ALL_AT_ONCE = "Run Group Items All at Once"
|
|
21
|
+
EXECUTION_MODE_CHOICES = [EXECUTION_MODE_ONE_AT_A_TIME, EXECUTION_MODE_ALL_AT_ONCE]
|
|
22
|
+
EXECUTION_MODE_VALUE_LOOKUP = {
|
|
23
|
+
EXECUTION_MODE_ONE_AT_A_TIME: True,
|
|
24
|
+
EXECUTION_MODE_ALL_AT_ONCE: False,
|
|
25
|
+
}
|
|
26
|
+
|
|
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"
|
|
17
34
|
|
|
18
35
|
|
|
19
36
|
class BaseIterativeNodeGroup(SubflowNodeGroup):
|
|
@@ -37,7 +54,6 @@ class BaseIterativeNodeGroup(SubflowNodeGroup):
|
|
|
37
54
|
_items: list[Any]
|
|
38
55
|
_current_iteration_count: int
|
|
39
56
|
_total_iterations: int
|
|
40
|
-
is_parallel: bool
|
|
41
57
|
|
|
42
58
|
# Results storage
|
|
43
59
|
_results_list: list[Any]
|
|
@@ -53,20 +69,31 @@ class BaseIterativeNodeGroup(SubflowNodeGroup):
|
|
|
53
69
|
self._items = []
|
|
54
70
|
self._current_iteration_count = 0
|
|
55
71
|
self._total_iterations = 0
|
|
56
|
-
self.is_parallel = False
|
|
57
72
|
self._results_list = []
|
|
58
73
|
|
|
59
|
-
#
|
|
74
|
+
# Hidden boolean parameter used by node_executor for execution logic
|
|
60
75
|
self.run_in_order = Parameter(
|
|
61
76
|
name="run_in_order",
|
|
62
77
|
tooltip="Execute all iterations in order or concurrently",
|
|
63
78
|
type=ParameterTypeBuiltin.BOOL.value,
|
|
64
79
|
allowed_modes={ParameterMode.PROPERTY},
|
|
65
80
|
default_value=True,
|
|
66
|
-
|
|
81
|
+
hide=True,
|
|
67
82
|
)
|
|
68
83
|
self.add_parameter(self.run_in_order)
|
|
69
84
|
|
|
85
|
+
# User selection that controls run_in_order
|
|
86
|
+
self.execution_mode = Parameter(
|
|
87
|
+
name="execution_mode",
|
|
88
|
+
tooltip="Execute all iterations in order or concurrently",
|
|
89
|
+
type=ParameterTypeBuiltin.STR.value,
|
|
90
|
+
allowed_modes={ParameterMode.PROPERTY},
|
|
91
|
+
default_value=EXECUTION_MODE_ONE_AT_A_TIME,
|
|
92
|
+
traits={Options(choices=EXECUTION_MODE_CHOICES, show_search=False)},
|
|
93
|
+
ui_options={"display_name": "Execution Mode"},
|
|
94
|
+
)
|
|
95
|
+
self.add_parameter(self.execution_mode)
|
|
96
|
+
|
|
70
97
|
# Index parameter - available in all iterative nodes (left side - feeds into group)
|
|
71
98
|
self.index_param = Parameter(
|
|
72
99
|
name="index",
|
|
@@ -83,7 +110,15 @@ class BaseIterativeNodeGroup(SubflowNodeGroup):
|
|
|
83
110
|
self.metadata["left_parameters"] = []
|
|
84
111
|
self.metadata["left_parameters"].append("index")
|
|
85
112
|
|
|
86
|
-
#
|
|
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)
|
|
87
122
|
self.new_item_to_add = Parameter(
|
|
88
123
|
name="new_item_to_add",
|
|
89
124
|
tooltip="Item to add to results list for each iteration",
|
|
@@ -92,6 +127,21 @@ class BaseIterativeNodeGroup(SubflowNodeGroup):
|
|
|
92
127
|
)
|
|
93
128
|
self.add_parameter(self.new_item_to_add)
|
|
94
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
|
+
|
|
95
145
|
self.results = Parameter(
|
|
96
146
|
name="results",
|
|
97
147
|
tooltip="Collected results from all iterations",
|
|
@@ -103,13 +153,34 @@ class BaseIterativeNodeGroup(SubflowNodeGroup):
|
|
|
103
153
|
# Track right parameters for UI layout
|
|
104
154
|
if "right_parameters" not in self.metadata:
|
|
105
155
|
self.metadata["right_parameters"] = []
|
|
106
|
-
self.metadata["right_parameters"].extend(
|
|
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
|
+
)
|
|
107
165
|
|
|
108
166
|
def after_value_set(self, parameter: Parameter, value: Any) -> None:
|
|
109
167
|
"""Handle parameter value changes."""
|
|
110
168
|
super().after_value_set(parameter, value)
|
|
111
|
-
if parameter == self.
|
|
112
|
-
|
|
169
|
+
if parameter == self.execution_mode:
|
|
170
|
+
# Convert string choice to boolean and update run_in_order parameter
|
|
171
|
+
run_in_order = EXECUTION_MODE_VALUE_LOOKUP.get(value, True)
|
|
172
|
+
self.set_parameter_value("run_in_order", run_in_order)
|
|
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)
|
|
113
184
|
|
|
114
185
|
@abstractmethod
|
|
115
186
|
def _get_iteration_items(self) -> list[Any]:
|
|
@@ -2,7 +2,15 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
-
from griptape_nodes.exe_types.
|
|
5
|
+
from griptape_nodes.exe_types.core_types import (
|
|
6
|
+
Parameter,
|
|
7
|
+
ParameterMode,
|
|
8
|
+
)
|
|
9
|
+
from griptape_nodes.exe_types.node_types import (
|
|
10
|
+
BaseNode,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
GROUP_SETTINGS_PARAMS_METADATA_KEY = "group_settings_params"
|
|
6
14
|
|
|
7
15
|
|
|
8
16
|
class BaseNodeGroup(BaseNode):
|
|
@@ -30,3 +38,58 @@ class BaseNodeGroup(BaseNode):
|
|
|
30
38
|
self.nodes = {}
|
|
31
39
|
self.metadata["is_node_group"] = True
|
|
32
40
|
self.metadata["executable"] = False
|
|
41
|
+
|
|
42
|
+
def add_parameter_to_group_settings(self, parameter: Parameter) -> None:
|
|
43
|
+
"""Add a parameter to the Group settings panel.
|
|
44
|
+
|
|
45
|
+
Group settings parameters are determined by metadata in the frontend.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
parameter: The parameter to add to settings
|
|
49
|
+
"""
|
|
50
|
+
if ParameterMode.PROPERTY not in parameter.allowed_modes:
|
|
51
|
+
msg = f"Parameter '{parameter.name}' must allow PROPERTY mode to be added to settings."
|
|
52
|
+
raise ValueError(msg)
|
|
53
|
+
|
|
54
|
+
if GROUP_SETTINGS_PARAMS_METADATA_KEY not in self.metadata:
|
|
55
|
+
self.metadata[GROUP_SETTINGS_PARAMS_METADATA_KEY] = []
|
|
56
|
+
|
|
57
|
+
group_settings_params: list[str] = self.metadata.get(GROUP_SETTINGS_PARAMS_METADATA_KEY, [])
|
|
58
|
+
if parameter.name not in group_settings_params:
|
|
59
|
+
group_settings_params.append(parameter.name)
|
|
60
|
+
self.metadata[GROUP_SETTINGS_PARAMS_METADATA_KEY] = group_settings_params
|
|
61
|
+
|
|
62
|
+
def add_nodes_to_group(self, nodes: list[BaseNode]) -> None:
|
|
63
|
+
"""Add nodes to this group.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
nodes: A list of nodes to add to this group
|
|
67
|
+
"""
|
|
68
|
+
for node in nodes:
|
|
69
|
+
self.nodes[node.name] = node
|
|
70
|
+
|
|
71
|
+
node_names_in_group = set(self.nodes.keys())
|
|
72
|
+
self.metadata["node_names_in_group"] = list(node_names_in_group)
|
|
73
|
+
|
|
74
|
+
def remove_nodes_from_group(self, nodes: list[BaseNode]) -> None:
|
|
75
|
+
"""Remove nodes from this group.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
nodes: A list of nodes to remove from this group
|
|
79
|
+
"""
|
|
80
|
+
for node in nodes:
|
|
81
|
+
if node.name in self.nodes:
|
|
82
|
+
del self.nodes[node.name]
|
|
83
|
+
|
|
84
|
+
def _add_nodes_to_group_dict(self, nodes: list[BaseNode]) -> None:
|
|
85
|
+
"""Add nodes to the group's node dictionary."""
|
|
86
|
+
for node in nodes:
|
|
87
|
+
node.parent_group = self
|
|
88
|
+
self.nodes[node.name] = node
|
|
89
|
+
|
|
90
|
+
def _validate_nodes_in_group(self, nodes: list[BaseNode]) -> None:
|
|
91
|
+
"""Validate that all nodes are in the group."""
|
|
92
|
+
for node in nodes:
|
|
93
|
+
if node.name not in self.nodes:
|
|
94
|
+
msg = f"Node {node.name} is not in node group {self.name}"
|
|
95
|
+
raise ValueError(msg)
|
|
@@ -14,7 +14,6 @@ from griptape_nodes.exe_types.core_types import (
|
|
|
14
14
|
from griptape_nodes.exe_types.node_groups.base_node_group import BaseNodeGroup
|
|
15
15
|
from griptape_nodes.exe_types.node_types import (
|
|
16
16
|
LOCAL_EXECUTION,
|
|
17
|
-
PRIVATE_EXECUTION,
|
|
18
17
|
get_library_names_with_publish_handlers,
|
|
19
18
|
)
|
|
20
19
|
from griptape_nodes.retained_mode.events.connection_events import (
|
|
@@ -300,26 +299,6 @@ class SubflowNodeGroup(BaseNodeGroup, ABC):
|
|
|
300
299
|
|
|
301
300
|
return proxy_param
|
|
302
301
|
|
|
303
|
-
def add_parameter_to_group_settings(self, parameter: Parameter) -> None:
|
|
304
|
-
"""Add a parameter to the Group settings panel.
|
|
305
|
-
|
|
306
|
-
Args:
|
|
307
|
-
parameter: The parameter to add to settings
|
|
308
|
-
"""
|
|
309
|
-
if ParameterMode.PROPERTY not in parameter.allowed_modes:
|
|
310
|
-
msg = f"Parameter '{parameter.name}' must allow PROPERTY mode to be added to settings."
|
|
311
|
-
raise ValueError(msg)
|
|
312
|
-
|
|
313
|
-
execution_environment: dict = self.metadata.get("execution_environment", {})
|
|
314
|
-
if LOCAL_EXECUTION not in execution_environment:
|
|
315
|
-
execution_environment[LOCAL_EXECUTION] = {"parameter_names": []}
|
|
316
|
-
if PRIVATE_EXECUTION not in execution_environment:
|
|
317
|
-
execution_environment[PRIVATE_EXECUTION] = {"parameter_names": []}
|
|
318
|
-
|
|
319
|
-
for library in execution_environment:
|
|
320
|
-
parameter_names = self.metadata["execution_environment"][library].get("parameter_names", [])
|
|
321
|
-
self.metadata["execution_environment"][library]["parameter_names"] = [parameter.name, *parameter_names]
|
|
322
|
-
|
|
323
302
|
def get_all_nodes(self) -> dict[str, BaseNode]:
|
|
324
303
|
all_nodes = {}
|
|
325
304
|
for node_name, node in self.nodes.items():
|
|
@@ -478,12 +457,6 @@ class SubflowNodeGroup(BaseNodeGroup, ABC):
|
|
|
478
457
|
for parent_group, node_list in child_nodes.items():
|
|
479
458
|
parent_group.remove_nodes_from_group(node_list)
|
|
480
459
|
|
|
481
|
-
def _add_nodes_to_group_dict(self, nodes: list[BaseNode]) -> None:
|
|
482
|
-
"""Add nodes to the group's node dictionary."""
|
|
483
|
-
for node in nodes:
|
|
484
|
-
node.parent_group = self
|
|
485
|
-
self.nodes[node.name] = node
|
|
486
|
-
|
|
487
460
|
def _cleanup_proxy_parameter(self, proxy_parameter: Parameter, metadata_key: str) -> None:
|
|
488
461
|
"""Clean up proxy parameter if it has no more connections.
|
|
489
462
|
|
|
@@ -892,13 +865,6 @@ class SubflowNodeGroup(BaseNodeGroup, ABC):
|
|
|
892
865
|
GriptapeNodes.handle_request(create_first_connection)
|
|
893
866
|
GriptapeNodes.handle_request(create_second_connection)
|
|
894
867
|
|
|
895
|
-
def _validate_nodes_in_group(self, nodes: list[BaseNode]) -> None:
|
|
896
|
-
"""Validate that all nodes are in the group."""
|
|
897
|
-
for node in nodes:
|
|
898
|
-
if node.name not in self.nodes:
|
|
899
|
-
msg = f"Node {node.name} is not in node group {self.name}"
|
|
900
|
-
raise ValueError(msg)
|
|
901
|
-
|
|
902
868
|
def delete_nodes_from_group(self, nodes: list[BaseNode]) -> None:
|
|
903
869
|
"""Delete nodes from the group and untrack their connections.
|
|
904
870
|
|
|
@@ -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
|
|
@@ -6,8 +6,9 @@ from griptape_nodes.exe_types.node_types import BaseNode
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class SeedParameter:
|
|
9
|
-
def __init__(self, node: BaseNode):
|
|
9
|
+
def __init__(self, node: BaseNode, max_seed: int = 2**32 - 1) -> None:
|
|
10
10
|
self._node = node
|
|
11
|
+
self._max_seed = max_seed
|
|
11
12
|
|
|
12
13
|
def add_input_parameters(self) -> None:
|
|
13
14
|
self._node.add_parameter(
|
|
@@ -51,7 +52,7 @@ class SeedParameter:
|
|
|
51
52
|
def preprocess(self) -> None:
|
|
52
53
|
if self._node.get_parameter_value("randomize_seed"):
|
|
53
54
|
# Not using for cryptographic purposes
|
|
54
|
-
seed = random.randint(0,
|
|
55
|
+
seed = random.randint(0, self._max_seed) # noqa: S311
|
|
55
56
|
self._node.set_parameter_value("seed", seed)
|
|
56
57
|
self._node.publish_update_to_parameter("seed", seed)
|
|
57
58
|
|
|
@@ -55,6 +55,7 @@ class ParameterAudio(Parameter):
|
|
|
55
55
|
settable: bool = True,
|
|
56
56
|
serializable: bool = True,
|
|
57
57
|
user_defined: bool = False,
|
|
58
|
+
private: bool = False,
|
|
58
59
|
element_id: str | None = None,
|
|
59
60
|
element_type: str | None = None,
|
|
60
61
|
parent_container_name: str | None = None,
|
|
@@ -90,6 +91,7 @@ class ParameterAudio(Parameter):
|
|
|
90
91
|
settable: Whether the parameter is settable
|
|
91
92
|
serializable: Whether the parameter is serializable
|
|
92
93
|
user_defined: Whether the parameter is user-defined
|
|
94
|
+
private: Whether this parameter is private
|
|
93
95
|
element_id: Element ID
|
|
94
96
|
element_type: Element type
|
|
95
97
|
parent_container_name: Name of parent container
|
|
@@ -145,6 +147,7 @@ class ParameterAudio(Parameter):
|
|
|
145
147
|
settable=settable,
|
|
146
148
|
serializable=serializable,
|
|
147
149
|
user_defined=user_defined,
|
|
150
|
+
private=private,
|
|
148
151
|
element_id=element_id,
|
|
149
152
|
element_type=element_type,
|
|
150
153
|
parent_container_name=parent_container_name,
|
|
@@ -51,6 +51,7 @@ class ParameterBool(Parameter):
|
|
|
51
51
|
settable: bool = True,
|
|
52
52
|
serializable: bool = True,
|
|
53
53
|
user_defined: bool = False,
|
|
54
|
+
private: bool = False,
|
|
54
55
|
element_id: str | None = None,
|
|
55
56
|
element_type: str | None = None,
|
|
56
57
|
parent_container_name: str | None = None,
|
|
@@ -82,6 +83,7 @@ class ParameterBool(Parameter):
|
|
|
82
83
|
settable: Whether the parameter is settable
|
|
83
84
|
serializable: Whether the parameter is serializable
|
|
84
85
|
user_defined: Whether the parameter is user-defined
|
|
86
|
+
private: Whether this parameter is private
|
|
85
87
|
element_id: Element ID
|
|
86
88
|
element_type: Element type
|
|
87
89
|
parent_container_name: Name of parent container
|
|
@@ -136,6 +138,7 @@ class ParameterBool(Parameter):
|
|
|
136
138
|
settable=settable,
|
|
137
139
|
serializable=serializable,
|
|
138
140
|
user_defined=user_defined,
|
|
141
|
+
private=private,
|
|
139
142
|
element_id=element_id,
|
|
140
143
|
element_type=element_type,
|
|
141
144
|
parent_container_name=parent_container_name,
|
|
@@ -97,6 +97,7 @@ class ParameterButton(Parameter):
|
|
|
97
97
|
settable: bool = True,
|
|
98
98
|
serializable: bool = True,
|
|
99
99
|
user_defined: bool = False,
|
|
100
|
+
private: bool = False,
|
|
100
101
|
element_id: str | None = None,
|
|
101
102
|
element_type: str | None = None,
|
|
102
103
|
parent_container_name: str | None = None,
|
|
@@ -139,6 +140,7 @@ class ParameterButton(Parameter):
|
|
|
139
140
|
settable: Whether the parameter is settable
|
|
140
141
|
serializable: Whether the parameter is serializable
|
|
141
142
|
user_defined: Whether the parameter is user-defined
|
|
143
|
+
private: Whether this parameter is private
|
|
142
144
|
element_id: Element ID
|
|
143
145
|
element_type: Element type
|
|
144
146
|
parent_container_name: Name of parent container
|
|
@@ -212,6 +214,7 @@ class ParameterButton(Parameter):
|
|
|
212
214
|
settable=settable,
|
|
213
215
|
serializable=serializable,
|
|
214
216
|
user_defined=user_defined,
|
|
217
|
+
private=private,
|
|
215
218
|
element_id=element_id,
|
|
216
219
|
element_type=element_type,
|
|
217
220
|
parent_container_name=parent_container_name,
|