Nous.Messages (nous v0.16.4)

View Source

Utilities for working with conversations and message lists.

This module provides functions to:

  • Work with lists of messages (conversations)
  • Convert between internal format and provider-specific formats
  • Extract data from conversations
  • Parse provider responses into internal format

Message Format

We use Nous.Message structs with standard roles:

  • %Message{role: :system} - System instructions
  • %Message{role: :user} - User input (text or multi-modal)
  • %Message{role: :assistant} - AI responses (with optional tool calls)
  • %Message{role: :tool} - Tool execution results

Example

# Build conversation
conversation = [
  Message.system("You are a helpful assistant"),
  Message.user("What is 2+2?"),
  Message.assistant("2+2 equals 4")
]

# Convert to provider format
openai_messages = Messages.to_openai_format(conversation)
anthropic_messages = Messages.to_anthropic_format(conversation)

# Parse provider response
response = Messages.from_openai_response(openai_response)
# => %Message{role: :assistant, content: "4"}

Summary

Functions

Count messages by role.

Extract text content from messages in a conversation.

Extract tool calls from a conversation.

Find messages by role in a conversation.

Parse Anthropic response into a Message.

Parse Gemini response into a Message.

Parse OpenAI response into a Message.

Parse provider response into a Message.

Get the last message from a conversation.

Normalize any message format to internal Message representation.

Convert messages to Anthropic format.

Convert messages to Gemini format.

Convert messages to OpenAI format.

Convert messages to provider-specific format.

Functions

count_by_role(messages)

@spec count_by_role([Nous.Message.t()]) :: map()

Count messages by role.

Examples

iex> conversation = [Message.system("Hi"), Message.user("Hello"), Message.user("World")]
iex> Messages.count_by_role(conversation)
%{system: 1, user: 2, assistant: 0, tool: 0}

extract_text(messages)

@spec extract_text([Nous.Message.t()] | Nous.Message.t()) :: [String.t()] | String.t()

Extract text content from messages in a conversation.

Examples

iex> conversation = [Message.system("Be helpful"), Message.user("Hello")]
iex> Messages.extract_text(conversation)
["Be helpful", "Hello"]

iex> message = Message.user("Hi there")
iex> Messages.extract_text(message)
"Hi there"

extract_tool_calls(messages)

@spec extract_tool_calls([Nous.Message.t()]) :: [map()]

Extract tool calls from a conversation.

Returns all tool calls found in assistant messages.

Examples

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

find_by_role(messages, role)

@spec find_by_role([Nous.Message.t()], atom()) :: [Nous.Message.t()]

Find messages by role in a conversation.

Examples

iex> conversation = [Message.system("Be helpful"), Message.user("Hello")]
iex> Messages.find_by_role(conversation, :system)
[%Message{role: :system, content: "Be helpful"}]

from_anthropic_response(response)

@spec from_anthropic_response(map()) :: Nous.Message.t()

Parse Anthropic response into a Message.

Examples

iex> anthropic_response = %{
...>   "content" => [%{"type" => "text", "text" => "Hello"}],
...>   "model" => "claude-3-sonnet"
...> }
iex> Messages.from_anthropic_response(anthropic_response)
%Message{role: :assistant, content: "Hello"}

from_gemini_response(response)

@spec from_gemini_response(map()) :: Nous.Message.t()

Parse Gemini response into a Message.

Examples

iex> gemini_response = %{
...>   "candidates" => [%{"content" => %{"parts" => [%{"text" => "Hello"}]}}]
...> }
iex> Messages.from_gemini_response(gemini_response)
%Message{role: :assistant, content: "Hello"}

from_openai_response(response)

@spec from_openai_response(map()) :: Nous.Message.t()

Parse OpenAI response into a Message.

Examples

iex> openai_response = %{
...>   "choices" => [%{"message" => %{"role" => "assistant", "content" => "Hello"}}],
...>   "usage" => %{"total_tokens" => 10}
...> }
iex> Messages.from_openai_response(openai_response)
%Message{role: :assistant, content: "Hello"}

from_provider_response(response, provider)

@spec from_provider_response(map(), atom()) :: Nous.Message.t()

Parse provider response into a Message.

Dispatches to appropriate provider-specific parser.

Examples

iex> Messages.from_provider_response(openai_response, :openai)
%Message{role: :assistant, content: "Hello"}

last_message(messages)

@spec last_message([Nous.Message.t()]) :: Nous.Message.t() | nil

Get the last message from a conversation.

Examples

iex> conversation = [Message.user("Hi"), Message.assistant("Hello")]
iex> Messages.last_message(conversation)
%Message{role: :assistant, content: "Hello"}

normalize_format(messages)

@spec normalize_format(any()) :: [Nous.Message.t()]

Normalize any message format to internal Message representation.

Attempts to detect format and convert to Message structs.

Examples

iex> Messages.normalize_format([%{"role" => "user", "content" => "Hi"}])
[%Message{role: :user, content: "Hi"}]

to_anthropic_format(messages)

@spec to_anthropic_format([Nous.Message.t()]) :: {String.t() | nil, [map()]}

Convert messages to Anthropic format.

Returns {system_prompt, messages} where system prompt is extracted and combined, and messages are converted to Anthropic format.

Examples

iex> conversation = [Message.system("Be helpful"), Message.user("Hello")]
iex> Messages.to_anthropic_format(conversation)
{"Be helpful", [%{"role" => "user", "content" => "Hello"}]}

to_gemini_format(messages)

@spec to_gemini_format([Nous.Message.t()]) :: {String.t() | nil, [map()]}

Convert messages to Gemini format.

Returns {system_prompt, contents} where system prompt is extracted and messages are converted to Gemini contents format.

Examples

iex> conversation = [Message.system("Be helpful"), Message.user("Hello")]
iex> Messages.to_gemini_format(conversation)
{"Be helpful", [%{"role" => "user", "parts" => [%{"text" => "Hello"}]}]}

to_openai_format(messages)

@spec to_openai_format([Nous.Message.t()]) :: [map()]

Convert messages to OpenAI format.

Examples

iex> conversation = [Message.system("Be helpful"), Message.user("Hello")]
iex> Messages.to_openai_format(conversation)
[
  %{"role" => "system", "content" => "Be helpful"},
  %{"role" => "user", "content" => "Hello"}
]

to_provider_format(messages, provider)

@spec to_provider_format([Nous.Message.t()], atom()) :: any()

Convert messages to provider-specific format.

Dispatches to the appropriate provider-specific conversion function.

Examples

iex> conversation = [Message.system("Be helpful"), Message.user("Hello")]
iex> Messages.to_provider_format(conversation, :openai)
[%{"role" => "system", "content" => "Be helpful"}, %{"role" => "user", "content" => "Hello"}]

iex> Messages.to_provider_format(conversation, :anthropic)
{"Be helpful", [%{"role" => "user", "content" => "Hello"}]}