Module: RubyLLM::Providers::Gemini::Capabilities

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

Overview

Determines capabilities and pricing for Google Gemini models

Constant Summary collapse

PRICES =
{
  flash_2: { # rubocop:disable Naming/VariableNumber
    input: 0.10,
    output: 0.40,
    audio_input: 0.70,
    cache: 0.025,
    cache_storage: 1.00,
    grounding_search: 35.00
  },
  flash_lite_2: { # rubocop:disable Naming/VariableNumber
    input: 0.075,
    output: 0.30
  },
  flash: {
    input: 0.075,
    output: 0.30,
    cache: 0.01875,
    cache_storage: 1.00,
    grounding_search: 35.00
  },
  flash_8b: {
    input: 0.0375,
    output: 0.15,
    cache: 0.01,
    cache_storage: 0.25,
    grounding_search: 35.00
  },
  pro: {
    input: 1.25,
    output: 5.0,
    cache: 0.3125,
    cache_storage: 4.50,
    grounding_search: 35.00
  },
  pro_2_5: { # rubocop:disable Naming/VariableNumber
    input: 0.12,
    output: 0.50
  },
  gemini_embedding: {
    input: 0.002,
    output: 0.004
  },
  embedding: {
    input: 0.00,
    output: 0.00
  },
  imagen: {
    price: 0.03
  },
  aqa: {
    input: 0.00,
    output: 0.00
  }
}.freeze

Class Method Summary collapse

Class Method Details

.capabilities_for(model_id) ⇒ Object



232
233
234
235
236
237
238
239
240
241
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 232

def capabilities_for(model_id)
  capabilities = ['streaming']

  capabilities << 'function_calling' if supports_functions?(model_id)
  capabilities << 'structured_output' if supports_json_mode?(model_id)
  capabilities << 'batch' if model_id.match?(/embedding|flash/)
  capabilities << 'caching' if supports_caching?(model_id)
  capabilities << 'fine_tuning' if supports_tuning?(model_id)
  capabilities
end

.context_length(model_id) ⇒ Object



146
147
148
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 146

def context_length(model_id)
  context_window_for(model_id)
end

.context_window_for(model_id) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 10

def context_window_for(model_id)
  case model_id
  when /gemini-2\.5-pro-exp-03-25/, /gemini-2\.0-flash/, /gemini-2\.0-flash-lite/, /gemini-1\.5-flash/, /gemini-1\.5-flash-8b/ # rubocop:disable Layout/LineLength
    1_048_576
  when /gemini-1\.5-pro/ then 2_097_152
  when /gemini-embedding-exp/ then 8_192
  when /text-embedding-004/, /embedding-001/ then 2_048
  when /aqa/ then 7_168
  when /imagen-3/ then nil
  else 32_768
  end
end

.default_input_priceObject



205
206
207
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 205

def default_input_price
  0.075
end

.default_output_priceObject



209
210
211
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 209

def default_output_price
  0.30
end

.format_display_name(model_id) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 73

def format_display_name(model_id)
  model_id
    .delete_prefix('models/')
    .split('-')
    .map(&:capitalize)
    .join(' ')
    .gsub(/(\d+\.\d+)/, ' \1')
    .gsub(/\s+/, ' ')
    .gsub('Aqa', 'AQA')
    .strip
end

.input_price_for(model_id) ⇒ Object



35
36
37
38
39
40
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 35

def input_price_for(model_id)
  base_price = PRICES.dig(pricing_family(model_id), :input) || default_input_price
  return base_price unless long_context_model?(model_id)

  context_window_for(model_id) > 128_000 ? base_price * 2 : base_price
end

.long_context_model?(model_id) ⇒ Boolean

Returns:

  • (Boolean)


142
143
144
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 142

def long_context_model?(model_id)
  model_id.match?(/gemini-1\.5-(?:pro|flash)|gemini-1\.5-flash-8b/)
end

.max_tokens_for(model_id) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 23

def max_tokens_for(model_id)
  case model_id
  when /gemini-2\.5-pro-exp-03-25/ then 64_000
  when /gemini-2\.0-flash/, /gemini-2\.0-flash-lite/, /gemini-1\.5-flash/, /gemini-1\.5-flash-8b/, /gemini-1\.5-pro/ # rubocop:disable Layout/LineLength
    8_192
  when /gemini-embedding-exp/ then nil
  when /text-embedding-004/, /embedding-001/ then 768
  when /imagen-3/ then 4
  else 4_096
  end
end

.modalities_for(model_id) ⇒ Object



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 213

def modalities_for(model_id)
  modalities = {
    input: ['text'],
    output: ['text']
  }

  if supports_vision?(model_id)
    modalities[:input] << 'image'
    modalities[:input] << 'pdf'
  end

  modalities[:input] << 'video' if supports_video?(model_id)
  modalities[:input] << 'audio' if model_id.match?(/audio/)
  modalities[:output] << 'embeddings' if model_id.match?(/embedding|gemini-embedding/)
  modalities[:output] = ['image'] if model_id.match?(/imagen/)

  modalities
end

.model_family(model_id) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 109

def model_family(model_id)
  case model_id
  when /gemini-2\.5-pro-exp-03-25/ then 'gemini25_pro_exp'
  when /gemini-2\.0-flash-lite/ then 'gemini20_flash_lite'
  when /gemini-2\.0-flash/ then 'gemini20_flash'
  when /gemini-1\.5-flash-8b/ then 'gemini15_flash_8b'
  when /gemini-1\.5-flash/ then 'gemini15_flash'
  when /gemini-1\.5-pro/ then 'gemini15_pro'
  when /gemini-embedding-exp/ then 'gemini_embedding_exp'
  when /text-embedding-004/ then 'embedding4'
  when /embedding-001/ then 'embedding1'
  when /aqa/ then 'aqa'
  when /imagen-3/ then 'imagen3'
  else 'other'
  end
end

.model_type(model_id) ⇒ Object



101
102
103
104
105
106
107
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 101

def model_type(model_id)
  case model_id
  when /text-embedding|embedding|gemini-embedding/ then 'embedding'
  when /imagen/ then 'image'
  else 'chat'
  end
end

.output_price_for(model_id) ⇒ Object



42
43
44
45
46
47
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 42

def output_price_for(model_id)
  base_price = PRICES.dig(pricing_family(model_id), :output) || default_output_price
  return base_price unless long_context_model?(model_id)

  context_window_for(model_id) > 128_000 ? base_price * 2 : base_price
end

.pricing_family(model_id) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 126

def pricing_family(model_id)
  case model_id
  when /gemini-2\.5-pro-exp-03-25/ then :pro_2_5 # rubocop:disable Naming/VariableNumber
  when /gemini-2\.0-flash-lite/ then :flash_lite_2 # rubocop:disable Naming/VariableNumber
  when /gemini-2\.0-flash/ then :flash_2 # rubocop:disable Naming/VariableNumber
  when /gemini-1\.5-flash-8b/ then :flash_8b
  when /gemini-1\.5-flash/ then :flash
  when /gemini-1\.5-pro/ then :pro
  when /gemini-embedding-exp/ then :gemini_embedding
  when /text-embedding|embedding/ then :embedding
  when /imagen/ then :imagen
  when /aqa/ then :aqa
  else :base
  end
end

.pricing_for(model_id) ⇒ Object



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 243

def pricing_for(model_id)
  family = pricing_family(model_id)
  prices = PRICES.fetch(family, { input: default_input_price, output: default_output_price })

  standard_pricing = {
    input_per_million: prices[:input],
    output_per_million: prices[:output]
  }

  standard_pricing[:cached_input_per_million] = prices[:input_hit] if prices[:input_hit]

  batch_pricing = {
    input_per_million: (standard_pricing[:input_per_million] || 0) * 0.5,
    output_per_million: (standard_pricing[:output_per_million] || 0) * 0.5
  }

  if standard_pricing[:cached_input_per_million]
    batch_pricing[:cached_input_per_million] = standard_pricing[:cached_input_per_million] * 0.5
  end

  pricing = {
    text_tokens: {
      standard: standard_pricing,
      batch: batch_pricing
    }
  }

  if model_id.match?(/embedding|gemini-embedding/)
    pricing[:embeddings] = {
      standard: { input_per_million: prices[:price] || 0.002 }
    }
  end

  pricing
end

.supports_audio?(model_id) ⇒ Boolean

Returns:

  • (Boolean)


97
98
99
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 97

def supports_audio?(model_id)
  model_id.match?(/gemini|pro|flash/)
end

.supports_caching?(model_id) ⇒ Boolean

Returns:

  • (Boolean)


85
86
87
88
89
90
91
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 85

def supports_caching?(model_id)
  if model_id.match?(/flash-lite|gemini-2\.5-pro-exp-03-25|aqa|imagen|text-embedding|embedding-001/)
    return false
  end

  model_id.match?(/gemini|pro|flash/)
end

.supports_functions?(model_id) ⇒ Boolean

Returns:

  • (Boolean)


59
60
61
62
63
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 59

def supports_functions?(model_id)
  return false if model_id.match?(/text-embedding|embedding-001|aqa|flash-lite|imagen|gemini-2\.0-flash-lite/)

  model_id.match?(/gemini|pro|flash/)
end

.supports_json_mode?(model_id) ⇒ Boolean

Returns:

  • (Boolean)


65
66
67
68
69
70
71
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 65

def supports_json_mode?(model_id)
  if model_id.match?(/text-embedding|embedding-001|aqa|imagen|gemini-2\.0-flash-lite|gemini-2\.5-pro-exp-03-25/)
    return false
  end

  model_id.match?(/gemini|pro|flash/)
end

.supports_tuning?(model_id) ⇒ Boolean

Returns:

  • (Boolean)


93
94
95
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 93

def supports_tuning?(model_id)
  model_id.match?(/gemini-1\.5-flash|gemini-1\.5-flash-8b/)
end

.supports_video?(model_id) ⇒ Boolean

Returns:

  • (Boolean)


55
56
57
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 55

def supports_video?(model_id)
  model_id.match?(/gemini/)
end

.supports_vision?(model_id) ⇒ Boolean

Returns:

  • (Boolean)


49
50
51
52
53
# File 'lib/ruby_llm/providers/gemini/capabilities.rb', line 49

def supports_vision?(model_id)
  return false if model_id.match?(/text-embedding|embedding-001|aqa/)

  model_id.match?(/gemini|flash|pro|imagen/)
end