Module: RubyLLM::Providers::OpenAI::Capabilities

Defined in:
lib/ruby_llm/providers/openai/capabilities.rb

Overview

Provider-level capability checks and narrow registry fallbacks.

Constant Summary collapse

MODEL_PATTERNS =
{
  gpt41: /^gpt-4\.1(?!-(?:mini|nano))/,
  gpt41_mini: /^gpt-4\.1-mini/,
  gpt41_nano: /^gpt-4\.1-nano/,
  gpt4: /^gpt-4(?:-\d{6})?$/,
  gpt4_turbo: /^gpt-4(?:\.5)?-(?:\d{6}-)?(preview|turbo)/,
  gpt35_turbo: /^gpt-3\.5-turbo/,
  gpt4o: /^gpt-4o(?!-(?:mini|audio|realtime|transcribe|tts|search))/,
  gpt4o_audio: /^gpt-4o-(?:audio)/,
  gpt4o_mini: /^gpt-4o-mini(?!-(?:audio|realtime|transcribe|tts|search))/,
  gpt4o_mini_audio: /^gpt-4o-mini-audio/,
  gpt4o_mini_realtime: /^gpt-4o-mini-realtime/,
  gpt4o_mini_transcribe: /^gpt-4o-mini-transcribe/,
  gpt4o_mini_tts: /^gpt-4o-mini-tts/,
  gpt4o_realtime: /^gpt-4o-realtime/,
  gpt4o_search: /^gpt-4o-search/,
  gpt4o_transcribe: /^gpt-4o-transcribe/,
  gpt5: /^gpt-5(?!.*(?:mini|nano))/,
  gpt5_mini: /^gpt-5.*mini/,
  gpt5_nano: /^gpt-5.*nano/,
  o1: /^o1(?!-(?:mini|pro))/,
  o1_mini: /^o1-mini/,
  o1_pro: /^o1-pro/,
  o3_mini: /^o3-mini/,
  babbage: /^babbage/,
  davinci: /^davinci/,
  embedding3_large: /^text-embedding-3-large/,
  embedding3_small: /^text-embedding-3-small/,
  embedding_ada: /^text-embedding-ada/,
  tts1: /^tts-1(?!-hd)/,
  tts1_hd: /^tts-1-hd/,
  whisper: /^whisper/,
  moderation: /^(?:omni|text)-moderation/
}.freeze
PRICES =
{
  gpt5: { input: 1.25, output: 10.0, cached_input: 0.125 },
  gpt5_mini: { input: 0.25, output: 2.0, cached_input: 0.025 },
  gpt5_nano: { input: 0.05, output: 0.4, cached_input: 0.005 },
  gpt41: { input: 2.0, output: 8.0, cached_input: 0.5 },
  gpt41_mini: { input: 0.4, output: 1.6, cached_input: 0.1 },
  gpt41_nano: { input: 0.1, output: 0.4 },
  gpt4: { input: 10.0, output: 30.0 },
  gpt4_turbo: { input: 10.0, output: 30.0 },
  gpt35_turbo: { input: 0.5, output: 1.5 },
  gpt4o: { input: 2.5, output: 10.0 },
  gpt4o_audio: { input: 2.5, output: 10.0 },
  gpt4o_mini: { input: 0.15, output: 0.6 },
  gpt4o_mini_audio: { input: 0.15, output: 0.6 },
  gpt4o_mini_realtime: { input: 0.6, output: 2.4 },
  gpt4o_mini_transcribe: { input: 1.25, output: 5.0 },
  gpt4o_mini_tts: { input: 0.6, output: 12.0 },
  gpt4o_realtime: { input: 5.0, output: 20.0 },
  gpt4o_search: { input: 2.5, output: 10.0 },
  gpt4o_transcribe: { input: 2.5, output: 10.0 },
  o1: { input: 15.0, output: 60.0 },
  o1_mini: { input: 1.1, output: 4.4 },
  o1_pro: { input: 150.0, output: 600.0 },
  o3_mini: { input: 1.1, output: 4.4 },
  babbage: { input: 0.4, output: 0.4 },
  davinci: { input: 2.0, output: 2.0 },
  embedding3_large: { price: 0.13 },
  embedding3_small: { price: 0.02 },
  embedding_ada: { price: 0.10 },
  tts1: { price: 15.0 },
  tts1_hd: { price: 30.0 },
  whisper: { price: 0.006 },
  moderation: { price: 0.0 }
}.freeze

Class Method Summary collapse

Class Method Details

.cached_input_price_for(model_id) ⇒ Object



186
187
188
189
# File 'lib/ruby_llm/providers/openai/capabilities.rb', line 186

def cached_input_price_for(model_id)
  family = model_family(model_id).to_sym
  PRICES.fetch(family, {})[:cached_input]
end

.context_window_for(model_id) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/ruby_llm/providers/openai/capabilities.rb', line 88

def context_window_for(model_id)
  case model_family(model_id)
  when 'gpt41', 'gpt41_mini', 'gpt41_nano' then 1_047_576
  when 'gpt5', 'gpt5_mini', 'gpt5_nano', 'gpt4_turbo', 'gpt4o', 'gpt4o_audio', 'gpt4o_mini',
       'gpt4o_mini_audio', 'gpt4o_mini_realtime', 'gpt4o_realtime', 'gpt4o_search',
       'gpt4o_transcribe', 'o1_mini' then 128_000
  when 'gpt4' then 8_192
  when 'gpt4o_mini_transcribe' then 16_000
  when 'o1', 'o1_pro', 'o3_mini' then 200_000
  when 'gpt35_turbo' then 16_385
  when 'gpt4o_mini_tts', 'tts1', 'tts1_hd', 'whisper', 'moderation',
       'embedding3_large', 'embedding3_small', 'embedding_ada' then nil
  else 4_096
  end
end

.critical_capabilities_for(model_id) ⇒ Object



119
120
121
122
123
124
125
126
# File 'lib/ruby_llm/providers/openai/capabilities.rb', line 119

def critical_capabilities_for(model_id)
  capabilities = []
  capabilities << 'function_calling' if supports_functions?(model_id)
  capabilities << 'structured_output' if supports_structured_output?(model_id)
  capabilities << 'vision' if supports_vision?(model_id)
  capabilities << 'reasoning' if model_id.match?(/o\d|gpt-5|codex/)
  capabilities
end

.input_price_for(model_id) ⇒ Object



178
179
180
# File 'lib/ruby_llm/providers/openai/capabilities.rb', line 178

def input_price_for(model_id)
  price_for(model_id, :input, 0.50)
end

.max_tokens_for(model_id) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/ruby_llm/providers/openai/capabilities.rb', line 104

def max_tokens_for(model_id)
  case model_family(model_id)
  when 'gpt5', 'gpt5_mini', 'gpt5_nano' then 400_000
  when 'gpt41', 'gpt41_mini', 'gpt41_nano' then 32_768
  when 'gpt4' then 8_192
  when 'gpt35_turbo' then 4_096
  when 'gpt4o_mini_transcribe' then 2_000
  when 'o1', 'o1_pro', 'o3_mini' then 100_000
  when 'o1_mini' then 65_536
  when 'gpt4o_mini_tts', 'tts1', 'tts1_hd', 'whisper', 'moderation',
       'embedding3_large', 'embedding3_small', 'embedding_ada' then nil
  else 16_384
  end
end

.model_family(model_id) ⇒ Object



140
141
142
143
144
145
146
# File 'lib/ruby_llm/providers/openai/capabilities.rb', line 140

def model_family(model_id)
  MODEL_PATTERNS.each do |family, pattern|
    return family.to_s if model_id.match?(pattern)
  end

  'other'
end

.output_price_for(model_id) ⇒ Object



182
183
184
# File 'lib/ruby_llm/providers/openai/capabilities.rb', line 182

def output_price_for(model_id)
  price_for(model_id, :output, 1.50)
end

.price_for(model_id, key, fallback) ⇒ Object



191
192
193
194
195
# File 'lib/ruby_llm/providers/openai/capabilities.rb', line 191

def price_for(model_id, key, fallback)
  family = model_family(model_id).to_sym
  prices = PRICES.fetch(family, { key => fallback })
  prices[key] || prices[:price] || fallback
end

.pricing_for(model_id) ⇒ Object



128
129
130
131
132
133
134
135
136
137
138
# File 'lib/ruby_llm/providers/openai/capabilities.rb', line 128

def pricing_for(model_id)
  standard_pricing = {
    input_per_million: input_price_for(model_id),
    output_per_million: output_price_for(model_id)
  }

  cached_price = cached_input_price_for(model_id)
  standard_pricing[:cached_input_per_million] = cached_price if cached_price

  { text_tokens: { standard: standard_pricing } }
end

.supports_functions?(model_id) ⇒ Object



158
159
160
161
162
163
164
165
166
# File 'lib/ruby_llm/providers/openai/capabilities.rb', line 158

def supports_functions?(model_id)
  case model_family(model_id)
  when 'gpt5', 'gpt5_mini', 'gpt5_nano', 'gpt41', 'gpt41_mini', 'gpt41_nano', 'gpt4',
       'gpt4_turbo', 'gpt4o', 'gpt4o_mini', 'o1', 'o1_pro', 'o3_mini'
    true
  else
    false
  end
end

.supports_structured_output?(model_id) ⇒ Object



168
169
170
171
172
173
174
175
176
# File 'lib/ruby_llm/providers/openai/capabilities.rb', line 168

def supports_structured_output?(model_id)
  case model_family(model_id)
  when 'gpt5', 'gpt5_mini', 'gpt5_nano', 'gpt41', 'gpt41_mini', 'gpt41_nano', 'gpt4o',
       'gpt4o_mini', 'o1', 'o1_pro', 'o3_mini'
    true
  else
    false
  end
end

.supports_tool_choice?(_model_id) ⇒ Boolean

Returns:

  • (Boolean)


80
81
82
# File 'lib/ruby_llm/providers/openai/capabilities.rb', line 80

def supports_tool_choice?(_model_id)
  true
end

.supports_tool_parallel_control?(_model_id) ⇒ Boolean

Returns:

  • (Boolean)


84
85
86
# File 'lib/ruby_llm/providers/openai/capabilities.rb', line 84

def supports_tool_parallel_control?(_model_id)
  true
end

.supports_vision?(model_id) ⇒ Object



148
149
150
151
152
153
154
155
156
# File 'lib/ruby_llm/providers/openai/capabilities.rb', line 148

def supports_vision?(model_id)
  case model_family(model_id)
  when 'gpt5', 'gpt5_mini', 'gpt5_nano', 'gpt41', 'gpt41_mini', 'gpt41_nano', 'gpt4',
       'gpt4_turbo', 'gpt4o', 'gpt4o_mini', 'o1', 'o1_pro', 'moderation', 'gpt4o_search'
    true
  else
    false
  end
end