griptape-nodes 0.60.4__py3-none-any.whl → 0.62.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/workflow_publishers/local_workflow_publisher.py +0 -1
- griptape_nodes/common/macro_parser/__init__.py +16 -1
- griptape_nodes/common/macro_parser/core.py +19 -7
- griptape_nodes/common/macro_parser/exceptions.py +99 -0
- griptape_nodes/common/macro_parser/formats.py +13 -4
- griptape_nodes/common/macro_parser/matching.py +5 -2
- griptape_nodes/common/macro_parser/parsing.py +48 -8
- griptape_nodes/common/macro_parser/resolution.py +23 -5
- griptape_nodes/common/project_templates/__init__.py +49 -0
- griptape_nodes/common/project_templates/default_project_template.py +87 -0
- griptape_nodes/common/project_templates/defaults/README.md +36 -0
- griptape_nodes/common/project_templates/directory.py +67 -0
- griptape_nodes/common/project_templates/loader.py +342 -0
- griptape_nodes/common/project_templates/project.py +252 -0
- griptape_nodes/common/project_templates/situation.py +143 -0
- griptape_nodes/common/project_templates/validation.py +140 -0
- griptape_nodes/exe_types/core_types.py +36 -3
- griptape_nodes/exe_types/node_types.py +4 -2
- griptape_nodes/exe_types/param_components/progress_bar_component.py +57 -0
- griptape_nodes/exe_types/param_types/parameter_audio.py +243 -0
- griptape_nodes/exe_types/param_types/parameter_image.py +243 -0
- griptape_nodes/exe_types/param_types/parameter_three_d.py +215 -0
- griptape_nodes/exe_types/param_types/parameter_video.py +243 -0
- griptape_nodes/node_library/workflow_registry.py +1 -1
- griptape_nodes/retained_mode/events/execution_events.py +41 -0
- griptape_nodes/retained_mode/events/node_events.py +90 -1
- griptape_nodes/retained_mode/events/os_events.py +108 -0
- griptape_nodes/retained_mode/events/parameter_events.py +1 -1
- griptape_nodes/retained_mode/events/project_events.py +528 -0
- griptape_nodes/retained_mode/events/workflow_events.py +19 -1
- griptape_nodes/retained_mode/griptape_nodes.py +9 -1
- griptape_nodes/retained_mode/managers/agent_manager.py +18 -24
- griptape_nodes/retained_mode/managers/event_manager.py +6 -9
- griptape_nodes/retained_mode/managers/flow_manager.py +63 -0
- griptape_nodes/retained_mode/managers/library_manager.py +55 -42
- griptape_nodes/retained_mode/managers/mcp_manager.py +14 -6
- griptape_nodes/retained_mode/managers/node_manager.py +232 -0
- griptape_nodes/retained_mode/managers/os_manager.py +399 -6
- griptape_nodes/retained_mode/managers/project_manager.py +1067 -0
- griptape_nodes/retained_mode/managers/settings.py +6 -0
- griptape_nodes/retained_mode/managers/sync_manager.py +4 -1
- griptape_nodes/retained_mode/managers/workflow_manager.py +8 -79
- griptape_nodes/traits/button.py +19 -0
- {griptape_nodes-0.60.4.dist-info → griptape_nodes-0.62.0.dist-info}/METADATA +5 -3
- {griptape_nodes-0.60.4.dist-info → griptape_nodes-0.62.0.dist-info}/RECORD +47 -32
- {griptape_nodes-0.60.4.dist-info → griptape_nodes-0.62.0.dist-info}/WHEEL +1 -1
- {griptape_nodes-0.60.4.dist-info → griptape_nodes-0.62.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""Situation template definitions for file path scenarios."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from enum import StrEnum
|
|
6
|
+
from typing import TYPE_CHECKING, Any, ClassVar
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, ConfigDict, Field, ValidationError, field_validator
|
|
9
|
+
|
|
10
|
+
from griptape_nodes.common.macro_parser import MacroSyntaxError, ParsedMacro
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from griptape_nodes.common.project_templates.loader import YAMLLineInfo
|
|
14
|
+
from griptape_nodes.common.project_templates.validation import ProjectValidationInfo
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SituationFilePolicy(StrEnum):
|
|
18
|
+
"""Policy for handling file collisions in situations.
|
|
19
|
+
|
|
20
|
+
Maps to ExistingFilePolicy for file operations, except PROMPT which
|
|
21
|
+
triggers user interaction before determining final policy.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
CREATE_NEW = "create_new" # Increment {_index} in macro
|
|
25
|
+
OVERWRITE = "overwrite" # Maps to ExistingFilePolicy.OVERWRITE
|
|
26
|
+
FAIL = "fail" # Maps to ExistingFilePolicy.FAIL
|
|
27
|
+
PROMPT = "prompt" # Special UI handling
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class SituationPolicy(BaseModel):
|
|
31
|
+
"""Policy for file operations in a situation."""
|
|
32
|
+
|
|
33
|
+
on_collision: SituationFilePolicy = Field(description="Policy for handling file collisions")
|
|
34
|
+
create_dirs: bool = Field(description="Whether to create directories automatically")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class SituationTemplate(BaseModel):
|
|
38
|
+
"""Template defining how files are saved in a specific situation."""
|
|
39
|
+
|
|
40
|
+
LATEST_SCHEMA_VERSION: ClassVar[str] = "0.1.0"
|
|
41
|
+
|
|
42
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
43
|
+
|
|
44
|
+
name: str = Field(description="Name of the situation")
|
|
45
|
+
macro: str = Field(description="Macro template for file path")
|
|
46
|
+
policy: SituationPolicy = Field(description="Policy for file operations")
|
|
47
|
+
fallback: str | None = Field(default=None, description="Name of fallback situation")
|
|
48
|
+
description: str | None = Field(default=None, description="Description of the situation")
|
|
49
|
+
|
|
50
|
+
@field_validator("macro")
|
|
51
|
+
@classmethod
|
|
52
|
+
def validate_macro_syntax(cls, v: str) -> str:
|
|
53
|
+
"""Validate macro syntax using macro parser."""
|
|
54
|
+
try:
|
|
55
|
+
ParsedMacro(v)
|
|
56
|
+
except MacroSyntaxError as e:
|
|
57
|
+
msg = f"Invalid macro syntax: {e}"
|
|
58
|
+
raise ValueError(msg) from e
|
|
59
|
+
return v
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def merge(
|
|
63
|
+
base: SituationTemplate,
|
|
64
|
+
overlay_data: dict[str, Any],
|
|
65
|
+
field_path: str,
|
|
66
|
+
validation_info: ProjectValidationInfo,
|
|
67
|
+
line_info: YAMLLineInfo,
|
|
68
|
+
) -> SituationTemplate:
|
|
69
|
+
"""Merge overlay fields onto base situation.
|
|
70
|
+
|
|
71
|
+
Field-level merge behavior:
|
|
72
|
+
- macro: Use overlay if present, else base
|
|
73
|
+
- description: Use overlay if present, else base
|
|
74
|
+
- fallback: Use overlay if present, else base
|
|
75
|
+
- policy: Use overlay if present (must be complete), else base
|
|
76
|
+
|
|
77
|
+
Policy validation:
|
|
78
|
+
- If policy provided in overlay, must contain both on_collision and create_dirs
|
|
79
|
+
- Adds error to validation_info if incomplete
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
base: Complete base situation to start from
|
|
83
|
+
overlay_data: Partial situation dict from overlay YAML
|
|
84
|
+
field_path: Path for validation errors (e.g., "situations.save_file")
|
|
85
|
+
validation_info: Shared validation info
|
|
86
|
+
line_info: Line tracking from overlay YAML
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
New merged SituationTemplate
|
|
90
|
+
"""
|
|
91
|
+
# Start with base fields as dict
|
|
92
|
+
merged_data = base.model_dump()
|
|
93
|
+
|
|
94
|
+
# Apply overlay fields if present
|
|
95
|
+
if "macro" in overlay_data:
|
|
96
|
+
merged_data["macro"] = overlay_data["macro"]
|
|
97
|
+
|
|
98
|
+
if "description" in overlay_data:
|
|
99
|
+
merged_data["description"] = overlay_data["description"]
|
|
100
|
+
|
|
101
|
+
if "fallback" in overlay_data:
|
|
102
|
+
merged_data["fallback"] = overlay_data["fallback"]
|
|
103
|
+
|
|
104
|
+
# Policy must be complete if provided
|
|
105
|
+
if "policy" in overlay_data:
|
|
106
|
+
policy = overlay_data["policy"]
|
|
107
|
+
if not isinstance(policy, dict):
|
|
108
|
+
validation_info.add_error(
|
|
109
|
+
field_path=f"{field_path}.policy",
|
|
110
|
+
message="Policy must be a dict",
|
|
111
|
+
line_number=line_info.get_line(f"{field_path}.policy"),
|
|
112
|
+
)
|
|
113
|
+
elif "on_collision" not in policy or "create_dirs" not in policy:
|
|
114
|
+
validation_info.add_error(
|
|
115
|
+
field_path=f"{field_path}.policy",
|
|
116
|
+
message="Policy must include both on_collision and create_dirs",
|
|
117
|
+
line_number=line_info.get_line(f"{field_path}.policy"),
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
merged_data["policy"] = policy
|
|
121
|
+
|
|
122
|
+
# Build merged situation using model_validate
|
|
123
|
+
# Note: name field is not in overlay_data, use base.name
|
|
124
|
+
merged_data_with_name = {"name": base.name, **merged_data}
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
return SituationTemplate.model_validate(merged_data_with_name)
|
|
128
|
+
except ValidationError as e:
|
|
129
|
+
# Convert Pydantic validation errors to our validation_info format
|
|
130
|
+
for error in e.errors():
|
|
131
|
+
error_field_path = ".".join(str(loc) for loc in error["loc"])
|
|
132
|
+
full_field_path = f"{field_path}.{error_field_path}"
|
|
133
|
+
message = error["msg"]
|
|
134
|
+
line_number = line_info.get_line(full_field_path)
|
|
135
|
+
|
|
136
|
+
validation_info.add_error(
|
|
137
|
+
field_path=full_field_path,
|
|
138
|
+
message=message,
|
|
139
|
+
line_number=line_number,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Return base on validation error (fault-tolerant)
|
|
143
|
+
return base
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""Validation infrastructure for project templates."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from enum import StrEnum
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ProjectValidationStatus(StrEnum):
|
|
10
|
+
"""Status of project template validation.
|
|
11
|
+
|
|
12
|
+
Mirrors WorkflowStatus pattern from workflow_manager.py.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
GOOD = "GOOD" # No errors detected
|
|
16
|
+
FLAWED = "FLAWED" # Some errors, but recoverable
|
|
17
|
+
UNUSABLE = "UNUSABLE" # Errors make template unusable
|
|
18
|
+
MISSING = "MISSING" # File not found
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ProjectValidationProblemSeverity(StrEnum):
|
|
22
|
+
"""Severity level of a validation problem."""
|
|
23
|
+
|
|
24
|
+
ERROR = "error"
|
|
25
|
+
WARNING = "warning"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ProjectOverrideCategory(StrEnum):
|
|
29
|
+
"""Category of template override during merge."""
|
|
30
|
+
|
|
31
|
+
SITUATION = "situation"
|
|
32
|
+
DIRECTORY = "directory"
|
|
33
|
+
ENVIRONMENT = "environment"
|
|
34
|
+
METADATA = "metadata"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ProjectOverrideAction(StrEnum):
|
|
38
|
+
"""Action taken during template merge."""
|
|
39
|
+
|
|
40
|
+
MODIFIED = "modified" # Existed in base, changed in overlay
|
|
41
|
+
ADDED = "added" # New in overlay, not in base
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class ProjectValidationProblem:
|
|
46
|
+
"""Single validation problem with context."""
|
|
47
|
+
|
|
48
|
+
line_number: int | None
|
|
49
|
+
field_path: str # e.g., "situations.copy_external_file.macro"
|
|
50
|
+
message: str
|
|
51
|
+
severity: ProjectValidationProblemSeverity
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
class ProjectOverride:
|
|
56
|
+
"""Record of a template override during merge.
|
|
57
|
+
|
|
58
|
+
Tracks what was customized in the overlay without affecting validation status.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
category: ProjectOverrideCategory
|
|
62
|
+
name: str
|
|
63
|
+
action: ProjectOverrideAction
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass
|
|
67
|
+
class ProjectValidationInfo:
|
|
68
|
+
"""Validation result for a project template.
|
|
69
|
+
|
|
70
|
+
Shared across construction chain - problems are accumulated as
|
|
71
|
+
the template is built from YAML.
|
|
72
|
+
|
|
73
|
+
Overrides track what was customized during merge without affecting status.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
status: ProjectValidationStatus
|
|
77
|
+
problems: list[ProjectValidationProblem] = field(default_factory=list)
|
|
78
|
+
overrides: list[ProjectOverride] = field(default_factory=list)
|
|
79
|
+
|
|
80
|
+
def add_error(self, field_path: str, message: str, line_number: int | None = None) -> None:
|
|
81
|
+
"""Add an error to the problems list.
|
|
82
|
+
|
|
83
|
+
Automatically downgrades status to UNUSABLE unless already MISSING.
|
|
84
|
+
Early returns if status is MISSING.
|
|
85
|
+
"""
|
|
86
|
+
if self.status == ProjectValidationStatus.MISSING:
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
self.problems.append(
|
|
90
|
+
ProjectValidationProblem(
|
|
91
|
+
line_number=line_number,
|
|
92
|
+
field_path=field_path,
|
|
93
|
+
message=message,
|
|
94
|
+
severity=ProjectValidationProblemSeverity.ERROR,
|
|
95
|
+
),
|
|
96
|
+
)
|
|
97
|
+
self.status = ProjectValidationStatus.UNUSABLE
|
|
98
|
+
|
|
99
|
+
def add_warning(self, field_path: str, message: str, line_number: int | None = None) -> None:
|
|
100
|
+
"""Add a warning to the problems list.
|
|
101
|
+
|
|
102
|
+
Automatically downgrades status from GOOD to FLAWED if current status is GOOD.
|
|
103
|
+
Does not change status if already FLAWED or UNUSABLE.
|
|
104
|
+
Early returns if status is MISSING.
|
|
105
|
+
"""
|
|
106
|
+
if self.status == ProjectValidationStatus.MISSING:
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
self.problems.append(
|
|
110
|
+
ProjectValidationProblem(
|
|
111
|
+
line_number=line_number,
|
|
112
|
+
field_path=field_path,
|
|
113
|
+
message=message,
|
|
114
|
+
severity=ProjectValidationProblemSeverity.WARNING,
|
|
115
|
+
),
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if self.status == ProjectValidationStatus.GOOD:
|
|
119
|
+
self.status = ProjectValidationStatus.FLAWED
|
|
120
|
+
|
|
121
|
+
def add_override(
|
|
122
|
+
self,
|
|
123
|
+
category: ProjectOverrideCategory,
|
|
124
|
+
name: str,
|
|
125
|
+
action: ProjectOverrideAction,
|
|
126
|
+
) -> None:
|
|
127
|
+
"""Record an override without affecting validation status.
|
|
128
|
+
|
|
129
|
+
Used during template merge to track what was customized in the overlay.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
category: Type of override (situation, directory, environment, metadata)
|
|
133
|
+
name: Name of the item that was overridden
|
|
134
|
+
action: Whether it was modified (existed in base) or added (new in overlay)
|
|
135
|
+
"""
|
|
136
|
+
self.overrides.append(ProjectOverride(category=category, name=name, action=action))
|
|
137
|
+
|
|
138
|
+
def is_usable(self) -> bool:
|
|
139
|
+
"""Check if template can be used (GOOD or FLAWED status)."""
|
|
140
|
+
return self.status in (ProjectValidationStatus.GOOD, ProjectValidationStatus.FLAWED)
|
|
@@ -787,7 +787,7 @@ class DeprecationMessage(ParameterMessage):
|
|
|
787
787
|
kwargs.setdefault("traits", {})
|
|
788
788
|
kwargs["traits"][Button(label=button_text, icon="plus", variant="secondary", on_click=migrate_function)] = None
|
|
789
789
|
|
|
790
|
-
super().__init__(value=value, **kwargs)
|
|
790
|
+
super().__init__(value=value, button_text=button_text, **kwargs)
|
|
791
791
|
|
|
792
792
|
def to_dict(self) -> dict:
|
|
793
793
|
"""Override to_dict to use element_type instead of class name.
|
|
@@ -804,9 +804,18 @@ class DeprecationMessage(ParameterMessage):
|
|
|
804
804
|
class ParameterGroup(BaseNodeElement, UIOptionsMixin):
|
|
805
805
|
"""UI element for a group of parameters."""
|
|
806
806
|
|
|
807
|
-
def __init__(self, name: str, ui_options: dict | None = None, **kwargs):
|
|
807
|
+
def __init__(self, name: str, ui_options: dict | None = None, *, collapsed: bool = False, **kwargs):
|
|
808
808
|
super().__init__(name=name, **kwargs)
|
|
809
|
-
|
|
809
|
+
if ui_options is None:
|
|
810
|
+
ui_options = {}
|
|
811
|
+
else:
|
|
812
|
+
ui_options = ui_options.copy()
|
|
813
|
+
|
|
814
|
+
# Add collapsed to ui_options if it's True
|
|
815
|
+
if collapsed:
|
|
816
|
+
ui_options["collapsed"] = collapsed
|
|
817
|
+
|
|
818
|
+
self._ui_options = ui_options
|
|
810
819
|
|
|
811
820
|
@property
|
|
812
821
|
def ui_options(self) -> dict:
|
|
@@ -817,6 +826,30 @@ class ParameterGroup(BaseNodeElement, UIOptionsMixin):
|
|
|
817
826
|
def ui_options(self, value: dict) -> None:
|
|
818
827
|
self._ui_options = value
|
|
819
828
|
|
|
829
|
+
@property
|
|
830
|
+
def collapsed(self) -> bool:
|
|
831
|
+
"""Get whether the parameter group is collapsed.
|
|
832
|
+
|
|
833
|
+
Returns:
|
|
834
|
+
True if the group is collapsed, False otherwise
|
|
835
|
+
"""
|
|
836
|
+
return self._ui_options.get("collapsed", False)
|
|
837
|
+
|
|
838
|
+
@collapsed.setter
|
|
839
|
+
@BaseNodeElement.emits_update_on_write
|
|
840
|
+
def collapsed(self, value: bool) -> None:
|
|
841
|
+
"""Set whether the parameter group is collapsed.
|
|
842
|
+
|
|
843
|
+
Args:
|
|
844
|
+
value: Whether to collapse the group
|
|
845
|
+
"""
|
|
846
|
+
if value:
|
|
847
|
+
self.update_ui_options_key("collapsed", value)
|
|
848
|
+
else:
|
|
849
|
+
ui_options = self._ui_options.copy()
|
|
850
|
+
ui_options.pop("collapsed", None)
|
|
851
|
+
self._ui_options = ui_options
|
|
852
|
+
|
|
820
853
|
def to_dict(self) -> dict[str, Any]:
|
|
821
854
|
"""Returns a nested dictionary representation of this node and its children.
|
|
822
855
|
|
|
@@ -793,13 +793,15 @@ class BaseNode(ABC):
|
|
|
793
793
|
|
|
794
794
|
def get_parameter_value(self, param_name: str) -> Any:
|
|
795
795
|
param = self.get_parameter_by_name(param_name)
|
|
796
|
-
if param
|
|
796
|
+
if param is None:
|
|
797
|
+
return None
|
|
798
|
+
if isinstance(param, ParameterContainer):
|
|
797
799
|
value = handle_container_parameter(self, param)
|
|
798
800
|
if value is not None:
|
|
799
801
|
return value
|
|
800
802
|
if param_name in self.parameter_values:
|
|
801
803
|
return self.parameter_values[param_name]
|
|
802
|
-
return param.default_value
|
|
804
|
+
return param.default_value
|
|
803
805
|
|
|
804
806
|
def get_parameter_list_value(self, param: str) -> list:
|
|
805
807
|
"""Flattens the given param from self.params into a single list.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from griptape_nodes.exe_types.core_types import Parameter, ParameterMode
|
|
4
|
+
from griptape_nodes.exe_types.node_types import BaseNode
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ProgressBarComponent:
|
|
10
|
+
def __init__(self, node: BaseNode):
|
|
11
|
+
self._node = node
|
|
12
|
+
self._total_steps = 0
|
|
13
|
+
self._current_step = 0
|
|
14
|
+
|
|
15
|
+
def add_property_parameters(self) -> None:
|
|
16
|
+
self._node.add_parameter(
|
|
17
|
+
Parameter(
|
|
18
|
+
name="progress",
|
|
19
|
+
output_type="float",
|
|
20
|
+
allowed_modes={ParameterMode.PROPERTY},
|
|
21
|
+
tooltip="Progress bar showing completion (0.0 to 1.0)",
|
|
22
|
+
ui_options={"progress_bar": True},
|
|
23
|
+
settable=False,
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def initialize(self, total_steps: int) -> None:
|
|
28
|
+
"""Initialize the progress bar with a total number of steps."""
|
|
29
|
+
self._total_steps = total_steps
|
|
30
|
+
self._current_step = 0
|
|
31
|
+
self._update_progress()
|
|
32
|
+
|
|
33
|
+
def increment(self, steps: int = 1) -> None:
|
|
34
|
+
"""Increment the progress by the specified number of steps."""
|
|
35
|
+
self._current_step += steps
|
|
36
|
+
if self._current_step > self._total_steps:
|
|
37
|
+
logger.warning(
|
|
38
|
+
"Current step %i exceeds total steps %i. Progress will not exceed 100%.",
|
|
39
|
+
self._current_step,
|
|
40
|
+
self._total_steps,
|
|
41
|
+
)
|
|
42
|
+
self._update_progress()
|
|
43
|
+
|
|
44
|
+
def reset(self) -> None:
|
|
45
|
+
"""Reset the progress bar to 0."""
|
|
46
|
+
self._current_step = 0
|
|
47
|
+
self._total_steps = 0
|
|
48
|
+
self._update_progress()
|
|
49
|
+
|
|
50
|
+
def _update_progress(self) -> None:
|
|
51
|
+
"""Update the progress parameter based on current step and total steps."""
|
|
52
|
+
if self._total_steps <= 0:
|
|
53
|
+
progress_value = 0.0
|
|
54
|
+
else:
|
|
55
|
+
progress_value = min(1.0, self._current_step / self._total_steps)
|
|
56
|
+
|
|
57
|
+
self._node.publish_update_to_parameter("progress", progress_value)
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""ParameterAudio component for audio inputs with enhanced UI options."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from griptape_nodes.exe_types.core_types import Parameter, ParameterMode, Trait
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ParameterAudio(Parameter):
|
|
10
|
+
"""A specialized Parameter class for audio inputs with enhanced UI options.
|
|
11
|
+
|
|
12
|
+
This class provides a convenient way to create audio parameters with common
|
|
13
|
+
UI customizations like file browser, microphone capture, and audio editing.
|
|
14
|
+
It exposes these UI options as direct properties for easy runtime modification.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
param = ParameterAudio(
|
|
18
|
+
name="input_audio",
|
|
19
|
+
tooltip="Select an audio file",
|
|
20
|
+
clickable_file_browser=True,
|
|
21
|
+
microphone_capture_audio=True,
|
|
22
|
+
edit_audio=True
|
|
23
|
+
)
|
|
24
|
+
param.pulse_on_run = True # Change UI options at runtime
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__( # noqa: PLR0913
|
|
28
|
+
self,
|
|
29
|
+
name: str,
|
|
30
|
+
tooltip: str | None = None,
|
|
31
|
+
*,
|
|
32
|
+
type: str = "AudioUrlArtifact", # noqa: A002, ARG002
|
|
33
|
+
input_types: list[str] | None = None, # noqa: ARG002
|
|
34
|
+
output_type: str = "AudioUrlArtifact", # noqa: ARG002
|
|
35
|
+
default_value: Any = None,
|
|
36
|
+
tooltip_as_input: str | None = None,
|
|
37
|
+
tooltip_as_property: str | None = None,
|
|
38
|
+
tooltip_as_output: str | None = None,
|
|
39
|
+
allowed_modes: set[ParameterMode] | None = None,
|
|
40
|
+
traits: set[type[Trait] | Trait] | None = None,
|
|
41
|
+
converters: list[Callable[[Any], Any]] | None = None,
|
|
42
|
+
validators: list[Callable[[Parameter, Any], None]] | None = None,
|
|
43
|
+
ui_options: dict | None = None,
|
|
44
|
+
pulse_on_run: bool = False,
|
|
45
|
+
clickable_file_browser: bool = True,
|
|
46
|
+
microphone_capture_audio: bool = False,
|
|
47
|
+
edit_audio: bool = False,
|
|
48
|
+
accept_any: bool = True,
|
|
49
|
+
hide: bool = False,
|
|
50
|
+
hide_label: bool = False,
|
|
51
|
+
hide_property: bool = False,
|
|
52
|
+
allow_input: bool = True,
|
|
53
|
+
allow_property: bool = True,
|
|
54
|
+
allow_output: bool = True,
|
|
55
|
+
settable: bool = True,
|
|
56
|
+
serializable: bool = True,
|
|
57
|
+
user_defined: bool = False,
|
|
58
|
+
element_id: str | None = None,
|
|
59
|
+
element_type: str | None = None,
|
|
60
|
+
parent_container_name: str | None = None,
|
|
61
|
+
) -> None:
|
|
62
|
+
"""Initialize an audio parameter with enhanced UI options.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
name: Parameter name
|
|
66
|
+
tooltip: Parameter tooltip
|
|
67
|
+
type: Parameter type (ignored, always "AudioUrlArtifact" for ParameterAudio)
|
|
68
|
+
input_types: Allowed input types (ignored, set based on accept_any)
|
|
69
|
+
output_type: Output type (ignored, always "AudioUrlArtifact" for ParameterAudio)
|
|
70
|
+
default_value: Default parameter value
|
|
71
|
+
tooltip_as_input: Tooltip for input mode
|
|
72
|
+
tooltip_as_property: Tooltip for property mode
|
|
73
|
+
tooltip_as_output: Tooltip for output mode
|
|
74
|
+
allowed_modes: Allowed parameter modes
|
|
75
|
+
traits: Parameter traits
|
|
76
|
+
converters: Parameter converters
|
|
77
|
+
validators: Parameter validators
|
|
78
|
+
ui_options: Dictionary of UI options
|
|
79
|
+
pulse_on_run: Whether to pulse the parameter on run
|
|
80
|
+
clickable_file_browser: Whether to show clickable file browser
|
|
81
|
+
microphone_capture_audio: Whether to enable microphone capture
|
|
82
|
+
edit_audio: Whether to enable audio editing functionality
|
|
83
|
+
accept_any: Whether to accept any input type and convert to audio (default: True)
|
|
84
|
+
hide: Whether to hide the entire parameter
|
|
85
|
+
hide_label: Whether to hide the parameter label
|
|
86
|
+
hide_property: Whether to hide the parameter in property mode
|
|
87
|
+
allow_input: Whether to allow input mode
|
|
88
|
+
allow_property: Whether to allow property mode
|
|
89
|
+
allow_output: Whether to allow output mode
|
|
90
|
+
settable: Whether the parameter is settable
|
|
91
|
+
serializable: Whether the parameter is serializable
|
|
92
|
+
user_defined: Whether the parameter is user-defined
|
|
93
|
+
element_id: Element ID
|
|
94
|
+
element_type: Element type
|
|
95
|
+
parent_container_name: Name of parent container
|
|
96
|
+
"""
|
|
97
|
+
# Build ui_options dictionary from the provided UI-specific parameters
|
|
98
|
+
if ui_options is None:
|
|
99
|
+
ui_options = {}
|
|
100
|
+
else:
|
|
101
|
+
ui_options = ui_options.copy()
|
|
102
|
+
|
|
103
|
+
# Add audio-specific UI options if they have values
|
|
104
|
+
if pulse_on_run:
|
|
105
|
+
ui_options["pulse_on_run"] = pulse_on_run
|
|
106
|
+
if clickable_file_browser:
|
|
107
|
+
ui_options["clickable_file_browser"] = clickable_file_browser
|
|
108
|
+
if microphone_capture_audio:
|
|
109
|
+
ui_options["microphone_capture_audio"] = microphone_capture_audio
|
|
110
|
+
if edit_audio:
|
|
111
|
+
ui_options["edit_audio"] = edit_audio
|
|
112
|
+
|
|
113
|
+
# Auto-disable clickable_file_browser if neither input nor property modes are allowed
|
|
114
|
+
if not allow_input and not allow_property and clickable_file_browser:
|
|
115
|
+
ui_options.pop("clickable_file_browser", None)
|
|
116
|
+
|
|
117
|
+
# Set up input types based on accept_any setting
|
|
118
|
+
if accept_any:
|
|
119
|
+
final_input_types = ["any"]
|
|
120
|
+
else:
|
|
121
|
+
final_input_types = ["AudioUrlArtifact"]
|
|
122
|
+
|
|
123
|
+
# Call parent with explicit parameters, following ControlParameter pattern
|
|
124
|
+
super().__init__(
|
|
125
|
+
name=name,
|
|
126
|
+
tooltip=tooltip,
|
|
127
|
+
type="AudioUrlArtifact", # Always an AudioUrlArtifact type for ParameterAudio
|
|
128
|
+
input_types=final_input_types,
|
|
129
|
+
output_type="AudioUrlArtifact", # Always output as AudioUrlArtifact
|
|
130
|
+
default_value=default_value,
|
|
131
|
+
tooltip_as_input=tooltip_as_input,
|
|
132
|
+
tooltip_as_property=tooltip_as_property,
|
|
133
|
+
tooltip_as_output=tooltip_as_output,
|
|
134
|
+
allowed_modes=allowed_modes,
|
|
135
|
+
traits=traits,
|
|
136
|
+
converters=converters,
|
|
137
|
+
validators=validators,
|
|
138
|
+
ui_options=ui_options,
|
|
139
|
+
hide=hide,
|
|
140
|
+
hide_label=hide_label,
|
|
141
|
+
hide_property=hide_property,
|
|
142
|
+
allow_input=allow_input,
|
|
143
|
+
allow_property=allow_property,
|
|
144
|
+
allow_output=allow_output,
|
|
145
|
+
settable=settable,
|
|
146
|
+
serializable=serializable,
|
|
147
|
+
user_defined=user_defined,
|
|
148
|
+
element_id=element_id,
|
|
149
|
+
element_type=element_type,
|
|
150
|
+
parent_container_name=parent_container_name,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def pulse_on_run(self) -> bool:
|
|
155
|
+
"""Get whether pulse on run is enabled.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
True if pulse on run is enabled, False otherwise
|
|
159
|
+
"""
|
|
160
|
+
return self.ui_options.get("pulse_on_run", False)
|
|
161
|
+
|
|
162
|
+
@pulse_on_run.setter
|
|
163
|
+
def pulse_on_run(self, value: bool) -> None:
|
|
164
|
+
"""Set whether pulse on run is enabled.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
value: Whether to enable pulse on run
|
|
168
|
+
"""
|
|
169
|
+
if value:
|
|
170
|
+
self.update_ui_options_key("pulse_on_run", value)
|
|
171
|
+
else:
|
|
172
|
+
ui_options = self.ui_options.copy()
|
|
173
|
+
ui_options.pop("pulse_on_run", None)
|
|
174
|
+
self.ui_options = ui_options
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def clickable_file_browser(self) -> bool:
|
|
178
|
+
"""Get whether clickable file browser is enabled.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
True if clickable file browser is enabled, False otherwise
|
|
182
|
+
"""
|
|
183
|
+
return self.ui_options.get("clickable_file_browser", False)
|
|
184
|
+
|
|
185
|
+
@clickable_file_browser.setter
|
|
186
|
+
def clickable_file_browser(self, value: bool) -> None:
|
|
187
|
+
"""Set whether clickable file browser is enabled.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
value: Whether to enable clickable file browser
|
|
191
|
+
"""
|
|
192
|
+
if value:
|
|
193
|
+
self.update_ui_options_key("clickable_file_browser", value)
|
|
194
|
+
else:
|
|
195
|
+
ui_options = self.ui_options.copy()
|
|
196
|
+
ui_options.pop("clickable_file_browser", None)
|
|
197
|
+
self.ui_options = ui_options
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def microphone_capture_audio(self) -> bool:
|
|
201
|
+
"""Get whether microphone capture audio is enabled.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
True if microphone capture audio is enabled, False otherwise
|
|
205
|
+
"""
|
|
206
|
+
return self.ui_options.get("microphone_capture_audio", False)
|
|
207
|
+
|
|
208
|
+
@microphone_capture_audio.setter
|
|
209
|
+
def microphone_capture_audio(self, value: bool) -> None:
|
|
210
|
+
"""Set whether microphone capture audio is enabled.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
value: Whether to enable microphone capture audio
|
|
214
|
+
"""
|
|
215
|
+
if value:
|
|
216
|
+
self.update_ui_options_key("microphone_capture_audio", value)
|
|
217
|
+
else:
|
|
218
|
+
ui_options = self.ui_options.copy()
|
|
219
|
+
ui_options.pop("microphone_capture_audio", None)
|
|
220
|
+
self.ui_options = ui_options
|
|
221
|
+
|
|
222
|
+
@property
|
|
223
|
+
def edit_audio(self) -> bool:
|
|
224
|
+
"""Get whether edit audio is enabled.
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
True if edit audio is enabled, False otherwise
|
|
228
|
+
"""
|
|
229
|
+
return self.ui_options.get("edit_audio", False)
|
|
230
|
+
|
|
231
|
+
@edit_audio.setter
|
|
232
|
+
def edit_audio(self, value: bool) -> None:
|
|
233
|
+
"""Set whether edit audio is enabled.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
value: Whether to enable edit audio
|
|
237
|
+
"""
|
|
238
|
+
if value:
|
|
239
|
+
self.update_ui_options_key("edit_audio", value)
|
|
240
|
+
else:
|
|
241
|
+
ui_options = self.ui_options.copy()
|
|
242
|
+
ui_options.pop("edit_audio", None)
|
|
243
|
+
self.ui_options = ui_options
|