# `Nous.Agent.Callbacks`
[🔗](https://github.com/nyo16/nous/blob/v0.17.1/lib/nous/agent/callbacks.ex#L1)

Callback execution for agent events.

Supports two notification mechanisms:
1. **Map-based callbacks** - Functions in the context's callbacks map
2. **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_thinking_delta` - Streaming reasoning/thinking chunk received
  (only fires under the `stream: true` path of `Nous.Agent.run/3`; the legacy
  `Nous.Agent.run_stream/3` keeps emitting reasoning as `[thinking] …` on
  `:on_llm_new_delta` for backward compatibility)
- `: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"}
    })

# `event`

```elixir
@type event() ::
  :on_agent_start
  | :on_llm_new_delta
  | :on_llm_new_thinking_delta
  | :on_llm_new_message
  | :on_tool_call
  | :on_tool_response
  | :on_agent_complete
  | :on_error
```

# `payload`

```elixir
@type payload() :: any()
```

# `events`

```elixir
@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_thinking_delta` - Payload: `String.t()` (reasoning 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_thinking_delta,
     :on_llm_new_message, :on_tool_call, :on_tool_response,
     :on_agent_complete, :on_error]

# `execute`

```elixir
@spec execute(Nous.Agent.Context.t(), event(), payload()) :: :ok
```

Execute a callback for the given event.

This function:
1. Invokes the map-based callback if present in `ctx.callbacks`
2. Sends a process message if `ctx.notify_pid` is set

Safe to call even if no callbacks are configured.

## Parameters

  * `ctx` - The agent context
  * `event` - The event name (atom)
  * `payload` - Event-specific data

## Examples

    iex> parent = self()
    iex> ctx = Context.new(callbacks: %{on_llm_new_delta: fn _event, delta -> send(parent, {:delta, delta}) end})
    iex> Callbacks.execute(ctx, :on_llm_new_delta, "Hello")
    :ok
    iex> receive do {:delta, text} -> text end
    "Hello"

    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"

# `execute_many`

```elixir
@spec execute_many(Nous.Agent.Context.t(), [{event(), payload()}]) :: :ok
```

Execute multiple events in sequence.

## Examples

    iex> ctx = Context.new(notify_pid: self())
    iex> Callbacks.execute_many(ctx, [
    ...>   {:on_tool_call, %{id: "1", name: "search"}},
    ...>   {:on_tool_response, %{id: "1", result: "found"}}
    ...> ])
    :ok
    iex> receive do {:tool_call, call} -> call.name end
    "search"

# `has_callback?`

```elixir
@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

# `has_notification?`

```elixir
@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

# `put_callback`

```elixir
@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

# `remove_callback`

```elixir
@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

# `set_notify_pid`

```elixir
@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

# `to_message`

```elixir
@spec to_message(event(), payload()) :: tuple()
```

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_thinking_delta` | `{:agent_thinking, 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"}}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
