griptape-nodes 0.43.1__tar.gz → 0.45.0__tar.gz
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-0.43.1 → griptape_nodes-0.45.0}/PKG-INFO +2 -1
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/pyproject.toml +2 -1
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/__init__.py +46 -52
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/app/api.py +37 -41
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/app/app.py +70 -3
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/app/watch.py +5 -2
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +7 -1
- griptape_nodes-0.45.0/src/griptape_nodes/bootstrap/workflow_executors/subprocess_workflow_executor.py +90 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +7 -1
- griptape_nodes-0.45.0/src/griptape_nodes/drivers/storage/base_storage_driver.py +128 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +48 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/drivers/storage/local_storage_driver.py +37 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/exe_types/core_types.py +222 -17
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/exe_types/node_types.py +20 -5
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/machines/control_flow.py +5 -4
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/machines/node_resolution.py +110 -74
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/mcp_server/server.py +16 -8
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/node_library/workflow_registry.py +29 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/app_events.py +3 -8
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/base_events.py +15 -7
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/flow_events.py +2 -1
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/node_events.py +36 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/os_events.py +98 -6
- griptape_nodes-0.45.0/src/griptape_nodes/retained_mode/events/sync_events.py +60 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/workflow_events.py +231 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/griptape_nodes.py +9 -4
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/config_manager.py +1 -1
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/flow_manager.py +6 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/library_manager.py +8 -26
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/node_manager.py +78 -7
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/operation_manager.py +7 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/os_manager.py +133 -8
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/settings.py +5 -0
- griptape_nodes-0.45.0/src/griptape_nodes/retained_mode/managers/sync_manager.py +498 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/workflow_manager.py +736 -33
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/retained_mode.py +23 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/traits/file_system_picker.py +18 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/updater/__init__.py +4 -2
- griptape_nodes-0.45.0/src/griptape_nodes/utils/uv_utils.py +18 -0
- griptape_nodes-0.45.0/src/griptape_nodes/utils/version_utils.py +51 -0
- griptape_nodes-0.43.1/src/griptape_nodes/bootstrap/bootstrap_script.py +0 -54
- griptape_nodes-0.43.1/src/griptape_nodes/bootstrap/post_build_install_script.sh +0 -3
- griptape_nodes-0.43.1/src/griptape_nodes/bootstrap/pre_build_install_script.sh +0 -4
- griptape_nodes-0.43.1/src/griptape_nodes/bootstrap/register_libraries_script.py +0 -32
- griptape_nodes-0.43.1/src/griptape_nodes/bootstrap/structure_config.yaml +0 -15
- griptape_nodes-0.43.1/src/griptape_nodes/bootstrap/workflow_runners/__init__.py +0 -1
- griptape_nodes-0.43.1/src/griptape_nodes/bootstrap/workflow_runners/bootstrap_workflow_runner.py +0 -28
- griptape_nodes-0.43.1/src/griptape_nodes/bootstrap/workflow_runners/local_workflow_runner.py +0 -237
- griptape_nodes-0.43.1/src/griptape_nodes/bootstrap/workflow_runners/subprocess_workflow_runner.py +0 -62
- griptape_nodes-0.43.1/src/griptape_nodes/bootstrap/workflow_runners/workflow_runner.py +0 -11
- griptape_nodes-0.43.1/src/griptape_nodes/drivers/storage/base_storage_driver.py +0 -38
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/README.md +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/app/.python-version +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/app/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/bootstrap/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/bootstrap/workflow_executors/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/drivers/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/drivers/storage/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/drivers/storage/storage_backend.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/exe_types/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/exe_types/connections.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/exe_types/flow.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/exe_types/type_validator.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/machines/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/machines/fsm.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/mcp_server/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/mcp_server/ws_request_manager.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/node_library/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/node_library/advanced_node_library.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/node_library/library_registry.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/py.typed +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/agent_events.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/arbitrary_python_events.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/config_events.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/connection_events.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/context_events.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/execution_events.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/generate_request_payload_schemas.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/library_events.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/logger_events.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/object_events.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/parameter_events.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/payload_registry.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/secrets_events.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/static_file_events.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/events/validation_events.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/agent_manager.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/context_manager.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/engine_identity_manager.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/event_manager.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/data_models.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_fsm.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/base.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/github.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/local_file.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/package.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/sandbox.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_status.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/object_manager.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/secrets_manager.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/session_manager.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/static_files_manager.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/managers/version_compatibility_manager.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/utils/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/utils/engine_identity.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/retained_mode/utils/name_generator.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/traits/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/traits/add_param_button.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/traits/button.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/traits/clamp.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/traits/compare.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/traits/compare_images.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/traits/minmax.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/traits/options.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/traits/slider.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/traits/trait_registry.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/traits/traits.json +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/updater/__main__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/utils/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/utils/dict_utils.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/utils/image_preview.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/utils/metaclasses.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/version_compatibility/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/version_compatibility/versions/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/version_compatibility/versions/v0_39_0/__init__.py +0 -0
- {griptape_nodes-0.43.1 → griptape_nodes-0.45.0}/src/griptape_nodes/version_compatibility/versions/v0_39_0/modified_parameters_set_removal.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: griptape-nodes
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.45.0
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Requires-Dist: griptape>=1.8.0
|
|
6
6
|
Requires-Dist: pydantic>=2.10.6
|
|
@@ -19,6 +19,7 @@ Requires-Dist: imageio-ffmpeg>=0.6.0
|
|
|
19
19
|
Requires-Dist: mcp[ws]>=1.10.1
|
|
20
20
|
Requires-Dist: binaryornot>=0.4.4
|
|
21
21
|
Requires-Dist: pillow>=11.3.0
|
|
22
|
+
Requires-Dist: watchfiles>=1.1.0
|
|
22
23
|
Requires-Dist: austin-dist>=3.7.0 ; extra == 'profiling'
|
|
23
24
|
Requires-Python: >=3.12.0, <3.13
|
|
24
25
|
Provides-Extra: profiling
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "griptape-nodes"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.45.0"
|
|
4
4
|
description = "Add your description here"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12.0, <3.13"
|
|
@@ -23,6 +23,7 @@ dependencies = [
|
|
|
23
23
|
"mcp[ws]>=1.10.1",
|
|
24
24
|
"binaryornot>=0.4.4",
|
|
25
25
|
"pillow>=11.3.0",
|
|
26
|
+
"watchfiles>=1.1.0",
|
|
26
27
|
]
|
|
27
28
|
|
|
28
29
|
[project.optional-dependencies]
|
|
@@ -6,7 +6,6 @@ console = Console()
|
|
|
6
6
|
|
|
7
7
|
with console.status("Loading Griptape Nodes...") as status:
|
|
8
8
|
import argparse
|
|
9
|
-
import importlib.metadata
|
|
10
9
|
import json
|
|
11
10
|
import os
|
|
12
11
|
import shutil
|
|
@@ -15,7 +14,7 @@ with console.status("Loading Griptape Nodes...") as status:
|
|
|
15
14
|
import tempfile
|
|
16
15
|
from dataclasses import dataclass
|
|
17
16
|
from pathlib import Path
|
|
18
|
-
from typing import Any
|
|
17
|
+
from typing import Any
|
|
19
18
|
|
|
20
19
|
import httpx
|
|
21
20
|
from rich.box import HEAVY_EDGE
|
|
@@ -28,10 +27,12 @@ with console.status("Loading Griptape Nodes...") as status:
|
|
|
28
27
|
from griptape_nodes.app import start_app
|
|
29
28
|
from griptape_nodes.drivers.storage import StorageBackend
|
|
30
29
|
from griptape_nodes.drivers.storage.griptape_cloud_storage_driver import GriptapeCloudStorageDriver
|
|
31
|
-
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
30
|
+
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
32
31
|
from griptape_nodes.retained_mode.managers.config_manager import ConfigManager
|
|
33
32
|
from griptape_nodes.retained_mode.managers.os_manager import OSManager
|
|
34
33
|
from griptape_nodes.retained_mode.managers.secrets_manager import SecretsManager
|
|
34
|
+
from griptape_nodes.utils.uv_utils import find_uv_bin
|
|
35
|
+
from griptape_nodes.utils.version_utils import get_complete_version_string, get_current_version, get_install_source
|
|
35
36
|
|
|
36
37
|
CONFIG_DIR = xdg_config_home() / "griptape_nodes"
|
|
37
38
|
DATA_DIR = xdg_data_home() / "griptape_nodes"
|
|
@@ -330,7 +331,12 @@ def _get_args() -> argparse.Namespace:
|
|
|
330
331
|
metavar="SUBCOMMAND",
|
|
331
332
|
required=True,
|
|
332
333
|
)
|
|
333
|
-
config_subparsers.add_parser("show", help="Show configuration values.")
|
|
334
|
+
config_show_parser = config_subparsers.add_parser("show", help="Show configuration values.")
|
|
335
|
+
config_show_parser.add_argument(
|
|
336
|
+
"config_path",
|
|
337
|
+
nargs="?",
|
|
338
|
+
help="Optional config path to show specific value (e.g., 'workspace_directory').",
|
|
339
|
+
)
|
|
334
340
|
config_subparsers.add_parser("list", help="List configuration values.")
|
|
335
341
|
config_subparsers.add_parser("reset", help="Reset configuration to defaults.")
|
|
336
342
|
|
|
@@ -609,7 +615,7 @@ def _get_latest_version(package: str, install_source: str) -> str:
|
|
|
609
615
|
return f"v{data['info']['version']}"
|
|
610
616
|
except httpx.HTTPStatusError as e:
|
|
611
617
|
console.print(f"[red]Error fetching latest version: {e}[/red]")
|
|
612
|
-
return
|
|
618
|
+
return get_current_version()
|
|
613
619
|
elif install_source == "git":
|
|
614
620
|
# We only install auto updating from the 'latest' tag
|
|
615
621
|
revision = LATEST_TAG
|
|
@@ -624,20 +630,20 @@ def _get_latest_version(package: str, install_source: str) -> str:
|
|
|
624
630
|
if "object" in data and "sha" in data["object"]:
|
|
625
631
|
return data["object"]["sha"][:7]
|
|
626
632
|
# Should not happen, but if it does, return the current version
|
|
627
|
-
return
|
|
633
|
+
return get_current_version()
|
|
628
634
|
except httpx.HTTPStatusError as e:
|
|
629
635
|
console.print(f"[red]Error fetching latest version: {e}[/red]")
|
|
630
|
-
return
|
|
636
|
+
return get_current_version()
|
|
631
637
|
else:
|
|
632
638
|
# If the package is installed from a file, just return the current version since the user is likely managing it manually
|
|
633
|
-
return
|
|
639
|
+
return get_current_version()
|
|
634
640
|
|
|
635
641
|
|
|
636
642
|
def _auto_update_self() -> None:
|
|
637
643
|
"""Automatically updates the script to the latest version if the user confirms."""
|
|
638
644
|
console.print("[bold green]Checking for updates...[/bold green]")
|
|
639
|
-
source, commit_id =
|
|
640
|
-
current_version =
|
|
645
|
+
source, commit_id = get_install_source()
|
|
646
|
+
current_version = get_current_version()
|
|
641
647
|
latest_version = _get_latest_version(PACKAGE_NAME, source)
|
|
642
648
|
|
|
643
649
|
if source == "git" and commit_id is not None:
|
|
@@ -663,10 +669,10 @@ def _update_self() -> None:
|
|
|
663
669
|
|
|
664
670
|
def _sync_libraries() -> None:
|
|
665
671
|
"""Download and sync Griptape Nodes libraries, copying only directories from synced libraries."""
|
|
666
|
-
install_source, _ =
|
|
672
|
+
install_source, _ = get_install_source()
|
|
667
673
|
# Unless we're installed from PyPi, grab libraries from the 'latest' tag
|
|
668
674
|
if install_source == "pypi":
|
|
669
|
-
version =
|
|
675
|
+
version = get_current_version()
|
|
670
676
|
else:
|
|
671
677
|
version = LATEST_TAG
|
|
672
678
|
|
|
@@ -681,12 +687,13 @@ def _sync_libraries() -> None:
|
|
|
681
687
|
|
|
682
688
|
# Streaming download with a tiny progress bar
|
|
683
689
|
with httpx.stream("GET", tar_url, follow_redirects=True) as r, Progress() as progress:
|
|
690
|
+
task = progress.add_task("[green]Downloading...", total=int(r.headers.get("Content-Length", 0)))
|
|
691
|
+
progress.start()
|
|
684
692
|
try:
|
|
685
693
|
r.raise_for_status()
|
|
686
694
|
except httpx.HTTPStatusError as e:
|
|
687
695
|
console.print(f"[red]Error fetching libraries: {e}[/red]")
|
|
688
696
|
return
|
|
689
|
-
task = progress.add_task("[green]Downloading...", total=int(r.headers.get("Content-Length", 0)))
|
|
690
697
|
with tar_path.open("wb") as f:
|
|
691
698
|
for chunk in r.iter_bytes():
|
|
692
699
|
f.write(chunk)
|
|
@@ -724,18 +731,29 @@ def _sync_libraries() -> None:
|
|
|
724
731
|
|
|
725
732
|
def _print_current_version() -> None:
|
|
726
733
|
"""Prints the current version of the script."""
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
console.print(f"[bold green]{version} ({source})[/bold green]")
|
|
731
|
-
else:
|
|
732
|
-
console.print(f"[bold green]{version} ({source} - {commit_id})[/bold green]")
|
|
734
|
+
version_string = get_complete_version_string()
|
|
735
|
+
console.print(f"[bold green]{version_string}[/bold green]")
|
|
736
|
+
|
|
733
737
|
|
|
738
|
+
def _print_user_config(config_path: str | None = None) -> None:
|
|
739
|
+
"""Prints the user configuration from the config file.
|
|
734
740
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
741
|
+
Args:
|
|
742
|
+
config_path: Optional path to specific config value. If None, prints entire config.
|
|
743
|
+
"""
|
|
744
|
+
if config_path is None:
|
|
745
|
+
config = config_manager.merged_config
|
|
746
|
+
sys.stdout.write(json.dumps(config, indent=2))
|
|
747
|
+
else:
|
|
748
|
+
try:
|
|
749
|
+
value = config_manager.get_config_value(config_path)
|
|
750
|
+
if isinstance(value, (dict, list)):
|
|
751
|
+
sys.stdout.write(json.dumps(value, indent=2))
|
|
752
|
+
else:
|
|
753
|
+
sys.stdout.write(str(value))
|
|
754
|
+
except (KeyError, AttributeError, ValueError):
|
|
755
|
+
console.print(f"[bold red]Config path '{config_path}' not found[/bold red]")
|
|
756
|
+
sys.exit(1)
|
|
739
757
|
|
|
740
758
|
|
|
741
759
|
def _list_user_configs() -> None:
|
|
@@ -791,7 +809,10 @@ def _uninstall_self() -> None:
|
|
|
791
809
|
# Remove the executable
|
|
792
810
|
console.print("[bold]Removing the executable...[/bold]")
|
|
793
811
|
console.print("[bold yellow]When done, press Enter to exit.[/bold yellow]")
|
|
794
|
-
|
|
812
|
+
|
|
813
|
+
# Remove the tool using UV
|
|
814
|
+
uv_path = find_uv_bin()
|
|
815
|
+
os_manager.replace_process([uv_path, "tool", "uninstall", "griptape-nodes"])
|
|
795
816
|
|
|
796
817
|
|
|
797
818
|
def _parse_key_value_pairs(pairs: list[str] | None) -> dict[str, Any] | None:
|
|
@@ -857,7 +878,7 @@ def _process_args(args: argparse.Namespace) -> None: # noqa: C901, PLR0912
|
|
|
857
878
|
elif args.subcommand == "reset":
|
|
858
879
|
_reset_user_config()
|
|
859
880
|
elif args.subcommand == "show":
|
|
860
|
-
_print_user_config()
|
|
881
|
+
_print_user_config(args.config_path)
|
|
861
882
|
elif args.command == "self":
|
|
862
883
|
if args.subcommand == "update":
|
|
863
884
|
_update_self()
|
|
@@ -873,33 +894,6 @@ def _process_args(args: argparse.Namespace) -> None: # noqa: C901, PLR0912
|
|
|
873
894
|
raise ValueError(msg)
|
|
874
895
|
|
|
875
896
|
|
|
876
|
-
def __get_current_version() -> str:
|
|
877
|
-
"""Returns the current version of the Griptape Nodes package."""
|
|
878
|
-
return f"v{engine_version}"
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
def __get_install_source() -> tuple[Literal["git", "file", "pypi"], str | None]:
|
|
882
|
-
"""Determines the install source of the Griptape Nodes package.
|
|
883
|
-
|
|
884
|
-
Returns:
|
|
885
|
-
tuple: A tuple containing the install source and commit ID (if applicable).
|
|
886
|
-
"""
|
|
887
|
-
dist = importlib.metadata.distribution("griptape_nodes")
|
|
888
|
-
direct_url_text = dist.read_text("direct_url.json")
|
|
889
|
-
# installing from pypi doesn't have a direct_url.json file
|
|
890
|
-
if direct_url_text is None:
|
|
891
|
-
return "pypi", None
|
|
892
|
-
|
|
893
|
-
direct_url_info = json.loads(direct_url_text)
|
|
894
|
-
url = direct_url_info.get("url")
|
|
895
|
-
if url.startswith("file://"):
|
|
896
|
-
return "file", None
|
|
897
|
-
if "vcs_info" in direct_url_info:
|
|
898
|
-
return "git", direct_url_info["vcs_info"].get("commit_id")[:7]
|
|
899
|
-
# Fall back to pypi if no other source is found
|
|
900
|
-
return "pypi", None
|
|
901
|
-
|
|
902
|
-
|
|
903
897
|
def __init_system_config() -> None:
|
|
904
898
|
"""Initializes the system config directory if it doesn't exist."""
|
|
905
899
|
if not CONFIG_DIR.exists():
|
|
@@ -13,8 +13,6 @@ from fastapi.middleware.cors import CORSMiddleware
|
|
|
13
13
|
from fastapi.staticfiles import StaticFiles
|
|
14
14
|
from rich.logging import RichHandler
|
|
15
15
|
|
|
16
|
-
from griptape_nodes.retained_mode.events.base_events import EventRequest, deserialize_event
|
|
17
|
-
|
|
18
16
|
if TYPE_CHECKING:
|
|
19
17
|
from queue import Queue
|
|
20
18
|
|
|
@@ -27,9 +25,11 @@ STATIC_SERVER_PORT = int(os.getenv("STATIC_SERVER_PORT", "8124"))
|
|
|
27
25
|
# URL path for the static server
|
|
28
26
|
STATIC_SERVER_URL = os.getenv("STATIC_SERVER_URL", "/static")
|
|
29
27
|
# Log level for the static server
|
|
30
|
-
STATIC_SERVER_LOG_LEVEL = os.getenv("STATIC_SERVER_LOG_LEVEL", "
|
|
28
|
+
STATIC_SERVER_LOG_LEVEL = os.getenv("STATIC_SERVER_LOG_LEVEL", "ERROR").lower()
|
|
31
29
|
|
|
32
30
|
logger = logging.getLogger("griptape_nodes_api")
|
|
31
|
+
logging.getLogger("uvicorn").addHandler(RichHandler(show_time=True, show_path=False, markup=True, rich_tracebacks=True))
|
|
32
|
+
|
|
33
33
|
|
|
34
34
|
# Global event queue - initialized as None and set when starting the API
|
|
35
35
|
event_queue: Queue | None = None
|
|
@@ -124,8 +124,41 @@ async def _list_static_files(static_directory: Annotated[Path, Depends(get_stati
|
|
|
124
124
|
return {"files": file_names}
|
|
125
125
|
|
|
126
126
|
|
|
127
|
+
@app.delete("/static-files/{file_path:path}")
|
|
128
|
+
async def _delete_static_file(file_path: str, static_directory: Annotated[Path, Depends(get_static_dir)]) -> dict:
|
|
129
|
+
"""Delete a static file from the static server."""
|
|
130
|
+
if not STATIC_SERVER_ENABLED:
|
|
131
|
+
msg = "Static server is not enabled. Please set STATIC_SERVER_ENABLED to True."
|
|
132
|
+
raise HTTPException(status_code=500, detail=msg)
|
|
133
|
+
|
|
134
|
+
file_full_path = Path(static_directory / file_path)
|
|
135
|
+
|
|
136
|
+
# Check if file exists
|
|
137
|
+
if not file_full_path.exists():
|
|
138
|
+
logger.warning("File not found for deletion: %s", file_path)
|
|
139
|
+
raise HTTPException(status_code=404, detail=f"File {file_path} not found")
|
|
140
|
+
|
|
141
|
+
# Check if it's actually a file (not a directory)
|
|
142
|
+
if not file_full_path.is_file():
|
|
143
|
+
msg = f"Path {file_path} is not a file"
|
|
144
|
+
logger.error(msg)
|
|
145
|
+
raise HTTPException(status_code=400, detail=msg)
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
file_full_path.unlink()
|
|
149
|
+
except (OSError, PermissionError) as e:
|
|
150
|
+
msg = f"Failed to delete file {file_path}: {e}"
|
|
151
|
+
logger.error(msg)
|
|
152
|
+
raise HTTPException(status_code=500, detail=msg) from e
|
|
153
|
+
else:
|
|
154
|
+
logger.info("Successfully deleted static file: %s", file_path)
|
|
155
|
+
return {"message": f"File {file_path} deleted successfully"}
|
|
156
|
+
|
|
157
|
+
|
|
127
158
|
@app.post("/engines/request")
|
|
128
159
|
async def _create_event(request: Request, queue: Annotated[Queue, Depends(get_event_queue)]) -> None:
|
|
160
|
+
from .app import _process_api_event
|
|
161
|
+
|
|
129
162
|
body = await request.json()
|
|
130
163
|
_process_api_event(body, queue)
|
|
131
164
|
|
|
@@ -136,10 +169,6 @@ def start_api(static_directory: Path, queue: Queue) -> None:
|
|
|
136
169
|
event_queue = queue
|
|
137
170
|
static_dir = static_directory
|
|
138
171
|
|
|
139
|
-
logging.getLogger("uvicorn").addHandler(
|
|
140
|
-
RichHandler(show_time=True, show_path=False, markup=True, rich_tracebacks=True)
|
|
141
|
-
)
|
|
142
|
-
|
|
143
172
|
if not static_dir.exists():
|
|
144
173
|
static_dir.mkdir(parents=True, exist_ok=True)
|
|
145
174
|
|
|
@@ -151,7 +180,7 @@ def start_api(static_directory: Path, queue: Queue) -> None:
|
|
|
151
180
|
"http://localhost:5173",
|
|
152
181
|
],
|
|
153
182
|
allow_credentials=True,
|
|
154
|
-
allow_methods=["OPTIONS", "GET", "POST", "PUT"],
|
|
183
|
+
allow_methods=["OPTIONS", "GET", "POST", "PUT", "DELETE"],
|
|
155
184
|
allow_headers=["*"],
|
|
156
185
|
)
|
|
157
186
|
|
|
@@ -164,36 +193,3 @@ def start_api(static_directory: Path, queue: Queue) -> None:
|
|
|
164
193
|
uvicorn.run(
|
|
165
194
|
app, host=STATIC_SERVER_HOST, port=STATIC_SERVER_PORT, log_level=STATIC_SERVER_LOG_LEVEL, log_config=None
|
|
166
195
|
)
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
def _process_api_event(event: dict, event_queue: Queue) -> None:
|
|
170
|
-
"""Process API events and send them to the event queue."""
|
|
171
|
-
payload = event.get("payload", {})
|
|
172
|
-
|
|
173
|
-
try:
|
|
174
|
-
payload["request"]
|
|
175
|
-
except KeyError:
|
|
176
|
-
msg = "Error: 'request' was expected but not found."
|
|
177
|
-
raise RuntimeError(msg) from None
|
|
178
|
-
|
|
179
|
-
try:
|
|
180
|
-
event_type = payload["event_type"]
|
|
181
|
-
if event_type != "EventRequest":
|
|
182
|
-
msg = "Error: 'event_type' was found on request, but did not match 'EventRequest' as expected."
|
|
183
|
-
raise RuntimeError(msg) from None
|
|
184
|
-
except KeyError:
|
|
185
|
-
msg = "Error: 'event_type' not found in request."
|
|
186
|
-
raise RuntimeError(msg) from None
|
|
187
|
-
|
|
188
|
-
# Now attempt to convert it into an EventRequest.
|
|
189
|
-
try:
|
|
190
|
-
request_event = deserialize_event(json_data=payload)
|
|
191
|
-
if not isinstance(request_event, EventRequest):
|
|
192
|
-
msg = f"Deserialized event is not an EventRequest: {type(request_event)}"
|
|
193
|
-
raise TypeError(msg) # noqa: TRY301
|
|
194
|
-
except Exception as e:
|
|
195
|
-
msg = f"Unable to convert request JSON into a valid EventRequest object. Error Message: '{e}'"
|
|
196
|
-
raise RuntimeError(msg) from None
|
|
197
|
-
|
|
198
|
-
# Add the event to the queue
|
|
199
|
-
event_queue.put(request_event)
|
|
@@ -9,7 +9,7 @@ import sys
|
|
|
9
9
|
import threading
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from queue import Queue
|
|
12
|
-
from typing import Any
|
|
12
|
+
from typing import Any, cast
|
|
13
13
|
from urllib.parse import urljoin
|
|
14
14
|
|
|
15
15
|
from griptape.events import (
|
|
@@ -24,9 +24,9 @@ from websockets.asyncio.client import connect
|
|
|
24
24
|
from websockets.exceptions import ConnectionClosed, WebSocketException
|
|
25
25
|
|
|
26
26
|
from griptape_nodes.mcp_server.server import main as mcp_server
|
|
27
|
+
from griptape_nodes.retained_mode.events import app_events, execution_events
|
|
27
28
|
|
|
28
29
|
# This import is necessary to register all events, even if not technically used
|
|
29
|
-
from griptape_nodes.retained_mode.events import app_events, execution_events
|
|
30
30
|
from griptape_nodes.retained_mode.events.base_events import (
|
|
31
31
|
AppEvent,
|
|
32
32
|
EventRequest,
|
|
@@ -36,11 +36,14 @@ from griptape_nodes.retained_mode.events.base_events import (
|
|
|
36
36
|
ExecutionGriptapeNodeEvent,
|
|
37
37
|
GriptapeNodeEvent,
|
|
38
38
|
ProgressEvent,
|
|
39
|
+
RequestPayload,
|
|
40
|
+
SkipTheLineMixin,
|
|
41
|
+
deserialize_event,
|
|
39
42
|
)
|
|
40
43
|
from griptape_nodes.retained_mode.events.logger_events import LogHandlerEvent
|
|
41
44
|
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
42
45
|
|
|
43
|
-
from .api import
|
|
46
|
+
from .api import start_api
|
|
44
47
|
|
|
45
48
|
# This is a global event queue that will be used to pass events between threads
|
|
46
49
|
event_queue = Queue()
|
|
@@ -395,3 +398,67 @@ def __schedule_async_task(coro: Any) -> None:
|
|
|
395
398
|
asyncio.run_coroutine_threadsafe(coro, event_loop)
|
|
396
399
|
else:
|
|
397
400
|
logger.warning("Event loop not available for scheduling async task")
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def _process_api_event(event: dict, event_queue: Queue) -> None:
|
|
404
|
+
"""Process API events and send them to the event queue."""
|
|
405
|
+
payload = event.get("payload", {})
|
|
406
|
+
|
|
407
|
+
try:
|
|
408
|
+
payload["request"]
|
|
409
|
+
except KeyError:
|
|
410
|
+
msg = "Error: 'request' was expected but not found."
|
|
411
|
+
raise RuntimeError(msg) from None
|
|
412
|
+
|
|
413
|
+
try:
|
|
414
|
+
event_type = payload["event_type"]
|
|
415
|
+
if event_type != "EventRequest":
|
|
416
|
+
msg = "Error: 'event_type' was found on request, but did not match 'EventRequest' as expected."
|
|
417
|
+
raise RuntimeError(msg) from None
|
|
418
|
+
except KeyError:
|
|
419
|
+
msg = "Error: 'event_type' not found in request."
|
|
420
|
+
raise RuntimeError(msg) from None
|
|
421
|
+
|
|
422
|
+
# Now attempt to convert it into an EventRequest.
|
|
423
|
+
try:
|
|
424
|
+
request_event = deserialize_event(json_data=payload)
|
|
425
|
+
if not isinstance(request_event, EventRequest):
|
|
426
|
+
msg = f"Deserialized event is not an EventRequest: {type(request_event)}"
|
|
427
|
+
raise TypeError(msg) # noqa: TRY301
|
|
428
|
+
except Exception as e:
|
|
429
|
+
msg = f"Unable to convert request JSON into a valid EventRequest object. Error Message: '{e}'"
|
|
430
|
+
raise RuntimeError(msg) from None
|
|
431
|
+
|
|
432
|
+
# Check if the event implements SkipTheLineMixin for priority processing
|
|
433
|
+
if isinstance(request_event.request, SkipTheLineMixin):
|
|
434
|
+
# Handle the event immediately without queuing
|
|
435
|
+
# The request is guaranteed to be a RequestPayload since it passed earlier validation
|
|
436
|
+
result_payload = GriptapeNodes.handle_request(
|
|
437
|
+
cast("RequestPayload", request_event.request),
|
|
438
|
+
response_topic=request_event.response_topic,
|
|
439
|
+
request_id=request_event.request_id,
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
# Create the result event and emit response immediately
|
|
443
|
+
if result_payload.succeeded():
|
|
444
|
+
result_event = EventResultSuccess(
|
|
445
|
+
request=cast("RequestPayload", request_event.request),
|
|
446
|
+
request_id=request_event.request_id,
|
|
447
|
+
result=result_payload,
|
|
448
|
+
response_topic=request_event.response_topic,
|
|
449
|
+
)
|
|
450
|
+
dest_socket = "success_result"
|
|
451
|
+
else:
|
|
452
|
+
result_event = EventResultFailure(
|
|
453
|
+
request=cast("RequestPayload", request_event.request),
|
|
454
|
+
request_id=request_event.request_id,
|
|
455
|
+
result=result_payload,
|
|
456
|
+
response_topic=request_event.response_topic,
|
|
457
|
+
)
|
|
458
|
+
dest_socket = "failure_result"
|
|
459
|
+
|
|
460
|
+
# Emit the response immediately
|
|
461
|
+
__schedule_async_task(__emit_message(dest_socket, result_event.json(), topic=result_event.response_topic))
|
|
462
|
+
else:
|
|
463
|
+
# Add the event to the queue for normal processing
|
|
464
|
+
event_queue.put(request_event)
|
|
@@ -8,6 +8,8 @@ from typing import Any
|
|
|
8
8
|
from watchdog.events import PatternMatchingEventHandler
|
|
9
9
|
from watchdog.observers import Observer
|
|
10
10
|
|
|
11
|
+
from griptape_nodes.utils.uv_utils import find_uv_bin
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
class ReloadHandler(PatternMatchingEventHandler):
|
|
13
15
|
def __init__(
|
|
@@ -30,8 +32,9 @@ class ReloadHandler(PatternMatchingEventHandler):
|
|
|
30
32
|
def start_process(self) -> None:
|
|
31
33
|
if self.process:
|
|
32
34
|
self.process.terminate()
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
uv_path = find_uv_bin()
|
|
36
|
+
self.process = subprocess.Popen( # noqa: S603
|
|
37
|
+
[uv_path, "run", "gtn"],
|
|
35
38
|
stdout=sys.stdout,
|
|
36
39
|
stderr=sys.stderr,
|
|
37
40
|
)
|
|
@@ -144,7 +144,13 @@ class LocalWorkflowExecutor(WorkflowExecutor):
|
|
|
144
144
|
|
|
145
145
|
return output
|
|
146
146
|
|
|
147
|
-
def run(
|
|
147
|
+
def run(
|
|
148
|
+
self,
|
|
149
|
+
workflow_name: str,
|
|
150
|
+
flow_input: Any,
|
|
151
|
+
storage_backend: StorageBackend = StorageBackend.LOCAL,
|
|
152
|
+
**kwargs: Any, # noqa: ARG002
|
|
153
|
+
) -> None:
|
|
148
154
|
"""Executes a local workflow.
|
|
149
155
|
|
|
150
156
|
Executes a workflow by setting up event listeners, registering libraries,
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
import sys
|
|
3
|
+
import threading
|
|
4
|
+
from multiprocessing import Process, Queue
|
|
5
|
+
from multiprocessing import Queue as ProcessQueue
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from griptape_nodes.app.api import start_api
|
|
10
|
+
from griptape_nodes.app.app import _build_static_dir
|
|
11
|
+
from griptape_nodes.bootstrap.workflow_executors.local_workflow_executor import LocalWorkflowExecutor
|
|
12
|
+
from griptape_nodes.bootstrap.workflow_executors.workflow_executor import WorkflowExecutor
|
|
13
|
+
from griptape_nodes.drivers.storage.storage_backend import StorageBackend
|
|
14
|
+
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SubprocessWorkflowExecutor(WorkflowExecutor):
|
|
18
|
+
@classmethod
|
|
19
|
+
def load_workflow(cls, path_to_workflow: str) -> None:
|
|
20
|
+
"""Load a workflow from a file."""
|
|
21
|
+
# Ensure file_path is a Path object
|
|
22
|
+
file_path = Path(path_to_workflow)
|
|
23
|
+
|
|
24
|
+
# Generate a unique module name
|
|
25
|
+
module_name = f"gtn_dynamic_module_{file_path.name.replace('.', '_')}_{hash(str(file_path))}"
|
|
26
|
+
|
|
27
|
+
# Load the module specification
|
|
28
|
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
29
|
+
if spec is None or spec.loader is None:
|
|
30
|
+
msg = f"Could not load module specification from {file_path}"
|
|
31
|
+
raise ImportError(msg)
|
|
32
|
+
|
|
33
|
+
# Create the module
|
|
34
|
+
module = importlib.util.module_from_spec(spec)
|
|
35
|
+
|
|
36
|
+
# Add to sys.modules to handle recursive imports
|
|
37
|
+
sys.modules[module_name] = module
|
|
38
|
+
|
|
39
|
+
# Execute the module
|
|
40
|
+
spec.loader.exec_module(module)
|
|
41
|
+
|
|
42
|
+
@staticmethod
|
|
43
|
+
def _subprocess_entry(
|
|
44
|
+
exception_queue: Queue,
|
|
45
|
+
workflow_name: str,
|
|
46
|
+
flow_input: Any,
|
|
47
|
+
workflow_path: str | None = None,
|
|
48
|
+
) -> None:
|
|
49
|
+
try:
|
|
50
|
+
static_dir = _build_static_dir()
|
|
51
|
+
event_queue = ProcessQueue()
|
|
52
|
+
threading.Thread(target=start_api, args=(static_dir, event_queue), daemon=True).start()
|
|
53
|
+
|
|
54
|
+
if workflow_path:
|
|
55
|
+
SubprocessWorkflowExecutor.load_workflow(workflow_path)
|
|
56
|
+
context_manager = GriptapeNodes.ContextManager()
|
|
57
|
+
workflow_name = context_manager.get_current_workflow_name()
|
|
58
|
+
|
|
59
|
+
workflow_runner = LocalWorkflowExecutor()
|
|
60
|
+
workflow_runner.run(workflow_name, flow_input, StorageBackend.LOCAL)
|
|
61
|
+
except Exception as e:
|
|
62
|
+
exception_queue.put(e)
|
|
63
|
+
raise
|
|
64
|
+
|
|
65
|
+
def run(
|
|
66
|
+
self,
|
|
67
|
+
workflow_name: str,
|
|
68
|
+
flow_input: Any,
|
|
69
|
+
storage_backend: StorageBackend = StorageBackend.LOCAL, # noqa: ARG002
|
|
70
|
+
**kwargs: Any,
|
|
71
|
+
) -> None:
|
|
72
|
+
workflow_path = kwargs.get("workflow_path")
|
|
73
|
+
exception_queue = Queue()
|
|
74
|
+
process = Process(
|
|
75
|
+
target=self._subprocess_entry,
|
|
76
|
+
args=(exception_queue, workflow_name, flow_input, workflow_path),
|
|
77
|
+
)
|
|
78
|
+
process.start()
|
|
79
|
+
process.join()
|
|
80
|
+
|
|
81
|
+
if not exception_queue.empty():
|
|
82
|
+
exception = exception_queue.get_nowait()
|
|
83
|
+
if isinstance(exception, Exception):
|
|
84
|
+
raise exception
|
|
85
|
+
msg = f"Expected an Exception but got: {type(exception)}"
|
|
86
|
+
raise RuntimeError(msg)
|
|
87
|
+
|
|
88
|
+
if process.exitcode != 0:
|
|
89
|
+
msg = f"Process exited with code {process.exitcode} but no exception was raised."
|
|
90
|
+
raise RuntimeError(msg)
|
|
@@ -9,5 +9,11 @@ logger = logging.getLogger(__name__)
|
|
|
9
9
|
|
|
10
10
|
class WorkflowExecutor(ABC):
|
|
11
11
|
@abstractmethod
|
|
12
|
-
def run(
|
|
12
|
+
def run(
|
|
13
|
+
self,
|
|
14
|
+
workflow_name: str,
|
|
15
|
+
flow_input: Any,
|
|
16
|
+
storage_backend: StorageBackend = StorageBackend.LOCAL,
|
|
17
|
+
**kwargs: Any,
|
|
18
|
+
) -> None:
|
|
13
19
|
pass
|