griptape-nodes 0.65.6__py3-none-any.whl → 0.66.1__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/common/node_executor.py +352 -27
- griptape_nodes/drivers/storage/base_storage_driver.py +12 -3
- griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +18 -2
- griptape_nodes/drivers/storage/local_storage_driver.py +42 -5
- griptape_nodes/exe_types/base_iterative_nodes.py +0 -1
- griptape_nodes/exe_types/connections.py +42 -0
- griptape_nodes/exe_types/core_types.py +2 -2
- griptape_nodes/exe_types/node_groups/__init__.py +2 -1
- griptape_nodes/exe_types/node_groups/base_iterative_node_group.py +177 -0
- griptape_nodes/exe_types/node_groups/base_node_group.py +1 -0
- griptape_nodes/exe_types/node_groups/subflow_node_group.py +35 -2
- griptape_nodes/exe_types/param_types/parameter_audio.py +1 -1
- griptape_nodes/exe_types/param_types/parameter_bool.py +1 -1
- griptape_nodes/exe_types/param_types/parameter_button.py +1 -1
- griptape_nodes/exe_types/param_types/parameter_float.py +1 -1
- griptape_nodes/exe_types/param_types/parameter_image.py +1 -1
- griptape_nodes/exe_types/param_types/parameter_int.py +1 -1
- griptape_nodes/exe_types/param_types/parameter_number.py +1 -1
- griptape_nodes/exe_types/param_types/parameter_string.py +1 -1
- griptape_nodes/exe_types/param_types/parameter_three_d.py +1 -1
- griptape_nodes/exe_types/param_types/parameter_video.py +1 -1
- griptape_nodes/machines/control_flow.py +5 -4
- griptape_nodes/machines/dag_builder.py +121 -55
- griptape_nodes/machines/fsm.py +10 -0
- griptape_nodes/machines/parallel_resolution.py +39 -38
- griptape_nodes/machines/sequential_resolution.py +29 -3
- griptape_nodes/node_library/library_registry.py +41 -2
- griptape_nodes/retained_mode/events/library_events.py +147 -8
- griptape_nodes/retained_mode/events/os_events.py +12 -4
- griptape_nodes/retained_mode/managers/fitness_problems/libraries/__init__.py +2 -0
- griptape_nodes/retained_mode/managers/fitness_problems/libraries/incompatible_requirements_problem.py +34 -0
- griptape_nodes/retained_mode/managers/flow_manager.py +133 -20
- griptape_nodes/retained_mode/managers/library_manager.py +1324 -564
- griptape_nodes/retained_mode/managers/node_manager.py +9 -3
- griptape_nodes/retained_mode/managers/os_manager.py +429 -65
- griptape_nodes/retained_mode/managers/resource_types/compute_resource.py +82 -0
- griptape_nodes/retained_mode/managers/resource_types/os_resource.py +17 -0
- griptape_nodes/retained_mode/managers/static_files_manager.py +21 -8
- griptape_nodes/retained_mode/managers/version_compatibility_manager.py +3 -3
- griptape_nodes/version_compatibility/versions/v0_39_0/modified_parameters_set_removal.py +5 -5
- griptape_nodes/version_compatibility/versions/v0_65_4/__init__.py +5 -0
- griptape_nodes/version_compatibility/versions/v0_65_4/run_in_parallel_to_run_in_order.py +79 -0
- griptape_nodes/version_compatibility/versions/v0_65_5/__init__.py +5 -0
- griptape_nodes/version_compatibility/versions/v0_65_5/flux_2_removed_parameters.py +85 -0
- {griptape_nodes-0.65.6.dist-info → griptape_nodes-0.66.1.dist-info}/METADATA +1 -1
- {griptape_nodes-0.65.6.dist-info → griptape_nodes-0.66.1.dist-info}/RECORD +48 -53
- griptape_nodes/retained_mode/managers/library_lifecycle/__init__.py +0 -45
- griptape_nodes/retained_mode/managers/library_lifecycle/data_models.py +0 -191
- griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +0 -346
- griptape_nodes/retained_mode/managers/library_lifecycle/library_fsm.py +0 -439
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/__init__.py +0 -17
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/base.py +0 -82
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/github.py +0 -116
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/local_file.py +0 -367
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/package.py +0 -104
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/sandbox.py +0 -155
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance.py +0 -18
- griptape_nodes/retained_mode/managers/library_lifecycle/library_status.py +0 -12
- {griptape_nodes-0.65.6.dist-info → griptape_nodes-0.66.1.dist-info}/WHEEL +0 -0
- {griptape_nodes-0.65.6.dist-info → griptape_nodes-0.66.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,439 +0,0 @@
|
|
|
1
|
-
"""Library lifecycle finite state machine implementation."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import logging
|
|
6
|
-
from dataclasses import dataclass
|
|
7
|
-
from typing import TYPE_CHECKING
|
|
8
|
-
|
|
9
|
-
from griptape_nodes.machines.fsm import FSM, State
|
|
10
|
-
from griptape_nodes.retained_mode.managers.library_lifecycle.data_models import (
|
|
11
|
-
EvaluationResult,
|
|
12
|
-
InspectionResult,
|
|
13
|
-
InstallationResult,
|
|
14
|
-
LibraryLoadedResult,
|
|
15
|
-
LifecycleIssue,
|
|
16
|
-
)
|
|
17
|
-
from griptape_nodes.retained_mode.managers.library_lifecycle.library_status import LibraryStatus
|
|
18
|
-
|
|
19
|
-
if TYPE_CHECKING:
|
|
20
|
-
from griptape_nodes.node_library.library_registry import LibrarySchema
|
|
21
|
-
from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance import LibraryProvenance
|
|
22
|
-
|
|
23
|
-
StateType = type[State]
|
|
24
|
-
|
|
25
|
-
logger = logging.getLogger("griptape_nodes")
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class InvalidStateTransitionError(Exception):
|
|
29
|
-
"""Raised when an invalid state transition is attempted."""
|
|
30
|
-
|
|
31
|
-
def __init__(self, current_state: StateType | None, requested_state: StateType, reason: str):
|
|
32
|
-
self.current_state = current_state
|
|
33
|
-
self.requested_state = requested_state
|
|
34
|
-
self.reason = reason
|
|
35
|
-
current_name = current_state.__name__ if current_state else "None"
|
|
36
|
-
super().__init__(f"Cannot transition from {current_name} to {requested_state.__name__}: {reason}")
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@dataclass
|
|
40
|
-
class LibraryLifecycleContext:
|
|
41
|
-
"""Context object for library lifecycle state machine."""
|
|
42
|
-
|
|
43
|
-
# Core identity
|
|
44
|
-
provenance: LibraryProvenance
|
|
45
|
-
|
|
46
|
-
# State machine data populated as we progress
|
|
47
|
-
inspection_result: InspectionResult | None = None
|
|
48
|
-
evaluation_result: EvaluationResult | None = None
|
|
49
|
-
installation_result: InstallationResult | None = None
|
|
50
|
-
library_loaded_result: LibraryLoadedResult | None = None
|
|
51
|
-
|
|
52
|
-
def get_library_schema(self) -> LibrarySchema | None:
|
|
53
|
-
"""Get the library schema from inspection result."""
|
|
54
|
-
return self.inspection_result.schema if self.inspection_result else None
|
|
55
|
-
|
|
56
|
-
def get_inspection_issues(self) -> list[LifecycleIssue]:
|
|
57
|
-
"""Get inspection issues from inspection result."""
|
|
58
|
-
return self.inspection_result.issues if self.inspection_result else []
|
|
59
|
-
|
|
60
|
-
def get_evaluation_issues(self) -> list[LifecycleIssue]:
|
|
61
|
-
"""Get evaluation issues from evaluation result."""
|
|
62
|
-
return self.evaluation_result.issues if self.evaluation_result else []
|
|
63
|
-
|
|
64
|
-
def get_installation_issues(self) -> list[LifecycleIssue]:
|
|
65
|
-
"""Get installation issues from installation result."""
|
|
66
|
-
return self.installation_result.issues if self.installation_result else []
|
|
67
|
-
|
|
68
|
-
def get_library_loaded_issues(self) -> list[LifecycleIssue]:
|
|
69
|
-
"""Get library loaded issues from library loaded result."""
|
|
70
|
-
return self.library_loaded_result.issues if self.library_loaded_result else []
|
|
71
|
-
|
|
72
|
-
def get_effective_active_state(self) -> bool:
|
|
73
|
-
"""Get the effective active state for this library."""
|
|
74
|
-
# TODO: Implement proper integration with LibraryPreferences from data_models (https://github.com/griptape-ai/griptape-nodes/issues/1234)
|
|
75
|
-
# For now, return True as a fallback until the proper integration is implemented
|
|
76
|
-
# This method should:
|
|
77
|
-
# 1. Get the runtime library preferences (not the settings preferences)
|
|
78
|
-
# 2. Check if library is active by name or provenance
|
|
79
|
-
# 3. Return appropriate active state
|
|
80
|
-
return True
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
# States for the Library Lifecycle FSM
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
class CandidateState(State):
|
|
87
|
-
"""Initial state where we have a library candidate ready for processing."""
|
|
88
|
-
|
|
89
|
-
@staticmethod
|
|
90
|
-
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
91
|
-
logger.info("Library %s is now a candidate for processing", context.provenance.get_display_name())
|
|
92
|
-
return None # Wait for explicit transition to InspectingState
|
|
93
|
-
|
|
94
|
-
@staticmethod
|
|
95
|
-
def get_allowed_transitions_for_context(context: LibraryLifecycleContext) -> set[StateType]: # noqa: ARG004
|
|
96
|
-
"""Get context-specific allowed transitions."""
|
|
97
|
-
return {InspectingState}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
class InspectingState(State):
|
|
101
|
-
"""State where we inspect the library to gather metadata."""
|
|
102
|
-
|
|
103
|
-
@staticmethod
|
|
104
|
-
def get_allowed_transitions_for_context(context: LibraryLifecycleContext) -> set[StateType]: # noqa: ARG004
|
|
105
|
-
"""Get context-specific allowed transitions."""
|
|
106
|
-
return {InspectedState}
|
|
107
|
-
|
|
108
|
-
@staticmethod
|
|
109
|
-
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
110
|
-
logger.info("Inspecting library %s", context.provenance.get_display_name())
|
|
111
|
-
|
|
112
|
-
# Store inspection result directly
|
|
113
|
-
context.inspection_result = context.provenance.inspect()
|
|
114
|
-
|
|
115
|
-
if context.inspection_result.schema:
|
|
116
|
-
# Library name is now accessible through the schema
|
|
117
|
-
library_name = context.inspection_result.schema.name
|
|
118
|
-
logger.info("Successfully loaded metadata for library %s", library_name)
|
|
119
|
-
else:
|
|
120
|
-
logger.warning("Failed to load metadata for library %s", context.provenance.get_display_name())
|
|
121
|
-
|
|
122
|
-
# Check if inspection result is disqualifying
|
|
123
|
-
if not context.inspection_result.is_usable():
|
|
124
|
-
logger.error(
|
|
125
|
-
"Library %s inspection failed with disqualifying issues: %s",
|
|
126
|
-
context.provenance.get_display_name(),
|
|
127
|
-
[issue.message for issue in context.inspection_result.issues],
|
|
128
|
-
)
|
|
129
|
-
# For disqualifying results, we still transition to InspectedState but it will have no allowed transitions
|
|
130
|
-
return InspectedState
|
|
131
|
-
|
|
132
|
-
# Auto-transition to InspectedState
|
|
133
|
-
return InspectedState
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
class InspectedState(State):
|
|
137
|
-
"""State where inspection is complete and we have metadata."""
|
|
138
|
-
|
|
139
|
-
@staticmethod
|
|
140
|
-
def get_allowed_transitions_for_context(context: LibraryLifecycleContext) -> set[StateType]:
|
|
141
|
-
"""Get context-specific allowed transitions."""
|
|
142
|
-
# If inspection result is unusable, block all transitions
|
|
143
|
-
if context.inspection_result and not context.inspection_result.is_usable():
|
|
144
|
-
return set()
|
|
145
|
-
return {EvaluatingState}
|
|
146
|
-
|
|
147
|
-
@staticmethod
|
|
148
|
-
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
149
|
-
if context.inspection_result and context.inspection_result.issues:
|
|
150
|
-
logger.warning(
|
|
151
|
-
"Library %s inspection completed with problems: %s",
|
|
152
|
-
context.provenance.get_display_name(),
|
|
153
|
-
[issue.message for issue in context.inspection_result.issues],
|
|
154
|
-
)
|
|
155
|
-
else:
|
|
156
|
-
logger.info("Library %s inspection completed successfully", context.provenance.get_display_name())
|
|
157
|
-
|
|
158
|
-
return None # Wait for explicit transition to EvaluatingState
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
class EvaluatingState(State):
|
|
162
|
-
"""State where we evaluate the library against current system state."""
|
|
163
|
-
|
|
164
|
-
@staticmethod
|
|
165
|
-
def get_allowed_transitions_for_context(context: LibraryLifecycleContext) -> set[StateType]: # noqa: ARG004
|
|
166
|
-
"""Get context-specific allowed transitions."""
|
|
167
|
-
return {EvaluatedState}
|
|
168
|
-
|
|
169
|
-
@staticmethod
|
|
170
|
-
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
171
|
-
logger.info("Evaluating library %s", context.provenance.get_display_name())
|
|
172
|
-
|
|
173
|
-
context.evaluation_result = context.provenance.evaluate(context)
|
|
174
|
-
|
|
175
|
-
# Auto-transition to EvaluatedState
|
|
176
|
-
return EvaluatedState
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
class EvaluatedState(State):
|
|
180
|
-
"""State where evaluation is complete."""
|
|
181
|
-
|
|
182
|
-
@staticmethod
|
|
183
|
-
def get_allowed_transitions_for_context(context: LibraryLifecycleContext) -> set[StateType]: # noqa: ARG004
|
|
184
|
-
"""Get context-specific allowed transitions."""
|
|
185
|
-
return {InstallingState}
|
|
186
|
-
|
|
187
|
-
@staticmethod
|
|
188
|
-
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
189
|
-
evaluation_issues = context.get_evaluation_issues()
|
|
190
|
-
if evaluation_issues:
|
|
191
|
-
logger.warning(
|
|
192
|
-
"Library %s evaluation completed with problems: %s",
|
|
193
|
-
context.provenance.get_display_name(),
|
|
194
|
-
[issue.message for issue in evaluation_issues],
|
|
195
|
-
)
|
|
196
|
-
else:
|
|
197
|
-
logger.info("Library %s evaluation completed successfully", context.provenance.get_display_name())
|
|
198
|
-
|
|
199
|
-
return None # Wait for explicit transition to InstallingState
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
class InstallingState(State):
|
|
203
|
-
"""State where we install the library and its dependencies."""
|
|
204
|
-
|
|
205
|
-
@staticmethod
|
|
206
|
-
def get_allowed_transitions_for_context(context: LibraryLifecycleContext) -> set[StateType]: # noqa: ARG004
|
|
207
|
-
"""Get context-specific allowed transitions."""
|
|
208
|
-
return {InstalledState}
|
|
209
|
-
|
|
210
|
-
@staticmethod
|
|
211
|
-
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
212
|
-
logger.info("Installing library %s", context.provenance.get_display_name())
|
|
213
|
-
|
|
214
|
-
# Check if user has disabled this library
|
|
215
|
-
if not context.get_effective_active_state():
|
|
216
|
-
issues = [LifecycleIssue(message="Library disabled by user", severity=LibraryStatus.FLAWED)]
|
|
217
|
-
context.installation_result = InstallationResult(installation_path="", venv_path="", issues=issues)
|
|
218
|
-
logger.info("Library %s installation skipped - disabled by user", context.provenance.get_display_name())
|
|
219
|
-
return InstalledState
|
|
220
|
-
|
|
221
|
-
# Perform installation using delegation
|
|
222
|
-
context.installation_result = await context.provenance.install(context)
|
|
223
|
-
|
|
224
|
-
# Auto-transition to InstalledState
|
|
225
|
-
return InstalledState
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
class InstalledState(State):
|
|
229
|
-
"""State where installation is complete."""
|
|
230
|
-
|
|
231
|
-
@staticmethod
|
|
232
|
-
def get_allowed_transitions_for_context(context: LibraryLifecycleContext) -> set[StateType]: # noqa: ARG004
|
|
233
|
-
"""Get context-specific allowed transitions."""
|
|
234
|
-
return {LoadingState}
|
|
235
|
-
|
|
236
|
-
@staticmethod
|
|
237
|
-
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
238
|
-
installation_issues = context.get_installation_issues()
|
|
239
|
-
if installation_issues:
|
|
240
|
-
logger.warning(
|
|
241
|
-
"Library %s installation completed with problems: %s",
|
|
242
|
-
context.provenance.get_display_name(),
|
|
243
|
-
[issue.message for issue in installation_issues],
|
|
244
|
-
)
|
|
245
|
-
else:
|
|
246
|
-
logger.info("Library %s installation completed successfully", context.provenance.get_display_name())
|
|
247
|
-
|
|
248
|
-
return None # Wait for explicit transition to LoadingState
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
class LoadingState(State):
|
|
252
|
-
"""State where we load the library into the registry."""
|
|
253
|
-
|
|
254
|
-
@staticmethod
|
|
255
|
-
def get_allowed_transitions_for_context(context: LibraryLifecycleContext) -> set[StateType]: # noqa: ARG004
|
|
256
|
-
"""Get context-specific allowed transitions."""
|
|
257
|
-
return {LoadedState}
|
|
258
|
-
|
|
259
|
-
@staticmethod
|
|
260
|
-
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
261
|
-
logger.info("Loading library %s", context.provenance.get_display_name())
|
|
262
|
-
|
|
263
|
-
# Check if user has disabled this library
|
|
264
|
-
if not context.get_effective_active_state():
|
|
265
|
-
issues = [LifecycleIssue(message="Library disabled by user", severity=LibraryStatus.FLAWED)]
|
|
266
|
-
context.library_loaded_result = LibraryLoadedResult(issues=issues)
|
|
267
|
-
logger.info("Library %s loading skipped - disabled by user", context.provenance.get_display_name())
|
|
268
|
-
return LoadedState
|
|
269
|
-
|
|
270
|
-
# Load the library into the registry using delegation
|
|
271
|
-
schema = context.get_library_schema()
|
|
272
|
-
if schema is None:
|
|
273
|
-
issues = [LifecycleIssue(message="No schema available for loading", severity=LibraryStatus.UNUSABLE)]
|
|
274
|
-
context.library_loaded_result = LibraryLoadedResult(issues=issues)
|
|
275
|
-
logger.error("Cannot load library %s - no schema available", context.provenance.get_display_name())
|
|
276
|
-
return LoadedState
|
|
277
|
-
|
|
278
|
-
context.library_loaded_result = context.provenance.load_library(context)
|
|
279
|
-
|
|
280
|
-
logger.info("Successfully loaded library %s", context.provenance.get_display_name())
|
|
281
|
-
|
|
282
|
-
# Auto-transition to LoadedState
|
|
283
|
-
return LoadedState
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
class LoadedState(State):
|
|
287
|
-
"""Final state where the library is loaded and ready for use."""
|
|
288
|
-
|
|
289
|
-
@staticmethod
|
|
290
|
-
def get_allowed_transitions_for_context(context: LibraryLifecycleContext) -> set[StateType]: # noqa: ARG004
|
|
291
|
-
"""Get context-specific allowed transitions."""
|
|
292
|
-
return set() # Terminal state
|
|
293
|
-
|
|
294
|
-
@staticmethod
|
|
295
|
-
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
296
|
-
library_loaded_issues = context.get_library_loaded_issues()
|
|
297
|
-
if library_loaded_issues:
|
|
298
|
-
logger.warning(
|
|
299
|
-
"Library %s loading completed with problems: %s",
|
|
300
|
-
context.provenance.get_display_name(),
|
|
301
|
-
[issue.message for issue in library_loaded_issues],
|
|
302
|
-
)
|
|
303
|
-
else:
|
|
304
|
-
logger.info("Library %s is now loaded and ready for use", context.provenance.get_display_name())
|
|
305
|
-
|
|
306
|
-
return None # Terminal state
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
class LibraryLifecycleFSM(FSM[LibraryLifecycleContext]):
|
|
310
|
-
"""Finite state machine for managing library lifecycle."""
|
|
311
|
-
|
|
312
|
-
def __init__(self, provenance: LibraryProvenance) -> None:
|
|
313
|
-
context = LibraryLifecycleContext(provenance=provenance)
|
|
314
|
-
super().__init__(context)
|
|
315
|
-
|
|
316
|
-
async def start_lifecycle(self) -> None:
|
|
317
|
-
"""Start the library lifecycle from CandidateState."""
|
|
318
|
-
if self._current_state is not None:
|
|
319
|
-
raise InvalidStateTransitionError(self._current_state, CandidateState, "Lifecycle has already been started")
|
|
320
|
-
await self.start(CandidateState)
|
|
321
|
-
|
|
322
|
-
async def begin_inspection(self) -> None:
|
|
323
|
-
"""Explicitly transition from Candidate to Inspecting."""
|
|
324
|
-
self._validate_state_transition(InspectingState)
|
|
325
|
-
await self.transition_state(InspectingState)
|
|
326
|
-
|
|
327
|
-
async def begin_evaluation(self) -> None:
|
|
328
|
-
"""Explicitly transition from Inspected to Evaluating."""
|
|
329
|
-
self._validate_state_transition(EvaluatingState)
|
|
330
|
-
await self.transition_state(EvaluatingState)
|
|
331
|
-
|
|
332
|
-
async def begin_installation(self) -> None:
|
|
333
|
-
"""Explicitly transition from Evaluated to Installing."""
|
|
334
|
-
self._validate_state_transition(InstallingState)
|
|
335
|
-
await self.transition_state(InstallingState)
|
|
336
|
-
|
|
337
|
-
async def begin_loading(self) -> None:
|
|
338
|
-
"""Explicitly transition from Installed to Loading."""
|
|
339
|
-
self._validate_state_transition(LoadingState)
|
|
340
|
-
await self.transition_state(LoadingState)
|
|
341
|
-
|
|
342
|
-
def get_context(self) -> LibraryLifecycleContext:
|
|
343
|
-
"""Get the current context."""
|
|
344
|
-
return self._context
|
|
345
|
-
|
|
346
|
-
def is_loaded(self) -> bool:
|
|
347
|
-
"""Check if the library is in the loaded state."""
|
|
348
|
-
return self._current_state is LoadedState
|
|
349
|
-
|
|
350
|
-
def has_problems(self) -> bool:
|
|
351
|
-
"""Check if the library has any problems."""
|
|
352
|
-
context = self._context
|
|
353
|
-
return bool(
|
|
354
|
-
context.get_inspection_issues()
|
|
355
|
-
or context.get_evaluation_issues()
|
|
356
|
-
or context.get_installation_issues()
|
|
357
|
-
or context.get_library_loaded_issues()
|
|
358
|
-
)
|
|
359
|
-
|
|
360
|
-
def get_all_problems(self) -> list[str]:
|
|
361
|
-
"""Get all problems from all stages."""
|
|
362
|
-
context = self._context
|
|
363
|
-
all_problems = []
|
|
364
|
-
all_problems.extend([issue.message for issue in context.get_inspection_issues()])
|
|
365
|
-
all_problems.extend([issue.message for issue in context.get_evaluation_issues()])
|
|
366
|
-
all_problems.extend([issue.message for issue in context.get_installation_issues()])
|
|
367
|
-
all_problems.extend([issue.message for issue in context.get_library_loaded_issues()])
|
|
368
|
-
return all_problems
|
|
369
|
-
|
|
370
|
-
def _validate_state_transition(self, target_state: StateType) -> None:
|
|
371
|
-
"""Validate that we can transition to the target state using state-based rules.
|
|
372
|
-
|
|
373
|
-
Args:
|
|
374
|
-
target_state: The state we want to transition to
|
|
375
|
-
"""
|
|
376
|
-
if self._current_state is None:
|
|
377
|
-
raise InvalidStateTransitionError(
|
|
378
|
-
self._current_state, target_state, "No current state - lifecycle not started"
|
|
379
|
-
)
|
|
380
|
-
|
|
381
|
-
# Check if target state is allowed from current state
|
|
382
|
-
allowed_transitions = self._current_state.get_allowed_transitions_for_context(self._context) # type: ignore[attr-defined]
|
|
383
|
-
if target_state not in allowed_transitions:
|
|
384
|
-
allowed_names = [state.__name__ for state in allowed_transitions]
|
|
385
|
-
if not allowed_names:
|
|
386
|
-
raise InvalidStateTransitionError(
|
|
387
|
-
self._current_state,
|
|
388
|
-
target_state,
|
|
389
|
-
f"No transitions allowed from {self._current_state.__name__} due to disqualifying inspection issues",
|
|
390
|
-
)
|
|
391
|
-
raise InvalidStateTransitionError(
|
|
392
|
-
self._current_state,
|
|
393
|
-
target_state,
|
|
394
|
-
f"Cannot transition from {self._current_state.__name__} to {target_state.__name__}. "
|
|
395
|
-
f"Allowed transitions: {allowed_names}",
|
|
396
|
-
)
|
|
397
|
-
|
|
398
|
-
# Special validation for LoadingState - requires installation result
|
|
399
|
-
installation_result = self._context.installation_result
|
|
400
|
-
if target_state is LoadingState and not installation_result:
|
|
401
|
-
raise InvalidStateTransitionError(
|
|
402
|
-
self._current_state, target_state, "Installation result is required before loading"
|
|
403
|
-
)
|
|
404
|
-
|
|
405
|
-
def can_transition_to(self, target_state: StateType) -> bool:
|
|
406
|
-
"""Check if we can transition to the target state."""
|
|
407
|
-
try:
|
|
408
|
-
self._validate_state_transition(target_state)
|
|
409
|
-
except InvalidStateTransitionError:
|
|
410
|
-
return False
|
|
411
|
-
else:
|
|
412
|
-
return True
|
|
413
|
-
|
|
414
|
-
def can_begin_inspection(self) -> bool:
|
|
415
|
-
"""Check if we can begin inspection."""
|
|
416
|
-
return self.can_transition_to(InspectingState)
|
|
417
|
-
|
|
418
|
-
def can_begin_evaluation(self) -> bool:
|
|
419
|
-
"""Check if we can begin evaluation."""
|
|
420
|
-
return self.can_transition_to(EvaluatingState)
|
|
421
|
-
|
|
422
|
-
def can_begin_installation(self) -> bool:
|
|
423
|
-
"""Check if we can begin installation."""
|
|
424
|
-
return self.can_transition_to(InstallingState)
|
|
425
|
-
|
|
426
|
-
def can_begin_loading(self) -> bool:
|
|
427
|
-
"""Check if we can begin loading."""
|
|
428
|
-
return self.can_transition_to(LoadingState)
|
|
429
|
-
|
|
430
|
-
def get_current_state_name(self) -> str:
|
|
431
|
-
"""Get the name of the current state."""
|
|
432
|
-
return self._current_state.__name__ if self._current_state else "None"
|
|
433
|
-
|
|
434
|
-
def get_allowed_transitions(self) -> set[StateType]:
|
|
435
|
-
"""Get the set of states that can be transitioned to from the current state."""
|
|
436
|
-
if self._current_state is None:
|
|
437
|
-
return set()
|
|
438
|
-
|
|
439
|
-
return self._current_state.get_allowed_transitions_for_context(self._context) # type: ignore[attr-defined]
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"""Library provenance implementations."""
|
|
2
|
-
|
|
3
|
-
from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance.base import LibraryProvenance
|
|
4
|
-
from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance.github import LibraryProvenanceGitHub
|
|
5
|
-
from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance.local_file import (
|
|
6
|
-
LibraryProvenanceLocalFile,
|
|
7
|
-
)
|
|
8
|
-
from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance.package import LibraryProvenancePackage
|
|
9
|
-
from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance.sandbox import LibraryProvenanceSandbox
|
|
10
|
-
|
|
11
|
-
__all__ = [
|
|
12
|
-
"LibraryProvenance",
|
|
13
|
-
"LibraryProvenanceGitHub",
|
|
14
|
-
"LibraryProvenanceLocalFile",
|
|
15
|
-
"LibraryProvenancePackage",
|
|
16
|
-
"LibraryProvenanceSandbox",
|
|
17
|
-
]
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
"""Abstract base class for library provenance."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from abc import ABC, abstractmethod
|
|
6
|
-
from dataclasses import dataclass
|
|
7
|
-
from typing import TYPE_CHECKING
|
|
8
|
-
|
|
9
|
-
from griptape_nodes.retained_mode.managers.library_lifecycle.data_models import (
|
|
10
|
-
EvaluationResult,
|
|
11
|
-
InspectionResult,
|
|
12
|
-
InstallationResult,
|
|
13
|
-
LibraryEntry,
|
|
14
|
-
LibraryLoadedResult,
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
if TYPE_CHECKING:
|
|
18
|
-
from griptape_nodes.retained_mode.managers.library_lifecycle.library_fsm import LibraryLifecycleContext
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@dataclass(frozen=True)
|
|
22
|
-
class LibraryProvenance(ABC):
|
|
23
|
-
"""Pure reference to a library source."""
|
|
24
|
-
|
|
25
|
-
def get_display_name(self) -> str:
|
|
26
|
-
"""Get a human-readable name for this provenance."""
|
|
27
|
-
return f"Unknown provenance: {type(self).__name__}"
|
|
28
|
-
|
|
29
|
-
def create_library_entry(self, *, active: bool = True) -> LibraryEntry:
|
|
30
|
-
"""Create a library entry for this provenance."""
|
|
31
|
-
|
|
32
|
-
# Create a basic library entry that includes this provenance
|
|
33
|
-
class BasicLibraryEntry(LibraryEntry):
|
|
34
|
-
def __init__(self, provenance: LibraryProvenance, *, active: bool = True):
|
|
35
|
-
super().__init__(active=active)
|
|
36
|
-
self._provenance = provenance
|
|
37
|
-
|
|
38
|
-
def get_provenance(self) -> LibraryProvenance:
|
|
39
|
-
return self._provenance
|
|
40
|
-
|
|
41
|
-
return BasicLibraryEntry(self, active=active)
|
|
42
|
-
|
|
43
|
-
@abstractmethod
|
|
44
|
-
def inspect(self) -> InspectionResult:
|
|
45
|
-
"""Inspect this provenance to extract schema and identify issues.
|
|
46
|
-
|
|
47
|
-
Returns:
|
|
48
|
-
InspectionResult with schema and categorized issues
|
|
49
|
-
"""
|
|
50
|
-
|
|
51
|
-
@abstractmethod
|
|
52
|
-
def evaluate(self, context: LibraryLifecycleContext) -> EvaluationResult:
|
|
53
|
-
"""Evaluate this provenance for conflicts/issues.
|
|
54
|
-
|
|
55
|
-
Args:
|
|
56
|
-
context: Lifecycle context containing inspection results
|
|
57
|
-
|
|
58
|
-
Returns:
|
|
59
|
-
EvaluationResult with structured issues and severity levels
|
|
60
|
-
"""
|
|
61
|
-
|
|
62
|
-
@abstractmethod
|
|
63
|
-
async def install(self, context: LibraryLifecycleContext) -> InstallationResult:
|
|
64
|
-
"""Install this provenance.
|
|
65
|
-
|
|
66
|
-
Args:
|
|
67
|
-
context: Lifecycle context containing inspection results
|
|
68
|
-
|
|
69
|
-
Returns:
|
|
70
|
-
InstallationResult with structured issues and severity levels
|
|
71
|
-
"""
|
|
72
|
-
|
|
73
|
-
@abstractmethod
|
|
74
|
-
def load_library(self, context: LibraryLifecycleContext) -> LibraryLoadedResult:
|
|
75
|
-
"""Load this provenance into the registry.
|
|
76
|
-
|
|
77
|
-
Args:
|
|
78
|
-
context: Lifecycle context containing inspection results
|
|
79
|
-
|
|
80
|
-
Returns:
|
|
81
|
-
LibraryLoadedResult with structured issues and severity levels
|
|
82
|
-
"""
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
"""GitHub 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 pydantic import BaseModel, Field
|
|
10
|
-
from xdg_base_dirs import xdg_data_home
|
|
11
|
-
|
|
12
|
-
from griptape_nodes.retained_mode.managers.library_lifecycle.data_models import (
|
|
13
|
-
EvaluationResult,
|
|
14
|
-
InspectionResult,
|
|
15
|
-
InstallationResult,
|
|
16
|
-
LibraryLoadedResult,
|
|
17
|
-
LifecycleIssue,
|
|
18
|
-
)
|
|
19
|
-
from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance.base import LibraryProvenance
|
|
20
|
-
from griptape_nodes.retained_mode.managers.library_lifecycle.library_status import LibraryStatus
|
|
21
|
-
|
|
22
|
-
if TYPE_CHECKING:
|
|
23
|
-
from griptape_nodes.retained_mode.managers.library_lifecycle.library_fsm import LibraryLifecycleContext
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class LibraryPreferenceGitHub(BaseModel):
|
|
27
|
-
"""Serializable preference for a GitHub repository library."""
|
|
28
|
-
|
|
29
|
-
repository_url: str = Field(description="GitHub repository URL")
|
|
30
|
-
branch: str | None = Field(default=None, description="Branch or ref to use (defaults to main/master)")
|
|
31
|
-
active: bool = Field(default=True, description="Whether this GitHub library is active")
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
@dataclass(frozen=True)
|
|
35
|
-
class LibraryProvenanceGitHub(LibraryProvenance):
|
|
36
|
-
"""Reference to a GitHub repository library."""
|
|
37
|
-
|
|
38
|
-
repository_url: str
|
|
39
|
-
ref: str | None = None
|
|
40
|
-
|
|
41
|
-
def get_display_name(self) -> str:
|
|
42
|
-
"""Get a human-readable name for this provenance."""
|
|
43
|
-
ref_part = f"@{self.ref}" if self.ref else ""
|
|
44
|
-
return f"GitHub: {self.repository_url}{ref_part}"
|
|
45
|
-
|
|
46
|
-
def inspect(self) -> InspectionResult:
|
|
47
|
-
"""Inspect this GitHub repository to extract schema and identify issues."""
|
|
48
|
-
# TODO: Implement GitHub repository inspection (https://github.com/griptape-ai/griptape-nodes/issues/1234)
|
|
49
|
-
# This should:
|
|
50
|
-
# 1. Clone or fetch repository contents
|
|
51
|
-
# 2. Look for library schema files
|
|
52
|
-
# 3. Extract and validate library schema
|
|
53
|
-
|
|
54
|
-
return InspectionResult(
|
|
55
|
-
schema=None,
|
|
56
|
-
issues=[
|
|
57
|
-
LifecycleIssue(
|
|
58
|
-
message=f"GitHub inspection not yet implemented for {self.repository_url}",
|
|
59
|
-
severity=LibraryStatus.UNUSABLE,
|
|
60
|
-
)
|
|
61
|
-
],
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
def evaluate(self, context: LibraryLifecycleContext) -> EvaluationResult: # noqa: ARG002
|
|
65
|
-
"""Evaluate this GitHub repository for conflicts/issues."""
|
|
66
|
-
issues = []
|
|
67
|
-
issues.append(
|
|
68
|
-
LifecycleIssue(
|
|
69
|
-
message="GitHub evaluation not yet implemented",
|
|
70
|
-
severity=LibraryStatus.UNUSABLE,
|
|
71
|
-
)
|
|
72
|
-
)
|
|
73
|
-
return EvaluationResult(issues=issues)
|
|
74
|
-
|
|
75
|
-
async def install(self, context: LibraryLifecycleContext) -> InstallationResult: # noqa: ARG002
|
|
76
|
-
"""Install this GitHub repository library."""
|
|
77
|
-
issues = []
|
|
78
|
-
issues.append(
|
|
79
|
-
LifecycleIssue(
|
|
80
|
-
message="GitHub installation not yet implemented",
|
|
81
|
-
severity=LibraryStatus.UNUSABLE,
|
|
82
|
-
)
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
# TODO: Implement GitHub repository installation (https://github.com/griptape-ai/griptape-nodes/issues/1234)
|
|
86
|
-
# This should:
|
|
87
|
-
# 1. Clone repository to local directory
|
|
88
|
-
# 2. Create virtual environment
|
|
89
|
-
# 3. Install dependencies
|
|
90
|
-
# 4. Install repository in development mode
|
|
91
|
-
|
|
92
|
-
return InstallationResult(
|
|
93
|
-
installation_path="",
|
|
94
|
-
venv_path="",
|
|
95
|
-
issues=issues,
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
def load_library(self, context: LibraryLifecycleContext) -> LibraryLoadedResult: # noqa: ARG002
|
|
99
|
-
"""Load this GitHub repository library into the registry."""
|
|
100
|
-
issues = []
|
|
101
|
-
issues.append(
|
|
102
|
-
LifecycleIssue(
|
|
103
|
-
message="GitHub loading not yet implemented",
|
|
104
|
-
severity=LibraryStatus.UNUSABLE,
|
|
105
|
-
)
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
return LibraryLoadedResult(issues=issues)
|
|
109
|
-
|
|
110
|
-
def _get_base_venv_directory(self) -> str:
|
|
111
|
-
"""Get the base directory for virtual environments."""
|
|
112
|
-
return str(xdg_data_home() / "griptape_nodes" / "library_venvs")
|
|
113
|
-
|
|
114
|
-
def _ensure_venv_directory_exists(self, venv_dir: str) -> None:
|
|
115
|
-
"""Ensure the virtual environment directory exists."""
|
|
116
|
-
Path(venv_dir).mkdir(parents=True, exist_ok=True)
|