ruby_llm 1.13.2 → 1.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -7
  3. data/lib/generators/ruby_llm/agent/agent_generator.rb +36 -0
  4. data/lib/generators/ruby_llm/agent/templates/agent.rb.tt +6 -0
  5. data/lib/generators/ruby_llm/agent/templates/instructions.txt.erb.tt +0 -0
  6. data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +110 -41
  7. data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +14 -15
  8. data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +8 -11
  9. data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +2 -2
  10. data/lib/generators/ruby_llm/chat_ui/templates/helpers/messages_helper.rb.tt +25 -0
  11. data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +1 -1
  12. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_chat.html.erb.tt +16 -0
  13. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_form.html.erb.tt +31 -0
  14. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/index.html.erb.tt +31 -0
  15. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/new.html.erb.tt +9 -0
  16. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/show.html.erb.tt +27 -0
  17. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_assistant.html.erb.tt +14 -0
  18. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_content.html.erb.tt +1 -0
  19. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_error.html.erb.tt +13 -0
  20. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_form.html.erb.tt +23 -0
  21. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_system.html.erb.tt +10 -0
  22. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool.html.erb.tt +2 -0
  23. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool_calls.html.erb.tt +4 -0
  24. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_user.html.erb.tt +14 -0
  25. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_calls/_default.html.erb.tt +13 -0
  26. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_results/_default.html.erb.tt +21 -0
  27. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/_model.html.erb.tt +17 -0
  28. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/index.html.erb.tt +40 -0
  29. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/show.html.erb.tt +27 -0
  30. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +2 -2
  31. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +2 -2
  32. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +19 -7
  33. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt +1 -1
  34. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +5 -3
  35. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_assistant.html.erb.tt +9 -0
  36. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_content.html.erb.tt +1 -1
  37. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_error.html.erb.tt +8 -0
  38. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +1 -1
  39. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_system.html.erb.tt +6 -0
  40. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool.html.erb.tt +2 -0
  41. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool_calls.html.erb.tt +4 -7
  42. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_user.html.erb.tt +9 -0
  43. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +5 -7
  44. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_calls/_default.html.erb.tt +8 -0
  45. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_results/_default.html.erb.tt +16 -0
  46. data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +11 -12
  47. data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +27 -17
  48. data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +3 -4
  49. data/lib/generators/ruby_llm/generator_helpers.rb +33 -17
  50. data/lib/generators/ruby_llm/install/install_generator.rb +21 -18
  51. data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +3 -4
  52. data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +1 -1
  53. data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +2 -2
  54. data/lib/generators/ruby_llm/schema/schema_generator.rb +26 -0
  55. data/lib/generators/ruby_llm/schema/templates/schema.rb.tt +2 -0
  56. data/lib/generators/ruby_llm/tool/templates/tool.rb.tt +9 -0
  57. data/lib/generators/ruby_llm/tool/templates/tool_call.html.erb.tt +13 -0
  58. data/lib/generators/ruby_llm/tool/templates/tool_result.html.erb.tt +13 -0
  59. data/lib/generators/ruby_llm/tool/tool_generator.rb +96 -0
  60. data/lib/generators/ruby_llm/upgrade_to_v1_10/upgrade_to_v1_10_generator.rb +1 -1
  61. data/lib/generators/ruby_llm/upgrade_to_v1_14/templates/add_v1_14_tool_call_columns.rb.tt +7 -0
  62. data/lib/generators/ruby_llm/upgrade_to_v1_14/upgrade_to_v1_14_generator.rb +49 -0
  63. data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +2 -4
  64. data/lib/generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator.rb +1 -1
  65. data/lib/ruby_llm/active_record/acts_as.rb +2 -0
  66. data/lib/ruby_llm/active_record/acts_as_legacy.rb +2 -0
  67. data/lib/ruby_llm/active_record/chat_methods.rb +13 -6
  68. data/lib/ruby_llm/active_record/message_methods.rb +17 -0
  69. data/lib/ruby_llm/active_record/model_methods.rb +1 -1
  70. data/lib/ruby_llm/active_record/payload_helpers.rb +26 -0
  71. data/lib/ruby_llm/active_record/tool_call_methods.rb +15 -0
  72. data/lib/ruby_llm/agent.rb +11 -0
  73. data/lib/ruby_llm/aliases.json +34 -15
  74. data/lib/ruby_llm/attachment.rb +3 -0
  75. data/lib/ruby_llm/configuration.rb +54 -73
  76. data/lib/ruby_llm/connection.rb +1 -3
  77. data/lib/ruby_llm/error.rb +5 -0
  78. data/lib/ruby_llm/model/info.rb +14 -12
  79. data/lib/ruby_llm/models.json +7446 -10126
  80. data/lib/ruby_llm/models.rb +10 -3
  81. data/lib/ruby_llm/provider.rb +5 -0
  82. data/lib/ruby_llm/providers/anthropic/capabilities.rb +1 -133
  83. data/lib/ruby_llm/providers/anthropic/models.rb +2 -8
  84. data/lib/ruby_llm/providers/anthropic.rb +4 -0
  85. data/lib/ruby_llm/providers/azure.rb +4 -0
  86. data/lib/ruby_llm/providers/bedrock.rb +4 -0
  87. data/lib/ruby_llm/providers/deepseek/capabilities.rb +1 -119
  88. data/lib/ruby_llm/providers/deepseek.rb +4 -0
  89. data/lib/ruby_llm/providers/gemini/capabilities.rb +45 -215
  90. data/lib/ruby_llm/providers/gemini/models.rb +2 -4
  91. data/lib/ruby_llm/providers/gemini.rb +4 -0
  92. data/lib/ruby_llm/providers/gpustack.rb +4 -0
  93. data/lib/ruby_llm/providers/mistral.rb +4 -0
  94. data/lib/ruby_llm/providers/ollama.rb +4 -0
  95. data/lib/ruby_llm/providers/openai/capabilities.rb +95 -203
  96. data/lib/ruby_llm/providers/openai/models.rb +2 -4
  97. data/lib/ruby_llm/providers/openai.rb +10 -0
  98. data/lib/ruby_llm/providers/openrouter/images.rb +1 -1
  99. data/lib/ruby_llm/providers/openrouter.rb +4 -0
  100. data/lib/ruby_llm/providers/perplexity/capabilities.rb +34 -99
  101. data/lib/ruby_llm/providers/perplexity/models.rb +12 -14
  102. data/lib/ruby_llm/providers/perplexity.rb +4 -0
  103. data/lib/ruby_llm/providers/vertexai.rb +4 -0
  104. data/lib/ruby_llm/providers/xai.rb +4 -0
  105. data/lib/ruby_llm/version.rb +1 -1
  106. data/lib/tasks/release.rake +1 -1
  107. data/lib/tasks/ruby_llm.rake +6 -5
  108. data/lib/tasks/vcr.rake +1 -1
  109. metadata +49 -11
  110. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt +0 -13
@@ -3,13 +3,35 @@
3
3
  module RubyLLM
4
4
  module Providers
5
5
  class Gemini
6
- # Determines capabilities and pricing for Google Gemini models
6
+ # Provider-level capability checks and narrow registry fallbacks.
7
7
  module Capabilities
8
8
  module_function
9
9
 
10
+ PRICES = {
11
+ flash_2: { input: 0.10, output: 0.40 }, # rubocop:disable Naming/VariableNumber
12
+ flash_lite_2: { input: 0.075, output: 0.30 }, # rubocop:disable Naming/VariableNumber
13
+ flash: { input: 0.075, output: 0.30 },
14
+ flash_8b: { input: 0.0375, output: 0.15 },
15
+ pro: { input: 1.25, output: 5.0 },
16
+ pro_2_5: { input: 0.12, output: 0.50 }, # rubocop:disable Naming/VariableNumber
17
+ gemini_embedding: { input: 0.002, output: 0.004 },
18
+ embedding: { input: 0.00, output: 0.00 },
19
+ imagen: { price: 0.03 },
20
+ aqa: { input: 0.00, output: 0.00 }
21
+ }.freeze
22
+
23
+ def supports_tool_choice?(_model_id)
24
+ true
25
+ end
26
+
27
+ def supports_tool_parallel_control?(_model_id)
28
+ false
29
+ end
30
+
10
31
  def context_window_for(model_id)
11
32
  case model_id
12
- 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
33
+ when /gemini-2\.5-pro-exp-03-25/, /gemini-2\.0-flash/, /gemini-2\.0-flash-lite/, /gemini-1\.5-flash/,
34
+ /gemini-1\.5-flash-8b/
13
35
  1_048_576
14
36
  when /gemini-1\.5-pro/ then 2_097_152
15
37
  when /gemini-embedding-exp/ then 8_192
@@ -23,7 +45,8 @@ module RubyLLM
23
45
  def max_tokens_for(model_id)
24
46
  case model_id
25
47
  when /gemini-2\.5-pro-exp-03-25/ then 64_000
26
- 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
48
+ when /gemini-2\.0-flash/, /gemini-2\.0-flash-lite/, /gemini-1\.5-flash/, /gemini-1\.5-flash-8b/,
49
+ /gemini-1\.5-pro/
27
50
  8_192
28
51
  when /gemini-embedding-exp/ then nil
29
52
  when /text-embedding-004/, /embedding-001/ then 768
@@ -32,18 +55,24 @@ module RubyLLM
32
55
  end
33
56
  end
34
57
 
35
- def input_price_for(model_id)
36
- base_price = PRICES.dig(pricing_family(model_id), :input) || default_input_price
37
- return base_price unless long_context_model?(model_id)
38
-
39
- context_window_for(model_id) > 128_000 ? base_price * 2 : base_price
58
+ def critical_capabilities_for(model_id)
59
+ capabilities = []
60
+ capabilities << 'function_calling' if supports_functions?(model_id)
61
+ capabilities << 'structured_output' if supports_structured_output?(model_id)
62
+ capabilities << 'vision' if supports_vision?(model_id)
63
+ capabilities
40
64
  end
41
65
 
42
- def output_price_for(model_id)
43
- base_price = PRICES.dig(pricing_family(model_id), :output) || default_output_price
44
- return base_price unless long_context_model?(model_id)
45
-
46
- context_window_for(model_id) > 128_000 ? base_price * 2 : base_price
66
+ def pricing_for(model_id)
67
+ prices = PRICES.fetch(pricing_family(model_id), { input: 0.075, output: 0.30 })
68
+ {
69
+ text_tokens: {
70
+ standard: {
71
+ input_per_million: prices[:input] || prices[:price] || 0.075,
72
+ output_per_million: prices[:output] || prices[:price] || 0.30
73
+ }
74
+ }
75
+ }
47
76
  end
48
77
 
49
78
  def supports_vision?(model_id)
@@ -52,25 +81,13 @@ module RubyLLM
52
81
  model_id.match?(/gemini|flash|pro|imagen/)
53
82
  end
54
83
 
55
- def supports_video?(model_id)
56
- model_id.match?(/gemini/)
57
- end
58
-
59
84
  def supports_functions?(model_id)
60
85
  return false if model_id.match?(/text-embedding|embedding-001|aqa|flash-lite|imagen|gemini-2\.0-flash-lite/)
61
86
 
62
87
  model_id.match?(/gemini|pro|flash/)
63
88
  end
64
89
 
65
- def supports_tool_choice?(_model_id)
66
- true
67
- end
68
-
69
- def supports_tool_parallel_control?(_model_id)
70
- false
71
- end
72
-
73
- def supports_json_mode?(model_id)
90
+ def supports_structured_output?(model_id)
74
91
  if model_id.match?(/text-embedding|embedding-001|aqa|imagen|gemini-2\.0-flash-lite|gemini-2\.5-pro-exp-03-25/)
75
92
  return false
76
93
  end
@@ -78,59 +95,6 @@ module RubyLLM
78
95
  model_id.match?(/gemini|pro|flash/)
79
96
  end
80
97
 
81
- def format_display_name(model_id)
82
- model_id
83
- .delete_prefix('models/')
84
- .split('-')
85
- .map(&:capitalize)
86
- .join(' ')
87
- .gsub(/(\d+\.\d+)/, ' \1')
88
- .gsub(/\s+/, ' ')
89
- .gsub('Aqa', 'AQA')
90
- .strip
91
- end
92
-
93
- def supports_caching?(model_id)
94
- if model_id.match?(/flash-lite|gemini-2\.5-pro-exp-03-25|aqa|imagen|text-embedding|embedding-001/)
95
- return false
96
- end
97
-
98
- model_id.match?(/gemini|pro|flash/)
99
- end
100
-
101
- def supports_tuning?(model_id)
102
- model_id.match?(/gemini-1\.5-flash|gemini-1\.5-flash-8b/)
103
- end
104
-
105
- def supports_audio?(model_id)
106
- model_id.match?(/gemini|pro|flash/)
107
- end
108
-
109
- def model_type(model_id)
110
- case model_id
111
- when /text-embedding|embedding|gemini-embedding/ then 'embedding'
112
- when /imagen/ then 'image'
113
- else 'chat'
114
- end
115
- end
116
-
117
- def model_family(model_id)
118
- case model_id
119
- when /gemini-2\.5-pro-exp-03-25/ then 'gemini25_pro_exp'
120
- when /gemini-2\.0-flash-lite/ then 'gemini20_flash_lite'
121
- when /gemini-2\.0-flash/ then 'gemini20_flash'
122
- when /gemini-1\.5-flash-8b/ then 'gemini15_flash_8b'
123
- when /gemini-1\.5-flash/ then 'gemini15_flash'
124
- when /gemini-1\.5-pro/ then 'gemini15_pro'
125
- when /gemini-embedding-exp/ then 'gemini_embedding_exp'
126
- when /text-embedding-004/ then 'embedding4'
127
- when /embedding-001/ then 'embedding1'
128
- when /aqa/ then 'aqa'
129
- when /imagen-3/ then 'imagen3'
130
- else 'other'
131
- end
132
- end
133
-
134
98
  def pricing_family(model_id)
135
99
  case model_id
136
100
  when /gemini-2\.5-pro-exp-03-25/ then :pro_2_5 # rubocop:disable Naming/VariableNumber
@@ -147,142 +111,8 @@ module RubyLLM
147
111
  end
148
112
  end
149
113
 
150
- def long_context_model?(model_id)
151
- model_id.match?(/gemini-1\.5-(?:pro|flash)|gemini-1\.5-flash-8b/)
152
- end
153
-
154
- def context_length(model_id)
155
- context_window_for(model_id)
156
- end
157
-
158
- PRICES = {
159
- flash_2: { # rubocop:disable Naming/VariableNumber
160
- input: 0.10,
161
- output: 0.40,
162
- audio_input: 0.70,
163
- cache: 0.025,
164
- cache_storage: 1.00,
165
- grounding_search: 35.00
166
- },
167
- flash_lite_2: { # rubocop:disable Naming/VariableNumber
168
- input: 0.075,
169
- output: 0.30
170
- },
171
- flash: {
172
- input: 0.075,
173
- output: 0.30,
174
- cache: 0.01875,
175
- cache_storage: 1.00,
176
- grounding_search: 35.00
177
- },
178
- flash_8b: {
179
- input: 0.0375,
180
- output: 0.15,
181
- cache: 0.01,
182
- cache_storage: 0.25,
183
- grounding_search: 35.00
184
- },
185
- pro: {
186
- input: 1.25,
187
- output: 5.0,
188
- cache: 0.3125,
189
- cache_storage: 4.50,
190
- grounding_search: 35.00
191
- },
192
- pro_2_5: { # rubocop:disable Naming/VariableNumber
193
- input: 0.12,
194
- output: 0.50
195
- },
196
- gemini_embedding: {
197
- input: 0.002,
198
- output: 0.004
199
- },
200
- embedding: {
201
- input: 0.00,
202
- output: 0.00
203
- },
204
- imagen: {
205
- price: 0.03
206
- },
207
- aqa: {
208
- input: 0.00,
209
- output: 0.00
210
- }
211
- }.freeze
212
-
213
- def default_input_price
214
- 0.075
215
- end
216
-
217
- def default_output_price
218
- 0.30
219
- end
220
-
221
- def modalities_for(model_id)
222
- modalities = {
223
- input: ['text'],
224
- output: ['text']
225
- }
226
-
227
- if supports_vision?(model_id)
228
- modalities[:input] << 'image'
229
- modalities[:input] << 'pdf'
230
- end
231
-
232
- modalities[:input] << 'video' if supports_video?(model_id)
233
- modalities[:input] << 'audio' if model_id.match?(/audio/)
234
- modalities[:output] << 'embeddings' if model_id.match?(/embedding|gemini-embedding/)
235
- modalities[:output] = ['image'] if model_id.match?(/imagen/)
236
-
237
- modalities
238
- end
239
-
240
- def capabilities_for(model_id)
241
- capabilities = ['streaming']
242
-
243
- capabilities << 'function_calling' if supports_functions?(model_id)
244
- capabilities << 'structured_output' if supports_json_mode?(model_id)
245
- capabilities << 'batch' if model_id.match?(/embedding|flash/)
246
- capabilities << 'caching' if supports_caching?(model_id)
247
- capabilities << 'fine_tuning' if supports_tuning?(model_id)
248
- capabilities
249
- end
250
-
251
- def pricing_for(model_id)
252
- family = pricing_family(model_id)
253
- prices = PRICES.fetch(family, { input: default_input_price, output: default_output_price })
254
-
255
- standard_pricing = {
256
- input_per_million: prices[:input],
257
- output_per_million: prices[:output]
258
- }
259
-
260
- standard_pricing[:cached_input_per_million] = prices[:input_hit] if prices[:input_hit]
261
-
262
- batch_pricing = {
263
- input_per_million: (standard_pricing[:input_per_million] || 0) * 0.5,
264
- output_per_million: (standard_pricing[:output_per_million] || 0) * 0.5
265
- }
266
-
267
- if standard_pricing[:cached_input_per_million]
268
- batch_pricing[:cached_input_per_million] = standard_pricing[:cached_input_per_million] * 0.5
269
- end
270
-
271
- pricing = {
272
- text_tokens: {
273
- standard: standard_pricing,
274
- batch: batch_pricing
275
- }
276
- }
277
-
278
- if model_id.match?(/embedding|gemini-embedding/)
279
- pricing[:embeddings] = {
280
- standard: { input_per_million: prices[:price] || 0.002 }
281
- }
282
- end
283
-
284
- pricing
285
- end
114
+ module_function :context_window_for, :max_tokens_for, :critical_capabilities_for, :pricing_for,
115
+ :supports_vision?, :supports_functions?, :supports_structured_output?, :pricing_family
286
116
  end
287
117
  end
288
118
  end
@@ -17,14 +17,12 @@ module RubyLLM
17
17
 
18
18
  Model::Info.new(
19
19
  id: model_id,
20
- name: model_data['displayName'],
20
+ name: model_data['displayName'] || model_id,
21
21
  provider: slug,
22
- family: capabilities.model_family(model_id),
23
22
  created_at: nil,
24
23
  context_window: model_data['inputTokenLimit'] || capabilities.context_window_for(model_id),
25
24
  max_output_tokens: model_data['outputTokenLimit'] || capabilities.max_tokens_for(model_id),
26
- modalities: capabilities.modalities_for(model_id),
27
- capabilities: capabilities.capabilities_for(model_id),
25
+ capabilities: capabilities.critical_capabilities_for(model_id),
28
26
  pricing: capabilities.pricing_for(model_id),
29
27
  metadata: {
30
28
  version: model_data['version'],
@@ -28,6 +28,10 @@ module RubyLLM
28
28
  Gemini::Capabilities
29
29
  end
30
30
 
31
+ def configuration_options
32
+ %i[gemini_api_key gemini_api_base]
33
+ end
34
+
31
35
  def configuration_requirements
32
36
  %i[gemini_api_key]
33
37
  end
@@ -21,6 +21,10 @@ module RubyLLM
21
21
  end
22
22
 
23
23
  class << self
24
+ def configuration_options
25
+ %i[gpustack_api_base gpustack_api_key]
26
+ end
27
+
24
28
  def local?
25
29
  true
26
30
  end
@@ -23,6 +23,10 @@ module RubyLLM
23
23
  Mistral::Capabilities
24
24
  end
25
25
 
26
+ def configuration_options
27
+ %i[mistral_api_key]
28
+ end
29
+
26
30
  def configuration_requirements
27
31
  %i[mistral_api_key]
28
32
  end
@@ -19,6 +19,10 @@ module RubyLLM
19
19
  end
20
20
 
21
21
  class << self
22
+ def configuration_options
23
+ %i[ollama_api_base ollama_api_key]
24
+ end
25
+
22
26
  def configuration_requirements
23
27
  %i[ollama_api_base]
24
28
  end