Packages
claude_code
0.27.0
0.36.5
0.36.4
0.36.3
0.36.2
0.36.1
0.36.0
0.35.0
0.34.0
0.33.1
0.32.2
0.32.0
0.31.0
0.30.0
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.1
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
Claude Agent SDK for Elixir – Build AI agents with Claude Code
Current section
Files
Jump to
Current section
Files
lib/claude_code/message/assistant_message.ex
defmodule ClaudeCode.Message.AssistantMessage do
@moduledoc """
Represents an assistant message from the Claude CLI.
Assistant messages contain Claude's responses, which can include text,
tool use requests, or a combination of both.
Matches the official SDK schema:
```
{
type: "assistant",
uuid: string,
message: { ... }, # Anthropic SDK Message type
session_id: string,
parent_tool_use_id?: string | null,
error?: "authentication_failed" | "billing_error" | "rate_limit"
| "invalid_request" | "server_error" | "max_output_tokens"
| "unknown" | null
}
```
"""
alias ClaudeCode.CLI.Parser
alias ClaudeCode.Types
@enforce_keys [
:type,
:message,
:session_id
]
defstruct [
:type,
:message,
:session_id,
:uuid,
:parent_tool_use_id,
:error
]
@type assistant_message_error ::
:authentication_failed
| :billing_error
| :rate_limit
| :invalid_request
| :server_error
| :max_output_tokens
| :unknown
@type t :: %__MODULE__{
type: :assistant,
message: Types.message(),
session_id: String.t(),
uuid: String.t() | nil,
parent_tool_use_id: String.t() | nil,
error: assistant_message_error() | nil
}
@doc """
Creates a new AssistantMessage from JSON data.
## Examples
iex> AssistantMessage.new(%{"type" => "assistant", "message" => %{...}})
{:ok, %AssistantMessage{...}}
iex> AssistantMessage.new(%{"type" => "user"})
{:error, :invalid_message_type}
"""
@spec new(map()) :: {:ok, t()} | {:error, atom() | tuple()}
def new(%{"type" => "assistant"} = json) do
case json do
%{"message" => message_data} ->
parse_message(message_data, json)
_ ->
{:error, :missing_message}
end
end
def new(_), do: {:error, :invalid_message_type}
@doc """
Type guard to check if a value is an AssistantMessage.
"""
@spec assistant_message?(any()) :: boolean()
def assistant_message?(%__MODULE__{type: :assistant}), do: true
def assistant_message?(_), do: false
defp parse_message(message_data, parent_json) do
case Parser.parse_all_contents(message_data["content"] || []) do
{:ok, content} ->
message_struct = %__MODULE__{
type: :assistant,
message: %{
id: message_data["id"],
type: :message,
role: :assistant,
content: content,
model: message_data["model"],
stop_reason: parse_stop_reason(message_data["stop_reason"]),
stop_sequence: message_data["stop_sequence"],
usage: parse_usage(message_data["usage"]),
context_management: message_data["context_management"]
},
session_id: parent_json["session_id"],
uuid: parent_json["uuid"],
parent_tool_use_id: parent_json["parent_tool_use_id"],
error: parse_error(parent_json["error"])
}
{:ok, message_struct}
{:error, error} ->
{:error, {:content_parse_error, error}}
end
end
defp parse_stop_reason(nil), do: nil
defp parse_stop_reason("end_turn"), do: :end_turn
defp parse_stop_reason("max_tokens"), do: :max_tokens
defp parse_stop_reason("stop_sequence"), do: :stop_sequence
defp parse_stop_reason("tool_use"), do: :tool_use
defp parse_stop_reason(other) when is_binary(other), do: String.to_atom(other)
defp parse_usage(usage_data) when is_map(usage_data) do
%{
input_tokens: usage_data["input_tokens"] || 0,
output_tokens: usage_data["output_tokens"] || 0,
cache_creation_input_tokens: usage_data["cache_creation_input_tokens"],
cache_read_input_tokens: usage_data["cache_read_input_tokens"],
cache_creation: parse_cache_creation(usage_data["cache_creation"]),
service_tier: usage_data["service_tier"],
inference_geo: usage_data["inference_geo"]
}
end
defp parse_usage(_) do
%{
input_tokens: 0,
output_tokens: 0,
cache_creation_input_tokens: nil,
cache_read_input_tokens: nil,
cache_creation: nil,
service_tier: nil,
inference_geo: nil
}
end
defp parse_cache_creation(%{"ephemeral_5m_input_tokens" => t5m, "ephemeral_1h_input_tokens" => t1h}) do
%{ephemeral_5m_input_tokens: t5m, ephemeral_1h_input_tokens: t1h}
end
defp parse_cache_creation(_), do: nil
defp parse_error(nil), do: nil
defp parse_error("authentication_failed"), do: :authentication_failed
defp parse_error("billing_error"), do: :billing_error
defp parse_error("rate_limit"), do: :rate_limit
defp parse_error("invalid_request"), do: :invalid_request
defp parse_error("server_error"), do: :server_error
defp parse_error("max_output_tokens"), do: :max_output_tokens
defp parse_error("unknown"), do: :unknown
defp parse_error(other) when is_binary(other), do: String.to_atom(other)
end
defimpl String.Chars, for: ClaudeCode.Message.AssistantMessage do
alias ClaudeCode.Content.TextBlock
def to_string(%{message: %{content: content}}) when is_list(content) do
content
|> Enum.filter(&match?(%TextBlock{}, &1))
|> Enum.map_join(& &1.text)
end
def to_string(_), do: ""
end
defimpl Jason.Encoder, for: ClaudeCode.Message.AssistantMessage do
def encode(message, opts) do
message
|> ClaudeCode.JSONEncoder.to_encodable()
|> Jason.Encoder.Map.encode(opts)
end
end
defimpl JSON.Encoder, for: ClaudeCode.Message.AssistantMessage do
def encode(message, encoder) do
message
|> ClaudeCode.JSONEncoder.to_encodable()
|> JSON.Encoder.Map.encode(encoder)
end
end