Files
lda-wf/src/wf_core/runtime/ops/flow.py
T

110 lines
2.8 KiB
Python

from __future__ import annotations
from typing import Any
from wf_core.models.workflow import Workflow
from wf_core.run_state import (
ExecutionFrame,
FrameStatus,
RunState,
RunStatus,
StepExecutionResult,
TraceEntry,
)
from wf_core.runtime.ops.schemas import validate_payload_against_schema
from wf_core.runtime.ops.state import project_output
from wf_core.runtime.scheduler import (
mark_frame_pending,
wake_parent_for_child_progress,
)
from wf_core.tokens import END
def append_trace(
run: RunState,
*,
frame_id: str,
node_id: str,
step_type: str,
resolved_input: dict[str, Any],
outcome: str,
next_node_id: str,
output: dict[str, Any],
state_changes: dict[str, Any],
) -> None:
run.trace.append(
TraceEntry(
frame_id=frame_id,
node_id=node_id,
step_type=step_type,
resolved_input=resolved_input,
outcome=outcome,
next_node_id=next_node_id,
output=output,
state_changes=state_changes,
)
)
def append_step_result_trace(
run: RunState,
*,
frame_id: str,
node_id: str,
step_type: str,
next_node_id: str,
result: StepExecutionResult,
) -> None:
append_trace(
run,
frame_id=frame_id,
node_id=node_id,
step_type=step_type,
resolved_input=result.resolved_input,
outcome=result.outcome,
next_node_id=next_node_id,
output=result.output,
state_changes=result.state_changes,
)
def advance_frame(
run: RunState,
frame: ExecutionFrame,
*,
outcome: str,
next_node_id: str,
front: bool = False,
) -> None:
frame.prior_outcome = outcome
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)
else:
frame.finished_at_node_id = None
mark_frame_pending(run, frame.id, front=front)
run.sync_from_current_frame()
def finalize_run(workflow: Workflow, run: RunState) -> RunState:
if run.outcome is None:
run.outcome = "ok"
run.output = project_output(
workflow,
run.state,
workflow_input=run.workflow_input,
)
validate_payload_against_schema(
workflow.output_schema, run.output, "workflow output"
)
run.status = RunStatus.COMPLETED
run.current_node_id = END
return run