ruby_llm_swarm 1.9.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 (154) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +175 -0
  4. data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +187 -0
  5. data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +39 -0
  6. data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +24 -0
  7. data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +14 -0
  8. data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +12 -0
  9. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +16 -0
  10. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +29 -0
  11. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +16 -0
  12. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt +11 -0
  13. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +23 -0
  14. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_content.html.erb.tt +1 -0
  15. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +21 -0
  16. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt +13 -0
  17. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool_calls.html.erb.tt +7 -0
  18. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +9 -0
  19. data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +16 -0
  20. data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +28 -0
  21. data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +18 -0
  22. data/lib/generators/ruby_llm/generator_helpers.rb +194 -0
  23. data/lib/generators/ruby_llm/install/install_generator.rb +106 -0
  24. data/lib/generators/ruby_llm/install/templates/add_references_to_chats_tool_calls_and_messages_migration.rb.tt +9 -0
  25. data/lib/generators/ruby_llm/install/templates/chat_model.rb.tt +3 -0
  26. data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +7 -0
  27. data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +16 -0
  28. data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +45 -0
  29. data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +20 -0
  30. data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +12 -0
  31. data/lib/generators/ruby_llm/install/templates/message_model.rb.tt +4 -0
  32. data/lib/generators/ruby_llm/install/templates/model_model.rb.tt +3 -0
  33. data/lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt +3 -0
  34. data/lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt +145 -0
  35. data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +124 -0
  36. data/lib/generators/ruby_llm/upgrade_to_v1_9/templates/add_v1_9_message_columns.rb.tt +15 -0
  37. data/lib/generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator.rb +49 -0
  38. data/lib/ruby_llm/active_record/acts_as.rb +174 -0
  39. data/lib/ruby_llm/active_record/acts_as_legacy.rb +384 -0
  40. data/lib/ruby_llm/active_record/chat_methods.rb +350 -0
  41. data/lib/ruby_llm/active_record/message_methods.rb +81 -0
  42. data/lib/ruby_llm/active_record/model_methods.rb +84 -0
  43. data/lib/ruby_llm/aliases.json +295 -0
  44. data/lib/ruby_llm/aliases.rb +38 -0
  45. data/lib/ruby_llm/attachment.rb +220 -0
  46. data/lib/ruby_llm/chat.rb +816 -0
  47. data/lib/ruby_llm/chunk.rb +6 -0
  48. data/lib/ruby_llm/configuration.rb +78 -0
  49. data/lib/ruby_llm/connection.rb +126 -0
  50. data/lib/ruby_llm/content.rb +73 -0
  51. data/lib/ruby_llm/context.rb +29 -0
  52. data/lib/ruby_llm/embedding.rb +29 -0
  53. data/lib/ruby_llm/error.rb +84 -0
  54. data/lib/ruby_llm/image.rb +49 -0
  55. data/lib/ruby_llm/message.rb +86 -0
  56. data/lib/ruby_llm/mime_type.rb +71 -0
  57. data/lib/ruby_llm/model/info.rb +111 -0
  58. data/lib/ruby_llm/model/modalities.rb +22 -0
  59. data/lib/ruby_llm/model/pricing.rb +48 -0
  60. data/lib/ruby_llm/model/pricing_category.rb +46 -0
  61. data/lib/ruby_llm/model/pricing_tier.rb +33 -0
  62. data/lib/ruby_llm/model.rb +7 -0
  63. data/lib/ruby_llm/models.json +33198 -0
  64. data/lib/ruby_llm/models.rb +231 -0
  65. data/lib/ruby_llm/models_schema.json +168 -0
  66. data/lib/ruby_llm/moderation.rb +56 -0
  67. data/lib/ruby_llm/provider.rb +243 -0
  68. data/lib/ruby_llm/providers/anthropic/capabilities.rb +134 -0
  69. data/lib/ruby_llm/providers/anthropic/chat.rb +125 -0
  70. data/lib/ruby_llm/providers/anthropic/content.rb +44 -0
  71. data/lib/ruby_llm/providers/anthropic/embeddings.rb +20 -0
  72. data/lib/ruby_llm/providers/anthropic/media.rb +92 -0
  73. data/lib/ruby_llm/providers/anthropic/models.rb +63 -0
  74. data/lib/ruby_llm/providers/anthropic/streaming.rb +45 -0
  75. data/lib/ruby_llm/providers/anthropic/tools.rb +109 -0
  76. data/lib/ruby_llm/providers/anthropic.rb +36 -0
  77. data/lib/ruby_llm/providers/bedrock/capabilities.rb +167 -0
  78. data/lib/ruby_llm/providers/bedrock/chat.rb +63 -0
  79. data/lib/ruby_llm/providers/bedrock/media.rb +61 -0
  80. data/lib/ruby_llm/providers/bedrock/models.rb +98 -0
  81. data/lib/ruby_llm/providers/bedrock/signing.rb +831 -0
  82. data/lib/ruby_llm/providers/bedrock/streaming/base.rb +51 -0
  83. data/lib/ruby_llm/providers/bedrock/streaming/content_extraction.rb +71 -0
  84. data/lib/ruby_llm/providers/bedrock/streaming/message_processing.rb +67 -0
  85. data/lib/ruby_llm/providers/bedrock/streaming/payload_processing.rb +80 -0
  86. data/lib/ruby_llm/providers/bedrock/streaming/prelude_handling.rb +78 -0
  87. data/lib/ruby_llm/providers/bedrock/streaming.rb +18 -0
  88. data/lib/ruby_llm/providers/bedrock.rb +82 -0
  89. data/lib/ruby_llm/providers/deepseek/capabilities.rb +130 -0
  90. data/lib/ruby_llm/providers/deepseek/chat.rb +16 -0
  91. data/lib/ruby_llm/providers/deepseek.rb +30 -0
  92. data/lib/ruby_llm/providers/gemini/capabilities.rb +281 -0
  93. data/lib/ruby_llm/providers/gemini/chat.rb +454 -0
  94. data/lib/ruby_llm/providers/gemini/embeddings.rb +37 -0
  95. data/lib/ruby_llm/providers/gemini/images.rb +47 -0
  96. data/lib/ruby_llm/providers/gemini/media.rb +112 -0
  97. data/lib/ruby_llm/providers/gemini/models.rb +40 -0
  98. data/lib/ruby_llm/providers/gemini/streaming.rb +61 -0
  99. data/lib/ruby_llm/providers/gemini/tools.rb +198 -0
  100. data/lib/ruby_llm/providers/gemini/transcription.rb +116 -0
  101. data/lib/ruby_llm/providers/gemini.rb +37 -0
  102. data/lib/ruby_llm/providers/gpustack/chat.rb +27 -0
  103. data/lib/ruby_llm/providers/gpustack/media.rb +46 -0
  104. data/lib/ruby_llm/providers/gpustack/models.rb +90 -0
  105. data/lib/ruby_llm/providers/gpustack.rb +34 -0
  106. data/lib/ruby_llm/providers/mistral/capabilities.rb +155 -0
  107. data/lib/ruby_llm/providers/mistral/chat.rb +24 -0
  108. data/lib/ruby_llm/providers/mistral/embeddings.rb +33 -0
  109. data/lib/ruby_llm/providers/mistral/models.rb +48 -0
  110. data/lib/ruby_llm/providers/mistral.rb +32 -0
  111. data/lib/ruby_llm/providers/ollama/chat.rb +27 -0
  112. data/lib/ruby_llm/providers/ollama/media.rb +46 -0
  113. data/lib/ruby_llm/providers/ollama/models.rb +36 -0
  114. data/lib/ruby_llm/providers/ollama.rb +30 -0
  115. data/lib/ruby_llm/providers/openai/capabilities.rb +299 -0
  116. data/lib/ruby_llm/providers/openai/chat.rb +88 -0
  117. data/lib/ruby_llm/providers/openai/embeddings.rb +33 -0
  118. data/lib/ruby_llm/providers/openai/images.rb +38 -0
  119. data/lib/ruby_llm/providers/openai/media.rb +81 -0
  120. data/lib/ruby_llm/providers/openai/models.rb +39 -0
  121. data/lib/ruby_llm/providers/openai/moderation.rb +34 -0
  122. data/lib/ruby_llm/providers/openai/streaming.rb +46 -0
  123. data/lib/ruby_llm/providers/openai/tools.rb +98 -0
  124. data/lib/ruby_llm/providers/openai/transcription.rb +70 -0
  125. data/lib/ruby_llm/providers/openai.rb +44 -0
  126. data/lib/ruby_llm/providers/openai_responses.rb +395 -0
  127. data/lib/ruby_llm/providers/openrouter/models.rb +73 -0
  128. data/lib/ruby_llm/providers/openrouter.rb +26 -0
  129. data/lib/ruby_llm/providers/perplexity/capabilities.rb +137 -0
  130. data/lib/ruby_llm/providers/perplexity/chat.rb +16 -0
  131. data/lib/ruby_llm/providers/perplexity/models.rb +42 -0
  132. data/lib/ruby_llm/providers/perplexity.rb +48 -0
  133. data/lib/ruby_llm/providers/vertexai/chat.rb +14 -0
  134. data/lib/ruby_llm/providers/vertexai/embeddings.rb +32 -0
  135. data/lib/ruby_llm/providers/vertexai/models.rb +130 -0
  136. data/lib/ruby_llm/providers/vertexai/streaming.rb +14 -0
  137. data/lib/ruby_llm/providers/vertexai/transcription.rb +16 -0
  138. data/lib/ruby_llm/providers/vertexai.rb +55 -0
  139. data/lib/ruby_llm/railtie.rb +35 -0
  140. data/lib/ruby_llm/responses_session.rb +77 -0
  141. data/lib/ruby_llm/stream_accumulator.rb +101 -0
  142. data/lib/ruby_llm/streaming.rb +153 -0
  143. data/lib/ruby_llm/tool.rb +209 -0
  144. data/lib/ruby_llm/tool_call.rb +22 -0
  145. data/lib/ruby_llm/tool_executors.rb +125 -0
  146. data/lib/ruby_llm/transcription.rb +35 -0
  147. data/lib/ruby_llm/utils.rb +91 -0
  148. data/lib/ruby_llm/version.rb +5 -0
  149. data/lib/ruby_llm.rb +140 -0
  150. data/lib/tasks/models.rake +525 -0
  151. data/lib/tasks/release.rake +67 -0
  152. data/lib/tasks/ruby_llm.rake +15 -0
  153. data/lib/tasks/vcr.rake +92 -0
  154. metadata +346 -0
@@ -0,0 +1,281 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyLLM
4
+ module Providers
5
+ class Gemini
6
+ # Determines capabilities and pricing for Google Gemini models
7
+ module Capabilities
8
+ module_function
9
+
10
+ def context_window_for(model_id)
11
+ 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
13
+ 1_048_576
14
+ when /gemini-1\.5-pro/ then 2_097_152
15
+ when /gemini-embedding-exp/ then 8_192
16
+ when /text-embedding-004/, /embedding-001/ then 2_048
17
+ when /aqa/ then 7_168
18
+ when /imagen-3/ then nil
19
+ else 32_768
20
+ end
21
+ end
22
+
23
+ def max_tokens_for(model_id)
24
+ case model_id
25
+ 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
27
+ 8_192
28
+ when /gemini-embedding-exp/ then nil
29
+ when /text-embedding-004/, /embedding-001/ then 768
30
+ when /imagen-3/ then 4
31
+ else 4_096
32
+ end
33
+ end
34
+
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
40
+ end
41
+
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
47
+ end
48
+
49
+ def supports_vision?(model_id)
50
+ return false if model_id.match?(/text-embedding|embedding-001|aqa/)
51
+
52
+ model_id.match?(/gemini|flash|pro|imagen/)
53
+ end
54
+
55
+ def supports_video?(model_id)
56
+ model_id.match?(/gemini/)
57
+ end
58
+
59
+ def supports_functions?(model_id)
60
+ return false if model_id.match?(/text-embedding|embedding-001|aqa|flash-lite|imagen|gemini-2\.0-flash-lite/)
61
+
62
+ model_id.match?(/gemini|pro|flash/)
63
+ end
64
+
65
+ def supports_json_mode?(model_id)
66
+ if model_id.match?(/text-embedding|embedding-001|aqa|imagen|gemini-2\.0-flash-lite|gemini-2\.5-pro-exp-03-25/)
67
+ return false
68
+ end
69
+
70
+ model_id.match?(/gemini|pro|flash/)
71
+ end
72
+
73
+ def format_display_name(model_id)
74
+ model_id
75
+ .delete_prefix('models/')
76
+ .split('-')
77
+ .map(&:capitalize)
78
+ .join(' ')
79
+ .gsub(/(\d+\.\d+)/, ' \1')
80
+ .gsub(/\s+/, ' ')
81
+ .gsub('Aqa', 'AQA')
82
+ .strip
83
+ end
84
+
85
+ def supports_caching?(model_id)
86
+ if model_id.match?(/flash-lite|gemini-2\.5-pro-exp-03-25|aqa|imagen|text-embedding|embedding-001/)
87
+ return false
88
+ end
89
+
90
+ model_id.match?(/gemini|pro|flash/)
91
+ end
92
+
93
+ def supports_tuning?(model_id)
94
+ model_id.match?(/gemini-1\.5-flash|gemini-1\.5-flash-8b/)
95
+ end
96
+
97
+ def supports_audio?(model_id)
98
+ model_id.match?(/gemini|pro|flash/)
99
+ end
100
+
101
+ def model_type(model_id)
102
+ case model_id
103
+ when /text-embedding|embedding|gemini-embedding/ then 'embedding'
104
+ when /imagen/ then 'image'
105
+ else 'chat'
106
+ end
107
+ end
108
+
109
+ def model_family(model_id)
110
+ case model_id
111
+ when /gemini-2\.5-pro-exp-03-25/ then 'gemini25_pro_exp'
112
+ when /gemini-2\.0-flash-lite/ then 'gemini20_flash_lite'
113
+ when /gemini-2\.0-flash/ then 'gemini20_flash'
114
+ when /gemini-1\.5-flash-8b/ then 'gemini15_flash_8b'
115
+ when /gemini-1\.5-flash/ then 'gemini15_flash'
116
+ when /gemini-1\.5-pro/ then 'gemini15_pro'
117
+ when /gemini-embedding-exp/ then 'gemini_embedding_exp'
118
+ when /text-embedding-004/ then 'embedding4'
119
+ when /embedding-001/ then 'embedding1'
120
+ when /aqa/ then 'aqa'
121
+ when /imagen-3/ then 'imagen3'
122
+ else 'other'
123
+ end
124
+ end
125
+
126
+ def pricing_family(model_id)
127
+ case model_id
128
+ when /gemini-2\.5-pro-exp-03-25/ then :pro_2_5 # rubocop:disable Naming/VariableNumber
129
+ when /gemini-2\.0-flash-lite/ then :flash_lite_2 # rubocop:disable Naming/VariableNumber
130
+ when /gemini-2\.0-flash/ then :flash_2 # rubocop:disable Naming/VariableNumber
131
+ when /gemini-1\.5-flash-8b/ then :flash_8b
132
+ when /gemini-1\.5-flash/ then :flash
133
+ when /gemini-1\.5-pro/ then :pro
134
+ when /gemini-embedding-exp/ then :gemini_embedding
135
+ when /text-embedding|embedding/ then :embedding
136
+ when /imagen/ then :imagen
137
+ when /aqa/ then :aqa
138
+ else :base
139
+ end
140
+ end
141
+
142
+ def long_context_model?(model_id)
143
+ model_id.match?(/gemini-1\.5-(?:pro|flash)|gemini-1\.5-flash-8b/)
144
+ end
145
+
146
+ def context_length(model_id)
147
+ context_window_for(model_id)
148
+ end
149
+
150
+ PRICES = {
151
+ flash_2: { # rubocop:disable Naming/VariableNumber
152
+ input: 0.10,
153
+ output: 0.40,
154
+ audio_input: 0.70,
155
+ cache: 0.025,
156
+ cache_storage: 1.00,
157
+ grounding_search: 35.00
158
+ },
159
+ flash_lite_2: { # rubocop:disable Naming/VariableNumber
160
+ input: 0.075,
161
+ output: 0.30
162
+ },
163
+ flash: {
164
+ input: 0.075,
165
+ output: 0.30,
166
+ cache: 0.01875,
167
+ cache_storage: 1.00,
168
+ grounding_search: 35.00
169
+ },
170
+ flash_8b: {
171
+ input: 0.0375,
172
+ output: 0.15,
173
+ cache: 0.01,
174
+ cache_storage: 0.25,
175
+ grounding_search: 35.00
176
+ },
177
+ pro: {
178
+ input: 1.25,
179
+ output: 5.0,
180
+ cache: 0.3125,
181
+ cache_storage: 4.50,
182
+ grounding_search: 35.00
183
+ },
184
+ pro_2_5: { # rubocop:disable Naming/VariableNumber
185
+ input: 0.12,
186
+ output: 0.50
187
+ },
188
+ gemini_embedding: {
189
+ input: 0.002,
190
+ output: 0.004
191
+ },
192
+ embedding: {
193
+ input: 0.00,
194
+ output: 0.00
195
+ },
196
+ imagen: {
197
+ price: 0.03
198
+ },
199
+ aqa: {
200
+ input: 0.00,
201
+ output: 0.00
202
+ }
203
+ }.freeze
204
+
205
+ def default_input_price
206
+ 0.075
207
+ end
208
+
209
+ def default_output_price
210
+ 0.30
211
+ end
212
+
213
+ def modalities_for(model_id)
214
+ modalities = {
215
+ input: ['text'],
216
+ output: ['text']
217
+ }
218
+
219
+ if supports_vision?(model_id)
220
+ modalities[:input] << 'image'
221
+ modalities[:input] << 'pdf'
222
+ end
223
+
224
+ modalities[:input] << 'video' if supports_video?(model_id)
225
+ modalities[:input] << 'audio' if model_id.match?(/audio/)
226
+ modalities[:output] << 'embeddings' if model_id.match?(/embedding|gemini-embedding/)
227
+ modalities[:output] = ['image'] if model_id.match?(/imagen/)
228
+
229
+ modalities
230
+ end
231
+
232
+ def capabilities_for(model_id)
233
+ capabilities = ['streaming']
234
+
235
+ capabilities << 'function_calling' if supports_functions?(model_id)
236
+ capabilities << 'structured_output' if supports_json_mode?(model_id)
237
+ capabilities << 'batch' if model_id.match?(/embedding|flash/)
238
+ capabilities << 'caching' if supports_caching?(model_id)
239
+ capabilities << 'fine_tuning' if supports_tuning?(model_id)
240
+ capabilities
241
+ end
242
+
243
+ def pricing_for(model_id)
244
+ family = pricing_family(model_id)
245
+ prices = PRICES.fetch(family, { input: default_input_price, output: default_output_price })
246
+
247
+ standard_pricing = {
248
+ input_per_million: prices[:input],
249
+ output_per_million: prices[:output]
250
+ }
251
+
252
+ standard_pricing[:cached_input_per_million] = prices[:input_hit] if prices[:input_hit]
253
+
254
+ batch_pricing = {
255
+ input_per_million: (standard_pricing[:input_per_million] || 0) * 0.5,
256
+ output_per_million: (standard_pricing[:output_per_million] || 0) * 0.5
257
+ }
258
+
259
+ if standard_pricing[:cached_input_per_million]
260
+ batch_pricing[:cached_input_per_million] = standard_pricing[:cached_input_per_million] * 0.5
261
+ end
262
+
263
+ pricing = {
264
+ text_tokens: {
265
+ standard: standard_pricing,
266
+ batch: batch_pricing
267
+ }
268
+ }
269
+
270
+ if model_id.match?(/embedding|gemini-embedding/)
271
+ pricing[:embeddings] = {
272
+ standard: { input_per_million: prices[:price] || 0.002 }
273
+ }
274
+ end
275
+
276
+ pricing
277
+ end
278
+ end
279
+ end
280
+ end
281
+ end