Packages
claude_code
0.13.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/result_message.ex
defmodule ClaudeCode.Message.ResultMessage do
@moduledoc """
Represents a result message from the Claude CLI.
Result messages are the final message in a conversation, containing
the final response, timing information, token usage, and cost.
## String.Chars Protocol
`Result` implements `String.Chars`, so you can use it directly with
`IO.puts/1` or string interpolation:
{:ok, result} = ClaudeCode.query(session, "Hello")
IO.puts(result) # prints just the result text
Matches the official SDK schema for successful results:
```
{
type: "result",
subtype: "success",
uuid: string,
duration_ms: float,
duration_api_ms: float,
is_error: boolean,
num_turns: int,
result: string,
session_id: string,
total_cost_usd: float,
usage: object,
modelUsage: {model: ModelUsage},
permission_denials: PermissionDenial[],
structured_output?: unknown
}
```
And for error results:
```
{
type: "result",
subtype: "error_max_turns" | "error_during_execution" | "error_max_budget_usd" | "error_max_structured_output_retries",
uuid: string,
duration_ms: float,
duration_api_ms: float,
is_error: boolean,
num_turns: int,
session_id: string,
total_cost_usd: float,
usage: object,
modelUsage: {model: ModelUsage},
permission_denials: PermissionDenial[],
errors: string[]
}
```
"""
alias ClaudeCode.Types
@enforce_keys [
:type,
:subtype,
:is_error,
:duration_ms,
:duration_api_ms,
:num_turns,
:result,
:session_id,
:total_cost_usd,
:usage
]
defstruct [
:type,
:subtype,
:is_error,
:duration_ms,
:duration_api_ms,
:num_turns,
:result,
:session_id,
:total_cost_usd,
:usage,
:uuid,
:model_usage,
:permission_denials,
:structured_output,
:errors
]
@type t :: %__MODULE__{
type: :result,
subtype: Types.result_subtype(),
is_error: boolean(),
duration_ms: float(),
duration_api_ms: float(),
num_turns: non_neg_integer(),
result: String.t(),
session_id: Types.session_id(),
total_cost_usd: float(),
usage: Types.usage(),
uuid: String.t() | nil,
model_usage: %{String.t() => Types.model_usage()} | nil,
permission_denials: [Types.permission_denial()] | nil,
structured_output: any() | nil,
errors: [String.t()] | nil
}
@doc """
Creates a new Result message from JSON data.
## Examples
iex> Result.new(%{"type" => "result", "subtype" => "success", ...})
{:ok, %Result{...}}
iex> Result.new(%{"type" => "assistant"})
{:error, :invalid_message_type}
"""
@spec new(map()) :: {:ok, t()} | {:error, atom() | {:missing_fields, [atom()]}}
def new(%{"type" => "result"} = json) do
required_fields = [
"subtype",
"is_error",
"duration_ms",
"duration_api_ms",
"num_turns",
"result",
"session_id",
"total_cost_usd",
"usage"
]
missing = Enum.filter(required_fields, &(not Map.has_key?(json, &1)))
if Enum.empty?(missing) do
message = %__MODULE__{
type: :result,
subtype: parse_subtype(json["subtype"]),
is_error: json["is_error"],
duration_ms: json["duration_ms"],
duration_api_ms: json["duration_api_ms"],
num_turns: json["num_turns"],
result: json["result"],
session_id: json["session_id"],
total_cost_usd: parse_float(json["total_cost_usd"]),
usage: parse_usage(json["usage"]),
uuid: json["uuid"],
model_usage: parse_model_usage(json["modelUsage"]),
permission_denials: parse_permission_denials(json["permission_denials"]),
structured_output: json["structured_output"],
errors: json["errors"]
}
{:ok, message}
else
{:error, {:missing_fields, Enum.map(missing, &String.to_atom/1)}}
end
end
def new(_), do: {:error, :invalid_message_type}
@doc """
Type guard to check if a value is a Result message.
"""
@spec result_message?(any()) :: boolean()
def result_message?(%__MODULE__{type: :result}), do: true
def result_message?(_), do: false
defp parse_subtype("success"), do: :success
defp parse_subtype("error_max_turns"), do: :error_max_turns
defp parse_subtype("error_during_execution"), do: :error_during_execution
defp parse_subtype("error_max_budget_usd"), do: :error_max_budget_usd
defp parse_subtype("error_max_structured_output_retries"), do: :error_max_structured_output_retries
defp parse_subtype(other) when is_binary(other), do: String.to_atom(other)
defp parse_float(value) when is_float(value), do: value
defp parse_float(value) when is_integer(value), do: value * 1.0
defp parse_float(_), do: 0.0
defp parse_usage(usage_data) when is_map(usage_data) do
%{
input_tokens: usage_data["input_tokens"] || 0,
cache_creation_input_tokens: usage_data["cache_creation_input_tokens"] || 0,
cache_read_input_tokens: usage_data["cache_read_input_tokens"] || 0,
output_tokens: usage_data["output_tokens"] || 0,
server_tool_use: parse_server_tool_use(usage_data["server_tool_use"])
}
end
defp parse_usage(_),
do: %{
input_tokens: 0,
cache_creation_input_tokens: 0,
cache_read_input_tokens: 0,
output_tokens: 0,
server_tool_use: %{web_search_requests: 0}
}
defp parse_server_tool_use(%{"web_search_requests" => count}), do: %{web_search_requests: count}
defp parse_server_tool_use(_), do: %{web_search_requests: 0}
defp parse_model_usage(nil), do: nil
defp parse_model_usage(model_usage) when is_map(model_usage) do
Map.new(model_usage, fn {model, usage_data} ->
{model, parse_single_model_usage(usage_data)}
end)
end
defp parse_model_usage(_), do: nil
defp parse_single_model_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"]
}
end
defp parse_single_model_usage(_), do: nil
defp parse_permission_denials(nil), do: nil
defp parse_permission_denials(denials) when is_list(denials) do
Enum.map(denials, fn denial ->
%{
tool_name: denial["tool_name"],
tool_use_id: denial["tool_use_id"],
tool_input: denial["tool_input"] || %{}
}
end)
end
defp parse_permission_denials(_), do: nil
end
defimpl String.Chars, for: ClaudeCode.Message.Result do
def to_string(%{result: result}), do: result
end