griptape-nodes 0.65.5__py3-none-any.whl → 0.66.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 (60) hide show
  1. griptape_nodes/common/node_executor.py +352 -27
  2. griptape_nodes/drivers/storage/base_storage_driver.py +12 -3
  3. griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +18 -2
  4. griptape_nodes/drivers/storage/local_storage_driver.py +42 -5
  5. griptape_nodes/exe_types/base_iterative_nodes.py +0 -1
  6. griptape_nodes/exe_types/connections.py +42 -0
  7. griptape_nodes/exe_types/core_types.py +2 -2
  8. griptape_nodes/exe_types/node_groups/__init__.py +2 -1
  9. griptape_nodes/exe_types/node_groups/base_iterative_node_group.py +177 -0
  10. griptape_nodes/exe_types/node_groups/base_node_group.py +1 -0
  11. griptape_nodes/exe_types/node_groups/subflow_node_group.py +35 -2
  12. griptape_nodes/exe_types/param_types/parameter_audio.py +1 -1
  13. griptape_nodes/exe_types/param_types/parameter_bool.py +1 -1
  14. griptape_nodes/exe_types/param_types/parameter_button.py +1 -1
  15. griptape_nodes/exe_types/param_types/parameter_float.py +1 -1
  16. griptape_nodes/exe_types/param_types/parameter_image.py +1 -1
  17. griptape_nodes/exe_types/param_types/parameter_int.py +1 -1
  18. griptape_nodes/exe_types/param_types/parameter_number.py +1 -1
  19. griptape_nodes/exe_types/param_types/parameter_string.py +1 -1
  20. griptape_nodes/exe_types/param_types/parameter_three_d.py +1 -1
  21. griptape_nodes/exe_types/param_types/parameter_video.py +1 -1
  22. griptape_nodes/machines/control_flow.py +5 -4
  23. griptape_nodes/machines/dag_builder.py +121 -55
  24. griptape_nodes/machines/fsm.py +10 -0
  25. griptape_nodes/machines/parallel_resolution.py +39 -38
  26. griptape_nodes/machines/sequential_resolution.py +29 -3
  27. griptape_nodes/node_library/library_registry.py +41 -2
  28. griptape_nodes/retained_mode/events/library_events.py +147 -8
  29. griptape_nodes/retained_mode/events/os_events.py +12 -4
  30. griptape_nodes/retained_mode/managers/fitness_problems/libraries/__init__.py +2 -0
  31. griptape_nodes/retained_mode/managers/fitness_problems/libraries/incompatible_requirements_problem.py +34 -0
  32. griptape_nodes/retained_mode/managers/flow_manager.py +133 -20
  33. griptape_nodes/retained_mode/managers/library_manager.py +1324 -564
  34. griptape_nodes/retained_mode/managers/node_manager.py +9 -3
  35. griptape_nodes/retained_mode/managers/os_manager.py +429 -65
  36. griptape_nodes/retained_mode/managers/resource_types/compute_resource.py +82 -0
  37. griptape_nodes/retained_mode/managers/resource_types/os_resource.py +17 -0
  38. griptape_nodes/retained_mode/managers/static_files_manager.py +21 -8
  39. griptape_nodes/retained_mode/managers/version_compatibility_manager.py +3 -3
  40. griptape_nodes/version_compatibility/versions/v0_39_0/modified_parameters_set_removal.py +5 -5
  41. griptape_nodes/version_compatibility/versions/v0_65_4/__init__.py +5 -0
  42. griptape_nodes/version_compatibility/versions/v0_65_4/run_in_parallel_to_run_in_order.py +79 -0
  43. griptape_nodes/version_compatibility/versions/v0_65_5/__init__.py +5 -0
  44. griptape_nodes/version_compatibility/versions/v0_65_5/flux_2_removed_parameters.py +85 -0
  45. {griptape_nodes-0.65.5.dist-info → griptape_nodes-0.66.0.dist-info}/METADATA +1 -1
  46. {griptape_nodes-0.65.5.dist-info → griptape_nodes-0.66.0.dist-info}/RECORD +48 -53
  47. griptape_nodes/retained_mode/managers/library_lifecycle/__init__.py +0 -45
  48. griptape_nodes/retained_mode/managers/library_lifecycle/data_models.py +0 -191
  49. griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +0 -346
  50. griptape_nodes/retained_mode/managers/library_lifecycle/library_fsm.py +0 -439
  51. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/__init__.py +0 -17
  52. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/base.py +0 -82
  53. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/github.py +0 -116
  54. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/local_file.py +0 -367
  55. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/package.py +0 -104
  56. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/sandbox.py +0 -155
  57. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance.py +0 -18
  58. griptape_nodes/retained_mode/managers/library_lifecycle/library_status.py +0 -12
  59. {griptape_nodes-0.65.5.dist-info → griptape_nodes-0.66.0.dist-info}/WHEEL +0 -0
  60. {griptape_nodes-0.65.5.dist-info → griptape_nodes-0.66.0.dist-info}/entry_points.txt +0 -0
@@ -1,367 +0,0 @@
1
- """Local file library provenance implementation."""
2
-
3
- from __future__ import annotations
4
-
5
- import json
6
- import logging
7
- import os
8
- import subprocess
9
- import sys
10
- from collections import defaultdict
11
- from dataclasses import dataclass
12
- from pathlib import Path
13
- from typing import TYPE_CHECKING
14
-
15
- from pydantic import BaseModel, Field, ValidationError
16
-
17
- from griptape_nodes.node_library.library_registry import LibraryRegistry, LibrarySchema
18
- from griptape_nodes.retained_mode.events.config_events import (
19
- GetConfigCategoryRequest,
20
- GetConfigCategoryResultSuccess,
21
- SetConfigCategoryRequest,
22
- SetConfigCategoryResultSuccess,
23
- )
24
- from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
25
- from griptape_nodes.retained_mode.managers.library_lifecycle.data_models import (
26
- EvaluationResult,
27
- InspectionResult,
28
- InstallationResult,
29
- LibraryLoadedResult,
30
- LifecycleIssue,
31
- )
32
- from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance.base import LibraryProvenance
33
- from griptape_nodes.retained_mode.managers.library_lifecycle.library_status import LibraryStatus
34
- from griptape_nodes.retained_mode.managers.os_manager import OSManager
35
- from griptape_nodes.utils.async_utils import subprocess_run
36
-
37
- if TYPE_CHECKING:
38
- from griptape_nodes.retained_mode.managers.library_lifecycle.library_fsm import LibraryLifecycleContext
39
-
40
- logger = logging.getLogger("griptape_nodes")
41
-
42
-
43
- class LibraryPreferenceLocalFile(BaseModel):
44
- """Serializable preference for a local file library."""
45
-
46
- file_path: str = Field(description="Path to the library file")
47
- active: bool = Field(default=True, description="Whether this local file library is active")
48
-
49
-
50
- @dataclass(frozen=True)
51
- class LibraryProvenanceLocalFile(LibraryProvenance):
52
- """Reference to a local file library."""
53
-
54
- file_path: str
55
-
56
- def get_display_name(self) -> str:
57
- """Get a human-readable name for this provenance."""
58
- return f"Local file: {self.file_path}"
59
-
60
- def inspect(self) -> InspectionResult:
61
- """Inspect this local file to extract schema and identify issues."""
62
- issues = []
63
-
64
- # File system validation
65
- if not self._validate_file_exists():
66
- issues.append(
67
- LifecycleIssue(
68
- message=f"Library file does not exist or is not readable: {self.file_path}",
69
- severity=LibraryStatus.UNUSABLE,
70
- )
71
- )
72
- return InspectionResult(schema=None, issues=issues)
73
-
74
- # Schema validation
75
- try:
76
- with Path(self.file_path).open(encoding="utf-8") as f:
77
- raw_data = json.load(f)
78
- except json.JSONDecodeError as e:
79
- issues.append(LifecycleIssue(message=f"Invalid JSON in library file: {e}", severity=LibraryStatus.UNUSABLE))
80
- return InspectionResult(schema=None, issues=issues)
81
- except Exception as e:
82
- issues.append(LifecycleIssue(message=f"Failed to read library file: {e}", severity=LibraryStatus.UNUSABLE))
83
- return InspectionResult(schema=None, issues=issues)
84
-
85
- # Validate library schema structure
86
- try:
87
- schema = LibrarySchema.model_validate(raw_data)
88
- except ValidationError as e:
89
- for error in e.errors():
90
- loc = " -> ".join(map(str, error["loc"]))
91
- msg = error["msg"]
92
- error_type = error["type"]
93
- problem = f"Error in section '{loc}': {error_type}, {msg}"
94
- issues.append(LifecycleIssue(message=problem, severity=LibraryStatus.UNUSABLE))
95
- return InspectionResult(schema=None, issues=issues)
96
-
97
- return InspectionResult(schema=schema, issues=issues)
98
-
99
- def evaluate(self, context: LibraryLifecycleContext) -> EvaluationResult:
100
- """Evaluate this local file for conflicts/issues."""
101
- issues = []
102
-
103
- # Get schema from context (guaranteed to be valid at this point)
104
- assert context.inspection_result is not None # noqa: S101
105
- schema = context.inspection_result.schema
106
- assert schema is not None # noqa: S101
107
-
108
- # Version compatibility validation
109
- version_issues = GriptapeNodes.VersionCompatibilityManager().check_library_version_compatibility(schema)
110
- for issue in version_issues:
111
- lifecycle_severity = LibraryStatus(issue.severity.value)
112
- # Collate the problem to get the display message
113
- problem_message = type(issue.problem).collate_problems_for_display([issue.problem])
114
- issues.append(LifecycleIssue(message=problem_message, severity=lifecycle_severity))
115
-
116
- # NOTE: Library name conflicts are checked at the manager level
117
- # across all evaluated libraries, not here
118
-
119
- return EvaluationResult(issues=issues)
120
-
121
- async def install(self, context: LibraryLifecycleContext) -> InstallationResult:
122
- """Install this local file library."""
123
- problems = []
124
- venv_path = ""
125
-
126
- # Get the LibraryManager instance to use its methods
127
- library_manager = GriptapeNodes.LibraryManager()
128
-
129
- # Get library schema from context (guaranteed to be valid at this point)
130
- assert context.inspection_result is not None # noqa: S101
131
- library_data = context.inspection_result.schema
132
- assert library_data is not None # noqa: S101
133
-
134
- # If no dependencies are specified, early out
135
- if not (
136
- library_data.metadata
137
- and library_data.metadata.dependencies
138
- and library_data.metadata.dependencies.pip_dependencies
139
- ):
140
- return InstallationResult(
141
- installation_path=self.file_path,
142
- venv_path="",
143
- issues=problems,
144
- )
145
-
146
- pip_install_flags = library_data.metadata.dependencies.pip_install_flags
147
- if pip_install_flags is None:
148
- pip_install_flags = []
149
- pip_dependencies = library_data.metadata.dependencies.pip_dependencies
150
-
151
- # Determine venv path for dependency installation
152
- venv_path = library_manager._get_library_venv_path(library_data.name, self.file_path)
153
-
154
- # Only install dependencies if conditions are met
155
- library_venv_python_path = None
156
- try:
157
- library_venv_python_path = await library_manager._init_library_venv(venv_path)
158
- except RuntimeError as e:
159
- problems.append(
160
- LifecycleIssue(
161
- message=str(e),
162
- severity=LibraryStatus.UNUSABLE,
163
- )
164
- )
165
- # Return early for blocking issues
166
- return InstallationResult(
167
- installation_path=self.file_path,
168
- venv_path=str(venv_path) if venv_path else "",
169
- issues=problems,
170
- )
171
-
172
- if library_venv_python_path and library_manager._can_write_to_venv_location(library_venv_python_path):
173
- # Check disk space before installing dependencies
174
- config_manager = GriptapeNodes.ConfigManager()
175
- min_space_gb = config_manager.get_config_value("minimum_disk_space_gb_libraries")
176
- if not OSManager.check_available_disk_space(Path(venv_path), min_space_gb):
177
- error_msg = OSManager.format_disk_space_error(Path(venv_path))
178
- problems.append(
179
- LifecycleIssue(
180
- message=f"Insufficient disk space for dependencies (requires {min_space_gb} GB): {error_msg}",
181
- severity=LibraryStatus.UNUSABLE,
182
- )
183
- )
184
- # Return early for blocking issues
185
- return InstallationResult(
186
- installation_path=self.file_path,
187
- venv_path=str(venv_path) if venv_path else "",
188
- issues=problems,
189
- )
190
-
191
- # Grab the python executable from the virtual environment so that we can pip install there
192
- logger.info("Installing dependencies for library '%s' with pip in venv at %s", library_data.name, venv_path)
193
- try:
194
- await subprocess_run(
195
- [
196
- sys.executable,
197
- "-m",
198
- "uv",
199
- "pip",
200
- "install",
201
- *pip_dependencies,
202
- *pip_install_flags,
203
- "--python",
204
- str(library_venv_python_path),
205
- ],
206
- check=True,
207
- capture_output=True,
208
- text=True,
209
- )
210
- except subprocess.CalledProcessError as e:
211
- error_details = f"return code={e.returncode}, stdout={e.stdout}, stderr={e.stderr}"
212
- problems.append(
213
- LifecycleIssue(
214
- message=f"Dependency installation failed: {error_details}",
215
- severity=LibraryStatus.FLAWED,
216
- )
217
- )
218
- elif library_venv_python_path:
219
- logger.debug(
220
- "Skipping dependency installation for library '%s' - venv location at %s is not writable",
221
- library_data.name,
222
- venv_path,
223
- )
224
-
225
- return InstallationResult(
226
- installation_path=self.file_path,
227
- venv_path=str(venv_path) if venv_path else "",
228
- issues=problems,
229
- )
230
-
231
- def load_library(self, context: LibraryLifecycleContext) -> LibraryLoadedResult: # noqa: C901
232
- """Load this local file library into the registry."""
233
- issues = []
234
-
235
- # Get the LibraryManager instance to use its methods
236
- library_manager = GriptapeNodes.LibraryManager()
237
-
238
- # Get library schema from context (guaranteed to be valid at this point)
239
- assert context.inspection_result is not None # noqa: S101
240
- library_data = context.inspection_result.schema
241
- assert library_data is not None # noqa: S101
242
-
243
- # Use the file path from this provenance
244
- file_path = self.file_path
245
- base_dir = Path(file_path).parent
246
-
247
- # Load advanced library module if specified
248
- advanced_library_instance = None
249
- if library_data.advanced_library_path:
250
- try:
251
- advanced_library_instance = library_manager._load_advanced_library_module(
252
- library_data=library_data,
253
- base_dir=base_dir,
254
- )
255
- except Exception as err:
256
- issues.append(
257
- LifecycleIssue(
258
- message=f"Failed to load Advanced Library module from '{library_data.advanced_library_path}': {err}",
259
- severity=LibraryStatus.UNUSABLE,
260
- )
261
- )
262
- return LibraryLoadedResult(issues=issues)
263
-
264
- # Create or get the library
265
- library = None
266
- try:
267
- # Try to create a new library
268
- library = LibraryRegistry.generate_new_library(
269
- library_data=library_data,
270
- mark_as_default_library=False, # TODO(#1234): determine if this should be configurable
271
- advanced_library=advanced_library_instance,
272
- )
273
- except KeyError:
274
- # Library already exists
275
- issues.append(
276
- LifecycleIssue(
277
- message="Failed because a library with this name was already registered. Check the Settings to ensure duplicate libraries are not being loaded.",
278
- severity=LibraryStatus.UNUSABLE,
279
- )
280
- )
281
- return LibraryLoadedResult(issues=issues)
282
-
283
- # Handle library settings
284
- if library_data.settings is not None:
285
- # Assign them into the config space
286
- for library_data_setting in library_data.settings:
287
- # Does the category exist?
288
- get_category_request = GetConfigCategoryRequest(category=library_data_setting.category)
289
- get_category_result = GriptapeNodes.handle_request(get_category_request)
290
- if not isinstance(get_category_result, GetConfigCategoryResultSuccess):
291
- # That's OK, we'll invent it. Or at least we'll try.
292
- create_new_category_request = SetConfigCategoryRequest(
293
- category=library_data_setting.category, contents=library_data_setting.contents
294
- )
295
- create_new_category_result = GriptapeNodes.handle_request(create_new_category_request)
296
- if not isinstance(create_new_category_result, SetConfigCategoryResultSuccess):
297
- issues.append(
298
- LifecycleIssue(
299
- message=f"Failed to create new config category '{library_data_setting.category}'.",
300
- severity=LibraryStatus.FLAWED,
301
- )
302
- )
303
- continue # SKIP IT
304
- else:
305
- # We had an existing category. Union our changes into it (not replacing anything that matched).
306
- existing_category_contents = get_category_result.contents
307
- existing_category_contents.update(library_data_setting.contents)
308
- set_category_request = SetConfigCategoryRequest(
309
- category=library_data_setting.category, contents=existing_category_contents
310
- )
311
- set_category_result = GriptapeNodes.handle_request(set_category_request)
312
- if not isinstance(set_category_result, SetConfigCategoryResultSuccess):
313
- issues.append(
314
- LifecycleIssue(
315
- message=f"Failed to update config category '{library_data_setting.category}'.",
316
- severity=LibraryStatus.FLAWED,
317
- )
318
- )
319
- continue # SKIP IT
320
-
321
- # Get library version from schema metadata
322
- library_version = library_data.metadata.library_version
323
-
324
- # Add the directory to the Python path to allow for relative imports
325
- sys.path.insert(0, str(base_dir))
326
-
327
- # Attempt to load nodes from the library
328
- library_load_results = library_manager._attempt_load_nodes_from_library(
329
- library_data=library_data,
330
- library=library,
331
- base_dir=base_dir,
332
- library_file_path=file_path,
333
- library_version=library_version,
334
- problems=[], # We'll handle problems through issues instead
335
- )
336
-
337
- # Convert any problems from library_load_results to issues
338
- if library_load_results.problems:
339
- # Group problems by type and collate them for display
340
- problems_by_type = defaultdict(list)
341
- for problem in library_load_results.problems:
342
- problems_by_type[type(problem)].append(problem)
343
-
344
- # Collate each group
345
- collated_strings = []
346
- for problem_class, instances in problems_by_type.items():
347
- collated_display = problem_class.collate_problems_for_display(instances)
348
- collated_strings.append(collated_display)
349
-
350
- collated_problems = "\n".join(collated_strings)
351
- issues.append(
352
- LifecycleIssue(
353
- message=collated_problems,
354
- severity=library_load_results.status,
355
- )
356
- )
357
-
358
- return LibraryLoadedResult(issues=issues)
359
-
360
- def _validate_file_exists(self) -> bool:
361
- """Validate that the library file exists and is readable."""
362
- try:
363
- path = Path(self.file_path)
364
- return path.exists() and path.is_file() and os.access(path, os.R_OK)
365
- except Exception as e:
366
- logger.error("Failed to validate file %s: %s", self.file_path, e)
367
- return False
@@ -1,104 +0,0 @@
1
- """Package library provenance implementation."""
2
-
3
- from __future__ import annotations
4
-
5
- from dataclasses import dataclass
6
- from pathlib import Path
7
- from typing import TYPE_CHECKING
8
-
9
- from xdg_base_dirs import xdg_data_home
10
-
11
- from griptape_nodes.retained_mode.managers.library_lifecycle.data_models import (
12
- EvaluationResult,
13
- InspectionResult,
14
- InstallationResult,
15
- LibraryLoadedResult,
16
- LifecycleIssue,
17
- )
18
- from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance.base import LibraryProvenance
19
- from griptape_nodes.retained_mode.managers.library_lifecycle.library_status import LibraryStatus
20
-
21
- if TYPE_CHECKING:
22
- from griptape_nodes.retained_mode.managers.library_lifecycle.library_fsm import LibraryLifecycleContext
23
-
24
-
25
- @dataclass(frozen=True)
26
- class LibraryProvenancePackage(LibraryProvenance):
27
- """Reference to a package library."""
28
-
29
- requirement_specifier: str
30
-
31
- def get_display_name(self) -> str:
32
- """Get a human-readable name for this provenance."""
33
- return f"Package: {self.requirement_specifier}"
34
-
35
- def inspect(self) -> InspectionResult:
36
- """Inspect this package to extract schema and identify issues."""
37
- # TODO: Implement package inspection (https://github.com/griptape-ai/griptape-nodes/issues/1234)
38
- # This should:
39
- # 1. Check if package is available in PyPI or other repositories
40
- # 2. Download and inspect package metadata
41
- # 3. Extract library schema from package
42
-
43
- return InspectionResult(
44
- schema=None,
45
- issues=[
46
- LifecycleIssue(
47
- message=f"Package inspection not yet implemented for {self.requirement_specifier}",
48
- severity=LibraryStatus.UNUSABLE,
49
- )
50
- ],
51
- )
52
-
53
- def evaluate(self, context: LibraryLifecycleContext) -> EvaluationResult: # noqa: ARG002
54
- """Evaluate this package for conflicts/issues."""
55
- issues = []
56
- issues.append(
57
- LifecycleIssue(
58
- message="Package evaluation not yet implemented",
59
- severity=LibraryStatus.UNUSABLE,
60
- )
61
- )
62
- return EvaluationResult(issues=issues)
63
-
64
- async def install(self, context: LibraryLifecycleContext) -> InstallationResult: # noqa: ARG002
65
- """Install this package library."""
66
- issues = []
67
- issues.append(
68
- LifecycleIssue(
69
- message="Package installation not yet implemented",
70
- severity=LibraryStatus.UNUSABLE,
71
- )
72
- )
73
-
74
- # TODO: Implement package installation (https://github.com/griptape-ai/griptape-nodes/issues/1234)
75
- # This should:
76
- # 1. Create virtual environment
77
- # 2. Install package using pip
78
- # 3. Extract library files from installed package
79
-
80
- return InstallationResult(
81
- installation_path="",
82
- venv_path="",
83
- issues=issues,
84
- )
85
-
86
- def load_library(self, context: LibraryLifecycleContext) -> LibraryLoadedResult: # noqa: ARG002
87
- """Load this package library into the registry."""
88
- issues = []
89
- issues.append(
90
- LifecycleIssue(
91
- message="Package loading not yet implemented",
92
- severity=LibraryStatus.UNUSABLE,
93
- )
94
- )
95
-
96
- return LibraryLoadedResult(issues=issues)
97
-
98
- def _get_base_venv_directory(self) -> str:
99
- """Get the base directory for virtual environments."""
100
- return str(xdg_data_home() / "griptape_nodes" / "library_venvs")
101
-
102
- def _ensure_venv_directory_exists(self, venv_dir: str) -> None:
103
- """Ensure the virtual environment directory exists."""
104
- Path(venv_dir).mkdir(parents=True, exist_ok=True)
@@ -1,155 +0,0 @@
1
- """Sandbox library provenance implementation."""
2
-
3
- from __future__ import annotations
4
-
5
- import logging
6
- import os
7
- from dataclasses import dataclass
8
- from pathlib import Path
9
- from typing import TYPE_CHECKING
10
-
11
- from pydantic import BaseModel, Field
12
-
13
- from griptape_nodes.node_library.library_registry import LibraryMetadata, LibrarySchema
14
- from griptape_nodes.retained_mode.managers.library_lifecycle.data_models import (
15
- EvaluationResult,
16
- InspectionResult,
17
- InstallationResult,
18
- LibraryLoadedResult,
19
- LifecycleIssue,
20
- )
21
- from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance.base import LibraryProvenance
22
- from griptape_nodes.retained_mode.managers.library_lifecycle.library_status import LibraryStatus
23
-
24
- if TYPE_CHECKING:
25
- from griptape_nodes.retained_mode.managers.library_lifecycle.library_fsm import LibraryLifecycleContext
26
-
27
- logger = logging.getLogger("griptape_nodes")
28
-
29
-
30
- class LibraryPreferenceSandbox(BaseModel):
31
- """Serializable preference for a sandbox library directory."""
32
-
33
- directory_path: str = Field(description="Path to the sandbox library directory")
34
- active: bool = Field(default=True, description="Whether this sandbox library is active")
35
-
36
-
37
- @dataclass(frozen=True)
38
- class LibraryProvenanceSandbox(LibraryProvenance):
39
- """Reference to a sandbox library (dynamically assembled from node files)."""
40
-
41
- sandbox_path: str
42
-
43
- def get_display_name(self) -> str:
44
- """Get a human-readable name for this provenance."""
45
- return f"Sandbox: {self.sandbox_path}"
46
-
47
- def inspect(self) -> InspectionResult:
48
- """Inspect this sandbox to dynamically create schema from node files."""
49
- if not self._validate_sandbox_path():
50
- return InspectionResult(
51
- schema=None,
52
- issues=[
53
- LifecycleIssue(
54
- message=f"Sandbox directory does not exist or is not readable: {self.sandbox_path}",
55
- severity=LibraryStatus.UNUSABLE,
56
- )
57
- ],
58
- )
59
-
60
- # TODO: Implement dynamic library schema creation from node files (https://github.com/griptape-ai/griptape-nodes/issues/1234)
61
- # This should:
62
- # 1. Scan the sandbox directory for Python files
63
- # 2. Extract node class definitions and metadata
64
- # 3. Dynamically create LibrarySchema with discovered nodes
65
- # 4. Generate appropriate categories and metadata
66
-
67
- # For now, return a basic schema structure
68
- # This is a placeholder that should be replaced with actual node discovery
69
- sandbox_name = Path(self.sandbox_path).name
70
-
71
- # Create minimal metadata for sandbox
72
- metadata = LibraryMetadata(
73
- author="Sandbox Developer",
74
- description=f"Dynamically discovered sandbox library from {sandbox_name}",
75
- library_version="dev",
76
- engine_version="1.0.0",
77
- tags=["sandbox", "development"],
78
- )
79
-
80
- # Create basic schema - this should be replaced with actual node discovery
81
- schema = LibrarySchema(
82
- name=sandbox_name,
83
- library_schema_version="1.0.0",
84
- metadata=metadata,
85
- categories=[], # Should be populated from discovered nodes
86
- nodes=[], # Should be populated from discovered nodes
87
- )
88
-
89
- return InspectionResult(schema=schema, issues=[])
90
-
91
- def evaluate(self, context: LibraryLifecycleContext) -> EvaluationResult: # noqa: ARG002
92
- """Evaluate this sandbox for conflicts/issues."""
93
- issues = []
94
-
95
- # Check if sandbox is still accessible
96
- if not self._validate_sandbox_path():
97
- issues.append(
98
- LifecycleIssue(
99
- message=f"Sandbox directory is no longer accessible: {self.sandbox_path}",
100
- severity=LibraryStatus.UNUSABLE,
101
- )
102
- )
103
- return EvaluationResult(issues=issues)
104
-
105
- # TODO: Add sandbox-specific evaluation logic (https://github.com/griptape-ai/griptape-nodes/issues/1234)
106
- # This could include:
107
- # - Checking for naming conflicts with existing libraries
108
- # - Validating node implementations
109
- # - Checking for missing dependencies
110
-
111
- return EvaluationResult(issues=issues)
112
-
113
- async def install(self, context: LibraryLifecycleContext) -> InstallationResult: # noqa: ARG002
114
- """Install this sandbox library."""
115
- issues = []
116
-
117
- # Sandbox libraries don't need complex installation
118
- # They're loaded directly from the sandbox directory
119
- return InstallationResult(
120
- installation_path=self.sandbox_path,
121
- venv_path="",
122
- issues=issues,
123
- )
124
-
125
- def load_library(self, context: LibraryLifecycleContext) -> LibraryLoadedResult:
126
- """Load this sandbox library into the registry."""
127
- issues = []
128
-
129
- # Get library schema from context
130
- library_schema = context.inspection_result.schema if context.inspection_result else None
131
-
132
- if not library_schema or not library_schema.metadata:
133
- issues.append(
134
- LifecycleIssue(
135
- message="No metadata available for loading",
136
- severity=LibraryStatus.FLAWED,
137
- )
138
- )
139
-
140
- # TODO: Actually register the sandbox library with the LibraryRegistry (https://github.com/griptape-ai/griptape-nodes/issues/1234)
141
- # This would involve:
142
- # 1. Creating a Library instance from the dynamically discovered nodes
143
- # 2. Adding it to the LibraryRegistry
144
- # 3. Handling any registration conflicts or errors
145
-
146
- return LibraryLoadedResult(issues=issues)
147
-
148
- def _validate_sandbox_path(self) -> bool:
149
- """Validate that the sandbox path exists and is readable."""
150
- try:
151
- path = Path(self.sandbox_path)
152
- return path.exists() and path.is_dir() and os.access(path, os.R_OK)
153
- except Exception as e:
154
- logger.error("Failed to validate sandbox path %s: %s", self.sandbox_path, e)
155
- return False
@@ -1,18 +0,0 @@
1
- """Library provenance classes for tracking library sources."""
2
-
3
- # Re-export all provenance classes from their new location
4
- from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance.base import LibraryProvenance
5
- from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance.github import LibraryProvenanceGitHub
6
- from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance.local_file import (
7
- LibraryProvenanceLocalFile,
8
- )
9
- from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance.package import LibraryProvenancePackage
10
- from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance.sandbox import LibraryProvenanceSandbox
11
-
12
- __all__ = [
13
- "LibraryProvenance",
14
- "LibraryProvenanceGitHub",
15
- "LibraryProvenanceLocalFile",
16
- "LibraryProvenancePackage",
17
- "LibraryProvenanceSandbox",
18
- ]
@@ -1,12 +0,0 @@
1
- """Library status enumeration."""
2
-
3
- from enum import StrEnum
4
-
5
-
6
- class LibraryStatus(StrEnum):
7
- """Status of the library that was attempted to be loaded."""
8
-
9
- GOOD = "GOOD" # No errors detected during loading. Registered.
10
- FLAWED = "FLAWED" # Some errors detected, but recoverable. Registered.
11
- UNUSABLE = "UNUSABLE" # Errors detected and not recoverable. Not registered.
12
- MISSING = "MISSING" # File not found. Not registered.