griptape-nodes 0.37.1__py3-none-any.whl → 0.38.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- griptape_nodes/__init__.py +292 -132
- griptape_nodes/app/__init__.py +1 -6
- griptape_nodes/app/app.py +108 -76
- griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +80 -5
- griptape_nodes/drivers/storage/local_storage_driver.py +5 -1
- griptape_nodes/exe_types/core_types.py +84 -3
- griptape_nodes/exe_types/node_types.py +260 -50
- griptape_nodes/machines/node_resolution.py +2 -14
- griptape_nodes/retained_mode/events/agent_events.py +7 -0
- griptape_nodes/retained_mode/events/base_events.py +16 -0
- griptape_nodes/retained_mode/events/library_events.py +26 -0
- griptape_nodes/retained_mode/events/parameter_events.py +31 -0
- griptape_nodes/retained_mode/griptape_nodes.py +32 -0
- griptape_nodes/retained_mode/managers/agent_manager.py +25 -12
- griptape_nodes/retained_mode/managers/config_manager.py +37 -4
- griptape_nodes/retained_mode/managers/event_manager.py +15 -0
- griptape_nodes/retained_mode/managers/flow_manager.py +64 -61
- griptape_nodes/retained_mode/managers/library_manager.py +215 -45
- griptape_nodes/retained_mode/managers/node_manager.py +344 -147
- griptape_nodes/retained_mode/managers/operation_manager.py +6 -0
- griptape_nodes/retained_mode/managers/os_manager.py +6 -1
- griptape_nodes/retained_mode/managers/secrets_manager.py +7 -2
- griptape_nodes/retained_mode/managers/settings.py +2 -11
- griptape_nodes/retained_mode/managers/static_files_manager.py +12 -3
- griptape_nodes/retained_mode/managers/version_compatibility_manager.py +105 -0
- griptape_nodes/retained_mode/managers/workflow_manager.py +4 -4
- griptape_nodes/updater/__init__.py +14 -8
- griptape_nodes/version_compatibility/__init__.py +1 -0
- griptape_nodes/version_compatibility/versions/__init__.py +1 -0
- griptape_nodes/version_compatibility/versions/v0_39_0/__init__.py +1 -0
- griptape_nodes/version_compatibility/versions/v0_39_0/modified_parameters_set_removal.py +77 -0
- {griptape_nodes-0.37.1.dist-info → griptape_nodes-0.38.0.dist-info}/METADATA +4 -1
- {griptape_nodes-0.37.1.dist-info → griptape_nodes-0.38.0.dist-info}/RECORD +36 -33
- griptape_nodes/app/app_websocket.py +0 -481
- griptape_nodes/app/nodes_api_socket_manager.py +0 -117
- {griptape_nodes-0.37.1.dist-info → griptape_nodes-0.38.0.dist-info}/WHEEL +0 -0
- {griptape_nodes-0.37.1.dist-info → griptape_nodes-0.38.0.dist-info}/entry_points.txt +0 -0
- {griptape_nodes-0.37.1.dist-info → griptape_nodes-0.38.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -72,10 +72,14 @@ from griptape_nodes.retained_mode.events.library_events import (
|
|
|
72
72
|
RegisterLibraryFromRequirementSpecifierRequest,
|
|
73
73
|
RegisterLibraryFromRequirementSpecifierResultFailure,
|
|
74
74
|
RegisterLibraryFromRequirementSpecifierResultSuccess,
|
|
75
|
+
ReloadAllLibrariesRequest,
|
|
76
|
+
ReloadAllLibrariesResultFailure,
|
|
77
|
+
ReloadAllLibrariesResultSuccess,
|
|
75
78
|
UnloadLibraryFromRegistryRequest,
|
|
76
79
|
UnloadLibraryFromRegistryResultFailure,
|
|
77
80
|
UnloadLibraryFromRegistryResultSuccess,
|
|
78
81
|
)
|
|
82
|
+
from griptape_nodes.retained_mode.events.object_events import ClearAllObjectStateRequest
|
|
79
83
|
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
80
84
|
from griptape_nodes.retained_mode.managers.os_manager import OSManager
|
|
81
85
|
|
|
@@ -147,6 +151,7 @@ class LibraryManager:
|
|
|
147
151
|
event_manager.assign_manager_to_request_type(
|
|
148
152
|
UnloadLibraryFromRegistryRequest, self.unload_library_from_registry_request
|
|
149
153
|
)
|
|
154
|
+
event_manager.assign_manager_to_request_type(ReloadAllLibrariesRequest, self.reload_all_libraries_request)
|
|
150
155
|
|
|
151
156
|
event_manager.add_listener_to_app_event(
|
|
152
157
|
AppInitializationComplete,
|
|
@@ -462,35 +467,61 @@ class LibraryManager:
|
|
|
462
467
|
pip_install_flags = []
|
|
463
468
|
pip_dependencies = library_data.metadata.dependencies.pip_dependencies
|
|
464
469
|
|
|
465
|
-
#
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
str(
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
470
|
+
# Determine venv path for dependency installation
|
|
471
|
+
venv_path = self._get_library_venv_path(library_data.name, file_path)
|
|
472
|
+
|
|
473
|
+
# Only install dependencies if conditions are met
|
|
474
|
+
try:
|
|
475
|
+
library_venv_python_path = self._init_library_venv(venv_path)
|
|
476
|
+
except RuntimeError as e:
|
|
477
|
+
self._library_file_path_to_info[file_path] = LibraryManager.LibraryInfo(
|
|
478
|
+
library_path=file_path,
|
|
479
|
+
library_name=library_data.name,
|
|
480
|
+
library_version=library_version,
|
|
481
|
+
status=LibraryManager.LibraryStatus.UNUSABLE,
|
|
482
|
+
problems=[str(e)],
|
|
483
|
+
)
|
|
484
|
+
details = f"Attempted to load Library JSON file from '{json_path}'. Failed when creating the virtual environment: {e}."
|
|
485
|
+
logger.error(details)
|
|
486
|
+
return RegisterLibraryFromFileResultFailure()
|
|
487
|
+
if self._can_write_to_venv_location(library_venv_python_path):
|
|
488
|
+
# Grab the python executable from the virtual environment so that we can pip install there
|
|
489
|
+
logger.info(
|
|
490
|
+
"Installing dependencies for library '%s' with pip in venv at %s", library_data.name, venv_path
|
|
491
|
+
)
|
|
492
|
+
subprocess.run( # noqa: S603
|
|
493
|
+
[
|
|
494
|
+
sys.executable,
|
|
495
|
+
"-m",
|
|
496
|
+
"uv",
|
|
497
|
+
"pip",
|
|
498
|
+
"install",
|
|
499
|
+
*pip_dependencies,
|
|
500
|
+
*pip_install_flags,
|
|
501
|
+
"--python",
|
|
502
|
+
str(library_venv_python_path),
|
|
503
|
+
],
|
|
504
|
+
check=True,
|
|
505
|
+
capture_output=True,
|
|
506
|
+
text=True,
|
|
507
|
+
)
|
|
508
|
+
else:
|
|
509
|
+
logger.debug(
|
|
510
|
+
"Skipping dependency installation for library '%s' - venv location at %s is not writable",
|
|
511
|
+
library_data.name,
|
|
512
|
+
venv_path,
|
|
513
|
+
)
|
|
482
514
|
except subprocess.CalledProcessError as e:
|
|
483
515
|
# Failed to create the library
|
|
516
|
+
error_details = f"return code={e.returncode}, stdout={e.stdout}, stderr={e.stderr}"
|
|
484
517
|
self._library_file_path_to_info[file_path] = LibraryManager.LibraryInfo(
|
|
485
518
|
library_path=file_path,
|
|
486
519
|
library_name=library_data.name,
|
|
487
520
|
library_version=library_version,
|
|
488
521
|
status=LibraryManager.LibraryStatus.UNUSABLE,
|
|
489
|
-
problems=[f"
|
|
490
|
-
)
|
|
491
|
-
details = (
|
|
492
|
-
f"Attempted to load Library JSON file from '{json_path}'. Failed when installing dependencies: {e}."
|
|
522
|
+
problems=[f"Dependency installation failed: {error_details}"],
|
|
493
523
|
)
|
|
524
|
+
details = f"Attempted to load Library JSON file from '{json_path}'. Failed when installing dependencies: {error_details}"
|
|
494
525
|
logger.error(details)
|
|
495
526
|
return RegisterLibraryFromFileResultFailure()
|
|
496
527
|
|
|
@@ -564,21 +595,39 @@ class LibraryManager:
|
|
|
564
595
|
) -> ResultPayload:
|
|
565
596
|
package_name = Requirement(request.requirement_specifier).name
|
|
566
597
|
try:
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
598
|
+
# Determine venv path for dependency installation
|
|
599
|
+
venv_path = self._get_library_venv_path(package_name, None)
|
|
600
|
+
|
|
601
|
+
# Only install dependencies if conditions are met
|
|
602
|
+
try:
|
|
603
|
+
library_python_venv_path = self._init_library_venv(venv_path)
|
|
604
|
+
except RuntimeError as e:
|
|
605
|
+
details = f"Attempted to install library '{request.requirement_specifier}'. Failed when creating the virtual environment: {e}"
|
|
606
|
+
logger.error(details)
|
|
607
|
+
return RegisterLibraryFromRequirementSpecifierResultFailure()
|
|
608
|
+
if self._can_write_to_venv_location(library_python_venv_path):
|
|
609
|
+
logger.info("Installing dependency '%s' with pip in venv at %s", package_name, venv_path)
|
|
610
|
+
subprocess.run( # noqa: S603
|
|
611
|
+
[
|
|
612
|
+
uv.find_uv_bin(),
|
|
613
|
+
"pip",
|
|
614
|
+
"install",
|
|
615
|
+
request.requirement_specifier,
|
|
616
|
+
"--python",
|
|
617
|
+
library_python_venv_path,
|
|
618
|
+
],
|
|
619
|
+
check=True,
|
|
620
|
+
capture_output=True,
|
|
621
|
+
text=True,
|
|
622
|
+
)
|
|
623
|
+
else:
|
|
624
|
+
logger.debug(
|
|
625
|
+
"Skipping dependency installation for package '%s' - venv location at %s is not writable",
|
|
626
|
+
package_name,
|
|
627
|
+
venv_path,
|
|
628
|
+
)
|
|
580
629
|
except subprocess.CalledProcessError as e:
|
|
581
|
-
details = f"Attempted to install library '{request.requirement_specifier}'. Failed
|
|
630
|
+
details = f"Attempted to install library '{request.requirement_specifier}'. Failed: return code={e.returncode}, stdout={e.stdout}, stderr={e.stderr}"
|
|
582
631
|
logger.error(details)
|
|
583
632
|
return RegisterLibraryFromRequirementSpecifierResultFailure()
|
|
584
633
|
|
|
@@ -592,20 +641,37 @@ class LibraryManager:
|
|
|
592
641
|
|
|
593
642
|
return RegisterLibraryFromRequirementSpecifierResultSuccess(library_name=request.requirement_specifier)
|
|
594
643
|
|
|
595
|
-
def
|
|
644
|
+
def _init_library_venv(self, library_venv_path: Path) -> Path:
|
|
645
|
+
"""Initialize a virtual environment for the library.
|
|
646
|
+
|
|
647
|
+
If the virtual environment already exists, it will not be recreated.
|
|
648
|
+
|
|
649
|
+
Args:
|
|
650
|
+
library_venv_path: Path to the virtual environment directory
|
|
651
|
+
|
|
652
|
+
Returns:
|
|
653
|
+
Path to the Python executable in the virtual environment
|
|
654
|
+
|
|
655
|
+
Raises:
|
|
656
|
+
RuntimeError: If the virtual environment cannot be created.
|
|
657
|
+
"""
|
|
596
658
|
# Create a virtual environment for the library
|
|
597
659
|
python_version = platform.python_version()
|
|
598
|
-
|
|
599
|
-
xdg_data_home() / "griptape_nodes" / "venvs" / python_version / library_name.replace(" ", "_").strip()
|
|
600
|
-
)
|
|
660
|
+
|
|
601
661
|
if library_venv_path.exists():
|
|
602
662
|
logger.debug("Virtual environment already exists at %s", library_venv_path)
|
|
603
663
|
else:
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
664
|
+
try:
|
|
665
|
+
logger.info("Creating virtual environment at %s with Python %s", library_venv_path, python_version)
|
|
666
|
+
subprocess.run( # noqa: S603
|
|
667
|
+
[sys.executable, "-m", "uv", "venv", str(library_venv_path), "--python", python_version],
|
|
668
|
+
check=True,
|
|
669
|
+
capture_output=True,
|
|
670
|
+
text=True,
|
|
671
|
+
)
|
|
672
|
+
except subprocess.CalledProcessError as e:
|
|
673
|
+
msg = f"Failed to create virtual environment at {library_venv_path} with Python {python_version}: return code={e.returncode}, stdout={e.stdout}, stderr={e.stderr}"
|
|
674
|
+
raise RuntimeError(msg) from e
|
|
609
675
|
logger.debug("Created virtual environment at %s", library_venv_path)
|
|
610
676
|
|
|
611
677
|
# Grab the python executable from the virtual environment so that we can pip install there
|
|
@@ -627,6 +693,57 @@ class LibraryManager:
|
|
|
627
693
|
|
|
628
694
|
return library_venv_python_path
|
|
629
695
|
|
|
696
|
+
def _get_library_venv_path(self, library_name: str, library_file_path: str | None = None) -> Path:
|
|
697
|
+
"""Get the path to the virtual environment directory for a library.
|
|
698
|
+
|
|
699
|
+
Args:
|
|
700
|
+
library_name: Name of the library
|
|
701
|
+
library_file_path: Optional path to the library JSON file
|
|
702
|
+
|
|
703
|
+
Returns:
|
|
704
|
+
Path to the virtual environment directory
|
|
705
|
+
"""
|
|
706
|
+
clean_library_name = library_name.replace(" ", "_").strip()
|
|
707
|
+
|
|
708
|
+
if library_file_path is not None:
|
|
709
|
+
# Create venv relative to the library.json file
|
|
710
|
+
library_dir = Path(library_file_path).parent.absolute()
|
|
711
|
+
return library_dir / ".venv"
|
|
712
|
+
|
|
713
|
+
# Create venv relative to the xdg data home
|
|
714
|
+
return xdg_data_home() / "griptape_nodes" / "libraries" / clean_library_name / ".venv"
|
|
715
|
+
|
|
716
|
+
def _can_write_to_venv_location(self, venv_python_path: Path) -> bool:
|
|
717
|
+
"""Check if we can write to the venv location (either create it or modify existing).
|
|
718
|
+
|
|
719
|
+
Args:
|
|
720
|
+
venv_python_path: Path to the python executable in the virtual environment
|
|
721
|
+
|
|
722
|
+
Returns:
|
|
723
|
+
True if we can write to the location, False otherwise
|
|
724
|
+
"""
|
|
725
|
+
# On Windows, permission checks are hard. Assume we can write
|
|
726
|
+
if OSManager.is_windows():
|
|
727
|
+
return True
|
|
728
|
+
|
|
729
|
+
venv_path = venv_python_path.parent.parent
|
|
730
|
+
|
|
731
|
+
# If venv doesn't exist, check if parent directory is writable
|
|
732
|
+
if not venv_path.exists():
|
|
733
|
+
parent_dir = venv_path.parent
|
|
734
|
+
try:
|
|
735
|
+
return parent_dir.exists() and os.access(parent_dir, os.W_OK)
|
|
736
|
+
except (OSError, AttributeError) as e:
|
|
737
|
+
logger.debug("Could not check parent directory permissions for %s: %s", parent_dir, e)
|
|
738
|
+
return False
|
|
739
|
+
|
|
740
|
+
# If venv exists, check if we can write to it
|
|
741
|
+
try:
|
|
742
|
+
return os.access(venv_path, os.W_OK)
|
|
743
|
+
except (OSError, AttributeError) as e:
|
|
744
|
+
logger.debug("Could not check venv write permissions for %s: %s", venv_path, e)
|
|
745
|
+
return False
|
|
746
|
+
|
|
630
747
|
def unload_library_from_registry_request(self, request: UnloadLibraryFromRegistryRequest) -> ResultPayload:
|
|
631
748
|
try:
|
|
632
749
|
LibraryRegistry.unregister_library(library_name=request.library_name)
|
|
@@ -915,7 +1032,7 @@ class LibraryManager:
|
|
|
915
1032
|
# Go tell the Workflow Manager that it's turn is now.
|
|
916
1033
|
GriptapeNodes.WorkflowManager().on_libraries_initialization_complete()
|
|
917
1034
|
|
|
918
|
-
def _attempt_load_nodes_from_library( # noqa: PLR0913
|
|
1035
|
+
def _attempt_load_nodes_from_library( # noqa: PLR0913, C901
|
|
919
1036
|
self,
|
|
920
1037
|
library_data: LibrarySchema,
|
|
921
1038
|
library: Library,
|
|
@@ -925,6 +1042,25 @@ class LibraryManager:
|
|
|
925
1042
|
problems: list[str],
|
|
926
1043
|
) -> LibraryManager.LibraryInfo:
|
|
927
1044
|
any_nodes_loaded_successfully = False
|
|
1045
|
+
|
|
1046
|
+
# Check for version-based compatibility issues and add to problems
|
|
1047
|
+
version_issues = GriptapeNodes.VersionCompatibilityManager().check_library_version_compatibility(library_data)
|
|
1048
|
+
has_disqualifying_issues = False
|
|
1049
|
+
for issue in version_issues:
|
|
1050
|
+
problems.append(issue.message)
|
|
1051
|
+
if issue.severity == LibraryManager.LibraryStatus.UNUSABLE:
|
|
1052
|
+
has_disqualifying_issues = True
|
|
1053
|
+
|
|
1054
|
+
# Early exit if any version issues are disqualifying
|
|
1055
|
+
if has_disqualifying_issues:
|
|
1056
|
+
return LibraryManager.LibraryInfo(
|
|
1057
|
+
library_path=library_file_path,
|
|
1058
|
+
library_name=library_data.name,
|
|
1059
|
+
library_version=library_version,
|
|
1060
|
+
status=LibraryManager.LibraryStatus.UNUSABLE,
|
|
1061
|
+
problems=problems,
|
|
1062
|
+
)
|
|
1063
|
+
|
|
928
1064
|
# Process each node in the metadata
|
|
929
1065
|
for node_definition in library_data.nodes:
|
|
930
1066
|
# Resolve relative path to absolute path
|
|
@@ -1142,3 +1278,37 @@ class LibraryManager:
|
|
|
1142
1278
|
library for library in libraries_to_register_category if library.lower() not in paths_to_remove
|
|
1143
1279
|
]
|
|
1144
1280
|
config_mgr.set_config_value(config_category, libraries_to_register_category)
|
|
1281
|
+
|
|
1282
|
+
def reload_all_libraries_request(self, request: ReloadAllLibrariesRequest) -> ResultPayload: # noqa: ARG002
|
|
1283
|
+
# Start with a clean slate.
|
|
1284
|
+
clear_all_request = ClearAllObjectStateRequest(i_know_what_im_doing=True)
|
|
1285
|
+
clear_all_result = GriptapeNodes.handle_request(clear_all_request)
|
|
1286
|
+
if not clear_all_result.succeeded():
|
|
1287
|
+
details = "Failed to clear the existing object state when preparing to reload all libraries."
|
|
1288
|
+
logger.error(details)
|
|
1289
|
+
return ReloadAllLibrariesResultFailure()
|
|
1290
|
+
|
|
1291
|
+
# Unload all libraries now.
|
|
1292
|
+
all_libraries_request = ListRegisteredLibrariesRequest()
|
|
1293
|
+
all_libraries_result = GriptapeNodes.handle_request(all_libraries_request)
|
|
1294
|
+
if not isinstance(all_libraries_result, ListRegisteredLibrariesResultSuccess):
|
|
1295
|
+
details = "When preparing to reload all libraries, failed to get registered libraries."
|
|
1296
|
+
logger.error(details)
|
|
1297
|
+
return ReloadAllLibrariesResultFailure()
|
|
1298
|
+
|
|
1299
|
+
for library_name in all_libraries_result.libraries:
|
|
1300
|
+
unload_library_request = UnloadLibraryFromRegistryRequest(library_name=library_name)
|
|
1301
|
+
unload_library_result = GriptapeNodes.handle_request(unload_library_request)
|
|
1302
|
+
if not unload_library_result.succeeded():
|
|
1303
|
+
details = f"When preparing to reload all libraries, failed to unload library '{library_name}'."
|
|
1304
|
+
logger.error(details)
|
|
1305
|
+
return ReloadAllLibrariesResultFailure()
|
|
1306
|
+
|
|
1307
|
+
# Load (or reload, which should trigger a hot reload) all libraries
|
|
1308
|
+
self.load_all_libraries_from_config()
|
|
1309
|
+
|
|
1310
|
+
details = (
|
|
1311
|
+
"Successfully reloaded all libraries. All object state was cleared and previous libraries were unloaded."
|
|
1312
|
+
)
|
|
1313
|
+
logger.info(details)
|
|
1314
|
+
return ReloadAllLibrariesResultSuccess()
|