griptape-nodes 0.61.0__py3-none-any.whl → 0.62.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- griptape_nodes/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/node_library/workflow_registry.py +1 -1
- griptape_nodes/retained_mode/events/project_events.py +208 -93
- 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/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.0.dist-info}/METADATA +1 -1
- {griptape_nodes-0.61.0.dist-info → griptape_nodes-0.62.0.dist-info}/RECORD +20 -21
- griptape_nodes/common/project_templates/defaults/project_template.yml +0 -89
- {griptape_nodes-0.61.0.dist-info → griptape_nodes-0.62.0.dist-info}/WHEEL +0 -0
- {griptape_nodes-0.61.0.dist-info → griptape_nodes-0.62.0.dist-info}/entry_points.txt +0 -0
|
@@ -42,9 +42,9 @@ class ParsedMacro:
|
|
|
42
42
|
segments.append(ParsedStaticValue(text=""))
|
|
43
43
|
self.segments = segments
|
|
44
44
|
|
|
45
|
-
def get_variables(self) ->
|
|
45
|
+
def get_variables(self) -> set[VariableInfo]:
|
|
46
46
|
"""Extract all VariableInfo from parsed segments."""
|
|
47
|
-
return
|
|
47
|
+
return {seg.info for seg in self.segments if isinstance(seg, ParsedVariable)}
|
|
48
48
|
|
|
49
49
|
def resolve(
|
|
50
50
|
self,
|
|
@@ -58,8 +58,8 @@ class ParsedMacro:
|
|
|
58
58
|
# Check if fully resolved
|
|
59
59
|
if not partial.is_fully_resolved():
|
|
60
60
|
unresolved = partial.get_unresolved_variables()
|
|
61
|
-
unresolved_names =
|
|
62
|
-
msg = f"Cannot fully resolve macro - missing required variables: {', '.join(unresolved_names)}"
|
|
61
|
+
unresolved_names = {var.info.name for var in unresolved}
|
|
62
|
+
msg = f"Cannot fully resolve macro - missing required variables: {', '.join(sorted(unresolved_names))}"
|
|
63
63
|
raise MacroResolutionError(
|
|
64
64
|
msg,
|
|
65
65
|
failure_reason=MacroResolutionFailureReason.MISSING_REQUIRED_VARIABLES,
|
|
@@ -47,7 +47,7 @@ class MacroResolutionError(Exception):
|
|
|
47
47
|
message: str,
|
|
48
48
|
failure_reason: MacroResolutionFailureReason | None = None,
|
|
49
49
|
variable_name: str | None = None,
|
|
50
|
-
missing_variables:
|
|
50
|
+
missing_variables: set[str] | None = None,
|
|
51
51
|
) -> None:
|
|
52
52
|
"""Initialize MacroResolutionError with optional structured fields.
|
|
53
53
|
|
|
@@ -55,7 +55,7 @@ class MacroResolutionError(Exception):
|
|
|
55
55
|
message: Human-readable error message
|
|
56
56
|
failure_reason: Specific reason for the resolution error
|
|
57
57
|
variable_name: Name of the problematic variable (if applicable)
|
|
58
|
-
missing_variables:
|
|
58
|
+
missing_variables: Set of missing variable names (for MISSING_REQUIRED_VARIABLES)
|
|
59
59
|
"""
|
|
60
60
|
super().__init__(message)
|
|
61
61
|
self.failure_reason = failure_reason
|
|
@@ -118,5 +118,5 @@ class MacroResolutionFailure:
|
|
|
118
118
|
|
|
119
119
|
failure_reason: MacroResolutionFailureReason
|
|
120
120
|
variable_name: str | None
|
|
121
|
-
missing_variables:
|
|
121
|
+
missing_variables: set[str] | None
|
|
122
122
|
error_details: str
|
|
@@ -32,7 +32,7 @@ class PartiallyResolvedMacro:
|
|
|
32
32
|
"""Convert to string (only valid if fully resolved)."""
|
|
33
33
|
if not self.is_fully_resolved():
|
|
34
34
|
unresolved = self.get_unresolved_variables()
|
|
35
|
-
unresolved_names =
|
|
35
|
+
unresolved_names = {var.info.name for var in unresolved}
|
|
36
36
|
msg = "Cannot convert partially resolved macro to string - unresolved variables remain"
|
|
37
37
|
raise MacroResolutionError(
|
|
38
38
|
msg,
|
|
@@ -135,7 +135,7 @@ def resolve_variable(
|
|
|
135
135
|
msg,
|
|
136
136
|
failure_reason=MacroResolutionFailureReason.MISSING_REQUIRED_VARIABLES,
|
|
137
137
|
variable_name=variable_name,
|
|
138
|
-
missing_variables=
|
|
138
|
+
missing_variables={variable_name},
|
|
139
139
|
)
|
|
140
140
|
# Optional variable not provided, return None to signal it should be skipped
|
|
141
141
|
return None
|
|
@@ -16,26 +16,25 @@ DEFAULT_PROJECT_TEMPLATE = ProjectTemplate(
|
|
|
16
16
|
directories={
|
|
17
17
|
"inputs": DirectoryDefinition(
|
|
18
18
|
name="inputs",
|
|
19
|
-
|
|
19
|
+
path_macro="inputs",
|
|
20
20
|
),
|
|
21
21
|
"outputs": DirectoryDefinition(
|
|
22
22
|
name="outputs",
|
|
23
|
-
|
|
23
|
+
path_macro="outputs",
|
|
24
24
|
),
|
|
25
25
|
"temp": DirectoryDefinition(
|
|
26
26
|
name="temp",
|
|
27
|
-
|
|
27
|
+
path_macro="temp",
|
|
28
28
|
),
|
|
29
29
|
"previews": DirectoryDefinition(
|
|
30
30
|
name="previews",
|
|
31
|
-
|
|
31
|
+
path_macro="previews",
|
|
32
32
|
),
|
|
33
33
|
},
|
|
34
34
|
environment={},
|
|
35
35
|
situations={
|
|
36
36
|
"save_file": SituationTemplate(
|
|
37
37
|
name="save_file",
|
|
38
|
-
situation_template_schema_version="0.1.0",
|
|
39
38
|
description="Generic file save operation",
|
|
40
39
|
macro="{file_name_base}{_index?:03}.{file_extension}",
|
|
41
40
|
policy=SituationPolicy(
|
|
@@ -46,7 +45,6 @@ DEFAULT_PROJECT_TEMPLATE = ProjectTemplate(
|
|
|
46
45
|
),
|
|
47
46
|
"copy_external_file": SituationTemplate(
|
|
48
47
|
name="copy_external_file",
|
|
49
|
-
situation_template_schema_version="0.1.0",
|
|
50
48
|
description="User copies external file to project",
|
|
51
49
|
macro="{inputs}/{node_name?:_}{parameter_name?:_}{file_name_base}{_index?:03}.{file_extension}",
|
|
52
50
|
policy=SituationPolicy(
|
|
@@ -57,7 +55,6 @@ DEFAULT_PROJECT_TEMPLATE = ProjectTemplate(
|
|
|
57
55
|
),
|
|
58
56
|
"download_url": SituationTemplate(
|
|
59
57
|
name="download_url",
|
|
60
|
-
situation_template_schema_version="0.1.0",
|
|
61
58
|
description="Download file from URL",
|
|
62
59
|
macro="{inputs}/{sanitized_url}",
|
|
63
60
|
policy=SituationPolicy(
|
|
@@ -68,9 +65,8 @@ DEFAULT_PROJECT_TEMPLATE = ProjectTemplate(
|
|
|
68
65
|
),
|
|
69
66
|
"save_node_output": SituationTemplate(
|
|
70
67
|
name="save_node_output",
|
|
71
|
-
situation_template_schema_version="0.1.0",
|
|
72
68
|
description="Node generates and saves output",
|
|
73
|
-
macro="{outputs}/{node_name?:_}{file_name_base}{_index?:03}.{file_extension}",
|
|
69
|
+
macro="{outputs}/{sub_dirs?:/}{node_name?:_}{file_name_base}{_index?:03}.{file_extension}",
|
|
74
70
|
policy=SituationPolicy(
|
|
75
71
|
on_collision=SituationFilePolicy.CREATE_NEW,
|
|
76
72
|
create_dirs=True,
|
|
@@ -79,7 +75,6 @@ DEFAULT_PROJECT_TEMPLATE = ProjectTemplate(
|
|
|
79
75
|
),
|
|
80
76
|
"save_preview": SituationTemplate(
|
|
81
77
|
name="save_preview",
|
|
82
|
-
situation_template_schema_version="0.1.0",
|
|
83
78
|
description="Generate preview/thumbnail",
|
|
84
79
|
macro="{previews}/{original_file_path}",
|
|
85
80
|
policy=SituationPolicy(
|
|
@@ -15,7 +15,7 @@ class DirectoryDefinition(BaseModel):
|
|
|
15
15
|
"""Definition of a logical directory in the project."""
|
|
16
16
|
|
|
17
17
|
name: str = Field(description="Logical name (e.g., 'inputs', 'outputs')")
|
|
18
|
-
|
|
18
|
+
path_macro: str = Field(description="Path string (may contain macros/env vars)")
|
|
19
19
|
|
|
20
20
|
@staticmethod
|
|
21
21
|
def merge(
|
|
@@ -28,7 +28,7 @@ class DirectoryDefinition(BaseModel):
|
|
|
28
28
|
"""Merge overlay fields onto base directory.
|
|
29
29
|
|
|
30
30
|
Field-level merge behavior:
|
|
31
|
-
-
|
|
31
|
+
- path_macro: Use overlay if present, else base
|
|
32
32
|
|
|
33
33
|
Args:
|
|
34
34
|
base: Complete base directory
|
|
@@ -41,11 +41,11 @@ class DirectoryDefinition(BaseModel):
|
|
|
41
41
|
New merged DirectoryDefinition
|
|
42
42
|
"""
|
|
43
43
|
# Start with base fields
|
|
44
|
-
merged_data = {"name": base.name, "
|
|
44
|
+
merged_data = {"name": base.name, "path_macro": base.path_macro}
|
|
45
45
|
|
|
46
46
|
# Apply overlay if present
|
|
47
|
-
if "
|
|
48
|
-
merged_data["
|
|
47
|
+
if "path_macro" in overlay_data:
|
|
48
|
+
merged_data["path_macro"] = overlay_data["path_macro"]
|
|
49
49
|
|
|
50
50
|
try:
|
|
51
51
|
return DirectoryDefinition.model_validate(merged_data)
|
|
@@ -152,11 +152,8 @@ def load_project_template_from_yaml( # noqa: C901
|
|
|
152
152
|
Returns:
|
|
153
153
|
ProjectTemplate on success, None if fatal errors prevent construction
|
|
154
154
|
"""
|
|
155
|
-
#
|
|
156
|
-
#
|
|
157
|
-
# - project.py imports YAMLLineInfo for type hints
|
|
158
|
-
# - loader.py needs ProjectTemplate at runtime
|
|
159
|
-
# TYPE_CHECKING import at top is for type hints only, this is for runtime
|
|
155
|
+
# Lazy import required: circular dependency between this module and project.py
|
|
156
|
+
# project.py imports ProjectOverlayData from this file, and we need ProjectTemplate from project.py
|
|
160
157
|
from griptape_nodes.common.project_templates.project import ProjectTemplate
|
|
161
158
|
|
|
162
159
|
# Pass 1: Load YAML with line tracking
|
|
@@ -208,9 +205,13 @@ def load_project_template_from_yaml( # noqa: C901
|
|
|
208
205
|
else:
|
|
209
206
|
# Add warnings for schema version mismatches
|
|
210
207
|
if template.project_template_schema_version != ProjectTemplate.LATEST_SCHEMA_VERSION:
|
|
208
|
+
message = (
|
|
209
|
+
f"Schema version '{template.project_template_schema_version}' "
|
|
210
|
+
f"differs from latest '{ProjectTemplate.LATEST_SCHEMA_VERSION}'"
|
|
211
|
+
)
|
|
211
212
|
validation_info.add_warning(
|
|
212
213
|
field_path=FIELD_PROJECT_TEMPLATE_SCHEMA_VERSION,
|
|
213
|
-
message=
|
|
214
|
+
message=message,
|
|
214
215
|
line_number=line_info.get_line(FIELD_PROJECT_TEMPLATE_SCHEMA_VERSION),
|
|
215
216
|
)
|
|
216
217
|
|
|
@@ -230,7 +231,7 @@ def load_partial_project_template(
|
|
|
230
231
|
|
|
231
232
|
Does NOT:
|
|
232
233
|
- Construct full SituationTemplate/DirectoryDefinition objects
|
|
233
|
-
- Validate situation
|
|
234
|
+
- Validate situation macros or policy values
|
|
234
235
|
- Check directory references
|
|
235
236
|
|
|
236
237
|
Use this for loading user overlay templates before merge.
|
|
@@ -29,7 +29,7 @@ class ProjectTemplate(BaseModel):
|
|
|
29
29
|
name: str = Field(description="Name of the project")
|
|
30
30
|
situations: dict[str, SituationTemplate] = Field(description="Situation templates (situation_name -> template)")
|
|
31
31
|
directories: dict[str, DirectoryDefinition] = Field(
|
|
32
|
-
description="Directory definitions (logical_name -> definition)"
|
|
32
|
+
description="Directory definitions (logical_name -> definition)",
|
|
33
33
|
)
|
|
34
34
|
environment: dict[str, str] = Field(default_factory=dict, description="Custom environment variables")
|
|
35
35
|
description: str | None = Field(default=None, description="Description of the project")
|
|
@@ -21,7 +21,7 @@ class SituationFilePolicy(StrEnum):
|
|
|
21
21
|
triggers user interaction before determining final policy.
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
CREATE_NEW = "create_new" # Increment {_index} in
|
|
24
|
+
CREATE_NEW = "create_new" # Increment {_index} in macro
|
|
25
25
|
OVERWRITE = "overwrite" # Maps to ExistingFilePolicy.OVERWRITE
|
|
26
26
|
FAIL = "fail" # Maps to ExistingFilePolicy.FAIL
|
|
27
27
|
PROMPT = "prompt" # Special UI handling
|
|
@@ -42,10 +42,7 @@ class SituationTemplate(BaseModel):
|
|
|
42
42
|
model_config = ConfigDict(populate_by_name=True)
|
|
43
43
|
|
|
44
44
|
name: str = Field(description="Name of the situation")
|
|
45
|
-
|
|
46
|
-
macro: str = Field(
|
|
47
|
-
description="Macro template for file path", serialization_alias="schema", validation_alias="schema"
|
|
48
|
-
)
|
|
45
|
+
macro: str = Field(description="Macro template for file path")
|
|
49
46
|
policy: SituationPolicy = Field(description="Policy for file operations")
|
|
50
47
|
fallback: str | None = Field(default=None, description="Name of fallback situation")
|
|
51
48
|
description: str | None = Field(default=None, description="Description of the situation")
|
|
@@ -61,11 +58,6 @@ class SituationTemplate(BaseModel):
|
|
|
61
58
|
raise ValueError(msg) from e
|
|
62
59
|
return v
|
|
63
60
|
|
|
64
|
-
@property
|
|
65
|
-
def schema(self) -> str:
|
|
66
|
-
"""Alias for macro to maintain YAML compatibility (schema field in YAML)."""
|
|
67
|
-
return self.macro
|
|
68
|
-
|
|
69
61
|
@staticmethod
|
|
70
62
|
def merge(
|
|
71
63
|
base: SituationTemplate,
|
|
@@ -77,11 +69,10 @@ class SituationTemplate(BaseModel):
|
|
|
77
69
|
"""Merge overlay fields onto base situation.
|
|
78
70
|
|
|
79
71
|
Field-level merge behavior:
|
|
80
|
-
-
|
|
72
|
+
- macro: Use overlay if present, else base
|
|
81
73
|
- description: Use overlay if present, else base
|
|
82
74
|
- fallback: Use overlay if present, else base
|
|
83
75
|
- policy: Use overlay if present (must be complete), else base
|
|
84
|
-
- situation_template_schema_version: Use overlay if present, else base
|
|
85
76
|
|
|
86
77
|
Policy validation:
|
|
87
78
|
- If policy provided in overlay, must contain both on_collision and create_dirs
|
|
@@ -101,8 +92,8 @@ class SituationTemplate(BaseModel):
|
|
|
101
92
|
merged_data = base.model_dump()
|
|
102
93
|
|
|
103
94
|
# Apply overlay fields if present
|
|
104
|
-
if "
|
|
105
|
-
merged_data["
|
|
95
|
+
if "macro" in overlay_data:
|
|
96
|
+
merged_data["macro"] = overlay_data["macro"]
|
|
106
97
|
|
|
107
98
|
if "description" in overlay_data:
|
|
108
99
|
merged_data["description"] = overlay_data["description"]
|
|
@@ -110,9 +101,6 @@ class SituationTemplate(BaseModel):
|
|
|
110
101
|
if "fallback" in overlay_data:
|
|
111
102
|
merged_data["fallback"] = overlay_data["fallback"]
|
|
112
103
|
|
|
113
|
-
if "situation_template_schema_version" in overlay_data:
|
|
114
|
-
merged_data["situation_template_schema_version"] = overlay_data["situation_template_schema_version"]
|
|
115
|
-
|
|
116
104
|
# Policy must be complete if provided
|
|
117
105
|
if "policy" in overlay_data:
|
|
118
106
|
policy = overlay_data["policy"]
|
|
@@ -46,7 +46,7 @@ class ProjectValidationProblem:
|
|
|
46
46
|
"""Single validation problem with context."""
|
|
47
47
|
|
|
48
48
|
line_number: int | None
|
|
49
|
-
field_path: str # e.g., "situations.copy_external_file.
|
|
49
|
+
field_path: str # e.g., "situations.copy_external_file.macro"
|
|
50
50
|
message: str
|
|
51
51
|
severity: ProjectValidationProblemSeverity
|
|
52
52
|
|
|
@@ -92,7 +92,7 @@ class ProjectValidationInfo:
|
|
|
92
92
|
field_path=field_path,
|
|
93
93
|
message=message,
|
|
94
94
|
severity=ProjectValidationProblemSeverity.ERROR,
|
|
95
|
-
)
|
|
95
|
+
),
|
|
96
96
|
)
|
|
97
97
|
self.status = ProjectValidationStatus.UNUSABLE
|
|
98
98
|
|
|
@@ -112,7 +112,7 @@ class ProjectValidationInfo:
|
|
|
112
112
|
field_path=field_path,
|
|
113
113
|
message=message,
|
|
114
114
|
severity=ProjectValidationProblemSeverity.WARNING,
|
|
115
|
-
)
|
|
115
|
+
),
|
|
116
116
|
)
|
|
117
117
|
|
|
118
118
|
if self.status == ProjectValidationStatus.GOOD:
|