Defines the behavior and specification for a workflow step.
A Step is the atomic unit of work in Orchid. It is responsible for receiving
data (wrapped in Orchid.Param), performing a specific task, and returning
new data.
It focuses solely on processing logic, unaware of the larger workflow context.
Usage
To define a step, use Orchid.Step and implement the run/2 callback:
defmodule MySteps.Upcase do
use Orchid.Step
alias Orchid.Param
@impl true
def run(input_param, _opts) do
payload = Param.get_payload(input_param)
result =
Param.new(:result, :string)
|> Param.set_payload(String.upcase(payload))
{:ok, result}
end
endInput Pattern Matching
The structure of the input argument in run/2 depends on how you define
the input keys in your Recipe:
- Single Key: If input is
:my_data,run/2receives a single%Param{}. - List of Keys: If input is
[:a, :b],run/2receives a list[%Param{}, %Param{}]. - Tuple of Keys: If input is
{:a, :b},run/2receives a tuple{%Param{}, %Param{}}.
Step Options
Options can be passed when defining the step in a recipe or injected dynamically. Reserved options include:
:extra_hooks_stack- A list of additional hooks to run for this specific step.:__reporter_ctx__- A map contains telemetry meta used byreport/3to send progress updates.
Summary
Types
The implementation of a step.
Options passed to the step execution context.
The schema definition of a step within a recipe.
Format: {Implementation, InputKeys, OutputKeys}.
A fully qualified step definition including options.
Format: {Implementation, InputKeys, OutputKeys, Options}.
Callbacks
Determines if this step contains an inner recipe (Nested Step).
Executes the step logic.
Validates the options passed to the step before execution.
Functions
Normalizes a step structure into the full 4-element tuple format.
Extracts the basic schema {Impl, Input, Output} from a step definition.
Injects or merges options into a step definition.
Reports progress or status to the executor.
Types
The implementation of a step.
module(): A module that implements theOrchid.Stepbehavior.function(): A functionfn input, opts -> {:ok, params} | {:error, term} end.
@type input() :: tuple() | Orchid.Param.t() | [Orchid.Param.t()]
@type input_keys() :: io_key()
@type output() :: tuple() | Orchid.Param.t() | [Orchid.Param.t()]
@type output_keys() :: io_key()
@type step_options() :: keyword()
Options passed to the step execution context.
@type step_schema() :: {implementation(), input_keys(), output_keys()}
The schema definition of a step within a recipe.
Format: {Implementation, InputKeys, OutputKeys}.
@type step_with_options() :: {implementation(), input_keys(), output_keys(), step_options()}
A fully qualified step definition including options.
Format: {Implementation, InputKeys, OutputKeys, Options}.
@type t() :: step_schema() | step_with_options()
Callbacks
@callback nested?() :: boolean()
Determines if this step contains an inner recipe (Nested Step).
@callback run(input(), step_options()) :: {:ok, output()} | {:error, term()}
Executes the step logic.
Arguments
input- The input parameters. Structure depends oninput_keysdefinition.opts- The keyword list of options available to this step.
Return Values
{:ok, output}- Execution succeeded.outputmust match the structure ofoutput_keys.{:error, reason}- Execution failed. The step (and potentially the workflow) halts.
@callback validate_options(step_options()) :: :ok | {:error, term()}
Validates the options passed to the step before execution.
This is useful for checking required configuration keys (e.g., API tokens, thresholds).
Returns :ok if valid, or {:error, reason} otherwise.
Functions
@spec ensure_full_step(t()) :: step_with_options()
Normalizes a step structure into the full 4-element tuple format.
@spec extract_schema(t()) :: step_schema()
Extracts the basic schema {Impl, Input, Output} from a step definition.
@spec inject_options( t(), keyword() ) :: step_with_options()
Injects or merges options into a step definition.
Supports both 3-element tuple (Schema) and 4-element tuple (Full Step).
Reports progress or status to the executor.
Example
def run(input, opts) do
report(opts, :processing, "Start heavy calculation...")
# ...
report(opts, :uploading, 50)
{:ok, result}
end