Puck.Content (Puck v0.2.11)

Copy Markdown View Source

Multi-modal content for messages.

Types

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")
])

Summary

Functions

Creates a file content part.

Creates an image content part from binary data.

Creates an image URL content part.

Creates a generic content part with custom type.

Creates a text content part.

Wraps content into a list of Content.Part structs.

Functions

audio(data, media_type \\ "audio/wav", metadata \\ %{})

Creates an audio content part.

Examples

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

file(data, media_type, opts \\ [])

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(data, media_type \\ "image/png", metadata \\ %{})

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(url, metadata \\ %{})

Creates an image URL content part.

Examples

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

new(type, fields \\ [])

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(content, metadata \\ %{})

Creates a text content part.

Examples

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

video(data, media_type \\ "video/mp4", metadata \\ %{})

Creates a video content part.

Examples

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

wrap(content)

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}"}]