Nous.Agent.Callbacks (nous v0.13.3)
View SourceCallback execution for agent events.
Supports two notification mechanisms:
- Map-based callbacks - Functions in the context's callbacks map
- Process messages - Messages sent to the context's notify_pid
Callback Events
:on_agent_start- Agent run begins:on_llm_new_delta- Streaming text chunk received:on_llm_new_message- Complete LLM response received:on_tool_call- Tool invocation started:on_tool_response- Tool execution completed:on_agent_complete- Agent run finished successfully:on_error- Error occurred during execution
Map-Based Callbacks
Configure callbacks as a map of event handlers:
ctx = Context.new(callbacks: %{
on_llm_new_delta: fn _event, delta -> IO.write(delta) end,
on_tool_call: fn _event, call -> IO.inspect(call, label: "Tool") end
})Callback functions receive (event_name, payload) and their return values
are discarded (side-effects only).
Process Messages
For LiveView integration, set notify_pid:
ctx = Context.new(notify_pid: self())
# Will receive messages like:
# {:agent_delta, text}
# {:tool_call, %{id: ..., name: ..., arguments: ...}}
# {:agent_complete, result}Example
alias Nous.Agent.{Context, Callbacks}
# Execute callback (safe - handles missing callbacks)
Callbacks.execute(ctx, :on_llm_new_delta, "Hello")
# Execute with metadata
Callbacks.execute(ctx, :on_tool_call, %{
id: "call_123",
name: "search",
arguments: %{"query" => "elixir"}
})
Summary
Functions
List of all supported callback events.
Execute a callback for the given event.
Execute multiple events in sequence.
Check if a specific callback is configured.
Check if process notification is enabled.
Add a callback to the context.
Remove a callback from the context.
Set the notification PID.
Convert an event to a process message format.
Types
@type event() ::
:on_agent_start
| :on_llm_new_delta
| :on_llm_new_message
| :on_tool_call
| :on_tool_response
| :on_agent_complete
| :on_error
@type payload() :: any()
Functions
@spec events() :: [event()]
List of all supported callback events.
Events
:on_agent_start- Payload:%{agent: agent}:on_llm_new_delta- Payload:String.t()(text chunk):on_llm_new_message- Payload:Message.t():on_tool_call- Payload:%{id: String.t(), name: String.t(), arguments: map()}:on_tool_response- Payload:%{id: String.t(), name: String.t(), result: any()}:on_agent_complete- Payload: result map:on_error- Payload: error term
Examples
iex> Callbacks.events()
[:on_agent_start, :on_llm_new_delta, :on_llm_new_message, ...]
@spec execute(Nous.Agent.Context.t(), event(), payload()) :: :ok
Execute a callback for the given event.
This function:
- Invokes the map-based callback if present in
ctx.callbacks - Sends a process message if
ctx.notify_pidis set
Safe to call even if no callbacks are configured.
Parameters
ctx- The agent contextevent- The event name (atom)payload- Event-specific data
Examples
iex> ctx = Context.new(callbacks: %{on_llm_new_delta: fn _, d -> IO.write(d) end})
iex> Callbacks.execute(ctx, :on_llm_new_delta, "Hello")
:ok
iex> ctx = Context.new(notify_pid: self())
iex> Callbacks.execute(ctx, :on_llm_new_delta, "Hello")
iex> receive do {:agent_delta, text} -> text end
"Hello"
@spec execute_many(Nous.Agent.Context.t(), [{event(), payload()}]) :: :ok
Execute multiple events in sequence.
Examples
iex> Callbacks.execute_many(ctx, [
...> {:on_tool_call, %{id: "1", name: "search"}},
...> {:on_tool_response, %{id: "1", result: "found"}}
...> ])
:ok
@spec has_callback?(Nous.Agent.Context.t(), event()) :: boolean()
Check if a specific callback is configured.
Examples
iex> ctx = Context.new(callbacks: %{on_llm_new_delta: fn _, _ -> :ok end})
iex> Callbacks.has_callback?(ctx, :on_llm_new_delta)
true
iex> ctx = Context.new()
iex> Callbacks.has_callback?(ctx, :on_llm_new_delta)
false
@spec has_notification?(Nous.Agent.Context.t()) :: boolean()
Check if process notification is enabled.
Examples
iex> ctx = Context.new(notify_pid: self())
iex> Callbacks.has_notification?(ctx)
true
iex> ctx = Context.new()
iex> Callbacks.has_notification?(ctx)
false
@spec put_callback(Nous.Agent.Context.t(), event(), function()) :: Nous.Agent.Context.t()
Add a callback to the context.
Examples
iex> ctx = Context.new()
iex> ctx = Callbacks.put_callback(ctx, :on_llm_new_delta, fn _, d -> IO.write(d) end)
iex> Callbacks.has_callback?(ctx, :on_llm_new_delta)
true
@spec remove_callback(Nous.Agent.Context.t(), event()) :: Nous.Agent.Context.t()
Remove a callback from the context.
Examples
iex> ctx = Context.new(callbacks: %{on_llm_new_delta: fn _, _ -> :ok end})
iex> ctx = Callbacks.remove_callback(ctx, :on_llm_new_delta)
iex> Callbacks.has_callback?(ctx, :on_llm_new_delta)
false
@spec set_notify_pid(Nous.Agent.Context.t(), pid() | nil) :: Nous.Agent.Context.t()
Set the notification PID.
Examples
iex> ctx = Context.new()
iex> ctx = Callbacks.set_notify_pid(ctx, self())
iex> Callbacks.has_notification?(ctx)
true
Convert an event to a process message format.
This is used internally but exposed for testing.
Message Format
| Event | Message |
|---|---|
:on_agent_start | {:agent_start, payload} |
:on_llm_new_delta | {:agent_delta, text} |
:on_llm_new_message | {:agent_message, message} |
:on_tool_call | {:tool_call, %{id, name, arguments}} |
:on_tool_response | {:tool_result, %{id, name, result}} |
:on_agent_complete | {:agent_complete, result} |
:on_error | {:agent_error, error} |
Examples
iex> Callbacks.to_message(:on_llm_new_delta, "Hello")
{:agent_delta, "Hello"}
iex> Callbacks.to_message(:on_tool_call, %{id: "1", name: "search"})
{:tool_call, %{id: "1", name: "search"}}