139 lines
4.7 KiB
Python
139 lines
4.7 KiB
Python
from __future__ import annotations
|
|
|
|
from wf_core import END, RuntimeContext, Workflow, execute_workflow
|
|
from wf_core.run_state import RunState
|
|
|
|
|
|
def build_raw_concurrent_foreach_workflow() -> Workflow:
|
|
"""Build the canonical JSON/Pydantic shape for concurrent foreach.
|
|
|
|
This example is intentionally raw `wf_core`: it is useful for MCP/LLM-facing
|
|
authoring surfaces that need to emit workflow JSON without Python builder
|
|
helpers.
|
|
"""
|
|
return Workflow.model_validate(
|
|
{
|
|
"name": "raw_concurrent_foreach",
|
|
"input_schema": {
|
|
"type": "object",
|
|
"properties": {"items": {"type": "array"}},
|
|
"required": ["items"],
|
|
},
|
|
"state_schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"items": {"type": "array"},
|
|
"seen": {
|
|
"type": "array",
|
|
"reducer": "wf.std.append",
|
|
},
|
|
"errors": {"type": "array"},
|
|
},
|
|
},
|
|
"output_schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"seen": {"type": "array"},
|
|
"errors": {"type": "array"},
|
|
},
|
|
},
|
|
"node_defs": [
|
|
{
|
|
"name": "record_item",
|
|
"input_schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"value": {},
|
|
"seen": {},
|
|
},
|
|
"required": ["value", "seen"],
|
|
},
|
|
"output_schema": {
|
|
"type": "object",
|
|
"properties": {"seen": {}},
|
|
"required": ["seen"],
|
|
},
|
|
"outcomes": ["ok"],
|
|
}
|
|
],
|
|
"start": "each",
|
|
"nodes": [
|
|
{
|
|
"id": "each",
|
|
"type": "foreach",
|
|
"over": {"root": "state", "parts": ["items"]},
|
|
"as": "item",
|
|
"mode": "concurrent",
|
|
"concurrent": {
|
|
"max_active": 2,
|
|
"max_outstanding": 2,
|
|
},
|
|
"item_error": {
|
|
"action": "collect",
|
|
"collect_to": {"root": "state", "parts": ["errors"]},
|
|
},
|
|
},
|
|
{
|
|
"id": "record",
|
|
"type": "node",
|
|
"node": "record_item",
|
|
"input": [
|
|
{
|
|
"target": {"root": "local", "parts": ["value"]},
|
|
"path": {"root": "context", "parts": ["item"]},
|
|
},
|
|
{
|
|
"target": {"root": "local", "parts": ["seen"]},
|
|
"path": {"root": "context", "parts": ["item"]},
|
|
},
|
|
],
|
|
"output": [
|
|
{
|
|
"source": {"root": "local", "parts": ["seen"]},
|
|
"target": {"root": "state", "parts": ["seen"]},
|
|
}
|
|
],
|
|
},
|
|
],
|
|
"edges": [
|
|
{"from": "each", "outcome": "loop", "to": "record"},
|
|
{"from": "record", "outcome": "ok", "to": "each"},
|
|
{"from": "each", "outcome": "done", "to": END},
|
|
{"from": "each", "outcome": "completed_with_errors", "to": END},
|
|
],
|
|
}
|
|
)
|
|
|
|
|
|
def build_raw_concurrent_foreach_registry():
|
|
"""Return handlers for the raw concurrent foreach example."""
|
|
|
|
def record_item(
|
|
payload: dict[str, object],
|
|
_context: RuntimeContext,
|
|
) -> dict[str, object]:
|
|
if payload["value"] == "bad":
|
|
raise ValueError("bad item")
|
|
return {"outcome": "ok", "output": {"seen": payload["seen"]}}
|
|
|
|
return {"record_item": record_item}
|
|
|
|
|
|
def run_raw_concurrent_foreach_example() -> RunState:
|
|
"""Run raw concurrent foreach with collected item errors."""
|
|
return execute_workflow(
|
|
build_raw_concurrent_foreach_workflow(),
|
|
{"items": ["a", "bad", "c"]},
|
|
build_raw_concurrent_foreach_registry(),
|
|
)
|
|
|
|
|
|
def main() -> None:
|
|
"""Run the example directly from the command line."""
|
|
run = run_raw_concurrent_foreach_example()
|
|
print(run.status.value, run.output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|