examples, docs, ergonomics and code review fixes

This commit is contained in:
lda
2026-05-25 15:48:39 +07:00 Verified
parent 7aa562c619
commit 6e3a6cac48
12 changed files with 255 additions and 33 deletions
+10 -21
View File
@@ -10,7 +10,7 @@ from wf_authoring import (
output_to,
state_path,
)
from wf_core import END, PreparedSubgraph, RunState, Workflow, execute_workflow
from wf_core import END, RunState
class ChildInput(BaseModel):
@@ -74,14 +74,15 @@ def build_child_workflow() -> WorkflowBuilder:
return child
def build_parent_workflow(child_workflow: Workflow) -> WorkflowBuilder:
"""Build a parent graph with one native child-workflow boundary."""
def build_parent_workflow(child: WorkflowBuilder) -> WorkflowBuilder:
"""Build a parent graph with one registered native child workflow."""
parent = WorkflowBuilder(
name="native_parent",
input_schema=ParentInput,
state_schema=ParentState,
output_schema=ParentOutput,
)
child_workflow = parent.prepare_subgraph(child)
run_child = parent.subgraph(
workflow=child_workflow,
id="run_child",
@@ -94,28 +95,16 @@ def build_parent_workflow(child_workflow: Workflow) -> WorkflowBuilder:
def run_native_subgraph_example(prompt: str = "hello") -> RunState:
"""Execute a native subgraph using its prepared local runtime dependency.
"""Execute a native subgraph using builder-managed local dependencies.
`WorkflowBuilder.subgraph()` records the child contract in the parent graph.
`PreparedSubgraph` separately supplies the Python handlers needed to execute
that contract; saved artifact/deployment resolution belongs above wf_core.
`prepare_subgraph()` separately registers the Python handlers needed to
execute that contract; saved artifact/deployment resolution belongs above
this local authoring helper.
"""
child = build_child_workflow()
child_workflow = child.compile()
parent = build_parent_workflow(child_workflow)
return execute_workflow(
parent.compile(),
{"prompt": prompt},
parent.registry(),
reducers=parent.reducer_registry(),
subgraphs={
child_workflow.name: PreparedSubgraph(
workflow=child_workflow,
registry=child.registry(),
reducers=child.reducer_registry(),
)
},
)
parent = build_parent_workflow(child)
return parent.execute({"prompt": prompt})
def main() -> None:
@@ -0,0 +1,107 @@
from __future__ import annotations
from pydantic import BaseModel
from wf_authoring import WorkflowBuilder, input_from, input_path, output_to, state_path
from wf_core import END, InterruptRequest, RunState
class ChildInput(BaseModel):
"""Question forwarded into the child workflow."""
question: str
class ChildState(BaseModel):
"""Child-owned answer populated only after resume."""
answer: str = ""
class ChildOutput(BaseModel):
"""Child result projected through the parent subgraph boundary."""
answer: str
class ParentInput(BaseModel):
"""Parent input used to build the child interrupt request."""
question: str
class ParentState(BaseModel):
"""Parent state updated only after the child resumes and completes."""
result: str = ""
class ParentOutput(BaseModel):
"""Final result exposed by the parent workflow."""
result: str
def build_interrupting_child() -> WorkflowBuilder:
"""Build a child graph whose first step pauses for one supplied answer."""
child = WorkflowBuilder(
name="answer_child",
input_schema=ChildInput,
state_schema=ChildState,
output_schema=ChildOutput,
)
ask = child.interrupt(
id="ask",
kind="input",
request=[input_from(input_path("question"), "question")],
resume=[output_to("answer", state_path("answer"))],
)
child.set_entry_point(ask)
child.connect(ask, "submitted", END)
return child
def build_interrupting_parent(child: WorkflowBuilder) -> WorkflowBuilder:
"""Build a parent graph that invokes the interrupting child natively."""
parent = WorkflowBuilder(
name="answer_parent",
input_schema=ParentInput,
state_schema=ParentState,
output_schema=ParentOutput,
)
child_workflow = parent.prepare_subgraph(child)
request_answer = parent.subgraph(
workflow=child_workflow,
id="request_answer",
input=[input_from(input_path("question"), "question")],
output=[output_to("answer", state_path("result"))],
)
parent.set_entry_point(request_answer)
parent.connect(request_answer, "ok", END)
return parent
def run_native_subgraph_interrupt_example() -> tuple[InterruptRequest, RunState]:
"""Pause in a child workflow, then resume it through authoring helpers.
`RunState` is resumed in place, so the example retains the emitted request
before continuing the run.
"""
parent = build_interrupting_parent(build_interrupting_child())
paused = parent.execute({"question": "What is your answer?"})
if paused.interrupt is None:
raise AssertionError("expected child workflow to interrupt")
request = paused.interrupt
resumed = parent.resume(paused, payload={"answer": "confirmed"})
return request, resumed
def main() -> None:
"""Run the native interrupt/resume example from the command line."""
request, resumed = run_native_subgraph_interrupt_example()
print("interrupt", request.payload)
print("result", resumed.output)
if __name__ == "__main__":
main()