Claudio.Messages.Response (Claudio v0.7.0)

View Source

Structured response from the Messages API.

stop_details is the raw API map ("type", "category", "explanation"), set only when stop_reason is :refusal. With Request.set_fallbacks/2 it can also carry "recommended_model" (a model to retry directly when the fallback attempt was skipped), "fallback_credit_token" and "fallback_has_prefill_claim". For streamed responses it is read from message_delta.delta next to stop_reason; that location is unconfirmed in Anthropic's streaming docs.

diagnostics is carried raw (see Request.enable_cache_diagnostics/2): nil when diagnostics were not requested, there was nothing to compare, or the comparison found no divergence; %{"cache_miss_reason" => nil} when the comparison was still pending (inconclusive — check the next turn); otherwise a reason map such as %{"cache_miss_reason" => %{"type" => "system_changed", "cache_missed_input_tokens" => n}}.

With Request.set_fallbacks/2, a refused request may be retried on another model. Each handoff is a :fallback content block (fallbacks/1); served_by/1 names the model that produced the message, and usage.iterations records every attempt.

Context management: context_management is the raw response map (%{"applied_edits" => [...]}, nil when the request configured no edits). A :compaction content block holds a compaction summary; stop_reason is :compaction when the reply is only that block (on-demand compaction, or pause_after_compaction). Continue with Request.apply_compaction/2.

input_transformations (only with the thinking-binding-controls-2026-08-01 beta, see Request.set_thinking_block_binding/2) is the raw list of what the API changed in the input: entries %{"type" => "thinking_dropped" | "thinking_mismatch_allowed", "path" => "messages.N.content.M", "reason" => "prefix_binding_mismatch" | "model_binding_mismatch"}. [] when nothing changed; nil without the beta. Ignore unknown type/reason values.

usage keeps every field the API returns: documented fields are atom keys (nil when absent); any other field keeps the key it arrived with (so it may be a string key). This applies when both input_tokens and output_tokens are present (either key style); a usage map missing one of them is returned as received.

Summary

Types

A compaction summary (Request.add_compaction/2 threshold compaction, or Request.request_compaction/2 on demand). content is the summary text (nil when compaction failed). raw is the block as received — including signature for on-demand blocks — and is what to_assistant_content/1 replays.

A server-side fallback handoff (Request.set_fallbacks/2). raw is the block as received; Response.to_assistant_content/1 re-emits it unchanged.

A server-tool result other than web search (web_fetch_tool_result, code_execution_tool_result, bash_code_execution_tool_result, text_editor_code_execution_tool_result, tool_search_tool_result, advisor_tool_result). content is the raw nested value (its variants and error codes change over time); raw is the block as received and is what to_assistant_content/1 replays.

t()

Token usage. Documented fields are atom keys (nil when the API did not send them); any other field the API returns is kept under the key it arrived with. A usage map missing input_tokens or output_tokens is returned as received. iterations (present when fallbacks was set) lists each attempt as the raw API map: "type" => "message" for a model that declined, "fallback_message" for the one that served; the top-level counts cover only the returned attempt. With compaction, iterations also holds "type" => "compaction" entries; the top-level counts exclude them (the billed total is the sum over iterations).

Functions

Returns the last compaction block, or nil. See Request.apply_compaction/2 to continue from it.

Returns every fallback block, in content order ([] when the request was not retried on a fallback model). One block marks each handoff between models.

Converts a raw API response map into a structured Response.

Aggregates every citation across all text blocks, in document order.

Extracts all MCP tool use requests from the response.

Extracts MCP tool use requests from the response for a specific server.

Returns server-tool result blocks in content order: web_search_tool_result, web_fetch_tool_result, code_execution_tool_result, bash_code_execution_tool_result, text_editor_code_execution_tool_result, tool_search_tool_result, advisor_tool_result and container_upload. content is the raw nested value.

Returns the server-tool result blocks of one type (e.g. :code_execution_tool_result).

Extracts all server-side tool use requests (server_tool_use) from the response — e.g. web_search / web_fetch invocations Claude ran on the server. The matching results are web_search_tool_result blocks.

Extracts all text content from the response.

Returns the non-empty thinking texts, in content order.

Extracts the tool use requests to execute. After a server-side fallback, tool_use blocks before the last fallback block came from the model that declined; they are skipped here, as to_assistant_content/1 drops them from the replay.

Returns the model that produced the returned message: the last fallback block's to.model, else model.

True when block is a thinking block holding the API's placeholder for an update that was cut off: "This part of the response was interrupted before it finished.".

Converts the response content into API-shaped assistant content blocks for replaying as the assistant turn in a follow-up request

Types

compaction_block()

@type compaction_block() :: %{
  type: :compaction,
  content: String.t() | nil,
  raw: map()
}

A compaction summary (Request.add_compaction/2 threshold compaction, or Request.request_compaction/2 on demand). content is the summary text (nil when compaction failed). raw is the block as received — including signature for on-demand blocks — and is what to_assistant_content/1 replays.

container_upload_block()

@type container_upload_block() :: %{
  type: :container_upload,
  file_id: String.t(),
  raw: map()
}

content_block()

fallback_block()

@type fallback_block() :: %{
  type: :fallback,
  from: map() | nil,
  to: map() | nil,
  trigger: map() | nil,
  raw: map()
}

A server-side fallback handoff (Request.set_fallbacks/2). raw is the block as received; Response.to_assistant_content/1 re-emits it unchanged.

mcp_tool_result_block()

@type mcp_tool_result_block() :: %{
  type: :mcp_tool_result,
  tool_use_id: String.t(),
  server_name: String.t(),
  content: term(),
  is_error: boolean()
}

mcp_tool_use_block()

@type mcp_tool_use_block() :: %{
  type: :mcp_tool_use,
  id: String.t(),
  name: String.t(),
  server_name: String.t(),
  input: map()
}

redacted_thinking_block()

@type redacted_thinking_block() :: %{type: :redacted_thinking, data: String.t()}

server_tool_result_block()

@type server_tool_result_block() :: %{
  type: atom(),
  tool_use_id: String.t(),
  content: term(),
  caller: map() | nil,
  raw: map()
}

A server-tool result other than web search (web_fetch_tool_result, code_execution_tool_result, bash_code_execution_tool_result, text_editor_code_execution_tool_result, tool_search_tool_result, advisor_tool_result). content is the raw nested value (its variants and error codes change over time); raw is the block as received and is what to_assistant_content/1 replays.

server_tool_use_block()

@type server_tool_use_block() :: %{
  type: :server_tool_use,
  id: String.t(),
  name: String.t(),
  input: map(),
  caller: map() | nil
}

stop_reason()

@type stop_reason() ::
  :end_turn
  | :max_tokens
  | :stop_sequence
  | :tool_use
  | :pause_turn
  | :refusal
  | :model_context_window_exceeded
  | :compaction

t()

@type t() :: %Claudio.Messages.Response{
  container: map() | nil,
  content: [content_block()],
  context_management: map() | nil,
  diagnostics: map() | nil,
  id: String.t(),
  input_transformations: [map()] | nil,
  model: String.t(),
  role: String.t(),
  stop_details: map() | nil,
  stop_reason: stop_reason() | nil,
  stop_sequence: String.t() | nil,
  type: String.t(),
  usage: usage()
}

text_block()

@type text_block() :: %{
  :type => :text,
  :text => String.t(),
  optional(:citations) => list()
}

thinking_block()

@type thinking_block() :: %{
  type: :thinking,
  thinking: String.t(),
  signature: String.t() | nil
}

tool_result_block()

@type tool_result_block() :: %{
  type: :tool_result,
  tool_use_id: String.t(),
  content: String.t() | list()
}

tool_use_block()

@type tool_use_block() :: %{
  type: :tool_use,
  id: String.t(),
  name: String.t(),
  input: map(),
  caller: map() | nil,
  toolset_name: String.t() | nil
}

usage()

@type usage() :: %{
  optional(atom() | String.t()) => term(),
  input_tokens: integer(),
  output_tokens: integer(),
  cache_creation_input_tokens: integer() | nil,
  cache_read_input_tokens: integer() | nil,
  output_tokens_details: map() | nil,
  cache_creation: map() | nil,
  service_tier: String.t() | nil,
  inference_geo: String.t() | nil,
  speed: String.t() | nil,
  iterations: [map()] | nil
}

Token usage. Documented fields are atom keys (nil when the API did not send them); any other field the API returns is kept under the key it arrived with. A usage map missing input_tokens or output_tokens is returned as received. iterations (present when fallbacks was set) lists each attempt as the raw API map: "type" => "message" for a model that declined, "fallback_message" for the one that served; the top-level counts cover only the returned attempt. With compaction, iterations also holds "type" => "compaction" entries; the top-level counts exclude them (the billed total is the sum over iterations).

web_search_tool_result_block()

@type web_search_tool_result_block() :: %{
  type: :web_search_tool_result,
  tool_use_id: String.t(),
  content: term(),
  caller: map() | nil,
  raw: map()
}

Functions

compaction_block(response)

@spec compaction_block(t()) :: compaction_block() | nil

Returns the last compaction block, or nil. See Request.apply_compaction/2 to continue from it.

fallbacks(response)

@spec fallbacks(t()) :: [fallback_block()]

Returns every fallback block, in content order ([] when the request was not retried on a fallback model). One block marks each handoff between models.

from_map(data)

@spec from_map(map()) :: t()

Converts a raw API response map into a structured Response.

get_citations(response)

@spec get_citations(t()) :: list()

Aggregates every citation across all text blocks, in document order.

Each entry is the raw citation map as returned by the API (e.g. char_location, page_location, content_block_location, search_result_location, web_search_result_location). Returns [] when no citations are present.

get_mcp_tool_uses(response)

@spec get_mcp_tool_uses(t()) :: [mcp_tool_use_block()]

Extracts all MCP tool use requests from the response.

get_mcp_tool_uses(response, server_name)

@spec get_mcp_tool_uses(t(), String.t()) :: [mcp_tool_use_block()]

Extracts MCP tool use requests from the response for a specific server.

get_server_tool_results(response)

@spec get_server_tool_results(t()) :: [map()]

Returns server-tool result blocks in content order: web_search_tool_result, web_fetch_tool_result, code_execution_tool_result, bash_code_execution_tool_result, text_editor_code_execution_tool_result, tool_search_tool_result, advisor_tool_result and container_upload. content is the raw nested value.

get_server_tool_results(response, type)

@spec get_server_tool_results(t(), atom()) :: [map()]

Returns the server-tool result blocks of one type (e.g. :code_execution_tool_result).

get_server_tool_uses(response)

@spec get_server_tool_uses(t()) :: [server_tool_use_block()]

Extracts all server-side tool use requests (server_tool_use) from the response — e.g. web_search / web_fetch invocations Claude ran on the server. The matching results are web_search_tool_result blocks.

get_text(arg1)

@spec get_text(t() | map()) :: String.t()

Extracts all text content from the response.

Also accepts the legacy string-keyed map returned by Claudio.Messages.create_message/2.

get_thinking(response)

@spec get_thinking(t()) :: [String.t()]

Returns the non-empty thinking texts, in content order.

A list, not a joined string: with display: :updates each thinking block is a separate progress note. Empty texts (display: :omitted) and redacted_thinking blocks are skipped. An interrupted update's placeholder text is kept — filter it with thinking_interrupted?/1.

get_tool_uses(response)

@spec get_tool_uses(t()) :: [tool_use_block()]

Extracts the tool use requests to execute. After a server-side fallback, tool_use blocks before the last fallback block came from the model that declined; they are skipped here, as to_assistant_content/1 drops them from the replay.

served_by(response)

@spec served_by(t()) :: String.t() | nil

Returns the model that produced the returned message: the last fallback block's to.model, else model.

Not simply model: a streamed response that fell back mid-output keeps the requested (declining) model from message_start in model. When every model in the chain declined (stop_reason: :refusal), it names the model whose refusal was returned.

thinking_interrupted?(arg1)

@spec thinking_interrupted?(content_block()) :: boolean()

True when block is a thinking block holding the API's placeholder for an update that was cut off: "This part of the response was interrupted before it finished.".

to_assistant_content(response)

@spec to_assistant_content(t()) :: [map()]

Converts the response content into API-shaped assistant content blocks for replaying as the assistant turn in a follow-up request:

request
|> Request.add_message(:assistant, Response.to_assistant_content(response))

Emits string-keyed blocks that preserve signature (thinking) and data (redacted_thinking) — both required by the API when continuing an extended-thinking + tool-use conversation. Unknown block types are passed through unchanged (coverage grows in later specs).

After a server-side fallback (Request.set_fallbacks/2) it applies the API's continuation rules: before the last fallback block it drops thinking, redacted_thinking, connector_text and tool_use blocks, and keeps a server_tool_use or mcp_tool_use only when its result block is present. fallback blocks stay where they are. In practice this only changes a streamed response that fell back mid-output; a non-streaming response normally puts the fallback block first. response.content still holds every block.

If you edit earlier messages before replaying, a thinking block's signature may no longer match; see Request.set_thinking_block_binding/2.