griptape-nodes 0.71.0__tar.gz → 0.72.0__tar.gz
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-0.71.0 → griptape_nodes-0.72.0}/PKG-INFO +2 -1
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/pyproject.toml +3 -4
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/app/app.py +4 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +10 -1
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/workflow_executors/utils/subprocess_script.py +4 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/workflow_publishers/utils/subprocess_script.py +4 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/node_executor.py +1 -1
- griptape_nodes-0.72.0/src/griptape_nodes/drivers/image_metadata/__init__.py +21 -0
- griptape_nodes-0.72.0/src/griptape_nodes/drivers/image_metadata/base_image_metadata_driver.py +63 -0
- griptape_nodes-0.72.0/src/griptape_nodes/drivers/image_metadata/exif_metadata_driver.py +218 -0
- griptape_nodes-0.72.0/src/griptape_nodes/drivers/image_metadata/image_metadata_driver_registry.py +55 -0
- griptape_nodes-0.72.0/src/griptape_nodes/drivers/image_metadata/png_metadata_driver.py +71 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/drivers/storage/base_storage_driver.py +32 -0
- griptape_nodes-0.72.0/src/griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +575 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/drivers/storage/local_storage_driver.py +65 -4
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/drivers/thread_storage/local_thread_storage_driver.py +1 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/node_groups/base_node_group.py +3 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/node_types.py +13 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_components/log_parameter.py +3 -2
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_types/parameter_float.py +4 -4
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_types/parameter_int.py +4 -4
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_types/parameter_number.py +34 -30
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/node_library/workflow_registry.py +5 -8
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/app_events.py +1 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/base_events.py +42 -26
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/flow_events.py +67 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/library_events.py +1 -1
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/node_events.py +1 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/os_events.py +22 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/static_file_events.py +28 -4
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/flow_manager.py +134 -0
- griptape_nodes-0.72.0/src/griptape_nodes/retained_mode/managers/image_metadata_injector.py +339 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/library_manager.py +71 -41
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/model_manager.py +1 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/node_manager.py +8 -5
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/os_manager.py +269 -32
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/project_manager.py +3 -7
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/session_manager.py +1 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/settings.py +5 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/static_files_manager.py +83 -17
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/workflow_manager.py +71 -41
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/servers/static.py +31 -0
- griptape_nodes-0.72.0/src/griptape_nodes/traits/clamp.py +79 -0
- griptape_nodes-0.72.0/src/griptape_nodes/utils/__init__.py +13 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/utils/file_utils.py +13 -13
- griptape_nodes-0.72.0/src/griptape_nodes/utils/http_file_patch.py +613 -0
- griptape_nodes-0.72.0/src/griptape_nodes/utils/path_utils.py +58 -0
- griptape_nodes-0.72.0/src/griptape_nodes/utils/url_utils.py +106 -0
- griptape_nodes-0.71.0/src/griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +0 -201
- griptape_nodes-0.71.0/src/griptape_nodes/traits/clamp.py +0 -36
- griptape_nodes-0.71.0/src/griptape_nodes/utils/__init__.py +0 -5
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/README.md +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/__main__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/api_client/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/api_client/client.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/api_client/request_client.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/app/.python-version +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/app/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/app/watch.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/utils/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/utils/python_subprocess_executor.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/utils/subprocess_websocket_base.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/utils/subprocess_websocket_listener.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/utils/subprocess_websocket_sender.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/workflow_executors/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/workflow_executors/local_session_workflow_executor.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/workflow_executors/subprocess_workflow_executor.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/workflow_executors/utils/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/workflow_publishers/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/workflow_publishers/local_session_workflow_publisher.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/workflow_publishers/local_workflow_publisher.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/workflow_publishers/subprocess_workflow_publisher.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/bootstrap/workflow_publishers/utils/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/cli/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/cli/commands/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/cli/commands/config.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/cli/commands/engine.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/cli/commands/init.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/cli/commands/libraries.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/cli/commands/models.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/cli/commands/self.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/cli/main.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/cli/shared.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/directed_graph.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/macro_parser/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/macro_parser/core.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/macro_parser/exceptions.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/macro_parser/formats.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/macro_parser/matching.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/macro_parser/parsing.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/macro_parser/resolution.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/macro_parser/segments.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/project_templates/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/project_templates/default_project_template.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/project_templates/defaults/README.md +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/project_templates/directory.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/project_templates/loader.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/project_templates/project.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/project_templates/situation.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/common/project_templates/validation.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/drivers/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/drivers/storage/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/drivers/storage/storage_backend.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/drivers/thread_storage/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/drivers/thread_storage/base_thread_storage_driver.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/drivers/thread_storage/griptape_cloud_thread_storage_driver.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/drivers/thread_storage/thread_storage_backend.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/base_iterative_nodes.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/connections.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/core_types.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/flow.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/node_groups/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/node_groups/base_iterative_node_group.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/node_groups/subflow_node_group.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_components/README.md +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_components/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_components/api_key_provider_parameter.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_components/artifact_url/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_components/artifact_url/public_artifact_url_parameter.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_components/execution_status_component.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_components/huggingface/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_model_parameter.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_file_parameter.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_parameter.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_variant_parameter.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_utils.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_components/progress_bar_component.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_components/seed_parameter.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_components/subflow_execution_component.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_types/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_types/parameter_audio.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_types/parameter_bool.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_types/parameter_button.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_types/parameter_dict.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_types/parameter_image.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_types/parameter_json.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_types/parameter_range.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_types/parameter_string.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_types/parameter_three_d.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/param_types/parameter_video.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/exe_types/type_validator.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/machines/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/machines/control_flow.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/machines/dag_builder.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/machines/fsm.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/machines/parallel_resolution.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/machines/sequential_resolution.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/mcp_server/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/node_library/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/node_library/advanced_node_library.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/node_library/library_registry.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/py.typed +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/agent_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/arbitrary_python_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/config_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/connection_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/context_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/execution_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/generate_request_payload_schemas.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/logger_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/mcp_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/model_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/object_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/parameter_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/payload_registry.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/project_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/resource_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/secrets_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/sync_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/validation_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/variable_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/events/workflow_events.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/griptape_nodes.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/agent_manager.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/config_manager.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/context_manager.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/engine_identity_manager.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/event_manager.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/advanced_library_load_failure_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/after_library_callback_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/before_library_callback_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/create_config_category_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/dependency_installation_failed_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/deprecated_node_warning_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/duplicate_library_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/duplicate_node_registration_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/engine_version_error_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/incompatible_requirements_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/insufficient_disk_space_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/invalid_version_string_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_json_decode_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_load_exception_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_not_found_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_schema_exception_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/library_schema_validation_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/modified_parameters_set_deprecation_warning_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/modified_parameters_set_removed_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/node_class_not_base_node_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/node_class_not_found_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/node_module_import_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/old_xdg_location_warning_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/sandbox_directory_missing_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/ui_options_field_modified_incompatible_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/ui_options_field_modified_warning_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/update_config_category_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/libraries/venv_creation_failed_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/deprecated_node_in_workflow_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_dependency_version_string_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_library_version_string_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_metadata_schema_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_metadata_section_count_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/invalid_toml_format_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_not_registered_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_version_below_required_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_version_large_difference_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_version_major_mismatch_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/library_version_minor_difference_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/missing_creation_date_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/missing_last_modified_date_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/missing_toml_section_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/node_type_not_found_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/workflow_not_found_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/workflow_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/fitness_problems/workflows/workflow_schema_version_problem.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/mcp_manager.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/object_manager.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/operation_manager.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/resource_components/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/resource_components/capability_field.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/resource_components/comparator.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/resource_components/resource_instance.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/resource_components/resource_type.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/resource_manager.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/resource_types/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/resource_types/compute_resource.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/resource_types/cpu_resource.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/resource_types/os_resource.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/secrets_manager.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/sync_manager.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/user_manager.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/variable_manager.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/managers/version_compatibility_manager.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/retained_mode.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/utils/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/utils/name_generator.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/retained_mode/variable_types.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/servers/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/servers/mcp.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/traits/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/traits/add_param_button.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/traits/button.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/traits/color_picker.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/traits/compare.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/traits/compare_images.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/traits/file_system_picker.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/traits/minmax.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/traits/multi_options.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/traits/numbers_selector.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/traits/options.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/traits/slider.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/traits/trait_registry.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/traits/traits.json +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/updater/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/updater/__main__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/utils/artifact_normalization.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/utils/async_utils.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/utils/dict_utils.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/utils/git_utils.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/utils/huggingface_utils.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/utils/image_preview.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/utils/library_utils.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/utils/metaclasses.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/utils/uv_utils.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/utils/version_utils.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/version_compatibility/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/version_compatibility/versions/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/version_compatibility/versions/v0_39_0/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/version_compatibility/versions/v0_39_0/modified_parameters_set_removal.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/version_compatibility/versions/v0_63_8/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/version_compatibility/versions/v0_63_8/deprecated_nodegroup_parameters.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/version_compatibility/versions/v0_65_4/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/version_compatibility/versions/v0_65_4/run_in_parallel_to_run_in_order.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/version_compatibility/versions/v0_65_5/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/version_compatibility/versions/v0_65_5/flux_2_removed_parameters.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/version_compatibility/versions/v0_7_0/__init__.py +0 -0
- {griptape_nodes-0.71.0 → griptape_nodes-0.72.0}/src/griptape_nodes/version_compatibility/versions/v0_7_0/local_executor_argument_addition.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: griptape-nodes
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.72.0
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Requires-Dist: griptape>=1.9.0
|
|
6
6
|
Requires-Dist: pydantic>=2.10.6
|
|
@@ -29,6 +29,7 @@ Requires-Dist: ruamel-yaml>=0.18.15
|
|
|
29
29
|
Requires-Dist: asyncio-thread-runner>=1.0
|
|
30
30
|
Requires-Dist: portalocker>=2.10.0
|
|
31
31
|
Requires-Dist: pygit2>=1.19.0
|
|
32
|
+
Requires-Dist: send2trash>=2.1.0
|
|
32
33
|
Requires-Dist: austin-dist>=3.7.0 ; extra == 'profiling'
|
|
33
34
|
Requires-Python: >=3.12.0, <3.13
|
|
34
35
|
Provides-Extra: profiling
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "griptape-nodes"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.72.0"
|
|
4
4
|
description = "Add your description here"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12.0, <3.13"
|
|
@@ -33,6 +33,7 @@ dependencies = [
|
|
|
33
33
|
"asyncio-thread-runner>=1.0",
|
|
34
34
|
"portalocker>=2.10.0",
|
|
35
35
|
"pygit2>=1.19.0",
|
|
36
|
+
"send2trash>=2.1.0",
|
|
36
37
|
]
|
|
37
38
|
|
|
38
39
|
[project.optional-dependencies]
|
|
@@ -124,9 +125,7 @@ mypy-init-return = true
|
|
|
124
125
|
convention = "google"
|
|
125
126
|
|
|
126
127
|
[tool.typos]
|
|
127
|
-
files.extend-exclude = [
|
|
128
|
-
"libraries/**/templates/*",
|
|
129
|
-
]
|
|
128
|
+
files.extend-exclude = ["libraries/**/templates/*"]
|
|
130
129
|
default.extend-ignore-re = [
|
|
131
130
|
# Ignore any line that ends with this comment:
|
|
132
131
|
"(?Rm)^.*(#|//)\\s*spellchecker:disable-line$",
|
|
@@ -30,6 +30,7 @@ from griptape_nodes.retained_mode.events.base_events import (
|
|
|
30
30
|
)
|
|
31
31
|
from griptape_nodes.retained_mode.events.logger_events import LogHandlerEvent
|
|
32
32
|
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
33
|
+
from griptape_nodes.utils import install_file_url_support
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
# WebSocket thread communication message types
|
|
@@ -78,6 +79,9 @@ REQUEST_SEMAPHORE = asyncio.Semaphore(100)
|
|
|
78
79
|
|
|
79
80
|
config_manager = GriptapeNodes.ConfigManager()
|
|
80
81
|
|
|
82
|
+
# Install file:// URL support for httpx/requests
|
|
83
|
+
install_file_url_support()
|
|
84
|
+
|
|
81
85
|
|
|
82
86
|
class EventLogHandler(logging.Handler):
|
|
83
87
|
"""Custom logging handler that emits log messages as AppEvents.
|
|
@@ -32,14 +32,23 @@ class LocalWorkflowExecutor(WorkflowExecutor):
|
|
|
32
32
|
def __init__(
|
|
33
33
|
self,
|
|
34
34
|
storage_backend: StorageBackend = StorageBackend.LOCAL,
|
|
35
|
+
*,
|
|
36
|
+
skip_library_loading: bool = False,
|
|
37
|
+
workflows_to_register: list[str] | None = None,
|
|
35
38
|
):
|
|
36
39
|
super().__init__()
|
|
37
40
|
self._set_storage_backend(storage_backend=storage_backend)
|
|
41
|
+
self._skip_library_loading = skip_library_loading
|
|
42
|
+
self._workflows_to_register = workflows_to_register or []
|
|
38
43
|
|
|
39
44
|
async def __aenter__(self) -> Self:
|
|
40
45
|
"""Async context manager entry: initialize queue and broadcast app initialization."""
|
|
41
46
|
GriptapeNodes.EventManager().initialize_queue()
|
|
42
|
-
await GriptapeNodes.EventManager().broadcast_app_event(
|
|
47
|
+
await GriptapeNodes.EventManager().broadcast_app_event(
|
|
48
|
+
AppInitializationComplete(
|
|
49
|
+
skip_library_loading=self._skip_library_loading, workflows_to_register=self._workflows_to_register
|
|
50
|
+
)
|
|
51
|
+
)
|
|
43
52
|
return self
|
|
44
53
|
|
|
45
54
|
async def __aexit__(
|
|
@@ -10,6 +10,10 @@ from workflow import execute_workflow # type: ignore[attr-defined]
|
|
|
10
10
|
|
|
11
11
|
from griptape_nodes.bootstrap.workflow_executors.local_session_workflow_executor import LocalSessionWorkflowExecutor
|
|
12
12
|
from griptape_nodes.drivers.storage import StorageBackend
|
|
13
|
+
from griptape_nodes.utils import install_file_url_support
|
|
14
|
+
|
|
15
|
+
# Install file:// URL support for httpx/requests in subprocess
|
|
16
|
+
install_file_url_support()
|
|
13
17
|
|
|
14
18
|
|
|
15
19
|
def _main() -> None:
|
|
@@ -7,6 +7,10 @@ from griptape_nodes.bootstrap.workflow_publishers.local_session_workflow_publish
|
|
|
7
7
|
LocalSessionWorkflowPublisher,
|
|
8
8
|
)
|
|
9
9
|
from griptape_nodes.bootstrap.workflow_publishers.local_workflow_publisher import LocalWorkflowPublisher
|
|
10
|
+
from griptape_nodes.utils import install_file_url_support
|
|
11
|
+
|
|
12
|
+
# Install file:// URL support for httpx/requests in subprocess
|
|
13
|
+
install_file_url_support()
|
|
10
14
|
|
|
11
15
|
logging.basicConfig(level=logging.INFO)
|
|
12
16
|
|
|
@@ -3232,7 +3232,7 @@ class NodeExecutor:
|
|
|
3232
3232
|
WorkflowRegistry.generate_new_workflow(str(workflow_path), result.metadata)
|
|
3233
3233
|
|
|
3234
3234
|
delete_request = DeleteWorkflowRequest(name=workflow_name)
|
|
3235
|
-
delete_result = GriptapeNodes.
|
|
3235
|
+
delete_result = await GriptapeNodes.ahandle_request(delete_request)
|
|
3236
3236
|
if isinstance(delete_result, DeleteWorkflowResultFailure):
|
|
3237
3237
|
logger.error(
|
|
3238
3238
|
"Failed to delete workflow '%s'. Error: %s",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Image metadata injection drivers.
|
|
2
|
+
|
|
3
|
+
Provides pluggable drivers for injecting workflow metadata into different image formats.
|
|
4
|
+
Drivers are automatically registered on module import.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from griptape_nodes.drivers.image_metadata.base_image_metadata_driver import BaseImageMetadataDriver
|
|
8
|
+
from griptape_nodes.drivers.image_metadata.exif_metadata_driver import ExifMetadataDriver
|
|
9
|
+
from griptape_nodes.drivers.image_metadata.image_metadata_driver_registry import ImageMetadataDriverRegistry
|
|
10
|
+
from griptape_nodes.drivers.image_metadata.png_metadata_driver import PngMetadataDriver
|
|
11
|
+
|
|
12
|
+
# Register core drivers on import
|
|
13
|
+
ImageMetadataDriverRegistry.register_driver(PngMetadataDriver())
|
|
14
|
+
ImageMetadataDriverRegistry.register_driver(ExifMetadataDriver())
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"BaseImageMetadataDriver",
|
|
18
|
+
"ExifMetadataDriver",
|
|
19
|
+
"ImageMetadataDriverRegistry",
|
|
20
|
+
"PngMetadataDriver",
|
|
21
|
+
]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Base class for image metadata injection and extraction drivers."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
from PIL import Image
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BaseImageMetadataDriver(ABC):
|
|
9
|
+
"""Base class for bidirectional image metadata drivers.
|
|
10
|
+
|
|
11
|
+
Each driver handles a specific metadata protocol (e.g., PNG text chunks, EXIF).
|
|
12
|
+
Drivers support both injection (writing) and extraction (reading) of metadata.
|
|
13
|
+
|
|
14
|
+
Extraction returns ALL available metadata for the format.
|
|
15
|
+
Injection writes only custom key-value pairs, preserving existing metadata.
|
|
16
|
+
|
|
17
|
+
Drivers are registered with ImageMetadataDriverRegistry and selected based on image format.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def get_supported_formats(self) -> list[str]:
|
|
22
|
+
"""Return list of PIL format strings this driver supports.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
List of format strings (e.g., ["PNG"], ["JPEG", "TIFF", "MPO"])
|
|
26
|
+
"""
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def inject_metadata(self, pil_image: Image.Image, metadata: dict[str, str]) -> bytes:
|
|
31
|
+
"""Inject metadata into image and return modified image bytes.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
pil_image: PIL Image object to inject metadata into
|
|
35
|
+
metadata: Dictionary of key-value string pairs to inject
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Image bytes with metadata injected
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
Exception: On metadata injection failures
|
|
42
|
+
"""
|
|
43
|
+
...
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def extract_metadata(self, pil_image: Image.Image) -> dict[str, str]:
|
|
47
|
+
"""Extract ALL metadata from image.
|
|
48
|
+
|
|
49
|
+
Returns all available metadata for the format:
|
|
50
|
+
- PNG: All text chunks
|
|
51
|
+
- EXIF: Standard tags, GPS data, and custom UserComment field
|
|
52
|
+
|
|
53
|
+
The amount and type of metadata depends on the format and what's
|
|
54
|
+
present in the image. Custom metadata injected via inject_metadata()
|
|
55
|
+
is included alongside format-specific metadata.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
pil_image: PIL Image object to extract metadata from
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Dictionary of metadata key-value pairs, empty dict if none found
|
|
62
|
+
"""
|
|
63
|
+
...
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""EXIF metadata injection and extraction driver for JPEG, TIFF, and MPO formats."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
from io import BytesIO
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from PIL import Image
|
|
9
|
+
from PIL.ExifTags import GPSTAGS, TAGS
|
|
10
|
+
|
|
11
|
+
from griptape_nodes.drivers.image_metadata.base_image_metadata_driver import BaseImageMetadataDriver
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger("griptape_nodes")
|
|
14
|
+
|
|
15
|
+
# EXIF tag IDs
|
|
16
|
+
EXIF_USERCOMMENT_TAG = 0x9286
|
|
17
|
+
EXIF_GPSINFO_TAG = 0x8825 # 34853
|
|
18
|
+
|
|
19
|
+
# GPS coordinate tuple length
|
|
20
|
+
GPS_COORD_TUPLE_LENGTH = 3
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ExifMetadataDriver(BaseImageMetadataDriver):
|
|
24
|
+
"""Bidirectional driver for EXIF metadata.
|
|
25
|
+
|
|
26
|
+
Supports reading ALL EXIF metadata (standard tags, GPS data, custom UserComment)
|
|
27
|
+
and writing custom metadata to EXIF UserComment field as JSON.
|
|
28
|
+
Preserves all existing EXIF data when writing.
|
|
29
|
+
All EXIF-specific logic is encapsulated in this driver.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def get_supported_formats(self) -> list[str]:
|
|
33
|
+
"""Return list of PIL format strings this driver supports.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
List containing "JPEG", "TIFF", "MPO"
|
|
37
|
+
"""
|
|
38
|
+
return ["JPEG", "TIFF", "MPO", "WEBP"]
|
|
39
|
+
|
|
40
|
+
def inject_metadata(self, pil_image: Image.Image, metadata: dict[str, str]) -> bytes:
|
|
41
|
+
"""Inject metadata into EXIF UserComment field as JSON.
|
|
42
|
+
|
|
43
|
+
Serializes metadata dictionary to JSON and stores in EXIF UserComment.
|
|
44
|
+
Preserves all existing EXIF tags.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
pil_image: PIL Image to inject metadata into
|
|
48
|
+
metadata: Dictionary of key-value pairs to inject
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Image bytes with metadata injected
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
Exception: On EXIF save errors
|
|
55
|
+
"""
|
|
56
|
+
# Get existing EXIF data to preserve it
|
|
57
|
+
exif_data = pil_image.getexif()
|
|
58
|
+
|
|
59
|
+
# Serialize metadata to JSON for UserComment field
|
|
60
|
+
metadata_json = json.dumps(metadata, separators=(",", ":"))
|
|
61
|
+
|
|
62
|
+
# Set UserComment field
|
|
63
|
+
exif_data[EXIF_USERCOMMENT_TAG] = metadata_json
|
|
64
|
+
|
|
65
|
+
# Save with updated EXIF
|
|
66
|
+
output_buffer = BytesIO()
|
|
67
|
+
pil_image.save(output_buffer, format=pil_image.format, exif=exif_data)
|
|
68
|
+
return output_buffer.getvalue()
|
|
69
|
+
|
|
70
|
+
def extract_metadata(self, pil_image: Image.Image) -> dict[str, str]:
|
|
71
|
+
"""Extract ALL EXIF metadata including standard tags, GPS data, and custom UserComment.
|
|
72
|
+
|
|
73
|
+
Returns combined metadata from all sources:
|
|
74
|
+
- Standard EXIF tags (Make, Model, DateTime, etc.)
|
|
75
|
+
- GPS metadata (prefixed with 'GPS_')
|
|
76
|
+
- Custom metadata from UserComment field (JSON parsed)
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
pil_image: PIL Image to extract metadata from
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Dictionary of all metadata key-value pairs, empty dict if no EXIF data
|
|
83
|
+
"""
|
|
84
|
+
exif_data = pil_image.getexif()
|
|
85
|
+
if not exif_data:
|
|
86
|
+
return {}
|
|
87
|
+
|
|
88
|
+
metadata = {}
|
|
89
|
+
|
|
90
|
+
# Extract standard EXIF tags
|
|
91
|
+
self._extract_standard_tags(exif_data, metadata)
|
|
92
|
+
|
|
93
|
+
# Extract GPS metadata
|
|
94
|
+
self._extract_gps_metadata(exif_data, metadata)
|
|
95
|
+
|
|
96
|
+
# Extract custom UserComment metadata
|
|
97
|
+
self._extract_user_comment_metadata(exif_data, metadata)
|
|
98
|
+
|
|
99
|
+
return metadata
|
|
100
|
+
|
|
101
|
+
def _exif_value_to_string(self, value: Any) -> str:
|
|
102
|
+
"""Convert EXIF value to readable string format.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
value: EXIF value (can be bytes, tuple, list, int, etc.)
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
String representation of the value
|
|
109
|
+
"""
|
|
110
|
+
if isinstance(value, bytes):
|
|
111
|
+
try:
|
|
112
|
+
return value.decode("utf-8", errors="ignore").strip("\x00")
|
|
113
|
+
except Exception:
|
|
114
|
+
return str(value)
|
|
115
|
+
elif isinstance(value, (tuple, list)):
|
|
116
|
+
return ", ".join(str(v) for v in value)
|
|
117
|
+
return str(value)
|
|
118
|
+
|
|
119
|
+
def _format_gps_coordinate(self, coord_tuple: tuple) -> str:
|
|
120
|
+
"""Format GPS coordinate tuple to decimal degrees string.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
coord_tuple: Tuple of (degrees, minutes, seconds) as rational numbers
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Decimal degrees as string
|
|
127
|
+
"""
|
|
128
|
+
if not coord_tuple or len(coord_tuple) != GPS_COORD_TUPLE_LENGTH:
|
|
129
|
+
return str(coord_tuple)
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
degrees = float(coord_tuple[0])
|
|
133
|
+
minutes = float(coord_tuple[1])
|
|
134
|
+
seconds = float(coord_tuple[2])
|
|
135
|
+
|
|
136
|
+
decimal_degrees = degrees + (minutes / 60.0) + (seconds / 3600.0)
|
|
137
|
+
except Exception:
|
|
138
|
+
return self._exif_value_to_string(coord_tuple)
|
|
139
|
+
else:
|
|
140
|
+
return f"{decimal_degrees:.6f}"
|
|
141
|
+
|
|
142
|
+
def _extract_standard_tags(self, exif_data: Any, metadata: dict[str, str]) -> None:
|
|
143
|
+
"""Extract standard EXIF tags.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
exif_data: EXIF data object from PIL
|
|
147
|
+
metadata: Dictionary to populate with standard tags (modified in-place)
|
|
148
|
+
"""
|
|
149
|
+
for tag_id, value in exif_data.items():
|
|
150
|
+
# Get tag name
|
|
151
|
+
tag_name = TAGS.get(tag_id, f"Tag_{tag_id}")
|
|
152
|
+
|
|
153
|
+
# Skip UserComment and GPSInfo, we'll handle them separately
|
|
154
|
+
if tag_id in (EXIF_USERCOMMENT_TAG, EXIF_GPSINFO_TAG):
|
|
155
|
+
continue
|
|
156
|
+
|
|
157
|
+
# Convert value to string
|
|
158
|
+
metadata[tag_name] = self._exif_value_to_string(value)
|
|
159
|
+
|
|
160
|
+
def _extract_gps_metadata(self, exif_data: Any, metadata: dict[str, str]) -> None:
|
|
161
|
+
"""Extract GPS metadata from EXIF data.
|
|
162
|
+
|
|
163
|
+
Formats GPS coordinates as decimal degrees and prefixes all GPS tags with 'GPS_'.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
exif_data: EXIF data object from PIL
|
|
167
|
+
metadata: Dictionary to populate with GPS metadata (modified in-place)
|
|
168
|
+
"""
|
|
169
|
+
gps_ifd = exif_data.get_ifd(EXIF_GPSINFO_TAG)
|
|
170
|
+
if not gps_ifd:
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
for gps_tag_id, gps_value in gps_ifd.items():
|
|
174
|
+
gps_tag_name = GPSTAGS.get(gps_tag_id, f"GPSTag_{gps_tag_id}")
|
|
175
|
+
|
|
176
|
+
# Format GPS coordinate values specially
|
|
177
|
+
if gps_tag_id in (1, 2, 3, 4): # GPSLatitudeRef, GPSLatitude, GPSLongitudeRef, GPSLongitude
|
|
178
|
+
if gps_tag_id in (2, 4): # Latitude or Longitude tuple
|
|
179
|
+
metadata[f"GPS_{gps_tag_name}"] = self._format_gps_coordinate(gps_value)
|
|
180
|
+
else:
|
|
181
|
+
metadata[f"GPS_{gps_tag_name}"] = str(gps_value)
|
|
182
|
+
else:
|
|
183
|
+
metadata[f"GPS_{gps_tag_name}"] = self._exif_value_to_string(gps_value)
|
|
184
|
+
|
|
185
|
+
def _extract_user_comment_metadata(self, exif_data: Any, metadata: dict[str, str]) -> None:
|
|
186
|
+
"""Extract custom metadata from EXIF UserComment field.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
exif_data: EXIF data object from PIL
|
|
190
|
+
metadata: Dictionary to populate with custom metadata (modified in-place)
|
|
191
|
+
"""
|
|
192
|
+
user_comment = exif_data.get(EXIF_USERCOMMENT_TAG)
|
|
193
|
+
if not user_comment:
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
# Parse JSON from UserComment field
|
|
197
|
+
try:
|
|
198
|
+
# Pillow handles the character code prefix, but might return bytes or string
|
|
199
|
+
if isinstance(user_comment, bytes):
|
|
200
|
+
comment_str = user_comment.decode("utf-8", errors="ignore").strip("\x00")
|
|
201
|
+
else:
|
|
202
|
+
comment_str = str(user_comment)
|
|
203
|
+
|
|
204
|
+
custom_metadata = json.loads(comment_str)
|
|
205
|
+
if not isinstance(custom_metadata, dict):
|
|
206
|
+
logger.debug("UserComment is not a dict, skipping custom metadata")
|
|
207
|
+
return
|
|
208
|
+
|
|
209
|
+
# Merge custom metadata directly into metadata dict
|
|
210
|
+
for key, value in custom_metadata.items():
|
|
211
|
+
metadata[key] = str(value)
|
|
212
|
+
|
|
213
|
+
if custom_metadata:
|
|
214
|
+
logger.debug("Merged %d custom metadata entries from EXIF UserComment", len(custom_metadata))
|
|
215
|
+
except (json.JSONDecodeError, ValueError) as e:
|
|
216
|
+
logger.debug("Could not parse UserComment as JSON: %s", e)
|
|
217
|
+
except Exception as e:
|
|
218
|
+
logger.debug("Unexpected error parsing UserComment: %s", e)
|
griptape_nodes-0.72.0/src/griptape_nodes/drivers/image_metadata/image_metadata_driver_registry.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Registry for image metadata injection drivers."""
|
|
2
|
+
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
5
|
+
from griptape_nodes.drivers.image_metadata.base_image_metadata_driver import BaseImageMetadataDriver
|
|
6
|
+
from griptape_nodes.utils.metaclasses import SingletonMeta
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ImageMetadataDriverRegistry(metaclass=SingletonMeta):
|
|
10
|
+
"""Registry for image metadata injection drivers.
|
|
11
|
+
|
|
12
|
+
Provides centralized registration and lookup of metadata injection drivers
|
|
13
|
+
based on image format. Follows singleton pattern to ensure single registry instance.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
_drivers: ClassVar[list[BaseImageMetadataDriver]] = []
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def register_driver(cls, driver: BaseImageMetadataDriver) -> None:
|
|
20
|
+
"""Register a metadata injection driver.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
driver: Driver instance to register
|
|
24
|
+
"""
|
|
25
|
+
instance = cls()
|
|
26
|
+
instance._drivers.append(driver)
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def get_driver_for_format(cls, format_str: str) -> BaseImageMetadataDriver | None:
|
|
30
|
+
"""Get the first driver that supports the given format.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
format_str: PIL format string (e.g., "PNG", "JPEG")
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Driver instance or None if no driver supports format
|
|
37
|
+
"""
|
|
38
|
+
instance = cls()
|
|
39
|
+
for driver in instance._drivers:
|
|
40
|
+
if format_str in driver.get_supported_formats():
|
|
41
|
+
return driver
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def get_supported_formats(cls) -> set[str]:
|
|
46
|
+
"""Get all formats supported by registered drivers.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Set of format strings supported by any registered driver
|
|
50
|
+
"""
|
|
51
|
+
instance = cls()
|
|
52
|
+
formats = set()
|
|
53
|
+
for driver in instance._drivers:
|
|
54
|
+
formats.update(driver.get_supported_formats())
|
|
55
|
+
return formats
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""PNG metadata injection and extraction driver using text chunks."""
|
|
2
|
+
|
|
3
|
+
from io import BytesIO
|
|
4
|
+
|
|
5
|
+
from PIL import Image, PngImagePlugin
|
|
6
|
+
|
|
7
|
+
from griptape_nodes.drivers.image_metadata.base_image_metadata_driver import BaseImageMetadataDriver
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PngMetadataDriver(BaseImageMetadataDriver):
|
|
11
|
+
"""Bidirectional driver for PNG metadata using text chunks.
|
|
12
|
+
|
|
13
|
+
Supports both reading and writing metadata in PNG text chunks.
|
|
14
|
+
Preserves existing text chunks when writing new metadata.
|
|
15
|
+
All PNG-specific logic is encapsulated in this driver.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def get_supported_formats(self) -> list[str]:
|
|
19
|
+
"""Return list of PIL format strings this driver supports.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
List containing "PNG"
|
|
23
|
+
"""
|
|
24
|
+
return ["PNG"]
|
|
25
|
+
|
|
26
|
+
def inject_metadata(self, pil_image: Image.Image, metadata: dict[str, str]) -> bytes:
|
|
27
|
+
"""Inject metadata into PNG text chunks.
|
|
28
|
+
|
|
29
|
+
Creates PngInfo with existing text chunks and adds new metadata.
|
|
30
|
+
New metadata overwrites existing keys with same name.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
pil_image: PIL Image to inject metadata into
|
|
34
|
+
metadata: Dictionary of key-value pairs to inject
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Image bytes with metadata injected
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
Exception: On PNG save errors
|
|
41
|
+
"""
|
|
42
|
+
# Create PNG info object
|
|
43
|
+
png_info = PngImagePlugin.PngInfo()
|
|
44
|
+
|
|
45
|
+
# Preserve existing text chunks
|
|
46
|
+
for key, value in pil_image.info.items():
|
|
47
|
+
# Only preserve string key-value pairs (text chunks), skip binary data
|
|
48
|
+
# Don't preserve keys that will be overwritten by new metadata
|
|
49
|
+
if isinstance(key, str) and isinstance(value, str) and key not in metadata:
|
|
50
|
+
png_info.add_text(key, value)
|
|
51
|
+
|
|
52
|
+
# Add new metadata
|
|
53
|
+
for key, value in metadata.items():
|
|
54
|
+
png_info.add_text(key, str(value))
|
|
55
|
+
|
|
56
|
+
# Save with metadata
|
|
57
|
+
output_buffer = BytesIO()
|
|
58
|
+
pil_image.save(output_buffer, format="PNG", pnginfo=png_info)
|
|
59
|
+
return output_buffer.getvalue()
|
|
60
|
+
|
|
61
|
+
def extract_metadata(self, pil_image: Image.Image) -> dict[str, str]:
|
|
62
|
+
"""Extract all text chunks from PNG image.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
pil_image: PIL Image to extract metadata from
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Dictionary of all PNG text chunks, empty dict if none found
|
|
69
|
+
"""
|
|
70
|
+
# PIL unpacks PNG text chunks directly into pil_image.info
|
|
71
|
+
return {key: value for key, value in pil_image.info.items() if isinstance(key, str) and isinstance(value, str)}
|
|
@@ -75,6 +75,38 @@ class BaseStorageDriver(ABC):
|
|
|
75
75
|
"""
|
|
76
76
|
...
|
|
77
77
|
|
|
78
|
+
@abstractmethod
|
|
79
|
+
def get_asset_url(self, path: Path) -> str:
|
|
80
|
+
"""Get the permanent unsigned URL for an asset.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
path: The path of the file
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Permanent URL for accessing the asset
|
|
87
|
+
"""
|
|
88
|
+
...
|
|
89
|
+
|
|
90
|
+
@abstractmethod
|
|
91
|
+
def save_file(
|
|
92
|
+
self, path: Path, file_content: bytes, existing_file_policy: ExistingFilePolicy = ExistingFilePolicy.OVERWRITE
|
|
93
|
+
) -> str:
|
|
94
|
+
"""Save a file to storage.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
path: The path of the file to save.
|
|
98
|
+
file_content: The file content as bytes.
|
|
99
|
+
existing_file_policy: How to handle existing files. Defaults to OVERWRITE.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
The absolute file path where the file was saved.
|
|
103
|
+
|
|
104
|
+
Raises:
|
|
105
|
+
FileExistsError: When existing_file_policy is FAIL and file already exists.
|
|
106
|
+
RuntimeError: If file save fails.
|
|
107
|
+
"""
|
|
108
|
+
...
|
|
109
|
+
|
|
78
110
|
def upload_file(
|
|
79
111
|
self, path: Path, file_content: bytes, existing_file_policy: ExistingFilePolicy = ExistingFilePolicy.OVERWRITE
|
|
80
112
|
) -> str:
|