Brute
Brute::MessageTransport::Anthropic
module Brute::MessageTransport class Anthropic < Brute::MessageTransport endendMessageTransport for the
official anthropic gem (https://github.com/anthropics/anthropic-sdk-ruby).
Brute does not require it — you do:
require "anthropic"
client = Anthropic::Client.new(api_key: ENV["ANTHROPIC_API_KEY"])response = client.messages.create( model: "claude-opus-4-8", max_tokens: 16_000, system_: Brute::MessageTransport::Anthropic.system_text(env[:messages]), messages: Brute::MessageTransport::Anthropic.dump_all(env[:messages]), tools: ...,)Brute::MessageTransport::Anthropic.wrap_each(response) { |m| env[:messages] << m }Anthropic’s Messages API differs from
chat-completions-shaped APIs in two ways this transport absorbs: the system
prompt is a top-level parameter (not a message), and tool results are content
blocks inside a user message — consecutive tool results are folded into one
user turn so roles keep alternating.
Constants
EmptyMessage
EmptyMessage = Class.new(StandardError)An assistant message this cannot put on the wire, because
Anthropic takes no
empty content and there is nothing in it to send.
FORMAT
FORMAT = "anthropic-claude-v1"Whose signatures these are.
OpenRouter names
the same format when it proxies Claude, so thinking that came back through it
goes home.
Class Methods
self.assistant_content
assistant_content(message)The assistant turn’s content blocks, in the order the wire wants them:
thinking first, because the sequence is part of what
Anthropic verifies,
then what was said, then what was called.
Anthropic takes no
empty content – not an empty string, not an empty block, not an empty array
– so a message with nothing in it is not one this can express. It says so
rather than sending a request that is already a 400.
self.dump
dump(message, model: nil)Brute::Message -> an
Anthropic message
param hash.
self.dump_all
dump_all(messages, model: nil)Brute log -> the messages: array.
Drops :system messages (see .system_text) and folds consecutive :tool results
into one user turn.
self.expressible?
expressible?(reasoning)Whether every stored block has an
Anthropic form. There
are two of them and no third – thinking and redacted_thinking – and both
carry a payload
Anthropic issued and
checks on the way back. So a block another provider signed, one carrying no
signature at all, and a summary (which is a precis, and has no block of its
own here) each have no form to take. The sequence goes whole or not at all,
because what
Anthropic verifies is
the sequence, so one such block answers for the rest.
self.system_text
system_text(messages)The :system messages’ text, for the top-level system_: parameter.
self.thinking_block
thinking_block(block)A thinking block, or the redacted one that stands in for it when the provider will not show its working. Both go back whole.
self.thinking_blocks
thinking_blocks(message)The stored blocks as
Anthropic content
blocks, in order. Where the sequence has no
Anthropic form there
is nothing to send – an unverifiable block is a 400, and no thinking beats a
failed request.
self.thinking_param
thinking_param(block)One stored block as the content block
Anthropic named it.
The opaque payload rides under data, the readable one under thinking with
the signature that verifies it.
self.tool_result_block
tool_result_block(message)Not documented.
Defined in
lib/brute/message_transport/anthropic.rb