Jido.Action behaviour (Jido v1.1.0-rc.2)
View SourceDefines a discrete, composable unit of functionality within the Jido system.
Each Action represents a delayed computation that can be composed with others to build complex workflows. Actions are defined at compile-time and provide a consistent interface for validating inputs, executing workflows, and handling results.
Features
- Compile-time configuration validation
- Runtime input parameter validation
- Consistent error handling and formatting
- Extensible lifecycle hooks
- JSON serialization support
Usage
To define a new Action, use the Jido.Action
behavior in your module:
defmodule MyAction do
use Jido.Action,
name: "my_action",
description: "Performs my action",
category: "processing",
tags: ["example", "demo"],
vsn: "1.0.0",
schema: [
input: [type: :string, required: true]
]
@impl true
def run(params, _context) do
# Your action logic here
{:ok, %{result: String.upcase(params.input)}}
end
end
Callbacks
Implementing modules must define the following callback:
run/2
: Executes the main logic of the Action.
Optional callbacks for custom behavior:
on_before_validate_params/1
: Called before parameter validation.on_after_validate_params/1
: Called after parameter validation.on_after_run/1
: Called after the Action's main logic has executed.
Error Handling
Actions use the OK
monad for consistent error handling. Errors are wrapped
in Jido.Error
structs for uniform error reporting across the system.
AI Tool Example
Actions can be converted to LLM compatible tools for use with LLM-based systems. This is particularly useful when building AI agents that need to interact with your system's capabilities:
# Define a weather action
iex> defmodule WeatherAction do
...> use Jido.Action,
...> name: "get_weather",
...> description: "Gets the current weather for a location",
...> category: "weather",
...> tags: ["weather", "location"],
...> vsn: "1.0.0",
...> schema: [
...> location: [
...> type: :string,
...> required: true,
...> doc: "The city or location to get weather for"
...> ]
...> ]
...>
...> @impl true
...> def run(params, _context) do
...> # Weather API logic here
...> {:ok, %{temperature: 72, conditions: "sunny"}}
...> end
...> end
{:module, WeatherAction, ...}
# Convert to tool format
iex> WeatherAction.to_tool()
%{
"name" => "get_weather",
"description" => "Gets the current weather for a location",
"parameters" => %{
"type" => "object",
"properties" => %{
"location" => %{
"type" => "string",
"description" => "The city or location to get weather for"
}
},
"required" => ["location"]
}
}
This tool definition can then be used with AI systems like OpenAI's function calling or other LLM frameworks that support tool/function specifications. The schema and validation ensure the AI system receives proper parameter constraints.
Testing
Actions can be tested directly by calling their run/2
function with test parameters and context:
defmodule WeatherActionTest do
use ExUnit.Case
test "gets weather for location" do
params = %{location: "Portland"}
context = %{}
assert {:ok, result} = WeatherAction.run(params, context)
assert is_map(result)
assert result.temperature > 0
end
test "handles invalid location" do
params = %{location: ""}
context = %{}
assert {:error, error} = WeatherAction.run(params, context)
assert error.type == :validation_error
end
end
For testing Actions in a more complete runtime environment, including signal routing, state
management, and error handling, use Jido.Exec
. This provides a full test harness for
validating Action behavior within s:
test "weather action in " do
{:ok, result} = Exec.run(WeatherAction, %{location: "Seattle"})
assert result.weather_data.temperature > 0
end
See Jido.Exec
documentation for more details on action-based testing.
Parameter Validation
Note on Validation: The validation process for Actions is intentionally open. Only fields specified in the schema are validated. Unspecified fields are not validated, allowing for easier Action composition. This approach enables Actions to accept and pass along additional parameters that may be required by other Actions in a chain without causing validation errors.
Summary
Callbacks
Called after the Action's main logic has executed.
Called after parameter validation.
Called before parameter validation.
Handles errors and performs compensation when enabled.
Executes the Action with the given parameters and context.
Functions
Defines a new Action module.
Raises an error indicating that Actions cannot be defined at runtime.
Types
Callbacks
Called after the Action's main logic has executed.
This optional callback allows for post-processing of the Action's result before it is returned to the caller.
Parameters
result
: The result map returned by therun/2
function.
Returns
{:ok, modified_result}
wheremodified_result
is a potentially modified result map.{:error, reason}
if post-processing fails.
Called after parameter validation.
This optional callback allows for post-processing of validated parameters
before they are passed to the run/2
function.
Parameters
params
: A map of validated input parameters.
Returns
{:ok, modified_params}
wheremodified_params
is a map of potentially modified parameters.{:error, reason}
if post-processing fails.
Called before parameter validation.
This optional callback allows for pre-processing of input parameters before they are validated against the Action's schema.
Parameters
params
: A map of raw input parameters.
Returns
{:ok, modified_params}
wheremodified_params
is a map of potentially modified parameters.{:error, reason}
if pre-processing fails.
@callback on_error( failed_params :: map(), error :: Jido.Error.t(), context :: map(), opts :: keyword() ) :: {:ok, map()} | {:error, Jido.Error.t()}
Handles errors and performs compensation when enabled.
Called when an error occurs during Action execution if compensation is enabled in the Action's configuration.
Parameters
failed_params
: The parameters that were passed to the failed executionerror
: The Error struct describing what went wrongcontext
: The execution context at the time of failureopts
: Additional options for compensation handling
Returns
{:ok, result}
if compensation succeeded{:error, reason}
if compensation failed
Examples
def on_error(params, error, context, opts) do
# Perform compensation logic
case rollback_changes(params) do
:ok -> {:ok, %{compensated: true, original_error: error}}
{:error, reason} -> {:error, "Compensation failed: #{reason}"}
end
end
Executes the Action with the given parameters and context.
This callback must be implemented by modules using Jido.Action
.
Parameters
params
: A map of validated input parameters.context
: A map containing any additional context for the .
Returns
{:ok, result}
whereresult
is a map containing the 's output.{:error, reason}
wherereason
describes why the failed.
Functions
Defines a new Action module.
This macro sets up the necessary structure and callbacks for a Action, including configuration validation and default implementations.
Options
:name
- Required. The name of the Action. Must contain only letters, numbers, and underscores.:description
(String.t/0
) - A description of what the Action does.:category
(String.t/0
) - The category of the Action.:tags
(list ofString.t/0
) - A list of tags associated with the Action. The default value is[]
.:vsn
(String.t/0
) - The version of the Action.:compensation
(keyword/0
) - The default value is[]
.:enabled
(boolean/0
) - The default value isfalse
.:max_retries
(non_neg_integer/0
) - The default value is1
.:timeout
(non_neg_integer/0
) - The default value is5000
.
:schema
(keyword/0
) - A NimbleOptions schema for validating the Action's input parameters. The default value is[]
.
Examples
defmodule MyAction do
use Jido.Action,
name: "my_action",
description: "Performs a specific ",
schema: [
input: [type: :string, required: true]
]
@impl true
def run(params, _context) do
{:ok, %{result: String.upcase(params.input)}}
end
end
@spec new() :: {:error, Jido.Error.t()}
Raises an error indicating that Actions cannot be defined at runtime.
This function exists to prevent misuse of the Action system, as Actions are designed to be defined at compile-time only.
Returns
Always returns {:error, reason}
where reason
is a config error.
Examples
iex> Jido.Action.new()
{:error, %Jido.Error{type: :config_error, message: "Actions should not be defined at runtime"}}
@spec new(map() | keyword()) :: {:error, Jido.Error.t()}