griptape-nodes 0.62.2__py3-none-any.whl → 0.63.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 (74) hide show
  1. griptape_nodes/cli/commands/libraries.py +6 -21
  2. griptape_nodes/drivers/thread_storage/__init__.py +15 -0
  3. griptape_nodes/drivers/thread_storage/base_thread_storage_driver.py +106 -0
  4. griptape_nodes/drivers/thread_storage/griptape_cloud_thread_storage_driver.py +213 -0
  5. griptape_nodes/drivers/thread_storage/local_thread_storage_driver.py +137 -0
  6. griptape_nodes/drivers/thread_storage/thread_storage_backend.py +10 -0
  7. griptape_nodes/node_library/library_registry.py +16 -9
  8. griptape_nodes/node_library/workflow_registry.py +1 -1
  9. griptape_nodes/retained_mode/events/agent_events.py +232 -9
  10. griptape_nodes/retained_mode/events/app_events.py +38 -0
  11. griptape_nodes/retained_mode/events/library_events.py +32 -3
  12. griptape_nodes/retained_mode/events/os_events.py +101 -1
  13. griptape_nodes/retained_mode/managers/agent_manager.py +335 -135
  14. griptape_nodes/retained_mode/managers/fitness_problems/__init__.py +1 -0
  15. griptape_nodes/retained_mode/managers/fitness_problems/libraries/__init__.py +59 -0
  16. griptape_nodes/retained_mode/managers/fitness_problems/libraries/advanced_library_load_failure_problem.py +33 -0
  17. griptape_nodes/retained_mode/managers/fitness_problems/libraries/after_library_callback_problem.py +32 -0
  18. griptape_nodes/retained_mode/managers/fitness_problems/libraries/before_library_callback_problem.py +32 -0
  19. griptape_nodes/retained_mode/managers/fitness_problems/libraries/create_config_category_problem.py +32 -0
  20. griptape_nodes/retained_mode/managers/fitness_problems/libraries/dependency_installation_failed_problem.py +32 -0
  21. griptape_nodes/retained_mode/managers/fitness_problems/libraries/deprecated_node_warning_problem.py +83 -0
  22. griptape_nodes/retained_mode/managers/fitness_problems/libraries/duplicate_library_problem.py +28 -0
  23. griptape_nodes/retained_mode/managers/fitness_problems/libraries/duplicate_node_registration_problem.py +44 -0
  24. griptape_nodes/retained_mode/managers/fitness_problems/libraries/engine_version_error_problem.py +28 -0
  25. griptape_nodes/retained_mode/managers/fitness_problems/libraries/insufficient_disk_space_problem.py +33 -0
  26. griptape_nodes/retained_mode/managers/fitness_problems/libraries/invalid_version_string_problem.py +32 -0
  27. griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_json_decode_problem.py +28 -0
  28. griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_load_exception_problem.py +32 -0
  29. griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_not_found_problem.py +30 -0
  30. griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_problem.py +20 -0
  31. griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_schema_exception_problem.py +32 -0
  32. griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_schema_validation_problem.py +38 -0
  33. griptape_nodes/retained_mode/managers/fitness_problems/libraries/modified_parameters_set_deprecation_warning_problem.py +44 -0
  34. griptape_nodes/retained_mode/managers/fitness_problems/libraries/modified_parameters_set_removed_problem.py +44 -0
  35. griptape_nodes/retained_mode/managers/fitness_problems/libraries/node_class_not_base_node_problem.py +40 -0
  36. griptape_nodes/retained_mode/managers/fitness_problems/libraries/node_class_not_found_problem.py +38 -0
  37. griptape_nodes/retained_mode/managers/fitness_problems/libraries/node_module_import_problem.py +53 -0
  38. griptape_nodes/retained_mode/managers/fitness_problems/libraries/sandbox_directory_missing_problem.py +28 -0
  39. griptape_nodes/retained_mode/managers/fitness_problems/libraries/ui_options_field_modified_incompatible_problem.py +44 -0
  40. griptape_nodes/retained_mode/managers/fitness_problems/libraries/ui_options_field_modified_warning_problem.py +35 -0
  41. griptape_nodes/retained_mode/managers/fitness_problems/libraries/update_config_category_problem.py +32 -0
  42. griptape_nodes/retained_mode/managers/fitness_problems/libraries/venv_creation_failed_problem.py +32 -0
  43. griptape_nodes/retained_mode/managers/fitness_problems/workflows/__init__.py +75 -0
  44. griptape_nodes/retained_mode/managers/fitness_problems/workflows/deprecated_node_in_workflow_problem.py +83 -0
  45. griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_dependency_version_string_problem.py +38 -0
  46. griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_library_version_string_problem.py +38 -0
  47. griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_metadata_schema_problem.py +31 -0
  48. griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_metadata_section_count_problem.py +31 -0
  49. griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_toml_format_problem.py +30 -0
  50. griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_not_registered_problem.py +35 -0
  51. griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_version_below_required_problem.py +41 -0
  52. griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_version_large_difference_problem.py +41 -0
  53. griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_version_major_mismatch_problem.py +41 -0
  54. griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_version_minor_difference_problem.py +41 -0
  55. griptape_nodes/retained_mode/managers/fitness_problems/workflows/missing_creation_date_problem.py +30 -0
  56. griptape_nodes/retained_mode/managers/fitness_problems/workflows/missing_last_modified_date_problem.py +30 -0
  57. griptape_nodes/retained_mode/managers/fitness_problems/workflows/missing_toml_section_problem.py +30 -0
  58. griptape_nodes/retained_mode/managers/fitness_problems/workflows/node_type_not_found_problem.py +51 -0
  59. griptape_nodes/retained_mode/managers/fitness_problems/workflows/workflow_not_found_problem.py +27 -0
  60. griptape_nodes/retained_mode/managers/fitness_problems/workflows/workflow_problem.py +20 -0
  61. griptape_nodes/retained_mode/managers/fitness_problems/workflows/workflow_schema_version_problem.py +39 -0
  62. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/local_file.py +17 -3
  63. griptape_nodes/retained_mode/managers/library_manager.py +226 -77
  64. griptape_nodes/retained_mode/managers/os_manager.py +172 -1
  65. griptape_nodes/retained_mode/managers/settings.py +5 -0
  66. griptape_nodes/retained_mode/managers/version_compatibility_manager.py +76 -51
  67. griptape_nodes/retained_mode/managers/workflow_manager.py +237 -159
  68. griptape_nodes/servers/static.py +18 -19
  69. griptape_nodes/version_compatibility/versions/v0_39_0/modified_parameters_set_removal.py +16 -12
  70. griptape_nodes/version_compatibility/workflow_versions/v0_7_0/local_executor_argument_addition.py +6 -3
  71. {griptape_nodes-0.62.2.dist-info → griptape_nodes-0.63.0.dist-info}/METADATA +2 -1
  72. {griptape_nodes-0.62.2.dist-info → griptape_nodes-0.63.0.dist-info}/RECORD +74 -21
  73. {griptape_nodes-0.62.2.dist-info → griptape_nodes-0.63.0.dist-info}/WHEEL +0 -0
  74. {griptape_nodes-0.62.2.dist-info → griptape_nodes-0.63.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from dataclasses import dataclass
5
+
6
+ from griptape_nodes.retained_mode.managers.fitness_problems.libraries.library_problem import LibraryProblem
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ @dataclass
12
+ class NodeClassNotFoundProblem(LibraryProblem):
13
+ """Problem indicating a node class was not found in its module.
14
+
15
+ This is stackable - multiple node classes can be missing.
16
+ """
17
+
18
+ class_name: str
19
+ file_path: str
20
+
21
+ @classmethod
22
+ def collate_problems_for_display(cls, instances: list[NodeClassNotFoundProblem]) -> str:
23
+ """Display node class not found problems.
24
+
25
+ Can handle multiple missing classes - they will be listed out sorted by class_name.
26
+ """
27
+ if len(instances) == 1:
28
+ problem = instances[0]
29
+ return f"Class '{problem.class_name}' not found in module '{problem.file_path}'"
30
+
31
+ # Multiple missing classes - list them sorted by class_name
32
+ sorted_instances = sorted(instances, key=lambda p: p.class_name)
33
+ error_lines = []
34
+ for i, problem in enumerate(sorted_instances, 1):
35
+ error_lines.append(f" {i}. Class '{problem.class_name}' not found in '{problem.file_path}'")
36
+
37
+ header = f"Encountered {len(instances)} node classes not found in their modules:"
38
+ return header + "\n" + "\n".join(error_lines)
@@ -0,0 +1,53 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from collections import defaultdict
5
+ from dataclasses import dataclass
6
+
7
+ from griptape_nodes.retained_mode.managers.fitness_problems.libraries.library_problem import LibraryProblem
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ @dataclass
13
+ class NodeModuleImportProblem(LibraryProblem):
14
+ """Problem indicating a node module could not be imported.
15
+
16
+ This is stackable - multiple node modules can fail to import.
17
+ """
18
+
19
+ class_name: str
20
+ file_path: str
21
+ error_message: str
22
+ root_cause: str
23
+
24
+ @classmethod
25
+ def collate_problems_for_display(cls, instances: list[NodeModuleImportProblem]) -> str:
26
+ """Display node module import problems.
27
+
28
+ Groups by root_cause and lists affected nodes under each cause.
29
+ """
30
+ if len(instances) == 1:
31
+ problem = instances[0]
32
+ return f"Failed to import module for node '{problem.class_name}': {problem.root_cause}"
33
+
34
+ # Group by root_cause
35
+ by_cause = defaultdict(list)
36
+ for problem in instances:
37
+ by_cause[problem.root_cause].append(problem)
38
+
39
+ # Sort root causes alphabetically
40
+ sorted_causes = sorted(by_cause.keys())
41
+
42
+ output_lines = []
43
+ output_lines.append(f"Encountered {len(instances)} node module import failures:")
44
+
45
+ for cause in sorted_causes:
46
+ nodes = by_cause[cause]
47
+ # Sort nodes by class_name within each cause group
48
+ nodes.sort(key=lambda p: p.class_name)
49
+
50
+ output_lines.append(f" {cause}:")
51
+ output_lines.extend(f" - {node.class_name}" for node in nodes)
52
+
53
+ return "\n".join(output_lines)
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from dataclasses import dataclass
5
+
6
+ from griptape_nodes.retained_mode.managers.fitness_problems.libraries.library_problem import LibraryProblem
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ @dataclass
12
+ class SandboxDirectoryMissingProblem(LibraryProblem):
13
+ """Problem indicating the sandbox directory does not exist."""
14
+
15
+ @classmethod
16
+ def collate_problems_for_display(cls, instances: list[SandboxDirectoryMissingProblem]) -> str:
17
+ """Display sandbox directory missing problem.
18
+
19
+ There should only be one instance per library since each LibraryInfo
20
+ is already associated with a specific library path.
21
+ """
22
+ if len(instances) > 1:
23
+ logger.error(
24
+ "SandboxDirectoryMissingProblem: Expected 1 instance but got %s. Each LibraryInfo should only have one SandboxDirectoryMissingProblem.",
25
+ len(instances),
26
+ )
27
+
28
+ return "Sandbox directory does not exist. If you wish to create a Sandbox directory to develop custom nodes: in the Griptape Nodes editor, go to Settings -> Libraries and navigate to the Sandbox Settings."
@@ -0,0 +1,44 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from dataclasses import dataclass
5
+
6
+ from griptape_nodes.retained_mode.managers.fitness_problems.libraries.library_problem import LibraryProblem
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ @dataclass
12
+ class UiOptionsFieldModifiedIncompatibleProblem(LibraryProblem):
13
+ """Problem indicating a library is incompatible due to ui_options field modification.
14
+
15
+ This is stackable - multiple libraries can have this issue.
16
+ This severity is UNUSABLE - the library cannot be loaded.
17
+ """
18
+
19
+ library_engine_version: str
20
+
21
+ @classmethod
22
+ def collate_problems_for_display(cls, instances: list[UiOptionsFieldModifiedIncompatibleProblem]) -> str:
23
+ """Display ui_options field modification incompatibility problems.
24
+
25
+ Can handle multiple instances - they will be listed out sorted by library_engine_version.
26
+ """
27
+ if len(instances) == 1:
28
+ version = instances[0].library_engine_version
29
+ return (
30
+ f"This library (built for engine version {version}) is incompatible with Griptape Nodes 0.39+."
31
+ "The 'ui_options' field has been modified on all Elements. In order to function properly, all nodes must update ui_options by setting its value to a new dictionary. Updating ui_options by accessing the private field _ui_options will no longer create UI updates in the editor."
32
+ "If this library accesses the private _ui_options field, it will not update the editor properly. Please update to a newer version of this library or contact the library author immediately."
33
+ )
34
+
35
+ # Multiple libraries with this issue - list them sorted by version
36
+ sorted_instances = sorted(instances, key=lambda p: p.library_engine_version)
37
+ error_lines = []
38
+ for i, problem in enumerate(sorted_instances, 1):
39
+ error_lines.append(
40
+ f" {i}. Library built for engine version {problem.library_engine_version} is incompatible due to ui_options field modification"
41
+ )
42
+
43
+ header = f"Encountered {len(instances)} libraries incompatible due to ui_options field modification:"
44
+ return header + "\n" + "\n".join(error_lines)
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from dataclasses import dataclass
5
+
6
+ from griptape_nodes.retained_mode.managers.fitness_problems.libraries.library_problem import LibraryProblem
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ @dataclass
12
+ class UiOptionsFieldModifiedWarningProblem(LibraryProblem):
13
+ """Problem warning that a library may not function properly due to ui_options field modification.
14
+
15
+ This is stackable - multiple libraries can have this warning.
16
+ This severity is FLAWED - the library can be loaded but has warnings.
17
+ """
18
+
19
+ @classmethod
20
+ def collate_problems_for_display(cls, instances: list[UiOptionsFieldModifiedWarningProblem]) -> str:
21
+ """Display ui_options field modification warnings.
22
+
23
+ Can handle multiple instances.
24
+ """
25
+ if len(instances) > 1:
26
+ logger.error(
27
+ "UiOptionsFieldModifiedWarningProblem: Expected 1 instance but got %s. This warning should only appear once per library.",
28
+ len(instances),
29
+ )
30
+
31
+ return (
32
+ "WARNING: The 'ui_options' field has been modified in Griptape Nodes 0.38 on all BaseNodeElements."
33
+ "In order to function properly, all nodes must update ui_options by setting its value to a new dictionary. Updating ui_options by accessing the private field _ui_options will no longer create UI updates in the editor."
34
+ "If this library accesses the private _ui_options field, it will not update the editor properly. Please update to a newer version of this library or contact the library author immediately."
35
+ )
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from dataclasses import dataclass
5
+
6
+ from griptape_nodes.retained_mode.managers.fitness_problems.libraries.library_problem import LibraryProblem
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ @dataclass
12
+ class UpdateConfigCategoryProblem(LibraryProblem):
13
+ """Problem indicating a config category failed to be updated."""
14
+
15
+ category_name: str
16
+
17
+ @classmethod
18
+ def collate_problems_for_display(cls, instances: list[UpdateConfigCategoryProblem]) -> str:
19
+ """Display config category update problem.
20
+
21
+ There should only be one instance per library since each LibraryInfo
22
+ is already associated with a specific library path.
23
+ """
24
+ if len(instances) > 1:
25
+ logger.error(
26
+ "UpdateConfigCategoryProblem: Expected 1 instance but got %s. Each LibraryInfo should only have one UpdateConfigCategoryProblem.",
27
+ len(instances),
28
+ )
29
+
30
+ # Use the first instance's category name
31
+ category = instances[0].category_name
32
+ return f"Failed to update config category '{category}'."
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from dataclasses import dataclass
5
+
6
+ from griptape_nodes.retained_mode.managers.fitness_problems.libraries.library_problem import LibraryProblem
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ @dataclass
12
+ class VenvCreationFailedProblem(LibraryProblem):
13
+ """Problem indicating virtual environment creation failed."""
14
+
15
+ error_message: str
16
+
17
+ @classmethod
18
+ def collate_problems_for_display(cls, instances: list[VenvCreationFailedProblem]) -> str:
19
+ """Display venv creation failed problem.
20
+
21
+ There should only be one instance per library since each LibraryInfo
22
+ is already associated with a specific library path.
23
+ """
24
+ if len(instances) > 1:
25
+ logger.error(
26
+ "VenvCreationFailedProblem: Expected 1 instance but got %s. Each LibraryInfo should only have one VenvCreationFailedProblem.",
27
+ len(instances),
28
+ )
29
+
30
+ # Use the first instance's error message
31
+ error_msg = instances[0].error_message
32
+ return f"Failed to create virtual environment for library dependencies. Error: {error_msg}"
@@ -0,0 +1,75 @@
1
+ """Workflow fitness problem classes."""
2
+
3
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.deprecated_node_in_workflow_problem import (
4
+ DeprecatedNodeInWorkflowProblem,
5
+ )
6
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.invalid_dependency_version_string_problem import (
7
+ InvalidDependencyVersionStringProblem,
8
+ )
9
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.invalid_library_version_string_problem import (
10
+ InvalidLibraryVersionStringProblem,
11
+ )
12
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.invalid_metadata_schema_problem import (
13
+ InvalidMetadataSchemaProblem,
14
+ )
15
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.invalid_metadata_section_count_problem import (
16
+ InvalidMetadataSectionCountProblem,
17
+ )
18
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.invalid_toml_format_problem import (
19
+ InvalidTomlFormatProblem,
20
+ )
21
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.library_not_registered_problem import (
22
+ LibraryNotRegisteredProblem,
23
+ )
24
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.library_version_below_required_problem import (
25
+ LibraryVersionBelowRequiredProblem,
26
+ )
27
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.library_version_large_difference_problem import (
28
+ LibraryVersionLargeDifferenceProblem,
29
+ )
30
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.library_version_major_mismatch_problem import (
31
+ LibraryVersionMajorMismatchProblem,
32
+ )
33
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.library_version_minor_difference_problem import (
34
+ LibraryVersionMinorDifferenceProblem,
35
+ )
36
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.missing_creation_date_problem import (
37
+ MissingCreationDateProblem,
38
+ )
39
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.missing_last_modified_date_problem import (
40
+ MissingLastModifiedDateProblem,
41
+ )
42
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.missing_toml_section_problem import (
43
+ MissingTomlSectionProblem,
44
+ )
45
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.node_type_not_found_problem import (
46
+ NodeTypeNotFoundProblem,
47
+ )
48
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.workflow_not_found_problem import (
49
+ WorkflowNotFoundProblem,
50
+ )
51
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.workflow_problem import WorkflowProblem
52
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.workflow_schema_version_problem import (
53
+ WorkflowSchemaVersionProblem,
54
+ )
55
+
56
+ __all__ = [
57
+ "DeprecatedNodeInWorkflowProblem",
58
+ "InvalidDependencyVersionStringProblem",
59
+ "InvalidLibraryVersionStringProblem",
60
+ "InvalidMetadataSchemaProblem",
61
+ "InvalidMetadataSectionCountProblem",
62
+ "InvalidTomlFormatProblem",
63
+ "LibraryNotRegisteredProblem",
64
+ "LibraryVersionBelowRequiredProblem",
65
+ "LibraryVersionLargeDifferenceProblem",
66
+ "LibraryVersionMajorMismatchProblem",
67
+ "LibraryVersionMinorDifferenceProblem",
68
+ "MissingCreationDateProblem",
69
+ "MissingLastModifiedDateProblem",
70
+ "MissingTomlSectionProblem",
71
+ "NodeTypeNotFoundProblem",
72
+ "WorkflowNotFoundProblem",
73
+ "WorkflowProblem",
74
+ "WorkflowSchemaVersionProblem",
75
+ ]
@@ -0,0 +1,83 @@
1
+ from __future__ import annotations
2
+
3
+ from collections import defaultdict
4
+ from dataclasses import dataclass
5
+
6
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.workflow_problem import WorkflowProblem
7
+
8
+
9
+ @dataclass
10
+ class DeprecatedNodeInWorkflowProblem(WorkflowProblem):
11
+ """Problem indicating a workflow uses a deprecated node.
12
+
13
+ This is stackable - workflows can use multiple deprecated nodes.
14
+ """
15
+
16
+ node_display_name: str
17
+ node_type: str
18
+ library_name: str
19
+ current_library_version: str
20
+ workflow_library_version: str | None
21
+ removal_version: str | None
22
+ deprecation_message: str | None
23
+
24
+ @classmethod
25
+ def collate_problems_for_display(cls, instances: list[DeprecatedNodeInWorkflowProblem]) -> str:
26
+ """Display deprecated node in workflow problems.
27
+
28
+ Groups by library, then by deprecation message within each library.
29
+ """
30
+ if len(instances) == 1:
31
+ problem = instances[0]
32
+ removal_info = (
33
+ f"removed in v{problem.removal_version}" if problem.removal_version else "may be removed in future"
34
+ )
35
+ message = (
36
+ f"Uses deprecated node '{problem.node_display_name}' from '{problem.library_name}' ({removal_info})"
37
+ )
38
+ if problem.deprecation_message:
39
+ message += f". {problem.deprecation_message}"
40
+ else:
41
+ message += ". No remediation steps provided by library author."
42
+ return message
43
+
44
+ # Group by library
45
+ by_library = defaultdict(list)
46
+ for problem in instances:
47
+ by_library[problem.library_name].append(problem)
48
+
49
+ # Sort libraries alphabetically
50
+ sorted_libraries = sorted(by_library.keys())
51
+
52
+ output_lines = []
53
+ output_lines.append(f"Uses {len(instances)} deprecated nodes:")
54
+
55
+ for library_name in sorted_libraries:
56
+ nodes = by_library[library_name]
57
+ output_lines.append(f" From '{library_name}':")
58
+
59
+ # Group nodes within this library by deprecation_message
60
+ by_message = defaultdict(list)
61
+ for node in nodes:
62
+ message_key = node.deprecation_message if node.deprecation_message else ""
63
+ by_message[message_key].append(node)
64
+
65
+ # Sort messages alphabetically (empty string first)
66
+ sorted_messages = sorted(by_message.keys(), key=lambda m: (m != "", m))
67
+
68
+ for message in sorted_messages:
69
+ message_nodes = by_message[message]
70
+ # Sort nodes by display_name within each message group
71
+ message_nodes.sort(key=lambda p: p.node_display_name)
72
+
73
+ if message:
74
+ output_lines.append(f" {message}:")
75
+ output_lines.extend(f" - {node.node_display_name}" for node in message_nodes)
76
+ else:
77
+ # No remediation steps provided
78
+ output_lines.extend(
79
+ f" - {node.node_display_name} (no remediation steps provided by library author)"
80
+ for node in message_nodes
81
+ )
82
+
83
+ return "\n".join(output_lines)
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.workflow_problem import WorkflowProblem
6
+
7
+
8
+ @dataclass
9
+ class InvalidDependencyVersionStringProblem(WorkflowProblem):
10
+ """Problem indicating workflow cited an invalid version string for a library dependency.
11
+
12
+ This is stackable - multiple libraries can have invalid version strings.
13
+ """
14
+
15
+ library_name: str
16
+ version_string: str
17
+
18
+ @classmethod
19
+ def collate_problems_for_display(cls, instances: list[InvalidDependencyVersionStringProblem]) -> str:
20
+ """Display invalid dependency version string problems.
21
+
22
+ Sorts by library_name and lists all affected libraries.
23
+ """
24
+ if len(instances) == 1:
25
+ problem = instances[0]
26
+ return f"Workflow has invalid version string '{problem.version_string}' for '{problem.library_name}'. Must be major.minor.patch format."
27
+
28
+ # Sort by library_name
29
+ sorted_instances = sorted(instances, key=lambda p: p.library_name)
30
+
31
+ output_lines = []
32
+ output_lines.append(
33
+ f"Workflow has {len(instances)} invalid dependency version strings (must be major.minor.patch format):"
34
+ )
35
+ for i, problem in enumerate(sorted_instances, 1):
36
+ output_lines.append(f" {i}. {problem.library_name}: '{problem.version_string}'")
37
+
38
+ return "\n".join(output_lines)
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.workflow_problem import WorkflowProblem
6
+
7
+
8
+ @dataclass
9
+ class InvalidLibraryVersionStringProblem(WorkflowProblem):
10
+ """Problem indicating a registered library has an invalid version string.
11
+
12
+ This is stackable - multiple libraries can have invalid version strings.
13
+ """
14
+
15
+ library_name: str
16
+ version_string: str
17
+
18
+ @classmethod
19
+ def collate_problems_for_display(cls, instances: list[InvalidLibraryVersionStringProblem]) -> str:
20
+ """Display invalid library version string problems.
21
+
22
+ Sorts by library_name and lists all affected libraries.
23
+ """
24
+ if len(instances) == 1:
25
+ problem = instances[0]
26
+ return f"'{problem.library_name}' has invalid version string '{problem.version_string}'. Must be major.minor.patch format."
27
+
28
+ # Sort by library_name
29
+ sorted_instances = sorted(instances, key=lambda p: p.library_name)
30
+
31
+ output_lines = []
32
+ output_lines.append(
33
+ f"{len(instances)} libraries with invalid version strings (must be major.minor.patch format):"
34
+ )
35
+ for i, problem in enumerate(sorted_instances, 1):
36
+ output_lines.append(f" {i}. {problem.library_name}: '{problem.version_string}'")
37
+
38
+ return "\n".join(output_lines)
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from dataclasses import dataclass
5
+
6
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.workflow_problem import WorkflowProblem
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ @dataclass
12
+ class InvalidMetadataSchemaProblem(WorkflowProblem):
13
+ """Problem indicating workflow metadata doesn't match required schema.
14
+
15
+ This is one-time only - should only occur once per workflow.
16
+ """
17
+
18
+ section_path: str
19
+ error_message: str
20
+
21
+ @classmethod
22
+ def collate_problems_for_display(cls, instances: list[InvalidMetadataSchemaProblem]) -> str:
23
+ """Display invalid metadata schema problem."""
24
+ if len(instances) > 1:
25
+ logger.error(
26
+ "InvalidMetadataSchemaProblem received %d instances but should only receive 1. This indicates a logic error.",
27
+ len(instances),
28
+ )
29
+
30
+ problem = instances[0]
31
+ return f"'{problem.section_path}' metadata doesn't match schema: {problem.error_message}"
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from dataclasses import dataclass
5
+
6
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.workflow_problem import WorkflowProblem
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ @dataclass
12
+ class InvalidMetadataSectionCountProblem(WorkflowProblem):
13
+ """Problem indicating workflow has wrong number of metadata sections.
14
+
15
+ This is one-time only - should only occur once per workflow.
16
+ """
17
+
18
+ section_name: str
19
+ count: int
20
+
21
+ @classmethod
22
+ def collate_problems_for_display(cls, instances: list[InvalidMetadataSectionCountProblem]) -> str:
23
+ """Display invalid metadata section count problem."""
24
+ if len(instances) > 1:
25
+ logger.error(
26
+ "InvalidMetadataSectionCountProblem received %d instances but should only receive 1. This indicates a logic error.",
27
+ len(instances),
28
+ )
29
+
30
+ problem = instances[0]
31
+ return f"Has {problem.count} '{problem.section_name}' sections. Expected exactly 1."
@@ -0,0 +1,30 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from dataclasses import dataclass
5
+
6
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.workflow_problem import WorkflowProblem
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ @dataclass
12
+ class InvalidTomlFormatProblem(WorkflowProblem):
13
+ """Problem indicating workflow metadata is not valid TOML.
14
+
15
+ This is one-time only - should only occur once per workflow.
16
+ """
17
+
18
+ error_message: str
19
+
20
+ @classmethod
21
+ def collate_problems_for_display(cls, instances: list[InvalidTomlFormatProblem]) -> str:
22
+ """Display invalid TOML format problem."""
23
+ if len(instances) > 1:
24
+ logger.error(
25
+ "InvalidTomlFormatProblem received %d instances but should only receive 1. This indicates a logic error.",
26
+ len(instances),
27
+ )
28
+
29
+ problem = instances[0]
30
+ return f"Metadata not valid TOML: {problem.error_message}"
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.workflow_problem import WorkflowProblem
6
+
7
+
8
+ @dataclass
9
+ class LibraryNotRegisteredProblem(WorkflowProblem):
10
+ """Problem indicating a required library was not successfully registered.
11
+
12
+ This is stackable - multiple libraries can fail to register.
13
+ """
14
+
15
+ library_name: str
16
+
17
+ @classmethod
18
+ def collate_problems_for_display(cls, instances: list[LibraryNotRegisteredProblem]) -> str:
19
+ """Display library not registered problems.
20
+
21
+ Sorts by library_name and lists all affected libraries.
22
+ """
23
+ if len(instances) == 1:
24
+ problem = instances[0]
25
+ return f"'{problem.library_name}' not registered. May have other problems preventing load."
26
+
27
+ # Sort by library_name
28
+ sorted_instances = sorted(instances, key=lambda p: p.library_name)
29
+
30
+ output_lines = []
31
+ output_lines.append(f"{len(instances)} libraries not registered (may have other problems preventing load):")
32
+ for i, problem in enumerate(sorted_instances, 1):
33
+ output_lines.append(f" {i}. {problem.library_name}")
34
+
35
+ return "\n".join(output_lines)