Nous.Plugin behaviour (nous v0.13.3)

View Source

Behaviour for composable agent extensions.

Plugins allow you to extend agent capabilities without modifying the core agent or creating monolithic behaviour modules. Multiple plugins can be composed together.

Example

defmodule MyApp.Plugins.Logging do
  @behaviour Nous.Plugin

  @impl true
  def init(_agent, ctx) do
    ctx
  end

  @impl true
  def before_request(_agent, ctx, _tools) do
    IO.puts("Making LLM request with #{length(ctx.messages)} messages")
    {ctx, []}
  end

  @impl true
  def after_response(_agent, response, ctx) do
    IO.puts("Got response: #{inspect(response.content)}")
    ctx
  end
end

Usage

agent = Agent.new("openai:gpt-4",
  plugins: [MyApp.Plugins.Logging, Nous.Plugins.TodoTracking]
)

Callback Execution Order

Plugins are executed in list order. The context flows through each plugin sequentially for all hooks:

  1. init/2 — once at run start
  2. system_prompt/2 — once, fragments joined into system message
  3. tools/2 — once per iteration, tools collected
  4. before_request/3 — before each LLM call
  5. after_response/3 — after each LLM response
  6. after_run/3 — once after the entire run completes (post-loop)

Built-in Plugins

Summary

Callbacks

Post-process after each LLM response.

Post-process after the entire agent run completes.

Pre-process before each LLM call.

Initialize the plugin when the agent run starts.

Contribute system prompt fragments.

Contribute additional tools for this agent run.

Functions

Collect system prompt fragments from all plugins.

Collect tools from all plugins.

Run after_response/3 across all plugins, threading context.

Run after_run/3 across all plugins, threading context.

Run before_request/3 across all plugins, threading context and tools.

Run init/2 across all plugins, threading context through each.

Callbacks

after_response(agent, response, ctx)

(optional)
@callback after_response(
  agent :: Nous.Agent.t(),
  response :: Nous.Message.t(),
  ctx :: Nous.Agent.Context.t()
) :: Nous.Agent.Context.t()

Post-process after each LLM response.

Receives the LLM response message and current context. Return the updated context.

after_run(agent, result, ctx)

(optional)
@callback after_run(
  agent :: Nous.Agent.t(),
  result :: map(),
  ctx :: Nous.Agent.Context.t()
) ::
  Nous.Agent.Context.t()

Post-process after the entire agent run completes.

Receives the agent, the final result map, and the final context. Return the updated context. Use this for end-of-run housekeeping like auto-updating memory.

before_request(agent, ctx, tools)

(optional)
@callback before_request(
  agent :: Nous.Agent.t(),
  ctx :: Nous.Agent.Context.t(),
  tools :: [Nous.Tool.t()]
) ::
  {Nous.Agent.Context.t(), [Nous.Tool.t()]}

Pre-process before each LLM call.

Receives the current context and tools list. Return the updated context and the updated tools list.

init(agent, ctx)

(optional)
@callback init(agent :: Nous.Agent.t(), ctx :: Nous.Agent.Context.t()) ::
  Nous.Agent.Context.t()

Initialize the plugin when the agent run starts.

Use this to set up initial state in ctx.deps, register callbacks, etc.

Called once at the start of each Nous.Agent.run/3.

system_prompt(agent, ctx)

(optional)
@callback system_prompt(agent :: Nous.Agent.t(), ctx :: Nous.Agent.Context.t()) ::
  String.t() | nil

Contribute system prompt fragments.

Return a string to append to the system prompt, or nil for no contribution. Fragments from all plugins are joined with newlines.

tools(agent, ctx)

(optional)
@callback tools(agent :: Nous.Agent.t(), ctx :: Nous.Agent.Context.t()) :: [Nous.Tool.t()]

Contribute additional tools for this agent run.

Return a list of Nous.Tool structs to add to the agent's tool set. Called once per iteration before the LLM request.

Functions

collect_system_prompts(plugins, agent, ctx)

@spec collect_system_prompts([module()], Nous.Agent.t(), Nous.Agent.Context.t()) ::
  String.t() | nil

Collect system prompt fragments from all plugins.

collect_tools(plugins, agent, ctx)

@spec collect_tools([module()], Nous.Agent.t(), Nous.Agent.Context.t()) :: [
  Nous.Tool.t()
]

Collect tools from all plugins.

run_after_response(plugins, agent, response, ctx)

@spec run_after_response(
  [module()],
  Nous.Agent.t(),
  Nous.Message.t(),
  Nous.Agent.Context.t()
) ::
  Nous.Agent.Context.t()

Run after_response/3 across all plugins, threading context.

run_after_run(plugins, agent, result, ctx)

@spec run_after_run([module()], Nous.Agent.t(), map(), Nous.Agent.Context.t()) ::
  Nous.Agent.Context.t()

Run after_run/3 across all plugins, threading context.

run_before_request(plugins, agent, ctx, tools)

@spec run_before_request([module()], Nous.Agent.t(), Nous.Agent.Context.t(), [
  Nous.Tool.t()
]) ::
  {Nous.Agent.Context.t(), [Nous.Tool.t()]}

Run before_request/3 across all plugins, threading context and tools.

Each plugin receives the current tools and returns the updated tools list.

run_init(plugins, agent, ctx)

Run init/2 across all plugins, threading context through each.