native subgraph execution
This commit is contained in:
@@ -80,6 +80,10 @@ def advance_frame(
|
||||
frame.activated_incoming_edge = frame.node_id
|
||||
frame.node_id = next_node_id
|
||||
if next_node_id == END:
|
||||
if frame.kind in {"workflow", "subgraph_root"}:
|
||||
# Legacy terminal routing emits the workflow-level `ok` outcome.
|
||||
# Explicit EndNode execution stores its declared outcome first.
|
||||
frame.metadata.setdefault("workflow_outcome", "ok")
|
||||
frame.status = FrameStatus.COMPLETED
|
||||
frame.finished_at_node_id = END
|
||||
wake_parent_for_child_progress(run, frame.id)
|
||||
|
||||
@@ -14,18 +14,18 @@ from wf_core.runtime.foreach_state import (
|
||||
)
|
||||
from wf_core.runtime.lineage import (
|
||||
add_lineage,
|
||||
append_lineage_writes,
|
||||
is_root_lineage_frame,
|
||||
commit_patch_for_frame,
|
||||
lineage_patch,
|
||||
scope_input_for_frame,
|
||||
)
|
||||
from wf_core.runtime.ops.flow import advance_frame, append_step_result_trace
|
||||
from wf_core.runtime.ops.frames import frame_context_values
|
||||
from wf_core.runtime.ops.index import WorkflowIndex
|
||||
from wf_core.runtime.ops.merges import ReducerDefinition
|
||||
from wf_core.runtime.ops.overlays import state_view_for_frame
|
||||
from wf_core.runtime.ops.state import (
|
||||
StatePatch,
|
||||
build_barrier_patch,
|
||||
commit_state_patch,
|
||||
)
|
||||
from wf_core.runtime.scheduler import (
|
||||
ForeachIterationMetadata,
|
||||
@@ -182,8 +182,8 @@ def _resolve_foreach_iterable(
|
||||
) -> list[object]:
|
||||
iterable = safe_resolve_path(
|
||||
str(step.over),
|
||||
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),
|
||||
)
|
||||
if not isinstance(iterable, list):
|
||||
@@ -346,19 +346,10 @@ def _finish_concurrent_foreach(
|
||||
combined = build_barrier_patch(
|
||||
workflow,
|
||||
item_patches,
|
||||
run.state,
|
||||
state_view_for_frame(run, frame),
|
||||
reducers=reducers,
|
||||
)
|
||||
if is_root_lineage_frame(frame):
|
||||
state_changes = commit_state_patch(run.state, combined)
|
||||
else:
|
||||
append_lineage_writes(
|
||||
run,
|
||||
scope_id=frame.scope_id,
|
||||
lineage_id=frame.lineage_id,
|
||||
writes=combined.writes,
|
||||
)
|
||||
state_changes = {}
|
||||
state_changes = commit_patch_for_frame(run, frame, combined)
|
||||
append_step_result_trace(
|
||||
run,
|
||||
frame_id=frame.id,
|
||||
|
||||
@@ -3,9 +3,11 @@ from __future__ import annotations
|
||||
from wf_core.conditions import eval_condition
|
||||
from wf_core.models.steps import ConditionNode, InterruptNode
|
||||
from wf_core.run_state import FrameStatus, 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
|
||||
from wf_core.runtime.ops.interrupts import build_interrupt_request
|
||||
from wf_core.runtime.ops.overlays import state_view_for_frame
|
||||
|
||||
|
||||
def handle_condition_step(
|
||||
@@ -15,8 +17,8 @@ def handle_condition_step(
|
||||
frame = run.current_frame()
|
||||
predicate = eval_condition(
|
||||
step.check,
|
||||
run.state,
|
||||
run.workflow_input,
|
||||
state_view_for_frame(run, frame),
|
||||
scope_input_for_frame(run, frame),
|
||||
frame.prior_outcome,
|
||||
)
|
||||
outcome = "true" if predicate else "false"
|
||||
|
||||
@@ -18,12 +18,16 @@ from wf_core.run_state import (
|
||||
StepExecutionResult,
|
||||
)
|
||||
from wf_core.runtime.foreach_state import ForeachBarrierState, item_frame_owner
|
||||
from wf_core.runtime.lineage import append_lineage_writes, is_root_lineage_frame
|
||||
from wf_core.runtime.lineage import (
|
||||
append_lineage_writes,
|
||||
commit_patch_for_frame,
|
||||
scope_input_for_frame,
|
||||
)
|
||||
from wf_core.runtime.ops.frames import frame_context_values
|
||||
from wf_core.runtime.ops.merges import ReducerDefinition
|
||||
from wf_core.runtime.ops.overlays import state_view_for_frame
|
||||
from wf_core.runtime.ops.schemas import validate_payload_against_schema
|
||||
from wf_core.runtime.ops.state import StatePatch, build_output_patch, commit_state_patch
|
||||
from wf_core.runtime.ops.state import StatePatch, build_output_patch
|
||||
|
||||
NodeHandler = Callable[[dict[str, Any], RuntimeContext], NodeResult | dict[str, Any]]
|
||||
AsyncNodeHandler = Callable[
|
||||
@@ -62,7 +66,7 @@ def _resolve_node_execution(
|
||||
value = safe_resolve_path(
|
||||
str(binding.path),
|
||||
state=state_view,
|
||||
workflow_input=run.workflow_input,
|
||||
workflow_input=scope_input_for_frame(run, frame),
|
||||
context=context_values,
|
||||
)
|
||||
else:
|
||||
@@ -121,18 +125,7 @@ def _finalize_node_execution(
|
||||
)
|
||||
owner = item_frame_owner(frame)
|
||||
if owner is None:
|
||||
if is_root_lineage_frame(frame):
|
||||
state_changes = commit_state_patch(run.state, patch)
|
||||
else:
|
||||
# Non-root frames are future subgraph/fork branch execution: writes
|
||||
# become lineage-local until an explicit boundary/barrier commits.
|
||||
append_lineage_writes(
|
||||
run,
|
||||
scope_id=frame.scope_id,
|
||||
lineage_id=frame.lineage_id,
|
||||
writes=patch.writes,
|
||||
)
|
||||
state_changes = {}
|
||||
state_changes = commit_patch_for_frame(run, frame, patch)
|
||||
else:
|
||||
parent_frame_id, foreach_node_id, item_index = owner
|
||||
parent_frame = run.frames[parent_frame_id]
|
||||
@@ -155,7 +148,7 @@ def _finalize_node_execution(
|
||||
barrier.save_to_frame(parent_frame, foreach_node_id)
|
||||
state_changes = {}
|
||||
else:
|
||||
state_changes = commit_state_patch(run.state, patch)
|
||||
state_changes = commit_patch_for_frame(run, parent_frame, patch)
|
||||
return StepExecutionResult(
|
||||
outcome=result.outcome,
|
||||
resolved_input=resolved_input,
|
||||
|
||||
@@ -18,12 +18,20 @@ from wf_core.run_state import (
|
||||
from wf_core.runtime.scheduler import add_frame
|
||||
|
||||
|
||||
def create_run_state(workflow: Workflow, workflow_input: dict[str, object]) -> RunState:
|
||||
def initial_state(
|
||||
workflow: Workflow, workflow_input: dict[str, object]
|
||||
) -> dict[str, object]:
|
||||
"""Create one scope's committed state from defaults plus workflow input."""
|
||||
state: dict[str, object] = {}
|
||||
for field in workflow.state_schema.fields:
|
||||
if field.default is not None:
|
||||
set_nested_value(state, list(field.path.parts), deepcopy(field.default))
|
||||
state.update(dict(workflow_input))
|
||||
return state
|
||||
|
||||
|
||||
def create_run_state(workflow: Workflow, workflow_input: dict[str, object]) -> RunState:
|
||||
state = initial_state(workflow, workflow_input)
|
||||
run = RunState(
|
||||
workflow_name=workflow.name,
|
||||
status=RunStatus.PENDING,
|
||||
@@ -33,6 +41,7 @@ def create_run_state(workflow: Workflow, workflow_input: dict[str, object]) -> R
|
||||
ROOT_SCOPE_ID: RuntimeScope(
|
||||
id=ROOT_SCOPE_ID,
|
||||
workflow_name=workflow.name,
|
||||
workflow_input=dict(workflow_input),
|
||||
committed_state=state,
|
||||
)
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user