child graphs can interrupt/resume

This commit is contained in:
lda
2026-05-25 05:11:16 +07:00 Verified
parent 7477a12b51
commit 7aa562c619
14 changed files with 397 additions and 95 deletions
+50 -3
View File
@@ -1,8 +1,17 @@
from __future__ import annotations
from wf_core.conditions import eval_condition
from wf_core.errors import WorkflowExecutionError
from wf_core.models.steps import ConditionNode, InterruptNode
from wf_core.run_state import FrameStatus, RunState, RunStatus, StepExecutionResult
from wf_core.run_state import (
ROOT_SCOPE_ID,
ExecutionFrame,
FrameStatus,
InterruptRoute,
RunState,
RunStatus,
StepExecutionResult,
)
from wf_core.runtime.lineage import scope_input_for_frame
from wf_core.runtime.ops.flow import append_trace
from wf_core.runtime.ops.frames import frame_context_values
@@ -44,12 +53,32 @@ def handle_interrupt_step(
step: InterruptNode,
) -> RunState:
frame = run.current_frame()
public_frame = frame
route = None
if frame.scope_id != ROOT_SCOPE_ID:
public_frame = _owning_subgraph_frame(run, frame)
scope = run.scopes.get(frame.scope_id)
if scope is None or scope.workflow_ref is None:
raise WorkflowExecutionError(
f"child interrupt frame {frame.id!r} has no workflow scope"
)
route = InterruptRoute(
frame_id=frame.id,
node_id=frame.node_id,
scope_id=frame.scope_id,
lineage_id=frame.lineage_id,
parent_frame_id=public_frame.id,
workflow_ref=scope.workflow_ref,
)
interrupt_request = build_interrupt_request(
step,
frame_id=frame.id,
state=run.state,
workflow_input=run.workflow_input,
state=state_view_for_frame(run, frame),
workflow_input=scope_input_for_frame(run, frame),
context=frame_context_values(frame),
public_frame_id=public_frame.id,
public_node_id=public_frame.node_id,
route=route,
)
run.interrupt = interrupt_request
run.status = RunStatus.INTERRUPTED
@@ -66,3 +95,21 @@ def handle_interrupt_step(
state_changes={},
)
return run
def _owning_subgraph_frame(run: RunState, frame: ExecutionFrame) -> ExecutionFrame:
"""Return the graph-boundary frame that owns one child-scope interrupt."""
cursor = frame
while cursor.parent_frame_id is not None:
parent = run.frames.get(cursor.parent_frame_id)
if parent is None:
raise WorkflowExecutionError(
f"child interrupt frame {frame.id!r} references missing parent "
f"{cursor.parent_frame_id!r}"
)
if parent.scope_id != frame.scope_id:
return parent
cursor = parent
raise WorkflowExecutionError(
f"child interrupt frame {frame.id!r} has no parent subgraph"
)