griptape-nodes 0.60.4__py3-none-any.whl → 0.61.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 (47) hide show
  1. griptape_nodes/bootstrap/workflow_publishers/local_workflow_publisher.py +0 -1
  2. griptape_nodes/common/macro_parser/__init__.py +16 -1
  3. griptape_nodes/common/macro_parser/core.py +15 -3
  4. griptape_nodes/common/macro_parser/exceptions.py +99 -0
  5. griptape_nodes/common/macro_parser/formats.py +13 -4
  6. griptape_nodes/common/macro_parser/matching.py +5 -2
  7. griptape_nodes/common/macro_parser/parsing.py +48 -8
  8. griptape_nodes/common/macro_parser/resolution.py +23 -5
  9. griptape_nodes/common/project_templates/__init__.py +49 -0
  10. griptape_nodes/common/project_templates/default_project_template.py +92 -0
  11. griptape_nodes/common/project_templates/defaults/README.md +36 -0
  12. griptape_nodes/common/project_templates/defaults/project_template.yml +89 -0
  13. griptape_nodes/common/project_templates/directory.py +67 -0
  14. griptape_nodes/common/project_templates/loader.py +341 -0
  15. griptape_nodes/common/project_templates/project.py +252 -0
  16. griptape_nodes/common/project_templates/situation.py +155 -0
  17. griptape_nodes/common/project_templates/validation.py +140 -0
  18. griptape_nodes/exe_types/core_types.py +36 -3
  19. griptape_nodes/exe_types/node_types.py +4 -2
  20. griptape_nodes/exe_types/param_components/progress_bar_component.py +57 -0
  21. griptape_nodes/exe_types/param_types/parameter_audio.py +243 -0
  22. griptape_nodes/exe_types/param_types/parameter_image.py +243 -0
  23. griptape_nodes/exe_types/param_types/parameter_three_d.py +215 -0
  24. griptape_nodes/exe_types/param_types/parameter_video.py +243 -0
  25. griptape_nodes/node_library/workflow_registry.py +1 -1
  26. griptape_nodes/retained_mode/events/execution_events.py +41 -0
  27. griptape_nodes/retained_mode/events/node_events.py +90 -1
  28. griptape_nodes/retained_mode/events/os_events.py +108 -0
  29. griptape_nodes/retained_mode/events/parameter_events.py +1 -1
  30. griptape_nodes/retained_mode/events/project_events.py +413 -0
  31. griptape_nodes/retained_mode/events/workflow_events.py +19 -1
  32. griptape_nodes/retained_mode/griptape_nodes.py +9 -1
  33. griptape_nodes/retained_mode/managers/agent_manager.py +18 -24
  34. griptape_nodes/retained_mode/managers/event_manager.py +6 -9
  35. griptape_nodes/retained_mode/managers/flow_manager.py +63 -0
  36. griptape_nodes/retained_mode/managers/library_manager.py +55 -42
  37. griptape_nodes/retained_mode/managers/mcp_manager.py +14 -6
  38. griptape_nodes/retained_mode/managers/node_manager.py +232 -0
  39. griptape_nodes/retained_mode/managers/os_manager.py +345 -0
  40. griptape_nodes/retained_mode/managers/project_manager.py +617 -0
  41. griptape_nodes/retained_mode/managers/settings.py +6 -0
  42. griptape_nodes/retained_mode/managers/workflow_manager.py +6 -69
  43. griptape_nodes/traits/button.py +18 -0
  44. {griptape_nodes-0.60.4.dist-info → griptape_nodes-0.61.0.dist-info}/METADATA +5 -3
  45. {griptape_nodes-0.60.4.dist-info → griptape_nodes-0.61.0.dist-info}/RECORD +47 -31
  46. {griptape_nodes-0.60.4.dist-info → griptape_nodes-0.61.0.dist-info}/WHEEL +1 -1
  47. {griptape_nodes-0.60.4.dist-info → griptape_nodes-0.61.0.dist-info}/entry_points.txt +0 -0
@@ -32,7 +32,6 @@ class LocalWorkflowPublisher(LocalWorkflowExecutor):
32
32
  publish_workflow_request = PublishWorkflowRequest(
33
33
  workflow_name=workflow_name,
34
34
  publisher_name=publisher_name,
35
- execute_on_publish=False,
36
35
  published_workflow_file_name=published_workflow_file_name,
37
36
  pickle_control_flow_result=pickle_control_flow_result,
38
37
  )
@@ -1,7 +1,16 @@
1
1
  """Macro language parser for template-based path generation."""
2
2
 
3
3
  from griptape_nodes.common.macro_parser.core import ParsedMacro
4
- from griptape_nodes.common.macro_parser.exceptions import MacroResolutionError, MacroSyntaxError
4
+ from griptape_nodes.common.macro_parser.exceptions import (
5
+ MacroMatchFailure,
6
+ MacroMatchFailureReason,
7
+ MacroParseFailure,
8
+ MacroParseFailureReason,
9
+ MacroResolutionError,
10
+ MacroResolutionFailure,
11
+ MacroResolutionFailureReason,
12
+ MacroSyntaxError,
13
+ )
5
14
  from griptape_nodes.common.macro_parser.formats import (
6
15
  DateFormat,
7
16
  LowerCaseFormat,
@@ -15,7 +24,13 @@ from griptape_nodes.common.macro_parser.segments import ParsedStaticValue, Parse
15
24
  __all__ = [
16
25
  "DateFormat",
17
26
  "LowerCaseFormat",
27
+ "MacroMatchFailure",
28
+ "MacroMatchFailureReason",
29
+ "MacroParseFailure",
30
+ "MacroParseFailureReason",
18
31
  "MacroResolutionError",
32
+ "MacroResolutionFailure",
33
+ "MacroResolutionFailureReason",
19
34
  "MacroSyntaxError",
20
35
  "NumericPaddingFormat",
21
36
  "ParsedMacro",
@@ -4,7 +4,11 @@ from __future__ import annotations
4
4
 
5
5
  from typing import TYPE_CHECKING
6
6
 
7
- from griptape_nodes.common.macro_parser.exceptions import MacroResolutionError, MacroSyntaxError
7
+ from griptape_nodes.common.macro_parser.exceptions import (
8
+ MacroResolutionError,
9
+ MacroResolutionFailureReason,
10
+ MacroSyntaxError,
11
+ )
8
12
  from griptape_nodes.common.macro_parser.matching import extract_unknown_variables
9
13
  from griptape_nodes.common.macro_parser.parsing import parse_segments
10
14
  from griptape_nodes.common.macro_parser.resolution import partial_resolve
@@ -28,7 +32,11 @@ class ParsedMacro:
28
32
  segments = parse_segments(template)
29
33
  except MacroSyntaxError as err:
30
34
  msg = f"Attempted to parse template string '{template}'. Failed due to: {err}"
31
- raise MacroSyntaxError(msg) from err
35
+ raise MacroSyntaxError(
36
+ msg,
37
+ failure_reason=err.failure_reason,
38
+ error_position=err.error_position,
39
+ ) from err
32
40
 
33
41
  if not segments:
34
42
  segments.append(ParsedStaticValue(text=""))
@@ -52,7 +60,11 @@ class ParsedMacro:
52
60
  unresolved = partial.get_unresolved_variables()
53
61
  unresolved_names = [var.info.name for var in unresolved]
54
62
  msg = f"Cannot fully resolve macro - missing required variables: {', '.join(unresolved_names)}"
55
- raise MacroResolutionError(msg)
63
+ raise MacroResolutionError(
64
+ msg,
65
+ failure_reason=MacroResolutionFailureReason.MISSING_REQUIRED_VARIABLES,
66
+ missing_variables=unresolved_names,
67
+ )
56
68
 
57
69
  # Convert to string
58
70
  return partial.to_string()
@@ -2,6 +2,9 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from dataclasses import dataclass
6
+ from enum import StrEnum
7
+
5
8
 
6
9
  class MacroSyntaxError(Exception):
7
10
  """Raised when macro template has invalid syntax.
@@ -12,6 +15,23 @@ class MacroSyntaxError(Exception):
12
15
  - Nested braces: "{outer_{inner}}"
13
16
  """
14
17
 
18
+ def __init__(
19
+ self,
20
+ message: str,
21
+ failure_reason: MacroParseFailureReason | None = None,
22
+ error_position: int | None = None,
23
+ ) -> None:
24
+ """Initialize MacroSyntaxError with optional structured fields.
25
+
26
+ Args:
27
+ message: Human-readable error message
28
+ failure_reason: Specific reason for the syntax error
29
+ error_position: Character position where error occurred
30
+ """
31
+ super().__init__(message)
32
+ self.failure_reason = failure_reason
33
+ self.error_position = error_position
34
+
15
35
 
16
36
  class MacroResolutionError(Exception):
17
37
  """Raised when macro cannot be resolved with provided variables.
@@ -21,3 +41,82 @@ class MacroResolutionError(Exception):
21
41
  - Environment variable referenced but not found in environment
22
42
  - Format specifier cannot be applied to value type (e.g., :03 on string)
23
43
  """
44
+
45
+ def __init__(
46
+ self,
47
+ message: str,
48
+ failure_reason: MacroResolutionFailureReason | None = None,
49
+ variable_name: str | None = None,
50
+ missing_variables: list[str] | None = None,
51
+ ) -> None:
52
+ """Initialize MacroResolutionError with optional structured fields.
53
+
54
+ Args:
55
+ message: Human-readable error message
56
+ failure_reason: Specific reason for the resolution error
57
+ variable_name: Name of the problematic variable (if applicable)
58
+ missing_variables: List of missing variable names (for MISSING_REQUIRED_VARIABLES)
59
+ """
60
+ super().__init__(message)
61
+ self.failure_reason = failure_reason
62
+ self.variable_name = variable_name
63
+ self.missing_variables = missing_variables
64
+
65
+
66
+ class MacroMatchFailureReason(StrEnum):
67
+ """Reason why path matching failed."""
68
+
69
+ STATIC_TEXT_MISMATCH = "STATIC_TEXT_MISMATCH"
70
+ DELIMITER_NOT_FOUND = "DELIMITER_NOT_FOUND"
71
+ FORMAT_REVERSAL_FAILED = "FORMAT_REVERSAL_FAILED"
72
+ INVALID_MACRO_SYNTAX = "INVALID_MACRO_SYNTAX"
73
+
74
+
75
+ class MacroParseFailureReason(StrEnum):
76
+ """Reason why macro parsing failed."""
77
+
78
+ UNMATCHED_CLOSING_BRACE = "UNMATCHED_CLOSING_BRACE"
79
+ UNCLOSED_BRACE = "UNCLOSED_BRACE"
80
+ NESTED_BRACES = "NESTED_BRACES"
81
+ EMPTY_VARIABLE = "EMPTY_VARIABLE"
82
+ UNEXPECTED_SEGMENT_TYPE = "UNEXPECTED_SEGMENT_TYPE"
83
+
84
+
85
+ class MacroResolutionFailureReason(StrEnum):
86
+ """Reason why macro resolution failed."""
87
+
88
+ NUMERIC_PADDING_ON_NON_NUMERIC = "NUMERIC_PADDING_ON_NON_NUMERIC"
89
+ INVALID_INTEGER_PARSE = "INVALID_INTEGER_PARSE"
90
+ DATE_FORMAT_NOT_IMPLEMENTED = "DATE_FORMAT_NOT_IMPLEMENTED"
91
+ MISSING_REQUIRED_VARIABLES = "MISSING_REQUIRED_VARIABLES"
92
+ ENVIRONMENT_VARIABLE_NOT_FOUND = "ENVIRONMENT_VARIABLE_NOT_FOUND"
93
+ UNEXPECTED_SEGMENT_TYPE = "UNEXPECTED_SEGMENT_TYPE"
94
+
95
+
96
+ @dataclass
97
+ class MacroMatchFailure:
98
+ """Details about why a macro match failed."""
99
+
100
+ failure_reason: MacroMatchFailureReason
101
+ expected_pattern: str
102
+ known_variables_used: dict[str, str | int]
103
+ error_details: str
104
+
105
+
106
+ @dataclass
107
+ class MacroParseFailure:
108
+ """Details about why macro parsing failed."""
109
+
110
+ failure_reason: MacroParseFailureReason
111
+ error_position: int | None
112
+ error_details: str
113
+
114
+
115
+ @dataclass
116
+ class MacroResolutionFailure:
117
+ """Details about why macro resolution failed."""
118
+
119
+ failure_reason: MacroResolutionFailureReason
120
+ variable_name: str | None
121
+ missing_variables: list[str] | None
122
+ error_details: str
@@ -6,7 +6,7 @@ import re
6
6
  from abc import ABC, abstractmethod
7
7
  from dataclasses import dataclass
8
8
 
9
- from griptape_nodes.common.macro_parser.exceptions import MacroResolutionError
9
+ from griptape_nodes.common.macro_parser.exceptions import MacroResolutionError, MacroResolutionFailureReason
10
10
 
11
11
 
12
12
  @dataclass
@@ -89,7 +89,10 @@ class NumericPaddingFormat(FormatSpec):
89
89
  f"Numeric padding format :{self.width:0{self.width}d} "
90
90
  f"cannot be applied to non-numeric value: {value}"
91
91
  )
92
- raise MacroResolutionError(msg)
92
+ raise MacroResolutionError(
93
+ msg,
94
+ failure_reason=MacroResolutionFailureReason.NUMERIC_PADDING_ON_NON_NUMERIC,
95
+ )
93
96
  value = int(value)
94
97
  return f"{value:0{self.width}d}"
95
98
 
@@ -99,7 +102,10 @@ class NumericPaddingFormat(FormatSpec):
99
102
  return int(value)
100
103
  except ValueError as e:
101
104
  msg = f"Cannot parse '{value}' as integer"
102
- raise MacroResolutionError(msg) from e
105
+ raise MacroResolutionError(
106
+ msg,
107
+ failure_reason=MacroResolutionFailureReason.INVALID_INTEGER_PARSE,
108
+ ) from e
103
109
 
104
110
 
105
111
  @dataclass
@@ -154,7 +160,10 @@ class DateFormat(FormatSpec):
154
160
  """Apply date formatting."""
155
161
  # TODO(https://github.com/griptape-ai/griptape-nodes/issues/2717): Implement date formatting
156
162
  msg = "DateFormat not yet fully implemented"
157
- raise MacroResolutionError(msg)
163
+ raise MacroResolutionError(
164
+ msg,
165
+ failure_reason=MacroResolutionFailureReason.DATE_FORMAT_NOT_IMPLEMENTED,
166
+ )
158
167
 
159
168
  def reverse(self, value: str) -> str:
160
169
  """Attempt to parse date string."""
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from typing import TYPE_CHECKING
6
6
 
7
- from griptape_nodes.common.macro_parser.exceptions import MacroSyntaxError
7
+ from griptape_nodes.common.macro_parser.exceptions import MacroParseFailureReason, MacroSyntaxError
8
8
  from griptape_nodes.common.macro_parser.segments import (
9
9
  ParsedSegment,
10
10
  ParsedStaticValue,
@@ -49,7 +49,10 @@ def extract_unknown_variables(
49
49
  current_pos = new_pos
50
50
  case _:
51
51
  msg = f"Unexpected segment type: {type(segment).__name__}"
52
- raise MacroSyntaxError(msg)
52
+ raise MacroSyntaxError(
53
+ msg,
54
+ failure_reason=MacroParseFailureReason.UNEXPECTED_SEGMENT_TYPE,
55
+ )
53
56
 
54
57
  return current_match
55
58
 
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import re
6
6
 
7
- from griptape_nodes.common.macro_parser.exceptions import MacroSyntaxError
7
+ from griptape_nodes.common.macro_parser.exceptions import MacroParseFailureReason, MacroSyntaxError
8
8
  from griptape_nodes.common.macro_parser.formats import (
9
9
  FORMAT_REGISTRY,
10
10
  DateFormat,
@@ -47,7 +47,11 @@ def parse_segments(template: str) -> list[ParsedSegment]:
47
47
  if "}" in static_text:
48
48
  closing_pos = current_pos + static_text.index("}")
49
49
  msg = f"Unmatched closing brace at position {closing_pos}"
50
- raise MacroSyntaxError(msg)
50
+ raise MacroSyntaxError(
51
+ msg,
52
+ failure_reason=MacroParseFailureReason.UNMATCHED_CLOSING_BRACE,
53
+ error_position=closing_pos,
54
+ )
51
55
  segments.append(ParsedStaticValue(text=static_text))
52
56
  break
53
57
 
@@ -58,26 +62,42 @@ def parse_segments(template: str) -> list[ParsedSegment]:
58
62
  if "}" in static_text:
59
63
  closing_pos = current_pos + static_text.index("}")
60
64
  msg = f"Unmatched closing brace at position {closing_pos}"
61
- raise MacroSyntaxError(msg)
65
+ raise MacroSyntaxError(
66
+ msg,
67
+ failure_reason=MacroParseFailureReason.UNMATCHED_CLOSING_BRACE,
68
+ error_position=closing_pos,
69
+ )
62
70
  segments.append(ParsedStaticValue(text=static_text))
63
71
 
64
72
  # Find matching closing brace
65
73
  brace_end = template.find("}", brace_start)
66
74
  if brace_end == -1:
67
75
  msg = f"Unclosed brace at position {brace_start}"
68
- raise MacroSyntaxError(msg)
76
+ raise MacroSyntaxError(
77
+ msg,
78
+ failure_reason=MacroParseFailureReason.UNCLOSED_BRACE,
79
+ error_position=brace_start,
80
+ )
69
81
 
70
82
  # Check for nested braces (opening brace before closing brace)
71
83
  next_open = template.find("{", brace_start + 1)
72
84
  if next_open != -1 and next_open < brace_end:
73
85
  msg = f"Nested braces are not allowed at position {next_open}"
74
- raise MacroSyntaxError(msg)
86
+ raise MacroSyntaxError(
87
+ msg,
88
+ failure_reason=MacroParseFailureReason.NESTED_BRACES,
89
+ error_position=next_open,
90
+ )
75
91
 
76
92
  # Extract and parse the variable content
77
93
  variable_content = template[brace_start + 1 : brace_end]
78
94
  if not variable_content:
79
95
  msg = f"Empty variable at position {brace_start}"
80
- raise MacroSyntaxError(msg)
96
+ raise MacroSyntaxError(
97
+ msg,
98
+ failure_reason=MacroParseFailureReason.EMPTY_VARIABLE,
99
+ error_position=brace_start,
100
+ )
81
101
 
82
102
  variable = parse_variable(variable_content)
83
103
  segments.append(variable)
@@ -111,6 +131,7 @@ def parse_variable(variable_content: str) -> ParsedVariable:
111
131
 
112
132
  # Check for format specifiers (:)
113
133
  format_specs: list[FormatSpec] = []
134
+ is_required = True
114
135
  if ":" in variable_content:
115
136
  parts = variable_content.split(":")
116
137
  variable_part = parts[0]
@@ -120,16 +141,35 @@ def parse_variable(variable_content: str) -> ParsedVariable:
120
141
  for format_part in format_parts:
121
142
  format_spec = parse_format_spec(format_part)
122
143
  format_specs.append(format_spec)
144
+
145
+ # Check if last format spec ends with unquoted ?
146
+ if format_parts:
147
+ last_format_part = format_parts[-1]
148
+
149
+ # Check if it's quoted (quoted formats preserve ? as literal)
150
+ is_quoted = last_format_part.startswith("'") and last_format_part.endswith("'")
151
+
152
+ if not is_quoted and last_format_part.endswith("?"):
153
+ # Strip the ? and re-parse the format
154
+ stripped_format = last_format_part[:-1]
155
+ if stripped_format:
156
+ # Re-parse without the ?
157
+ format_specs[-1] = parse_format_spec(stripped_format)
158
+ else:
159
+ # Format was just "?", remove it entirely
160
+ format_specs.pop()
161
+
162
+ # Mark variable as optional
163
+ is_required = False
123
164
  else:
124
165
  variable_part = variable_content
125
166
 
126
- # Check for optional marker (?)
167
+ # Check for optional marker (?) after variable name
127
168
  if variable_part.endswith("?"):
128
169
  name = variable_part[:-1]
129
170
  is_required = False
130
171
  else:
131
172
  name = variable_part
132
- is_required = True
133
173
 
134
174
  info = VariableInfo(name=name, is_required=is_required)
135
175
  return ParsedVariable(info=info, format_specs=format_specs, default_value=default_value)
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  from dataclasses import dataclass
6
6
  from typing import TYPE_CHECKING
7
7
 
8
- from griptape_nodes.common.macro_parser.exceptions import MacroResolutionError
8
+ from griptape_nodes.common.macro_parser.exceptions import MacroResolutionError, MacroResolutionFailureReason
9
9
  from griptape_nodes.common.macro_parser.segments import ParsedSegment, ParsedStaticValue, ParsedVariable
10
10
 
11
11
  if TYPE_CHECKING:
@@ -31,8 +31,14 @@ class PartiallyResolvedMacro:
31
31
  def to_string(self) -> str:
32
32
  """Convert to string (only valid if fully resolved)."""
33
33
  if not self.is_fully_resolved():
34
+ unresolved = self.get_unresolved_variables()
35
+ unresolved_names = [var.info.name for var in unresolved]
34
36
  msg = "Cannot convert partially resolved macro to string - unresolved variables remain"
35
- raise MacroResolutionError(msg)
37
+ raise MacroResolutionError(
38
+ msg,
39
+ failure_reason=MacroResolutionFailureReason.MISSING_REQUIRED_VARIABLES,
40
+ missing_variables=unresolved_names,
41
+ )
36
42
  # All segments are ParsedStaticValue at this point
37
43
  return "".join(seg.text for seg in self.segments if isinstance(seg, ParsedStaticValue))
38
44
 
@@ -92,7 +98,10 @@ def partial_resolve(
92
98
  continue
93
99
  case _:
94
100
  msg = f"Unexpected segment type: {type(segment).__name__}"
95
- raise MacroResolutionError(msg)
101
+ raise MacroResolutionError(
102
+ msg,
103
+ failure_reason=MacroResolutionFailureReason.UNEXPECTED_SEGMENT_TYPE,
104
+ )
96
105
 
97
106
  return PartiallyResolvedMacro(
98
107
  original_template=template,
@@ -122,7 +131,12 @@ def resolve_variable(
122
131
  if variable_name not in variables:
123
132
  if variable.info.is_required:
124
133
  msg = f"Required variable '{variable_name}' not found in variables dict"
125
- raise MacroResolutionError(msg)
134
+ raise MacroResolutionError(
135
+ msg,
136
+ failure_reason=MacroResolutionFailureReason.MISSING_REQUIRED_VARIABLES,
137
+ variable_name=variable_name,
138
+ missing_variables=[variable_name],
139
+ )
126
140
  # Optional variable not provided, return None to signal it should be skipped
127
141
  return None
128
142
 
@@ -162,7 +176,11 @@ def resolve_env_var(value: str | int, secrets_manager: SecretsManager) -> str |
162
176
 
163
177
  if env_value is None:
164
178
  msg = f"Environment variable '{env_var_name}' not found"
165
- raise MacroResolutionError(msg)
179
+ raise MacroResolutionError(
180
+ msg,
181
+ failure_reason=MacroResolutionFailureReason.ENVIRONMENT_VARIABLE_NOT_FOUND,
182
+ variable_name=env_var_name,
183
+ )
166
184
 
167
185
  # Return resolved env var value
168
186
  return env_value
@@ -0,0 +1,49 @@
1
+ """Project template system for managing project.yml files and situations."""
2
+
3
+ from griptape_nodes.common.project_templates.default_project_template import DEFAULT_PROJECT_TEMPLATE
4
+ from griptape_nodes.common.project_templates.directory import DirectoryDefinition
5
+ from griptape_nodes.common.project_templates.loader import (
6
+ ProjectOverlayData,
7
+ YAMLLineInfo,
8
+ YAMLParseResult,
9
+ load_partial_project_template,
10
+ load_project_template_from_yaml,
11
+ load_yaml_with_line_tracking,
12
+ )
13
+ from griptape_nodes.common.project_templates.project import ProjectTemplate
14
+ from griptape_nodes.common.project_templates.situation import (
15
+ SituationFilePolicy,
16
+ SituationPolicy,
17
+ SituationTemplate,
18
+ )
19
+ from griptape_nodes.common.project_templates.validation import (
20
+ ProjectOverride,
21
+ ProjectOverrideAction,
22
+ ProjectOverrideCategory,
23
+ ProjectValidationInfo,
24
+ ProjectValidationProblem,
25
+ ProjectValidationProblemSeverity,
26
+ ProjectValidationStatus,
27
+ )
28
+
29
+ __all__ = [
30
+ "DEFAULT_PROJECT_TEMPLATE",
31
+ "DirectoryDefinition",
32
+ "ProjectOverlayData",
33
+ "ProjectOverride",
34
+ "ProjectOverrideAction",
35
+ "ProjectOverrideCategory",
36
+ "ProjectTemplate",
37
+ "ProjectValidationInfo",
38
+ "ProjectValidationProblem",
39
+ "ProjectValidationProblemSeverity",
40
+ "ProjectValidationStatus",
41
+ "SituationFilePolicy",
42
+ "SituationPolicy",
43
+ "SituationTemplate",
44
+ "YAMLLineInfo",
45
+ "YAMLParseResult",
46
+ "load_partial_project_template",
47
+ "load_project_template_from_yaml",
48
+ "load_yaml_with_line_tracking",
49
+ ]
@@ -0,0 +1,92 @@
1
+ """Default project template defined in Python using Pydantic models."""
2
+
3
+ from griptape_nodes.common.project_templates.directory import DirectoryDefinition
4
+ from griptape_nodes.common.project_templates.project import ProjectTemplate
5
+ from griptape_nodes.common.project_templates.situation import (
6
+ SituationFilePolicy,
7
+ SituationPolicy,
8
+ SituationTemplate,
9
+ )
10
+
11
+ # Default project template matching the values from project_template.yml
12
+ DEFAULT_PROJECT_TEMPLATE = ProjectTemplate(
13
+ project_template_schema_version="0.1.0",
14
+ name="Default Project",
15
+ description="System default configuration",
16
+ directories={
17
+ "inputs": DirectoryDefinition(
18
+ name="inputs",
19
+ path_schema="inputs",
20
+ ),
21
+ "outputs": DirectoryDefinition(
22
+ name="outputs",
23
+ path_schema="outputs",
24
+ ),
25
+ "temp": DirectoryDefinition(
26
+ name="temp",
27
+ path_schema="temp",
28
+ ),
29
+ "previews": DirectoryDefinition(
30
+ name="previews",
31
+ path_schema="previews",
32
+ ),
33
+ },
34
+ environment={},
35
+ situations={
36
+ "save_file": SituationTemplate(
37
+ name="save_file",
38
+ situation_template_schema_version="0.1.0",
39
+ description="Generic file save operation",
40
+ macro="{file_name_base}{_index?:03}.{file_extension}",
41
+ policy=SituationPolicy(
42
+ on_collision=SituationFilePolicy.CREATE_NEW,
43
+ create_dirs=True,
44
+ ),
45
+ fallback=None,
46
+ ),
47
+ "copy_external_file": SituationTemplate(
48
+ name="copy_external_file",
49
+ situation_template_schema_version="0.1.0",
50
+ description="User copies external file to project",
51
+ macro="{inputs}/{node_name?:_}{parameter_name?:_}{file_name_base}{_index?:03}.{file_extension}",
52
+ policy=SituationPolicy(
53
+ on_collision=SituationFilePolicy.CREATE_NEW,
54
+ create_dirs=True,
55
+ ),
56
+ fallback="save_file",
57
+ ),
58
+ "download_url": SituationTemplate(
59
+ name="download_url",
60
+ situation_template_schema_version="0.1.0",
61
+ description="Download file from URL",
62
+ macro="{inputs}/{sanitized_url}",
63
+ policy=SituationPolicy(
64
+ on_collision=SituationFilePolicy.OVERWRITE,
65
+ create_dirs=True,
66
+ ),
67
+ fallback="save_file",
68
+ ),
69
+ "save_node_output": SituationTemplate(
70
+ name="save_node_output",
71
+ situation_template_schema_version="0.1.0",
72
+ description="Node generates and saves output",
73
+ macro="{outputs}/{node_name?:_}{file_name_base}{_index?:03}.{file_extension}",
74
+ policy=SituationPolicy(
75
+ on_collision=SituationFilePolicy.CREATE_NEW,
76
+ create_dirs=True,
77
+ ),
78
+ fallback="save_file",
79
+ ),
80
+ "save_preview": SituationTemplate(
81
+ name="save_preview",
82
+ situation_template_schema_version="0.1.0",
83
+ description="Generate preview/thumbnail",
84
+ macro="{previews}/{original_file_path}",
85
+ policy=SituationPolicy(
86
+ on_collision=SituationFilePolicy.OVERWRITE,
87
+ create_dirs=True,
88
+ ),
89
+ fallback="save_file",
90
+ ),
91
+ },
92
+ )
@@ -0,0 +1,36 @@
1
+ # System Default Project Template
2
+
3
+ This directory contains the system default project template used when no `project.yml` exists in a project.
4
+
5
+ ## Files
6
+
7
+ - `project_template.yml` - Complete default project configuration with all situations
8
+
9
+ ## Validation
10
+
11
+ The default template is loaded and validated at module import time. If validation fails, the system will raise a RuntimeError with details about what's broken.
12
+
13
+ This ensures:
14
+
15
+ - Default template is always valid
16
+ - Typos/errors are caught immediately in CI/tests
17
+ - Users can rely on defaults being correct
18
+
19
+ ## Modifying Defaults
20
+
21
+ When modifying `project_template.yml`:
22
+
23
+ 1. Ensure all syntax is correct (YAML, macro templates, etc.)
24
+ 1. Run tests to validate changes
25
+ 1. Update version numbers if making breaking changes
26
+ 1. Update comments to keep documentation in sync
27
+
28
+ ## Usage
29
+
30
+ Users can copy this file to their project root as `project.yml` and customize it:
31
+
32
+ ```bash
33
+ cp defaults/project_template.yml /path/to/project/project.yml
34
+ ```
35
+
36
+ The system will automatically use the project-specific version instead of these defaults.