Class: PromptObjects::LLM::OpenAIAdapter

Inherits:
Object
  • Object
show all
Defined in:
lib/prompt_objects/llm/openai_adapter.rb

Overview

OpenAI API adapter for LLM calls.

Constant Summary collapse

DEFAULT_MODEL =
"gpt-5.6-luna"

Instance Method Summary collapse

Constructor Details

#initialize(api_key: nil, model: nil, base_url: nil, extra_headers: nil, provider_name: nil) ⇒ OpenAIAdapter

Returns a new instance of OpenAIAdapter.



9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/prompt_objects/llm/openai_adapter.rb', line 9

def initialize(api_key: nil, model: nil, base_url: nil, extra_headers: nil, provider_name: nil)
  @api_key = api_key || ENV.fetch("OPENAI_API_KEY") do
    raise Error, "OPENAI_API_KEY environment variable not set"
  end
  @model = model || DEFAULT_MODEL
  @provider_name = provider_name || "openai"

  client_opts = { access_token: @api_key }
  client_opts[:uri_base] = base_url if base_url
  client_opts[:extra_headers] = extra_headers if extra_headers
  @client = OpenAI::Client.new(**client_opts)
end

Instance Method Details

#chat(system:, messages:, tools: []) ⇒ Response

Make a chat completion request.

Parameters:

  • system (String)

    System prompt

  • messages (Array<Hash>)

    Conversation history

  • tools (Array<Hash>) (defaults to: [])

    Tool descriptors (optional)

Returns:



27
28
29
30
31
32
33
34
35
36
37
38
39
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
# File 'lib/prompt_objects/llm/openai_adapter.rb', line 27

def chat(system:, messages:, tools: [])
  params = {
    model: @model,
    messages: build_messages(system, messages)
  }

  # Only include tools if we have any
  if tools.any?
    params[:tools] = tools
    params[:tool_choice] = "auto"
    # GPT-5.6 supports function tools through Chat Completions only when
    # reasoning is disabled. Other OpenAI-compatible providers may not
    # recognize this parameter, so keep the compatibility fix narrow.
    params[:reasoning_effort] = "none" if openai_gpt_5_6?
  end

  raw_response = @client.chat(parameters: params)

  # Check for error responses (Ollama and some providers return errors inline)
  if raw_response.is_a?(Hash) && raw_response["error"]
    error_msg = raw_response["error"]
    error_msg = error_msg["message"] if error_msg.is_a?(Hash)
    raise Error, "#{@provider_name} API error: #{error_msg}"
  end

  parse_response(raw_response)
rescue Faraday::ClientError => e
  # Extract error body from 4xx responses (ruby-openai wraps these)
  body = e.response&.dig(:body) rescue nil
  detail = if body.is_a?(String)
             begin
               parsed = JSON.parse(body)
               parsed.dig("error", "message") || parsed["error"] || body
             rescue JSON::ParserError
               body
             end
           elsif body.is_a?(Hash)
             body.dig("error", "message") || body["error"] || body.to_s
           end
  raise Error, "#{@provider_name} API error (#{e.response&.dig(:status)}): #{detail || e.message}"
end