Class: Brute::MessageTransport::Anthropic

Inherits:
MessageTransport
  • Object
show all
Defined in:
lib/brute/message_transport/anthropic.rb

Overview

MessageTransport 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.

Class Method Summary collapse

Class Method Details

.dump(message) ⇒ Object

Brute::Message -> an Anthropic message param hash.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/brute/message_transport/anthropic.rb', line 51

def self.dump(message)
  case message.role
  when :tool
    { role: "user", content: [tool_result_block(message)] }
  when :assistant
    if message.tool_call?
      blocks = []
      blocks << { type: "text", text: message.content } unless message.content.to_s.empty?
      blocks += message.tool_calls.map { |tc| { type: "tool_use", id: tc.id, name: tc.name, input: tc.arguments } }
      { role: "assistant", content: blocks }
    else
      { role: "assistant", content: message.content }
    end
  else
    { role: message.role.to_s, content: message.content }
  end
end

.dump_all(messages) ⇒ Object

Brute log -> the messages: array. Drops :system messages (see .system_text) and folds consecutive :tool results into one user turn.



38
39
40
41
42
43
44
45
46
47
48
# File 'lib/brute/message_transport/anthropic.rb', line 38

def self.dump_all(messages)
  chat = messages.reject { |m| m.role == :system }

  chat.chunk_while { |a, b| a.role == :tool && b.role == :tool }.map do |group|
    if group.first.role == :tool
      { role: "user", content: group.map { |m| tool_result_block(m) } }
    else
      dump(group.first)
    end
  end
end

.system_text(messages) ⇒ Object

The :system messages' text, for the top-level system_: parameter.



32
33
34
# File 'lib/brute/message_transport/anthropic.rb', line 32

def self.system_text(messages)
  messages.select { |m| m.role == :system }.map(&:content).join("\n\n")
end

.tool_result_block(message) ⇒ Object



69
70
71
# File 'lib/brute/message_transport/anthropic.rb', line 69

def self.tool_result_block(message)
  { type: "tool_result", tool_use_id: message.tool_call_id, content: message.content.to_s }
end