ALLM.Message (allm v0.4.0)

Copy Markdown View Source

A chat message — Layer A serializable data.

Every conversation in ALLM is a list of %Message{}s. The :role atom is a closed union of :system | :user | :assistant | :tool; :content is either a binary or a list of ALLM.TextPart.t | ALLM.ImagePart.t structs (multimodal). Raw maps in a content list are rejected by ALLM.Validate.message/1 with {:content, :invalid_part_type}.

:tool_call_id is required when role: :tool so the model can match the tool result back to the call that produced it; this invariant is enforced by ALLM.Validate.message/1, not by the struct.

Fields

FieldTypeDefaultNotes
:role:system | :user | :assistant | :tool(required)Enforced via @enforce_keys.
:contentString.t or [%TextPart{} | %ImagePart{}](required)Multimodal lists for vision input.
:nameString.t | nilnilOptional named author.
:tool_call_idString.t | nilnilRequired when role: :tool.
:metadatamap%{}Caller-owned.

Construct with new/1, the ALLM.user/1, ALLM.system/1, ALLM.assistant/1, ALLM.tool_result/2 shortcuts, or directly via %ALLM.Message{}.

Multimodal example

iex> img = ALLM.Image.from_url("https://example.com/cat.png")
iex> ALLM.Message.new(role: :user, content: [
...> %ALLM.TextPart{text: "What is in this image?"},
...> %ALLM.ImagePart{image: img}
...> ]).role
:user

See also guides/getting_started.md, guides/vision.md.

Summary

Types

Multimodal content — either a string or a list of TextPart/ImagePart structs.

Message role — closed union.

t()

Functions

Build a %Message{} from keyword opts.

Lift a String.t content value to a single-element [%TextPart{}] list, or pass an already-list content through unchanged.

Types

content()

@type content() :: String.t() | [ALLM.TextPart.t() | ALLM.ImagePart.t()]

Multimodal content — either a string or a list of TextPart/ImagePart structs.

role()

@type role() :: :system | :user | :assistant | :tool

Message role — closed union.

t()

@type t() :: %ALLM.Message{
  content: content(),
  metadata: map(),
  name: String.t() | nil,
  role: role(),
  tool_call_id: String.t() | nil
}

Functions

new(opts)

@spec new(keyword()) :: t()

Build a %Message{} from keyword opts.

:role and :content are required; omitting either raises ArgumentError via struct!/2. Optional fields: :name, :tool_call_id, :metadata.

new/1 does not validate role/content invariants — use ALLM.Validate.message/1 for that.

Examples

iex> ALLM.Message.new(role: :user, content: "hi")
%ALLM.Message{role: :user, content: "hi", name: nil, tool_call_id: nil, metadata: %{}}

iex> ALLM.Message.new(role: :tool, content: "ok", tool_call_id: "call_1").tool_call_id
"call_1"

normalize_content(content)

@spec normalize_content(content()) :: [ALLM.TextPart.t() | ALLM.ImagePart.t()]

Lift a String.t content value to a single-element [%TextPart{}] list, or pass an already-list content through unchanged.

Used by chat-side adapters at the wire-shape boundary so the translator handles only the structured form. Does NOT mutate Message.content — this is a one-way normalization helper.

Examples

iex> ALLM.Message.normalize_content("hi")
[%ALLM.TextPart{text: "hi", metadata: %{}}]

iex> parts = [%ALLM.TextPart{text: "a"}, %ALLM.TextPart{text: "b"}]
iex> ALLM.Message.normalize_content(parts) == parts
true