# `Puck.Content`
[🔗](https://github.com/bradleygolden/puck/blob/v0.2.25/lib/puck/content.ex#L1)

Multi-modal content for messages.

## Types

- `text/1` - Plain text
- `image_url/1` - Image from URL
- `image/2` - Binary image data
- `file/2` - Files (PDF, CSV, etc.)
- `audio/2`, `video/2` - Media content

## Examples

    alias Puck.Content

    Content.text("What's in this image?")
    Content.image_url("https://example.com/cat.png")
    Content.image(image_bytes, "image/png")
    Content.file(pdf_bytes, "application/pdf", filename: "report.pdf")

    # Multi-modal call
    client = Puck.Client.new({Puck.Backends.ReqLLM, "anthropic:claude-sonnet-4-5"})
    {:ok, response, _ctx} = Puck.call(client, [
      Content.text("Describe this"),
      Content.image_url("https://example.com/photo.png")
    ])

# `audio`

Creates an audio content part.

## Examples

    audio_bytes = File.read!("speech.mp3")
    Content.audio(audio_bytes, "audio/mp3")

# `file`

Creates a file content part.

Supports any file type - PDFs, CSVs, documents, etc.
The data should be raw binary bytes.

## Options

- `:filename` - Optional filename for the file

## Examples

    pdf_bytes = File.read!("report.pdf")
    Content.file(pdf_bytes, "application/pdf")
    Content.file(pdf_bytes, "application/pdf", filename: "report.pdf")

    csv_bytes = File.read!("data.csv")
    Content.file(csv_bytes, "text/csv", filename: "data.csv")

# `image`

Creates an image content part from binary data.

The data should be the raw binary image bytes (not base64 encoded).
Encoding is handled by the backend adapter.

## Examples

    bytes = File.read!("photo.png")
    Content.image(bytes, "image/png")

# `image_url`

Creates an image URL content part.

## Examples

    Content.image_url("https://example.com/photo.jpg")

# `new`

Creates a generic content part with custom type.

Use this for provider-specific content types not covered by the
standard factory functions.

## Examples

    Content.new(:thinking, text: "Let me think about this...")
    Content.new(:tool_result, text: "42", metadata: %{tool_id: "calc_1"})

# `text`

Creates a text content part.

## Examples

    Content.text("Hello, world!")
    Content.text("Analyze this", %{cache: true})

# `video`

Creates a video content part.

## Examples

    video_bytes = File.read!("clip.mp4")
    Content.video(video_bytes, "video/mp4")

# `wrap`

Wraps content into a list of Content.Part structs.

Delegates to the `Puck.Content.Wrappable` protocol, which handles:
- Strings → text parts
- Part structs → wrapped in list
- Lists of parts → pass through unchanged
- Maps/structs → JSON-encoded into text parts

Maps and structs are JSON-encoded to support tool results and structured
data in multi-turn conversations. When a backend returns structured data
like `%{result: 42}` or a struct, this is serialized so the LLM can read it.

## Customizing Behavior

Implement the `Puck.Content.Wrappable` protocol for custom types:

    defimpl Puck.Content.Wrappable, for: MyApp.CustomType do
      def wrap(value), do: [Puck.Content.text(to_string(value))]
    end

## Examples

    Content.wrap("Hello")
    #=> [%Part{type: :text, text: "Hello"}]

    Content.wrap(Content.text("Hi"))
    #=> [%Part{type: :text, text: "Hi"}]

    Content.wrap([Content.text("Hi"), Content.image_url("...")])
    #=> [%Part{...}, %Part{...}]

    # Maps are JSON-encoded (for tool results, structured data)
    Content.wrap(%{result: 42, status: "success"})
    #=> [%Part{type: :text, text: "{"result":42,"status":"success"}"}]

    # Structs are also JSON-encoded
    Content.wrap(%MyApp.Person{name: "Alice", age: 30})
    #=> [%Part{type: :text, text: "{"name":"Alice","age":30}"}]

---

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