griptape-nodes 0.42.0__py3-none-any.whl → 0.43.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 +0 -0
- griptape_nodes/app/.python-version +0 -0
- griptape_nodes/app/__init__.py +1 -6
- griptape_nodes/app/api.py +199 -0
- griptape_nodes/app/app.py +140 -225
- griptape_nodes/app/watch.py +1 -1
- griptape_nodes/bootstrap/__init__.py +0 -0
- griptape_nodes/bootstrap/bootstrap_script.py +0 -0
- griptape_nodes/bootstrap/register_libraries_script.py +0 -0
- griptape_nodes/bootstrap/structure_config.yaml +0 -0
- griptape_nodes/bootstrap/workflow_executors/__init__.py +0 -0
- griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +0 -0
- griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +0 -0
- griptape_nodes/bootstrap/workflow_runners/__init__.py +0 -0
- griptape_nodes/bootstrap/workflow_runners/bootstrap_workflow_runner.py +0 -0
- griptape_nodes/bootstrap/workflow_runners/local_workflow_runner.py +0 -0
- griptape_nodes/bootstrap/workflow_runners/subprocess_workflow_runner.py +6 -2
- griptape_nodes/bootstrap/workflow_runners/workflow_runner.py +0 -0
- griptape_nodes/drivers/__init__.py +0 -0
- griptape_nodes/drivers/storage/__init__.py +0 -0
- griptape_nodes/drivers/storage/base_storage_driver.py +0 -0
- griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +0 -0
- griptape_nodes/drivers/storage/local_storage_driver.py +2 -1
- griptape_nodes/drivers/storage/storage_backend.py +0 -0
- griptape_nodes/exe_types/__init__.py +0 -0
- griptape_nodes/exe_types/connections.py +0 -0
- griptape_nodes/exe_types/core_types.py +0 -0
- griptape_nodes/exe_types/flow.py +0 -0
- griptape_nodes/exe_types/node_types.py +17 -1
- griptape_nodes/exe_types/type_validator.py +0 -0
- griptape_nodes/machines/__init__.py +0 -0
- griptape_nodes/machines/control_flow.py +41 -12
- griptape_nodes/machines/fsm.py +16 -2
- griptape_nodes/machines/node_resolution.py +0 -0
- griptape_nodes/mcp_server/__init__.py +1 -0
- griptape_nodes/mcp_server/server.py +126 -0
- griptape_nodes/mcp_server/ws_request_manager.py +268 -0
- griptape_nodes/node_library/__init__.py +0 -0
- griptape_nodes/node_library/advanced_node_library.py +0 -0
- griptape_nodes/node_library/library_registry.py +0 -0
- griptape_nodes/node_library/workflow_registry.py +1 -1
- griptape_nodes/py.typed +0 -0
- griptape_nodes/retained_mode/__init__.py +0 -0
- griptape_nodes/retained_mode/events/__init__.py +0 -0
- griptape_nodes/retained_mode/events/agent_events.py +0 -0
- griptape_nodes/retained_mode/events/app_events.py +6 -2
- griptape_nodes/retained_mode/events/arbitrary_python_events.py +0 -0
- griptape_nodes/retained_mode/events/base_events.py +6 -6
- griptape_nodes/retained_mode/events/config_events.py +0 -0
- griptape_nodes/retained_mode/events/connection_events.py +0 -0
- griptape_nodes/retained_mode/events/context_events.py +0 -0
- griptape_nodes/retained_mode/events/execution_events.py +0 -0
- griptape_nodes/retained_mode/events/flow_events.py +0 -0
- griptape_nodes/retained_mode/events/generate_request_payload_schemas.py +0 -0
- griptape_nodes/retained_mode/events/library_events.py +2 -2
- griptape_nodes/retained_mode/events/logger_events.py +0 -0
- griptape_nodes/retained_mode/events/node_events.py +0 -0
- griptape_nodes/retained_mode/events/object_events.py +0 -0
- griptape_nodes/retained_mode/events/os_events.py +104 -2
- griptape_nodes/retained_mode/events/parameter_events.py +0 -0
- griptape_nodes/retained_mode/events/payload_registry.py +0 -0
- griptape_nodes/retained_mode/events/secrets_events.py +0 -0
- griptape_nodes/retained_mode/events/static_file_events.py +0 -0
- griptape_nodes/retained_mode/events/validation_events.py +0 -0
- griptape_nodes/retained_mode/events/workflow_events.py +0 -0
- griptape_nodes/retained_mode/griptape_nodes.py +43 -40
- griptape_nodes/retained_mode/managers/__init__.py +0 -0
- griptape_nodes/retained_mode/managers/agent_manager.py +48 -22
- griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +0 -0
- griptape_nodes/retained_mode/managers/config_manager.py +0 -0
- griptape_nodes/retained_mode/managers/context_manager.py +0 -0
- griptape_nodes/retained_mode/managers/engine_identity_manager.py +0 -0
- griptape_nodes/retained_mode/managers/event_manager.py +0 -0
- griptape_nodes/retained_mode/managers/flow_manager.py +2 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/__init__.py +45 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/data_models.py +191 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +346 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_fsm.py +439 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/__init__.py +17 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/base.py +82 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/github.py +116 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/local_file.py +352 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/package.py +104 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/sandbox.py +155 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance.py +18 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_status.py +12 -0
- griptape_nodes/retained_mode/managers/library_manager.py +144 -39
- griptape_nodes/retained_mode/managers/node_manager.py +86 -72
- griptape_nodes/retained_mode/managers/object_manager.py +0 -0
- griptape_nodes/retained_mode/managers/operation_manager.py +0 -0
- griptape_nodes/retained_mode/managers/os_manager.py +517 -12
- griptape_nodes/retained_mode/managers/secrets_manager.py +0 -0
- griptape_nodes/retained_mode/managers/session_manager.py +0 -0
- griptape_nodes/retained_mode/managers/settings.py +0 -0
- griptape_nodes/retained_mode/managers/static_files_manager.py +0 -0
- griptape_nodes/retained_mode/managers/version_compatibility_manager.py +2 -2
- griptape_nodes/retained_mode/managers/workflow_manager.py +199 -2
- griptape_nodes/retained_mode/retained_mode.py +0 -0
- griptape_nodes/retained_mode/utils/__init__.py +0 -0
- griptape_nodes/retained_mode/utils/engine_identity.py +0 -0
- griptape_nodes/retained_mode/utils/name_generator.py +0 -0
- griptape_nodes/traits/__init__.py +0 -0
- griptape_nodes/traits/add_param_button.py +0 -0
- griptape_nodes/traits/button.py +0 -0
- griptape_nodes/traits/clamp.py +0 -0
- griptape_nodes/traits/compare.py +0 -0
- griptape_nodes/traits/compare_images.py +0 -0
- griptape_nodes/traits/file_system_picker.py +127 -0
- griptape_nodes/traits/minmax.py +0 -0
- griptape_nodes/traits/options.py +0 -0
- griptape_nodes/traits/slider.py +0 -0
- griptape_nodes/traits/trait_registry.py +0 -0
- griptape_nodes/traits/traits.json +0 -0
- griptape_nodes/updater/__init__.py +2 -2
- griptape_nodes/updater/__main__.py +0 -0
- griptape_nodes/utils/__init__.py +0 -0
- griptape_nodes/utils/dict_utils.py +0 -0
- griptape_nodes/utils/image_preview.py +128 -0
- griptape_nodes/utils/metaclasses.py +0 -0
- griptape_nodes/version_compatibility/__init__.py +0 -0
- griptape_nodes/version_compatibility/versions/__init__.py +0 -0
- griptape_nodes/version_compatibility/versions/v0_39_0/__init__.py +0 -0
- griptape_nodes/version_compatibility/versions/v0_39_0/modified_parameters_set_removal.py +5 -5
- griptape_nodes-0.43.0.dist-info/METADATA +90 -0
- griptape_nodes-0.43.0.dist-info/RECORD +129 -0
- griptape_nodes-0.43.0.dist-info/WHEEL +4 -0
- {griptape_nodes-0.42.0.dist-info → griptape_nodes-0.43.0.dist-info}/entry_points.txt +1 -0
- griptape_nodes/app/app_sessions.py +0 -554
- griptape_nodes-0.42.0.dist-info/METADATA +0 -78
- griptape_nodes-0.42.0.dist-info/RECORD +0 -113
- griptape_nodes-0.42.0.dist-info/WHEEL +0 -4
- griptape_nodes-0.42.0.dist-info/licenses/LICENSE +0 -201
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import json
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
5
|
from dataclasses import asdict, dataclass, field, is_dataclass
|
|
6
|
-
from typing import TYPE_CHECKING, Any, ClassVar,
|
|
6
|
+
from typing import TYPE_CHECKING, Any, ClassVar, TypeVar
|
|
7
7
|
|
|
8
8
|
from griptape.artifacts import BaseArtifact
|
|
9
9
|
from griptape.events import BaseEvent as GtBaseEvent
|
|
@@ -101,7 +101,7 @@ class AppPayload(Payload):
|
|
|
101
101
|
|
|
102
102
|
|
|
103
103
|
# Type variables for our generic payloads
|
|
104
|
-
P = TypeVar("P", bound=
|
|
104
|
+
P = TypeVar("P", bound=RequestPayload)
|
|
105
105
|
R = TypeVar("R", bound=ResultPayload)
|
|
106
106
|
E = TypeVar("E", bound=ExecutionPayload)
|
|
107
107
|
A = TypeVar("A", bound=AppPayload)
|
|
@@ -181,7 +181,7 @@ class BaseEvent(BaseModel, ABC):
|
|
|
181
181
|
"""
|
|
182
182
|
|
|
183
183
|
|
|
184
|
-
class EventRequest
|
|
184
|
+
class EventRequest[P: Payload](BaseEvent):
|
|
185
185
|
"""Request event."""
|
|
186
186
|
|
|
187
187
|
request: P
|
|
@@ -245,7 +245,7 @@ class EventRequest(BaseEvent, Generic[P]):
|
|
|
245
245
|
return cls(request=request_payload, **event_data)
|
|
246
246
|
|
|
247
247
|
|
|
248
|
-
class EventResult
|
|
248
|
+
class EventResult[P: RequestPayload, R: ResultPayload](BaseEvent, ABC):
|
|
249
249
|
"""Abstract base class for result events."""
|
|
250
250
|
|
|
251
251
|
request: P
|
|
@@ -429,7 +429,7 @@ def deserialize_event(json_data: str | dict | Any) -> BaseEvent:
|
|
|
429
429
|
|
|
430
430
|
|
|
431
431
|
# EXECUTION EVENT BASE (this event type is used for the execution of a Griptape Nodes flow)
|
|
432
|
-
class ExecutionEvent
|
|
432
|
+
class ExecutionEvent[E: ExecutionPayload](BaseEvent):
|
|
433
433
|
payload: E
|
|
434
434
|
|
|
435
435
|
def __init__(self, **data) -> None:
|
|
@@ -490,7 +490,7 @@ class ExecutionEvent(BaseEvent, Generic[E]):
|
|
|
490
490
|
|
|
491
491
|
|
|
492
492
|
# Events sent as part of the lifecycle of the Griptape Nodes application.
|
|
493
|
-
class AppEvent
|
|
493
|
+
class AppEvent[A: AppPayload](BaseEvent):
|
|
494
494
|
payload: A
|
|
495
495
|
|
|
496
496
|
def __init__(self, **data) -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -14,7 +14,7 @@ from griptape_nodes.retained_mode.events.payload_registry import PayloadRegistry
|
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
16
|
from griptape_nodes.node_library.library_registry import LibraryMetadata, LibrarySchema, NodeMetadata
|
|
17
|
-
from griptape_nodes.retained_mode.managers.
|
|
17
|
+
from griptape_nodes.retained_mode.managers.library_lifecycle.library_status import LibraryStatus
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
@dataclass
|
|
@@ -207,7 +207,7 @@ class LoadLibraryMetadataFromFileResultFailure(WorkflowNotAlteredMixin, ResultPa
|
|
|
207
207
|
|
|
208
208
|
library_path: str
|
|
209
209
|
library_name: str | None
|
|
210
|
-
status:
|
|
210
|
+
status: LibraryStatus
|
|
211
211
|
problems: list[str]
|
|
212
212
|
|
|
213
213
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -9,6 +9,17 @@ from griptape_nodes.retained_mode.events.base_events import (
|
|
|
9
9
|
from griptape_nodes.retained_mode.events.payload_registry import PayloadRegistry
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
@dataclass
|
|
13
|
+
class FileSystemEntry:
|
|
14
|
+
"""Represents a file or directory in the file system."""
|
|
15
|
+
|
|
16
|
+
name: str
|
|
17
|
+
path: str
|
|
18
|
+
is_dir: bool
|
|
19
|
+
size: int
|
|
20
|
+
modified_time: float
|
|
21
|
+
|
|
22
|
+
|
|
12
23
|
@dataclass
|
|
13
24
|
@PayloadRegistry.register
|
|
14
25
|
class OpenAssociatedFileRequest(RequestPayload):
|
|
@@ -18,12 +29,14 @@ class OpenAssociatedFileRequest(RequestPayload):
|
|
|
18
29
|
providing file viewing capabilities, implementing file associations.
|
|
19
30
|
|
|
20
31
|
Args:
|
|
21
|
-
path_to_file: Path to the file to open
|
|
32
|
+
path_to_file: Path to the file to open (mutually exclusive with file_entry)
|
|
33
|
+
file_entry: FileSystemEntry object from directory listing (mutually exclusive with path_to_file)
|
|
22
34
|
|
|
23
35
|
Results: OpenAssociatedFileResultSuccess | OpenAssociatedFileResultFailure (file not found, no association)
|
|
24
36
|
"""
|
|
25
37
|
|
|
26
|
-
path_to_file: str
|
|
38
|
+
path_to_file: str | None = None
|
|
39
|
+
file_entry: FileSystemEntry | None = None
|
|
27
40
|
|
|
28
41
|
|
|
29
42
|
@dataclass
|
|
@@ -36,3 +49,92 @@ class OpenAssociatedFileResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSucc
|
|
|
36
49
|
@PayloadRegistry.register
|
|
37
50
|
class OpenAssociatedFileResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
38
51
|
"""File opening failed. Common causes: file not found, no associated application, permission denied."""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
@PayloadRegistry.register
|
|
56
|
+
class ListDirectoryRequest(RequestPayload):
|
|
57
|
+
"""List contents of a directory.
|
|
58
|
+
|
|
59
|
+
Use when: Browsing file system, showing directory contents,
|
|
60
|
+
implementing file pickers, navigating folder structures.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
directory_path: Path to the directory to list (None for current directory)
|
|
64
|
+
show_hidden: Whether to show hidden files/folders
|
|
65
|
+
workspace_only: If True, constrain to workspace directory. If False, allow system-wide browsing.
|
|
66
|
+
If None, workspace constraints don't apply (e.g., cloud environments).
|
|
67
|
+
|
|
68
|
+
Results: ListDirectoryResultSuccess (with entries) | ListDirectoryResultFailure (access denied, not found)
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
directory_path: str | None = None
|
|
72
|
+
show_hidden: bool = False
|
|
73
|
+
workspace_only: bool | None = True
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dataclass
|
|
77
|
+
@PayloadRegistry.register
|
|
78
|
+
class ListDirectoryResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
79
|
+
"""Directory listing retrieved successfully."""
|
|
80
|
+
|
|
81
|
+
entries: list[FileSystemEntry]
|
|
82
|
+
current_path: str
|
|
83
|
+
is_workspace_path: bool
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass
|
|
87
|
+
@PayloadRegistry.register
|
|
88
|
+
class ListDirectoryResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
89
|
+
"""Directory listing failed. Common causes: access denied, path not found."""
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass
|
|
93
|
+
@PayloadRegistry.register
|
|
94
|
+
class ReadFileRequest(RequestPayload):
|
|
95
|
+
"""Read contents of a file, automatically detecting if it's text or binary using MIME types.
|
|
96
|
+
|
|
97
|
+
Use when: Reading file contents for display, processing, or analysis.
|
|
98
|
+
Automatically detects file type using MIME type detection and returns appropriate content format.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
file_path: Path to the file to read (mutually exclusive with file_entry)
|
|
102
|
+
file_entry: FileSystemEntry object from directory listing (mutually exclusive with file_path)
|
|
103
|
+
encoding: Text encoding to use if file is detected as text (default: 'utf-8')
|
|
104
|
+
workspace_only: If True, constrain to workspace directory. If False, allow system-wide access.
|
|
105
|
+
If None, workspace constraints don't apply (e.g., cloud environments).
|
|
106
|
+
|
|
107
|
+
Results: ReadFileResultSuccess (with content) | ReadFileResultFailure (file not found, permission denied)
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
file_path: str | None = None
|
|
111
|
+
file_entry: FileSystemEntry | None = None
|
|
112
|
+
encoding: str = "utf-8"
|
|
113
|
+
workspace_only: bool | None = True
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@dataclass
|
|
117
|
+
@PayloadRegistry.register
|
|
118
|
+
class ReadFileResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
119
|
+
"""File contents read successfully."""
|
|
120
|
+
|
|
121
|
+
content: str | bytes # String for text files, bytes for binary files
|
|
122
|
+
file_size: int
|
|
123
|
+
mime_type: str # e.g., "text/plain", "image/png", "application/pdf"
|
|
124
|
+
encoding: str | None # Text encoding used (None for binary files)
|
|
125
|
+
compression_encoding: str | None = None # Compression encoding (e.g., "gzip", "bzip2", None)
|
|
126
|
+
is_text: bool = False # Will be computed from content type
|
|
127
|
+
|
|
128
|
+
def __post_init__(self) -> None:
|
|
129
|
+
"""Compute is_text from content type after initialization."""
|
|
130
|
+
# For images, even though content is a string (base64), it's not text content
|
|
131
|
+
if self.mime_type.startswith("image/"):
|
|
132
|
+
self.is_text = False
|
|
133
|
+
else:
|
|
134
|
+
self.is_text = isinstance(self.content, str)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@dataclass
|
|
138
|
+
@PayloadRegistry.register
|
|
139
|
+
class ReadFileResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
140
|
+
"""File reading failed. Common causes: file not found, permission denied, encoding error."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -9,11 +9,10 @@ from dataclasses import dataclass
|
|
|
9
9
|
from datetime import UTC, datetime
|
|
10
10
|
from typing import TYPE_CHECKING, Any
|
|
11
11
|
|
|
12
|
-
import httpx
|
|
13
|
-
|
|
14
12
|
from griptape_nodes.exe_types.flow import ControlFlow
|
|
15
13
|
from griptape_nodes.node_library.workflow_registry import WorkflowRegistry
|
|
16
14
|
from griptape_nodes.retained_mode.events.app_events import (
|
|
15
|
+
AppConnectionEstablished,
|
|
17
16
|
AppEndSessionRequest,
|
|
18
17
|
AppEndSessionResultFailure,
|
|
19
18
|
AppEndSessionResultSuccess,
|
|
@@ -119,6 +118,10 @@ class Version:
|
|
|
119
118
|
"""Equality comparison."""
|
|
120
119
|
return (self.major, self.minor, self.patch) == (other.major, other.minor, other.patch)
|
|
121
120
|
|
|
121
|
+
def __hash__(self) -> int:
|
|
122
|
+
"""Hash function for Version."""
|
|
123
|
+
return hash((self.major, self.minor, self.patch))
|
|
124
|
+
|
|
122
125
|
|
|
123
126
|
class GriptapeNodes(metaclass=SingletonMeta):
|
|
124
127
|
_event_manager: EventManager
|
|
@@ -171,8 +174,8 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
171
174
|
# Initialize only if our managers haven't been created yet
|
|
172
175
|
if not hasattr(self, "_event_manager"):
|
|
173
176
|
self._event_manager = EventManager()
|
|
174
|
-
self._os_manager = OSManager(self._event_manager)
|
|
175
177
|
self._config_manager = ConfigManager(self._event_manager)
|
|
178
|
+
self._os_manager = OSManager(self._event_manager)
|
|
176
179
|
self._secrets_manager = SecretsManager(self._config_manager, self._event_manager)
|
|
177
180
|
self._object_manager = ObjectManager(self._event_manager)
|
|
178
181
|
self._node_manager = NodeManager(self._event_manager)
|
|
@@ -198,6 +201,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
198
201
|
AppStartSessionRequest, self.handle_session_start_request
|
|
199
202
|
)
|
|
200
203
|
self._event_manager.assign_manager_to_request_type(AppEndSessionRequest, self.handle_session_end_request)
|
|
204
|
+
self._event_manager.add_listener_to_app_event(AppConnectionEstablished, self.on_app_connection_established)
|
|
201
205
|
self._event_manager.assign_manager_to_request_type(AppGetSessionRequest, self.handle_get_session_request)
|
|
202
206
|
self._event_manager.assign_manager_to_request_type(
|
|
203
207
|
SessionHeartbeatRequest, self.handle_session_heartbeat_request
|
|
@@ -293,6 +297,10 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
293
297
|
def ConfigManager(cls) -> ConfigManager:
|
|
294
298
|
return GriptapeNodes.get_instance()._config_manager
|
|
295
299
|
|
|
300
|
+
@classmethod
|
|
301
|
+
def OSManager(cls) -> OSManager:
|
|
302
|
+
return GriptapeNodes.get_instance()._os_manager
|
|
303
|
+
|
|
296
304
|
@classmethod
|
|
297
305
|
def SecretsManager(cls) -> SecretsManager:
|
|
298
306
|
return GriptapeNodes.get_instance()._secrets_manager
|
|
@@ -344,6 +352,27 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
344
352
|
msg = "Failed to successfully delete all objects"
|
|
345
353
|
raise ValueError(msg)
|
|
346
354
|
|
|
355
|
+
def on_app_connection_established(self, _payload: AppConnectionEstablished) -> None:
|
|
356
|
+
from griptape_nodes.app.app import subscribe_to_topic
|
|
357
|
+
|
|
358
|
+
# Subscribe to request topic (engine discovery)
|
|
359
|
+
subscribe_to_topic("request")
|
|
360
|
+
|
|
361
|
+
# Get engine ID and subscribe to engine_id/request
|
|
362
|
+
engine_id = GriptapeNodes.get_engine_id()
|
|
363
|
+
if engine_id:
|
|
364
|
+
subscribe_to_topic(f"engines/{engine_id}/request")
|
|
365
|
+
else:
|
|
366
|
+
logger.warning("Engine ID not available for subscription")
|
|
367
|
+
|
|
368
|
+
# Get session ID and subscribe to session_id/request if available
|
|
369
|
+
session_id = GriptapeNodes.get_session_id()
|
|
370
|
+
if session_id:
|
|
371
|
+
topic = f"sessions/{session_id}/request"
|
|
372
|
+
subscribe_to_topic(topic)
|
|
373
|
+
else:
|
|
374
|
+
logger.info("No session ID available for subscription")
|
|
375
|
+
|
|
347
376
|
def handle_engine_version_request(self, request: GetEngineVersionRequest) -> ResultPayload: # noqa: ARG002
|
|
348
377
|
try:
|
|
349
378
|
engine_ver = Version.from_string(engine_version)
|
|
@@ -362,6 +391,8 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
362
391
|
return GetEngineVersionResultFailure()
|
|
363
392
|
|
|
364
393
|
def handle_session_start_request(self, request: AppStartSessionRequest) -> ResultPayload: # noqa: ARG002
|
|
394
|
+
from griptape_nodes.app.app import subscribe_to_topic
|
|
395
|
+
|
|
365
396
|
current_session_id = GriptapeNodes.SessionManager().get_active_session_id()
|
|
366
397
|
if current_session_id is None:
|
|
367
398
|
# Client wants a new session
|
|
@@ -372,9 +403,15 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
372
403
|
else:
|
|
373
404
|
details = f"Session '{current_session_id}' already active. Joining..."
|
|
374
405
|
|
|
406
|
+
topic = f"sessions/{current_session_id}/request"
|
|
407
|
+
subscribe_to_topic(topic)
|
|
408
|
+
logger.info("Subscribed to new session topic: %s", topic)
|
|
409
|
+
|
|
375
410
|
return AppStartSessionResultSuccess(current_session_id)
|
|
376
411
|
|
|
377
412
|
def handle_session_end_request(self, _: AppEndSessionRequest) -> ResultPayload:
|
|
413
|
+
from griptape_nodes.app.app import unsubscribe_from_topic
|
|
414
|
+
|
|
378
415
|
try:
|
|
379
416
|
previous_session_id = GriptapeNodes.SessionManager().get_active_session_id()
|
|
380
417
|
if previous_session_id is None:
|
|
@@ -385,6 +422,9 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
385
422
|
logger.info(details)
|
|
386
423
|
GriptapeNodes.SessionManager().clear_saved_session()
|
|
387
424
|
|
|
425
|
+
unsubscribe_topic = f"sessions/{previous_session_id}/request"
|
|
426
|
+
unsubscribe_from_topic(unsubscribe_topic)
|
|
427
|
+
|
|
388
428
|
return AppEndSessionResultSuccess(session_id=previous_session_id)
|
|
389
429
|
except Exception as err:
|
|
390
430
|
details = f"Failed to end session due to '{err}'."
|
|
@@ -485,45 +525,8 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
485
525
|
# Determine deployment type based on presence of instance environment variables
|
|
486
526
|
instance_info["deployment_type"] = "griptape_hosted" if any(instance_info.values()) else "local"
|
|
487
527
|
|
|
488
|
-
# Get public IP address
|
|
489
|
-
public_ip = self._get_public_ip()
|
|
490
|
-
if public_ip:
|
|
491
|
-
instance_info["public_ip"] = public_ip
|
|
492
|
-
|
|
493
528
|
return instance_info
|
|
494
529
|
|
|
495
|
-
def _get_public_ip(self) -> str | None:
|
|
496
|
-
"""Get the public IP address of this device.
|
|
497
|
-
|
|
498
|
-
Returns the public IP address if available, None otherwise.
|
|
499
|
-
"""
|
|
500
|
-
try:
|
|
501
|
-
# Try multiple services in case one is down
|
|
502
|
-
services = [
|
|
503
|
-
"https://api.ipify.org",
|
|
504
|
-
"https://ipinfo.io/ip",
|
|
505
|
-
"https://icanhazip.com",
|
|
506
|
-
]
|
|
507
|
-
|
|
508
|
-
for service in services:
|
|
509
|
-
try:
|
|
510
|
-
with httpx.Client(timeout=5.0) as client:
|
|
511
|
-
response = client.get(service)
|
|
512
|
-
response.raise_for_status()
|
|
513
|
-
public_ip = response.text.strip()
|
|
514
|
-
if public_ip:
|
|
515
|
-
logger.debug("Retrieved public IP from %s: %s", service, public_ip)
|
|
516
|
-
return public_ip
|
|
517
|
-
except Exception as err:
|
|
518
|
-
logger.debug("Failed to get public IP from %s: %s", service, err)
|
|
519
|
-
continue
|
|
520
|
-
logger.warning("Unable to retrieve public IP from any service")
|
|
521
|
-
except Exception as err:
|
|
522
|
-
logger.warning("Failed to get public IP: %s", err)
|
|
523
|
-
return None
|
|
524
|
-
else:
|
|
525
|
-
return None
|
|
526
|
-
|
|
527
530
|
def _get_current_workflow_info(self) -> dict[str, Any]:
|
|
528
531
|
"""Get information about the currently loaded workflow.
|
|
529
532
|
|
|
File without changes
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
|
+
import os
|
|
3
4
|
import threading
|
|
4
5
|
import uuid
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
5
7
|
|
|
6
8
|
from attrs import define, field
|
|
7
9
|
from griptape.artifacts import ErrorArtifact, ImageUrlArtifact, JsonArtifact
|
|
@@ -14,6 +16,7 @@ from griptape.memory.structure import ConversationMemory
|
|
|
14
16
|
from griptape.rules import Rule, Ruleset
|
|
15
17
|
from griptape.structures import Agent
|
|
16
18
|
from griptape.tools import BaseImageGenerationTool
|
|
19
|
+
from griptape.tools.mcp.tool import MCPTool
|
|
17
20
|
from griptape.utils.decorators import activity
|
|
18
21
|
from json_repair import repair_json
|
|
19
22
|
from schema import Literal, Schema
|
|
@@ -42,10 +45,14 @@ from griptape_nodes.retained_mode.managers.static_files_manager import (
|
|
|
42
45
|
StaticFilesManager,
|
|
43
46
|
)
|
|
44
47
|
|
|
48
|
+
if TYPE_CHECKING:
|
|
49
|
+
from griptape.tools.mcp.sessions import StreamableHttpConnection
|
|
50
|
+
|
|
45
51
|
logger = logging.getLogger("griptape_nodes")
|
|
46
52
|
|
|
47
53
|
API_KEY_ENV_VAR = "GT_CLOUD_API_KEY"
|
|
48
54
|
SERVICE = "Griptape"
|
|
55
|
+
GTN_MCP_SERVER_PORT = int(os.getenv("GTN_MCP_SERVER_PORT", "9927"))
|
|
49
56
|
|
|
50
57
|
config_manager = ConfigManager()
|
|
51
58
|
secrets_manager = SecretsManager(config_manager)
|
|
@@ -84,6 +91,7 @@ class AgentManager:
|
|
|
84
91
|
self.conversation_memory = ConversationMemory()
|
|
85
92
|
self.prompt_driver = None
|
|
86
93
|
self.image_tool = None
|
|
94
|
+
self.mcp_tool = None
|
|
87
95
|
self.static_files_manager = static_files_manager
|
|
88
96
|
|
|
89
97
|
if event_manager is not None:
|
|
@@ -113,14 +121,53 @@ class AgentManager:
|
|
|
113
121
|
static_files_manager=self.static_files_manager,
|
|
114
122
|
)
|
|
115
123
|
|
|
124
|
+
def _initialize_mcp_tool(self) -> MCPTool:
|
|
125
|
+
connection: StreamableHttpConnection = { # type: ignore[reportAssignmentType]
|
|
126
|
+
"transport": "streamable_http",
|
|
127
|
+
"url": f"http://localhost:{GTN_MCP_SERVER_PORT}/mcp/",
|
|
128
|
+
}
|
|
129
|
+
return MCPTool(connection=connection)
|
|
130
|
+
|
|
116
131
|
def on_handle_run_agent_request(self, request: RunAgentRequest) -> ResultPayload:
|
|
117
132
|
if self.prompt_driver is None:
|
|
118
133
|
self.prompt_driver = self._initialize_prompt_driver()
|
|
119
134
|
if self.image_tool is None:
|
|
120
135
|
self.image_tool = self._initialize_image_tool()
|
|
136
|
+
if self.mcp_tool is None:
|
|
137
|
+
self.mcp_tool = self._initialize_mcp_tool()
|
|
121
138
|
threading.Thread(target=self._on_handle_run_agent_request, args=(request, EventBus.event_listeners)).start()
|
|
122
139
|
return RunAgentResultStarted()
|
|
123
140
|
|
|
141
|
+
def _create_agent(self) -> Agent:
|
|
142
|
+
output_schema = Schema(
|
|
143
|
+
{
|
|
144
|
+
"generated_image_urls": [str],
|
|
145
|
+
"conversation_output": str,
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
tools = []
|
|
150
|
+
if self.image_tool is not None:
|
|
151
|
+
tools.append(self.image_tool)
|
|
152
|
+
if self.mcp_tool is not None:
|
|
153
|
+
tools.append(self.mcp_tool)
|
|
154
|
+
|
|
155
|
+
return Agent(
|
|
156
|
+
prompt_driver=self.prompt_driver,
|
|
157
|
+
conversation_memory=self.conversation_memory,
|
|
158
|
+
tools=tools,
|
|
159
|
+
output_schema=output_schema,
|
|
160
|
+
rulesets=[
|
|
161
|
+
Ruleset(
|
|
162
|
+
name="generated_image_urls",
|
|
163
|
+
rules=[
|
|
164
|
+
Rule("Do not hallucinate generated_image_urls."),
|
|
165
|
+
Rule("Only set generated_image_urls with images generated with your tools."),
|
|
166
|
+
],
|
|
167
|
+
),
|
|
168
|
+
],
|
|
169
|
+
)
|
|
170
|
+
|
|
124
171
|
def _on_handle_run_agent_request(
|
|
125
172
|
self, request: RunAgentRequest, event_listeners: list[EventListener]
|
|
126
173
|
) -> ResultPayload:
|
|
@@ -131,28 +178,7 @@ class AgentManager:
|
|
|
131
178
|
for url_artifact in request.url_artifacts
|
|
132
179
|
if url_artifact["type"] == "ImageUrlArtifact"
|
|
133
180
|
]
|
|
134
|
-
|
|
135
|
-
output_schema = Schema(
|
|
136
|
-
{
|
|
137
|
-
"generated_image_urls": [str],
|
|
138
|
-
"conversation_output": str,
|
|
139
|
-
}
|
|
140
|
-
)
|
|
141
|
-
agent = Agent(
|
|
142
|
-
prompt_driver=self.prompt_driver,
|
|
143
|
-
conversation_memory=self.conversation_memory,
|
|
144
|
-
tools=[self.image_tool] if self.image_tool else [],
|
|
145
|
-
output_schema=output_schema,
|
|
146
|
-
rulesets=[
|
|
147
|
-
Ruleset(
|
|
148
|
-
name="generated_image_urls",
|
|
149
|
-
rules=[
|
|
150
|
-
Rule("Do not hallucinate generated_image_urls."),
|
|
151
|
-
Rule("Only set generated_image_urls with images generated with your tools."),
|
|
152
|
-
],
|
|
153
|
-
),
|
|
154
|
-
],
|
|
155
|
-
)
|
|
181
|
+
agent = self._create_agent()
|
|
156
182
|
*events, last_event = agent.run_stream([request.input, *artifacts])
|
|
157
183
|
full_result = ""
|
|
158
184
|
last_conversation_output = ""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1810,6 +1810,8 @@ class FlowManager:
|
|
|
1810
1810
|
def unresolve_whole_flow(self, flow: ControlFlow) -> None:
|
|
1811
1811
|
for node in flow.nodes.values():
|
|
1812
1812
|
node.make_node_unresolved(current_states_to_trigger_change_event=None)
|
|
1813
|
+
# Clear entry control parameter for new execution
|
|
1814
|
+
node.set_entry_control_parameter(None)
|
|
1813
1815
|
|
|
1814
1816
|
def flow_state(self, flow: ControlFlow) -> tuple[str | None, str | None]: # noqa: ARG002
|
|
1815
1817
|
if not self.check_for_existing_running_flow():
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Library lifecycle management subsystem."""
|
|
2
|
+
|
|
3
|
+
from griptape_nodes.retained_mode.managers.library_lifecycle.data_models import (
|
|
4
|
+
EvaluationResult,
|
|
5
|
+
InspectionResult,
|
|
6
|
+
InstallationResult,
|
|
7
|
+
LibraryByType,
|
|
8
|
+
LibraryEntry,
|
|
9
|
+
LibraryLoadedResult,
|
|
10
|
+
LibraryPreferences,
|
|
11
|
+
LifecycleIssue,
|
|
12
|
+
)
|
|
13
|
+
from griptape_nodes.retained_mode.managers.library_lifecycle.library_directory import LibraryDirectory
|
|
14
|
+
from griptape_nodes.retained_mode.managers.library_lifecycle.library_fsm import (
|
|
15
|
+
LibraryLifecycleContext,
|
|
16
|
+
LibraryLifecycleFSM,
|
|
17
|
+
)
|
|
18
|
+
from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance import (
|
|
19
|
+
LibraryProvenance,
|
|
20
|
+
LibraryProvenanceGitHub,
|
|
21
|
+
LibraryProvenanceLocalFile,
|
|
22
|
+
LibraryProvenancePackage,
|
|
23
|
+
LibraryProvenanceSandbox,
|
|
24
|
+
)
|
|
25
|
+
from griptape_nodes.retained_mode.managers.library_lifecycle.library_status import LibraryStatus
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"EvaluationResult",
|
|
29
|
+
"InspectionResult",
|
|
30
|
+
"InstallationResult",
|
|
31
|
+
"LibraryByType",
|
|
32
|
+
"LibraryDirectory",
|
|
33
|
+
"LibraryEntry",
|
|
34
|
+
"LibraryLifecycleContext",
|
|
35
|
+
"LibraryLifecycleFSM",
|
|
36
|
+
"LibraryLoadedResult",
|
|
37
|
+
"LibraryPreferences",
|
|
38
|
+
"LibraryProvenance",
|
|
39
|
+
"LibraryProvenanceGitHub",
|
|
40
|
+
"LibraryProvenanceLocalFile",
|
|
41
|
+
"LibraryProvenancePackage",
|
|
42
|
+
"LibraryProvenanceSandbox",
|
|
43
|
+
"LibraryStatus",
|
|
44
|
+
"LifecycleIssue",
|
|
45
|
+
]
|