ExAgent.Capability behaviour (ExAgent v0.1.0)

Copy Markdown View Source

Composable middleware that observes/transforms an agent run at well-defined points. This is ExAgent's "capabilities" spine, modelled the Elixir way: a behaviour whose callbacks default to no-ops via use ExAgent.Capability, so a capability overrides only the hooks it cares about.

A capability is any module. It is passed as the first argument to its own callbacks (so it can carry config as module state or read its own attributes).

Hooks (all optional)

  • before_model_request(cap, state) — return (possibly modified) state. Set state.request_messages to alter what's sent to the model without touching the canonical history (e.g. keep a sliding window, redact PII).
  • after_model_request(cap, state) — observe/modify state after the model responded (state already includes the new response + merged usage).
  • before_tool_execute(cap, ctx, tool_call) — observe/replace a tool call.
  • after_tool_execute(cap, ctx, tool_call, result) — observe a tool result.

Example

defmodule MyApp.RedactPII do
  use ExAgent.Capability

  @impl true
  def before_model_request(_cap, state) do
    redacted = redact(state.messages)
    %{state | request_messages: redacted}
  end
end

ExAgent.new(model: "openai:gpt-4o", capabilities: [MyApp.RedactPII])

Summary

Types

state()

@type state() :: map()

Callbacks

after_model_request(cap, state)

(optional)
@callback after_model_request(cap :: module(), state()) :: state()

after_tool_execute(cap, map, struct, term)

(optional)
@callback after_tool_execute(cap :: module(), map(), struct(), term()) :: term()

before_model_request(cap, state)

(optional)
@callback before_model_request(cap :: module(), state()) :: state()

before_tool_execute(cap, map, struct)

(optional)
@callback before_tool_execute(cap :: module(), map(), struct()) :: struct()