StateMachineStep — A First‑Class DSL Primitive
StateMachineStep
is a high‑level orchestration primitive that drives execution through named states. Each state maps to its own Pipeline. Transitions are controlled via context scratchpad keys.
When to Use
Use a StateMachineStep
when you need explicit, named phases with clear transitions (e.g., iterate until analysis → refine → finalize).
Python DSL
from typing import Any
from flujo.domain.dsl import Pipeline, Step
from flujo.domain.dsl.state_machine import StateMachineStep
async def set_next_state(_: Any, *, context=None) -> str:
# Signal transition to "refine"
context.scratchpad["next_state"] = "refine"
return "ok"
analyze = Pipeline.from_step(Step.from_callable(set_next_state, name="Analyze"))
refine = Pipeline.from_step(Step.from_callable(lambda x: x, name="Refine"))
sm = StateMachineStep(
name="SM",
states={"analyze": analyze, "refine": refine},
start_state="analyze",
end_states=["refine"],
)
pipe = Pipeline.from_step(sm)
YAML
version: "0.1"
steps:
- kind: StateMachine
name: SM
start_state: analyze
end_states: [refine]
states:
analyze:
- kind: step
name: SetNext
refine:
- kind: step
name: Done
The YAML loader converts each states.<name>
block into a Pipeline and instantiates the model through the registry.
Imports In States
StateMachine states can import child pipelines via uses: imports.<alias>
. The loader compiles these into first‑class ImportStep
s, preserving policy‑driven execution and ImportStep semantics (context inheritance and outputs mapping).
Example:
version: "0.1"
imports:
clarify: "./clarification/pipeline.yaml"
refine: "./refinement/pipeline.yaml"
steps:
- kind: StateMachine
name: Orchestrate
start_state: clarification
end_states: [done]
states:
clarification:
- name: Clarify
uses: imports.clarify
updates_context: true
config:
inherit_context: true
outputs:
- { child: "scratchpad.cohort", parent: "scratchpad.cohort" }
- name: SetNext
uses: flujo.builtins.stringify # or any agent
updates_context: true
done:
- kind: step
name: Done
Notes:
- Use config.inherit_context: true
to run the child with a deep copy of the parent context.
- Use config.outputs
to map child context fields back into the parent when updates_context: true
.
- Execution for state pipelines uses the core policy router; no separate runner is spawned.
Execution Semantics
- The policy executor reads
current_state
fromcontext.scratchpad["current_state"]
when present; otherwise starts withstart_state
- The selected state’s Pipeline is executed using the core’s policy path
- On completion, the executor checks
context.scratchpad["next_state"]
; if present, it becomes the nextcurrent_state
- Execution stops when
current_state
is inend_states
, or after one hop without an explicitnext_state
Context Keys
scratchpad.current_state
(optional): force the starting state at runtimescratchpad.next_state
: set by your state pipelines to transition to the next state
Testing Tips
- Unit test your step pipelines independently
- For integration, build a
Pipeline
withStateMachineStep
and run viaExecutorCore
to exercise policy routing - If a state is terminal (in
end_states
), the policy won’t execute its body