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
OPUS_4_7_MODEL_PREFIX =
"claude-opus-4-7"
OPUS_4_7_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:



28
29
30
31
32
33
34
# File 'lib/boxcars/engine/anthropic.rb', line 28

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_opus_4_7_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



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

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.



42
43
44
45
46
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
# File 'lib/boxcars/engine/anthropic.rb', line 42

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



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

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



99
100
101
102
103
104
105
106
107
108
109
# File 'lib/boxcars/engine/anthropic.rb', line 99

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



84
85
86
87
88
89
90
# File 'lib/boxcars/engine/anthropic.rb', line 84

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_opus_4_7_unsupported_sampling_params(params)
  combine_assistant(params)
end

#default_paramsObject



79
80
81
# File 'lib/boxcars/engine/anthropic.rb', line 79

def default_params
  llm_params
end

#default_prefixesObject



111
112
113
# File 'lib/boxcars/engine/anthropic.rb', line 111

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