Nous.Message (nous v0.17.1)

Copy Markdown View Source

Represents a message in a conversation with an AI model.

Messages support multi-modal content, tool calls, and various roles following OpenAI's standard message format while providing Elixir-native validation and type safety.

Message Roles

  • :system - System instructions and context
  • :user - User input and queries
  • :assistant - AI model responses
  • :tool - Tool execution results

Examples

# Simple text messages
iex> msg = Message.system("You are a helpful assistant")
iex> {msg.role, msg.content}
{:system, "You are a helpful assistant"}

iex> msg = Message.user("Hello!")
iex> {msg.role, msg.content}
{:user, "Hello!"}

# Multi-modal user message: parts are flattened into `content` and the
# original ContentPart list is kept under `metadata.content_parts`.
iex> msg = Message.user([
...>   ContentPart.text("What's in this image?"),
...>   ContentPart.image_url("https://example.com/image.jpg")
...> ])
iex> msg.content
"What's in this image?[Image: https://example.com/image.jpg]"
iex> Enum.map(msg.metadata.content_parts, & &1.type)
[:text, :image_url]

# Assistant message with tool calls
iex> msg = Message.assistant("Let me search for that", tool_calls: [
...>   %{id: "call_123", name: "search", arguments: %{"query" => "elixir"}}
...> ])
iex> {msg.role, Message.has_tool_calls?(msg)}
{:assistant, true}

Summary

Functions

Create an assistant message.

Extract text content from a message.

Check if message is from assistant.

Convert from legacy tuple format.

Check if message is from user.

Get message content as ContentPart list.

Get metadata from a message.

Check if message has tool calls.

Check if message is system instruction.

Check if message is tool-related (tool call or tool result).

Create a new message.

Create a new message, raising on validation failure.

Add metadata to a message.

Split messages into {system_prompt, other_messages}.

Create a system message.

Convert message content to plain text representation.

Create a tool result message.

Create a user message.

Types

t()

@type t() :: %Nous.Message{
  content: String.t() | nil,
  created_at: DateTime.t(),
  metadata: map(),
  name: String.t() | nil,
  reasoning_content: String.t() | nil,
  role: atom(),
  tool_call_id: String.t() | nil,
  tool_calls: [map()]
}

Functions

assistant(content, opts \\ [])

@spec assistant(
  String.t() | [Nous.Message.ContentPart.t()],
  keyword()
) :: t()

Create an assistant message.

Assistant messages contain AI model responses, including tool calls.

Examples

iex> msg = Message.assistant("Hello there!")
iex> {msg.role, msg.content}
{:assistant, "Hello there!"}

iex> msg = Message.assistant("Let me search", tool_calls: [
...>   %{id: "call_1", name: "search", arguments: %{"query" => "elixir"}}
...> ])
iex> msg.tool_calls
[%{id: "call_1", name: "search", arguments: %{"query" => "elixir"}}]

extract_text(message)

@spec extract_text(t()) :: String.t()

Extract text content from a message.

Examples

iex> message = Message.user("Hello world")
iex> Message.extract_text(message)
"Hello world"

iex> parts = [ContentPart.text("Hi"), ContentPart.image_url("https://example.com/i.png")]
iex> Message.extract_text(%Message{role: :user, content: parts})
"Hi"

from_assistant?(arg1)

@spec from_assistant?(t()) :: boolean()

Check if message is from assistant.

Examples

iex> Message.from_assistant?(Message.assistant("hello"))
true

iex> Message.from_assistant?(Message.user("hi"))
false

from_legacy(response)

@spec from_legacy(tuple() | map()) :: t()

Convert from legacy tuple format.

Examples

iex> msg = Message.from_legacy({:user_prompt, "Hello"})
iex> {msg.role, msg.content}
{:user, "Hello"}

iex> msg = Message.from_legacy({:system_prompt, "Instructions"})
iex> {msg.role, msg.content}
{:system, "Instructions"}

iex> msg = Message.from_legacy({:tool_return, %{call_id: "call_1", result: "42"}})
iex> {msg.role, msg.tool_call_id, msg.content}
{:tool, "call_1", "42"}

from_user?(arg1)

@spec from_user?(t()) :: boolean()

Check if message is from user.

Examples

iex> Message.from_user?(Message.user("hello"))
true

iex> Message.from_user?(Message.assistant("hi"))
false

get_content_parts(message)

@spec get_content_parts(t()) :: [Nous.Message.ContentPart.t()]

Get message content as ContentPart list.

Always returns a list, converting string content to text parts.

Examples

iex> Message.get_content_parts(Message.user("Hello"))
[%ContentPart{type: :text, content: "Hello"}]

get_metadata(message, key, default \\ nil)

@spec get_metadata(t(), atom() | String.t(), any()) :: any()

Get metadata from a message.

Examples

iex> message = Message.user("hello", metadata: %{source: "api"})
iex> Message.get_metadata(message, :source)
"api"

has_tool_calls?(message)

@spec has_tool_calls?(t()) :: boolean()

Check if message has tool calls.

Examples

iex> message = Message.assistant("Hello")
iex> Message.has_tool_calls?(message)
false

iex> message = Message.assistant("Search", tool_calls: [%{id: "call_1", name: "search"}])
iex> Message.has_tool_calls?(message)
true

is_system?(arg1)

@spec is_system?(t()) :: boolean()

Check if message is system instruction.

Examples

iex> Message.is_system?(Message.system("You are helpful"))
true

iex> Message.is_system?(Message.user("hi"))
false

is_tool_related?(message)

@spec is_tool_related?(t()) :: boolean()

Check if message is tool-related (tool call or tool result).

Examples

iex> Message.is_tool_related?(Message.tool("call_1", "result"))
true

iex> Message.is_tool_related?(Message.assistant("text", tool_calls: [%{}]))
true

iex> Message.is_tool_related?(Message.user("hello"))
false

new(attrs)

@spec new(map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}

Create a new message.

Returns {:ok, message} on success or {:error, changeset} on validation failure.

Examples

iex> {:ok, msg} = Message.new(%{role: :user, content: "Hello"})
iex> {msg.role, msg.content}
{:user, "Hello"}

iex> {:error, changeset} = Message.new(%{role: :invalid})
iex> changeset.valid?
false

new!(attrs)

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

Create a new message, raising on validation failure.

Examples

iex> msg = Message.new!(%{role: :user, content: "Hello"})
iex> {msg.role, msg.content}
{:user, "Hello"}

put_metadata(message, key, value)

@spec put_metadata(t(), atom() | String.t(), any()) :: t()

Add metadata to a message.

Examples

iex> message = Message.user("hello")
iex> message = Message.put_metadata(message, :source, "web_ui")
iex> message.metadata
%{source: "web_ui"}

split_system(messages)

@spec split_system([t()]) :: {String.t() | nil, [t()]}

Split messages into {system_prompt, other_messages}.

Providers that take the system prompt out-of-band (Anthropic, Gemini) use this before converting the remaining messages. Multiple system messages are joined with blank lines; no system messages yields nil.

Examples

iex> {system, rest} = Message.split_system([Message.system("Be helpful"), Message.user("Hi")])
iex> {system, Enum.map(rest, & &1.content)}
{"Be helpful", ["Hi"]}

iex> {system, rest} = Message.split_system([Message.user("Hi")])
iex> {system, length(rest)}
{nil, 1}

system(content, opts \\ [])

@spec system(
  String.t() | [Nous.Message.ContentPart.t()],
  keyword()
) :: t()

Create a system message.

System messages provide instructions and context for the AI model.

Examples

iex> msg = Message.system("You are a helpful assistant")
iex> {msg.role, msg.content}
{:system, "You are a helpful assistant"}

to_text(message)

@spec to_text(t()) :: String.t()

Convert message content to plain text representation.

Examples

iex> message = Message.user([
...>   ContentPart.text("Check this out: "),
...>   ContentPart.image_url("https://example.com/img.jpg")
...> ])
iex> Message.to_text(message)
"Check this out: [Image: https://example.com/img.jpg]"

tool(tool_call_id, result, opts \\ [])

@spec tool(String.t(), String.t() | map(), keyword()) :: t()

Create a tool result message.

Tool messages contain the results of tool/function executions.

Examples

iex> msg = Message.tool("call_123", "Search results: 42", name: "search")
iex> {msg.role, msg.tool_call_id, msg.name, msg.content}
{:tool, "call_123", "search", "Search results: 42"}

user(content, opts \\ [])

@spec user(
  String.t() | [Nous.Message.ContentPart.t()],
  keyword()
) :: t()

Create a user message.

User messages contain input, queries, and multi-modal content.

Examples

iex> msg = Message.user("Hello!")
iex> {msg.role, msg.content}
{:user, "Hello!"}

iex> msg = Message.user([ContentPart.text("Hi "), ContentPart.image_url("https://example.com/i.png")])
iex> msg.content
"Hi [Image: https://example.com/i.png]"
iex> Enum.map(msg.metadata.content_parts, & &1.type)
[:text, :image_url]