|
31 | 31 | from .metadata import Metadata |
32 | 32 | from .orchestrator import BlockOrchestrator |
33 | 33 | from .resolver import UnifiedVariableResolver |
34 | | -from .schema import BlockDefinition, DependencySpec, WorkflowSchema |
| 34 | +from .schema import BlockDefinition, DependencySpec, InputType, WorkflowSchema |
35 | 35 | from .secrets import EnvVarSecretProvider, SecretAuditLog, SecretRedactor |
36 | 36 |
|
37 | 37 | logger = logging.getLogger(__name__) |
@@ -955,7 +955,7 @@ def _create_initial_execution_context( |
955 | 955 | # Create execution context (workflow-level has no metadata - only blocks have metadata) |
956 | 956 | workflow_start_time = datetime.now(UTC).isoformat() |
957 | 957 | exec_context = Execution( |
958 | | - inputs=self._merge_workflow_inputs(workflow, runtime_inputs), |
| 958 | + inputs=self._merge_workflow_inputs(workflow, runtime_inputs, scratch_dir), |
959 | 959 | metadata=None, # Workflow-level Execution has no block metadata |
960 | 960 | blocks={}, |
961 | 961 | depth=len(context.workflow_stack) if context else 0, |
@@ -1076,37 +1076,41 @@ def _merge_workflow_inputs( |
1076 | 1076 | self, |
1077 | 1077 | workflow: WorkflowSchema, |
1078 | 1078 | runtime_inputs: dict[str, Any] | None, |
| 1079 | + scratch_dir: Path, |
1079 | 1080 | ) -> dict[str, Any]: |
1080 | | - """Merge default and runtime inputs.""" |
| 1081 | + """Merge default and runtime inputs with {{tmp}} resolution and validation.""" |
1081 | 1082 | merged = {} |
1082 | 1083 |
|
1083 | | - # Apply defaults |
| 1084 | + # Apply defaults with {{tmp}} resolution |
1084 | 1085 | for input_name, input_decl in workflow.inputs.items(): |
1085 | 1086 | if input_decl.default is not None: |
1086 | | - merged[input_name] = input_decl.default |
| 1087 | + value = input_decl.default |
| 1088 | + if isinstance(value, str): |
| 1089 | + value = value.replace("{{tmp}}", str(scratch_dir)) |
| 1090 | + merged[input_name] = value |
1087 | 1091 |
|
1088 | 1092 | # Override with runtime inputs |
1089 | 1093 | if runtime_inputs: |
1090 | 1094 | merged.update(runtime_inputs) |
1091 | 1095 |
|
1092 | 1096 | # Validate required inputs |
1093 | | - missing_inputs = [] |
1094 | | - empty_string_inputs = [] |
1095 | | - for input_name, input_decl in workflow.inputs.items(): |
1096 | | - is_required = getattr(input_decl, "required", False) |
1097 | | - if is_required: |
1098 | | - if input_name not in merged: |
1099 | | - missing_inputs.append(input_name) |
1100 | | - elif input_decl.type.value == "str" and merged[input_name] == "": |
1101 | | - empty_string_inputs.append(input_name) |
| 1097 | + missing = [ |
| 1098 | + name for name, decl in workflow.inputs.items() if decl.required and name not in merged |
| 1099 | + ] |
| 1100 | + empty = [ |
| 1101 | + name |
| 1102 | + for name, decl in workflow.inputs.items() |
| 1103 | + if decl.required |
| 1104 | + and name in merged |
| 1105 | + and decl.type == InputType.STR |
| 1106 | + and merged[name] == "" |
| 1107 | + ] |
1102 | 1108 |
|
1103 | 1109 | errors = [] |
1104 | | - if missing_inputs: |
1105 | | - errors.append(f"Missing required inputs: {', '.join(missing_inputs)}") |
1106 | | - if empty_string_inputs: |
1107 | | - errors.append( |
1108 | | - f"Required string inputs cannot be empty: {', '.join(empty_string_inputs)}" |
1109 | | - ) |
| 1110 | + if missing: |
| 1111 | + errors.append(f"Missing required inputs: {', '.join(missing)}") |
| 1112 | + if empty: |
| 1113 | + errors.append(f"Required string inputs cannot be empty: {', '.join(empty)}") |
1110 | 1114 |
|
1111 | 1115 | if errors: |
1112 | 1116 | raise ValueError("; ".join(errors)) |
|
0 commit comments