griptape-nodes 0.42.0__py3-none-any.whl → 0.43.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. griptape_nodes/__init__.py +0 -0
  2. griptape_nodes/app/.python-version +0 -0
  3. griptape_nodes/app/__init__.py +1 -6
  4. griptape_nodes/app/api.py +199 -0
  5. griptape_nodes/app/app.py +140 -225
  6. griptape_nodes/app/watch.py +1 -1
  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 +2 -1
  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 +0 -0
  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 +41 -12
  33. griptape_nodes/machines/fsm.py +16 -2
  34. griptape_nodes/machines/node_resolution.py +0 -0
  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 +1 -1
  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 +0 -0
  46. griptape_nodes/retained_mode/events/app_events.py +6 -2
  47. griptape_nodes/retained_mode/events/arbitrary_python_events.py +0 -0
  48. griptape_nodes/retained_mode/events/base_events.py +6 -6
  49. griptape_nodes/retained_mode/events/config_events.py +0 -0
  50. griptape_nodes/retained_mode/events/connection_events.py +0 -0
  51. griptape_nodes/retained_mode/events/context_events.py +0 -0
  52. griptape_nodes/retained_mode/events/execution_events.py +0 -0
  53. griptape_nodes/retained_mode/events/flow_events.py +0 -0
  54. griptape_nodes/retained_mode/events/generate_request_payload_schemas.py +0 -0
  55. griptape_nodes/retained_mode/events/library_events.py +2 -2
  56. griptape_nodes/retained_mode/events/logger_events.py +0 -0
  57. griptape_nodes/retained_mode/events/node_events.py +0 -0
  58. griptape_nodes/retained_mode/events/object_events.py +0 -0
  59. griptape_nodes/retained_mode/events/os_events.py +104 -2
  60. griptape_nodes/retained_mode/events/parameter_events.py +0 -0
  61. griptape_nodes/retained_mode/events/payload_registry.py +0 -0
  62. griptape_nodes/retained_mode/events/secrets_events.py +0 -0
  63. griptape_nodes/retained_mode/events/static_file_events.py +0 -0
  64. griptape_nodes/retained_mode/events/validation_events.py +0 -0
  65. griptape_nodes/retained_mode/events/workflow_events.py +0 -0
  66. griptape_nodes/retained_mode/griptape_nodes.py +43 -40
  67. griptape_nodes/retained_mode/managers/__init__.py +0 -0
  68. griptape_nodes/retained_mode/managers/agent_manager.py +48 -22
  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 +0 -0
  73. griptape_nodes/retained_mode/managers/event_manager.py +0 -0
  74. griptape_nodes/retained_mode/managers/flow_manager.py +2 -0
  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 +144 -39
  88. griptape_nodes/retained_mode/managers/node_manager.py +86 -72
  89. griptape_nodes/retained_mode/managers/object_manager.py +0 -0
  90. griptape_nodes/retained_mode/managers/operation_manager.py +0 -0
  91. griptape_nodes/retained_mode/managers/os_manager.py +517 -12
  92. griptape_nodes/retained_mode/managers/secrets_manager.py +0 -0
  93. griptape_nodes/retained_mode/managers/session_manager.py +0 -0
  94. griptape_nodes/retained_mode/managers/settings.py +0 -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 +199 -2
  98. griptape_nodes/retained_mode/retained_mode.py +0 -0
  99. griptape_nodes/retained_mode/utils/__init__.py +0 -0
  100. griptape_nodes/retained_mode/utils/engine_identity.py +0 -0
  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.42.0.dist-info → griptape_nodes-0.43.0.dist-info}/entry_points.txt +1 -0
  128. griptape_nodes/app/app_sessions.py +0 -554
  129. griptape_nodes-0.42.0.dist-info/METADATA +0 -78
  130. griptape_nodes-0.42.0.dist-info/RECORD +0 -113
  131. griptape_nodes-0.42.0.dist-info/WHEEL +0 -4
  132. griptape_nodes-0.42.0.dist-info/licenses/LICENSE +0 -201
@@ -1332,6 +1332,9 @@ class WorkflowManager:
1332
1332
  right=ast.Constant(value=None),
1333
1333
  )
1334
1334
 
1335
+ # Generate the ensure flow context function call
1336
+ ensure_context_call = self._generate_ensure_flow_context_call()
1337
+
1335
1338
  # Create conditional logic: workflow_executor = workflow_executor or LocalWorkflowExecutor()
1336
1339
  executor_assign = ast.Assign(
1337
1340
  targets=[ast.Name(id="workflow_executor", ctx=ast.Store())],
@@ -1373,7 +1376,7 @@ class WorkflowManager:
1373
1376
  func_def = ast.FunctionDef(
1374
1377
  name="execute_workflow",
1375
1378
  args=args,
1376
- body=[executor_assign, run_call, return_stmt],
1379
+ body=[ensure_context_call, executor_assign, run_call, return_stmt],
1377
1380
  decorator_list=[],
1378
1381
  returns=return_annotation,
1379
1382
  type_params=[],
@@ -1592,7 +1595,201 @@ class WorkflowManager:
1592
1595
  )
1593
1596
  ast.fix_missing_locations(if_node)
1594
1597
 
1595
- return [func_def, if_node]
1598
+ # Generate the ensure flow context function
1599
+ ensure_context_func = self._generate_ensure_flow_context_function(import_recorder)
1600
+
1601
+ return [ensure_context_func, func_def, if_node]
1602
+
1603
+ def _generate_ensure_flow_context_function(
1604
+ self,
1605
+ import_recorder: ImportRecorder,
1606
+ ) -> ast.FunctionDef:
1607
+ """Generates the _ensure_workflow_context function for the serialized workflow file."""
1608
+ import_recorder.add_from_import("griptape_nodes.retained_mode.events.flow_events", "GetTopLevelFlowRequest")
1609
+ import_recorder.add_from_import(
1610
+ "griptape_nodes.retained_mode.events.flow_events", "GetTopLevelFlowResultSuccess"
1611
+ )
1612
+
1613
+ # Function signature: def _ensure_workflow_context():
1614
+ func_def = ast.FunctionDef(
1615
+ name="_ensure_workflow_context",
1616
+ args=ast.arguments(
1617
+ posonlyargs=[],
1618
+ args=[],
1619
+ vararg=None,
1620
+ kwonlyargs=[],
1621
+ kw_defaults=[],
1622
+ kwarg=None,
1623
+ defaults=[],
1624
+ ),
1625
+ body=[],
1626
+ decorator_list=[],
1627
+ returns=None,
1628
+ type_params=[],
1629
+ )
1630
+
1631
+ context_manager_assign = ast.Assign(
1632
+ targets=[ast.Name(id="context_manager", ctx=ast.Store())],
1633
+ value=ast.Call(
1634
+ func=ast.Attribute(
1635
+ value=ast.Name(id="GriptapeNodes", ctx=ast.Load()),
1636
+ attr="ContextManager",
1637
+ ctx=ast.Load(),
1638
+ ),
1639
+ args=[],
1640
+ keywords=[],
1641
+ ),
1642
+ )
1643
+
1644
+ # if not context_manager.has_current_flow():
1645
+ has_flow_check = ast.UnaryOp(
1646
+ op=ast.Not(),
1647
+ operand=ast.Call(
1648
+ func=ast.Attribute(
1649
+ value=ast.Name(id="context_manager", ctx=ast.Load()),
1650
+ attr="has_current_flow",
1651
+ ctx=ast.Load(),
1652
+ ),
1653
+ args=[],
1654
+ keywords=[],
1655
+ ),
1656
+ )
1657
+
1658
+ # top_level_flow_request = GetTopLevelFlowRequest() # noqa: ERA001
1659
+ flow_request_assign = ast.Assign(
1660
+ targets=[ast.Name(id="top_level_flow_request", ctx=ast.Store())],
1661
+ value=ast.Call(
1662
+ func=ast.Name(id="GetTopLevelFlowRequest", ctx=ast.Load()),
1663
+ args=[],
1664
+ keywords=[],
1665
+ ),
1666
+ )
1667
+
1668
+ # top_level_flow_result = GriptapeNodes.handle_request(top_level_flow_request) # noqa: ERA001
1669
+ flow_result_assign = ast.Assign(
1670
+ targets=[ast.Name(id="top_level_flow_result", ctx=ast.Store())],
1671
+ value=ast.Call(
1672
+ func=ast.Attribute(
1673
+ value=ast.Name(id="GriptapeNodes", ctx=ast.Load()),
1674
+ attr="handle_request",
1675
+ ctx=ast.Load(),
1676
+ ),
1677
+ args=[ast.Name(id="top_level_flow_request", ctx=ast.Load())],
1678
+ keywords=[],
1679
+ ),
1680
+ )
1681
+
1682
+ # isinstance check and flow_name is not None
1683
+ isinstance_check = ast.Call(
1684
+ func=ast.Name(id="isinstance", ctx=ast.Load()),
1685
+ args=[
1686
+ ast.Name(id="top_level_flow_result", ctx=ast.Load()),
1687
+ ast.Name(id="GetTopLevelFlowResultSuccess", ctx=ast.Load()),
1688
+ ],
1689
+ keywords=[],
1690
+ )
1691
+
1692
+ flow_name_check = ast.Compare(
1693
+ left=ast.Attribute(
1694
+ value=ast.Name(id="top_level_flow_result", ctx=ast.Load()),
1695
+ attr="flow_name",
1696
+ ctx=ast.Load(),
1697
+ ),
1698
+ ops=[ast.IsNot()],
1699
+ comparators=[ast.Constant(value=None)],
1700
+ )
1701
+
1702
+ success_condition = ast.BoolOp(
1703
+ op=ast.And(),
1704
+ values=[isinstance_check, flow_name_check],
1705
+ )
1706
+
1707
+ # flow_manager = GriptapeNodes.FlowManager() # noqa: ERA001
1708
+ flow_manager_assign = ast.Assign(
1709
+ targets=[ast.Name(id="flow_manager", ctx=ast.Store())],
1710
+ value=ast.Call(
1711
+ func=ast.Attribute(
1712
+ value=ast.Name(id="GriptapeNodes", ctx=ast.Load()),
1713
+ attr="FlowManager",
1714
+ ctx=ast.Load(),
1715
+ ),
1716
+ args=[],
1717
+ keywords=[],
1718
+ ),
1719
+ )
1720
+
1721
+ # flow_obj = flow_manager.get_flow_by_name(top_level_flow_result.flow_name) # noqa: ERA001
1722
+ flow_obj_assign = ast.Assign(
1723
+ targets=[ast.Name(id="flow_obj", ctx=ast.Store())],
1724
+ value=ast.Call(
1725
+ func=ast.Attribute(
1726
+ value=ast.Name(id="flow_manager", ctx=ast.Load()),
1727
+ attr="get_flow_by_name",
1728
+ ctx=ast.Load(),
1729
+ ),
1730
+ args=[
1731
+ ast.Attribute(
1732
+ value=ast.Name(id="top_level_flow_result", ctx=ast.Load()),
1733
+ attr="flow_name",
1734
+ ctx=ast.Load(),
1735
+ )
1736
+ ],
1737
+ keywords=[],
1738
+ ),
1739
+ )
1740
+
1741
+ # context_manager.push_flow(flow_obj) # noqa: ERA001
1742
+ push_flow_call = ast.Expr(
1743
+ value=ast.Call(
1744
+ func=ast.Attribute(
1745
+ value=ast.Name(id="context_manager", ctx=ast.Load()),
1746
+ attr="push_flow",
1747
+ ctx=ast.Load(),
1748
+ ),
1749
+ args=[ast.Name(id="flow_obj", ctx=ast.Load())],
1750
+ keywords=[],
1751
+ ),
1752
+ )
1753
+
1754
+ # Build the inner if statement for success condition
1755
+ success_if = ast.If(
1756
+ test=success_condition,
1757
+ body=[
1758
+ flow_manager_assign,
1759
+ flow_obj_assign,
1760
+ push_flow_call,
1761
+ ],
1762
+ orelse=[],
1763
+ )
1764
+
1765
+ # Build the main if statement
1766
+ main_if = ast.If(
1767
+ test=has_flow_check,
1768
+ body=[
1769
+ flow_request_assign,
1770
+ flow_result_assign,
1771
+ success_if,
1772
+ ],
1773
+ orelse=[],
1774
+ )
1775
+
1776
+ # Set the function body
1777
+ func_def.body = [context_manager_assign, main_if]
1778
+ ast.fix_missing_locations(func_def)
1779
+
1780
+ return func_def
1781
+
1782
+ def _generate_ensure_flow_context_call(
1783
+ self,
1784
+ ) -> ast.Expr:
1785
+ """Generates the call to _ensure_workflow_context() function."""
1786
+ return ast.Expr(
1787
+ value=ast.Call(
1788
+ func=ast.Name(id="_ensure_workflow_context", ctx=ast.Load()),
1789
+ args=[],
1790
+ keywords=[],
1791
+ )
1792
+ )
1596
1793
 
1597
1794
  def _generate_workflow_run_prerequisite_code(
1598
1795
  self,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,127 @@
1
+ from collections.abc import Callable
2
+ from dataclasses import dataclass, field
3
+ from typing import Any
4
+
5
+ from griptape_nodes.exe_types.core_types import Parameter, Trait
6
+
7
+
8
+ @dataclass(eq=False)
9
+ class FileSystemPicker(Trait):
10
+ allow_files: bool = False
11
+ allow_directories: bool = True
12
+ multiple: bool = False
13
+ file_types: list[str] = field(default_factory=list)
14
+ file_extensions: list[str] = field(default_factory=list)
15
+ exclude_patterns: list[str] = field(default_factory=list)
16
+ include_patterns: list[str] = field(default_factory=list)
17
+ max_file_size: int | None = None
18
+ min_file_size: int | None = None
19
+ workspace_only: bool = True
20
+ initial_path: str | None = None
21
+ element_id: str = field(default_factory=lambda: "FileSystemPicker")
22
+
23
+ def __init__( # noqa: PLR0913
24
+ self,
25
+ *,
26
+ allow_files: bool = False,
27
+ allow_directories: bool = True,
28
+ multiple: bool = False,
29
+ file_types: list[str] | None = None,
30
+ file_extensions: list[str] | None = None,
31
+ exclude_patterns: list[str] | None = None,
32
+ include_patterns: list[str] | None = None,
33
+ max_file_size: int | None = None,
34
+ min_file_size: int | None = None,
35
+ workspace_only: bool = True,
36
+ initial_path: str | None = None,
37
+ ) -> None:
38
+ super().__init__()
39
+ self.allow_files = allow_files
40
+ self.allow_directories = allow_directories
41
+ self.multiple = multiple
42
+ self.file_types = file_types or []
43
+ self.file_extensions = file_extensions or []
44
+ self.exclude_patterns = exclude_patterns or []
45
+ self.include_patterns = include_patterns or []
46
+ self.max_file_size = max_file_size
47
+ self.min_file_size = min_file_size
48
+ self.workspace_only = workspace_only
49
+ self.initial_path = initial_path
50
+
51
+ @classmethod
52
+ def get_trait_keys(cls) -> list[str]:
53
+ return ["fileSystemPicker", "file_picker", "folder_picker"]
54
+
55
+ def ui_options_for_trait(self) -> dict[str, Any]:
56
+ """Generate the fileSystemPicker UI options dictionary."""
57
+ options: dict[str, Any] = {
58
+ "allowFiles": self.allow_files,
59
+ "allowDirectories": self.allow_directories,
60
+ "multiple": self.multiple,
61
+ "workspaceOnly": self.workspace_only,
62
+ }
63
+
64
+ # Add file types/extensions
65
+ if self.file_types:
66
+ options["fileTypes"] = self.file_types
67
+ elif self.file_extensions:
68
+ options["fileExtensions"] = self.file_extensions
69
+
70
+ # Add patterns
71
+ if self.exclude_patterns:
72
+ options["excludePatterns"] = self.exclude_patterns
73
+ if self.include_patterns:
74
+ options["includePatterns"] = self.include_patterns
75
+
76
+ # Add size limits
77
+ if self.max_file_size is not None:
78
+ options["maxFileSize"] = self.max_file_size
79
+ if self.min_file_size is not None:
80
+ options["minFileSize"] = self.min_file_size
81
+
82
+ # Add initial path
83
+ if self.initial_path:
84
+ options["initialPath"] = self.initial_path
85
+
86
+ return {"fileSystemPicker": options}
87
+
88
+ def validators_for_trait(self) -> list[Callable[[Parameter, Any], Any]]:
89
+ """Validate file system picker configuration."""
90
+
91
+ def validate(param: Parameter, value: Any) -> None: # noqa: ARG001
92
+ # Validate that at least one selection type is enabled
93
+ if not self.allow_files and not self.allow_directories:
94
+ msg = "At least one of allow_files or allow_directories must be True"
95
+ raise ValueError(msg)
96
+
97
+ # Validate file size limits
98
+ if (
99
+ self.max_file_size is not None
100
+ and self.min_file_size is not None
101
+ and self.max_file_size < self.min_file_size
102
+ ):
103
+ msg = "max_file_size cannot be less than min_file_size"
104
+ raise ValueError(msg)
105
+
106
+ # Validate that file types/extensions are valid
107
+ all_file_types = self.file_types + self.file_extensions
108
+ for file_type in all_file_types:
109
+ if not file_type.startswith("."):
110
+ msg = f"File type '{file_type}' must start with a dot (e.g., '.py')"
111
+ raise ValueError(msg)
112
+
113
+ return [validate]
114
+
115
+ def converters_for_trait(self) -> list[Callable]:
116
+ """Convert file system picker values if needed."""
117
+
118
+ def converter(value: Any) -> Any:
119
+ # If value is a string and we expect a list, convert it
120
+ if isinstance(value, str) and self.multiple:
121
+ return [value] if value else []
122
+ return value
123
+
124
+ return [converter]
125
+
126
+
127
+ # These Traits get added to a list on the parameter. When they are added they apply their functions to the parameter.
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -38,7 +38,7 @@ def _download_and_run_installer() -> None:
38
38
  """Runs the update commands for the engine."""
39
39
  console.print("[bold green]Updating self...[/bold green]")
40
40
  try:
41
- subprocess.run( # noqa: S603
41
+ subprocess.run(
42
42
  ["uv", "tool", "upgrade", "griptape-nodes"], # noqa: S607
43
43
  text=True,
44
44
  capture_output=True,
@@ -57,7 +57,7 @@ def _sync_libraries() -> None:
57
57
  """Syncs the libraries for the engine."""
58
58
  console.print("[bold green]Syncing libraries...[/bold green]")
59
59
  try:
60
- subprocess.run( # noqa: S603
60
+ subprocess.run(
61
61
  ["griptape-nodes", "libraries", "sync"], # noqa: S607
62
62
  text=True,
63
63
  capture_output=True,
File without changes
File without changes
File without changes
@@ -0,0 +1,128 @@
1
+ """Image preview utilities for generating thumbnails and previews."""
2
+
3
+ import base64
4
+ import io
5
+ from pathlib import Path
6
+
7
+ from PIL import Image
8
+
9
+ from griptape_nodes.retained_mode.griptape_nodes import logger
10
+
11
+
12
+ def create_image_preview(
13
+ image_path: Path, max_width: int = 512, max_height: int = 512, quality: int = 85, image_format: str = "WEBP"
14
+ ) -> str | None:
15
+ """Create a small preview image from a file path.
16
+
17
+ Args:
18
+ image_path: Path to the image file
19
+ max_width: Maximum width for the preview
20
+ max_height: Maximum height for the preview
21
+ quality: WebP quality (1-100)
22
+ image_format: Output format (WEBP, JPEG, PNG, etc.)
23
+
24
+ Returns:
25
+ Base64 encoded data URL of the preview, or None if failed
26
+ """
27
+ try:
28
+ # Open and resize the image
29
+ with Image.open(image_path) as img:
30
+ # Convert to RGB if necessary (for WebP/JPEG output)
31
+ if image_format.upper() in ("WEBP", "JPEG") and img.mode in ("RGBA", "LA", "P"):
32
+ converted_img = img.convert("RGB")
33
+ else:
34
+ converted_img = img
35
+
36
+ # Calculate new size maintaining aspect ratio
37
+ converted_img.thumbnail((max_width, max_height), Image.Resampling.LANCZOS)
38
+
39
+ # Save to bytes buffer
40
+ buffer = io.BytesIO()
41
+ converted_img.save(buffer, format=image_format, quality=quality, optimize=True)
42
+ buffer.seek(0)
43
+
44
+ # Convert to base64
45
+ image_bytes = buffer.getvalue()
46
+ base64_data = base64.b64encode(image_bytes).decode("utf-8")
47
+
48
+ # Create data URL
49
+ mime_type = f"image/{image_format.lower()}"
50
+ data_url = f"data:{mime_type};base64,{base64_data}"
51
+
52
+ logger.debug(f"Created preview for {image_path}: {img.size} -> {len(image_bytes)} bytes")
53
+ return data_url
54
+
55
+ except Exception as e:
56
+ logger.warning(f"Failed to create preview for {image_path}: {e}")
57
+ return None
58
+
59
+
60
+ def create_image_preview_from_bytes(
61
+ image_bytes: bytes, max_width: int = 512, max_height: int = 512, quality: int = 85, image_format: str = "WEBP"
62
+ ) -> str | None:
63
+ """Create a small preview image from bytes.
64
+
65
+ Args:
66
+ image_bytes: Raw image bytes
67
+ max_width: Maximum width for the preview
68
+ max_height: Maximum height for the preview
69
+ quality: WebP quality (1-100)
70
+ image_format: Output format (WEBP, JPEG, PNG, etc.)
71
+
72
+ Returns:
73
+ Base64 encoded data URL of the preview, or None if failed
74
+ """
75
+ try:
76
+ # Open image from bytes
77
+ with Image.open(io.BytesIO(image_bytes)) as img:
78
+ # Convert to RGB if necessary (for WebP/JPEG output)
79
+ if image_format.upper() in ("WEBP", "JPEG") and img.mode in ("RGBA", "LA", "P"):
80
+ converted_img = img.convert("RGB")
81
+ else:
82
+ converted_img = img
83
+
84
+ # Calculate new size maintaining aspect ratio
85
+ converted_img.thumbnail((max_width, max_height), Image.Resampling.LANCZOS)
86
+
87
+ # Save to bytes buffer
88
+ buffer = io.BytesIO()
89
+ converted_img.save(buffer, format=image_format, quality=quality, optimize=True)
90
+ buffer.seek(0)
91
+
92
+ # Convert to base64
93
+ preview_bytes = buffer.getvalue()
94
+ base64_data = base64.b64encode(preview_bytes).decode("utf-8")
95
+
96
+ # Create data URL
97
+ mime_type = f"image/{image_format.lower()}"
98
+ data_url = f"data:{mime_type};base64,{base64_data}"
99
+
100
+ logger.debug(f"Created preview from bytes: {len(image_bytes)} -> {len(preview_bytes)} bytes")
101
+ return data_url
102
+
103
+ except Exception as e:
104
+ logger.warning(f"Failed to create preview from bytes: {e}")
105
+ return None
106
+
107
+
108
+ def get_image_info(image_path: Path) -> dict | None:
109
+ """Get basic information about an image file.
110
+
111
+ Args:
112
+ image_path: Path to the image file
113
+
114
+ Returns:
115
+ Dictionary with image info (width, height, format, mode), or None if failed
116
+ """
117
+ try:
118
+ with Image.open(image_path) as img:
119
+ return {
120
+ "width": img.width,
121
+ "height": img.height,
122
+ "format": img.format,
123
+ "mode": img.mode,
124
+ "size_bytes": image_path.stat().st_size,
125
+ }
126
+ except Exception as e:
127
+ logger.warning(f"Failed to get image info for {image_path}: {e}")
128
+ return None
File without changes
File without changes
File without changes
@@ -7,7 +7,7 @@ from griptape_nodes.retained_mode.events.app_events import (
7
7
  GetEngineVersionResultSuccess,
8
8
  )
9
9
  from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes, Version
10
- from griptape_nodes.retained_mode.managers.library_manager import LibraryManager
10
+ from griptape_nodes.retained_mode.managers.library_lifecycle.library_status import LibraryStatus
11
11
  from griptape_nodes.retained_mode.managers.version_compatibility_manager import (
12
12
  LibraryVersionCompatibilityCheck,
13
13
  LibraryVersionCompatibilityIssue,
@@ -47,13 +47,13 @@ class ModifiedParametersSetRemovalCheck(LibraryVersionCompatibilityCheck):
47
47
  message=f"This library (built for engine version {library_version_str}) is incompatible with Griptape Nodes 0.39+. "
48
48
  "The 'modified_parameters_set' parameter has been removed from BaseNode methods: 'after_incoming_connection', 'after_outgoing_connection', 'after_incoming_connection_removed', 'after_outgoing_connection_removed', 'before_value_set', and 'after_value_set'. "
49
49
  "If this library overrides any of these methods, it will not load or function properly. Please update to a newer version of this library or contact the library author immediately.",
50
- severity=LibraryManager.LibraryStatus.UNUSABLE,
50
+ severity=LibraryStatus.UNUSABLE,
51
51
  ),
52
52
  LibraryVersionCompatibilityIssue(
53
53
  message=f"This library (built for engine version {library_version_str}) is incompatible with Griptape Nodes 0.39+."
54
54
  "The 'ui_options' field has been modified on all Elements. In order to function properly, all nodes must update ui_options by setting its value to a new dictionary. Updating ui_options by accessing the private field _ui_options will no longer create UI updates in the editor."
55
55
  "If this library accesses the private _ui_options field, it will not update the editor properly. Please update to a newer version of this library or contact the library author immediately.",
56
- severity=LibraryManager.LibraryStatus.UNUSABLE,
56
+ severity=LibraryStatus.UNUSABLE,
57
57
  ),
58
58
  ]
59
59
  if current_engine_version >= Version(0, 38, 0):
@@ -63,13 +63,13 @@ class ModifiedParametersSetRemovalCheck(LibraryVersionCompatibilityCheck):
63
63
  message=f"WARNING: The 'modified_parameters_set' parameter will be removed in Griptape Nodes 0.39 from BaseNode methods: 'after_incoming_connection', 'after_outgoing_connection', 'after_incoming_connection_removed', 'after_outgoing_connection_removed', 'before_value_set', and 'after_value_set'. "
64
64
  f"This library (built for engine version {library_version_str}) must be updated before the 0.39 release. "
65
65
  "If this library overrides any of these methods, it will fail to load in 0.39. If not, no action is necessary. Please contact the library author to confirm whether this library is impacted.",
66
- severity=LibraryManager.LibraryStatus.FLAWED,
66
+ severity=LibraryStatus.FLAWED,
67
67
  ),
68
68
  LibraryVersionCompatibilityIssue(
69
69
  message="WARNING: The 'ui_options' field has been modified in Griptape Nodes 0.38 on all BaseNodeElements."
70
70
  "In order to function properly, all nodes must update ui_options by setting its value to a new dictionary. Updating ui_options by accessing the private field _ui_options will no longer create UI updates in the editor."
71
71
  "If this library accesses the private _ui_options field, it will not update the editor properly. Please update to a newer version of this library or contact the library author immediately.",
72
- severity=LibraryManager.LibraryStatus.FLAWED,
72
+ severity=LibraryStatus.FLAWED,
73
73
  ),
74
74
  ]
75
75