# `Nous.Message`
[🔗](https://github.com/nyo16/nous/blob/v0.17.1/lib/nous/message.ex#L1)

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}

# `t`

```elixir
@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()]
}
```

# `assistant`

```elixir
@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`

```elixir
@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?`

```elixir
@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`

```elixir
@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?`

```elixir
@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`

```elixir
@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`

```elixir
@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?`

```elixir
@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?`

```elixir
@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?`

```elixir
@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`

```elixir
@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!`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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]

---

*Consult [api-reference.md](api-reference.md) for complete listing*
