griptape-nodes 0.41.0__py3-none-any.whl → 0.42.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 (49) hide show
  1. griptape_nodes/app/__init__.py +1 -5
  2. griptape_nodes/app/app.py +12 -9
  3. griptape_nodes/app/app_sessions.py +132 -36
  4. griptape_nodes/app/watch.py +3 -1
  5. griptape_nodes/drivers/storage/local_storage_driver.py +3 -2
  6. griptape_nodes/exe_types/flow.py +68 -368
  7. griptape_nodes/machines/control_flow.py +16 -13
  8. griptape_nodes/machines/node_resolution.py +16 -14
  9. griptape_nodes/node_library/workflow_registry.py +2 -2
  10. griptape_nodes/retained_mode/events/agent_events.py +70 -8
  11. griptape_nodes/retained_mode/events/app_events.py +132 -11
  12. griptape_nodes/retained_mode/events/arbitrary_python_events.py +23 -0
  13. griptape_nodes/retained_mode/events/base_events.py +7 -25
  14. griptape_nodes/retained_mode/events/config_events.py +87 -11
  15. griptape_nodes/retained_mode/events/connection_events.py +56 -5
  16. griptape_nodes/retained_mode/events/context_events.py +27 -4
  17. griptape_nodes/retained_mode/events/execution_events.py +99 -14
  18. griptape_nodes/retained_mode/events/flow_events.py +165 -7
  19. griptape_nodes/retained_mode/events/library_events.py +193 -15
  20. griptape_nodes/retained_mode/events/logger_events.py +11 -0
  21. griptape_nodes/retained_mode/events/node_events.py +242 -22
  22. griptape_nodes/retained_mode/events/object_events.py +40 -4
  23. griptape_nodes/retained_mode/events/os_events.py +13 -2
  24. griptape_nodes/retained_mode/events/parameter_events.py +212 -8
  25. griptape_nodes/retained_mode/events/secrets_events.py +59 -7
  26. griptape_nodes/retained_mode/events/static_file_events.py +57 -4
  27. griptape_nodes/retained_mode/events/validation_events.py +39 -4
  28. griptape_nodes/retained_mode/events/workflow_events.py +188 -17
  29. griptape_nodes/retained_mode/griptape_nodes.py +46 -323
  30. griptape_nodes/retained_mode/managers/agent_manager.py +1 -1
  31. griptape_nodes/retained_mode/managers/engine_identity_manager.py +146 -0
  32. griptape_nodes/retained_mode/managers/event_manager.py +14 -2
  33. griptape_nodes/retained_mode/managers/flow_manager.py +749 -64
  34. griptape_nodes/retained_mode/managers/library_manager.py +112 -2
  35. griptape_nodes/retained_mode/managers/node_manager.py +34 -31
  36. griptape_nodes/retained_mode/managers/object_manager.py +11 -3
  37. griptape_nodes/retained_mode/managers/os_manager.py +70 -1
  38. griptape_nodes/retained_mode/managers/secrets_manager.py +4 -0
  39. griptape_nodes/retained_mode/managers/session_manager.py +328 -0
  40. griptape_nodes/retained_mode/managers/settings.py +7 -0
  41. griptape_nodes/retained_mode/managers/workflow_manager.py +523 -454
  42. griptape_nodes/retained_mode/retained_mode.py +44 -0
  43. griptape_nodes/retained_mode/utils/engine_identity.py +141 -27
  44. {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.42.0.dist-info}/METADATA +2 -2
  45. {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.42.0.dist-info}/RECORD +48 -47
  46. griptape_nodes/retained_mode/utils/session_persistence.py +0 -105
  47. {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.42.0.dist-info}/WHEEL +0 -0
  48. {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.42.0.dist-info}/entry_points.txt +0 -0
  49. {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.42.0.dist-info}/licenses/LICENSE +0 -0
@@ -7,11 +7,10 @@ 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
12
  import httpx
13
13
 
14
- from griptape_nodes.exe_types.core_types import BaseNodeElement, Parameter, ParameterContainer, ParameterGroup
15
14
  from griptape_nodes.exe_types.flow import ControlFlow
16
15
  from griptape_nodes.node_library.workflow_registry import WorkflowRegistry
17
16
  from griptape_nodes.retained_mode.events.app_events import (
@@ -40,34 +39,23 @@ from griptape_nodes.retained_mode.events.app_events import (
40
39
  )
41
40
  from griptape_nodes.retained_mode.events.base_events import (
42
41
  AppPayload,
43
- BaseEvent,
44
42
  RequestPayload,
45
43
  ResultPayload,
46
44
  ResultPayloadFailure,
47
45
  )
48
- from griptape_nodes.retained_mode.events.connection_events import (
49
- CreateConnectionRequest,
50
- )
51
46
  from griptape_nodes.retained_mode.events.flow_events import (
52
- CreateFlowRequest,
53
47
  DeleteFlowRequest,
54
48
  )
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
49
  from griptape_nodes.utils.metaclasses import SingletonMeta
62
50
 
63
51
  if TYPE_CHECKING:
64
- from griptape_nodes.exe_types.node_types import BaseNode
65
52
  from griptape_nodes.retained_mode.managers.agent_manager import AgentManager
66
53
  from griptape_nodes.retained_mode.managers.arbitrary_code_exec_manager import (
67
54
  ArbitraryCodeExecManager,
68
55
  )
69
56
  from griptape_nodes.retained_mode.managers.config_manager import ConfigManager
70
57
  from griptape_nodes.retained_mode.managers.context_manager import ContextManager
58
+ from griptape_nodes.retained_mode.managers.engine_identity_manager import EngineIdentityManager
71
59
  from griptape_nodes.retained_mode.managers.event_manager import EventManager
72
60
  from griptape_nodes.retained_mode.managers.flow_manager import FlowManager
73
61
  from griptape_nodes.retained_mode.managers.library_manager import LibraryManager
@@ -78,6 +66,7 @@ if TYPE_CHECKING:
78
66
  )
79
67
  from griptape_nodes.retained_mode.managers.os_manager import OSManager
80
68
  from griptape_nodes.retained_mode.managers.secrets_manager import SecretsManager
69
+ from griptape_nodes.retained_mode.managers.session_manager import SessionManager
81
70
  from griptape_nodes.retained_mode.managers.static_files_manager import (
82
71
  StaticFilesManager,
83
72
  )
@@ -147,6 +136,8 @@ class GriptapeNodes(metaclass=SingletonMeta):
147
136
  _static_files_manager: StaticFilesManager
148
137
  _agent_manager: AgentManager
149
138
  _version_compatibility_manager: VersionCompatibilityManager
139
+ _session_manager: SessionManager
140
+ _engine_identity_manager: EngineIdentityManager
150
141
 
151
142
  def __init__(self) -> None:
152
143
  from griptape_nodes.retained_mode.managers.agent_manager import AgentManager
@@ -155,6 +146,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
155
146
  )
156
147
  from griptape_nodes.retained_mode.managers.config_manager import ConfigManager
157
148
  from griptape_nodes.retained_mode.managers.context_manager import ContextManager
149
+ from griptape_nodes.retained_mode.managers.engine_identity_manager import EngineIdentityManager
158
150
  from griptape_nodes.retained_mode.managers.event_manager import EventManager
159
151
  from griptape_nodes.retained_mode.managers.flow_manager import FlowManager
160
152
  from griptape_nodes.retained_mode.managers.library_manager import LibraryManager
@@ -165,6 +157,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
165
157
  )
166
158
  from griptape_nodes.retained_mode.managers.os_manager import OSManager
167
159
  from griptape_nodes.retained_mode.managers.secrets_manager import SecretsManager
160
+ from griptape_nodes.retained_mode.managers.session_manager import SessionManager
168
161
  from griptape_nodes.retained_mode.managers.static_files_manager import (
169
162
  StaticFilesManager,
170
163
  )
@@ -194,6 +187,8 @@ class GriptapeNodes(metaclass=SingletonMeta):
194
187
  )
195
188
  self._agent_manager = AgentManager(self._static_files_manager, self._event_manager)
196
189
  self._version_compatibility_manager = VersionCompatibilityManager(self._event_manager)
190
+ self._session_manager = SessionManager(self._event_manager)
191
+ self._engine_identity_manager = EngineIdentityManager(self._event_manager)
197
192
 
198
193
  # Assign handlers now that these are created.
199
194
  self._event_manager.assign_manager_to_request_type(
@@ -223,16 +218,23 @@ class GriptapeNodes(metaclass=SingletonMeta):
223
218
  return cls()
224
219
 
225
220
  @classmethod
226
- def handle_request(cls, request: RequestPayload) -> ResultPayload:
221
+ def handle_request(
222
+ cls,
223
+ request: RequestPayload,
224
+ *,
225
+ response_topic: str | None = None,
226
+ request_id: str | None = None,
227
+ ) -> ResultPayload:
227
228
  event_mgr = GriptapeNodes.EventManager()
228
229
  obj_depth_mgr = GriptapeNodes.OperationDepthManager()
229
230
  workflow_mgr = GriptapeNodes.WorkflowManager()
230
-
231
231
  try:
232
232
  return event_mgr.handle_request(
233
233
  request=request,
234
234
  operation_depth_mgr=obj_depth_mgr,
235
235
  workflow_mgr=workflow_mgr,
236
+ response_topic=response_topic,
237
+ request_id=request_id,
236
238
  )
237
239
  except Exception as e:
238
240
  logger.exception(
@@ -249,7 +251,11 @@ class GriptapeNodes(metaclass=SingletonMeta):
249
251
 
250
252
  @classmethod
251
253
  def get_session_id(cls) -> str | None:
252
- return BaseEvent._session_id
254
+ return GriptapeNodes.SessionManager().get_active_session_id()
255
+
256
+ @classmethod
257
+ def get_engine_id(cls) -> str | None:
258
+ return GriptapeNodes.EngineIdentityManager().get_active_engine_id()
253
259
 
254
260
  @classmethod
255
261
  def EventManager(cls) -> EventManager:
@@ -307,6 +313,14 @@ class GriptapeNodes(metaclass=SingletonMeta):
307
313
  def VersionCompatibilityManager(cls) -> VersionCompatibilityManager:
308
314
  return GriptapeNodes.get_instance()._version_compatibility_manager
309
315
 
316
+ @classmethod
317
+ def SessionManager(cls) -> SessionManager:
318
+ return GriptapeNodes.get_instance()._session_manager
319
+
320
+ @classmethod
321
+ def EngineIdentityManager(cls) -> EngineIdentityManager:
322
+ return GriptapeNodes.get_instance()._engine_identity_manager
323
+
310
324
  @classmethod
311
325
  def clear_data(cls) -> None:
312
326
  # Get canvas
@@ -348,13 +362,11 @@ class GriptapeNodes(metaclass=SingletonMeta):
348
362
  return GetEngineVersionResultFailure()
349
363
 
350
364
  def handle_session_start_request(self, request: AppStartSessionRequest) -> ResultPayload: # noqa: ARG002
351
- current_session_id = BaseEvent._session_id
365
+ current_session_id = GriptapeNodes.SessionManager().get_active_session_id()
352
366
  if current_session_id is None:
353
367
  # Client wants a new session
354
368
  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)
369
+ GriptapeNodes.SessionManager().save_session(current_session_id)
358
370
  details = f"New session '{current_session_id}' started at {datetime.now(tz=UTC)}."
359
371
  logger.info(details)
360
372
  else:
@@ -364,16 +376,14 @@ class GriptapeNodes(metaclass=SingletonMeta):
364
376
 
365
377
  def handle_session_end_request(self, _: AppEndSessionRequest) -> ResultPayload:
366
378
  try:
367
- previous_session_id = BaseEvent._session_id
368
- if BaseEvent._session_id is None:
379
+ previous_session_id = GriptapeNodes.SessionManager().get_active_session_id()
380
+ if previous_session_id is None:
369
381
  details = "No active session to end."
370
382
  logger.info(details)
371
383
  else:
372
- details = f"Session '{BaseEvent._session_id}' ended at {datetime.now(tz=UTC)}."
384
+ details = f"Session '{previous_session_id}' ended at {datetime.now(tz=UTC)}."
373
385
  logger.info(details)
374
- BaseEvent._session_id = None
375
- # Clear the persisted session ID from XDG state directory
376
- SessionPersistence.clear_persisted_session()
386
+ GriptapeNodes.SessionManager().clear_saved_session()
377
387
 
378
388
  return AppEndSessionResultSuccess(session_id=previous_session_id)
379
389
  except Exception as err:
@@ -382,7 +392,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
382
392
  return AppEndSessionResultFailure()
383
393
 
384
394
  def handle_get_session_request(self, _: AppGetSessionRequest) -> ResultPayload:
385
- return AppGetSessionResultSuccess(session_id=BaseEvent._session_id)
395
+ return AppGetSessionResultSuccess(session_id=GriptapeNodes.SessionManager().get_active_session_id())
386
396
 
387
397
  def handle_session_heartbeat_request(self, request: SessionHeartbeatRequest) -> ResultPayload: # noqa: ARG002
388
398
  """Handle session heartbeat requests.
@@ -390,11 +400,12 @@ class GriptapeNodes(metaclass=SingletonMeta):
390
400
  Simply verifies that the session is active and responds with success.
391
401
  """
392
402
  try:
393
- if BaseEvent._session_id is None:
403
+ active_session_id = GriptapeNodes.SessionManager().get_active_session_id()
404
+ if active_session_id is None:
394
405
  logger.warning("Session heartbeat received but no active session found")
395
406
  return SessionHeartbeatResultFailure()
396
407
 
397
- logger.debug("Session heartbeat successful for session: %s", BaseEvent._session_id)
408
+ logger.debug("Session heartbeat successful for session: %s", active_session_id)
398
409
  return SessionHeartbeatResultSuccess()
399
410
  except Exception as err:
400
411
  logger.error("Failed to handle session heartbeat: %s", err)
@@ -413,15 +424,15 @@ class GriptapeNodes(metaclass=SingletonMeta):
413
424
  workflow_info = self._get_current_workflow_info()
414
425
 
415
426
  # Get engine name
416
- engine_name = EngineIdentity.get_engine_name()
427
+ engine_name = GriptapeNodes.EngineIdentityManager().get_engine_name()
417
428
 
418
429
  logger.debug("Engine heartbeat successful")
419
430
  return EngineHeartbeatResultSuccess(
420
431
  heartbeat_id=request.heartbeat_id,
421
432
  engine_version=engine_version,
422
433
  engine_name=engine_name,
423
- engine_id=BaseEvent._engine_id,
424
- session_id=BaseEvent._session_id,
434
+ engine_id=GriptapeNodes.EngineIdentityManager().get_active_engine_id(),
435
+ session_id=GriptapeNodes.SessionManager().get_active_session_id(),
425
436
  timestamp=datetime.now(tz=UTC).isoformat(),
426
437
  **instance_info,
427
438
  **workflow_info,
@@ -433,7 +444,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
433
444
  def handle_get_engine_name_request(self, request: GetEngineNameRequest) -> ResultPayload: # noqa: ARG002
434
445
  """Handle requests to get the current engine name."""
435
446
  try:
436
- engine_name = EngineIdentity.get_engine_name()
447
+ engine_name = GriptapeNodes.EngineIdentityManager().get_engine_name()
437
448
  logger.debug("Retrieved engine name: %s", engine_name)
438
449
  return GetEngineNameResultSuccess(engine_name=engine_name)
439
450
  except Exception as err:
@@ -451,7 +462,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
451
462
  return SetEngineNameResultFailure(error_message=error_message)
452
463
 
453
464
  # Set the new engine name
454
- EngineIdentity.set_engine_name(request.engine_name.strip())
465
+ GriptapeNodes.EngineIdentityManager().set_engine_name(request.engine_name.strip())
455
466
  logger.info("Engine name set to: %s", request.engine_name.strip())
456
467
  return SetEngineNameResultSuccess(engine_name=request.engine_name.strip())
457
468
 
@@ -543,291 +554,3 @@ class GriptapeNodes(metaclass=SingletonMeta):
543
554
  logger.warning("Failed to get current workflow info: %s", err)
544
555
 
545
556
  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)
@@ -109,7 +109,7 @@ class AgentManager:
109
109
  msg = f"Secret '{API_KEY_ENV_VAR}' not found"
110
110
  raise ValueError(msg)
111
111
  return NodesPromptImageGenerationTool(
112
- image_generation_driver=GriptapeCloudImageGenerationDriver(api_key=api_key, model="dall-e-3"),
112
+ image_generation_driver=GriptapeCloudImageGenerationDriver(api_key=api_key, model="gpt-image-1"),
113
113
  static_files_manager=self.static_files_manager,
114
114
  )
115
115
 
@@ -0,0 +1,146 @@
1
+ """Manages engine identity state.
2
+
3
+ Centralizes engine identity management, providing a consistent interface for
4
+ engine ID and name operations.
5
+ """
6
+
7
+ import logging
8
+ from pathlib import Path
9
+
10
+ from griptape_nodes.retained_mode.events.base_events import BaseEvent
11
+ from griptape_nodes.retained_mode.managers.event_manager import EventManager
12
+ from griptape_nodes.retained_mode.utils.engine_identity import EngineIdentity
13
+
14
+ logger = logging.getLogger("griptape_nodes")
15
+
16
+
17
+ class EngineIdentityManager:
18
+ """Manages engine identity and active engine state."""
19
+
20
+ _active_engine_id: str | None = None
21
+
22
+ def __init__(self, event_manager: EventManager | None = None) -> None:
23
+ """Initialize the EngineIdentityManager.
24
+
25
+ Args:
26
+ event_manager: The EventManager instance to use for event handling.
27
+ """
28
+ if event_manager is not None:
29
+ # Register event handlers here when engine events are defined
30
+ pass
31
+
32
+ @classmethod
33
+ def get_active_engine_id(cls) -> str | None:
34
+ """Get the active engine ID.
35
+
36
+ Returns:
37
+ str | None: The active engine ID or None if not set
38
+ """
39
+ return cls._active_engine_id
40
+
41
+ @classmethod
42
+ def set_active_engine_id(cls, engine_id: str) -> None:
43
+ """Set the active engine ID.
44
+
45
+ Args:
46
+ engine_id: The engine ID to set as active
47
+ """
48
+ cls._active_engine_id = engine_id
49
+ logger.debug("Set active engine ID to: %s", engine_id)
50
+
51
+ @classmethod
52
+ def initialize_engine_id(cls) -> str:
53
+ """Initialize the engine ID if not already set."""
54
+ if cls._active_engine_id is None:
55
+ engine_id = EngineIdentity.get_engine_id()
56
+ BaseEvent._engine_id = engine_id
57
+ cls._active_engine_id = engine_id
58
+ logger.debug("Initialized engine ID: %s", engine_id)
59
+
60
+ return cls._active_engine_id
61
+
62
+ @classmethod
63
+ def get_engine_data(cls) -> dict:
64
+ """Get the current engine data, creating default if it doesn't exist.
65
+
66
+ Returns:
67
+ dict: The current engine data
68
+ """
69
+ return EngineIdentity.get_engine_data()
70
+
71
+ @classmethod
72
+ def get_engine_name(cls) -> str:
73
+ """Get the engine name.
74
+
75
+ Returns:
76
+ str: The engine name
77
+ """
78
+ return EngineIdentity.get_engine_name()
79
+
80
+ @classmethod
81
+ def set_engine_name(cls, engine_name: str) -> None:
82
+ """Set and persist the current engine name.
83
+
84
+ Args:
85
+ engine_name: The new engine name to set
86
+ """
87
+ EngineIdentity.set_engine_name(engine_name)
88
+ logger.info("Updated engine name to: %s", engine_name)
89
+
90
+ @classmethod
91
+ def get_all_engines(cls) -> list[dict]:
92
+ """Get all registered engines.
93
+
94
+ Returns:
95
+ list[dict]: List of all engine data
96
+ """
97
+ return EngineIdentity.get_all_engines()
98
+
99
+ @classmethod
100
+ def get_default_engine_id(cls) -> str | None:
101
+ """Get the default engine ID.
102
+
103
+ Returns:
104
+ str | None: The default engine ID or None if not set
105
+ """
106
+ return EngineIdentity.get_default_engine_id()
107
+
108
+ @classmethod
109
+ def set_default_engine_id(cls, engine_id: str) -> None:
110
+ """Set the default engine ID.
111
+
112
+ Args:
113
+ engine_id: The engine ID to set as default
114
+
115
+ Raises:
116
+ ValueError: If engine_id is not found in registered engines
117
+ """
118
+ try:
119
+ EngineIdentity.set_default_engine_id(engine_id)
120
+ logger.info("Set default engine ID to: %s", engine_id)
121
+ except ValueError as e:
122
+ logger.error("Failed to set default engine ID: %s", e)
123
+ raise
124
+
125
+ @classmethod
126
+ def get_engine_data_file_path(cls) -> Path:
127
+ """Get the path where engine data is stored (for debugging/inspection).
128
+
129
+ Returns:
130
+ Path: The path to the engine data file
131
+ """
132
+ return EngineIdentity.get_engine_data_file_path()
133
+
134
+ @classmethod
135
+ def ensure_engine_initialized(cls) -> str:
136
+ """Ensure engine is initialized and return the engine ID.
137
+
138
+ Returns:
139
+ str: The initialized engine ID
140
+ """
141
+ cls.initialize_engine_id()
142
+ engine_id = cls.get_active_engine_id()
143
+ if engine_id is None:
144
+ msg = "Failed to initialize engine ID"
145
+ raise RuntimeError(msg)
146
+ return engine_id