griptape-nodes 0.41.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.
Files changed (133) hide show
  1. griptape_nodes/__init__.py +0 -0
  2. griptape_nodes/app/.python-version +0 -0
  3. griptape_nodes/app/__init__.py +1 -10
  4. griptape_nodes/app/api.py +199 -0
  5. griptape_nodes/app/app.py +140 -222
  6. griptape_nodes/app/watch.py +4 -2
  7. griptape_nodes/bootstrap/__init__.py +0 -0
  8. griptape_nodes/bootstrap/bootstrap_script.py +0 -0
  9. griptape_nodes/bootstrap/register_libraries_script.py +0 -0
  10. griptape_nodes/bootstrap/structure_config.yaml +0 -0
  11. griptape_nodes/bootstrap/workflow_executors/__init__.py +0 -0
  12. griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +0 -0
  13. griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +0 -0
  14. griptape_nodes/bootstrap/workflow_runners/__init__.py +0 -0
  15. griptape_nodes/bootstrap/workflow_runners/bootstrap_workflow_runner.py +0 -0
  16. griptape_nodes/bootstrap/workflow_runners/local_workflow_runner.py +0 -0
  17. griptape_nodes/bootstrap/workflow_runners/subprocess_workflow_runner.py +6 -2
  18. griptape_nodes/bootstrap/workflow_runners/workflow_runner.py +0 -0
  19. griptape_nodes/drivers/__init__.py +0 -0
  20. griptape_nodes/drivers/storage/__init__.py +0 -0
  21. griptape_nodes/drivers/storage/base_storage_driver.py +0 -0
  22. griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +0 -0
  23. griptape_nodes/drivers/storage/local_storage_driver.py +5 -3
  24. griptape_nodes/drivers/storage/storage_backend.py +0 -0
  25. griptape_nodes/exe_types/__init__.py +0 -0
  26. griptape_nodes/exe_types/connections.py +0 -0
  27. griptape_nodes/exe_types/core_types.py +0 -0
  28. griptape_nodes/exe_types/flow.py +68 -368
  29. griptape_nodes/exe_types/node_types.py +17 -1
  30. griptape_nodes/exe_types/type_validator.py +0 -0
  31. griptape_nodes/machines/__init__.py +0 -0
  32. griptape_nodes/machines/control_flow.py +52 -20
  33. griptape_nodes/machines/fsm.py +16 -2
  34. griptape_nodes/machines/node_resolution.py +16 -14
  35. griptape_nodes/mcp_server/__init__.py +1 -0
  36. griptape_nodes/mcp_server/server.py +126 -0
  37. griptape_nodes/mcp_server/ws_request_manager.py +268 -0
  38. griptape_nodes/node_library/__init__.py +0 -0
  39. griptape_nodes/node_library/advanced_node_library.py +0 -0
  40. griptape_nodes/node_library/library_registry.py +0 -0
  41. griptape_nodes/node_library/workflow_registry.py +2 -2
  42. griptape_nodes/py.typed +0 -0
  43. griptape_nodes/retained_mode/__init__.py +0 -0
  44. griptape_nodes/retained_mode/events/__init__.py +0 -0
  45. griptape_nodes/retained_mode/events/agent_events.py +70 -8
  46. griptape_nodes/retained_mode/events/app_events.py +137 -12
  47. griptape_nodes/retained_mode/events/arbitrary_python_events.py +23 -0
  48. griptape_nodes/retained_mode/events/base_events.py +13 -31
  49. griptape_nodes/retained_mode/events/config_events.py +87 -11
  50. griptape_nodes/retained_mode/events/connection_events.py +56 -5
  51. griptape_nodes/retained_mode/events/context_events.py +27 -4
  52. griptape_nodes/retained_mode/events/execution_events.py +99 -14
  53. griptape_nodes/retained_mode/events/flow_events.py +165 -7
  54. griptape_nodes/retained_mode/events/generate_request_payload_schemas.py +0 -0
  55. griptape_nodes/retained_mode/events/library_events.py +195 -17
  56. griptape_nodes/retained_mode/events/logger_events.py +11 -0
  57. griptape_nodes/retained_mode/events/node_events.py +242 -22
  58. griptape_nodes/retained_mode/events/object_events.py +40 -4
  59. griptape_nodes/retained_mode/events/os_events.py +116 -3
  60. griptape_nodes/retained_mode/events/parameter_events.py +212 -8
  61. griptape_nodes/retained_mode/events/payload_registry.py +0 -0
  62. griptape_nodes/retained_mode/events/secrets_events.py +59 -7
  63. griptape_nodes/retained_mode/events/static_file_events.py +57 -4
  64. griptape_nodes/retained_mode/events/validation_events.py +39 -4
  65. griptape_nodes/retained_mode/events/workflow_events.py +188 -17
  66. griptape_nodes/retained_mode/griptape_nodes.py +89 -363
  67. griptape_nodes/retained_mode/managers/__init__.py +0 -0
  68. griptape_nodes/retained_mode/managers/agent_manager.py +49 -23
  69. griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +0 -0
  70. griptape_nodes/retained_mode/managers/config_manager.py +0 -0
  71. griptape_nodes/retained_mode/managers/context_manager.py +0 -0
  72. griptape_nodes/retained_mode/managers/engine_identity_manager.py +146 -0
  73. griptape_nodes/retained_mode/managers/event_manager.py +14 -2
  74. griptape_nodes/retained_mode/managers/flow_manager.py +751 -64
  75. griptape_nodes/retained_mode/managers/library_lifecycle/__init__.py +45 -0
  76. griptape_nodes/retained_mode/managers/library_lifecycle/data_models.py +191 -0
  77. griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +346 -0
  78. griptape_nodes/retained_mode/managers/library_lifecycle/library_fsm.py +439 -0
  79. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/__init__.py +17 -0
  80. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/base.py +82 -0
  81. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/github.py +116 -0
  82. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/local_file.py +352 -0
  83. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/package.py +104 -0
  84. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/sandbox.py +155 -0
  85. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance.py +18 -0
  86. griptape_nodes/retained_mode/managers/library_lifecycle/library_status.py +12 -0
  87. griptape_nodes/retained_mode/managers/library_manager.py +255 -40
  88. griptape_nodes/retained_mode/managers/node_manager.py +120 -103
  89. griptape_nodes/retained_mode/managers/object_manager.py +11 -3
  90. griptape_nodes/retained_mode/managers/operation_manager.py +0 -0
  91. griptape_nodes/retained_mode/managers/os_manager.py +582 -8
  92. griptape_nodes/retained_mode/managers/secrets_manager.py +4 -0
  93. griptape_nodes/retained_mode/managers/session_manager.py +328 -0
  94. griptape_nodes/retained_mode/managers/settings.py +7 -0
  95. griptape_nodes/retained_mode/managers/static_files_manager.py +0 -0
  96. griptape_nodes/retained_mode/managers/version_compatibility_manager.py +2 -2
  97. griptape_nodes/retained_mode/managers/workflow_manager.py +722 -456
  98. griptape_nodes/retained_mode/retained_mode.py +44 -0
  99. griptape_nodes/retained_mode/utils/__init__.py +0 -0
  100. griptape_nodes/retained_mode/utils/engine_identity.py +141 -27
  101. griptape_nodes/retained_mode/utils/name_generator.py +0 -0
  102. griptape_nodes/traits/__init__.py +0 -0
  103. griptape_nodes/traits/add_param_button.py +0 -0
  104. griptape_nodes/traits/button.py +0 -0
  105. griptape_nodes/traits/clamp.py +0 -0
  106. griptape_nodes/traits/compare.py +0 -0
  107. griptape_nodes/traits/compare_images.py +0 -0
  108. griptape_nodes/traits/file_system_picker.py +127 -0
  109. griptape_nodes/traits/minmax.py +0 -0
  110. griptape_nodes/traits/options.py +0 -0
  111. griptape_nodes/traits/slider.py +0 -0
  112. griptape_nodes/traits/trait_registry.py +0 -0
  113. griptape_nodes/traits/traits.json +0 -0
  114. griptape_nodes/updater/__init__.py +2 -2
  115. griptape_nodes/updater/__main__.py +0 -0
  116. griptape_nodes/utils/__init__.py +0 -0
  117. griptape_nodes/utils/dict_utils.py +0 -0
  118. griptape_nodes/utils/image_preview.py +128 -0
  119. griptape_nodes/utils/metaclasses.py +0 -0
  120. griptape_nodes/version_compatibility/__init__.py +0 -0
  121. griptape_nodes/version_compatibility/versions/__init__.py +0 -0
  122. griptape_nodes/version_compatibility/versions/v0_39_0/__init__.py +0 -0
  123. griptape_nodes/version_compatibility/versions/v0_39_0/modified_parameters_set_removal.py +5 -5
  124. griptape_nodes-0.43.0.dist-info/METADATA +90 -0
  125. griptape_nodes-0.43.0.dist-info/RECORD +129 -0
  126. griptape_nodes-0.43.0.dist-info/WHEEL +4 -0
  127. {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.43.0.dist-info}/entry_points.txt +1 -0
  128. griptape_nodes/app/app_sessions.py +0 -458
  129. griptape_nodes/retained_mode/utils/session_persistence.py +0 -105
  130. griptape_nodes-0.41.0.dist-info/METADATA +0 -78
  131. griptape_nodes-0.41.0.dist-info/RECORD +0 -112
  132. griptape_nodes-0.41.0.dist-info/WHEEL +0 -4
  133. griptape_nodes-0.41.0.dist-info/licenses/LICENSE +0 -201
@@ -7,14 +7,12 @@ import re
7
7
  import uuid
8
8
  from dataclasses import dataclass
9
9
  from datetime import UTC, datetime
10
- from typing import IO, TYPE_CHECKING, Any, TextIO
10
+ from typing import TYPE_CHECKING, Any
11
11
 
12
- import httpx
13
-
14
- from griptape_nodes.exe_types.core_types import BaseNodeElement, Parameter, ParameterContainer, ParameterGroup
15
12
  from griptape_nodes.exe_types.flow import ControlFlow
16
13
  from griptape_nodes.node_library.workflow_registry import WorkflowRegistry
17
14
  from griptape_nodes.retained_mode.events.app_events import (
15
+ AppConnectionEstablished,
18
16
  AppEndSessionRequest,
19
17
  AppEndSessionResultFailure,
20
18
  AppEndSessionResultSuccess,
@@ -40,34 +38,23 @@ from griptape_nodes.retained_mode.events.app_events import (
40
38
  )
41
39
  from griptape_nodes.retained_mode.events.base_events import (
42
40
  AppPayload,
43
- BaseEvent,
44
41
  RequestPayload,
45
42
  ResultPayload,
46
43
  ResultPayloadFailure,
47
44
  )
48
- from griptape_nodes.retained_mode.events.connection_events import (
49
- CreateConnectionRequest,
50
- )
51
45
  from griptape_nodes.retained_mode.events.flow_events import (
52
- CreateFlowRequest,
53
46
  DeleteFlowRequest,
54
47
  )
55
- from griptape_nodes.retained_mode.events.parameter_events import (
56
- AddParameterToNodeRequest,
57
- AlterParameterDetailsRequest,
58
- )
59
- from griptape_nodes.retained_mode.utils.engine_identity import EngineIdentity
60
- from griptape_nodes.retained_mode.utils.session_persistence import SessionPersistence
61
48
  from griptape_nodes.utils.metaclasses import SingletonMeta
62
49
 
63
50
  if TYPE_CHECKING:
64
- from griptape_nodes.exe_types.node_types import BaseNode
65
51
  from griptape_nodes.retained_mode.managers.agent_manager import AgentManager
66
52
  from griptape_nodes.retained_mode.managers.arbitrary_code_exec_manager import (
67
53
  ArbitraryCodeExecManager,
68
54
  )
69
55
  from griptape_nodes.retained_mode.managers.config_manager import ConfigManager
70
56
  from griptape_nodes.retained_mode.managers.context_manager import ContextManager
57
+ from griptape_nodes.retained_mode.managers.engine_identity_manager import EngineIdentityManager
71
58
  from griptape_nodes.retained_mode.managers.event_manager import EventManager
72
59
  from griptape_nodes.retained_mode.managers.flow_manager import FlowManager
73
60
  from griptape_nodes.retained_mode.managers.library_manager import LibraryManager
@@ -78,6 +65,7 @@ if TYPE_CHECKING:
78
65
  )
79
66
  from griptape_nodes.retained_mode.managers.os_manager import OSManager
80
67
  from griptape_nodes.retained_mode.managers.secrets_manager import SecretsManager
68
+ from griptape_nodes.retained_mode.managers.session_manager import SessionManager
81
69
  from griptape_nodes.retained_mode.managers.static_files_manager import (
82
70
  StaticFilesManager,
83
71
  )
@@ -130,6 +118,10 @@ class Version:
130
118
  """Equality comparison."""
131
119
  return (self.major, self.minor, self.patch) == (other.major, other.minor, other.patch)
132
120
 
121
+ def __hash__(self) -> int:
122
+ """Hash function for Version."""
123
+ return hash((self.major, self.minor, self.patch))
124
+
133
125
 
134
126
  class GriptapeNodes(metaclass=SingletonMeta):
135
127
  _event_manager: EventManager
@@ -147,6 +139,8 @@ class GriptapeNodes(metaclass=SingletonMeta):
147
139
  _static_files_manager: StaticFilesManager
148
140
  _agent_manager: AgentManager
149
141
  _version_compatibility_manager: VersionCompatibilityManager
142
+ _session_manager: SessionManager
143
+ _engine_identity_manager: EngineIdentityManager
150
144
 
151
145
  def __init__(self) -> None:
152
146
  from griptape_nodes.retained_mode.managers.agent_manager import AgentManager
@@ -155,6 +149,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
155
149
  )
156
150
  from griptape_nodes.retained_mode.managers.config_manager import ConfigManager
157
151
  from griptape_nodes.retained_mode.managers.context_manager import ContextManager
152
+ from griptape_nodes.retained_mode.managers.engine_identity_manager import EngineIdentityManager
158
153
  from griptape_nodes.retained_mode.managers.event_manager import EventManager
159
154
  from griptape_nodes.retained_mode.managers.flow_manager import FlowManager
160
155
  from griptape_nodes.retained_mode.managers.library_manager import LibraryManager
@@ -165,6 +160,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
165
160
  )
166
161
  from griptape_nodes.retained_mode.managers.os_manager import OSManager
167
162
  from griptape_nodes.retained_mode.managers.secrets_manager import SecretsManager
163
+ from griptape_nodes.retained_mode.managers.session_manager import SessionManager
168
164
  from griptape_nodes.retained_mode.managers.static_files_manager import (
169
165
  StaticFilesManager,
170
166
  )
@@ -178,8 +174,8 @@ class GriptapeNodes(metaclass=SingletonMeta):
178
174
  # Initialize only if our managers haven't been created yet
179
175
  if not hasattr(self, "_event_manager"):
180
176
  self._event_manager = EventManager()
181
- self._os_manager = OSManager(self._event_manager)
182
177
  self._config_manager = ConfigManager(self._event_manager)
178
+ self._os_manager = OSManager(self._event_manager)
183
179
  self._secrets_manager = SecretsManager(self._config_manager, self._event_manager)
184
180
  self._object_manager = ObjectManager(self._event_manager)
185
181
  self._node_manager = NodeManager(self._event_manager)
@@ -194,6 +190,8 @@ class GriptapeNodes(metaclass=SingletonMeta):
194
190
  )
195
191
  self._agent_manager = AgentManager(self._static_files_manager, self._event_manager)
196
192
  self._version_compatibility_manager = VersionCompatibilityManager(self._event_manager)
193
+ self._session_manager = SessionManager(self._event_manager)
194
+ self._engine_identity_manager = EngineIdentityManager(self._event_manager)
197
195
 
198
196
  # Assign handlers now that these are created.
199
197
  self._event_manager.assign_manager_to_request_type(
@@ -203,6 +201,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
203
201
  AppStartSessionRequest, self.handle_session_start_request
204
202
  )
205
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)
206
205
  self._event_manager.assign_manager_to_request_type(AppGetSessionRequest, self.handle_get_session_request)
207
206
  self._event_manager.assign_manager_to_request_type(
208
207
  SessionHeartbeatRequest, self.handle_session_heartbeat_request
@@ -223,16 +222,23 @@ class GriptapeNodes(metaclass=SingletonMeta):
223
222
  return cls()
224
223
 
225
224
  @classmethod
226
- def handle_request(cls, request: RequestPayload) -> ResultPayload:
225
+ def handle_request(
226
+ cls,
227
+ request: RequestPayload,
228
+ *,
229
+ response_topic: str | None = None,
230
+ request_id: str | None = None,
231
+ ) -> ResultPayload:
227
232
  event_mgr = GriptapeNodes.EventManager()
228
233
  obj_depth_mgr = GriptapeNodes.OperationDepthManager()
229
234
  workflow_mgr = GriptapeNodes.WorkflowManager()
230
-
231
235
  try:
232
236
  return event_mgr.handle_request(
233
237
  request=request,
234
238
  operation_depth_mgr=obj_depth_mgr,
235
239
  workflow_mgr=workflow_mgr,
240
+ response_topic=response_topic,
241
+ request_id=request_id,
236
242
  )
237
243
  except Exception as e:
238
244
  logger.exception(
@@ -249,7 +255,11 @@ class GriptapeNodes(metaclass=SingletonMeta):
249
255
 
250
256
  @classmethod
251
257
  def get_session_id(cls) -> str | None:
252
- return BaseEvent._session_id
258
+ return GriptapeNodes.SessionManager().get_active_session_id()
259
+
260
+ @classmethod
261
+ def get_engine_id(cls) -> str | None:
262
+ return GriptapeNodes.EngineIdentityManager().get_active_engine_id()
253
263
 
254
264
  @classmethod
255
265
  def EventManager(cls) -> EventManager:
@@ -287,6 +297,10 @@ class GriptapeNodes(metaclass=SingletonMeta):
287
297
  def ConfigManager(cls) -> ConfigManager:
288
298
  return GriptapeNodes.get_instance()._config_manager
289
299
 
300
+ @classmethod
301
+ def OSManager(cls) -> OSManager:
302
+ return GriptapeNodes.get_instance()._os_manager
303
+
290
304
  @classmethod
291
305
  def SecretsManager(cls) -> SecretsManager:
292
306
  return GriptapeNodes.get_instance()._secrets_manager
@@ -307,6 +321,14 @@ class GriptapeNodes(metaclass=SingletonMeta):
307
321
  def VersionCompatibilityManager(cls) -> VersionCompatibilityManager:
308
322
  return GriptapeNodes.get_instance()._version_compatibility_manager
309
323
 
324
+ @classmethod
325
+ def SessionManager(cls) -> SessionManager:
326
+ return GriptapeNodes.get_instance()._session_manager
327
+
328
+ @classmethod
329
+ def EngineIdentityManager(cls) -> EngineIdentityManager:
330
+ return GriptapeNodes.get_instance()._engine_identity_manager
331
+
310
332
  @classmethod
311
333
  def clear_data(cls) -> None:
312
334
  # Get canvas
@@ -330,6 +352,27 @@ class GriptapeNodes(metaclass=SingletonMeta):
330
352
  msg = "Failed to successfully delete all objects"
331
353
  raise ValueError(msg)
332
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
+
333
376
  def handle_engine_version_request(self, request: GetEngineVersionRequest) -> ResultPayload: # noqa: ARG002
334
377
  try:
335
378
  engine_ver = Version.from_string(engine_version)
@@ -348,32 +391,39 @@ class GriptapeNodes(metaclass=SingletonMeta):
348
391
  return GetEngineVersionResultFailure()
349
392
 
350
393
  def handle_session_start_request(self, request: AppStartSessionRequest) -> ResultPayload: # noqa: ARG002
351
- current_session_id = BaseEvent._session_id
394
+ from griptape_nodes.app.app import subscribe_to_topic
395
+
396
+ current_session_id = GriptapeNodes.SessionManager().get_active_session_id()
352
397
  if current_session_id is None:
353
398
  # Client wants a new session
354
399
  current_session_id = uuid.uuid4().hex
355
- BaseEvent._session_id = current_session_id
356
- # Persist the session ID to XDG state directory
357
- SessionPersistence.persist_session(current_session_id)
400
+ GriptapeNodes.SessionManager().save_session(current_session_id)
358
401
  details = f"New session '{current_session_id}' started at {datetime.now(tz=UTC)}."
359
402
  logger.info(details)
360
403
  else:
361
404
  details = f"Session '{current_session_id}' already active. Joining..."
362
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
+
363
410
  return AppStartSessionResultSuccess(current_session_id)
364
411
 
365
412
  def handle_session_end_request(self, _: AppEndSessionRequest) -> ResultPayload:
413
+ from griptape_nodes.app.app import unsubscribe_from_topic
414
+
366
415
  try:
367
- previous_session_id = BaseEvent._session_id
368
- if BaseEvent._session_id is None:
416
+ previous_session_id = GriptapeNodes.SessionManager().get_active_session_id()
417
+ if previous_session_id is None:
369
418
  details = "No active session to end."
370
419
  logger.info(details)
371
420
  else:
372
- details = f"Session '{BaseEvent._session_id}' ended at {datetime.now(tz=UTC)}."
421
+ details = f"Session '{previous_session_id}' ended at {datetime.now(tz=UTC)}."
373
422
  logger.info(details)
374
- BaseEvent._session_id = None
375
- # Clear the persisted session ID from XDG state directory
376
- SessionPersistence.clear_persisted_session()
423
+ GriptapeNodes.SessionManager().clear_saved_session()
424
+
425
+ unsubscribe_topic = f"sessions/{previous_session_id}/request"
426
+ unsubscribe_from_topic(unsubscribe_topic)
377
427
 
378
428
  return AppEndSessionResultSuccess(session_id=previous_session_id)
379
429
  except Exception as err:
@@ -382,7 +432,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
382
432
  return AppEndSessionResultFailure()
383
433
 
384
434
  def handle_get_session_request(self, _: AppGetSessionRequest) -> ResultPayload:
385
- return AppGetSessionResultSuccess(session_id=BaseEvent._session_id)
435
+ return AppGetSessionResultSuccess(session_id=GriptapeNodes.SessionManager().get_active_session_id())
386
436
 
387
437
  def handle_session_heartbeat_request(self, request: SessionHeartbeatRequest) -> ResultPayload: # noqa: ARG002
388
438
  """Handle session heartbeat requests.
@@ -390,11 +440,12 @@ class GriptapeNodes(metaclass=SingletonMeta):
390
440
  Simply verifies that the session is active and responds with success.
391
441
  """
392
442
  try:
393
- if BaseEvent._session_id is None:
443
+ active_session_id = GriptapeNodes.SessionManager().get_active_session_id()
444
+ if active_session_id is None:
394
445
  logger.warning("Session heartbeat received but no active session found")
395
446
  return SessionHeartbeatResultFailure()
396
447
 
397
- logger.debug("Session heartbeat successful for session: %s", BaseEvent._session_id)
448
+ logger.debug("Session heartbeat successful for session: %s", active_session_id)
398
449
  return SessionHeartbeatResultSuccess()
399
450
  except Exception as err:
400
451
  logger.error("Failed to handle session heartbeat: %s", err)
@@ -413,15 +464,15 @@ class GriptapeNodes(metaclass=SingletonMeta):
413
464
  workflow_info = self._get_current_workflow_info()
414
465
 
415
466
  # Get engine name
416
- engine_name = EngineIdentity.get_engine_name()
467
+ engine_name = GriptapeNodes.EngineIdentityManager().get_engine_name()
417
468
 
418
469
  logger.debug("Engine heartbeat successful")
419
470
  return EngineHeartbeatResultSuccess(
420
471
  heartbeat_id=request.heartbeat_id,
421
472
  engine_version=engine_version,
422
473
  engine_name=engine_name,
423
- engine_id=BaseEvent._engine_id,
424
- session_id=BaseEvent._session_id,
474
+ engine_id=GriptapeNodes.EngineIdentityManager().get_active_engine_id(),
475
+ session_id=GriptapeNodes.SessionManager().get_active_session_id(),
425
476
  timestamp=datetime.now(tz=UTC).isoformat(),
426
477
  **instance_info,
427
478
  **workflow_info,
@@ -433,7 +484,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
433
484
  def handle_get_engine_name_request(self, request: GetEngineNameRequest) -> ResultPayload: # noqa: ARG002
434
485
  """Handle requests to get the current engine name."""
435
486
  try:
436
- engine_name = EngineIdentity.get_engine_name()
487
+ engine_name = GriptapeNodes.EngineIdentityManager().get_engine_name()
437
488
  logger.debug("Retrieved engine name: %s", engine_name)
438
489
  return GetEngineNameResultSuccess(engine_name=engine_name)
439
490
  except Exception as err:
@@ -451,7 +502,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
451
502
  return SetEngineNameResultFailure(error_message=error_message)
452
503
 
453
504
  # Set the new engine name
454
- EngineIdentity.set_engine_name(request.engine_name.strip())
505
+ GriptapeNodes.EngineIdentityManager().set_engine_name(request.engine_name.strip())
455
506
  logger.info("Engine name set to: %s", request.engine_name.strip())
456
507
  return SetEngineNameResultSuccess(engine_name=request.engine_name.strip())
457
508
 
@@ -474,45 +525,8 @@ class GriptapeNodes(metaclass=SingletonMeta):
474
525
  # Determine deployment type based on presence of instance environment variables
475
526
  instance_info["deployment_type"] = "griptape_hosted" if any(instance_info.values()) else "local"
476
527
 
477
- # Get public IP address
478
- public_ip = self._get_public_ip()
479
- if public_ip:
480
- instance_info["public_ip"] = public_ip
481
-
482
528
  return instance_info
483
529
 
484
- def _get_public_ip(self) -> str | None:
485
- """Get the public IP address of this device.
486
-
487
- Returns the public IP address if available, None otherwise.
488
- """
489
- try:
490
- # Try multiple services in case one is down
491
- services = [
492
- "https://api.ipify.org",
493
- "https://ipinfo.io/ip",
494
- "https://icanhazip.com",
495
- ]
496
-
497
- for service in services:
498
- try:
499
- with httpx.Client(timeout=5.0) as client:
500
- response = client.get(service)
501
- response.raise_for_status()
502
- public_ip = response.text.strip()
503
- if public_ip:
504
- logger.debug("Retrieved public IP from %s: %s", service, public_ip)
505
- return public_ip
506
- except Exception as err:
507
- logger.debug("Failed to get public IP from %s: %s", service, err)
508
- continue
509
- logger.warning("Unable to retrieve public IP from any service")
510
- except Exception as err:
511
- logger.warning("Failed to get public IP: %s", err)
512
- return None
513
- else:
514
- return None
515
-
516
530
  def _get_current_workflow_info(self) -> dict[str, Any]:
517
531
  """Get information about the currently loaded workflow.
518
532
 
@@ -543,291 +557,3 @@ class GriptapeNodes(metaclass=SingletonMeta):
543
557
  logger.warning("Failed to get current workflow info: %s", err)
544
558
 
545
559
  return workflow_info
546
-
547
-
548
- def create_flows_in_order(flow_name: str, flow_manager: FlowManager, created_flows: list, file: IO) -> list | None:
549
- """Creates flows in the correct order based on their dependencies."""
550
- # If this flow is already created, we can return
551
- if flow_name in created_flows:
552
- return None
553
-
554
- # Get the parent of this flow
555
- parent = flow_manager.get_parent_flow(flow_name)
556
-
557
- # If there's a parent, create it first
558
- if parent:
559
- create_flows_in_order(parent, flow_manager, created_flows, file)
560
-
561
- # Now create this flow (only if not already created)
562
- if flow_name not in created_flows:
563
- # Here you would actually send the request and handle response
564
- creation_request = CreateFlowRequest(flow_name=flow_name, parent_flow_name=parent)
565
- code_string = f"GriptapeNodes.handle_request({creation_request})"
566
- file.write(code_string + "\n")
567
- created_flows.append(flow_name)
568
-
569
- return created_flows
570
-
571
-
572
- def handle_flow_saving(file: TextIO, obj_manager: ObjectManager, created_flows: list) -> str:
573
- """Handles the creation and saving of flows."""
574
- flow_manager = GriptapeNodes.FlowManager()
575
- connection_request_workflows = ""
576
- for flow_name, flow in obj_manager.get_filtered_subset(type=ControlFlow).items():
577
- create_flows_in_order(flow_name, flow_manager, created_flows, file)
578
- # While creating flows - let's create all of our connections
579
- for connection in flow.connections.connections.values():
580
- creation_request = CreateConnectionRequest(
581
- source_node_name=connection.source_node.name,
582
- source_parameter_name=connection.source_parameter.name,
583
- target_node_name=connection.target_node.name,
584
- target_parameter_name=connection.target_parameter.name,
585
- initial_setup=True,
586
- )
587
- code_string = f"GriptapeNodes.handle_request({creation_request})"
588
- connection_request_workflows += code_string + "\n"
589
- return connection_request_workflows
590
-
591
-
592
- def handle_parameter_creation_saving(node: BaseNode, values_created: dict) -> tuple[str, bool]:
593
- """Handles the creation and saving of parameters for a node."""
594
- parameter_details = ""
595
- saved_properly = True
596
- # Get all parameters, even ones that aren't direct children.
597
- for parameter in node.root_ui_element.find_elements_by_type(BaseNodeElement):
598
- if isinstance(parameter, (Parameter, ParameterGroup, ParameterContainer)):
599
- param_dict = parameter.to_dict()
600
- # Create the parameter, or alter it on the existing node
601
- if isinstance(parameter, Parameter) and parameter.user_defined:
602
- param_dict["node_name"] = node.name
603
- param_dict["initial_setup"] = True
604
- creation_request = AddParameterToNodeRequest.create(**param_dict)
605
- code_string = f"GriptapeNodes.handle_request({creation_request})\n"
606
- parameter_details += code_string
607
- else:
608
- base_node_obj = type(node)(name="test")
609
- diff = manage_alter_details(parameter, base_node_obj)
610
- relevant = False
611
- for key in diff:
612
- if key in AlterParameterDetailsRequest.relevant_parameters():
613
- relevant = True
614
- break
615
- if relevant:
616
- diff["node_name"] = node.name
617
- diff["parameter_name"] = parameter.name
618
- diff["initial_setup"] = True
619
- creation_request = AlterParameterDetailsRequest.create(**diff)
620
- code_string = f"GriptapeNodes.handle_request({creation_request})\n"
621
- parameter_details += code_string
622
- if not isinstance(parameter, ParameterGroup) and (
623
- parameter.name in node.parameter_values or parameter.name in node.parameter_output_values
624
- ):
625
- # SetParameterValueRequest event
626
- code_string = handle_parameter_value_saving(parameter, node, values_created)
627
- if code_string:
628
- code_string = code_string + "\n"
629
- parameter_details += code_string
630
- else:
631
- saved_properly = False
632
- return parameter_details, saved_properly
633
-
634
-
635
- def handle_parameter_value_saving(parameter: Parameter, node: BaseNode, values_created: dict) -> str | None:
636
- """Generates code to save a parameter value for a node in a Griptape workflow.
637
-
638
- This function handles the process of creating code that will reconstruct and set
639
- parameter values for nodes. It performs the following steps:
640
- 1. Retrieves the parameter value from the node's parameter values or output values
641
- 2. Checks if the value has already been created in the generated code
642
- 3. If not, generates code to reconstruct the value
643
- 4. Creates a SetParameterValueRequest to apply the value to the node
644
-
645
- Args:
646
- parameter (Parameter): The parameter object containing metadata
647
- node (BaseNode): The node object that contains the parameter
648
- values_created (dict): Dictionary mapping value identifiers to variable names
649
- that have already been created in the code
650
-
651
- Returns:
652
- str | None: Python code as a string that will reconstruct and set the parameter
653
- value when executed. Returns None if the parameter has no value or
654
- if the value cannot be properly represented.
655
-
656
- Notes:
657
- - Parameter output values take precedence over regular parameter values
658
- - For values that can be hashed, the value itself is used as the key in values_created
659
- - For unhashable values, the object's id is used as the key
660
- - The function will reuse already created values to avoid duplication
661
- """
662
- value = None
663
- is_output = False
664
- if parameter.name in node.parameter_values:
665
- value = node.get_parameter_value(parameter.name)
666
- # Output values are more important
667
- if parameter.name in node.parameter_output_values:
668
- value = node.parameter_output_values[parameter.name]
669
- is_output = True
670
- if value is not None:
671
- try:
672
- hash(value)
673
- value_id = value
674
- except TypeError:
675
- value_id = id(value)
676
- if value_id in values_created:
677
- var_name = values_created[value_id]
678
- # We've already created this object. we're all good.
679
- return f"GriptapeNodes.handle_request(SetParameterValueRequest(parameter_name='{parameter.name}', node_name='{node.name}', value={var_name}, initial_setup=True, is_output={is_output}))"
680
- # Set it up as a object in the code
681
- imports = []
682
- var_name = f"{node.name}_{parameter.name}_value"
683
- values_created[value_id] = var_name
684
- reconstruction_code = _convert_value_to_str_representation(var_name, value, imports)
685
- # If it doesn't have a custom __str__, convert to dict if possible
686
- if reconstruction_code != "":
687
- # Add the request handling code
688
- final_code = (
689
- reconstruction_code
690
- + f"GriptapeNodes.handle_request(SetParameterValueRequest(parameter_name='{parameter.name}', node_name='{node.name}', value={var_name}, initial_setup=True, is_output={is_output}))"
691
- )
692
- # Combine imports and code
693
- import_statements = ""
694
- if imports:
695
- import_statements = "\n".join(list(set(imports))) + "\n\n" # Remove duplicates with set()
696
- return import_statements + final_code
697
- return None
698
-
699
-
700
- def _convert_value_to_str_representation(var_name: str, value: Any, imports: list) -> str:
701
- """Converts a Python value to its string representation as executable code.
702
-
703
- This function generates Python code that can recreate the given value
704
- when executed. It handles different types of values with specific strategies:
705
- - Objects with a 'to_dict' method: Uses _create_object_in_file for reconstruction
706
- - Basic Python types: Uses their repr representation
707
- - If not representable: Returns empty string
708
-
709
- Args:
710
- var_name (str): The variable name to assign the value to in the generated code
711
- value (Any): The Python value to convert to code
712
- imports (list): List to which any required import statements will be appended
713
-
714
- Returns:
715
- str: Python code as a string that will reconstruct the value when executed.
716
- Returns empty string if the value cannot be properly represented.
717
- """
718
- reconstruction_code = ""
719
- # If it doesn't have a custom __str__, convert to dict if possible
720
- if hasattr(value, "to_dict") and callable(value.to_dict):
721
- # For objects with to_dict method
722
- reconstruction_code = _create_object_in_file(value, var_name, imports)
723
- return reconstruction_code
724
- if isinstance(value, (int, float, str, bool)) or value is None:
725
- # For basic types, use repr to create a literal
726
- return f"{var_name} = {value!r}\n"
727
- if isinstance(value, (list, dict, tuple, set)):
728
- reconstruction_code = _convert_container_to_str_representation(var_name, value, imports, type(value))
729
- return reconstruction_code
730
- return ""
731
-
732
-
733
- def _convert_container_to_str_representation(var_name: str, value: Any, imports: list, value_type: type) -> str:
734
- """Creates code to reconstruct a container type (list, dict, tuple, set) with its elements.
735
-
736
- Args:
737
- var_name (str): The variable name to assign the container to
738
- value (Any): The container value to convert to code
739
- imports (list): List to which any required import statements will be appended
740
- value_type (type): The type of container (list, dict, tuple, or set)
741
-
742
- Returns:
743
- str: Python code as a string that will reconstruct the container
744
- """
745
- # Get the initialization brackets from an empty container
746
- empty_container = value_type()
747
- init_brackets = repr(empty_container)
748
- # Initialize the container
749
- code = f"{var_name} = {init_brackets}\n"
750
- temp_var_base = f"{var_name}_item"
751
- if value_type is dict:
752
- # Process dictionary items
753
- for i, (k, v) in enumerate(value.items()):
754
- temp_var = f"{temp_var_base}_{i}"
755
- # Convert the value to code
756
- value_code = _convert_value_to_str_representation(temp_var, v, imports)
757
- if value_code:
758
- code += value_code
759
- code += f"{var_name}[{k!r}] = {temp_var}\n"
760
- else:
761
- code += f"{var_name}[{k!r}] = {v!r}\n"
762
- else:
763
- # Process sequence items (list, tuple, set)
764
- # For immutable types like tuple and set, we need to build a list first
765
- for i, item in enumerate(value):
766
- temp_var = f"{temp_var_base}_{i}"
767
- # Convert the item to code
768
- item_code = _convert_value_to_str_representation(temp_var, item, imports)
769
- if item_code != "":
770
- code += item_code
771
- code += f"{var_name}.append({temp_var})\n"
772
- else:
773
- code += f"{var_name}.append({item!r})\n"
774
- # Convert the list to the final type if needed
775
- if value_type in (tuple, set):
776
- code += f"{var_name} = {value_type.__name__}({var_name})\n"
777
- return code
778
-
779
-
780
- def _create_object_in_file(value: Any, var_name: str, imports: list) -> str:
781
- """Creates Python code to reconstruct an object from its dictionary representation and adds necessary import statements.
782
-
783
- Args:
784
- value (Any): The object to be serialized into Python code
785
- var_name (str): The name of the variable to assign the object to in the generated code
786
- imports (list): List to which import statements will be appended
787
-
788
- Returns:
789
- str: Python code string that reconstructs the object when executed
790
- Returns empty string if object cannot be properly reconstructed
791
-
792
- Notes:
793
- - The function assumes the object has a 'to_dict()' method to serialize it. It is only called if the object does have that method.
794
- - For class instances, it will add appropriate import statements to 'imports'
795
- - The generated code will create a dictionary representation first, then
796
- reconstruct the object using a 'from_dict' class method
797
- """
798
- obj_dict = value.to_dict()
799
- reconstruction_code = f"{var_name} = {obj_dict!r}\n"
800
- # If we know the class, we can reconstruct it and add import
801
- if hasattr(value, "__class__"):
802
- class_name = value.__class__.__name__
803
- module_name = value.__class__.__module__
804
- if module_name != "builtins":
805
- imports.append(f"from {module_name} import {class_name}")
806
- reconstruction_code += f"{var_name} = {class_name}.from_dict({var_name})\n"
807
- return reconstruction_code
808
- return ""
809
-
810
-
811
- def manage_alter_details(parameter: Parameter | ParameterGroup, base_node_obj: BaseNode) -> dict:
812
- """Alters the details of a parameter based on the base node object."""
813
- if isinstance(parameter, Parameter):
814
- base_param = base_node_obj.get_parameter_by_name(parameter.name)
815
- if base_param is not None:
816
- diff = base_param.equals(parameter)
817
- else:
818
- return vars(parameter)
819
- else:
820
- base_param_group = base_node_obj.get_group_by_name_or_element_id(parameter.name)
821
- if base_param_group is not None:
822
- diff = base_param_group.equals(parameter)
823
- else:
824
- return vars(parameter)
825
- return diff
826
-
827
-
828
- def __getattr__(name: str) -> logging.Logger:
829
- """Convenience function so that node authors only need to write 'logger.debug()'."""
830
- if name == "logger":
831
- return logger
832
- msg = f"module '{__name__}' has no attribute '{name}'"
833
- raise AttributeError(msg)
File without changes