concurrent foreach preparation, types, validation, refactors

This commit is contained in:
lda
2026-05-22 12:11:51 +07:00 Verified
parent afafe40109
commit d8d5770c9f
17 changed files with 712 additions and 15 deletions
+6 -1
View File
@@ -75,7 +75,12 @@ def _validate_nodes(
)
elif isinstance(node, ForeachNode):
validate_foreach_node(
node, index, report, state_root_fields, input_root_fields
node,
index,
report,
state_root_fields,
input_root_fields,
workflow,
)
elif isinstance(node, InterruptNode):
validate_interrupt_node(
+1
View File
@@ -21,6 +21,7 @@ class ValidationIssueCode(StrEnum):
EMPTY_CONDITION_ARGS = "empty_condition_args"
INVALID_CONDITION_PATH = "invalid_condition_path"
INVALID_FOREACH_SOURCE = "invalid_foreach_source"
INVALID_FOREACH_COLLECT_DESTINATION = "invalid_foreach_collect_destination"
INVALID_INTERRUPT_SOURCE = "invalid_interrupt_source"
INVALID_INTERRUPT_DESTINATION = "invalid_interrupt_destination"
+4 -1
View File
@@ -13,7 +13,10 @@ def declared_outcomes_for_step(step: Step, node_defs: dict[str, NodeDef]) -> set
if step.type == "condition":
return {"true", "false"}
if step.type == "foreach":
return {"loop", "done"}
outcomes = {"loop", "done"}
if step.item_error.action in {"skip", "collect"}:
outcomes.add("completed_with_errors")
return outcomes
if step.type == "join":
return {"done"}
if isinstance(step, InterruptNode):
+22
View File
@@ -149,6 +149,7 @@ def validate_foreach_node(
report: ValidationReport,
state_root_fields: set[str],
input_root_fields: set[str],
workflow: Workflow,
) -> None:
if not is_valid_source_path(node.over, state_root_fields, input_root_fields):
report.add(
@@ -156,6 +157,27 @@ def validate_foreach_node(
f"nodes[{index}].over",
"foreach source path must start with input. or state. and reference a declared root field",
)
if node.item_error.action != "collect":
return
collect_to = node.item_error.collect_to
if collect_to is None:
return
destination_root = _state_destination_root(collect_to)
state_fields = workflow.state_schema.field_index()
field = state_fields.get(collect_to)
if destination_root is None or destination_root not in state_root_fields:
report.add(
ValidationIssueCode.INVALID_FOREACH_COLLECT_DESTINATION,
f"nodes[{index}].item_error.collect_to",
"collect_to must start with state. and reference a declared state field",
)
return
if field is None or field.type != "array":
report.add(
ValidationIssueCode.INVALID_FOREACH_COLLECT_DESTINATION,
f"nodes[{index}].item_error.collect_to",
"collect_to must reference a declared array state field",
)
def validate_interrupt_node(