Packages
AI agent framework for Elixir built on OTP. TEA-based agents with crash isolation, inter-agent messaging, team supervision, and real SSE streaming to Anthropic, OpenAI, Ollama, and more.
Current section
Files
Jump to
Current section
Files
lib/raxol/agent.ex
defmodule Raxol.Agent do
@moduledoc """
Thin layer over `Raxol.Core.Runtime.Application` for building AI agents.
Agents implement the same TEA callbacks (`init/1`, `update/2`, `view/1`)
as any Raxol app. Input comes from LLMs, tools, or other agents instead
of a keyboard. OTP provides supervision, crash isolation, and hot reload.
`view/1` is optional -- headless agents skip rendering entirely.
## Example
defmodule MyAgent do
use Raxol.Agent
def init(_context) do
%{findings: [], status: :idle}
end
def update({:agent_message, _from, {:analyze, file}}, model) do
{%{model | status: :working}, Command.async(fn sender ->
result = do_analysis(file)
sender.({:analysis_done, result})
end)}
end
def update({:command_result, {:analysis_done, result}}, model) do
{%{model | findings: [result | model.findings], status: :idle}, Command.none()}
end
end
"""
defmacro __using__(_opts) do
quote do
@behaviour Raxol.Core.Runtime.Application
import Raxol.Core.Renderer.View, except: [view: 1]
alias Raxol.Core.Events.Event
alias Raxol.Core.Runtime.Command
def init(_), do: %{}
def update(_, state), do: {state, Command.none()}
def view(_model), do: nil
def subscribe(_), do: []
def subscriptions(_), do: []
def handle_event(_), do: nil
def handle_tick(model), do: {model, []}
def handle_message(_, model), do: {model, []}
def terminate(_, _), do: :ok
@doc """
Returns compaction config for automatic session history compaction.
Override to customize token limits. Return `nil` to disable compaction.
Agents that store message history in `model.history` get automatic
compaction after each action in `Agent.Process`.
## Default Config
%{max_tokens: 8_000, preserve_recent: 4, summary_max_tokens: 1_000}
"""
def compaction_config do
%{max_tokens: 8_000, preserve_recent: 4, summary_max_tokens: 1_000}
end
@doc """
Returns a list of `Raxol.Agent.CommandHook` modules for this agent.
Hooks intercept command execution with pre/post semantics.
Return `[]` to disable hooks (default).
"""
def command_hooks, do: []
@doc """
Returns a list of Action modules available to this agent.
Used by `Agent.Process` with a Strategy to determine which
Actions can be invoked. Override to expose Actions as tools.
Return `[]` to disable (default).
"""
def available_actions, do: []
defoverridable init: 1,
update: 2,
view: 1,
subscribe: 1,
subscriptions: 1,
handle_event: 1,
handle_tick: 1,
handle_message: 2,
terminate: 2,
compaction_config: 0,
command_hooks: 0,
available_actions: 0
@doc false
def async(fun), do: Command.async(fun)
@doc false
def shell(command, opts \\ []), do: Command.shell(command, opts)
@doc false
def send_agent(target, message), do: Command.send_agent(target, message)
@doc "Run an action synchronously. Returns `{:ok, result}` or `{:error, reason}`."
@spec run_action(module(), map(), map()) ::
{:ok, term()} | {:error, term()}
def run_action(action_module, params, context \\ %{}) do
action_module.call(params, context)
end
@doc "Run an action asynchronously. Result arrives as `{:command_result, {:action_result, module, result}}`."
@spec run_action_async(module(), map(), map()) :: Command.t()
def run_action_async(action_module, params, context \\ %{}) do
Command.async(fn sender ->
case action_module.call(params, context) do
{:ok, result} ->
sender.({:action_result, action_module, result})
{:ok, result, _commands} ->
sender.({:action_result, action_module, result})
{:error, reason} ->
sender.({:action_error, action_module, reason})
end
end)
end
@doc "Run a pipeline asynchronously. Result arrives as `{:command_result, {:pipeline_result, result}}`."
@spec run_pipeline_async([module()], map(), map()) :: Command.t()
def run_pipeline_async(steps, params, context \\ %{}) do
Command.async(fn sender ->
case Raxol.Agent.Action.Pipeline.run(steps, params, context) do
{:ok, result, _commands} ->
sender.({:pipeline_result, result})
{:error, {step, reason}} ->
sender.({:pipeline_error, step, reason})
end
end)
end
end
end
end