griptape-nodes 0.61.0__py3-none-any.whl → 0.62.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/macro_parser/core.py +4 -4
- griptape_nodes/common/macro_parser/exceptions.py +3 -3
- griptape_nodes/common/macro_parser/resolution.py +2 -2
- griptape_nodes/common/project_templates/default_project_template.py +5 -10
- griptape_nodes/common/project_templates/directory.py +5 -5
- griptape_nodes/common/project_templates/loader.py +8 -7
- griptape_nodes/common/project_templates/project.py +1 -1
- griptape_nodes/common/project_templates/situation.py +5 -17
- griptape_nodes/common/project_templates/validation.py +3 -3
- griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +2 -2
- griptape_nodes/drivers/storage/local_storage_driver.py +2 -2
- griptape_nodes/node_library/workflow_registry.py +1 -1
- griptape_nodes/retained_mode/events/project_events.py +208 -93
- griptape_nodes/retained_mode/managers/event_manager.py +24 -9
- griptape_nodes/retained_mode/managers/library_manager.py +12 -21
- griptape_nodes/retained_mode/managers/os_manager.py +54 -6
- griptape_nodes/retained_mode/managers/project_manager.py +709 -259
- griptape_nodes/retained_mode/managers/static_files_manager.py +1 -5
- griptape_nodes/retained_mode/managers/sync_manager.py +4 -1
- griptape_nodes/retained_mode/managers/workflow_manager.py +2 -10
- griptape_nodes/traits/button.py +2 -1
- {griptape_nodes-0.61.0.dist-info → griptape_nodes-0.62.1.dist-info}/METADATA +1 -1
- {griptape_nodes-0.61.0.dist-info → griptape_nodes-0.62.1.dist-info}/RECORD +25 -26
- {griptape_nodes-0.61.0.dist-info → griptape_nodes-0.62.1.dist-info}/WHEEL +1 -1
- griptape_nodes/common/project_templates/defaults/project_template.yml +0 -89
- {griptape_nodes-0.61.0.dist-info → griptape_nodes-0.62.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
"""Events for project template management."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
from dataclasses import dataclass
|
|
4
6
|
from enum import StrEnum
|
|
5
|
-
from
|
|
6
|
-
from typing import Any
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
7
8
|
|
|
8
|
-
from griptape_nodes.common.macro_parser import MacroMatchFailure, MacroParseFailure, VariableInfo
|
|
9
|
-
from griptape_nodes.common.project_templates import ProjectTemplate, ProjectValidationInfo
|
|
10
9
|
from griptape_nodes.retained_mode.events.base_events import (
|
|
11
10
|
RequestPayload,
|
|
12
11
|
ResultPayloadFailure,
|
|
@@ -15,6 +14,13 @@ from griptape_nodes.retained_mode.events.base_events import (
|
|
|
15
14
|
)
|
|
16
15
|
from griptape_nodes.retained_mode.events.payload_registry import PayloadRegistry
|
|
17
16
|
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
from griptape_nodes.common.macro_parser import MacroMatchFailure, ParsedMacro, VariableInfo
|
|
21
|
+
from griptape_nodes.common.project_templates import ProjectTemplate, ProjectValidationInfo, SituationTemplate
|
|
22
|
+
from griptape_nodes.retained_mode.managers.project_manager import ProjectID, ProjectInfo
|
|
23
|
+
|
|
18
24
|
# Type alias for macro variable dictionaries (used by ParsedMacro)
|
|
19
25
|
MacroVariables = dict[str, str | int]
|
|
20
26
|
|
|
@@ -49,12 +55,12 @@ class LoadProjectTemplateResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuc
|
|
|
49
55
|
"""Project template loaded successfully.
|
|
50
56
|
|
|
51
57
|
Args:
|
|
52
|
-
|
|
58
|
+
project_id: The identifier for the loaded project
|
|
53
59
|
template: The merged ProjectTemplate (system defaults + user customizations)
|
|
54
60
|
validation: Validation info with status and any problems encountered
|
|
55
61
|
"""
|
|
56
62
|
|
|
57
|
-
|
|
63
|
+
project_id: ProjectID
|
|
58
64
|
template: ProjectTemplate
|
|
59
65
|
validation: ProjectValidationInfo
|
|
60
66
|
|
|
@@ -65,28 +71,26 @@ class LoadProjectTemplateResultFailure(WorkflowNotAlteredMixin, ResultPayloadFai
|
|
|
65
71
|
"""Project template loading failed.
|
|
66
72
|
|
|
67
73
|
Args:
|
|
68
|
-
project_path: Path to the project.yml that failed to load
|
|
69
74
|
validation: Validation info with error details
|
|
70
75
|
"""
|
|
71
76
|
|
|
72
|
-
project_path: Path
|
|
73
77
|
validation: ProjectValidationInfo
|
|
74
78
|
|
|
75
79
|
|
|
76
80
|
@dataclass
|
|
77
81
|
@PayloadRegistry.register
|
|
78
82
|
class GetProjectTemplateRequest(RequestPayload):
|
|
79
|
-
"""Get cached project template for a
|
|
83
|
+
"""Get cached project template for a project ID.
|
|
80
84
|
|
|
81
85
|
Use when: Querying current project configuration, checking validation status.
|
|
82
86
|
|
|
83
87
|
Args:
|
|
84
|
-
|
|
88
|
+
project_id: Identifier of the project
|
|
85
89
|
|
|
86
90
|
Results: GetProjectTemplateResultSuccess | GetProjectTemplateResultFailure
|
|
87
91
|
"""
|
|
88
92
|
|
|
89
|
-
|
|
93
|
+
project_id: ProjectID
|
|
90
94
|
|
|
91
95
|
|
|
92
96
|
@dataclass
|
|
@@ -109,40 +113,80 @@ class GetProjectTemplateResultFailure(WorkflowNotAlteredMixin, ResultPayloadFail
|
|
|
109
113
|
"""Project template retrieval failed (not loaded yet)."""
|
|
110
114
|
|
|
111
115
|
|
|
116
|
+
@dataclass
|
|
117
|
+
class ProjectTemplateInfo:
|
|
118
|
+
"""Information about a loaded or failed project template."""
|
|
119
|
+
|
|
120
|
+
project_id: ProjectID
|
|
121
|
+
validation: ProjectValidationInfo
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@dataclass
|
|
125
|
+
@PayloadRegistry.register
|
|
126
|
+
class ListProjectTemplatesRequest(RequestPayload):
|
|
127
|
+
"""List all project templates that have been loaded or attempted to load.
|
|
128
|
+
|
|
129
|
+
Use when: Displaying available projects, checking which projects are loaded.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
include_system_builtins: Whether to include system builtin templates like SYSTEM_DEFAULTS_KEY
|
|
133
|
+
|
|
134
|
+
Results: ListProjectTemplatesResultSuccess
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
include_system_builtins: bool = False
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@dataclass
|
|
141
|
+
@PayloadRegistry.register
|
|
142
|
+
class ListProjectTemplatesResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
143
|
+
"""List of all project templates retrieved.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
successfully_loaded: List of templates that loaded successfully
|
|
147
|
+
failed_to_load: List of templates that failed to load with validation errors
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
successfully_loaded: list[ProjectTemplateInfo]
|
|
151
|
+
failed_to_load: list[ProjectTemplateInfo]
|
|
152
|
+
|
|
153
|
+
|
|
112
154
|
@dataclass
|
|
113
155
|
@PayloadRegistry.register
|
|
114
|
-
class
|
|
115
|
-
"""Get the
|
|
156
|
+
class GetSituationRequest(RequestPayload):
|
|
157
|
+
"""Get the full situation template for a specific situation.
|
|
158
|
+
|
|
159
|
+
Returns the complete SituationTemplate including macro and policy.
|
|
116
160
|
|
|
117
|
-
Use when: Need
|
|
161
|
+
Use when: Need situation macro and/or policy for file operations.
|
|
162
|
+
Uses the current project for context.
|
|
118
163
|
|
|
119
164
|
Args:
|
|
120
|
-
project_path: Path to the project.yml to use
|
|
121
165
|
situation_name: Name of the situation template (e.g., "save_node_output")
|
|
122
166
|
|
|
123
|
-
Results:
|
|
167
|
+
Results: GetSituationResultSuccess | GetSituationResultFailure
|
|
124
168
|
"""
|
|
125
169
|
|
|
126
|
-
project_path: Path
|
|
127
170
|
situation_name: str
|
|
128
171
|
|
|
129
172
|
|
|
130
173
|
@dataclass
|
|
131
174
|
@PayloadRegistry.register
|
|
132
|
-
class
|
|
133
|
-
"""Situation
|
|
175
|
+
class GetSituationResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
176
|
+
"""Situation template retrieved successfully.
|
|
134
177
|
|
|
135
178
|
Args:
|
|
136
|
-
|
|
179
|
+
situation: The complete situation template including macro and policy.
|
|
180
|
+
Access via situation.macro, situation.policy.create_dirs, etc.
|
|
137
181
|
"""
|
|
138
182
|
|
|
139
|
-
|
|
183
|
+
situation: SituationTemplate
|
|
140
184
|
|
|
141
185
|
|
|
142
186
|
@dataclass
|
|
143
187
|
@PayloadRegistry.register
|
|
144
|
-
class
|
|
145
|
-
"""Situation
|
|
188
|
+
class GetSituationResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
189
|
+
"""Situation template retrieval failed (situation not found or template not loaded)."""
|
|
146
190
|
|
|
147
191
|
|
|
148
192
|
@dataclass
|
|
@@ -152,16 +196,17 @@ class GetPathForMacroRequest(RequestPayload):
|
|
|
152
196
|
|
|
153
197
|
Use when: Resolving paths, saving files. Works with any macro string, not tied to situations.
|
|
154
198
|
|
|
199
|
+
Uses the current project for context. Caller must parse the macro string
|
|
200
|
+
into a ParsedMacro before creating this request.
|
|
201
|
+
|
|
155
202
|
Args:
|
|
156
|
-
|
|
157
|
-
macro_schema: The macro template string to resolve (e.g., "{inputs}/{file_name}.{file_ext}")
|
|
203
|
+
parsed_macro: The parsed macro to resolve
|
|
158
204
|
variables: Variable values for macro substitution (e.g., {"file_name": "output", "file_ext": "png"})
|
|
159
205
|
|
|
160
206
|
Results: GetPathForMacroResultSuccess | GetPathForMacroResultFailure
|
|
161
207
|
"""
|
|
162
208
|
|
|
163
|
-
|
|
164
|
-
macro_schema: str
|
|
209
|
+
parsed_macro: ParsedMacro
|
|
165
210
|
variables: MacroVariables
|
|
166
211
|
|
|
167
212
|
|
|
@@ -171,10 +216,12 @@ class GetPathForMacroResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess
|
|
|
171
216
|
"""Path resolved successfully from macro.
|
|
172
217
|
|
|
173
218
|
Args:
|
|
174
|
-
resolved_path: The
|
|
219
|
+
resolved_path: The relative project path after macro substitution (e.g., "outputs/file.png")
|
|
220
|
+
absolute_path: The absolute filesystem path (e.g., "/workspace/outputs/file.png")
|
|
175
221
|
"""
|
|
176
222
|
|
|
177
223
|
resolved_path: Path
|
|
224
|
+
absolute_path: Path
|
|
178
225
|
|
|
179
226
|
|
|
180
227
|
@dataclass
|
|
@@ -186,29 +233,27 @@ class GetPathForMacroResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure
|
|
|
186
233
|
failure_reason: Specific reason for failure
|
|
187
234
|
missing_variables: List of required variable names that were not provided (for MISSING_REQUIRED_VARIABLES)
|
|
188
235
|
conflicting_variables: List of variables that conflict with directory names (for DIRECTORY_OVERRIDE_ATTEMPTED)
|
|
189
|
-
error_details: Additional error details from ParsedMacro (for MACRO_RESOLUTION_ERROR)
|
|
190
236
|
"""
|
|
191
237
|
|
|
192
238
|
failure_reason: PathResolutionFailureReason
|
|
193
|
-
missing_variables:
|
|
194
|
-
conflicting_variables:
|
|
195
|
-
error_details: str | None = None
|
|
239
|
+
missing_variables: set[str] | None = None
|
|
240
|
+
conflicting_variables: set[str] | None = None
|
|
196
241
|
|
|
197
242
|
|
|
198
243
|
@dataclass
|
|
199
244
|
@PayloadRegistry.register
|
|
200
245
|
class SetCurrentProjectRequest(RequestPayload):
|
|
201
|
-
"""Set which project
|
|
246
|
+
"""Set which project user has currently selected.
|
|
202
247
|
|
|
203
248
|
Use when: User switches between projects, opens a new workspace.
|
|
204
249
|
|
|
205
250
|
Args:
|
|
206
|
-
|
|
251
|
+
project_id: Identifier of the project to set as current (None to clear)
|
|
207
252
|
|
|
208
253
|
Results: SetCurrentProjectResultSuccess
|
|
209
254
|
"""
|
|
210
255
|
|
|
211
|
-
|
|
256
|
+
project_id: ProjectID | None
|
|
212
257
|
|
|
213
258
|
|
|
214
259
|
@dataclass
|
|
@@ -224,7 +269,7 @@ class GetCurrentProjectRequest(RequestPayload):
|
|
|
224
269
|
|
|
225
270
|
Use when: Need to know which project user is working with.
|
|
226
271
|
|
|
227
|
-
Results: GetCurrentProjectResultSuccess
|
|
272
|
+
Results: GetCurrentProjectResultSuccess | GetCurrentProjectResultFailure
|
|
228
273
|
"""
|
|
229
274
|
|
|
230
275
|
|
|
@@ -234,10 +279,16 @@ class GetCurrentProjectResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSucce
|
|
|
234
279
|
"""Current project retrieved.
|
|
235
280
|
|
|
236
281
|
Args:
|
|
237
|
-
|
|
282
|
+
project_info: Complete information about the current project
|
|
238
283
|
"""
|
|
239
284
|
|
|
240
|
-
|
|
285
|
+
project_info: ProjectInfo
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
@dataclass
|
|
289
|
+
@PayloadRegistry.register
|
|
290
|
+
class GetCurrentProjectResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
291
|
+
"""No current project is set."""
|
|
241
292
|
|
|
242
293
|
|
|
243
294
|
@dataclass
|
|
@@ -261,13 +312,7 @@ class SaveProjectTemplateRequest(RequestPayload):
|
|
|
261
312
|
@dataclass
|
|
262
313
|
@PayloadRegistry.register
|
|
263
314
|
class SaveProjectTemplateResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
264
|
-
"""Project template saved successfully.
|
|
265
|
-
|
|
266
|
-
Args:
|
|
267
|
-
project_path: Path where project.yml was saved
|
|
268
|
-
"""
|
|
269
|
-
|
|
270
|
-
project_path: Path
|
|
315
|
+
"""Project template saved successfully."""
|
|
271
316
|
|
|
272
317
|
|
|
273
318
|
@dataclass
|
|
@@ -281,122 +326,192 @@ class SaveProjectTemplateResultFailure(WorkflowNotAlteredMixin, ResultPayloadFai
|
|
|
281
326
|
- Disk full
|
|
282
327
|
"""
|
|
283
328
|
|
|
284
|
-
project_path: Path
|
|
285
|
-
|
|
286
329
|
|
|
287
330
|
@dataclass
|
|
288
331
|
@PayloadRegistry.register
|
|
289
|
-
class
|
|
290
|
-
"""
|
|
332
|
+
class AttemptMatchPathAgainstMacroRequest(RequestPayload):
|
|
333
|
+
"""Attempt to match a path against a macro schema and extract variables.
|
|
291
334
|
|
|
292
335
|
Use when: Validating paths, extracting info from file paths,
|
|
293
336
|
identifying which schema produced a file.
|
|
294
337
|
|
|
338
|
+
Uses the current project for context. Caller must parse the macro string
|
|
339
|
+
into a ParsedMacro before creating this request.
|
|
340
|
+
|
|
341
|
+
Pattern non-matches are returned as success with match_failure populated.
|
|
342
|
+
Only true system errors (missing SecretsManager, etc.) return failure.
|
|
343
|
+
|
|
295
344
|
Args:
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
file_path: Path to test
|
|
345
|
+
parsed_macro: Parsed macro template to match against
|
|
346
|
+
file_path: Path string to test
|
|
299
347
|
known_variables: Variables we already know
|
|
300
348
|
|
|
301
|
-
Results:
|
|
349
|
+
Results: AttemptMatchPathAgainstMacroResultSuccess | AttemptMatchPathAgainstMacroResultFailure
|
|
302
350
|
"""
|
|
303
351
|
|
|
304
|
-
|
|
305
|
-
macro_schema: str
|
|
352
|
+
parsed_macro: ParsedMacro
|
|
306
353
|
file_path: str
|
|
307
354
|
known_variables: MacroVariables
|
|
308
355
|
|
|
309
356
|
|
|
310
357
|
@dataclass
|
|
311
358
|
@PayloadRegistry.register
|
|
312
|
-
class
|
|
313
|
-
"""
|
|
359
|
+
class AttemptMatchPathAgainstMacroResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
360
|
+
"""Attempt completed (match succeeded or pattern didn't match).
|
|
314
361
|
|
|
315
|
-
|
|
362
|
+
Check match_failure to determine outcome:
|
|
363
|
+
- match_failure is None: Pattern matched, extracted_variables contains results
|
|
364
|
+
- match_failure is not None: Pattern didn't match (normal case, not an error)
|
|
365
|
+
"""
|
|
366
|
+
|
|
367
|
+
extracted_variables: MacroVariables | None
|
|
368
|
+
match_failure: MacroMatchFailure | None
|
|
316
369
|
|
|
317
370
|
|
|
318
371
|
@dataclass
|
|
319
372
|
@PayloadRegistry.register
|
|
320
|
-
class
|
|
321
|
-
"""
|
|
322
|
-
|
|
323
|
-
match_failure: MacroMatchFailure
|
|
373
|
+
class AttemptMatchPathAgainstMacroResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
374
|
+
"""System error occurred (missing SecretsManager, invalid configuration, etc.)."""
|
|
324
375
|
|
|
325
376
|
|
|
326
377
|
@dataclass
|
|
327
378
|
@PayloadRegistry.register
|
|
328
|
-
class
|
|
329
|
-
"""
|
|
379
|
+
class GetStateForMacroRequest(RequestPayload):
|
|
380
|
+
"""Analyze a macro and return comprehensive state information.
|
|
330
381
|
|
|
331
|
-
Use when: Building UI forms,
|
|
332
|
-
|
|
382
|
+
Use when: Building UI forms, real-time validation, checking if resolution
|
|
383
|
+
would succeed before actually resolving.
|
|
384
|
+
|
|
385
|
+
Uses the current project for context. Caller must parse the macro string
|
|
386
|
+
into a ParsedMacro before creating this request.
|
|
333
387
|
|
|
334
388
|
Args:
|
|
335
|
-
|
|
389
|
+
parsed_macro: The parsed macro to analyze
|
|
390
|
+
variables: Currently provided variable values
|
|
336
391
|
|
|
337
|
-
Results:
|
|
392
|
+
Results: GetStateForMacroResultSuccess | GetStateForMacroResultFailure
|
|
338
393
|
"""
|
|
339
394
|
|
|
340
|
-
|
|
395
|
+
parsed_macro: ParsedMacro
|
|
396
|
+
variables: MacroVariables
|
|
341
397
|
|
|
342
398
|
|
|
343
399
|
@dataclass
|
|
344
400
|
@PayloadRegistry.register
|
|
345
|
-
class
|
|
346
|
-
"""
|
|
401
|
+
class GetStateForMacroResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
402
|
+
"""Macro state analysis completed successfully.
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
all_variables: All variables found in the macro
|
|
406
|
+
satisfied_variables: Variables that have values (from user, directories, or builtins)
|
|
407
|
+
missing_required_variables: Required variables that are missing values
|
|
408
|
+
conflicting_variables: Variables that conflict (e.g., user overriding builtin with different value)
|
|
409
|
+
can_resolve: Whether the macro can be fully resolved (no missing required vars, no conflicts)
|
|
410
|
+
"""
|
|
347
411
|
|
|
348
|
-
|
|
412
|
+
all_variables: set[VariableInfo]
|
|
413
|
+
satisfied_variables: set[str]
|
|
414
|
+
missing_required_variables: set[str]
|
|
415
|
+
conflicting_variables: set[str]
|
|
416
|
+
can_resolve: bool
|
|
349
417
|
|
|
350
418
|
|
|
351
419
|
@dataclass
|
|
352
420
|
@PayloadRegistry.register
|
|
353
|
-
class
|
|
354
|
-
"""
|
|
421
|
+
class GetStateForMacroResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
422
|
+
"""Macro state analysis failed.
|
|
355
423
|
|
|
356
|
-
|
|
424
|
+
Failure occurs when:
|
|
425
|
+
- No current project is set
|
|
426
|
+
- Current project template is not loaded
|
|
427
|
+
- A builtin variable cannot be resolved (RuntimeError or NotImplementedError)
|
|
428
|
+
"""
|
|
357
429
|
|
|
358
430
|
|
|
359
431
|
@dataclass
|
|
360
432
|
@PayloadRegistry.register
|
|
361
|
-
class
|
|
362
|
-
"""
|
|
433
|
+
class AttemptMapAbsolutePathToProjectRequest(RequestPayload):
|
|
434
|
+
"""Find out if an absolute path exists anywhere within a Project directory.
|
|
435
|
+
|
|
436
|
+
Use when: User selects or types an absolute path via FilePicker and you need to know:
|
|
437
|
+
1. Is this path inside any project directory?
|
|
438
|
+
2. If yes, what's the macro form (e.g., {outputs}/file.png)?
|
|
363
439
|
|
|
364
|
-
|
|
365
|
-
|
|
440
|
+
This enables automatic conversion of absolute paths to portable macro form for workflow portability.
|
|
441
|
+
|
|
442
|
+
Uses longest prefix matching to find the most specific directory match.
|
|
443
|
+
Returns Success with mapped_path if inside project, or Success with None if outside.
|
|
444
|
+
Returns Failure if operation cannot be performed (no project loaded, secrets unavailable).
|
|
366
445
|
|
|
367
446
|
Args:
|
|
368
|
-
|
|
447
|
+
absolute_path: The absolute filesystem path to check
|
|
448
|
+
|
|
449
|
+
Results: AttemptMapAbsolutePathToProjectResultSuccess | AttemptMapAbsolutePathToProjectResultFailure
|
|
369
450
|
|
|
370
|
-
|
|
451
|
+
Examples:
|
|
452
|
+
Path inside project directory:
|
|
453
|
+
Request: absolute_path = /Users/james/project/outputs/renders/image.png
|
|
454
|
+
Result: mapped_path = "{outputs}/renders/image.png"
|
|
455
|
+
|
|
456
|
+
Path outside project:
|
|
457
|
+
Request: absolute_path = /Users/james/Downloads/image.png
|
|
458
|
+
Result: mapped_path = None
|
|
459
|
+
|
|
460
|
+
Path at directory root:
|
|
461
|
+
Request: absolute_path = /Users/james/project/outputs
|
|
462
|
+
Result: mapped_path = "{outputs}"
|
|
371
463
|
"""
|
|
372
464
|
|
|
373
|
-
|
|
465
|
+
absolute_path: Path
|
|
374
466
|
|
|
375
467
|
|
|
376
468
|
@dataclass
|
|
377
469
|
@PayloadRegistry.register
|
|
378
|
-
class
|
|
379
|
-
"""
|
|
470
|
+
class AttemptMapAbsolutePathToProjectResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
471
|
+
"""Path check completed successfully.
|
|
472
|
+
|
|
473
|
+
Success means the check was performed (not necessarily that a match was found).
|
|
474
|
+
- mapped_path is NOT None: Path is inside a project directory (macro form returned)
|
|
475
|
+
- mapped_path is None: Path is outside all project directories (valid answer)
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
mapped_path: The macro form if path is inside a project directory (e.g., "{outputs}/file.png"),
|
|
479
|
+
or None if path is outside all project directories
|
|
480
|
+
|
|
481
|
+
Examples:
|
|
482
|
+
Path inside project:
|
|
483
|
+
mapped_path = "{outputs}/renders/image.png"
|
|
484
|
+
result_details = "Successfully mapped absolute path to '{outputs}/renders/image.png'"
|
|
380
485
|
|
|
381
|
-
|
|
382
|
-
|
|
486
|
+
Path outside project:
|
|
487
|
+
mapped_path = None
|
|
488
|
+
result_details = "Attempted to map absolute path '/Users/james/Downloads/image.png'. Path is outside all project directories"
|
|
489
|
+
"""
|
|
490
|
+
|
|
491
|
+
mapped_path: str | None
|
|
383
492
|
|
|
384
493
|
|
|
385
494
|
@dataclass
|
|
386
495
|
@PayloadRegistry.register
|
|
387
|
-
class
|
|
388
|
-
"""
|
|
496
|
+
class AttemptMapAbsolutePathToProjectResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
497
|
+
"""Path mapping attempt failed.
|
|
389
498
|
|
|
390
|
-
|
|
391
|
-
|
|
499
|
+
Returned when the operation cannot be performed (no current project, secrets manager unavailable).
|
|
500
|
+
This is distinct from "path is outside project" which returns Success with None values.
|
|
501
|
+
|
|
502
|
+
Examples:
|
|
503
|
+
No current project:
|
|
504
|
+
result_details = "Attempted to map absolute path. Failed because no current project is set"
|
|
505
|
+
|
|
506
|
+
Secrets manager unavailable:
|
|
507
|
+
result_details = "Attempted to map absolute path. Failed because SecretsManager not available"
|
|
508
|
+
"""
|
|
392
509
|
|
|
393
510
|
|
|
394
511
|
@dataclass
|
|
395
512
|
@PayloadRegistry.register
|
|
396
513
|
class GetAllSituationsForProjectRequest(RequestPayload):
|
|
397
|
-
"""Get all situation names and schemas from
|
|
398
|
-
|
|
399
|
-
project_path: Path
|
|
514
|
+
"""Get all situation names and schemas from current project template."""
|
|
400
515
|
|
|
401
516
|
|
|
402
517
|
@dataclass
|
|
@@ -98,6 +98,19 @@ class EventManager:
|
|
|
98
98
|
self._event_loop = None
|
|
99
99
|
self._loop_thread_id = None
|
|
100
100
|
|
|
101
|
+
def _is_cross_thread_call(self) -> bool:
|
|
102
|
+
"""Check if the current call is from a different thread than the event loop.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
True if we're on a different thread and need thread-safe operations
|
|
106
|
+
"""
|
|
107
|
+
current_thread_id = threading.get_ident()
|
|
108
|
+
return (
|
|
109
|
+
self._loop_thread_id is not None
|
|
110
|
+
and current_thread_id != self._loop_thread_id
|
|
111
|
+
and self._event_loop is not None
|
|
112
|
+
)
|
|
113
|
+
|
|
101
114
|
def put_event(self, event: Any) -> None:
|
|
102
115
|
"""Put event into async queue from sync context (non-blocking).
|
|
103
116
|
|
|
@@ -109,15 +122,9 @@ class EventManager:
|
|
|
109
122
|
if self._event_queue is None:
|
|
110
123
|
return
|
|
111
124
|
|
|
112
|
-
|
|
113
|
-
current_thread_id = threading.get_ident()
|
|
114
|
-
|
|
115
|
-
if (
|
|
116
|
-
self._loop_thread_id is not None
|
|
117
|
-
and current_thread_id != self._loop_thread_id
|
|
118
|
-
and self._event_loop is not None
|
|
119
|
-
):
|
|
125
|
+
if self._is_cross_thread_call() and self._event_loop is not None:
|
|
120
126
|
# We're in a different thread from the event loop, use thread-safe method
|
|
127
|
+
# _is_cross_thread_call() guarantees _event_loop is not None
|
|
121
128
|
self._event_loop.call_soon_threadsafe(self._event_queue.put_nowait, event)
|
|
122
129
|
else:
|
|
123
130
|
# We're on the same thread as the event loop or no loop thread tracked, use direct method
|
|
@@ -126,13 +133,21 @@ class EventManager:
|
|
|
126
133
|
async def aput_event(self, event: Any) -> None:
|
|
127
134
|
"""Put event into async queue from async context.
|
|
128
135
|
|
|
136
|
+
Automatically detects if we're in a different thread and uses thread-safe operations.
|
|
137
|
+
|
|
129
138
|
Args:
|
|
130
139
|
event: The event to publish to the queue
|
|
131
140
|
"""
|
|
132
141
|
if self._event_queue is None:
|
|
133
142
|
return
|
|
134
143
|
|
|
135
|
-
|
|
144
|
+
if self._is_cross_thread_call() and self._event_loop is not None:
|
|
145
|
+
# We're in a different thread from the event loop, use thread-safe method
|
|
146
|
+
# _is_cross_thread_call() guarantees _event_loop is not None
|
|
147
|
+
self._event_loop.call_soon_threadsafe(self._event_queue.put_nowait, event)
|
|
148
|
+
else:
|
|
149
|
+
# We're on the same thread as the event loop or no loop thread tracked, use async method
|
|
150
|
+
await self._event_queue.put(event)
|
|
136
151
|
|
|
137
152
|
def assign_manager_to_request_type(
|
|
138
153
|
self,
|
|
@@ -1639,6 +1639,13 @@ class LibraryManager:
|
|
|
1639
1639
|
user_libraries_section = "app_events.on_app_initialization_complete.libraries_to_register"
|
|
1640
1640
|
libraries_to_register: list[str] = config_mgr.get_config_value(user_libraries_section)
|
|
1641
1641
|
|
|
1642
|
+
# Filter out empty or whitespace-only entries
|
|
1643
|
+
original_count = len(libraries_to_register) if libraries_to_register else 0
|
|
1644
|
+
libraries_to_register = [path for path in (libraries_to_register or []) if path and path.strip()]
|
|
1645
|
+
filtered_count = original_count - len(libraries_to_register)
|
|
1646
|
+
if filtered_count > 0:
|
|
1647
|
+
logger.warning("Filtered out %d empty library path entries from configuration", filtered_count)
|
|
1648
|
+
|
|
1642
1649
|
if not libraries_to_register:
|
|
1643
1650
|
logger.info("No libraries to register from config")
|
|
1644
1651
|
return
|
|
@@ -1986,24 +1993,6 @@ class LibraryManager:
|
|
|
1986
1993
|
ret_val.append(file_path)
|
|
1987
1994
|
return ret_val
|
|
1988
1995
|
|
|
1989
|
-
def _load_libraries_from_config_category(self, config_category: str, *, load_as_default_library: bool) -> None:
|
|
1990
|
-
config_mgr = GriptapeNodes.ConfigManager()
|
|
1991
|
-
libraries_to_register_category: list[str] = config_mgr.get_config_value(config_category)
|
|
1992
|
-
|
|
1993
|
-
if libraries_to_register_category is not None:
|
|
1994
|
-
for library_to_register in libraries_to_register_category:
|
|
1995
|
-
if library_to_register:
|
|
1996
|
-
if library_to_register.endswith(".json"):
|
|
1997
|
-
library_load_request = RegisterLibraryFromFileRequest(
|
|
1998
|
-
file_path=library_to_register,
|
|
1999
|
-
load_as_default_library=load_as_default_library,
|
|
2000
|
-
)
|
|
2001
|
-
else:
|
|
2002
|
-
library_load_request = RegisterLibraryFromRequirementSpecifierRequest(
|
|
2003
|
-
requirement_specifier=library_to_register
|
|
2004
|
-
)
|
|
2005
|
-
GriptapeNodes.handle_request(library_load_request)
|
|
2006
|
-
|
|
2007
1996
|
def _remove_missing_libraries_from_config(self, config_category: str) -> None:
|
|
2008
1997
|
# Now remove all libraries that were missing from the user's config.
|
|
2009
1998
|
config_mgr = GriptapeNodes.ConfigManager()
|
|
@@ -2076,8 +2065,10 @@ class LibraryManager:
|
|
|
2076
2065
|
# Add from config
|
|
2077
2066
|
config_libraries = config_mgr.get_config_value(user_libraries_section, default=[])
|
|
2078
2067
|
for library_path_str in config_libraries:
|
|
2079
|
-
|
|
2080
|
-
if
|
|
2081
|
-
|
|
2068
|
+
# Filter out falsy values that will resolve to current directory
|
|
2069
|
+
if library_path_str:
|
|
2070
|
+
library_path = Path(library_path_str)
|
|
2071
|
+
if library_path.exists():
|
|
2072
|
+
process_path(library_path)
|
|
2082
2073
|
|
|
2083
2074
|
return list(discovered_libraries)
|