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



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

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



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

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.



40
41
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
# File 'lib/boxcars/engine/anthropic.rb', line 40

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



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

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



96
97
98
99
100
101
102
103
104
105
106
# File 'lib/boxcars/engine/anthropic.rb', line 96

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



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

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

#default_paramsObject



77
78
79
# File 'lib/boxcars/engine/anthropic.rb', line 77

def default_params
  llm_params
end

#default_prefixesObject



108
109
110
# File 'lib/boxcars/engine/anthropic.rb', line 108

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