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.

Files changed (98) hide show
  1. __init__.py +0 -0
  2. griptape_nodes/__init__.py +0 -0
  3. griptape_nodes/exe_types/__init__.py +1 -0
  4. griptape_nodes/exe_types/core_types.py +244 -0
  5. griptape_nodes/exe_types/data_types.py +363 -0
  6. griptape_nodes/exe_types/flow_node.py +31 -0
  7. griptape_nodes/exe_types/node_types.py +271 -0
  8. griptape_nodes/exe_types/type_validator.py +913 -0
  9. griptape_nodes/machines/__init__.py +1 -0
  10. griptape_nodes/machines/control_flow.py +170 -0
  11. griptape_nodes/machines/fsm.py +55 -0
  12. griptape_nodes/machines/node_resolution.py +290 -0
  13. griptape_nodes/node_library/__init__.py +1 -0
  14. griptape_nodes/node_library/library_registry.py +237 -0
  15. griptape_nodes/node_library/script_registry.py +100 -0
  16. griptape_nodes/nodes/__init__.py +1 -0
  17. griptape_nodes/nodes/agent.py +132 -0
  18. griptape_nodes/nodes/comfy_nodes.py +312 -0
  19. griptape_nodes/nodes/create_dict.py +90 -0
  20. griptape_nodes/nodes/create_image.py +131 -0
  21. griptape_nodes/nodes/create_string.py +32 -0
  22. griptape_nodes/nodes/drivers/__init__.py +1 -0
  23. griptape_nodes/nodes/drivers/anthropic_prompt_driver.py +60 -0
  24. griptape_nodes/nodes/drivers/audio_transcription_drivers.py +31 -0
  25. griptape_nodes/nodes/drivers/azure_openai_chat_prompt_driver.py +66 -0
  26. griptape_nodes/nodes/drivers/azure_openai_image_driver.py +130 -0
  27. griptape_nodes/nodes/drivers/base_driver.py +51 -0
  28. griptape_nodes/nodes/drivers/base_embedding_driver.py +27 -0
  29. griptape_nodes/nodes/drivers/base_image_driver.py +63 -0
  30. griptape_nodes/nodes/drivers/base_prompt_driver.py +122 -0
  31. griptape_nodes/nodes/drivers/cohere_prompt_driver.py +50 -0
  32. griptape_nodes/nodes/drivers/griptape_cloud_prompt_driver.py +66 -0
  33. griptape_nodes/nodes/drivers/ollama_embedding_driver.py +69 -0
  34. griptape_nodes/nodes/drivers/ollama_prompt_driver.py +43 -0
  35. griptape_nodes/nodes/drivers/openai_chat_prompt_driver.py +67 -0
  36. griptape_nodes/nodes/drivers/openai_image_driver.py +8 -0
  37. griptape_nodes/nodes/drivers/web_search_drivers.py +27 -0
  38. griptape_nodes/nodes/end_flow.py +17 -0
  39. griptape_nodes/nodes/key_value_pair.py +60 -0
  40. griptape_nodes/nodes/load_image.py +25 -0
  41. griptape_nodes/nodes/load_text.py +72 -0
  42. griptape_nodes/nodes/merge_dict.py +65 -0
  43. griptape_nodes/nodes/merge_texts.py +73 -0
  44. griptape_nodes/nodes/rules/__init__.py +1 -0
  45. griptape_nodes/nodes/rules/base_ruleset.py +55 -0
  46. griptape_nodes/nodes/save_text.py +70 -0
  47. griptape_nodes/nodes/simple_agent.py +75 -0
  48. griptape_nodes/nodes/start_flow.py +17 -0
  49. griptape_nodes/nodes/string_list.py +47 -0
  50. griptape_nodes/nodes/tools/__init__.py +1 -0
  51. griptape_nodes/nodes/tools/audio_transcription_tool.py +19 -0
  52. griptape_nodes/nodes/tools/base_tool.py +36 -0
  53. griptape_nodes/nodes/tools/calculator_tool.py +13 -0
  54. griptape_nodes/nodes/tools/convert_agent_to_tool.py +30 -0
  55. griptape_nodes/nodes/tools/date_time_tool.py +13 -0
  56. griptape_nodes/nodes/tools/extraction_tool.py +34 -0
  57. griptape_nodes/nodes/tools/file_manager_tool.py +13 -0
  58. griptape_nodes/nodes/tools/prompt_summary_tool.py +33 -0
  59. griptape_nodes/nodes/tools/query_tool.py +83 -0
  60. griptape_nodes/nodes/tools/rag_tool.py +26 -0
  61. griptape_nodes/nodes/tools/tool_list.py +59 -0
  62. griptape_nodes/nodes/tools/tools.py +84 -0
  63. griptape_nodes/nodes/tools/vector_store_tool.py +63 -0
  64. griptape_nodes/nodes/tools/web_scraper_tool.py +22 -0
  65. griptape_nodes/nodes/tools/web_search_tool.py +25 -0
  66. griptape_nodes/nodes/utilities.py +222 -0
  67. griptape_nodes/nodes/utils/__init__.py +1 -0
  68. griptape_nodes/nodes/utils/anthropic_utils.py +31 -0
  69. griptape_nodes/retained_mode/__init__.py +1 -0
  70. griptape_nodes/retained_mode/events/__init__.py +1 -0
  71. griptape_nodes/retained_mode/events/app_events.py +10 -0
  72. griptape_nodes/retained_mode/events/arbitrary_python_events.py +30 -0
  73. griptape_nodes/retained_mode/events/base_events.py +493 -0
  74. griptape_nodes/retained_mode/events/config_events.py +83 -0
  75. griptape_nodes/retained_mode/events/connection_events.py +83 -0
  76. griptape_nodes/retained_mode/events/execution_events.py +259 -0
  77. griptape_nodes/retained_mode/events/flow_events.py +83 -0
  78. griptape_nodes/retained_mode/events/generate_event_schemas.py +18 -0
  79. griptape_nodes/retained_mode/events/library_events.py +118 -0
  80. griptape_nodes/retained_mode/events/node_events.py +121 -0
  81. griptape_nodes/retained_mode/events/object_events.py +28 -0
  82. griptape_nodes/retained_mode/events/os_events.py +26 -0
  83. griptape_nodes/retained_mode/events/parameter_events.py +198 -0
  84. griptape_nodes/retained_mode/events/payload_registry.py +59 -0
  85. griptape_nodes/retained_mode/events/script_events.py +137 -0
  86. griptape_nodes/retained_mode/griptape_nodes.py +2619 -0
  87. griptape_nodes/retained_mode/managers/__init__.py +1 -0
  88. griptape_nodes/retained_mode/managers/config_manager.py +311 -0
  89. griptape_nodes/retained_mode/managers/connection_manager.py +188 -0
  90. griptape_nodes/retained_mode/managers/event_manager.py +113 -0
  91. griptape_nodes/retained_mode/managers/operation_manager.py +388 -0
  92. griptape_nodes/retained_mode/managers/os_manager.py +87 -0
  93. griptape_nodes/retained_mode/retained_mode.py +787 -0
  94. griptape_nodes-0.1.0.dist-info/METADATA +490 -0
  95. griptape_nodes-0.1.0.dist-info/RECORD +98 -0
  96. griptape_nodes-0.1.0.dist-info/WHEEL +5 -0
  97. griptape_nodes-0.1.0.dist-info/licenses/LICENSE +201 -0
  98. 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)