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
@@ -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:
@@ -109,18 +117,57 @@ class AgentManager:
109
117
  msg = f"Secret '{API_KEY_ENV_VAR}' not found"
110
118
  raise ValueError(msg)
111
119
  return NodesPromptImageGenerationTool(
112
- image_generation_driver=GriptapeCloudImageGenerationDriver(api_key=api_key, model="dall-e-3"),
120
+ image_generation_driver=GriptapeCloudImageGenerationDriver(api_key=api_key, model="gpt-image-1"),
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
@@ -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
@@ -66,7 +66,13 @@ class EventManager:
66
66
  del self._request_type_to_manager[request_type]
67
67
 
68
68
  def handle_request(
69
- self, request: RP, operation_depth_mgr: "OperationDepthManager", workflow_mgr: "WorkflowManager"
69
+ self,
70
+ request: RP,
71
+ *,
72
+ operation_depth_mgr: "OperationDepthManager",
73
+ workflow_mgr: "WorkflowManager",
74
+ response_topic: str | None = None,
75
+ request_id: str | None = None,
70
76
  ) -> ResultPayload:
71
77
  """Publish an event to the manager assigned to its type.
72
78
 
@@ -74,6 +80,8 @@ class EventManager:
74
80
  request: The request to handle
75
81
  operation_depth_mgr: The operation depth manager to use
76
82
  workflow_mgr: The workflow manager to use
83
+ response_topic: The topic to send the response to (optional)
84
+ request_id: The ID of the request to correlate with the response (optional)
77
85
  """
78
86
  # Notify the manager of the event type
79
87
  with operation_depth_mgr as depth_manager:
@@ -91,7 +99,7 @@ class EventManager:
91
99
 
92
100
  retained_mode_str = None
93
101
  # If request_id exists, that means it's a direct request from the GUI (not internal), and should be echoed by retained mode.
94
- if depth_manager.is_top_level() and request.request_id is not None:
102
+ if depth_manager.is_top_level() and request_id is not None:
95
103
  retained_mode_str = depth_manager.request_retained_mode_translation(request)
96
104
 
97
105
  # Some requests have fields marked as "omit_from_result" which should be removed from the request
@@ -101,8 +109,10 @@ class EventManager:
101
109
  if result_payload.succeeded():
102
110
  result_event = EventResultSuccess(
103
111
  request=request,
112
+ request_id=request_id,
104
113
  result=result_payload,
105
114
  retained_mode=retained_mode_str,
115
+ response_topic=response_topic,
106
116
  )
107
117
  # If the result is a success, and the WorkflowAlteredMixin is present, that means the flow has been changed in some way.
108
118
  # In that case, we need to flush the element changes, so we add one to the event queue.
@@ -114,8 +124,10 @@ class EventManager:
114
124
  else:
115
125
  result_event = EventResultFailure(
116
126
  request=request,
127
+ request_id=request_id,
117
128
  result=result_payload,
118
129
  retained_mode=retained_mode_str,
130
+ response_topic=response_topic,
119
131
  )
120
132
  wrapped_event = GriptapeNodeEvent(wrapped_event=result_event)
121
133
  EventBus.publish_event(wrapped_event)