griptape_nodes 0.1.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.
Potentially problematic release.
This version of griptape_nodes might be problematic. Click here for more details.
- __init__.py +0 -0
- griptape_nodes/__init__.py +0 -0
- griptape_nodes/exe_types/__init__.py +1 -0
- griptape_nodes/exe_types/core_types.py +244 -0
- griptape_nodes/exe_types/data_types.py +363 -0
- griptape_nodes/exe_types/flow_node.py +31 -0
- griptape_nodes/exe_types/node_types.py +271 -0
- griptape_nodes/exe_types/type_validator.py +913 -0
- griptape_nodes/machines/__init__.py +1 -0
- griptape_nodes/machines/control_flow.py +170 -0
- griptape_nodes/machines/fsm.py +55 -0
- griptape_nodes/machines/node_resolution.py +290 -0
- griptape_nodes/node_library/__init__.py +1 -0
- griptape_nodes/node_library/library_registry.py +237 -0
- griptape_nodes/node_library/script_registry.py +100 -0
- griptape_nodes/nodes/__init__.py +1 -0
- griptape_nodes/nodes/agent.py +132 -0
- griptape_nodes/nodes/comfy_nodes.py +312 -0
- griptape_nodes/nodes/create_dict.py +90 -0
- griptape_nodes/nodes/create_image.py +131 -0
- griptape_nodes/nodes/create_string.py +32 -0
- griptape_nodes/nodes/drivers/__init__.py +1 -0
- griptape_nodes/nodes/drivers/anthropic_prompt_driver.py +60 -0
- griptape_nodes/nodes/drivers/audio_transcription_drivers.py +31 -0
- griptape_nodes/nodes/drivers/azure_openai_chat_prompt_driver.py +66 -0
- griptape_nodes/nodes/drivers/azure_openai_image_driver.py +130 -0
- griptape_nodes/nodes/drivers/base_driver.py +51 -0
- griptape_nodes/nodes/drivers/base_embedding_driver.py +27 -0
- griptape_nodes/nodes/drivers/base_image_driver.py +63 -0
- griptape_nodes/nodes/drivers/base_prompt_driver.py +122 -0
- griptape_nodes/nodes/drivers/cohere_prompt_driver.py +50 -0
- griptape_nodes/nodes/drivers/griptape_cloud_prompt_driver.py +66 -0
- griptape_nodes/nodes/drivers/ollama_embedding_driver.py +69 -0
- griptape_nodes/nodes/drivers/ollama_prompt_driver.py +43 -0
- griptape_nodes/nodes/drivers/openai_chat_prompt_driver.py +67 -0
- griptape_nodes/nodes/drivers/openai_image_driver.py +8 -0
- griptape_nodes/nodes/drivers/web_search_drivers.py +27 -0
- griptape_nodes/nodes/end_flow.py +17 -0
- griptape_nodes/nodes/key_value_pair.py +60 -0
- griptape_nodes/nodes/load_image.py +25 -0
- griptape_nodes/nodes/load_text.py +72 -0
- griptape_nodes/nodes/merge_dict.py +65 -0
- griptape_nodes/nodes/merge_texts.py +73 -0
- griptape_nodes/nodes/rules/__init__.py +1 -0
- griptape_nodes/nodes/rules/base_ruleset.py +55 -0
- griptape_nodes/nodes/save_text.py +70 -0
- griptape_nodes/nodes/simple_agent.py +75 -0
- griptape_nodes/nodes/start_flow.py +17 -0
- griptape_nodes/nodes/string_list.py +47 -0
- griptape_nodes/nodes/tools/__init__.py +1 -0
- griptape_nodes/nodes/tools/audio_transcription_tool.py +19 -0
- griptape_nodes/nodes/tools/base_tool.py +36 -0
- griptape_nodes/nodes/tools/calculator_tool.py +13 -0
- griptape_nodes/nodes/tools/convert_agent_to_tool.py +30 -0
- griptape_nodes/nodes/tools/date_time_tool.py +13 -0
- griptape_nodes/nodes/tools/extraction_tool.py +34 -0
- griptape_nodes/nodes/tools/file_manager_tool.py +13 -0
- griptape_nodes/nodes/tools/prompt_summary_tool.py +33 -0
- griptape_nodes/nodes/tools/query_tool.py +83 -0
- griptape_nodes/nodes/tools/rag_tool.py +26 -0
- griptape_nodes/nodes/tools/tool_list.py +59 -0
- griptape_nodes/nodes/tools/tools.py +84 -0
- griptape_nodes/nodes/tools/vector_store_tool.py +63 -0
- griptape_nodes/nodes/tools/web_scraper_tool.py +22 -0
- griptape_nodes/nodes/tools/web_search_tool.py +25 -0
- griptape_nodes/nodes/utilities.py +222 -0
- griptape_nodes/nodes/utils/__init__.py +1 -0
- griptape_nodes/nodes/utils/anthropic_utils.py +31 -0
- griptape_nodes/retained_mode/__init__.py +1 -0
- griptape_nodes/retained_mode/events/__init__.py +1 -0
- griptape_nodes/retained_mode/events/app_events.py +10 -0
- griptape_nodes/retained_mode/events/arbitrary_python_events.py +30 -0
- griptape_nodes/retained_mode/events/base_events.py +493 -0
- griptape_nodes/retained_mode/events/config_events.py +83 -0
- griptape_nodes/retained_mode/events/connection_events.py +83 -0
- griptape_nodes/retained_mode/events/execution_events.py +259 -0
- griptape_nodes/retained_mode/events/flow_events.py +83 -0
- griptape_nodes/retained_mode/events/generate_event_schemas.py +18 -0
- griptape_nodes/retained_mode/events/library_events.py +118 -0
- griptape_nodes/retained_mode/events/node_events.py +121 -0
- griptape_nodes/retained_mode/events/object_events.py +28 -0
- griptape_nodes/retained_mode/events/os_events.py +26 -0
- griptape_nodes/retained_mode/events/parameter_events.py +198 -0
- griptape_nodes/retained_mode/events/payload_registry.py +59 -0
- griptape_nodes/retained_mode/events/script_events.py +137 -0
- griptape_nodes/retained_mode/griptape_nodes.py +2619 -0
- griptape_nodes/retained_mode/managers/__init__.py +1 -0
- griptape_nodes/retained_mode/managers/config_manager.py +311 -0
- griptape_nodes/retained_mode/managers/connection_manager.py +188 -0
- griptape_nodes/retained_mode/managers/event_manager.py +113 -0
- griptape_nodes/retained_mode/managers/operation_manager.py +388 -0
- griptape_nodes/retained_mode/managers/os_manager.py +87 -0
- griptape_nodes/retained_mode/retained_mode.py +787 -0
- griptape_nodes-0.1.0.dist-info/METADATA +490 -0
- griptape_nodes-0.1.0.dist-info/RECORD +98 -0
- griptape_nodes-0.1.0.dist-info/WHEEL +5 -0
- griptape_nodes-0.1.0.dist-info/licenses/LICENSE +201 -0
- griptape_nodes-0.1.0.dist-info/top_level.txt +2 -0
__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Executable types package."""
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from enum import Enum, auto
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from exe_types.type_validator import TypeValidator
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Types of Modes provided for Parameters
|
|
13
|
+
class ParameterMode(Enum):
|
|
14
|
+
OUTPUT = auto()
|
|
15
|
+
INPUT = auto()
|
|
16
|
+
PROPERTY = auto()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# I'm a special way to say my Type is for Control flow.
|
|
20
|
+
class ParameterControlType:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Keys and values for the UI_Options schema.
|
|
25
|
+
@dataclass
|
|
26
|
+
class ParameterUIOptions:
|
|
27
|
+
@dataclass
|
|
28
|
+
class Option(ABC): # noqa: B024
|
|
29
|
+
def __eq__(self, other) -> bool:
|
|
30
|
+
if not isinstance(other, type(self)):
|
|
31
|
+
return False
|
|
32
|
+
self_dict = self.__dict__
|
|
33
|
+
other_dict = self.__dict__
|
|
34
|
+
return self_dict == other_dict
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class TypeOption(Option, ABC):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class ContainerOption(Option, ABC):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class StringType(TypeOption):
|
|
46
|
+
multiline: bool | None = None
|
|
47
|
+
markdown: bool | None = None
|
|
48
|
+
placeholder_text: str | None = None
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class BooleanType(TypeOption):
|
|
52
|
+
on_label: str | None = None
|
|
53
|
+
off_label: str | None = None
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class SliderWidget:
|
|
57
|
+
min_val: Any
|
|
58
|
+
max_val: Any
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class NumberType(TypeOption):
|
|
62
|
+
slider: ParameterUIOptions.SliderWidget | None = None
|
|
63
|
+
step: Any | None = None
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class SimpleDropdown(Option):
|
|
67
|
+
enum_choices: list[str] | None = None
|
|
68
|
+
placeholder_text: str | None = None
|
|
69
|
+
|
|
70
|
+
@dataclass
|
|
71
|
+
class FancyDropdown(Option):
|
|
72
|
+
enum_dict: dict[Any, Any] | None = None
|
|
73
|
+
|
|
74
|
+
@dataclass
|
|
75
|
+
class ImageType(TypeOption):
|
|
76
|
+
clickable_file_browser: bool | None = None
|
|
77
|
+
expander: bool | None = None
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class VideoType(TypeOption):
|
|
81
|
+
clickable_file_browser: bool | None = None
|
|
82
|
+
play_button: bool | None = None
|
|
83
|
+
playback_range: bool | None = None
|
|
84
|
+
expander: bool | None = None
|
|
85
|
+
|
|
86
|
+
@dataclass
|
|
87
|
+
class AudioType(TypeOption):
|
|
88
|
+
clickable_file_browser: bool | None = None
|
|
89
|
+
|
|
90
|
+
@dataclass
|
|
91
|
+
class PropertyArrayType(ContainerOption, TypeOption):
|
|
92
|
+
property_type_option: ParameterUIOptions.TypeOption | None = None
|
|
93
|
+
stacked: bool | None = None
|
|
94
|
+
color: bool | None = None
|
|
95
|
+
|
|
96
|
+
@dataclass
|
|
97
|
+
class ListContainer(ContainerOption):
|
|
98
|
+
element_type_option: ParameterUIOptions.TypeOption | None = None
|
|
99
|
+
stacked: bool | None = None
|
|
100
|
+
|
|
101
|
+
string_type_options: StringType | None = None
|
|
102
|
+
boolean_type_options: BooleanType | None = None
|
|
103
|
+
number_type_options: NumberType | None = None
|
|
104
|
+
simple_dropdown_options: SimpleDropdown | None = None
|
|
105
|
+
fancy_dropdown_options: FancyDropdown | None = None
|
|
106
|
+
image_type_options: ImageType | None = None
|
|
107
|
+
video_type_options: VideoType | None = None
|
|
108
|
+
audio_type_options: AudioType | None = None
|
|
109
|
+
property_array_type_options: PropertyArrayType | None = None
|
|
110
|
+
list_container_options: ListContainer | None = None
|
|
111
|
+
|
|
112
|
+
def __eq__(self, other) -> bool:
|
|
113
|
+
if not isinstance(other, ParameterUIOptions):
|
|
114
|
+
return False
|
|
115
|
+
self_dict = self.__dict__
|
|
116
|
+
other_dict = other.__dict__
|
|
117
|
+
return self_dict == other_dict
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@dataclass
|
|
121
|
+
class Parameter:
|
|
122
|
+
name: str # must be unique from other parameters in Node
|
|
123
|
+
allowed_types: list[str]
|
|
124
|
+
tooltip: str # Default tooltip
|
|
125
|
+
default_value: Any = None
|
|
126
|
+
tooltip_as_input: str | None = None
|
|
127
|
+
tooltip_as_property: str | None = None
|
|
128
|
+
tooltip_as_output: str | None = None
|
|
129
|
+
settable: bool = True
|
|
130
|
+
user_defined: bool = False
|
|
131
|
+
validations: bool = True # This exists to accelerate early prototyping and node work. We shouldn't be refactoring this piecemeal, but come back to it with a larger clearer set of needs
|
|
132
|
+
allowed_modes: set = field(
|
|
133
|
+
default_factory=lambda: {
|
|
134
|
+
ParameterMode.OUTPUT,
|
|
135
|
+
ParameterMode.INPUT,
|
|
136
|
+
ParameterMode.PROPERTY,
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
options: list[Any] | None = None
|
|
140
|
+
ui_options: ParameterUIOptions | None = None
|
|
141
|
+
next: Parameter | None = None
|
|
142
|
+
prev: Parameter | None = None
|
|
143
|
+
|
|
144
|
+
def is_type_allowed(self, type_as_str: str) -> bool:
|
|
145
|
+
# Skip validation if validations is False
|
|
146
|
+
if not self.validations:
|
|
147
|
+
return True
|
|
148
|
+
|
|
149
|
+
# Original code continues here...
|
|
150
|
+
# Can't just do a string compare as we'll whiff on things like "list" not matching "List"
|
|
151
|
+
test_type = TypeValidator.convert_to_type(type_as_str)
|
|
152
|
+
for allowed_type_str in self.allowed_types:
|
|
153
|
+
allowed_type = TypeValidator.convert_to_type(allowed_type_str)
|
|
154
|
+
if allowed_type == test_type:
|
|
155
|
+
return True
|
|
156
|
+
return False
|
|
157
|
+
|
|
158
|
+
def is_value_allowed(self, value: Any) -> bool:
|
|
159
|
+
if not self.validations: # TEMP FOR DEV - JO
|
|
160
|
+
return True # TEMP FOR DEV - JO
|
|
161
|
+
|
|
162
|
+
for allowed_type_str in self.allowed_types:
|
|
163
|
+
if TypeValidator.is_instance(value, allowed_type_str):
|
|
164
|
+
return True
|
|
165
|
+
try:
|
|
166
|
+
print(f"Value not allowed {self.name}: {value=}, {allowed_type_str=}")
|
|
167
|
+
except Exception:
|
|
168
|
+
print(f"Value not allowed {self.name}: [error], {allowed_type_str=}")
|
|
169
|
+
return False
|
|
170
|
+
|
|
171
|
+
def set_default_value(self, value: Any) -> None:
|
|
172
|
+
if not self.validations or self.is_value_allowed(value):
|
|
173
|
+
self.default_value = value
|
|
174
|
+
else:
|
|
175
|
+
errormsg = "Type does not match allowed value types"
|
|
176
|
+
raise TypeError(errormsg)
|
|
177
|
+
|
|
178
|
+
def get_mode(self) -> set:
|
|
179
|
+
return self.allowed_modes
|
|
180
|
+
|
|
181
|
+
def add_mode(self, mode: ParameterMode) -> None:
|
|
182
|
+
self.allowed_modes.add(mode)
|
|
183
|
+
|
|
184
|
+
def remove_mode(self, mode: ParameterMode) -> None:
|
|
185
|
+
self.allowed_modes.remove(mode)
|
|
186
|
+
|
|
187
|
+
def copy(self) -> Parameter:
|
|
188
|
+
param = deepcopy(self)
|
|
189
|
+
param.next = None
|
|
190
|
+
param.prev = None
|
|
191
|
+
return param
|
|
192
|
+
|
|
193
|
+
# intentionally not overwriting __eq__ because I want to return a dict not true or false
|
|
194
|
+
def equals(self, other: Parameter) -> dict:
|
|
195
|
+
self_dict = self.__dict__
|
|
196
|
+
other_dict = other.__dict__
|
|
197
|
+
self_dict.pop("next", None)
|
|
198
|
+
self_dict.pop("prev", None)
|
|
199
|
+
other_dict.pop("next", None)
|
|
200
|
+
other_dict.pop("prev", None)
|
|
201
|
+
if self_dict == other_dict:
|
|
202
|
+
return {}
|
|
203
|
+
differences = {}
|
|
204
|
+
for key, self_value in self_dict.items():
|
|
205
|
+
other_value = other_dict.get(key)
|
|
206
|
+
if isinstance(other_value, ParameterUIOptions) and other_value != self_value:
|
|
207
|
+
# check if these two objects are equal
|
|
208
|
+
differences["key"] = other_value
|
|
209
|
+
if isinstance(self_value, (list, set)) and isinstance(other_value, (list, set)):
|
|
210
|
+
if set(self_value) != set(other_value):
|
|
211
|
+
differences[key] = other_value
|
|
212
|
+
elif self_value != other_value:
|
|
213
|
+
differences[key] = other_value
|
|
214
|
+
return differences
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
# Convenience classes to reduce boilerplate in node definitions
|
|
218
|
+
@dataclass(kw_only=True)
|
|
219
|
+
class ControlParameter(Parameter, ABC):
|
|
220
|
+
allowed_types: list[str] = field(default_factory=lambda: [ParameterControlType.__name__])
|
|
221
|
+
default_value: Any = None
|
|
222
|
+
settable: bool = False
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@dataclass(kw_only=True)
|
|
226
|
+
class ControlParameter_Input(ControlParameter):
|
|
227
|
+
name: str = "exec_in"
|
|
228
|
+
tooltip: str = "Connection from previous node in the execution chain"
|
|
229
|
+
allowed_modes: set = field(
|
|
230
|
+
default_factory=lambda: {
|
|
231
|
+
ParameterMode.INPUT,
|
|
232
|
+
}
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
@dataclass
|
|
237
|
+
class ControlParameter_Output(ControlParameter):
|
|
238
|
+
name: str = "exec_out"
|
|
239
|
+
tooltip: str = "Connection to the next node in the execution chain"
|
|
240
|
+
allowed_modes: set = field(
|
|
241
|
+
default_factory=lambda: {
|
|
242
|
+
ParameterMode.OUTPUT,
|
|
243
|
+
}
|
|
244
|
+
)
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from typing import TYPE_CHECKING, Generic, TypeVar
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from collections.abc import Iterator
|
|
9
|
+
|
|
10
|
+
T = TypeVar("T") # Generic type for component values
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class PropertyArray(Generic[T]):
|
|
15
|
+
components: list[T]
|
|
16
|
+
_aliases: dict[str, str] = field(default_factory=dict)
|
|
17
|
+
|
|
18
|
+
def __init__(self, *args: T) -> None:
|
|
19
|
+
self.components = list(args)
|
|
20
|
+
self._aliases = {}
|
|
21
|
+
|
|
22
|
+
# Handle any dimension-specific default names (x, y, z, w)
|
|
23
|
+
self._default_names = ["x", "y", "z", "w"]
|
|
24
|
+
# Limit to actual dimensions
|
|
25
|
+
self._default_names = self._default_names[: len(args)]
|
|
26
|
+
|
|
27
|
+
def set_aliases(self, **kwargs) -> PropertyArray[T]:
|
|
28
|
+
"""Set custom aliases for components.
|
|
29
|
+
|
|
30
|
+
Example: vec.set_aliases(x='width', y='height').
|
|
31
|
+
"""
|
|
32
|
+
self._aliases.update(kwargs)
|
|
33
|
+
return self
|
|
34
|
+
|
|
35
|
+
# Add sequence protocol methods
|
|
36
|
+
def __getitem__(self, index) -> T:
|
|
37
|
+
return self.components[index]
|
|
38
|
+
|
|
39
|
+
def __setitem__(self, index, value) -> None:
|
|
40
|
+
self.components[index] = value
|
|
41
|
+
|
|
42
|
+
def __len__(self) -> int:
|
|
43
|
+
return len(self.components)
|
|
44
|
+
|
|
45
|
+
def __iter__(self) -> Iterator[T]:
|
|
46
|
+
return iter(self.components)
|
|
47
|
+
|
|
48
|
+
def __getattr__(self, name) -> T:
|
|
49
|
+
# Check if it's an alias
|
|
50
|
+
if name in self._aliases:
|
|
51
|
+
original_name = self._aliases[name]
|
|
52
|
+
if original_name in self._default_names:
|
|
53
|
+
index = self._default_names.index(original_name)
|
|
54
|
+
return self.components[index]
|
|
55
|
+
|
|
56
|
+
# Check if it's a default name
|
|
57
|
+
if name in self._default_names:
|
|
58
|
+
index = self._default_names.index(name)
|
|
59
|
+
return self.components[index]
|
|
60
|
+
|
|
61
|
+
# Check if it's a custom name that was set as an alias
|
|
62
|
+
for default_name, alias in self._aliases.items():
|
|
63
|
+
if alias == name and default_name in self._default_names:
|
|
64
|
+
index = self._default_names.index(default_name)
|
|
65
|
+
return self.components[index]
|
|
66
|
+
|
|
67
|
+
msg = f"'{self.__class__.__name__}' has no attribute '{name}'"
|
|
68
|
+
raise AttributeError(msg)
|
|
69
|
+
|
|
70
|
+
def __setattr__(self, name, value) -> None:
|
|
71
|
+
# Handle special attributes normally
|
|
72
|
+
if name in ["components", "_aliases", "_default_names"]:
|
|
73
|
+
super().__setattr__(name, value)
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
# Check if it's a default name
|
|
77
|
+
if hasattr(self, "_default_names") and name in self._default_names:
|
|
78
|
+
index = self._default_names.index(name)
|
|
79
|
+
if 0 <= index < len(self.components):
|
|
80
|
+
self.components[index] = value
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
# Check if it's an alias
|
|
84
|
+
if hasattr(self, "_aliases") and name in self._aliases.values():
|
|
85
|
+
# Find which default name this is an alias for
|
|
86
|
+
for default_name, alias in self._aliases.items():
|
|
87
|
+
if alias == name and default_name in self._default_names:
|
|
88
|
+
index = self._default_names.index(default_name)
|
|
89
|
+
self.components[index] = value
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
super().__setattr__(name, value)
|
|
93
|
+
|
|
94
|
+
def __repr__(self) -> str:
|
|
95
|
+
components_str = ", ".join(str(c) for c in self.components)
|
|
96
|
+
return f"{self.__class__.__name__}({components_str})"
|
|
97
|
+
|
|
98
|
+
def to_dict(self) -> dict:
|
|
99
|
+
"""Convert PropertyArray to a dictionary for serialization."""
|
|
100
|
+
# Convert components, handling Enum values appropriately
|
|
101
|
+
serialized_components = []
|
|
102
|
+
for comp in self.components:
|
|
103
|
+
if isinstance(comp, Enum):
|
|
104
|
+
serialized_components.append(comp.name) # Use name for enums
|
|
105
|
+
else:
|
|
106
|
+
serialized_components.append(comp)
|
|
107
|
+
|
|
108
|
+
result = {"components": serialized_components, "aliases": self._aliases}
|
|
109
|
+
|
|
110
|
+
# Include named components
|
|
111
|
+
for i, name in enumerate(self._default_names):
|
|
112
|
+
if i < len(self.components):
|
|
113
|
+
comp = self.components[i]
|
|
114
|
+
if isinstance(comp, Enum):
|
|
115
|
+
result[name] = comp.name
|
|
116
|
+
else:
|
|
117
|
+
result[name] = comp
|
|
118
|
+
|
|
119
|
+
return result
|
|
120
|
+
|
|
121
|
+
@classmethod
|
|
122
|
+
def from_dict(cls, data: dict) -> PropertyArray[T]:
|
|
123
|
+
"""Create a PropertyArray from a dictionary."""
|
|
124
|
+
# Extract components
|
|
125
|
+
components = data.get("components", [])
|
|
126
|
+
|
|
127
|
+
# Create instance
|
|
128
|
+
instance = cls(*components)
|
|
129
|
+
|
|
130
|
+
# Set aliases if provided
|
|
131
|
+
aliases = data.get("aliases", {})
|
|
132
|
+
if aliases:
|
|
133
|
+
instance.set_aliases(**aliases)
|
|
134
|
+
|
|
135
|
+
return instance
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class EnumPropertyArray(PropertyArray[Enum]):
|
|
139
|
+
"""Base class for property arrays of Enum values."""
|
|
140
|
+
|
|
141
|
+
# List of enum types for each component
|
|
142
|
+
enum_types: list[type[Enum] | None] = []
|
|
143
|
+
|
|
144
|
+
def _process_enum_value(self, value, index) -> str | float | int:
|
|
145
|
+
"""Convert string to enum value if needed, using the enum type at the specified index."""
|
|
146
|
+
if not self.enum_types or index >= len(self.enum_types) or self.enum_types[index] is None:
|
|
147
|
+
return value
|
|
148
|
+
|
|
149
|
+
enum_type = self.enum_types[index]
|
|
150
|
+
|
|
151
|
+
if isinstance(value, enum_type):
|
|
152
|
+
return value
|
|
153
|
+
|
|
154
|
+
if isinstance(value, str):
|
|
155
|
+
# Try to convert string to enum value
|
|
156
|
+
try:
|
|
157
|
+
# Try by name first
|
|
158
|
+
return enum_type[value]
|
|
159
|
+
except (KeyError, TypeError):
|
|
160
|
+
# Try by value
|
|
161
|
+
for enum_val in enum_type:
|
|
162
|
+
if enum_val.value == value:
|
|
163
|
+
return enum_val
|
|
164
|
+
elif isinstance(value, (int, float)) and any(isinstance(e.value, (int, float)) for e in enum_type):
|
|
165
|
+
# Try to match by value for numeric enums
|
|
166
|
+
for enum_val in enum_type:
|
|
167
|
+
if enum_val.value == value:
|
|
168
|
+
return enum_val
|
|
169
|
+
|
|
170
|
+
return value
|
|
171
|
+
|
|
172
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
173
|
+
# Process args to convert strings/values to enum values if possible
|
|
174
|
+
processed_args = []
|
|
175
|
+
for i, arg in enumerate(args):
|
|
176
|
+
processed_args.append(self._process_enum_value(arg, i))
|
|
177
|
+
|
|
178
|
+
super().__init__(*processed_args, **kwargs)
|
|
179
|
+
|
|
180
|
+
@classmethod
|
|
181
|
+
def from_dict(cls, data: dict) -> EnumPropertyArray:
|
|
182
|
+
"""Create a EnumPropertyArray from a dictionary, converting values to enum values."""
|
|
183
|
+
# Get the components
|
|
184
|
+
components = data.get("components", [])
|
|
185
|
+
|
|
186
|
+
# Create a temporary instance to access enum_types and _process_enum_value
|
|
187
|
+
temp_instance = cls()
|
|
188
|
+
|
|
189
|
+
# Process components to convert values to enum values
|
|
190
|
+
processed_components = [temp_instance._process_enum_value(comp, i) for i, comp in enumerate(components)]
|
|
191
|
+
|
|
192
|
+
# Create the real instance
|
|
193
|
+
instance = cls(*processed_components)
|
|
194
|
+
|
|
195
|
+
# Set aliases if provided
|
|
196
|
+
aliases = data.get("aliases", {})
|
|
197
|
+
if aliases:
|
|
198
|
+
instance.set_aliases(**aliases)
|
|
199
|
+
|
|
200
|
+
return instance
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
# Create specialized types for convenience
|
|
204
|
+
class Int2(PropertyArray[int]):
|
|
205
|
+
def __init__(self, x=0, y=0) -> None:
|
|
206
|
+
super().__init__(x, y)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class Int3(PropertyArray[int]):
|
|
210
|
+
def __init__(self, x=0, y=0, z=0) -> None:
|
|
211
|
+
super().__init__(x, y, z)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class Int4(PropertyArray[int]):
|
|
215
|
+
def __init__(self, x=0, y=0, z=0, w=0) -> None:
|
|
216
|
+
super().__init__(x, y, z, w)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class Float2(PropertyArray[float]):
|
|
220
|
+
def __init__(self, x=0.0, y=0.0) -> None:
|
|
221
|
+
super().__init__(x, y)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class Float3(PropertyArray[float]):
|
|
225
|
+
def __init__(self, x=0.0, y=0.0, z=0.0) -> None:
|
|
226
|
+
super().__init__(x, y, z)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class Float4(PropertyArray[float]):
|
|
230
|
+
def __init__(self, x=0.0, y=0.0, z=0.0, w=0.0) -> None:
|
|
231
|
+
super().__init__(x, y, z, w)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class Str2(PropertyArray[str]):
|
|
235
|
+
def __init__(self, x="", y="") -> None:
|
|
236
|
+
super().__init__(x, y)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class Str3(PropertyArray[str]):
|
|
240
|
+
def __init__(self, x="", y="", z="") -> None:
|
|
241
|
+
super().__init__(x, y, z)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class Str4(PropertyArray[str]):
|
|
245
|
+
def __init__(self, x="", y="", z="", w="") -> None:
|
|
246
|
+
super().__init__(x, y, z, w)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class Enum2(EnumPropertyArray):
|
|
250
|
+
"""A 2D property array for Enum values."""
|
|
251
|
+
|
|
252
|
+
enum_types = [None, None] # Placeholder, to be set by subclassing
|
|
253
|
+
|
|
254
|
+
def __init__(self, x=None, y=None) -> None:
|
|
255
|
+
# Set defaults based on enum types
|
|
256
|
+
if x is None and self.enum_types[0] is not None:
|
|
257
|
+
enum_values = list(self.enum_types[0])
|
|
258
|
+
if enum_values:
|
|
259
|
+
x = enum_values[0]
|
|
260
|
+
|
|
261
|
+
if y is None and self.enum_types[1] is not None:
|
|
262
|
+
enum_values = list(self.enum_types[1])
|
|
263
|
+
if enum_values:
|
|
264
|
+
y = enum_values[0]
|
|
265
|
+
|
|
266
|
+
super().__init__(x, y)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class Enum3(EnumPropertyArray):
|
|
270
|
+
"""A 3D property array for Enum values."""
|
|
271
|
+
|
|
272
|
+
enum_types = [None, None, None]
|
|
273
|
+
|
|
274
|
+
def __init__(self, x=None, y=None, z=None) -> None:
|
|
275
|
+
# Set defaults based on enum types
|
|
276
|
+
defaults: list = [None, None, None]
|
|
277
|
+
|
|
278
|
+
for i, enum_type in enumerate([self.enum_types[0], self.enum_types[1], self.enum_types[2]]):
|
|
279
|
+
if enum_type is not None:
|
|
280
|
+
enum_values = list(enum_type)
|
|
281
|
+
if enum_values:
|
|
282
|
+
defaults[i] = enum_values[0]
|
|
283
|
+
|
|
284
|
+
if x is None:
|
|
285
|
+
x = defaults[0]
|
|
286
|
+
if y is None:
|
|
287
|
+
y = defaults[1]
|
|
288
|
+
if z is None:
|
|
289
|
+
z = defaults[2]
|
|
290
|
+
|
|
291
|
+
super().__init__(x, y, z)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
class Enum4(EnumPropertyArray):
|
|
295
|
+
"""A 4D property array for Enum values."""
|
|
296
|
+
|
|
297
|
+
enum_types = [None, None, None, None]
|
|
298
|
+
|
|
299
|
+
def __init__(self, x=None, y=None, z=None, w=None) -> None:
|
|
300
|
+
# Set defaults based on enum types
|
|
301
|
+
defaults: list = [None, None, None, None]
|
|
302
|
+
|
|
303
|
+
for i, enum_type in enumerate(self.enum_types):
|
|
304
|
+
if enum_type is not None:
|
|
305
|
+
enum_values = list(enum_type)
|
|
306
|
+
if enum_values:
|
|
307
|
+
defaults[i] = enum_values[0]
|
|
308
|
+
|
|
309
|
+
if x is None:
|
|
310
|
+
x = defaults[0]
|
|
311
|
+
if y is None:
|
|
312
|
+
y = defaults[1]
|
|
313
|
+
if z is None:
|
|
314
|
+
z = defaults[2]
|
|
315
|
+
if w is None:
|
|
316
|
+
w = defaults[3]
|
|
317
|
+
|
|
318
|
+
super().__init__(x, y, z, w)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
# Helper function to create Enum arrays for specific enum types
|
|
322
|
+
def create_enum_array(enum_types, dimension=None) -> type:
|
|
323
|
+
"""Create a EnumPropertyArray subclass with the specified enum types.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
enum_types: List of Enum classes, one for each component
|
|
327
|
+
dimension: Optional dimension (if not provided, derived from len(enum_types))
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
A subclass of EnumPropertyArray configured for the specified enum types
|
|
331
|
+
"""
|
|
332
|
+
if dimension is None:
|
|
333
|
+
dimension = len(enum_types)
|
|
334
|
+
# Ensure enum_types matches the requested dimension
|
|
335
|
+
elif len(enum_types) < dimension:
|
|
336
|
+
# Pad with None for unspecified types
|
|
337
|
+
enum_types = list(enum_types) + [None] * (dimension - len(enum_types))
|
|
338
|
+
elif len(enum_types) > dimension:
|
|
339
|
+
# Truncate if too many types provided
|
|
340
|
+
enum_types = enum_types[:dimension]
|
|
341
|
+
|
|
342
|
+
if dimension == 2: # noqa: PLR2004
|
|
343
|
+
base_class = Enum2
|
|
344
|
+
elif dimension == 3: # noqa: PLR2004
|
|
345
|
+
base_class = Enum3
|
|
346
|
+
elif dimension == 4: # noqa: PLR2004
|
|
347
|
+
base_class = Enum4
|
|
348
|
+
else:
|
|
349
|
+
msg = f"Unsupported dimension: {dimension}"
|
|
350
|
+
raise ValueError(msg)
|
|
351
|
+
|
|
352
|
+
# Generate a descriptive class name
|
|
353
|
+
type_names = []
|
|
354
|
+
for et in enum_types:
|
|
355
|
+
if et is None:
|
|
356
|
+
type_names.append("None")
|
|
357
|
+
else:
|
|
358
|
+
type_names.append(et.__name__)
|
|
359
|
+
|
|
360
|
+
class_name = f"Enum{dimension}_{'-'.join(type_names)}"
|
|
361
|
+
|
|
362
|
+
# Create a new class with the enum_types set
|
|
363
|
+
return type(class_name, (base_class,), {"enum_types": list(enum_types)})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from exe_types.core_types import Parameter
|
|
4
|
+
from exe_types.node_types import NodeBase
|
|
5
|
+
|
|
6
|
+
from flow import ControlFlow
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FlowNode(NodeBase):
|
|
10
|
+
control_flow: ControlFlow
|
|
11
|
+
|
|
12
|
+
def __init__(self, name) -> None:
|
|
13
|
+
super().__init__(name)
|
|
14
|
+
# generates it's own control flow
|
|
15
|
+
self.control_flow = ControlFlow()
|
|
16
|
+
|
|
17
|
+
# Begins the flow and the process.
|
|
18
|
+
def process(self) -> Any:
|
|
19
|
+
self.control_flow.start_flow()
|
|
20
|
+
|
|
21
|
+
def add_connection(
|
|
22
|
+
self,
|
|
23
|
+
source_node: NodeBase,
|
|
24
|
+
source_parameter: Parameter,
|
|
25
|
+
target_node: NodeBase,
|
|
26
|
+
target_parameter: Parameter,
|
|
27
|
+
) -> None:
|
|
28
|
+
self.control_flow.add_connection(source_node, source_parameter, target_node, target_parameter)
|
|
29
|
+
|
|
30
|
+
def add_node(self, node: NodeBase) -> None:
|
|
31
|
+
self.control_flow.add_node(node)
|