Jido Action
Validated Actions and data-first Flow composition for Elixir.
jido_action is part of the Jido
ecosystem. See jido.run for the project and its packages.
jido_action defines validated actions, executable call frames, data-first Flows,
and one public execution boundary.
Jido Flow is a declarative, in-memory graph execution layer for Jido Actions. Runic owns graph mechanics, planning, runnable discovery, node execution, and graph-state transitions. Jido Flow owns its DSL, validation, lossless Map/JSON representation, compilation, and Flow semantics. Jido Exec owns one in-memory execution session: step-wise execution, bounded concurrency, Action invocation, errors, telemetry, and final results.
Durable orchestration is not provided. An outer system must own persistence,
queues, scheduling, recovery, retries, cancellation policy, distributed
coordination, supervision, and deployment-safe continuation. Jido.Exec can
enforce one caller-selected timeout for a complete in-memory call.
This foundation keeps the action boundary small:
Jido.Executableis the advanced descriptor API for the common Action and Flow target contract.Jido.Actiondefines a named action with Zoi input and output schemas.Jido.Instructioncaptures one requested executable call as data.Jido.Flowcomposes actions as a validated graph with steps and Choices.Jido.Execruns actions, instructions, and Flows, including step-wise Flows.
Version 3.0.0-beta.1 is a public beta. It introduces the declarative Flow DSL, runtime Flow construction, safe stored Flow maps, and one Flow execution engine. The v3 API can still change before the stable release, and this beta locks Runic 0.1.0-alpha.9. Use it for evaluation and controlled trials before you use it for critical production work. See the v3 migration guide for the confirmed breaking changes.
Install
def deps do
[
{:jido_action, "~> 3.0.0-beta.1"}
]
end
Define An Action
defmodule MyApp.Actions.GreetUser do
use Jido.Action,
name: "greet_user",
description: "Builds a greeting for a user",
schema:
Zoi.object(%{
name: Zoi.string() |> Zoi.min(1),
excited?: Zoi.boolean() |> Zoi.default(false)
}),
output_schema:
Zoi.object(%{
greeting: Zoi.string()
})
@impl true
def run(%{name: name, excited?: excited?}, _context) do
suffix = if excited?, do: "!", else: "."
{:ok, %{greeting: "Hello, #{name}#{suffix}"}}
end
end
Public action functions:
name/0description/0schema/0output_schema/0validate_params/1validate_output/1run/2
Run An Action
{:ok, %{greeting: "Hello, Ada!"}} =
Jido.Exec.run(
MyApp.Actions.GreetUser,
%{name: "Ada", excited?: true},
%{request_id: "req-123"}
)
Jido.Exec validates the Action input and output and runs the Action under the
configured Task Supervisor. Code that integrates its own executor can use
validate_params/1, run/2, and validate_output/1 directly.
The Action run/2 callback must return one of:
{:ok, result}{:ok, result, extra}{:error, reason}{:error, reason, extra}
Three-tuple returns let callers receive an extra value alongside the result or error.
Capture A Call Frame
Use Jido.Instruction when the intent to run an executable needs to be passed,
logged, queued, or enriched before execution.
instruction =
Jido.Instruction.new!(
target: MyApp.Actions.GreetUser,
params: %{name: "Ada"},
context: %{request_id: "req-123"}
)
An Instruction holds one Action module, Flow module, or runtime Flow target. It does not define a workflow, program, or runtime policy.
Compose A Flow
Use Jido.Flow when several actions must execute as one validated graph.
defmodule MyApp.Actions.Notify do
use Jido.Action,
name: "notify",
schema: Zoi.object(%{message: Zoi.string()})
@impl true
def run(%{message: message}, _context) do
{:ok, %{message: message, status: "queued"}}
end
end
defmodule MyApp.Flows.GreetAndNotify do
use Jido.Flow,
name: "greet_and_notify",
schema: Zoi.object(%{name: Zoi.string()}),
output_schema: Zoi.map()
flow do
step "greet",
action: MyApp.Actions.GreetUser,
params: %{name: input(:name), excited?: false}
step "notify",
action: MyApp.Actions.Notify,
params: %{message: select(result("greet"), :greeting)}
output result("notify")
end
end
{:ok, result} =
Jido.Exec.run(MyApp.Flows.GreetAndNotify, %{name: "Ada"}, %{})
Every Flow declares one output expression. Flows also support ordered Choices, Map and Reduce collections, bounded Iterate components with State, independent components that can run in parallel, and a step-wise execution API.
Build A Flow At Runtime
Use Jido.Flow.Builder when runtime data defines the graph. Each node has an
explicit name, and each result reference uses that name.
alias Jido.Flow.Builder
builder =
Builder.new(name: "runtime_greeting")
|> Builder.step(
"greet",
MyApp.Actions.GreetUser,
%{name: Builder.input(:name), excited?: Builder.value(false)}
)
|> Builder.output(Builder.result("greet"))
{:ok, runtime_flow} = Builder.build(builder)
{:ok, %{greeting: "Hello, Ada."}} =
Jido.Exec.run(runtime_flow, %{name: "Ada"})
The Builder and the Flow module DSL produce the same canonical Flow model.
Load A Flow From JSON Or A Map
Use a versioned stored map when a database, web UI, or AI system defines the
Flow. The host owns a flat Jido.Flow.Registry that maps stable identifiers to
trusted Action modules, schemas, and data atoms.
registry =
Jido.Flow.Registry.new!(%{
"actions/greet-user/v1" => {:action, MyApp.Actions.GreetUser},
"schemas/empty/v1" => {:schema, []},
"atoms/excited/v1" => {:atom, :excited?},
"atoms/name/v1" => {:atom, :name}
})
{:ok, stored} = Jido.Flow.Codec.encode(runtime_flow, registry)
json = JSON.encode!(stored)
decoded = JSON.decode!(json)
case Jido.Flow.Codec.decode(decoded, registry) do
{:ok, flow} ->
Jido.Flow.validate_executable(flow)
{:error, error} ->
{:error, Jido.Flow.Error.to_map(error)}
end
Jido.Flow.Codec.decode/2 does not execute the Flow. Invalid or incomplete maps
return a structured error instead of raising. Stored identifiers cannot create
atoms or select a module outside the host Registry.
Use Jido.Flow.Codec.diagnose/2 for a browser or AI editor that needs all
independent stored-document and graph errors. It returns one ordered Splode
error group with JSON paths and never returns a partial Flow.
The Flow module DSL, Builder, stored JSON Codec, and direct constructors produce
one canonical %Jido.Flow{} model. The Codec uses explicit component kinds.
It does not infer old records or module names.
Run A Flow Step By Step
Run-to-completion and step-wise execution use the same engine:
{:ok, execution} = Jido.Exec.start(runtime_flow, %{name: "Ada"})
[runnable] = Jido.Exec.ready(execution)
%Runic.Workflow{} = Jido.Exec.workflow(execution)
%Jido.Flow.Compiled{} = Jido.Exec.compiled(execution)
{:ok, %Runic.Workflow.Runnable{status: :completed}, execution} =
Jido.Exec.step(execution, runnable)
:succeeded = Jido.Exec.status(execution)
{:ok, %{greeting: "Hello, Ada."}} = Jido.Exec.result(execution)
wave/1 runs the current ready set. continue/1 runs until the Flow reaches a
terminal result. Always pass the newest execution value to the next call. The
caller owns this in-memory lifecycle. Jido does not persist or recover it.
Jido rejects reuse of a stale execution revision.
Observe Execution
Telemetry covers Action, Flow, Flow node, and collection work-unit lifecycles.
Direct Actions and Instructions emit [:jido, :action, :start],
[:jido, :action, :stop], and [:jido, :action, :error]. Flows and their
nodes use the [:jido, :flow] namespace. Map items, Reduce items, and Iterate
iterations add work-unit spans in that namespace. One execution_id
correlates nested work. Step and selected Choice Actions emit a target
lifecycle with the Action module and selected option. An Action inside a Flow
does not emit a separate direct Action lifecycle. Telemetry observes execution
only; it does not control scheduling or results.
See Execution for exact event names, measurements, metadata, nesting, and step-wise semantics.
Docs
Start with the runnable Getting Started
Livebook. ExDoc adds a Run in Livebook link to each .livemd guide.
Start Here
Core Contracts
Author Flows
- Flow DSL
- Steps And Output
- References And Data
- Dependencies And Parallel Work
- Choices And Conditions
- Map And Reduce
- Iterate And State
- Nested Flows
- Flow Modules
- Direct Construction And Builder
- Store Flows As JSON
- Inspect Flows
Run And Operate
Upgrade
Jido Ecosystem
- Jido is the core agent framework.
- Jido website contains project documentation and news.
- Jido ecosystem lists the related packages.
- Jido Workbench provides development and inspection tools.
- Jido Discord is the community support channel.
Contributing
See the contribution guide for development and pull-request guidance.
License
Copyright 2024-2026 Mike Hostetler
Licensed under the Apache License, Version 2.0. See LICENSE.