Class: Ask::Providers::OpenAI

Inherits:
Ask::Provider
  • Object
show all
Includes:
LLM::SSEBuffer
Defined in:
lib/ask/provider/openai.rb

Overview

OpenAI API provider. Also handles all OpenAI-compatible providers (OpenRouter, DeepSeek, Azure, XAI, Perplexity, GPUStack, etc.) via base_url override.

Direct Known Subclasses

DeepSeek, Mimo, OpenCode, OpenCodeGo, OpenRouter

Class Method Summary collapse

Instance Method Summary collapse

Methods included from LLM::SSEBuffer

#each_sse_event, #init_sse_buffer

Constructor Details

#initialize(config = {}) ⇒ OpenAI

Returns a new instance of OpenAI.



10
11
12
13
14
15
# File 'lib/ask/provider/openai.rb', line 10

def initialize(config = {})
  @provider_keys = extract_provider_keys(config)
  config = normalize_config(config)
  super(config)
  @http = build_http
end

Class Method Details

.assume_models_exist?Boolean

Returns:

  • (Boolean)


62
# File 'lib/ask/provider/openai.rb', line 62

def assume_models_exist?; false; end

.capabilitiesObject



57
58
59
# File 'lib/ask/provider/openai.rb', line 57

def capabilities
  { chat: true, streaming: true, tool_calls: true, vision: true, thinking: true, structured_output: true, embed: true, transcribe: true, paint: true, moderate: true }
end

.configuration_optionsObject



60
# File 'lib/ask/provider/openai.rb', line 60

def configuration_options; %i[api_key base_url organization_id project_id]; end

.configuration_requirementsObject



61
# File 'lib/ask/provider/openai.rb', line 61

def configuration_requirements; %i[api_key]; end

.slugObject



56
# File 'lib/ask/provider/openai.rb', line 56

def slug; "openai"; end

Instance Method Details

#api_baseObject



17
18
19
# File 'lib/ask/provider/openai.rb', line 17

def api_base
  @config.base_url || "https://api.openai.com/v1"
end

#chat(messages, model:, tools: nil, temperature: nil, stream: nil, schema: nil, **params, &block) ⇒ Object



30
31
32
33
34
# File 'lib/ask/provider/openai.rb', line 30

def chat(messages, model:, tools: nil, temperature: nil, stream: nil, schema: nil, **params, &block)
  msgs = messages.is_a?(Ask::Conversation) ? messages.to_a : messages
  payload = build_chat_payload(msgs, model, tools, temperature, stream, schema, **params)
  stream ? chat_stream(payload, model, &block) : chat_nonstream(payload, model)
end

#embed(texts, model:) ⇒ Object



36
37
38
39
40
41
42
# File 'lib/ask/provider/openai.rb', line 36

def embed(texts, model:)
  texts = Array(texts)
  response = @http.post("embeddings") { |r| r.body = { model: model, input: texts } }
  raise LLM::HTTP.map_error(response.status, response.body, provider: "OpenAI") unless response.success?
  embeddings = response.body["data"].map { |d| d["embedding"] }
  Ask::Result.success(embeddings.one? ? embeddings.first : embeddings)
end

#headersObject



21
22
23
24
25
26
27
28
# File 'lib/ask/provider/openai.rb', line 21

def headers
  key = @config.api_key || @config.openai_api_key
  h = { "Content-Type" => "application/json" }
  h["Authorization"] = "Bearer #{key}" if key
  h["OpenAI-Organization"] = @config.organization_id if @config.organization_id
  h["OpenAI-Project"] = @config.project_id if @config.project_id
  h
end

#list_modelsObject



44
45
46
47
48
# File 'lib/ask/provider/openai.rb', line 44

def list_models
  response = @http.get("models")
  return [] unless response.success?
  response.body["data"].map { |m| Ask::ModelInfo.new(id: m["id"], provider: slug, metadata: { owned_by: m["owned_by"] }) }
end

#parse_error(response) ⇒ Object



50
51
52
53
# File 'lib/ask/provider/openai.rb', line 50

def parse_error(response)
  body = response.body rescue nil
  body&.dig("error", "message") || body&.dig("error", "code")
end