AI.Message behaviour (fnord v0.9.40)

View Source

Canonical in-memory representation of conversation messages, modeled after the Responses API item shape.

Each message is a struct from one of the impl modules below. Pattern-matching on %mod{} dispatches the behaviour callbacks (text/1, for_transcript/1, to_map/1). Construction goes through the short helpers at this module's top level (e.g. AI.Message.user/1); persistence and wire serialization go through to_map/1; hydration from disk or API response goes through from_map/1.

Impls

Invariants

  • FunctionCall.arguments is ALWAYS a JSON string. Never decode it to a map for persistence - LLM-emitted garbage keys exhaust the BEAM atom table downstream. This is the cliff the previous Responses API attempt drove over; see the "Conversation file corruption" engram memory.
  • Content for User/Assistant/System is stored as a list of typed parts matching the wire format (%{type: "input_text" | "output_text", text: ...}). The text/1 callback joins the parts back to a single binary.

Summary

Callbacks

Returns a transcript-formatted block (with role headers, tool-call summary, etc.) suitable for inclusion in AI.Util.research_transcript/1-style outputs. nil if the message is omitted from transcripts.

Returns the plain-text representation of a message, or nil if the message has no textual content (e.g. a tool-call request or opaque reasoning blob). Joins multi-part content lists with newlines.

Serializes a message struct to its on-the-wire (Responses API native) map shape. The result is what gets persisted to disk and sent to the API.

Functions

Build an AI.Message.Assistant from a binary or pre-built content list.

Hydrate a struct from a Responses-API-shaped map. Accepts either atom-keyed or string-keyed maps - on-disk persistence uses string keys; in-memory construction uses atoms.

Same as from_map/1 but raises on unknown shapes. Use when persistence guarantees the shape (e.g. immediately after a successful Format migration).

Build an AI.Message.FunctionCall. arguments MUST be a JSON-encoded string (the same form the API returns). Never pass a decoded map - see the module doc.

Build an AI.Message.Reasoning from the raw reasoning item map returned by the API. The struct round-trips the raw shape so reasoning continuity is preserved when store: false.

Build an AI.Message.System. Role defaults to "developer" (OpenAI's Responses-era convention); pass role: "system" for providers that prefer the legacy label.

Build an AI.Message.User from a binary or pre-built content list.

Types

Callbacks

for_transcript(t)

@callback for_transcript(t()) :: binary() | nil

Returns a transcript-formatted block (with role headers, tool-call summary, etc.) suitable for inclusion in AI.Util.research_transcript/1-style outputs. nil if the message is omitted from transcripts.

text(t)

@callback text(t()) :: binary() | nil

Returns the plain-text representation of a message, or nil if the message has no textual content (e.g. a tool-call request or opaque reasoning blob). Joins multi-part content lists with newlines.

to_map(t)

@callback to_map(t()) :: map()

Serializes a message struct to its on-the-wire (Responses API native) map shape. The result is what gets persisted to disk and sent to the API.

Functions

assistant(content)

@spec assistant(binary() | [map()]) :: AI.Message.Assistant.t()

Build an AI.Message.Assistant from a binary or pre-built content list.

for_transcript(msg)

@spec for_transcript(t()) :: binary() | nil

from_map(raw)

@spec from_map(map()) :: {:ok, t()} | {:error, {:unknown_message_shape, map()}}

Hydrate a struct from a Responses-API-shaped map. Accepts either atom-keyed or string-keyed maps - on-disk persistence uses string keys; in-memory construction uses atoms.

Dispatches on type (and role for "message" items). Unknown shapes return {:error, {:unknown_message_shape, raw}}.

from_map!(raw)

@spec from_map!(map()) :: t()

Same as from_map/1 but raises on unknown shapes. Use when persistence guarantees the shape (e.g. immediately after a successful Format migration).

function_call(call_id, name, arguments)

@spec function_call(binary(), binary(), binary()) :: AI.Message.FunctionCall.t()

Build an AI.Message.FunctionCall. arguments MUST be a JSON-encoded string (the same form the API returns). Never pass a decoded map - see the module doc.

function_call_output(call_id, output)

@spec function_call_output(binary(), binary()) :: AI.Message.FunctionCallOutput.t()

Build an AI.Message.FunctionCallOutput from a tool's textual result.

reasoning(raw)

@spec reasoning(map()) :: AI.Message.Reasoning.t()

Build an AI.Message.Reasoning from the raw reasoning item map returned by the API. The struct round-trips the raw shape so reasoning continuity is preserved when store: false.

system(content, opts \\ [])

@spec system(
  binary() | [map()],
  keyword()
) :: AI.Message.System.t()

Build an AI.Message.System. Role defaults to "developer" (OpenAI's Responses-era convention); pass role: "system" for providers that prefer the legacy label.

text(msg)

@spec text(t()) :: binary() | nil

to_map(msg)

@spec to_map(t()) :: map()

user(content)

@spec user(binary() | [map()]) :: AI.Message.User.t()

Build an AI.Message.User from a binary or pre-built content list.