Class: Boxcars::Openai

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

Overview

Engine that talks to OpenAI’s REST API.

Constant Summary collapse

CHAT_MODEL_REGEX =
/(^gpt-4)|(-turbo\b)|(^o\d)|(gpt-3\.5-turbo)/
O_SERIES_REGEX =
/^o/
GPT5_MODEL_REGEX =
/\Agpt-[56].*/
DEFAULT_PARAMS =
{
  model: "gpt-4o-mini",
  temperature: 0.1,
  max_tokens: 4096
}.freeze
DEFAULT_NAME =
"OpenAI engine"
DEFAULT_DESCRIPTION =
"Useful when you need AI to answer questions. Ask targeted questions."

Instance Attribute Summary collapse

Attributes inherited from Engine

#user_id

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Engine

#generate, #generation_info, #get_num_tokens

Constructor Details

#initialize(name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, prompts: [], batch_size: 20, **kwargs) ⇒ Openai


Construction



31
32
33
34
35
36
37
38
39
40
41
# File 'lib/boxcars/engine/openai.rb', line 31

def initialize(name: DEFAULT_NAME,
               description: DEFAULT_DESCRIPTION,
               prompts: [],
               batch_size: 20,
               **kwargs)
  user_id          = kwargs.delete(:user_id)
  @open_ai_params  = adjust_for_o_series!(DEFAULT_PARAMS.merge(kwargs))
  @prompts         = prompts
  @batch_size      = batch_size
  super(description:, name:, user_id:)
end

Instance Attribute Details

#batch_sizeObject (readonly)

Returns the value of attribute batch_size.



26
27
28
# File 'lib/boxcars/engine/openai.rb', line 26

def batch_size
  @batch_size
end

#open_ai_paramsObject (readonly)

Returns the value of attribute open_ai_params.



26
27
28
# File 'lib/boxcars/engine/openai.rb', line 26

def open_ai_params
  @open_ai_params
end

#promptsObject (readonly)

Returns the value of attribute prompts.



26
27
28
# File 'lib/boxcars/engine/openai.rb', line 26

def prompts
  @prompts
end

Class Method Details

.open_ai_client(openai_access_token: nil) ⇒ Object


Class helpers



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

def self.open_ai_client(openai_access_token: nil)
  ::OpenAI::Client.new(
    access_token: Boxcars.configuration.openai_access_token(openai_access_token:),
    organization_id: Boxcars.configuration.organization_id,
    log_errors: true
  )
end

Instance Method Details

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


Public API



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
78
79
# File 'lib/boxcars/engine/openai.rb', line 46

def client(prompt:, inputs: {}, openai_access_token: nil, **kwargs)
  start_time       = Time.now
  response_data    = { response_obj: nil, parsed_json: nil,
                       success: false, error: nil, status_code: nil }
  current_params   = open_ai_params.merge(kwargs)
  is_chat_model    = chat_model?(current_params[:model])
  prompt_object    = prompt.is_a?(Array) ? prompt.first : prompt
  api_request      = build_api_request(prompt_object, inputs, current_params, chat: is_chat_model)

  begin
    raw_response = execute_api_call(
      self.class.open_ai_client(openai_access_token:),
      is_chat_model,
      api_request
    )
    process_response(raw_response, response_data)
  rescue ::OpenAI::Error, StandardError => e
    handle_error(e, response_data)
  ensure
    track_openai_observability(
      {
        start_time:,
        prompt_object: prompt_object,
        inputs: inputs,
        api_request: api_request,
        current_params: current_params,
        is_chat_model: is_chat_model
      },
      response_data
    )
  end

  handle_call_outcome(response_data:)
end

#default_paramsObject

Expose the defaults so callers can introspect or dup/merge them



91
# File 'lib/boxcars/engine/openai.rb', line 91

def default_params = open_ai_params

#run(question) ⇒ Object

Convenience one-shot helper used by Engine#generate



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

def run(question, **)
  prompt      = Prompt.new(template: question)
  raw_json    = client(prompt:, inputs: {}, **)
  ans         = extract_answer(raw_json)
  Boxcars.debug("Answer: #{ans}", :cyan)
  ans
end

#validate_response!(response, must_haves: %w[choices])) ⇒ Object

– Public helper ————————————————————- Some callers outside this class still invoke ‘validate_response!` directly. It simply raises if the JSON body contains an “error” payload.



107
108
109
110
111
112
113
# File 'lib/boxcars/engine/openai.rb', line 107

def validate_response!(response, must_haves: %w[choices])
  if response.is_a?(Hash) && response.key?("output")
    super(response, must_haves: %w[output])
  else
    super
  end
end