ExAthena.Loop.Mode behaviour (ExAthena v0.4.3)

Copy Markdown View Source

Pluggable control-flow strategy for the agent loop.

Modes decide what to do each turn. The kernel handles everything else — tool dispatch, permissions, hooks, events, accounting. Modes sit on top and shape the iteration: ReAct is "infer → tool → loop", Plan-and-Solve is "plan once → execute many", Reflexion adds a self-critique pass.

Writing a mode

defmodule MyMode do
  @behaviour ExAthena.Loop.Mode

  @impl true
  def init(state), do: {:ok, state}

  @impl true
  def iterate(state) do
    # Run inference, handle tool calls, update state, decide continue/halt.
    # Return {:continue, state} to keep looping, {:halt, state} to stop.
    {:continue, state}
  end
end

The builtin ExAthena.Modes.ReAct is a reference implementation.

Atom shortcuts

:react, :plan_and_solve, :reflexion resolve to the builtin modules. Any other atom or a module reference is used verbatim.

Summary

Callbacks

Called once before the first iteration. Use to prime mode-specific state.

Drive one iteration. Return one of

Functions

Resolve an atom shortcut or module to the Mode module.

Callbacks

init(t)

@callback init(ExAthena.Loop.State.t()) ::
  {:ok, ExAthena.Loop.State.t()} | {:error, term()}

Called once before the first iteration. Use to prime mode-specific state.

iterate(t)

@callback iterate(ExAthena.Loop.State.t()) ::
  {:continue, ExAthena.Loop.State.t()}
  | {:halt, ExAthena.Loop.State.t()}
  | {:error, term()}

Drive one iteration. Return one of:

  • {:continue, State.t()} — keep looping (kernel checks caps + budget).
  • {:halt, State.t()} — stop looping; the kernel produces a Result from the terminal state's finish_reason (which the mode should set via ExAthena.Loop.set_finish_reason/2 before returning).
  • {:error, reason} — abort with an unrecoverable error. The kernel wraps this in :error_during_execution.

Functions

resolve(mode)

@spec resolve(atom() | module()) :: module()

Resolve an atom shortcut or module to the Mode module.