Multi-modal content for messages.
Types
text/1- Plain textimage_url/1- Image from URLimage/2- Binary image datafile/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")
])
Summary
Functions
Creates an audio content part.
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.
Creates a video content part.
Wraps content into a list of Content.Part structs.
Functions
Creates an audio content part.
Examples
audio_bytes = File.read!("speech.mp3")
Content.audio(audio_bytes, "audio/mp3")
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")
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")
Creates an image URL content part.
Examples
Content.image_url("https://example.com/photo.jpg")
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"})
Creates a text content part.
Examples
Content.text("Hello, world!")
Content.text("Analyze this", %{cache: true})
Creates a video content part.
Examples
video_bytes = File.read!("clip.mp4")
Content.video(video_bytes, "video/mp4")
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))]
endExamples
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}"}]