griptape-nodes 0.71.0__py3-none-any.whl → 0.72.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/app/app.py +4 -0
- griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +10 -1
- griptape_nodes/bootstrap/workflow_executors/utils/subprocess_script.py +4 -0
- griptape_nodes/bootstrap/workflow_publishers/utils/subprocess_script.py +4 -0
- griptape_nodes/common/node_executor.py +1 -1
- griptape_nodes/drivers/image_metadata/__init__.py +21 -0
- griptape_nodes/drivers/image_metadata/base_image_metadata_driver.py +63 -0
- griptape_nodes/drivers/image_metadata/exif_metadata_driver.py +218 -0
- griptape_nodes/drivers/image_metadata/image_metadata_driver_registry.py +55 -0
- griptape_nodes/drivers/image_metadata/png_metadata_driver.py +71 -0
- griptape_nodes/drivers/storage/base_storage_driver.py +32 -0
- griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +384 -10
- griptape_nodes/drivers/storage/local_storage_driver.py +65 -4
- griptape_nodes/drivers/thread_storage/local_thread_storage_driver.py +1 -0
- griptape_nodes/exe_types/node_groups/base_node_group.py +3 -0
- griptape_nodes/exe_types/node_types.py +13 -0
- griptape_nodes/exe_types/param_components/log_parameter.py +3 -2
- griptape_nodes/exe_types/param_types/parameter_float.py +4 -4
- griptape_nodes/exe_types/param_types/parameter_int.py +4 -4
- griptape_nodes/exe_types/param_types/parameter_number.py +34 -30
- griptape_nodes/node_library/workflow_registry.py +5 -8
- griptape_nodes/retained_mode/events/app_events.py +1 -0
- griptape_nodes/retained_mode/events/base_events.py +42 -26
- griptape_nodes/retained_mode/events/flow_events.py +67 -0
- griptape_nodes/retained_mode/events/library_events.py +1 -1
- griptape_nodes/retained_mode/events/node_events.py +1 -0
- griptape_nodes/retained_mode/events/os_events.py +22 -0
- griptape_nodes/retained_mode/events/static_file_events.py +28 -4
- griptape_nodes/retained_mode/managers/flow_manager.py +134 -0
- griptape_nodes/retained_mode/managers/image_metadata_injector.py +339 -0
- griptape_nodes/retained_mode/managers/library_manager.py +71 -41
- griptape_nodes/retained_mode/managers/model_manager.py +1 -0
- griptape_nodes/retained_mode/managers/node_manager.py +8 -5
- griptape_nodes/retained_mode/managers/os_manager.py +269 -32
- griptape_nodes/retained_mode/managers/project_manager.py +3 -7
- griptape_nodes/retained_mode/managers/session_manager.py +1 -0
- griptape_nodes/retained_mode/managers/settings.py +5 -0
- griptape_nodes/retained_mode/managers/static_files_manager.py +83 -17
- griptape_nodes/retained_mode/managers/workflow_manager.py +71 -41
- griptape_nodes/servers/static.py +31 -0
- griptape_nodes/traits/clamp.py +52 -9
- griptape_nodes/utils/__init__.py +9 -1
- griptape_nodes/utils/file_utils.py +13 -13
- griptape_nodes/utils/http_file_patch.py +613 -0
- griptape_nodes/utils/path_utils.py +58 -0
- griptape_nodes/utils/url_utils.py +106 -0
- {griptape_nodes-0.71.0.dist-info → griptape_nodes-0.72.0.dist-info}/METADATA +2 -1
- {griptape_nodes-0.71.0.dist-info → griptape_nodes-0.72.0.dist-info}/RECORD +50 -41
- {griptape_nodes-0.71.0.dist-info → griptape_nodes-0.72.0.dist-info}/WHEEL +1 -1
- {griptape_nodes-0.71.0.dist-info → griptape_nodes-0.72.0.dist-info}/entry_points.txt +0 -0
|
@@ -37,8 +37,8 @@ class ParameterNumber(Parameter):
|
|
|
37
37
|
ui_options: dict | None = None,
|
|
38
38
|
step: float | None = None,
|
|
39
39
|
slider: bool = False,
|
|
40
|
-
min_val: float =
|
|
41
|
-
max_val: float =
|
|
40
|
+
min_val: float | None = None,
|
|
41
|
+
max_val: float | None = None,
|
|
42
42
|
validate_min_max: bool = False,
|
|
43
43
|
accept_any: bool = True,
|
|
44
44
|
hide: bool | None = None,
|
|
@@ -74,8 +74,8 @@ class ParameterNumber(Parameter):
|
|
|
74
74
|
ui_options: Dictionary of UI options
|
|
75
75
|
step: Step size for numeric input controls
|
|
76
76
|
slider: Whether to use slider trait
|
|
77
|
-
min_val: Minimum value for constraints
|
|
78
|
-
max_val: Maximum value for constraints
|
|
77
|
+
min_val: Minimum value for constraints (None to disable constraints)
|
|
78
|
+
max_val: Maximum value for constraints (None to disable constraints)
|
|
79
79
|
validate_min_max: Whether to validate min/max with error
|
|
80
80
|
accept_any: Whether to accept any input type and convert to number (default: True)
|
|
81
81
|
hide: Whether to hide the entire parameter
|
|
@@ -220,8 +220,8 @@ class ParameterNumber(Parameter):
|
|
|
220
220
|
traits: set[type[Trait] | Trait] | None,
|
|
221
221
|
*,
|
|
222
222
|
slider: bool,
|
|
223
|
-
min_val: float,
|
|
224
|
-
max_val: float,
|
|
223
|
+
min_val: float | None,
|
|
224
|
+
max_val: float | None,
|
|
225
225
|
validate_min_max: bool,
|
|
226
226
|
) -> None:
|
|
227
227
|
"""Set up constraint traits based on parameters.
|
|
@@ -235,12 +235,6 @@ class ParameterNumber(Parameter):
|
|
|
235
235
|
validate_min_max: Whether to validate min/max with error
|
|
236
236
|
"""
|
|
237
237
|
# Validation rules
|
|
238
|
-
if min_val is not None and max_val is None:
|
|
239
|
-
msg = f"{name}: If min_val is provided, max_val must also be provided"
|
|
240
|
-
raise ValueError(msg)
|
|
241
|
-
if max_val is not None and min_val is None:
|
|
242
|
-
msg = f"{name}: If max_val is provided, min_val must also be provided"
|
|
243
|
-
raise ValueError(msg)
|
|
244
238
|
if slider and (min_val is None or max_val is None):
|
|
245
239
|
msg = f"{name}: If slider is True, both min_val and max_val must be provided"
|
|
246
240
|
raise ValueError(msg)
|
|
@@ -259,7 +253,7 @@ class ParameterNumber(Parameter):
|
|
|
259
253
|
traits.add(Slider(min_val=min_val, max_val=max_val))
|
|
260
254
|
elif validate_min_max and min_val is not None and max_val is not None:
|
|
261
255
|
traits.add(MinMax(min_val=min_val, max_val=max_val))
|
|
262
|
-
elif min_val is not None
|
|
256
|
+
elif min_val is not None or max_val is not None:
|
|
263
257
|
traits.add(Clamp(min_val=min_val, max_val=max_val))
|
|
264
258
|
|
|
265
259
|
# Store traits for later use
|
|
@@ -278,14 +272,14 @@ class ParameterNumber(Parameter):
|
|
|
278
272
|
def slider(self, value: bool) -> None:
|
|
279
273
|
"""Set slider trait."""
|
|
280
274
|
if value:
|
|
281
|
-
|
|
275
|
+
# Get min/max from stored values
|
|
276
|
+
min_val = getattr(self, "_min_val", None)
|
|
277
|
+
max_val = getattr(self, "_max_val", None)
|
|
278
|
+
if min_val is None or max_val is None:
|
|
282
279
|
msg = f"{self.name}: Cannot enable slider without min_val and max_val"
|
|
283
280
|
raise ValueError(msg)
|
|
284
281
|
# Find existing constraint traits and replace with slider
|
|
285
282
|
self._remove_constraint_traits()
|
|
286
|
-
# Get min/max from existing traits or use defaults
|
|
287
|
-
min_val = getattr(self, "_min_val", 0)
|
|
288
|
-
max_val = getattr(self, "_max_val", 100)
|
|
289
283
|
self.add_trait(Slider(min_val=min_val, max_val=max_val))
|
|
290
284
|
else:
|
|
291
285
|
# Remove slider trait
|
|
@@ -304,9 +298,6 @@ class ParameterNumber(Parameter):
|
|
|
304
298
|
@min_val.setter
|
|
305
299
|
def min_val(self, value: float | None) -> None:
|
|
306
300
|
"""Set minimum value and update constraint traits."""
|
|
307
|
-
if value is not None and self.max_val is None:
|
|
308
|
-
msg = f"{self.name}: Cannot set min_val without max_val"
|
|
309
|
-
raise ValueError(msg)
|
|
310
301
|
self._min_val = value
|
|
311
302
|
self._update_constraint_traits()
|
|
312
303
|
|
|
@@ -321,9 +312,6 @@ class ParameterNumber(Parameter):
|
|
|
321
312
|
@max_val.setter
|
|
322
313
|
def max_val(self, value: float | None) -> None:
|
|
323
314
|
"""Set maximum value and update constraint traits."""
|
|
324
|
-
if value is not None and self.min_val is None:
|
|
325
|
-
msg = f"{self.name}: Cannot set max_val without min_val"
|
|
326
|
-
raise ValueError(msg)
|
|
327
315
|
self._max_val = value
|
|
328
316
|
self._update_constraint_traits()
|
|
329
317
|
|
|
@@ -337,14 +325,12 @@ class ParameterNumber(Parameter):
|
|
|
337
325
|
"""Set MinMax validation."""
|
|
338
326
|
if value:
|
|
339
327
|
# Check if we have stored min/max values
|
|
340
|
-
min_val
|
|
341
|
-
max_val = getattr(self, "_max_val", None)
|
|
342
|
-
if min_val is None or max_val is None:
|
|
328
|
+
if self.min_val is None or self.max_val is None:
|
|
343
329
|
msg = f"{self.name}: Cannot enable validate_min_max without min_val and max_val"
|
|
344
330
|
raise ValueError(msg)
|
|
345
331
|
# Replace existing constraint traits with MinMax
|
|
346
332
|
self._remove_constraint_traits()
|
|
347
|
-
self.add_trait(MinMax(min_val=min_val, max_val=max_val))
|
|
333
|
+
self.add_trait(MinMax(min_val=self.min_val, max_val=self.max_val))
|
|
348
334
|
else:
|
|
349
335
|
# Remove MinMax trait and replace with Clamp if we have min/max
|
|
350
336
|
min_max_traits = self.find_elements_by_type(MinMax)
|
|
@@ -367,17 +353,35 @@ class ParameterNumber(Parameter):
|
|
|
367
353
|
min_val = getattr(self, "_min_val", None)
|
|
368
354
|
max_val = getattr(self, "_max_val", None)
|
|
369
355
|
|
|
370
|
-
if min_val is None
|
|
356
|
+
if min_val is None and max_val is None:
|
|
371
357
|
self._remove_constraint_traits()
|
|
372
358
|
return
|
|
373
359
|
|
|
360
|
+
if self.slider and (min_val is None or max_val is None):
|
|
361
|
+
msg = f"{self.name}: Cannot enable slider without min_val and max_val"
|
|
362
|
+
raise ValueError(msg)
|
|
363
|
+
|
|
364
|
+
if self.validate_min_max and (min_val is None or max_val is None):
|
|
365
|
+
msg = f"{self.name}: Cannot enable validate_min_max without min_val and max_val"
|
|
366
|
+
raise ValueError(msg)
|
|
367
|
+
|
|
374
368
|
# Determine which trait to use based on current state
|
|
375
369
|
if self.slider:
|
|
370
|
+
# Checked above: both min_val and max_val are available when slider is enabled.
|
|
371
|
+
# Type narrowing: assign to variables after None check so type checker understands
|
|
372
|
+
min_val_float: float = min_val # type: ignore[assignment]
|
|
373
|
+
max_val_float: float = max_val # type: ignore[assignment]
|
|
374
|
+
# Python will naturally coerce int to float if needed (no precision loss for reasonable values)
|
|
376
375
|
self._remove_constraint_traits()
|
|
377
|
-
self.add_trait(Slider(min_val=
|
|
376
|
+
self.add_trait(Slider(min_val=min_val_float, max_val=max_val_float))
|
|
378
377
|
elif self.validate_min_max:
|
|
378
|
+
# Checked above: both min_val and max_val are available when validate_min_max is enabled.
|
|
379
|
+
# Type narrowing: assign to variables after None check so type checker understands
|
|
380
|
+
min_val_float: float = min_val # type: ignore[assignment]
|
|
381
|
+
max_val_float: float = max_val # type: ignore[assignment]
|
|
382
|
+
# Python will naturally coerce int to float if needed (no precision loss for reasonable values)
|
|
379
383
|
self._remove_constraint_traits()
|
|
380
|
-
self.add_trait(MinMax(min_val=
|
|
384
|
+
self.add_trait(MinMax(min_val=min_val_float, max_val=max_val_float))
|
|
381
385
|
else:
|
|
382
386
|
self._remove_constraint_traits()
|
|
383
387
|
self.add_trait(Clamp(min_val=min_val, max_val=max_val))
|
|
@@ -12,6 +12,7 @@ from griptape_nodes.node_library.library_registry import (
|
|
|
12
12
|
LibraryNameAndVersion, # noqa: TC001 (putting this into type checking causes it to not be defined)
|
|
13
13
|
)
|
|
14
14
|
from griptape_nodes.utils.metaclasses import SingletonMeta
|
|
15
|
+
from griptape_nodes.utils.path_utils import resolve_workspace_path
|
|
15
16
|
|
|
16
17
|
logger = logging.getLogger("griptape_nodes")
|
|
17
18
|
|
|
@@ -45,7 +46,7 @@ class WorkflowShape(BaseModel):
|
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
class WorkflowMetadata(BaseModel):
|
|
48
|
-
LATEST_SCHEMA_VERSION: ClassVar[str] = "0.
|
|
49
|
+
LATEST_SCHEMA_VERSION: ClassVar[str] = "0.15.0"
|
|
49
50
|
|
|
50
51
|
name: str
|
|
51
52
|
schema_version: str
|
|
@@ -175,15 +176,11 @@ class WorkflowRegistry(metaclass=SingletonMeta):
|
|
|
175
176
|
def get_complete_file_path(cls, relative_file_path: str) -> str:
|
|
176
177
|
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
177
178
|
|
|
178
|
-
#
|
|
179
|
-
if Path(relative_file_path).is_absolute():
|
|
180
|
-
return relative_file_path
|
|
181
|
-
|
|
182
|
-
# Otherwise, resolve it relative to the workspace
|
|
179
|
+
# Resolve path using utility function
|
|
183
180
|
config_mgr = GriptapeNodes.ConfigManager()
|
|
184
181
|
workspace_path = config_mgr.workspace_path
|
|
185
|
-
|
|
186
|
-
return str(
|
|
182
|
+
resolved_path = resolve_workspace_path(Path(relative_file_path), workspace_path)
|
|
183
|
+
return str(resolved_path)
|
|
187
184
|
|
|
188
185
|
@classmethod
|
|
189
186
|
def delete_workflow_by_name(cls, name: str) -> Workflow:
|
|
@@ -113,6 +113,7 @@ class AppInitializationComplete(AppPayload):
|
|
|
113
113
|
libraries_to_register: list[str] = field(default_factory=list)
|
|
114
114
|
workflows_to_register: list[str] = field(default_factory=list)
|
|
115
115
|
models_to_download: list[str] = field(default_factory=list)
|
|
116
|
+
skip_library_loading: bool = False
|
|
116
117
|
|
|
117
118
|
|
|
118
119
|
@dataclass
|
|
@@ -16,6 +16,31 @@ if TYPE_CHECKING:
|
|
|
16
16
|
import builtins
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
def default_json_encoder(obj: Any) -> Any:
|
|
20
|
+
"""Custom JSON encoder for various object types.
|
|
21
|
+
|
|
22
|
+
Attempts the following encodings in order:
|
|
23
|
+
1. If the object is a SerializableMixin, call to_dict()
|
|
24
|
+
2. If the object is a Pydantic model, call model_dump()
|
|
25
|
+
3. Attempt to use the default JSON encoder
|
|
26
|
+
4. If all else fails, return the string representation of the object
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
obj: The object to encode
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
JSON-serializable representation of the object
|
|
33
|
+
"""
|
|
34
|
+
if isinstance(obj, SerializableMixin):
|
|
35
|
+
return obj.to_dict()
|
|
36
|
+
if isinstance(obj, BaseModel):
|
|
37
|
+
return obj.model_dump()
|
|
38
|
+
try:
|
|
39
|
+
return json.JSONEncoder().default(obj)
|
|
40
|
+
except TypeError:
|
|
41
|
+
return str(obj)
|
|
42
|
+
|
|
43
|
+
|
|
19
44
|
@dataclass
|
|
20
45
|
class ResultDetail:
|
|
21
46
|
"""A single detail about an operation result, including logging level and human readable message."""
|
|
@@ -71,6 +96,22 @@ class ResultDetails:
|
|
|
71
96
|
class Payload(ABC): # noqa: B024
|
|
72
97
|
"""Base class for all payload types. Customers will derive from this."""
|
|
73
98
|
|
|
99
|
+
def to_json(self, **kwargs) -> str:
|
|
100
|
+
"""Serialize this payload to JSON string.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
JSON string representation of the payload
|
|
104
|
+
"""
|
|
105
|
+
# Convert payload to dict
|
|
106
|
+
if is_dataclass(self):
|
|
107
|
+
payload_dict = asdict(self)
|
|
108
|
+
elif hasattr(self, "__dict__"):
|
|
109
|
+
payload_dict = self.__dict__
|
|
110
|
+
else:
|
|
111
|
+
payload_dict = str(self)
|
|
112
|
+
|
|
113
|
+
return json.dumps(payload_dict, default=default_json_encoder, **kwargs)
|
|
114
|
+
|
|
74
115
|
|
|
75
116
|
# Request payload base class with optional request ID
|
|
76
117
|
@dataclass(kw_only=True)
|
|
@@ -228,32 +269,7 @@ class BaseEvent(BaseModel, ABC):
|
|
|
228
269
|
|
|
229
270
|
def json(self, **kwargs) -> str:
|
|
230
271
|
"""Serialize to JSON string."""
|
|
231
|
-
|
|
232
|
-
# TODO: https://github.com/griptape-ai/griptape-nodes/issues/906
|
|
233
|
-
def default_encoder(obj: Any) -> Any:
|
|
234
|
-
"""Custom JSON encoder for various object types.
|
|
235
|
-
|
|
236
|
-
Attempts the following encodings in order:
|
|
237
|
-
1. If the object is a SerializableMixin, call to_dict()
|
|
238
|
-
2. If the object is a Pydantic model, call model_dump()
|
|
239
|
-
3. Attempt to use the default JSON encoder
|
|
240
|
-
4. If all else fails, return the string representation of the object
|
|
241
|
-
|
|
242
|
-
Args:
|
|
243
|
-
obj: The object to encode
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
"""
|
|
247
|
-
if isinstance(obj, SerializableMixin):
|
|
248
|
-
return obj.to_dict()
|
|
249
|
-
if isinstance(obj, BaseModel):
|
|
250
|
-
return obj.model_dump()
|
|
251
|
-
try:
|
|
252
|
-
return json.JSONEncoder().default(obj)
|
|
253
|
-
except TypeError:
|
|
254
|
-
return str(obj)
|
|
255
|
-
|
|
256
|
-
return json.dumps(self.dict(), default=default_encoder, **kwargs)
|
|
272
|
+
return json.dumps(self.dict(), default=default_json_encoder, **kwargs)
|
|
257
273
|
|
|
258
274
|
@abstractmethod
|
|
259
275
|
def get_request(self) -> Payload:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
+
from enum import StrEnum
|
|
4
5
|
from typing import TYPE_CHECKING, Any, NamedTuple
|
|
5
6
|
|
|
6
7
|
if TYPE_CHECKING:
|
|
@@ -19,6 +20,18 @@ from griptape_nodes.retained_mode.events.base_events import (
|
|
|
19
20
|
from griptape_nodes.retained_mode.events.payload_registry import PayloadRegistry
|
|
20
21
|
|
|
21
22
|
|
|
23
|
+
class FlowMetadataExtractionFailureReason(StrEnum):
|
|
24
|
+
"""Reasons why flow metadata extraction from image failed."""
|
|
25
|
+
|
|
26
|
+
FILE_NOT_FOUND = "file_not_found"
|
|
27
|
+
FILE_READ_ERROR = "file_read_error"
|
|
28
|
+
INVALID_IMAGE_FORMAT = "invalid_image_format"
|
|
29
|
+
NO_FLOW_METADATA = "no_flow_metadata"
|
|
30
|
+
INVALID_BASE64 = "invalid_base64"
|
|
31
|
+
INVALID_PICKLE = "invalid_pickle"
|
|
32
|
+
DESERIALIZATION_FAILED = "deserialization_failed"
|
|
33
|
+
|
|
34
|
+
|
|
22
35
|
@dataclass(kw_only=True)
|
|
23
36
|
@PayloadRegistry.register
|
|
24
37
|
class CreateFlowRequest(RequestPayload):
|
|
@@ -288,6 +301,60 @@ class DeserializeFlowFromCommandsResultFailure(ResultPayloadFailure):
|
|
|
288
301
|
pass
|
|
289
302
|
|
|
290
303
|
|
|
304
|
+
@dataclass
|
|
305
|
+
@PayloadRegistry.register
|
|
306
|
+
class ExtractFlowCommandsFromImageMetadataRequest(RequestPayload):
|
|
307
|
+
"""Extract flow commands from PNG image metadata.
|
|
308
|
+
|
|
309
|
+
This request reads a PNG image file, extracts the embedded workflow metadata
|
|
310
|
+
(specifically the gtn_flow_commands field), decodes it from base64, unpickles it,
|
|
311
|
+
and returns the SerializedFlowCommands object. Optionally, it can automatically
|
|
312
|
+
deserialize the flow by calling DeserializeFlowFromCommandsRequest.
|
|
313
|
+
|
|
314
|
+
Use when: Loading flow commands from an exported image, inspecting workflow
|
|
315
|
+
structure before deserialization, extracting flows shared as PNG files.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
file_url_or_path: Path to the PNG image file (absolute or relative) or URL
|
|
319
|
+
deserialize: If True, automatically deserialize the flow after extraction (default: False)
|
|
320
|
+
|
|
321
|
+
Results:
|
|
322
|
+
ExtractFlowCommandsFromImageMetadataResultSuccess - Commands extracted (and optionally deserialized)
|
|
323
|
+
ExtractFlowCommandsFromImageMetadataResultFailure - Extraction or deserialization failed
|
|
324
|
+
"""
|
|
325
|
+
|
|
326
|
+
file_url_or_path: str
|
|
327
|
+
deserialize: bool = False
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
@dataclass(kw_only=True)
|
|
331
|
+
@PayloadRegistry.register
|
|
332
|
+
class ExtractFlowCommandsFromImageMetadataResultSuccess(ResultPayloadSuccess):
|
|
333
|
+
"""Flow commands extracted successfully from image metadata.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
serialized_flow_commands: The extracted and decoded SerializedFlowCommands object
|
|
337
|
+
flow_name: Name of the deserialized flow (only present if deserialize=True)
|
|
338
|
+
node_name_mappings: Mapping from original to deserialized node names (only present if deserialize=True)
|
|
339
|
+
"""
|
|
340
|
+
|
|
341
|
+
serialized_flow_commands: SerializedFlowCommands
|
|
342
|
+
flow_name: str | None = None
|
|
343
|
+
node_name_mappings: dict[str, str] = field(default_factory=dict)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
@dataclass
|
|
347
|
+
@PayloadRegistry.register
|
|
348
|
+
class ExtractFlowCommandsFromImageMetadataResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
349
|
+
"""Flow commands extraction failed.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
file_path: The file path that was attempted (for error context)
|
|
353
|
+
"""
|
|
354
|
+
|
|
355
|
+
file_path: str
|
|
356
|
+
|
|
357
|
+
|
|
291
358
|
@dataclass
|
|
292
359
|
@PayloadRegistry.register
|
|
293
360
|
class GetFlowDetailsRequest(RequestPayload):
|
|
@@ -607,7 +607,7 @@ class DiscoverLibrariesRequest(RequestPayload):
|
|
|
607
607
|
class DiscoverLibrariesResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
608
608
|
"""Libraries discovered successfully."""
|
|
609
609
|
|
|
610
|
-
libraries_discovered:
|
|
610
|
+
libraries_discovered: list[DiscoveredLibrary] # Discovered libraries in config order
|
|
611
611
|
|
|
612
612
|
|
|
613
613
|
@dataclass
|
|
@@ -54,6 +54,24 @@ class FileIOFailureReason(StrEnum):
|
|
|
54
54
|
IO_ERROR = "io_error" # Generic I/O error
|
|
55
55
|
UNKNOWN = "unknown" # Unexpected error
|
|
56
56
|
|
|
57
|
+
# Recycle bin errors
|
|
58
|
+
RECYCLE_BIN_UNAVAILABLE = "recycle_bin_unavailable" # Recycle bin unavailable and behavior was RECYCLE_BIN_ONLY
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class DeletionBehavior(StrEnum):
|
|
62
|
+
"""How to handle file/directory deletion."""
|
|
63
|
+
|
|
64
|
+
PERMANENTLY_DELETE = "permanently_delete" # Permanently delete (default, current behavior)
|
|
65
|
+
RECYCLE_BIN_ONLY = "recycle_bin_only" # Send to recycle bin; fail if unavailable
|
|
66
|
+
PREFER_RECYCLE_BIN = "prefer_recycle_bin" # Try recycle bin; fall back to permanent deletion
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class DeletionOutcome(StrEnum):
|
|
70
|
+
"""The actual outcome of a deletion operation."""
|
|
71
|
+
|
|
72
|
+
PERMANENTLY_DELETED = "permanently_deleted"
|
|
73
|
+
SENT_TO_RECYCLE_BIN = "sent_to_recycle_bin"
|
|
74
|
+
|
|
57
75
|
|
|
58
76
|
@dataclass
|
|
59
77
|
class FileSystemEntry:
|
|
@@ -585,6 +603,7 @@ class DeleteFileRequest(RequestPayload):
|
|
|
585
603
|
path: Path to file/directory to delete (mutually exclusive with file_entry)
|
|
586
604
|
file_entry: FileSystemEntry from directory listing (mutually exclusive with path)
|
|
587
605
|
workspace_only: If True, constrain to workspace directory
|
|
606
|
+
deletion_behavior: How to handle deletion (permanent, recycle bin only, or prefer recycle bin)
|
|
588
607
|
|
|
589
608
|
Results: DeleteFileResultSuccess | DeleteFileResultFailure
|
|
590
609
|
"""
|
|
@@ -592,6 +611,7 @@ class DeleteFileRequest(RequestPayload):
|
|
|
592
611
|
path: str | None = None
|
|
593
612
|
file_entry: FileSystemEntry | None = None
|
|
594
613
|
workspace_only: bool | None = True
|
|
614
|
+
deletion_behavior: DeletionBehavior = DeletionBehavior.PREFER_RECYCLE_BIN
|
|
595
615
|
|
|
596
616
|
|
|
597
617
|
@dataclass
|
|
@@ -603,11 +623,13 @@ class DeleteFileResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
|
603
623
|
deleted_path: The absolute path that was deleted (primary path)
|
|
604
624
|
was_directory: Whether the deleted item was a directory
|
|
605
625
|
deleted_paths: List of all paths that were deleted (for recursive deletes, includes all files/dirs)
|
|
626
|
+
outcome: The actual outcome of the deletion (permanently deleted or sent to recycle bin)
|
|
606
627
|
"""
|
|
607
628
|
|
|
608
629
|
deleted_path: str
|
|
609
630
|
was_directory: bool
|
|
610
631
|
deleted_paths: list[str]
|
|
632
|
+
outcome: DeletionOutcome
|
|
611
633
|
|
|
612
634
|
|
|
613
635
|
@dataclass
|
|
@@ -78,11 +78,13 @@ class CreateStaticFileUploadUrlResultSuccess(WorkflowNotAlteredMixin, ResultPayl
|
|
|
78
78
|
url: Presigned URL for uploading the file
|
|
79
79
|
headers: HTTP headers required for the upload request
|
|
80
80
|
method: HTTP method to use for upload (typically PUT)
|
|
81
|
+
file_url: File URI (file://) for the absolute path where the file will be accessible after upload
|
|
81
82
|
"""
|
|
82
83
|
|
|
83
84
|
url: str
|
|
84
85
|
headers: dict = field(default_factory=dict)
|
|
85
86
|
method: str = "PUT"
|
|
87
|
+
file_url: str = ""
|
|
86
88
|
|
|
87
89
|
|
|
88
90
|
@dataclass
|
|
@@ -100,13 +102,13 @@ class CreateStaticFileUploadUrlResultFailure(WorkflowNotAlteredMixin, ResultPayl
|
|
|
100
102
|
@dataclass
|
|
101
103
|
@PayloadRegistry.register
|
|
102
104
|
class CreateStaticFileDownloadUrlRequest(RequestPayload):
|
|
103
|
-
"""Create a presigned URL for downloading a static file via HTTP GET.
|
|
105
|
+
"""Create a presigned URL for downloading a static file from the staticfiles directory via HTTP GET.
|
|
104
106
|
|
|
105
|
-
Use when: Providing secure file access
|
|
106
|
-
enabling temporary download links, controlling file access permissions.
|
|
107
|
+
Use when: Providing secure file access to files in the staticfiles directory,
|
|
108
|
+
implementing file sharing, enabling temporary download links, controlling file access permissions.
|
|
107
109
|
|
|
108
110
|
Args:
|
|
109
|
-
file_name: Name of the file to be downloaded
|
|
111
|
+
file_name: Name of the file to be downloaded from the staticfiles directory
|
|
110
112
|
|
|
111
113
|
Results: CreateStaticFileDownloadUrlResultSuccess (with URL) | CreateStaticFileDownloadUrlResultFailure (URL creation error)
|
|
112
114
|
"""
|
|
@@ -114,6 +116,26 @@ class CreateStaticFileDownloadUrlRequest(RequestPayload):
|
|
|
114
116
|
file_name: str
|
|
115
117
|
|
|
116
118
|
|
|
119
|
+
@dataclass
|
|
120
|
+
@PayloadRegistry.register
|
|
121
|
+
class CreateStaticFileDownloadUrlFromPathRequest(RequestPayload):
|
|
122
|
+
"""Create a presigned URL for downloading a file from an arbitrary path.
|
|
123
|
+
|
|
124
|
+
Use when: Need to create download URLs for files outside the staticfiles directory,
|
|
125
|
+
working with absolute paths, file:// URLs, or workspace-relative paths.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
file_path: File path or URL. Accepts:
|
|
129
|
+
- file:// URLs (e.g., "file:///absolute/path/to/file.jpg")
|
|
130
|
+
- Absolute paths (e.g., "/absolute/path/to/file.jpg")
|
|
131
|
+
- Workspace-relative paths (e.g., "relative/path/to/file.jpg")
|
|
132
|
+
|
|
133
|
+
Results: CreateStaticFileDownloadUrlResultSuccess (with URL) | CreateStaticFileDownloadUrlResultFailure (URL creation error)
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
file_path: str
|
|
137
|
+
|
|
138
|
+
|
|
117
139
|
@dataclass
|
|
118
140
|
@PayloadRegistry.register
|
|
119
141
|
class CreateStaticFileDownloadUrlResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
@@ -121,9 +143,11 @@ class CreateStaticFileDownloadUrlResultSuccess(WorkflowNotAlteredMixin, ResultPa
|
|
|
121
143
|
|
|
122
144
|
Args:
|
|
123
145
|
url: Presigned URL for downloading the file
|
|
146
|
+
file_url: File URI (file://) for the absolute path to the file that was used to create the download URL
|
|
124
147
|
"""
|
|
125
148
|
|
|
126
149
|
url: str
|
|
150
|
+
file_url: str = ""
|
|
127
151
|
|
|
128
152
|
|
|
129
153
|
@dataclass
|