Skip to main content

State shapes

Coevolved steps accept and return a “state” object. In practice, you’ll use one of:
  • A Python dict (most common)
  • A Pydantic BaseModel (when you want strong contracts)
  • A custom object (supported, but easier to get wrong)
Many prebuilt utilities (like the ReAct agent) assume dict-like state.

Pydantic input/output validation

Step supports validating both inputs and outputs via Pydantic models:
  • input_schema: validate the incoming state
  • output_schema: validate what your function returns
If validation fails, Step raises a ValueError with details from Pydantic.

Updating state safely

When your state is a dict, a simple pattern keeps steps predictable:
def step_fn(state: dict) -> dict:
    # Avoid in-place mutation; return a new dict.
    return {**state, "field": "value"}
When your state is a Pydantic model, return an updated copy:
from pydantic import BaseModel

class State(BaseModel):
    count: int = 0

def inc(state: State) -> State:
    return state.model_copy(update={"count": state.count + 1})
Treat “state” as immutable by convention. This makes parallel composition safer and debugging easier.

Schema design guidelines

  • Prefer a small number of stable fields rather than dozens of loosely named keys.
  • Use nested models when fields naturally group (e.g. usage, inputs, outputs).
  • Make “optional until set” fields explicitly optional (Optional[...]) rather than missing keys.
  • For agent loops, define a clear contract for “stop condition fields” (e.g. final).

Next steps