griptape-nodes 0.53.0__py3-none-any.whl → 0.54.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/__init__.py +5 -2
- griptape_nodes/app/app.py +4 -26
- griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +35 -5
- griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +15 -1
- griptape_nodes/cli/commands/config.py +4 -1
- griptape_nodes/cli/commands/init.py +5 -3
- griptape_nodes/cli/commands/libraries.py +14 -8
- griptape_nodes/cli/commands/models.py +504 -0
- griptape_nodes/cli/commands/self.py +5 -2
- griptape_nodes/cli/main.py +11 -1
- griptape_nodes/cli/shared.py +0 -9
- griptape_nodes/common/directed_graph.py +17 -1
- griptape_nodes/drivers/storage/base_storage_driver.py +40 -20
- griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +24 -29
- griptape_nodes/drivers/storage/local_storage_driver.py +17 -13
- griptape_nodes/exe_types/node_types.py +219 -14
- griptape_nodes/exe_types/param_components/__init__.py +1 -0
- griptape_nodes/exe_types/param_components/execution_status_component.py +138 -0
- griptape_nodes/machines/control_flow.py +129 -92
- griptape_nodes/machines/dag_builder.py +207 -0
- griptape_nodes/machines/parallel_resolution.py +264 -276
- griptape_nodes/machines/sequential_resolution.py +9 -7
- griptape_nodes/node_library/library_registry.py +34 -1
- griptape_nodes/retained_mode/events/app_events.py +5 -1
- griptape_nodes/retained_mode/events/base_events.py +7 -7
- griptape_nodes/retained_mode/events/config_events.py +30 -0
- griptape_nodes/retained_mode/events/execution_events.py +2 -2
- griptape_nodes/retained_mode/events/model_events.py +296 -0
- griptape_nodes/retained_mode/griptape_nodes.py +10 -1
- griptape_nodes/retained_mode/managers/agent_manager.py +14 -0
- griptape_nodes/retained_mode/managers/config_manager.py +44 -3
- griptape_nodes/retained_mode/managers/event_manager.py +8 -2
- griptape_nodes/retained_mode/managers/flow_manager.py +45 -14
- griptape_nodes/retained_mode/managers/library_manager.py +3 -3
- griptape_nodes/retained_mode/managers/model_manager.py +1107 -0
- griptape_nodes/retained_mode/managers/node_manager.py +26 -26
- griptape_nodes/retained_mode/managers/object_manager.py +1 -1
- griptape_nodes/retained_mode/managers/os_manager.py +6 -6
- griptape_nodes/retained_mode/managers/settings.py +87 -9
- griptape_nodes/retained_mode/managers/static_files_manager.py +77 -9
- griptape_nodes/retained_mode/managers/sync_manager.py +10 -5
- griptape_nodes/retained_mode/managers/workflow_manager.py +98 -92
- griptape_nodes/retained_mode/retained_mode.py +19 -0
- griptape_nodes/servers/__init__.py +1 -0
- griptape_nodes/{mcp_server/server.py → servers/mcp.py} +1 -1
- griptape_nodes/{app/api.py → servers/static.py} +43 -40
- griptape_nodes/traits/button.py +124 -6
- griptape_nodes/traits/multi_options.py +188 -0
- griptape_nodes/traits/numbers_selector.py +77 -0
- griptape_nodes/traits/options.py +93 -2
- griptape_nodes/utils/async_utils.py +31 -0
- {griptape_nodes-0.53.0.dist-info → griptape_nodes-0.54.0.dist-info}/METADATA +3 -1
- {griptape_nodes-0.53.0.dist-info → griptape_nodes-0.54.0.dist-info}/RECORD +56 -47
- {griptape_nodes-0.53.0.dist-info → griptape_nodes-0.54.0.dist-info}/WHEEL +1 -1
- /griptape_nodes/{mcp_server → servers}/ws_request_manager.py +0 -0
- {griptape_nodes-0.53.0.dist-info → griptape_nodes-0.54.0.dist-info}/entry_points.txt +0 -0
griptape_nodes/__init__.py
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import sys
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
def main() -> None:
|
|
8
10
|
"""Main entry point for the Griptape Nodes CLI."""
|
|
@@ -12,8 +14,9 @@ def main() -> None:
|
|
|
12
14
|
# but current engine relies on importing files rather than packages.
|
|
13
15
|
sys.path.append(str(Path.cwd()))
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
console = Console()
|
|
18
|
+
with console.status("[bold green]Loading Griptape Nodes...", spinner="dots"):
|
|
19
|
+
from griptape_nodes.cli.main import app
|
|
17
20
|
|
|
18
21
|
app()
|
|
19
22
|
|
griptape_nodes/app/app.py
CHANGED
|
@@ -7,7 +7,6 @@ import os
|
|
|
7
7
|
import sys
|
|
8
8
|
import threading
|
|
9
9
|
from dataclasses import dataclass
|
|
10
|
-
from pathlib import Path
|
|
11
10
|
from typing import Any
|
|
12
11
|
from urllib.parse import urljoin
|
|
13
12
|
|
|
@@ -18,8 +17,6 @@ from rich.panel import Panel
|
|
|
18
17
|
from websockets.asyncio.client import connect
|
|
19
18
|
from websockets.exceptions import ConnectionClosed, ConnectionClosedError, WebSocketException
|
|
20
19
|
|
|
21
|
-
from griptape_nodes.app.api import start_static_server
|
|
22
|
-
from griptape_nodes.mcp_server.server import start_mcp_server
|
|
23
20
|
from griptape_nodes.retained_mode.events import app_events, execution_events
|
|
24
21
|
|
|
25
22
|
# This import is necessary to register all events, even if not technically used
|
|
@@ -80,9 +77,6 @@ websocket_event_loop: asyncio.AbstractEventLoop | None = None
|
|
|
80
77
|
websocket_event_loop_ready = threading.Event()
|
|
81
78
|
|
|
82
79
|
|
|
83
|
-
# Whether to enable the static server
|
|
84
|
-
STATIC_SERVER_ENABLED = os.getenv("STATIC_SERVER_ENABLED", "true").lower() == "true"
|
|
85
|
-
|
|
86
80
|
# Semaphore to limit concurrent requests
|
|
87
81
|
REQUEST_SEMAPHORE = asyncio.Semaphore(100)
|
|
88
82
|
|
|
@@ -135,14 +129,6 @@ async def astart_app() -> None:
|
|
|
135
129
|
main_loop = asyncio.get_running_loop()
|
|
136
130
|
|
|
137
131
|
try:
|
|
138
|
-
# Start MCP server in daemon thread
|
|
139
|
-
threading.Thread(target=start_mcp_server, args=(api_key,), daemon=True, name="mcp-server").start()
|
|
140
|
-
|
|
141
|
-
# Start static server in daemon thread if enabled
|
|
142
|
-
if STATIC_SERVER_ENABLED:
|
|
143
|
-
static_dir = _build_static_dir()
|
|
144
|
-
threading.Thread(target=start_static_server, args=(static_dir,), daemon=True, name="static-server").start()
|
|
145
|
-
|
|
146
132
|
# Start WebSocket tasks in daemon thread
|
|
147
133
|
threading.Thread(
|
|
148
134
|
target=_start_websocket_connection, args=(api_key, main_loop), daemon=True, name="websocket-tasks"
|
|
@@ -187,13 +173,11 @@ async def _run_websocket_tasks(api_key: str, main_loop: asyncio.AbstractEventLoo
|
|
|
187
173
|
initialized = False
|
|
188
174
|
|
|
189
175
|
async for ws_connection in connection_stream:
|
|
190
|
-
logger.
|
|
176
|
+
logger.debug("WebSocket connection established")
|
|
191
177
|
try:
|
|
192
178
|
# Emit initialization event only for the first connection
|
|
193
179
|
if not initialized:
|
|
194
|
-
griptape_nodes.EventManager().
|
|
195
|
-
main_loop, AppEvent(payload=app_events.AppInitializationComplete())
|
|
196
|
-
)
|
|
180
|
+
griptape_nodes.EventManager().put_event(AppEvent(payload=app_events.AppInitializationComplete()))
|
|
197
181
|
initialized = True
|
|
198
182
|
|
|
199
183
|
# Emit connection established event for every connection
|
|
@@ -233,12 +217,6 @@ def _ensure_api_key() -> str:
|
|
|
233
217
|
return api_key
|
|
234
218
|
|
|
235
219
|
|
|
236
|
-
def _build_static_dir() -> Path:
|
|
237
|
-
"""Build the static directory path based on the workspace configuration."""
|
|
238
|
-
config_manager = griptape_nodes.ConfigManager()
|
|
239
|
-
return Path(config_manager.workspace_path) / config_manager.merged_config["static_files_directory"]
|
|
240
|
-
|
|
241
|
-
|
|
242
220
|
async def _process_incoming_messages(ws_connection: Any, main_loop: asyncio.AbstractEventLoop) -> None:
|
|
243
221
|
"""Process incoming WebSocket requests from Nodes API."""
|
|
244
222
|
logger.debug("Processing incoming WebSocket requests from WebSocket connection")
|
|
@@ -366,7 +344,7 @@ async def _send_unsubscribe_command(ws_connection: Any, topic: str) -> None:
|
|
|
366
344
|
|
|
367
345
|
async def _process_event_queue() -> None:
|
|
368
346
|
"""Process events concurrently - runs on main thread."""
|
|
369
|
-
logger.
|
|
347
|
+
logger.debug("Starting event queue processor on main thread")
|
|
370
348
|
background_tasks = set()
|
|
371
349
|
|
|
372
350
|
def _handle_task_result(task: asyncio.Task) -> None:
|
|
@@ -399,7 +377,7 @@ async def _process_event_queue() -> None:
|
|
|
399
377
|
task.add_done_callback(_handle_task_result)
|
|
400
378
|
event_queue.task_done()
|
|
401
379
|
except asyncio.CancelledError:
|
|
402
|
-
logger.
|
|
380
|
+
logger.debug("Event queue processor shutdown complete")
|
|
403
381
|
raise
|
|
404
382
|
|
|
405
383
|
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import logging
|
|
2
|
-
from typing import Any
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Self
|
|
3
5
|
|
|
4
6
|
from griptape_nodes.bootstrap.workflow_executors.workflow_executor import WorkflowExecutor
|
|
5
7
|
from griptape_nodes.drivers.storage import StorageBackend
|
|
6
8
|
from griptape_nodes.exe_types.node_types import EndNode, StartNode
|
|
9
|
+
from griptape_nodes.retained_mode.events.app_events import AppInitializationComplete
|
|
7
10
|
from griptape_nodes.retained_mode.events.base_events import (
|
|
8
11
|
EventRequest,
|
|
9
12
|
ExecutionGriptapeNodeEvent,
|
|
@@ -15,6 +18,9 @@ from griptape_nodes.retained_mode.events.workflow_events import (
|
|
|
15
18
|
)
|
|
16
19
|
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
17
20
|
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from types import TracebackType
|
|
23
|
+
|
|
18
24
|
logger = logging.getLogger(__name__)
|
|
19
25
|
|
|
20
26
|
|
|
@@ -23,6 +29,29 @@ class LocalExecutorError(Exception):
|
|
|
23
29
|
|
|
24
30
|
|
|
25
31
|
class LocalWorkflowExecutor(WorkflowExecutor):
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
storage_backend: StorageBackend = StorageBackend.LOCAL,
|
|
35
|
+
):
|
|
36
|
+
super().__init__()
|
|
37
|
+
self._set_storage_backend(storage_backend=storage_backend)
|
|
38
|
+
|
|
39
|
+
async def __aenter__(self) -> Self:
|
|
40
|
+
"""Async context manager entry: initialize queue and broadcast app initialization."""
|
|
41
|
+
GriptapeNodes.EventManager().initialize_queue()
|
|
42
|
+
await GriptapeNodes.EventManager().broadcast_app_event(AppInitializationComplete())
|
|
43
|
+
return self
|
|
44
|
+
|
|
45
|
+
async def __aexit__(
|
|
46
|
+
self,
|
|
47
|
+
exc_type: type[BaseException] | None,
|
|
48
|
+
exc_val: BaseException | None,
|
|
49
|
+
exc_tb: TracebackType | None,
|
|
50
|
+
) -> None:
|
|
51
|
+
"""Async context manager exit."""
|
|
52
|
+
# TODO: Broadcast shutdown https://github.com/griptape-ai/griptape-nodes/issues/2149
|
|
53
|
+
return
|
|
54
|
+
|
|
26
55
|
def _load_flow_for_workflow(self) -> str:
|
|
27
56
|
try:
|
|
28
57
|
context_manager = GriptapeNodes.ContextManager()
|
|
@@ -124,7 +153,7 @@ class LocalWorkflowExecutor(WorkflowExecutor):
|
|
|
124
153
|
self,
|
|
125
154
|
workflow_name: str,
|
|
126
155
|
flow_input: Any,
|
|
127
|
-
storage_backend: StorageBackend =
|
|
156
|
+
storage_backend: StorageBackend | None = None,
|
|
128
157
|
**kwargs: Any,
|
|
129
158
|
) -> None:
|
|
130
159
|
"""Executes a local workflow.
|
|
@@ -140,12 +169,13 @@ class LocalWorkflowExecutor(WorkflowExecutor):
|
|
|
140
169
|
Returns:
|
|
141
170
|
None
|
|
142
171
|
"""
|
|
172
|
+
if storage_backend is not None:
|
|
173
|
+
msg = "The storage_backend parameter is deprecated. Pass `storage_backend` to the constructor instead."
|
|
174
|
+
raise ValueError(msg)
|
|
175
|
+
|
|
143
176
|
logger.info("Executing workflow: %s", workflow_name)
|
|
144
177
|
GriptapeNodes.EventManager().initialize_queue()
|
|
145
178
|
|
|
146
|
-
# Set the storage backend
|
|
147
|
-
self._set_storage_backend(storage_backend=storage_backend)
|
|
148
|
-
|
|
149
179
|
# Load workflow from file if workflow_path is provided
|
|
150
180
|
workflow_path = kwargs.get("workflow_path")
|
|
151
181
|
if workflow_path:
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
3
|
from abc import abstractmethod
|
|
4
|
-
from
|
|
4
|
+
from types import TracebackType
|
|
5
|
+
from typing import Any, Self
|
|
5
6
|
|
|
6
7
|
from griptape_nodes.drivers.storage import StorageBackend
|
|
7
8
|
|
|
@@ -12,6 +13,19 @@ class WorkflowExecutor:
|
|
|
12
13
|
def __init__(self) -> None:
|
|
13
14
|
self.output: dict | None = None
|
|
14
15
|
|
|
16
|
+
async def __aenter__(self) -> Self:
|
|
17
|
+
"""Async context manager entry."""
|
|
18
|
+
return self
|
|
19
|
+
|
|
20
|
+
async def __aexit__(
|
|
21
|
+
self,
|
|
22
|
+
exc_type: type[BaseException] | None,
|
|
23
|
+
exc_val: BaseException | None,
|
|
24
|
+
exc_tb: TracebackType | None,
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Async context manager exit."""
|
|
27
|
+
return
|
|
28
|
+
|
|
15
29
|
def run(
|
|
16
30
|
self,
|
|
17
31
|
workflow_name: str,
|
|
@@ -5,7 +5,10 @@ import sys
|
|
|
5
5
|
|
|
6
6
|
import typer
|
|
7
7
|
|
|
8
|
-
from griptape_nodes.cli.shared import
|
|
8
|
+
from griptape_nodes.cli.shared import console
|
|
9
|
+
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
10
|
+
|
|
11
|
+
config_manager = GriptapeNodes.ConfigManager()
|
|
9
12
|
|
|
10
13
|
app = typer.Typer(help="Manage configuration.")
|
|
11
14
|
|
|
@@ -20,12 +20,14 @@ from griptape_nodes.cli.shared import (
|
|
|
20
20
|
GT_CLOUD_BASE_URL,
|
|
21
21
|
NODES_APP_URL,
|
|
22
22
|
InitConfig,
|
|
23
|
-
config_manager,
|
|
24
23
|
console,
|
|
25
|
-
secrets_manager,
|
|
26
24
|
)
|
|
27
25
|
from griptape_nodes.drivers.storage import StorageBackend
|
|
28
26
|
from griptape_nodes.drivers.storage.griptape_cloud_storage_driver import GriptapeCloudStorageDriver
|
|
27
|
+
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
28
|
+
|
|
29
|
+
config_manager = GriptapeNodes.ConfigManager()
|
|
30
|
+
secrets_manager = GriptapeNodes.SecretsManager()
|
|
29
31
|
|
|
30
32
|
|
|
31
33
|
def init_command( # noqa: PLR0913
|
|
@@ -98,7 +100,7 @@ def _run_init(config: InitConfig) -> None:
|
|
|
98
100
|
|
|
99
101
|
# Sync libraries
|
|
100
102
|
if config.libraries_sync is not False:
|
|
101
|
-
asyncio.run(_sync_libraries())
|
|
103
|
+
asyncio.run(_sync_libraries(load_libraries_from_config=False))
|
|
102
104
|
|
|
103
105
|
console.print("[bold green]Initialization complete![/bold green]")
|
|
104
106
|
|
|
@@ -28,8 +28,13 @@ def sync() -> None:
|
|
|
28
28
|
asyncio.run(_sync_libraries())
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
async def _sync_libraries() -> None:
|
|
32
|
-
"""Download and sync Griptape Nodes libraries, copying only directories from synced libraries.
|
|
31
|
+
async def _sync_libraries(*, load_libraries_from_config: bool = True) -> None:
|
|
32
|
+
"""Download and sync Griptape Nodes libraries, copying only directories from synced libraries.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
load_libraries_from_config (bool): If True, re-initialize all libraries from config
|
|
36
|
+
|
|
37
|
+
"""
|
|
33
38
|
install_source, _ = get_install_source()
|
|
34
39
|
# Unless we're installed from PyPi, grab libraries from the 'latest' tag
|
|
35
40
|
if install_source == "pypi":
|
|
@@ -80,11 +85,12 @@ async def _sync_libraries() -> None:
|
|
|
80
85
|
console.print(f"[green]Synced library: {library_dir.name}[/green]")
|
|
81
86
|
|
|
82
87
|
# Re-initialize all libraries from config
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
if load_libraries_from_config:
|
|
89
|
+
console.print("[bold cyan]Initializing libraries...[/bold cyan]")
|
|
90
|
+
try:
|
|
91
|
+
await GriptapeNodes.LibraryManager().load_all_libraries_from_config()
|
|
92
|
+
console.print("[bold green]Libraries Initialized successfully.[/bold green]")
|
|
93
|
+
except Exception as e:
|
|
94
|
+
console.print(f"[red]Error initializing libraries: {e}[/red]")
|
|
89
95
|
|
|
90
96
|
console.print("[bold green]Libraries synced.[/bold green]")
|