# `LangChain.MessageDelta`
[🔗](https://github.com/brainlid/langchain/blob/v0.8.9/lib/message_delta.ex#L1)

Models a "delta" message from a chat LLM. A delta is a small chunk, or piece
of a much larger complete message. A series of deltas are used to construct
the complete message.

Delta messages must be applied in order for them to be valid. Delta messages
can be combined and transformed into a `LangChain.Message` once the final
piece is received.

## Roles

* `:unknown` - The role data is missing for the delta.
* `:assistant` - Responses coming back from the LLM.

## Tool Usage

Tools can be used or called by the assistant (LLM). A tool call is also split
across many message deltas and must be fully assembled before it can be
executed.

## Metadata

The `metadata` field is a map that can contain any additional information
about the message delta. It is used to store token usage, model, and other
LLM-specific information.

## Content Fields

The `content` field may contain:
- A string (for backward compatibility)
- A `LangChain.Message.ContentPart` struct
- An empty list `[]` that is received from some services like Anthropic, which
  is a signal that the content will be a list of content parts

The module uses two content-related fields:

* `content` - The raw content received from the LLM. This can be either a
  string (for backward compatibility), a `LangChain.Message.ContentPart`
  struct, or a `[]` indicating it will be a list of content parts. This field
  is cleared (set to `nil`) after merging into `merged_content`.

* `merged_content` - The accumulated list of `ContentPart`s after merging
  deltas. This is the source of truth for the message content and is used when
  converting to a `LangChain.Message`. When merging deltas:
  - For string content, it's converted to a `ContentPart` of type `:text`
  - For `ContentPart` content, it's merged based on the `index` field
  - Multiple content parts can be maintained in the list to support
    multi-modal responses (text, images, audio) or separate thinking content
    from final text

# `t`
[🔗](https://github.com/brainlid/langchain/blob/v0.8.9/lib/message_delta.ex#L82)

```elixir
@type t() :: %LangChain.MessageDelta{
  content: term(),
  index: term(),
  merged_content: term(),
  metadata: term(),
  role: term(),
  status: term(),
  tool_calls: term()
}
```

# `accumulate_token_usage`
[🔗](https://github.com/brainlid/langchain/blob/v0.8.9/lib/message_delta.ex#L495)

```elixir
@spec accumulate_token_usage(t(), t()) :: t()
```

Accumulates token usage from delta messages. Uses `LangChain.TokenUsage.add/2` to combine
the usage data from both deltas.

## Example

    iex> alias LangChain.TokenUsage
    iex> alias LangChain.MessageDelta
    iex> delta1 = %MessageDelta{
    ...>   metadata: %{
    ...>     usage: %TokenUsage{input: 10, output: 5}
    ...>   }
    ...> }
    iex> delta2 = %MessageDelta{
    ...>   metadata: %{
    ...>     usage: %TokenUsage{input: 5, output: 15}
    ...>   }
    ...> }
    iex> result = MessageDelta.accumulate_token_usage(delta1, delta2)
    iex> result.metadata.usage.input
    15
    iex> result.metadata.usage.output
    20

# `all_tools_terminal?`
[🔗](https://github.com/brainlid/langchain/blob/v0.8.9/lib/message_delta.ex#L667)

```elixir
@spec all_tools_terminal?(t()) :: boolean()
```

Check whether every `ToolCall` in the delta has reached a terminal
execution status. Returns `true` only when there is at least one tool
call AND every call has `metadata["execution_status"]` set to
`"completed"` or `"failed"`.

Returns `false` for `nil` or empty `tool_calls` — there is nothing to
gate on yet.

Intended for UI flows that keep a streaming delta visible while any
tool is still mid-execution and only clear it once all tools have
terminated, so sibling tool calls don't lose their UI state when one
finishes before another.

## Examples

    iex> alias LangChain.Message.ToolCall
    iex> done = %ToolCall{call_id: "a", metadata: %{"execution_status" => "completed"}}
    iex> running = %ToolCall{call_id: "b", metadata: %{"execution_status" => "executing"}}
    iex> LangChain.MessageDelta.all_tools_terminal?(%LangChain.MessageDelta{tool_calls: [done]})
    true
    iex> LangChain.MessageDelta.all_tools_terminal?(%LangChain.MessageDelta{tool_calls: [done, running]})
    false

# `content_to_string`
[🔗](https://github.com/brainlid/langchain/blob/v0.8.9/lib/message_delta.ex#L422)

```elixir
@spec content_to_string(t(), type :: atom()) :: nil | String.t()
```

Convert the MessageDelta's merged content to a string. Specify the type of
content to convert so it can return just the text parts or thinking parts,
etc. Defaults to `:text`.

# `merge_delta`
[🔗](https://github.com/brainlid/langchain/blob/v0.8.9/lib/message_delta.ex#L158)

```elixir
@spec merge_delta(nil | t(), t() | {:error, any()}) :: t()
```

Merge two `MessageDelta` structs. The first `MessageDelta` is the `primary`
one that smaller deltas are merged into.

The merging process:
1. Migrates any string content to `ContentPart`s for backward compatibility
2. Merges the content into `merged_content` based on the `index` field
3. Clears the `content` field (sets to `nil`) after merging
4. Updates other fields (tool_calls, status, etc.)

## Examples

    iex> delta_1 =
    ...>   %LangChain.MessageDelta{
    ...>     content: nil,
    ...>     index: 0,
    ...>     tool_calls: [],
    ...>     role: :assistant,
    ...>     status: :incomplete
    ...>   }
    iex> delta_2 =
    ...>   %LangChain.MessageDelta{
    ...>     content: "Hello",
    ...>     index: 0,
    ...>     tool_calls: [],
    ...>     role: :unknown,
    ...>     status: :incomplete
    ...>   }
    iex> LangChain.MessageDelta.merge_delta(delta_1, delta_2)
    %LangChain.MessageDelta{
      content: nil,
      merged_content: [%LangChain.Message.ContentPart{type: :text, content: "Hello"}],
      status: :incomplete,
      index: 0,
      role: :assistant,
      tool_calls: []
    }

A set of deltas can be easily merged like this:

      MessageDelta.merge_deltas(list_of_delta_messages)

# `merge_deltas`
[🔗](https://github.com/brainlid/langchain/blob/v0.8.9/lib/message_delta.ex#L239)

```elixir
@spec merge_deltas([t()]) :: t()
```

Merges a list of `MessageDelta`s into a single `MessageDelta`. The deltas
are merged in order, with each delta being merged into the result of the
previous merge.

## Examples

    iex> deltas = [
    ...>   %LangChain.MessageDelta{content: "Hello", role: :assistant},
    ...>   %LangChain.MessageDelta{content: " world", role: :assistant},
    ...>   %LangChain.MessageDelta{content: "!", role: :assistant, status: :complete}
    ...> ]
    iex> LangChain.MessageDelta.merge_deltas(deltas)
    %LangChain.MessageDelta{
      content: nil,
      merged_content: [%LangChain.Message.ContentPart{type: :text, content: "Hello world!"}],
      status: :complete,
      role: :assistant
    }

# `merge_deltas`
[🔗](https://github.com/brainlid/langchain/blob/v0.8.9/lib/message_delta.ex#L211)

```elixir
@spec merge_deltas(nil | t(), [t()]) :: t()
```

Merges a list of `MessageDelta`s into an accumulated `MessageDelta`. This is
useful for UI applications that accumulate deltas as they are received from
a streaming LLM response.

The first argument is the accumulated delta (or `nil` if no deltas have been
processed yet). The second argument is a list of new deltas to merge in.

## Examples

    iex> accumulated = nil
    iex> batch_1 = [
    ...>   %LangChain.MessageDelta{content: "Hello", role: :assistant},
    ...>   %LangChain.MessageDelta{content: " world", role: :assistant}
    ...> ]
    iex> accumulated = LangChain.MessageDelta.merge_deltas(accumulated, batch_1)
    iex> batch_2 = [
    ...>   %LangChain.MessageDelta{content: "!", role: :assistant, status: :complete}
    ...> ]
    iex> result = LangChain.MessageDelta.merge_deltas(accumulated, batch_2)
    iex> result
    %LangChain.MessageDelta{
      content: nil,
      merged_content: [%LangChain.Message.ContentPart{type: :text, content: "Hello world!"}],
      status: :complete,
      role: :assistant
    }

# `migrate_to_content_parts`
[🔗](https://github.com/brainlid/langchain/blob/v0.8.9/lib/message_delta.ex#L524)

```elixir
@spec migrate_to_content_parts(t()) :: t()
```

Migrates a MessageDelta's string content to use `LangChain.Message.ContentPart`.
This is for backward compatibility with models that don't yet support ContentPart streaming.

## Examples

    iex> delta = %LangChain.MessageDelta{content: "Hello world"}
    iex> upgraded = migrate_to_content_parts(delta)
    iex> upgraded.content
    %LangChain.Message.ContentPart{type: :text, content: "Hello world"}

# `new`
[🔗](https://github.com/brainlid/langchain/blob/v0.8.9/lib/message_delta.ex#L91)

```elixir
@spec new(attrs :: map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
```

Create a new `MessageDelta` that represents a message chunk.

# `new!`
[🔗](https://github.com/brainlid/langchain/blob/v0.8.9/lib/message_delta.ex#L104)

```elixir
@spec new!(attrs :: map()) :: t() | no_return()
```

Create a new `MessageDelta` that represents a message chunk and return it or
raise an error if invalid.

# `set_tool_display_text`
[🔗](https://github.com/brainlid/langchain/blob/v0.8.9/lib/message_delta.ex#L634)

```elixir
@spec set_tool_display_text(t(), String.t(), String.t() | nil) :: t()
```

Set the `display_text` on the `ToolCall` with the matching `call_id`.

A non-nil incoming value overwrites the existing one. As a tool executes
and the caller learns more, it is reasonable to refine the display from
`"Reading file"` to `"Reading "outline.md" (lines 60–100)"`, and this
helper supports that flow.

A `nil` incoming value is a no-op and leaves the existing `display_text`
alone, so the UI never goes from showing something to showing nothing.

Returns the delta unchanged if no tool call matches the given `call_id`.

## Examples

    iex> alias LangChain.Message.ToolCall
    iex> delta = %LangChain.MessageDelta{
    ...>   role: :assistant,
    ...>   tool_calls: [%ToolCall{call_id: "abc", name: "search"}]
    ...> }
    iex> updated = LangChain.MessageDelta.set_tool_display_text(delta, "abc", "Searching")
    iex> hd(updated.tool_calls).display_text
    "Searching"

# `set_tool_execution_status`
[🔗](https://github.com/brainlid/langchain/blob/v0.8.9/lib/message_delta.ex#L603)

```elixir
@spec set_tool_execution_status(t(), String.t(), String.t()) :: t()
```

Find the `ToolCall` with the matching `call_id` and update its
`"execution_status"` metadata. Delegates to
`LangChain.Message.ToolCall.set_execution_status/2`.

Intended for callers like Phoenix LiveView and Sagents that track the
lifecycle of a streaming tool call (identified → executing →
completed/failed) and need to drive the UI as the agent progresses.

Returns the delta unchanged if no tool call matches the given `call_id`.

## Examples

    iex> alias LangChain.Message.ToolCall
    iex> delta = %LangChain.MessageDelta{
    ...>   role: :assistant,
    ...>   tool_calls: [%ToolCall{call_id: "abc", name: "search"}]
    ...> }
    iex> updated = LangChain.MessageDelta.set_tool_execution_status(delta, "abc", "executing")
    iex> hd(updated.tool_calls).metadata
    %{"execution_status" => "executing"}

# `to_message`
[🔗](https://github.com/brainlid/langchain/blob/v0.8.9/lib/message_delta.ex#L440)

```elixir
@spec to_message(t()) :: {:ok, LangChain.Message.t()} | {:error, String.t()}
```

Convert the MessageDelta to a Message. Can only convert a fully complete
MessageDelta.

This is assumed to be the result of merging all the received `MessageDelta`s.
An error is returned if the `status` is `:incomplete`.

If the `MessageDelta` fails to convert to a `LangChain.Message`, an error is
returned with the reason.

# `upsert_tool_call`
[🔗](https://github.com/brainlid/langchain/blob/v0.8.9/lib/message_delta.ex#L565)

```elixir
@spec upsert_tool_call(t(), LangChain.Message.ToolCall.t()) :: t()
```

Insert a `ToolCall` into the delta's `tool_calls` list, or merge its
non-nil fields onto an existing `ToolCall` with the same `call_id`.

Intended for streaming pipelines that learn about a tool call across
several events: the first event creates the call, later events fill in
fields like `display_text` or update `metadata`.

Merge rules when a `ToolCall` with the same `call_id` already exists:

- Non-nil scalar fields on the incoming call overwrite the existing value
  (`name`, `arguments`, `display_text`).
- `status` is only promoted forward — an incoming `:complete` overwrites
  an existing `:incomplete`, but never the other way around.
- `metadata` is shallow-merged, with incoming keys winning per-key.

A call with `call_id: nil` is ignored and the delta is returned unchanged
(there is no key to upsert against).

## Examples

    iex> alias LangChain.Message.ToolCall
    iex> delta = %LangChain.MessageDelta{role: :assistant, tool_calls: []}
    iex> tc = %ToolCall{call_id: "abc", name: "search"}
    iex> updated = LangChain.MessageDelta.upsert_tool_call(delta, tc)
    iex> updated.tool_calls
    [%ToolCall{call_id: "abc", name: "search"}]

---

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