griptape-nodes 0.59.2__tar.gz → 0.60.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.59.2 → griptape_nodes-0.60.0}/PKG-INFO +1 -1
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/pyproject.toml +1 -1
- griptape_nodes-0.60.0/src/griptape_nodes/common/macro_parser/__init__.py +28 -0
- griptape_nodes-0.60.0/src/griptape_nodes/common/macro_parser/core.py +230 -0
- griptape_nodes-0.60.0/src/griptape_nodes/common/macro_parser/exceptions.py +23 -0
- griptape_nodes-0.60.0/src/griptape_nodes/common/macro_parser/formats.py +170 -0
- griptape_nodes-0.60.0/src/griptape_nodes/common/macro_parser/matching.py +134 -0
- griptape_nodes-0.60.0/src/griptape_nodes/common/macro_parser/parsing.py +172 -0
- griptape_nodes-0.60.0/src/griptape_nodes/common/macro_parser/resolution.py +168 -0
- griptape_nodes-0.60.0/src/griptape_nodes/common/macro_parser/segments.py +42 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/exe_types/core_types.py +241 -4
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/exe_types/node_types.py +7 -1
- griptape_nodes-0.60.0/src/griptape_nodes/exe_types/param_components/huggingface/__init__.py +1 -0
- griptape_nodes-0.60.0/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_model_parameter.py +168 -0
- griptape_nodes-0.60.0/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_file_parameter.py +38 -0
- griptape_nodes-0.60.0/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_parameter.py +33 -0
- griptape_nodes-0.60.0/src/griptape_nodes/exe_types/param_components/huggingface/huggingface_utils.py +136 -0
- griptape_nodes-0.60.0/src/griptape_nodes/exe_types/param_components/log_parameter.py +136 -0
- griptape_nodes-0.60.0/src/griptape_nodes/exe_types/param_components/seed_parameter.py +59 -0
- griptape_nodes-0.60.0/src/griptape_nodes/exe_types/param_types/__init__.py +1 -0
- griptape_nodes-0.60.0/src/griptape_nodes/exe_types/param_types/parameter_bool.py +221 -0
- griptape_nodes-0.60.0/src/griptape_nodes/exe_types/param_types/parameter_float.py +179 -0
- griptape_nodes-0.60.0/src/griptape_nodes/exe_types/param_types/parameter_int.py +183 -0
- griptape_nodes-0.60.0/src/griptape_nodes/exe_types/param_types/parameter_number.py +380 -0
- griptape_nodes-0.60.0/src/griptape_nodes/exe_types/param_types/parameter_string.py +232 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/node_library/library_registry.py +2 -1
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/app_events.py +21 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/os_events.py +142 -6
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/parameter_events.py +2 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/griptape_nodes.py +14 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/agent_manager.py +5 -3
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +19 -1
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/library_manager.py +27 -32
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/node_manager.py +14 -1
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/os_manager.py +403 -124
- griptape_nodes-0.60.0/src/griptape_nodes/retained_mode/managers/user_manager.py +120 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/workflow_manager.py +44 -34
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/traits/multi_options.py +26 -2
- griptape_nodes-0.60.0/src/griptape_nodes/utils/huggingface_utils.py +136 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/README.md +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/__main__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/api_client/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/api_client/client.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/api_client/request_client.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/app/.python-version +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/app/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/app/app.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/app/watch.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/bootstrap/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/bootstrap/utils/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/bootstrap/utils/python_subprocess_executor.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/bootstrap/workflow_executors/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/bootstrap/workflow_executors/local_session_workflow_executor.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/bootstrap/workflow_executors/subprocess_workflow_executor.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/bootstrap/workflow_executors/utils/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/bootstrap/workflow_executors/utils/subprocess_script.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/bootstrap/workflow_publishers/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/bootstrap/workflow_publishers/local_workflow_publisher.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/bootstrap/workflow_publishers/subprocess_workflow_publisher.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/bootstrap/workflow_publishers/utils/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/bootstrap/workflow_publishers/utils/subprocess_script.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/cli/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/cli/commands/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/cli/commands/config.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/cli/commands/engine.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/cli/commands/init.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/cli/commands/libraries.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/cli/commands/models.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/cli/commands/self.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/cli/main.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/cli/shared.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/common/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/common/directed_graph.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/common/node_executor.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/drivers/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/drivers/storage/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/drivers/storage/base_storage_driver.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/drivers/storage/local_storage_driver.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/drivers/storage/storage_backend.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/exe_types/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/exe_types/connections.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/exe_types/flow.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/exe_types/param_components/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/exe_types/param_components/execution_status_component.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/exe_types/type_validator.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/machines/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/machines/control_flow.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/machines/dag_builder.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/machines/fsm.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/machines/parallel_resolution.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/machines/sequential_resolution.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/mcp_server/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/node_library/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/node_library/advanced_node_library.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/node_library/workflow_registry.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/py.typed +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/agent_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/arbitrary_python_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/base_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/config_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/connection_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/context_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/execution_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/flow_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/generate_request_payload_schemas.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/library_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/logger_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/mcp_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/model_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/node_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/object_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/payload_registry.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/resource_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/secrets_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/static_file_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/sync_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/validation_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/variable_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/events/workflow_events.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/config_manager.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/context_manager.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/engine_identity_manager.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/event_manager.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/flow_manager.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/data_models.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_fsm.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/base.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/github.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/local_file.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/package.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/sandbox.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/library_lifecycle/library_status.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/mcp_manager.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/model_manager.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/object_manager.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/operation_manager.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/resource_components/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/resource_components/capability_field.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/resource_components/comparator.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/resource_components/resource_instance.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/resource_components/resource_type.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/resource_manager.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/resource_types/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/resource_types/cpu_resource.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/resource_types/os_resource.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/secrets_manager.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/session_manager.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/settings.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/static_files_manager.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/sync_manager.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/variable_manager.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/managers/version_compatibility_manager.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/retained_mode.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/utils/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/utils/name_generator.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/retained_mode/variable_types.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/servers/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/servers/mcp.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/servers/static.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/traits/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/traits/add_param_button.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/traits/button.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/traits/clamp.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/traits/color_picker.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/traits/compare.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/traits/compare_images.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/traits/file_system_picker.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/traits/minmax.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/traits/numbers_selector.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/traits/options.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/traits/slider.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/traits/trait_registry.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/traits/traits.json +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/updater/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/updater/__main__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/utils/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/utils/async_utils.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/utils/dict_utils.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/utils/image_preview.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/utils/metaclasses.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/utils/uv_utils.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/utils/version_utils.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/version_compatibility/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/version_compatibility/versions/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/version_compatibility/versions/v0_39_0/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/version_compatibility/versions/v0_39_0/modified_parameters_set_removal.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/version_compatibility/workflow_versions/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/version_compatibility/workflow_versions/v0_7_0/__init__.py +0 -0
- {griptape_nodes-0.59.2 → griptape_nodes-0.60.0}/src/griptape_nodes/version_compatibility/workflow_versions/v0_7_0/local_executor_argument_addition.py +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Macro language parser for template-based path generation."""
|
|
2
|
+
|
|
3
|
+
from griptape_nodes.common.macro_parser.core import ParsedMacro
|
|
4
|
+
from griptape_nodes.common.macro_parser.exceptions import MacroResolutionError, MacroSyntaxError
|
|
5
|
+
from griptape_nodes.common.macro_parser.formats import (
|
|
6
|
+
DateFormat,
|
|
7
|
+
LowerCaseFormat,
|
|
8
|
+
NumericPaddingFormat,
|
|
9
|
+
SeparatorFormat,
|
|
10
|
+
SlugFormat,
|
|
11
|
+
UpperCaseFormat,
|
|
12
|
+
)
|
|
13
|
+
from griptape_nodes.common.macro_parser.segments import ParsedStaticValue, ParsedVariable, VariableInfo
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"DateFormat",
|
|
17
|
+
"LowerCaseFormat",
|
|
18
|
+
"MacroResolutionError",
|
|
19
|
+
"MacroSyntaxError",
|
|
20
|
+
"NumericPaddingFormat",
|
|
21
|
+
"ParsedMacro",
|
|
22
|
+
"ParsedStaticValue",
|
|
23
|
+
"ParsedVariable",
|
|
24
|
+
"SeparatorFormat",
|
|
25
|
+
"SlugFormat",
|
|
26
|
+
"UpperCaseFormat",
|
|
27
|
+
"VariableInfo",
|
|
28
|
+
]
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"""Core ParsedMacro class - main API for macro templates."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from griptape_nodes.common.macro_parser.exceptions import MacroResolutionError, MacroSyntaxError
|
|
8
|
+
from griptape_nodes.common.macro_parser.matching import extract_unknown_variables
|
|
9
|
+
from griptape_nodes.common.macro_parser.parsing import parse_segments
|
|
10
|
+
from griptape_nodes.common.macro_parser.resolution import partial_resolve
|
|
11
|
+
from griptape_nodes.common.macro_parser.segments import ParsedStaticValue, ParsedVariable, VariableInfo
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from griptape_nodes.retained_mode.managers.secrets_manager import SecretsManager
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ParsedMacro:
|
|
18
|
+
"""Parsed macro template with methods for resolving and matching paths.
|
|
19
|
+
|
|
20
|
+
This is the main API class for working with macro templates.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, template: str) -> None:
|
|
24
|
+
"""Parse a macro template string, validating syntax."""
|
|
25
|
+
self.template = template
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
segments = parse_segments(template)
|
|
29
|
+
except MacroSyntaxError as err:
|
|
30
|
+
msg = f"Attempted to parse template string '{template}'. Failed due to: {err}"
|
|
31
|
+
raise MacroSyntaxError(msg) from err
|
|
32
|
+
|
|
33
|
+
if not segments:
|
|
34
|
+
segments.append(ParsedStaticValue(text=""))
|
|
35
|
+
self.segments = segments
|
|
36
|
+
|
|
37
|
+
def get_variables(self) -> list[VariableInfo]:
|
|
38
|
+
"""Extract all VariableInfo from parsed segments."""
|
|
39
|
+
return [seg.info for seg in self.segments if isinstance(seg, ParsedVariable)]
|
|
40
|
+
|
|
41
|
+
def resolve(
|
|
42
|
+
self,
|
|
43
|
+
variables: dict[str, str | int],
|
|
44
|
+
secrets_manager: SecretsManager,
|
|
45
|
+
) -> str:
|
|
46
|
+
"""Fully resolve the macro template with variable values."""
|
|
47
|
+
# Partially resolve with known variables
|
|
48
|
+
partial = partial_resolve(self.template, self.segments, variables, secrets_manager)
|
|
49
|
+
|
|
50
|
+
# Check if fully resolved
|
|
51
|
+
if not partial.is_fully_resolved():
|
|
52
|
+
unresolved = partial.get_unresolved_variables()
|
|
53
|
+
unresolved_names = [var.info.name for var in unresolved]
|
|
54
|
+
msg = f"Cannot fully resolve macro - missing required variables: {', '.join(unresolved_names)}"
|
|
55
|
+
raise MacroResolutionError(msg)
|
|
56
|
+
|
|
57
|
+
# Convert to string
|
|
58
|
+
return partial.to_string()
|
|
59
|
+
|
|
60
|
+
def matches(
|
|
61
|
+
self,
|
|
62
|
+
path: str,
|
|
63
|
+
known_variables: dict[str, str | int],
|
|
64
|
+
secrets_manager: SecretsManager,
|
|
65
|
+
) -> bool:
|
|
66
|
+
"""Check if a path matches this template."""
|
|
67
|
+
result = self.find_matches_detailed(path, known_variables, secrets_manager)
|
|
68
|
+
return result is not None
|
|
69
|
+
|
|
70
|
+
def extract_variables(
|
|
71
|
+
self,
|
|
72
|
+
path: str,
|
|
73
|
+
known_variables: dict[str, str | int],
|
|
74
|
+
secrets_manager: SecretsManager,
|
|
75
|
+
) -> dict[str, str | int] | None:
|
|
76
|
+
"""Extract variable values from a path (plain string keys)."""
|
|
77
|
+
detailed = self.find_matches_detailed(path, known_variables, secrets_manager)
|
|
78
|
+
if detailed is None:
|
|
79
|
+
return None
|
|
80
|
+
# Convert VariableInfo keys to plain string keys
|
|
81
|
+
return {var_info.name: value for var_info, value in detailed.items()}
|
|
82
|
+
|
|
83
|
+
def find_matches_detailed(
|
|
84
|
+
self,
|
|
85
|
+
path: str,
|
|
86
|
+
known_variables: dict[str, str | int],
|
|
87
|
+
secrets_manager: SecretsManager,
|
|
88
|
+
) -> dict[VariableInfo, str | int] | None:
|
|
89
|
+
"""Extract variable values from a path with metadata (greedy match).
|
|
90
|
+
|
|
91
|
+
This is the advanced version that returns detailed variable metadata with VariableInfo keys.
|
|
92
|
+
Most callers should use extract_variables() for plain dict or matches() for boolean check.
|
|
93
|
+
|
|
94
|
+
Given a parsed template and a path, extracts variable values by matching
|
|
95
|
+
the path against the template pattern. Known variables are resolved before
|
|
96
|
+
matching to reduce ambiguity. Uses greedy matching strategy to return a single
|
|
97
|
+
result instead of exploring all possible interpretations.
|
|
98
|
+
|
|
99
|
+
MATCHING SCENARIOS (how this method handles different cases):
|
|
100
|
+
|
|
101
|
+
Scenario A: All variables known, path matches
|
|
102
|
+
Template: "{inputs}/{file_name}"
|
|
103
|
+
Known: {"inputs": "inputs", "file_name": "photo.jpg"}
|
|
104
|
+
Path: "inputs/photo.jpg"
|
|
105
|
+
Result: {"inputs": "inputs", "file_name": "photo.jpg"}
|
|
106
|
+
Flow: Step 1 → fully resolved → Step 2 → exact match → return result
|
|
107
|
+
|
|
108
|
+
Scenario B: All variables known, path doesn't match
|
|
109
|
+
Template: "{inputs}/{file_name}"
|
|
110
|
+
Known: {"inputs": "inputs", "file_name": "photo.jpg"}
|
|
111
|
+
Path: "outputs/photo.jpg"
|
|
112
|
+
Result: None
|
|
113
|
+
Flow: Step 1 → fully resolved → Step 2 → no match → return None
|
|
114
|
+
|
|
115
|
+
Scenario C: Some variables known, path matches
|
|
116
|
+
Template: "{inputs}/{workflow_name}/{file_name}"
|
|
117
|
+
Known: {"inputs": "inputs"}
|
|
118
|
+
Path: "inputs/my_workflow/photo.jpg"
|
|
119
|
+
Result: {"inputs": "inputs", "workflow_name": "my_workflow", "file_name": "photo.jpg"}
|
|
120
|
+
Flow: Step 1 → partial resolve → Step 2 skipped → Step 3 → static validated
|
|
121
|
+
→ Step 4 → extract unknowns (workflow_name, file_name) → merge with knowns → return
|
|
122
|
+
|
|
123
|
+
Scenario D: Some variables known, known variable value doesn't match path
|
|
124
|
+
Template: "{inputs}/{workflow_name}/{file_name}"
|
|
125
|
+
Known: {"inputs": "outputs"}
|
|
126
|
+
Path: "inputs/my_workflow/photo.jpg"
|
|
127
|
+
Result: None
|
|
128
|
+
Flow: Step 1 → partial resolve → Step 2 skipped → Step 3 → static mismatch → return None
|
|
129
|
+
|
|
130
|
+
Scenario E: Optional variable present in path
|
|
131
|
+
Template: "{inputs}/{workflow_name?:_}{file_name}"
|
|
132
|
+
Known: {"inputs": "inputs"}
|
|
133
|
+
Path: "inputs/my_workflow_photo.jpg"
|
|
134
|
+
Result: {"inputs": "inputs", "workflow_name": "my_workflow", "file_name": "photo.jpg"}
|
|
135
|
+
Flow: Step 1 → partial resolve → Step 2 skipped → Step 3 → validated
|
|
136
|
+
→ Step 4 → extract with separator matching → return
|
|
137
|
+
|
|
138
|
+
Scenario F: Optional variable omitted from path
|
|
139
|
+
Template: "{inputs}/{workflow_name?:_}{file_name}"
|
|
140
|
+
Known: {"inputs": "inputs"}
|
|
141
|
+
Path: "inputs/photo.jpg"
|
|
142
|
+
Result: {"inputs": "inputs", "file_name": "photo.jpg"}
|
|
143
|
+
Flow: Step 1 → partial resolve (optional removed) → Step 2 skipped → Step 3 → validated
|
|
144
|
+
→ Step 4 → extract file_name only → return
|
|
145
|
+
|
|
146
|
+
Scenario G: Multiple unknowns with delimiters
|
|
147
|
+
Template: "{inputs}/{dir}/{file_name}.{ext}"
|
|
148
|
+
Known: {"inputs": "inputs"}
|
|
149
|
+
Path: "inputs/render/output.png"
|
|
150
|
+
Result: {"inputs": "inputs", "dir": "render", "file_name": "output", "ext": "png"}
|
|
151
|
+
Flow: Step 1 → partial resolve → Step 2 skipped → Step 3 → validated
|
|
152
|
+
→ Step 4 → extract dir, file_name, ext using "/" and "." delimiters → return
|
|
153
|
+
|
|
154
|
+
Scenario H: Format spec reversal (numeric padding)
|
|
155
|
+
Template: "{inputs}/{frame:03}.png"
|
|
156
|
+
Known: {"inputs": "inputs"}
|
|
157
|
+
Path: "inputs/005.png"
|
|
158
|
+
Result: {"inputs": "inputs", "frame": 5} # Note: integer value
|
|
159
|
+
Flow: Step 1 → partial resolve → Step 2 skipped → Step 3 → validated
|
|
160
|
+
→ Step 4 → extract "005", reverse format spec → 5 → return
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
path: Actual path string to match against template
|
|
164
|
+
known_variables: Dictionary of variables with known values. These will be
|
|
165
|
+
resolved before matching to reduce ambiguity. Pass empty
|
|
166
|
+
dict {} if no variables are known.
|
|
167
|
+
secrets_manager: SecretsManager instance for resolving env vars in known variables
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Dictionary mapping VariableInfo to extracted values, or None if path doesn't
|
|
171
|
+
match the template pattern. Uses greedy matching to return a single result.
|
|
172
|
+
"""
|
|
173
|
+
# STEP 1: Partial resolve - resolve known variables into static text
|
|
174
|
+
# This reduces the matching problem from "match everything" to "match only the unknowns"
|
|
175
|
+
#
|
|
176
|
+
# Scenarios affected:
|
|
177
|
+
# - All scenarios: always runs first
|
|
178
|
+
# - Scenarios A, B: will be fully resolved (all variables known)
|
|
179
|
+
# - Scenarios C-H: will have mix of static and unknown variables
|
|
180
|
+
# - Scenarios E, F: optional variables not in known_variables are removed
|
|
181
|
+
partial = partial_resolve(self.template, self.segments, known_variables, secrets_manager)
|
|
182
|
+
|
|
183
|
+
# STEP 2: Check if fully resolved (all variables were known)
|
|
184
|
+
# If so, we can do a direct string comparison
|
|
185
|
+
#
|
|
186
|
+
# Scenarios affected:
|
|
187
|
+
# - Scenario A: fully resolved, path matches → return result dict
|
|
188
|
+
# - Scenario B: fully resolved, path doesn't match → return None
|
|
189
|
+
# - Scenarios C-H: NOT fully resolved, skip this step
|
|
190
|
+
if partial.is_fully_resolved():
|
|
191
|
+
resolved_path = partial.to_string()
|
|
192
|
+
if resolved_path == path:
|
|
193
|
+
# Scenario A: exact match
|
|
194
|
+
result: dict[VariableInfo, str | int] = {}
|
|
195
|
+
for segment in self.segments:
|
|
196
|
+
if isinstance(segment, ParsedVariable) and segment.info.name in known_variables:
|
|
197
|
+
result[segment.info] = known_variables[segment.info.name]
|
|
198
|
+
return result
|
|
199
|
+
# Scenario B: no match
|
|
200
|
+
return None
|
|
201
|
+
|
|
202
|
+
# STEP 3: Extract unknown variables from path
|
|
203
|
+
# Use static segments as anchors to extract variable values between them
|
|
204
|
+
#
|
|
205
|
+
# Scenarios affected:
|
|
206
|
+
# - Scenario C: extract workflow_name="my_workflow", file_name="photo.jpg"
|
|
207
|
+
# - Scenario D: static "outputs" doesn't match "inputs/" → return None
|
|
208
|
+
# - Scenario E: extract workflow_name="my_workflow", file_name="photo.jpg" (separator matched)
|
|
209
|
+
# - Scenario F: extract file_name="photo.jpg" (optional was removed in Step 1)
|
|
210
|
+
# - Scenario G: extract dir="render", file_name="output", ext="png" (multiple delimiters)
|
|
211
|
+
# - Scenario H: extract frame="005", reverse format spec → 5
|
|
212
|
+
extracted = extract_unknown_variables(partial.segments, path)
|
|
213
|
+
if extracted is None:
|
|
214
|
+
# Extraction failed (static segments don't match or can't extract variables)
|
|
215
|
+
return None
|
|
216
|
+
|
|
217
|
+
# STEP 4: Merge extracted unknowns with known variables to create complete result
|
|
218
|
+
# The extracted dict contains only extracted unknowns, need to add knowns back in
|
|
219
|
+
#
|
|
220
|
+
# Scenarios affected (D was eliminated in Step 3):
|
|
221
|
+
# - Scenario C: merge inputs="inputs" → final: {inputs, workflow_name, file_name}
|
|
222
|
+
# - Scenario E: merge inputs="inputs" → final: {inputs, workflow_name, file_name}
|
|
223
|
+
# - Scenario F: merge inputs="inputs" → final: {inputs, file_name}
|
|
224
|
+
# - Scenario G: merge inputs="inputs" → final: {inputs, dir, file_name, ext}
|
|
225
|
+
# - Scenario H: merge inputs="inputs" → final: {inputs, frame}
|
|
226
|
+
for segment in self.segments:
|
|
227
|
+
if isinstance(segment, ParsedVariable) and segment.info.name in known_variables:
|
|
228
|
+
extracted[segment.info] = known_variables[segment.info.name]
|
|
229
|
+
|
|
230
|
+
return extracted
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Exceptions for macro language parsing and resolution."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MacroSyntaxError(Exception):
|
|
7
|
+
"""Raised when macro template has invalid syntax.
|
|
8
|
+
|
|
9
|
+
Examples of syntax errors:
|
|
10
|
+
- Unbalanced braces: "{inputs}/{file_name"
|
|
11
|
+
- Invalid format specifier: "{index:xyz}"
|
|
12
|
+
- Nested braces: "{outer_{inner}}"
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MacroResolutionError(Exception):
|
|
17
|
+
"""Raised when macro cannot be resolved with provided variables.
|
|
18
|
+
|
|
19
|
+
Examples of resolution errors:
|
|
20
|
+
- Required variable missing from variables dict
|
|
21
|
+
- Environment variable referenced but not found in environment
|
|
22
|
+
- Format specifier cannot be applied to value type (e.g., :03 on string)
|
|
23
|
+
"""
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""Format specifier classes for macro variable transformations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
|
|
9
|
+
from griptape_nodes.common.macro_parser.exceptions import MacroResolutionError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class FormatSpec(ABC):
|
|
14
|
+
"""Base class for format specifiers."""
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def apply(self, value: str | int) -> str | int:
|
|
18
|
+
"""Apply this format spec to a value during resolution.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
value: Value to transform
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Transformed value
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
MacroResolutionError: If format cannot be applied to value type
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
>>> # NumericPaddingFormat(width=3).apply(5)
|
|
31
|
+
"005"
|
|
32
|
+
>>> # LowerCaseFormat().apply("MyWorkflow")
|
|
33
|
+
"myworkflow"
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def reverse(self, value: str) -> str | int:
|
|
38
|
+
"""Reverse this format spec during matching (best effort).
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
value: Formatted string value from a path
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Original value before format was applied
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
MacroResolutionError: If value cannot be reversed
|
|
48
|
+
|
|
49
|
+
Examples:
|
|
50
|
+
>>> # NumericPaddingFormat(width=3).reverse("005")
|
|
51
|
+
5
|
|
52
|
+
>>> # SeparatorFormat(separator="_").reverse("workflow_")
|
|
53
|
+
"workflow"
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class SeparatorFormat(FormatSpec):
|
|
59
|
+
"""Separator appended to variable value like :_, :/, :foo.
|
|
60
|
+
|
|
61
|
+
Must be first format spec in list (if present).
|
|
62
|
+
Syntax: {var:_} or {var:'lower'} (quotes to disambiguate from transformations)
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
separator: str # e.g., "_", "/", "foo"
|
|
66
|
+
|
|
67
|
+
def apply(self, value: str | int) -> str:
|
|
68
|
+
"""Append separator to value."""
|
|
69
|
+
return str(value) + self.separator
|
|
70
|
+
|
|
71
|
+
def reverse(self, value: str) -> str:
|
|
72
|
+
"""Remove separator from end of value."""
|
|
73
|
+
if value.endswith(self.separator):
|
|
74
|
+
return value[: -len(self.separator)]
|
|
75
|
+
return value
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass
|
|
79
|
+
class NumericPaddingFormat(FormatSpec):
|
|
80
|
+
"""Numeric padding format like :03, :04."""
|
|
81
|
+
|
|
82
|
+
width: int # e.g., 3 for :03
|
|
83
|
+
|
|
84
|
+
def apply(self, value: str | int) -> str:
|
|
85
|
+
"""Apply numeric padding: 5 → "005"."""
|
|
86
|
+
if not isinstance(value, int):
|
|
87
|
+
if not str(value).isdigit():
|
|
88
|
+
msg = (
|
|
89
|
+
f"Numeric padding format :{self.width:0{self.width}d} "
|
|
90
|
+
f"cannot be applied to non-numeric value: {value}"
|
|
91
|
+
)
|
|
92
|
+
raise MacroResolutionError(msg)
|
|
93
|
+
value = int(value)
|
|
94
|
+
return f"{value:0{self.width}d}"
|
|
95
|
+
|
|
96
|
+
def reverse(self, value: str) -> int:
|
|
97
|
+
"""Reverse numeric padding: "005" → 5."""
|
|
98
|
+
try:
|
|
99
|
+
return int(value)
|
|
100
|
+
except ValueError as e:
|
|
101
|
+
msg = f"Cannot parse '{value}' as integer"
|
|
102
|
+
raise MacroResolutionError(msg) from e
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass
|
|
106
|
+
class LowerCaseFormat(FormatSpec):
|
|
107
|
+
"""Lowercase transformation :lower."""
|
|
108
|
+
|
|
109
|
+
def apply(self, value: str | int) -> str:
|
|
110
|
+
"""Convert value to lowercase."""
|
|
111
|
+
return str(value).lower()
|
|
112
|
+
|
|
113
|
+
def reverse(self, value: str) -> str:
|
|
114
|
+
"""Cannot reliably reverse case - return as-is."""
|
|
115
|
+
return value
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@dataclass
|
|
119
|
+
class UpperCaseFormat(FormatSpec):
|
|
120
|
+
"""Uppercase transformation :upper."""
|
|
121
|
+
|
|
122
|
+
def apply(self, value: str | int) -> str:
|
|
123
|
+
"""Convert value to uppercase."""
|
|
124
|
+
return str(value).upper()
|
|
125
|
+
|
|
126
|
+
def reverse(self, value: str) -> str:
|
|
127
|
+
"""Cannot reliably reverse case - return as-is."""
|
|
128
|
+
return value
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@dataclass
|
|
132
|
+
class SlugFormat(FormatSpec):
|
|
133
|
+
"""Slugification format :slug (spaces to hyphens, safe chars only)."""
|
|
134
|
+
|
|
135
|
+
def apply(self, value: str | int) -> str:
|
|
136
|
+
"""Convert to slug: spaces→hyphens, lowercase, safe chars."""
|
|
137
|
+
s = str(value).lower()
|
|
138
|
+
s = re.sub(r"\s+", "-", s) # Spaces to hyphens
|
|
139
|
+
s = re.sub(r"[^a-z0-9\-_]", "", s) # Keep only safe chars
|
|
140
|
+
return s
|
|
141
|
+
|
|
142
|
+
def reverse(self, value: str) -> str:
|
|
143
|
+
"""Cannot reliably reverse slugification - return as-is."""
|
|
144
|
+
return value
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@dataclass
|
|
148
|
+
class DateFormat(FormatSpec):
|
|
149
|
+
"""Date formatting like :%Y-%m-%d."""
|
|
150
|
+
|
|
151
|
+
pattern: str # e.g., "%Y-%m-%d"
|
|
152
|
+
|
|
153
|
+
def apply(self, _value: str | int) -> str:
|
|
154
|
+
"""Apply date formatting."""
|
|
155
|
+
# TODO(https://github.com/griptape-ai/griptape-nodes/issues/2717): Implement date formatting
|
|
156
|
+
msg = "DateFormat not yet fully implemented"
|
|
157
|
+
raise MacroResolutionError(msg)
|
|
158
|
+
|
|
159
|
+
def reverse(self, value: str) -> str:
|
|
160
|
+
"""Attempt to parse date string."""
|
|
161
|
+
# TODO(https://github.com/griptape-ai/griptape-nodes/issues/2717): Implement date parsing
|
|
162
|
+
return value
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
# Module-level registry of known format transformations
|
|
166
|
+
FORMAT_REGISTRY: dict[str, FormatSpec] = {
|
|
167
|
+
"lower": LowerCaseFormat(),
|
|
168
|
+
"upper": UpperCaseFormat(),
|
|
169
|
+
"slug": SlugFormat(),
|
|
170
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Matching logic for extracting variables from paths."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from griptape_nodes.common.macro_parser.exceptions import MacroSyntaxError
|
|
8
|
+
from griptape_nodes.common.macro_parser.segments import (
|
|
9
|
+
ParsedSegment,
|
|
10
|
+
ParsedStaticValue,
|
|
11
|
+
ParsedVariable,
|
|
12
|
+
VariableInfo,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from griptape_nodes.common.macro_parser.formats import FormatSpec
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def extract_unknown_variables(
|
|
20
|
+
pattern_segments: list[ParsedSegment],
|
|
21
|
+
path: str,
|
|
22
|
+
) -> dict[VariableInfo, str | int] | None:
|
|
23
|
+
"""Extract unknown variable values from path (greedy matching).
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
pattern_segments: Partially resolved segments to match against
|
|
27
|
+
path: Path string to extract variables from
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Dict mapping VariableInfo to extracted values, or None if no match.
|
|
31
|
+
"""
|
|
32
|
+
current_match: dict[VariableInfo, str | int] = {}
|
|
33
|
+
current_pos = 0
|
|
34
|
+
|
|
35
|
+
for i, segment in enumerate(pattern_segments):
|
|
36
|
+
match segment:
|
|
37
|
+
case ParsedStaticValue():
|
|
38
|
+
# Verify static text matches at current position
|
|
39
|
+
if not path[current_pos:].startswith(segment.text):
|
|
40
|
+
# Static text doesn't match at this position
|
|
41
|
+
return None
|
|
42
|
+
current_pos += len(segment.text)
|
|
43
|
+
case ParsedVariable():
|
|
44
|
+
result = extract_single_variable(segment, pattern_segments[i + 1 :], path, current_pos)
|
|
45
|
+
if result is None:
|
|
46
|
+
return None
|
|
47
|
+
value, new_pos = result
|
|
48
|
+
current_match[segment.info] = value
|
|
49
|
+
current_pos = new_pos
|
|
50
|
+
case _:
|
|
51
|
+
msg = f"Unexpected segment type: {type(segment).__name__}"
|
|
52
|
+
raise MacroSyntaxError(msg)
|
|
53
|
+
|
|
54
|
+
return current_match
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def extract_single_variable(
|
|
58
|
+
variable: ParsedVariable,
|
|
59
|
+
remaining_segments: list[ParsedSegment],
|
|
60
|
+
path: str,
|
|
61
|
+
start_pos: int,
|
|
62
|
+
) -> tuple[str | int, int] | None:
|
|
63
|
+
"""Extract value for a single variable from path.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
variable: The variable to extract
|
|
67
|
+
remaining_segments: Segments after this variable
|
|
68
|
+
path: Full path being matched
|
|
69
|
+
start_pos: Position in path to start extraction
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Tuple of (extracted_value, new_position) or None if extraction fails.
|
|
73
|
+
"""
|
|
74
|
+
# Find next static segment to determine end position
|
|
75
|
+
next_static = find_next_static(remaining_segments)
|
|
76
|
+
if next_static:
|
|
77
|
+
end_pos = path.find(next_static.text, start_pos)
|
|
78
|
+
if end_pos == -1:
|
|
79
|
+
# Can't find next static - no match
|
|
80
|
+
return None
|
|
81
|
+
else:
|
|
82
|
+
# No more static segments - consume to end
|
|
83
|
+
end_pos = len(path)
|
|
84
|
+
|
|
85
|
+
# Extract raw value
|
|
86
|
+
raw_value = path[start_pos:end_pos]
|
|
87
|
+
|
|
88
|
+
# Reverse format specs
|
|
89
|
+
reversed_value = reverse_format_specs(raw_value, variable.format_specs)
|
|
90
|
+
if reversed_value is None:
|
|
91
|
+
# Can't reverse format specs - no match
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
return (reversed_value, end_pos)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def find_next_static(segments: list[ParsedSegment]) -> ParsedStaticValue | None:
|
|
98
|
+
"""Find next static segment in list.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
segments: List of segments to search
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
First ParsedStaticValue found, or None if no static segments.
|
|
105
|
+
"""
|
|
106
|
+
for seg in segments:
|
|
107
|
+
if isinstance(seg, ParsedStaticValue):
|
|
108
|
+
return seg
|
|
109
|
+
# No static segment found
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def reverse_format_specs(value: str, format_specs: list[FormatSpec]) -> str | int | None:
|
|
114
|
+
"""Apply format spec reversal in reverse order.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
value: String value extracted from path
|
|
118
|
+
format_specs: List of format specs to reverse
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Reversed value (might be int after NumericPaddingFormat.reverse), or None if reversal fails.
|
|
122
|
+
"""
|
|
123
|
+
result: str | int = value
|
|
124
|
+
# Apply in reverse order (last spec first)
|
|
125
|
+
for spec in reversed(format_specs):
|
|
126
|
+
# reverse() expects str but result might be int, so convert if needed
|
|
127
|
+
str_result = str(result) if isinstance(result, int) else result
|
|
128
|
+
reversed_result = spec.reverse(str_result)
|
|
129
|
+
if reversed_result is None:
|
|
130
|
+
# Can't reverse this format spec
|
|
131
|
+
return None
|
|
132
|
+
result = reversed_result
|
|
133
|
+
# Return reversed value (might be int after NumericPaddingFormat.reverse)
|
|
134
|
+
return result
|