Class: Boxcars::Anthropic

Inherits:
Engine
  • Object
show all
Includes:
OpenAICompatibleChatHelpers, UnifiedObservability
Defined in:
lib/boxcars/engine/anthropic.rb

Overview

An engine that uses Anthropic's API.

Constant Summary collapse

DEFAULT_PARAMS =

The default parameters to use when asking the engine.

{
  model: "claude-3-5-sonnet-20240620",
  max_tokens: 4096,
  temperature: 0.1
}.freeze
MODELS_WITHOUT_SAMPLING_PARAMS =
%w[
  claude-opus-4-7
  claude-sonnet-5
  claude-opus-5
  claude-fable-5
].freeze
UNSUPPORTED_SAMPLING_PARAMS =
%i[temperature top_p top_k].freeze
DEFAULT_NAME =

The default name of the engine.

"Anthropic engine"
DEFAULT_DESCRIPTION =

The default description of the engine.

"useful for when you need to use Anthropic AI to answer questions. " \
"You should ask targeted questions"

Instance Attribute Summary collapse

Attributes inherited from Engine

#batch_size, #user_id

Instance Method Summary collapse

Methods inherited from Engine

#add_usage_detail!, #aggregate_generate_usage!, #aggregate_token_usage_details!, #append_generate_choices!, #capabilities, #extract_answer, #generate, #generate_one, #generation_info, #get_num_tokens, #normalize_generate_response, #process_generate_prompt!, #process_generate_response!, #run, #supports?, #usage_nested_token_value, #usage_token_value, #validate_response!

Constructor Details

#initialize(name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, **kwargs) ⇒ Anthropic

Initializes an Anthropic engine instance.

Raises:



33
34
35
36
37
38
39
# File 'lib/boxcars/engine/anthropic.rb', line 33

def initialize(name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, **kwargs)
  raise ArgumentError, "unknown keyword: :prompts" if kwargs.key?(:prompts)

  user_id = kwargs.delete(:user_id)
  @llm_params = remove_unsupported_sampling_params(DEFAULT_PARAMS.merge(kwargs))
  super(description:, name:, batch_size: 20, user_id:)
end

Instance Attribute Details

#llm_paramsObject (readonly)

Returns the value of attribute llm_params.



10
11
12
# File 'lib/boxcars/engine/anthropic.rb', line 10

def llm_params
  @llm_params
end

Instance Method Details

#anthropic_client(anthropic_api_key: nil) ⇒ Object



41
42
43
44
# File 'lib/boxcars/engine/anthropic.rb', line 41

def anthropic_client(anthropic_api_key: nil)
  Boxcars::OptionalDependency.require!("ruby-anthropic", feature: "Boxcars::Anthropic", require_as: "anthropic")
  ::Anthropic::Client.new(access_token: anthropic_api_key)
end

#client(prompt:, inputs: {}, **kwargs) ⇒ Object

Calls Anthropic and returns the parsed response object.



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/boxcars/engine/anthropic.rb', line 47

def client(prompt:, inputs: {}, **kwargs)
  start_time = Time.now
  response_data = { response_obj: nil, parsed_json: nil, success: false, error: nil, status_code: nil }
  current_params = llm_params.merge(kwargs)
  current_prompt_object = prompt
  api_request_params = nil

  begin
    api_key = Boxcars.configuration.anthropic_api_key(**kwargs)
    aclient = anthropic_client(anthropic_api_key: api_key)
    api_request_params = convert_to_anthropic(current_prompt_object.as_messages(inputs).merge(current_params))

    if Boxcars.configuration.log_prompts
      if api_request_params[:messages].length < 2 && api_request_params[:system] && !api_request_params[:system].empty?
        Boxcars.debug(">>>>>> Role: system <<<<<<\n#{api_request_params[:system]}")
      end
      log_messages_debug(api_request_params[:messages])
    end

    raw_response = aclient.messages(parameters: api_request_params)
    process_anthropic_response(raw_response, response_data)
  rescue StandardError => e
    handle_anthropic_error(e, response_data)
  ensure
    call_context = {
      start_time:,
      prompt_object: current_prompt_object,
      inputs:,
      api_request_params:,
      current_params:
    }
    track_anthropic_observability(call_context, response_data)
  end

  anthropic_handle_call_outcome(response_data:)
end

#combine_assistant(params) ⇒ Object



97
98
99
100
101
# File 'lib/boxcars/engine/anthropic.rb', line 97

def combine_assistant(params)
  params[:messages] = combine_assistant_entries(params[:messages])
  params[:messages].last[:content].rstrip! if params[:messages].last[:role] == :assistant
  params
end

#combine_assistant_entries(hashes) ⇒ Object

if we have multiple assistant entries in a row, we need to combine them



104
105
106
107
108
109
110
111
112
113
114
# File 'lib/boxcars/engine/anthropic.rb', line 104

def combine_assistant_entries(hashes)
  combined_hashes = []
  hashes.each do |hash|
    if combined_hashes.empty? || combined_hashes.last[:role] != :assistant || hash[:role] != :assistant
      combined_hashes << hash
    else
      combined_hashes.last[:content].concat("\n", hash[:content].rstrip)
    end
  end
  combined_hashes
end

#convert_to_anthropic(params) ⇒ Object

convert generic parameters to Anthopic specific ones



89
90
91
92
93
94
95
# File 'lib/boxcars/engine/anthropic.rb', line 89

def convert_to_anthropic(params)
  params[:stop_sequences] = params.delete(:stop) if params.key?(:stop)
  params[:system] = params[:messages].shift[:content] if params.dig(:messages, 0, :role) == :system
  params[:messages].pop if params[:messages].last[:content].nil? || params[:messages].last[:content].strip.empty?
  remove_unsupported_sampling_params(params)
  combine_assistant(params)
end

#default_paramsObject



84
85
86
# File 'lib/boxcars/engine/anthropic.rb', line 84

def default_params
  llm_params
end

#default_prefixesObject



116
117
118
# File 'lib/boxcars/engine/anthropic.rb', line 116

def default_prefixes
  { system: "Human: ", user: "Human: ", assistant: "Assistant: ", history: :history }
end