# `LangChain.Message.ContentPart`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L1)

Models a `ContentPart`. ContentParts are now used for multi-modal support in
both messages and tool results. This enables richer responses, allowing text,
images, files, and thinking blocks to be combined in a single message or tool
result.

## Types

- `:text` - The message part is text.
- `:image_url` - The message part is a URL to an image.
- `:image` - The message part is image data that is base64 encoded text.
- `:file` - The message part is file data that is base64 encoded text.
- `:file_url` - The message part is a URL to a file.
- `:thinking` - A thinking block from a reasoning model like Anthropic.
- `:unsupported` - A part that is not supported but may need to be present.
  This includes Anthropic's `redacted_thinking` block which has no value in
  being displayed because it is encrypted, but can be provided back to the LLM
  to maintain reasoning continuity. The specific parts of the data are stored
  in `:options`.

## Fields

- `:content` - Text content.
- `:options` - Options are a keyword list of values that may be specific to
  the LLM for a particular message type. For example, multi-modal message
  (ones that include image data) use the `:media` option to specify the
  mimetype information. Options may also contain key-value settings like
  `cache_control: true` for models like Anthropic that support caching, or
  `prompt_cache_breakpoint: %{mode: "explicit"}` for supported OpenAI Responses
  input blocks. See `LangChain.ChatModels.ChatOpenAIResponses` for prompt
  caching configuration.

  When receiving content parts like with Anthropic Claude's thinking model,
  the options may contain LLM specific data that is recommended to be
  preserved like a `signature` or `redacted_thinking` data used by the LLM.

  A map of options is accepted and converted to a keyword list, so a part
  holds the same shape whichever way it was built and keeps it across a
  serialization round trip. The keys must be atoms; a map with a string key
  is rejected rather than converted, because turning caller-supplied strings
  into atoms would let untrusted input grow the atom table.

## Narration and answers

Some models speak more than once in a single turn. Before calling a tool, a
model may say what it is about to do ("I'll check the logs first"). That text
is narration: a status update about work in progress, not a reply. The turn
is not over until the model answers.

A part records which kind of utterance it is in the `:utterance` option:

- `"narration"` - the model is describing work in progress and has more to
  do. Build one with `narration!/1`.
- `"answer"` - the model is replying. Build one with `answer!/1`.
- `nil` (option absent) - unmarked. The provider did not say, and the part is
  treated as an answer.

Read the marker with `utterance/1` or `narration?/1` rather than from the
options directly. Chat models that support the distinction set the marker on
the parts they decode, and send it back to the provider when the message is
replayed as history, because a model that sees its own narration replayed as
an unlabelled reply behaves worse on later turns.

`LangChain.Message.narration?/1` answers the question for a whole message,
and `LangChain.Chains.LLMChain` uses it to keep a turn open.

## Image mime types

The `:media` option is used to specify the mime type of the image. Various
LLMs handle this differently or perhaps not at all.

Examples:

- `media: :jpg` - turns into `"image/jpeg"` or `"image/jpg"`, depending on
  what the LLM accepts.
- `media: :png` - turns into `"image/png"`
- `media: "image/webp"` - stays as `"image/webp"`. Any specified string value
  is passed through unchanged. This allows for future formats to be supported
  quickly.
- When omitted, the LLM may error or some will accept it but may require the
  `base64` encoded content data to be prefixed with the mime type information.
  Basically, you must handle the content needs yourself.

# `t`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L106)

```elixir
@type t() :: %LangChain.Message.ContentPart{
  citations: term(),
  content: term(),
  options: term(),
  type: term()
}
```

# `utterance`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L109)

```elixir
@type utterance() :: String.t()
```

The kind of utterance a text part holds. See "Narration and answers".

# `answer!`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L202)

```elixir
@spec answer!(String.t()) :: t() | no_return()
```

Create a text ContentPart explicitly marked as an answer. Raises an exception
if not valid.

# `citations`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L480)

```elixir
@spec citations(t()) :: [LangChain.Message.Citation.t()]
```

Returns the citations for this content part, defaulting to empty list.

# `content_to_string`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L468)

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

Convert "content" to a string. Content may be `nil`, a string, or a list of ContentParts.

# `file!`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L273)

```elixir
@spec file!(String.t(), Keyword.t()) :: t() | no_return()
```

Create a new ContentPart that contains a file encoded as base64 data.

# `file_url!`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L289)

```elixir
@spec file_url!(String.t(), Keyword.t()) :: t() | no_return()
```

Create a new ContentPart that contains a URL to an file. Raises an exception if not valid.

# `has_citations?`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L487)

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

Returns true if this content part has any citations.

# `image!`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L265)

```elixir
@spec image!(String.t(), Keyword.t()) :: t() | no_return()
```

Create a new ContentPart that contains an image encoded as base64 data. Raises
an exception if not valid.

## Options

- `:media` - Provide the "media type" for the image. Examples: "image/jpeg",
  "image/png", etc. ChatGPT does not require this but other LLMs may.
- `:detail` - if the LLM supports it, most images must be resized or cropped
  before given to the LLM for analysis. A detail option may specify the level
  detail of the image to present to the LLM. The higher the detail, the more
  tokens consumed. Currently only supported by OpenAI and the values of "low",
  "high", and "auto".

ChatGPT requires media type information to prefix the base64 content. Setting
the `media: "image/jpeg"` type will do that. Otherwise the data must be
provided with the required prefix.

Anthropic requires the media type information to be submitted as separate
information with the JSON request. This media option provides an abstraction
to normalize the behavior.

# `image_url!`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L281)

```elixir
@spec image_url!(String.t(), Keyword.t()) :: t() | no_return()
```

Create a new ContentPart that contains a URL to an image. Raises an exception if not valid.

# `merge_part`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L326)

```elixir
@spec merge_part(nil | t(), t()) :: t()
```

Merge two `ContentPart` structs for the same index in a MessageDelta. The
first `ContentPart` is the `primary` one that smaller deltas are merged into.
The primary is what is being accumulated.

A set of ContentParts can be merged like this:

    Enum.reduce(list_of_content_parts, nil, fn new_part, acc ->
      ContentPart.merge_part(acc, new_part)
    end)

# `narration!`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L195)

```elixir
@spec narration!(String.t()) :: t() | no_return()
```

Create a text ContentPart marked as narration: the model describing work in
progress rather than answering. Raises an exception if not valid.

## Example

    ContentPart.narration!("I'll check the logs first.")

# `narration?`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L240)

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

Return `true` when the part is marked as narration.

# `new`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L121)

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

Build a new message and return an `:ok`/`:error` tuple with the result.

# `new!`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L168)

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

Build a new message and return it or raise an error if invalid.

## Example

    ContentPart.new!(%{type: :text, content: "Greetings!"})

    ContentPart.new!(%{type: :image_url, content: "https://example.com/images/house.jpg"})

    ContentPart.new!(%{type: :thinking, content: "I've been asked...", options: [signature: "SIGNATURE_DATA"]}

    ContentPart.new!(%{type: :unsupported, content: "redacted_data", options: [type: "redacted_thinking"]}

# `parts_to_string`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L449)

```elixir
@spec parts_to_string([t() | nil], type :: atom()) :: nil | String.t()
```

Helper function for easily getting plain text from a list of ContentParts.

This function processes a list of ContentParts and joins the text parts together
using "

" characters. Only parts where `type: :text` are used. All other parts
are ignored.

## Examples

    iex> parts = [
    ...>   text!("Hello"),
    ...>   image!("base64data"),
    ...>   text!("world")
    ...> ]
    iex> parts_to_string(parts)
    "Hello\n\nworld"

    iex> parts_to_string([])
    nil

# `put_utterance`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L209)

```elixir
@spec put_utterance(t(), utterance()) :: t()
```

Mark a part as `"narration"` or `"answer"`. Any other value raises a
`FunctionClauseError`.

# `set_option_on_last_part`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L412)

```elixir
@spec set_option_on_last_part([t()], atom(), any()) :: [t()]
```

Sets an option on the last text part in a list of ContentParts. Returns the updated content parts.

# `text!`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L182)

Create a new ContentPart that contains text. Raises an exception if not valid.

# `thinking!`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L297)

```elixir
@spec thinking!(String.t(), Keyword.t()) :: t() | no_return()
```

Create a new ContentPart that contains thinking text. Raises an exception if not valid.

# `utterance`
[🔗](https://github.com/brainlid/langchain/blob/v0.15.3/lib/message/content_part.ex#L227)

```elixir
@spec utterance(t()) :: utterance() | nil
```

Return the part's utterance marker: `"narration"`, `"answer"`, or `nil` when
the part is unmarked. A value other than the two recognized strings reads as
`nil`.

## Examples

    iex> utterance(narration!("Looking that up."))
    "narration"

    iex> utterance(text!("Hello"))
    nil

---

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