ruby_llm 1.6.3 → 1.7.0
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.
- checksums.yaml +4 -4
- data/README.md +6 -3
- data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +115 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +39 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +24 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +14 -0
- data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +12 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +29 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt +11 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +23 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +21 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt +10 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +9 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +30 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +18 -0
- data/lib/generators/ruby_llm/install/templates/chat_model.rb.tt +2 -2
- data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +4 -4
- data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +8 -7
- data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +43 -0
- data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +6 -5
- data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +10 -4
- data/lib/generators/ruby_llm/install/templates/message_model.rb.tt +4 -3
- data/lib/generators/ruby_llm/install/templates/model_model.rb.tt +3 -0
- data/lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt +2 -2
- data/lib/generators/ruby_llm/install_generator.rb +129 -33
- data/lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt +137 -0
- data/lib/generators/ruby_llm/upgrade_to_v1_7_generator.rb +160 -0
- data/lib/ruby_llm/active_record/acts_as.rb +112 -319
- data/lib/ruby_llm/active_record/acts_as_legacy.rb +398 -0
- data/lib/ruby_llm/active_record/chat_methods.rb +336 -0
- data/lib/ruby_llm/active_record/message_methods.rb +72 -0
- data/lib/ruby_llm/active_record/model_methods.rb +84 -0
- data/lib/ruby_llm/aliases.json +58 -13
- data/lib/ruby_llm/attachment.rb +20 -0
- data/lib/ruby_llm/chat.rb +8 -7
- data/lib/ruby_llm/configuration.rb +9 -0
- data/lib/ruby_llm/connection.rb +4 -4
- data/lib/ruby_llm/model/info.rb +12 -0
- data/lib/ruby_llm/models.json +3579 -2029
- data/lib/ruby_llm/models.rb +51 -22
- data/lib/ruby_llm/provider.rb +3 -3
- data/lib/ruby_llm/providers/anthropic/chat.rb +2 -2
- data/lib/ruby_llm/providers/anthropic/media.rb +1 -1
- data/lib/ruby_llm/providers/anthropic/tools.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/chat.rb +2 -2
- data/lib/ruby_llm/providers/bedrock/models.rb +19 -1
- data/lib/ruby_llm/providers/gemini/chat.rb +53 -25
- data/lib/ruby_llm/providers/gemini/media.rb +1 -1
- data/lib/ruby_llm/providers/gpustack/chat.rb +11 -0
- data/lib/ruby_llm/providers/gpustack/media.rb +45 -0
- data/lib/ruby_llm/providers/gpustack/models.rb +44 -8
- data/lib/ruby_llm/providers/gpustack.rb +1 -0
- data/lib/ruby_llm/providers/ollama/media.rb +2 -6
- data/lib/ruby_llm/providers/ollama/models.rb +36 -0
- data/lib/ruby_llm/providers/ollama.rb +1 -0
- data/lib/ruby_llm/providers/openai/chat.rb +1 -1
- data/lib/ruby_llm/providers/openai/media.rb +4 -4
- data/lib/ruby_llm/providers/openai/tools.rb +11 -6
- data/lib/ruby_llm/providers/openai.rb +2 -2
- data/lib/ruby_llm/providers/vertexai/chat.rb +14 -0
- data/lib/ruby_llm/providers/vertexai/embeddings.rb +32 -0
- data/lib/ruby_llm/providers/vertexai/models.rb +130 -0
- data/lib/ruby_llm/providers/vertexai/streaming.rb +14 -0
- data/lib/ruby_llm/providers/vertexai.rb +55 -0
- data/lib/ruby_llm/railtie.rb +20 -3
- data/lib/ruby_llm/streaming.rb +1 -1
- data/lib/ruby_llm/utils.rb +5 -9
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +4 -3
- data/lib/tasks/models.rake +525 -0
- data/lib/tasks/release.rake +37 -2
- data/lib/tasks/ruby_llm.rake +15 -0
- data/lib/tasks/vcr.rake +2 -2
- metadata +37 -5
- data/lib/generators/ruby_llm/install/templates/INSTALL_INFO.md.tt +0 -108
- data/lib/tasks/aliases.rake +0 -205
- data/lib/tasks/models_docs.rake +0 -214
- data/lib/tasks/models_update.rake +0 -108
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_llm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carmine Paolino
|
@@ -136,17 +136,40 @@ extra_rdoc_files: []
|
|
136
136
|
files:
|
137
137
|
- LICENSE
|
138
138
|
- README.md
|
139
|
-
- lib/generators/ruby_llm/
|
139
|
+
- lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb
|
140
|
+
- lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt
|
141
|
+
- lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt
|
142
|
+
- lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt
|
143
|
+
- lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt
|
144
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt
|
145
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt
|
146
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt
|
147
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt
|
148
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt
|
149
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt
|
150
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt
|
151
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt
|
152
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt
|
153
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt
|
154
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt
|
140
155
|
- lib/generators/ruby_llm/install/templates/chat_model.rb.tt
|
141
156
|
- lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt
|
142
157
|
- lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt
|
158
|
+
- lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt
|
143
159
|
- lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt
|
144
160
|
- lib/generators/ruby_llm/install/templates/initializer.rb.tt
|
145
161
|
- lib/generators/ruby_llm/install/templates/message_model.rb.tt
|
162
|
+
- lib/generators/ruby_llm/install/templates/model_model.rb.tt
|
146
163
|
- lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt
|
147
164
|
- lib/generators/ruby_llm/install_generator.rb
|
165
|
+
- lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt
|
166
|
+
- lib/generators/ruby_llm/upgrade_to_v1_7_generator.rb
|
148
167
|
- lib/ruby_llm.rb
|
149
168
|
- lib/ruby_llm/active_record/acts_as.rb
|
169
|
+
- lib/ruby_llm/active_record/acts_as_legacy.rb
|
170
|
+
- lib/ruby_llm/active_record/chat_methods.rb
|
171
|
+
- lib/ruby_llm/active_record/message_methods.rb
|
172
|
+
- lib/ruby_llm/active_record/model_methods.rb
|
150
173
|
- lib/ruby_llm/aliases.json
|
151
174
|
- lib/ruby_llm/aliases.rb
|
152
175
|
- lib/ruby_llm/attachment.rb
|
@@ -205,6 +228,7 @@ files:
|
|
205
228
|
- lib/ruby_llm/providers/gemini/tools.rb
|
206
229
|
- lib/ruby_llm/providers/gpustack.rb
|
207
230
|
- lib/ruby_llm/providers/gpustack/chat.rb
|
231
|
+
- lib/ruby_llm/providers/gpustack/media.rb
|
208
232
|
- lib/ruby_llm/providers/gpustack/models.rb
|
209
233
|
- lib/ruby_llm/providers/mistral.rb
|
210
234
|
- lib/ruby_llm/providers/mistral/capabilities.rb
|
@@ -214,6 +238,7 @@ files:
|
|
214
238
|
- lib/ruby_llm/providers/ollama.rb
|
215
239
|
- lib/ruby_llm/providers/ollama/chat.rb
|
216
240
|
- lib/ruby_llm/providers/ollama/media.rb
|
241
|
+
- lib/ruby_llm/providers/ollama/models.rb
|
217
242
|
- lib/ruby_llm/providers/openai.rb
|
218
243
|
- lib/ruby_llm/providers/openai/capabilities.rb
|
219
244
|
- lib/ruby_llm/providers/openai/chat.rb
|
@@ -229,6 +254,11 @@ files:
|
|
229
254
|
- lib/ruby_llm/providers/perplexity/capabilities.rb
|
230
255
|
- lib/ruby_llm/providers/perplexity/chat.rb
|
231
256
|
- lib/ruby_llm/providers/perplexity/models.rb
|
257
|
+
- lib/ruby_llm/providers/vertexai.rb
|
258
|
+
- lib/ruby_llm/providers/vertexai/chat.rb
|
259
|
+
- lib/ruby_llm/providers/vertexai/embeddings.rb
|
260
|
+
- lib/ruby_llm/providers/vertexai/models.rb
|
261
|
+
- lib/ruby_llm/providers/vertexai/streaming.rb
|
232
262
|
- lib/ruby_llm/railtie.rb
|
233
263
|
- lib/ruby_llm/stream_accumulator.rb
|
234
264
|
- lib/ruby_llm/streaming.rb
|
@@ -236,10 +266,9 @@ files:
|
|
236
266
|
- lib/ruby_llm/tool_call.rb
|
237
267
|
- lib/ruby_llm/utils.rb
|
238
268
|
- lib/ruby_llm/version.rb
|
239
|
-
- lib/tasks/
|
240
|
-
- lib/tasks/models_docs.rake
|
241
|
-
- lib/tasks/models_update.rake
|
269
|
+
- lib/tasks/models.rake
|
242
270
|
- lib/tasks/release.rake
|
271
|
+
- lib/tasks/ruby_llm.rake
|
243
272
|
- lib/tasks/vcr.rake
|
244
273
|
homepage: https://rubyllm.com
|
245
274
|
licenses:
|
@@ -251,6 +280,9 @@ metadata:
|
|
251
280
|
documentation_uri: https://rubyllm.com
|
252
281
|
bug_tracker_uri: https://github.com/crmne/ruby_llm/issues
|
253
282
|
rubygems_mfa_required: 'true'
|
283
|
+
post_install_message: |
|
284
|
+
Upgrading from RubyLLM <= 1.6.x? Check the upgrade guide for new features and migration instructions
|
285
|
+
--> https://rubyllm.com/upgrading-to-1-7/
|
254
286
|
rdoc_options: []
|
255
287
|
require_paths:
|
256
288
|
- lib
|
@@ -1,108 +0,0 @@
|
|
1
|
-
# RubyLLM Rails Setup Complete!
|
2
|
-
|
3
|
-
Thanks for installing RubyLLM in your Rails application. Here's what was created:
|
4
|
-
|
5
|
-
## Models
|
6
|
-
|
7
|
-
- `<%= options[:chat_model_name] %>` - Stores chat sessions and their associated model ID
|
8
|
-
- `<%= options[:message_model_name] %>` - Stores individual messages in a chat
|
9
|
-
- `<%= options[:tool_call_model_name] %>` - Stores tool calls made by language models
|
10
|
-
|
11
|
-
**Note:** Do not add `validates :content, presence: true` to your Message model - RubyLLM creates empty assistant messages before API calls for streaming support.
|
12
|
-
|
13
|
-
## Configuration Options
|
14
|
-
|
15
|
-
The generator supports the following options to customize model names:
|
16
|
-
|
17
|
-
```bash
|
18
|
-
rails generate ruby_llm:install \
|
19
|
-
--chat-model-name=Conversation \
|
20
|
-
--message-model-name=ChatMessage \
|
21
|
-
--tool-call-model-name=FunctionCall
|
22
|
-
```
|
23
|
-
|
24
|
-
This is useful when you need to avoid namespace collisions with existing models in your application. Table names will be automatically derived from the model names following Rails conventions.
|
25
|
-
|
26
|
-
## Next Steps
|
27
|
-
|
28
|
-
1. **Run migrations:**
|
29
|
-
```bash
|
30
|
-
rails db:migrate
|
31
|
-
```
|
32
|
-
|
33
|
-
**Database Note:** The migrations use `jsonb` for PostgreSQL and `json` for MySQL/SQLite automatically.
|
34
|
-
|
35
|
-
2. **Set your API keys** in `config/initializers/ruby_llm.rb` or using environment variables:
|
36
|
-
```ruby
|
37
|
-
# config/initializers/ruby_llm.rb
|
38
|
-
RubyLLM.configure do |config|
|
39
|
-
config.openai_api_key = ENV['OPENAI_API_KEY']
|
40
|
-
config.anthropic_api_key = ENV['ANTHROPIC_API_KEY']
|
41
|
-
config.gemini_api_key = ENV['GEMINI_API_KEY']
|
42
|
-
# ... add other providers as needed
|
43
|
-
end
|
44
|
-
```
|
45
|
-
|
46
|
-
3. **Start using RubyLLM in your code:**
|
47
|
-
```ruby
|
48
|
-
# Basic usage
|
49
|
-
chat = <%= options[:chat_model_name] %>.create!(model_id: 'gpt-4.1-nano')
|
50
|
-
response = chat.ask("What is Ruby on Rails?")
|
51
|
-
|
52
|
-
# With file attachments (requires ActiveStorage setup)
|
53
|
-
chat.ask("What's in this file?", with: "report.pdf")
|
54
|
-
chat.ask("Analyze these files", with: ["image.jpg", "data.csv", "notes.txt"])
|
55
|
-
```
|
56
|
-
|
57
|
-
4. **For streaming responses** with Hotwire/Turbo:
|
58
|
-
```ruby
|
59
|
-
# app/models/<%= options[:message_model_name].underscore %>.rb
|
60
|
-
class <%= options[:message_model_name] %> < ApplicationRecord
|
61
|
-
acts_as_message
|
62
|
-
|
63
|
-
# Helper to broadcast chunks during streaming
|
64
|
-
def broadcast_append_chunk(chunk_content)
|
65
|
-
broadcast_append_to [ chat, "messages" ],
|
66
|
-
target: dom_id(self, "content"),
|
67
|
-
html: chunk_content
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
# app/jobs/chat_stream_job.rb
|
72
|
-
class ChatStreamJob < ApplicationJob
|
73
|
-
def perform(chat_id, user_content)
|
74
|
-
chat = <%= options[:chat_model_name] %>.find(chat_id)
|
75
|
-
chat.ask(user_content) do |chunk|
|
76
|
-
assistant_message = chat.messages.last
|
77
|
-
if chunk.content && assistant_message
|
78
|
-
assistant_message.broadcast_append_chunk(chunk.content)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
# In your controller
|
85
|
-
ChatStreamJob.perform_later(@chat.id, params[:content])
|
86
|
-
```
|
87
|
-
|
88
|
-
## Optional: ActiveStorage for Attachments
|
89
|
-
|
90
|
-
If you want to use file attachments (PDFs, images, etc.), set up ActiveStorage:
|
91
|
-
|
92
|
-
```bash
|
93
|
-
rails active_storage:install
|
94
|
-
rails db:migrate
|
95
|
-
```
|
96
|
-
|
97
|
-
Then add to your Message model:
|
98
|
-
```ruby
|
99
|
-
class <%= options[:message_model_name] %> < ApplicationRecord
|
100
|
-
acts_as_message
|
101
|
-
has_many_attached :attachments
|
102
|
-
end
|
103
|
-
```
|
104
|
-
|
105
|
-
## Learn More
|
106
|
-
|
107
|
-
- See the [Rails Integration Guide](https://rubyllm.com/guides/rails) for detailed examples
|
108
|
-
- Visit the [RubyLLM Documentation](https://rubyllm.com) for full API reference
|
data/lib/tasks/aliases.rake
DELETED
@@ -1,205 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'json'
|
4
|
-
|
5
|
-
namespace :aliases do # rubocop:disable Metrics/BlockLength
|
6
|
-
desc 'Generate aliases.json from models in the registry'
|
7
|
-
task :generate do # rubocop:disable Metrics/BlockLength
|
8
|
-
require 'ruby_llm'
|
9
|
-
|
10
|
-
# Group models by provider
|
11
|
-
models = Hash.new { |h, k| h[k] = [] }
|
12
|
-
|
13
|
-
RubyLLM.models.all.each do |model|
|
14
|
-
models[model.provider] << model.id
|
15
|
-
end
|
16
|
-
|
17
|
-
aliases = {}
|
18
|
-
|
19
|
-
# OpenAI models
|
20
|
-
models['openai'].each do |model|
|
21
|
-
openrouter_model = "openai/#{model}"
|
22
|
-
next unless models['openrouter'].include?(openrouter_model)
|
23
|
-
|
24
|
-
alias_key = model.gsub('-latest', '')
|
25
|
-
aliases[alias_key] = {
|
26
|
-
'openai' => model,
|
27
|
-
'openrouter' => openrouter_model
|
28
|
-
}
|
29
|
-
end
|
30
|
-
|
31
|
-
anthropic_latest = group_anthropic_models_by_base_name(models['anthropic'])
|
32
|
-
|
33
|
-
anthropic_latest.each do |base_name, latest_model|
|
34
|
-
openrouter_variants = [
|
35
|
-
"anthropic/#{base_name}", # anthropic/claude-3-5-sonnet
|
36
|
-
"anthropic/#{base_name.gsub(/-(\d)/, '.\1')}", # anthropic/claude-3.5-sonnet
|
37
|
-
"anthropic/#{base_name.gsub(/claude-(\d+)-(\d+)/, 'claude-\1.\2')}", # claude-3-5 -> claude-3.5
|
38
|
-
"anthropic/#{base_name.gsub(/(\d+)-(\d+)/, '\1.\2')}" # any X-Y -> X.Y pattern
|
39
|
-
]
|
40
|
-
|
41
|
-
openrouter_model = openrouter_variants.find { |v| models['openrouter'].include?(v) }
|
42
|
-
|
43
|
-
bedrock_model = find_best_bedrock_model(latest_model, models['bedrock'])
|
44
|
-
|
45
|
-
next unless openrouter_model || bedrock_model || models['anthropic'].include?(latest_model)
|
46
|
-
|
47
|
-
aliases[base_name] = {
|
48
|
-
'anthropic' => latest_model
|
49
|
-
}
|
50
|
-
|
51
|
-
aliases[base_name]['openrouter'] = openrouter_model if openrouter_model
|
52
|
-
aliases[base_name]['bedrock'] = bedrock_model if bedrock_model
|
53
|
-
end
|
54
|
-
|
55
|
-
models['bedrock'].each do |bedrock_model|
|
56
|
-
next unless bedrock_model.start_with?('anthropic.')
|
57
|
-
|
58
|
-
next unless bedrock_model =~ /anthropic\.(claude-[\d\.]+-[a-z]+)/
|
59
|
-
|
60
|
-
base_name = Regexp.last_match(1)
|
61
|
-
anthropic_name = base_name.tr('.', '-')
|
62
|
-
|
63
|
-
next if aliases[anthropic_name]
|
64
|
-
|
65
|
-
openrouter_variants = [
|
66
|
-
"anthropic/#{anthropic_name}",
|
67
|
-
"anthropic/#{base_name}" # Keep the dots
|
68
|
-
]
|
69
|
-
|
70
|
-
openrouter_model = openrouter_variants.find { |v| models['openrouter'].include?(v) }
|
71
|
-
|
72
|
-
aliases[anthropic_name] = {
|
73
|
-
'bedrock' => bedrock_model
|
74
|
-
}
|
75
|
-
|
76
|
-
aliases[anthropic_name]['anthropic'] = anthropic_name if models['anthropic'].include?(anthropic_name)
|
77
|
-
aliases[anthropic_name]['openrouter'] = openrouter_model if openrouter_model
|
78
|
-
end
|
79
|
-
|
80
|
-
models['gemini'].each do |model|
|
81
|
-
openrouter_variants = [
|
82
|
-
"google/#{model}",
|
83
|
-
"google/#{model.gsub('gemini-', 'gemini-').tr('.', '-')}",
|
84
|
-
"google/#{model.gsub('gemini-', 'gemini-')}"
|
85
|
-
]
|
86
|
-
|
87
|
-
openrouter_model = openrouter_variants.find { |v| models['openrouter'].include?(v) }
|
88
|
-
next unless openrouter_model
|
89
|
-
|
90
|
-
alias_key = model.gsub('-latest', '')
|
91
|
-
aliases[alias_key] = {
|
92
|
-
'gemini' => model,
|
93
|
-
'openrouter' => openrouter_model
|
94
|
-
}
|
95
|
-
end
|
96
|
-
|
97
|
-
models['deepseek'].each do |model|
|
98
|
-
openrouter_model = "deepseek/#{model}"
|
99
|
-
next unless models['openrouter'].include?(openrouter_model)
|
100
|
-
|
101
|
-
alias_key = model.gsub('-latest', '')
|
102
|
-
aliases[alias_key] = {
|
103
|
-
'deepseek' => model,
|
104
|
-
'openrouter' => openrouter_model
|
105
|
-
}
|
106
|
-
end
|
107
|
-
|
108
|
-
sorted_aliases = aliases.sort.to_h
|
109
|
-
File.write(RubyLLM::Aliases.aliases_file, JSON.pretty_generate(sorted_aliases))
|
110
|
-
|
111
|
-
puts "Generated #{sorted_aliases.size} aliases"
|
112
|
-
end
|
113
|
-
|
114
|
-
def group_anthropic_models_by_base_name(anthropic_models) # rubocop:disable Rake/MethodDefinitionInTask
|
115
|
-
grouped = Hash.new { |h, k| h[k] = [] }
|
116
|
-
|
117
|
-
anthropic_models.each do |model|
|
118
|
-
base_name = extract_base_name(model)
|
119
|
-
grouped[base_name] << model
|
120
|
-
end
|
121
|
-
|
122
|
-
latest_models = {}
|
123
|
-
grouped.each do |base_name, model_list|
|
124
|
-
if model_list.size == 1
|
125
|
-
latest_models[base_name] = model_list.first
|
126
|
-
else
|
127
|
-
latest_model = model_list.max_by { |model| extract_date_from_model(model) }
|
128
|
-
latest_models[base_name] = latest_model
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
latest_models
|
133
|
-
end
|
134
|
-
|
135
|
-
def extract_base_name(model) # rubocop:disable Rake/MethodDefinitionInTask
|
136
|
-
if model =~ /^(.+)-(\d{8})$/
|
137
|
-
Regexp.last_match(1)
|
138
|
-
else
|
139
|
-
model
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
def extract_date_from_model(model) # rubocop:disable Rake/MethodDefinitionInTask
|
144
|
-
if model =~ /-(\d{8})$/
|
145
|
-
Regexp.last_match(1)
|
146
|
-
else
|
147
|
-
'00000000'
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
def find_best_bedrock_model(anthropic_model, bedrock_models) # rubocop:disable Metrics/PerceivedComplexity,Rake/MethodDefinitionInTask
|
152
|
-
base_pattern = case anthropic_model
|
153
|
-
when 'claude-2.0', 'claude-2'
|
154
|
-
'claude-v2'
|
155
|
-
when 'claude-2.1'
|
156
|
-
'claude-v2:1'
|
157
|
-
when 'claude-instant-v1', 'claude-instant'
|
158
|
-
'claude-instant'
|
159
|
-
else
|
160
|
-
extract_base_name(anthropic_model)
|
161
|
-
end
|
162
|
-
|
163
|
-
matching_models = bedrock_models.select do |bedrock_model|
|
164
|
-
model_without_prefix = bedrock_model.sub(/^(?:us\.)?anthropic\./, '')
|
165
|
-
model_without_prefix.start_with?(base_pattern)
|
166
|
-
end
|
167
|
-
|
168
|
-
return nil if matching_models.empty?
|
169
|
-
|
170
|
-
begin
|
171
|
-
model_info = RubyLLM.models.find(anthropic_model)
|
172
|
-
target_context = model_info.context_window
|
173
|
-
rescue StandardError
|
174
|
-
target_context = nil
|
175
|
-
end
|
176
|
-
|
177
|
-
if target_context
|
178
|
-
target_k = target_context / 1000
|
179
|
-
|
180
|
-
with_context = matching_models.select do |m|
|
181
|
-
m.include?(":#{target_k}k") || m.include?(":0:#{target_k}k")
|
182
|
-
end
|
183
|
-
|
184
|
-
return with_context.first if with_context.any?
|
185
|
-
end
|
186
|
-
|
187
|
-
matching_models.min_by do |model|
|
188
|
-
context_priority = if model =~ /:(?:\d+:)?(\d+)k/
|
189
|
-
-Regexp.last_match(1).to_i
|
190
|
-
else
|
191
|
-
0
|
192
|
-
end
|
193
|
-
|
194
|
-
version_priority = if model =~ /-v(\d+):/
|
195
|
-
-Regexp.last_match(1).to_i
|
196
|
-
else
|
197
|
-
0
|
198
|
-
end
|
199
|
-
|
200
|
-
# Prefer models with explicit context windows
|
201
|
-
has_context_priority = model.include?('k') ? -1 : 0
|
202
|
-
[has_context_priority, context_priority, version_priority]
|
203
|
-
end
|
204
|
-
end
|
205
|
-
end
|
data/lib/tasks/models_docs.rake
DELETED
@@ -1,214 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'dotenv/load'
|
4
|
-
require 'fileutils'
|
5
|
-
|
6
|
-
namespace :models do
|
7
|
-
desc 'Generate available models documentation'
|
8
|
-
task :docs do
|
9
|
-
FileUtils.mkdir_p('docs')
|
10
|
-
output = generate_models_markdown
|
11
|
-
File.write('docs/_reference/available-models.md', output)
|
12
|
-
puts 'Generated docs/_reference/available-models.md'
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def generate_models_markdown
|
17
|
-
<<~MARKDOWN
|
18
|
-
---
|
19
|
-
layout: default
|
20
|
-
title: Available Models
|
21
|
-
nav_order: 1
|
22
|
-
description: Browse hundreds of AI models from every major provider. Always up-to-date, automatically generated.
|
23
|
-
redirect_from:
|
24
|
-
- /guides/available-models
|
25
|
-
---
|
26
|
-
|
27
|
-
# {{ page.title }}
|
28
|
-
{: .no_toc }
|
29
|
-
|
30
|
-
{{ page.description }}
|
31
|
-
{: .fs-6 .fw-300 }
|
32
|
-
|
33
|
-
## Table of contents
|
34
|
-
{: .no_toc .text-delta }
|
35
|
-
|
36
|
-
1. TOC
|
37
|
-
{:toc}
|
38
|
-
|
39
|
-
---
|
40
|
-
|
41
|
-
After reading this guide, you will know:
|
42
|
-
|
43
|
-
* How RubyLLM's model registry works and where data comes from
|
44
|
-
* How to find models by provider, capability, or purpose
|
45
|
-
* What information is available for each model
|
46
|
-
* How to use model aliases for simpler configuration
|
47
|
-
|
48
|
-
## How Model Data Works
|
49
|
-
|
50
|
-
RubyLLM's model registry combines data from multiple sources:
|
51
|
-
|
52
|
-
- **OpenAI, Anthropic, DeepSeek, Gemini**: Data from [Parsera](https://api.parsera.org/v1/llm-specs)
|
53
|
-
- **OpenRouter**: Direct from OpenRouter's API
|
54
|
-
- **Other providers**: Defined in `capabilities.rb` files
|
55
|
-
|
56
|
-
## Contributing Model Updates
|
57
|
-
|
58
|
-
**For major providers** (OpenAI, Anthropic, DeepSeek, Gemini): File issues with [Parsera](https://github.com/parsera-labs/api-llm-specs/issues) for public model data corrections.
|
59
|
-
|
60
|
-
**For other providers**: Edit `lib/ruby_llm/providers/<provider>/capabilities.rb` then run `rake models:update`.
|
61
|
-
|
62
|
-
See the [Contributing Guide](https://github.com/crmne/ruby_llm/blob/main/CONTRIBUTING.md) for details.
|
63
|
-
|
64
|
-
## Last Updated
|
65
|
-
{: .d-inline-block }
|
66
|
-
|
67
|
-
#{Time.now.utc.strftime('%Y-%m-%d')}
|
68
|
-
{: .label .label-green }
|
69
|
-
|
70
|
-
## Models by Provider
|
71
|
-
|
72
|
-
#{generate_provider_sections}
|
73
|
-
|
74
|
-
## Models by Capability
|
75
|
-
|
76
|
-
#{generate_capability_sections}
|
77
|
-
|
78
|
-
## Models by Modality
|
79
|
-
|
80
|
-
#{generate_modality_sections}
|
81
|
-
MARKDOWN
|
82
|
-
end
|
83
|
-
|
84
|
-
def generate_provider_sections
|
85
|
-
RubyLLM::Provider.providers.filter_map do |provider, provider_class|
|
86
|
-
models = RubyLLM.models.by_provider(provider)
|
87
|
-
next if models.none?
|
88
|
-
|
89
|
-
<<~PROVIDER
|
90
|
-
### #{provider_class.name} (#{models.count})
|
91
|
-
|
92
|
-
#{models_table(models)}
|
93
|
-
PROVIDER
|
94
|
-
end.join("\n\n")
|
95
|
-
end
|
96
|
-
|
97
|
-
def generate_capability_sections
|
98
|
-
capabilities = {
|
99
|
-
'Function Calling' => RubyLLM.models.select(&:function_calling?),
|
100
|
-
'Structured Output' => RubyLLM.models.select(&:structured_output?),
|
101
|
-
'Streaming' => RubyLLM.models.select { |m| m.capabilities.include?('streaming') },
|
102
|
-
# 'Reasoning' => RubyLLM.models.select { |m| m.capabilities.include?('reasoning') },
|
103
|
-
'Batch Processing' => RubyLLM.models.select { |m| m.capabilities.include?('batch') }
|
104
|
-
}
|
105
|
-
|
106
|
-
capabilities.filter_map do |capability, models|
|
107
|
-
next if models.none?
|
108
|
-
|
109
|
-
<<~CAPABILITY
|
110
|
-
### #{capability} (#{models.count})
|
111
|
-
|
112
|
-
#{models_table(models)}
|
113
|
-
CAPABILITY
|
114
|
-
end.join("\n\n")
|
115
|
-
end
|
116
|
-
|
117
|
-
def generate_modality_sections # rubocop:disable Metrics/PerceivedComplexity
|
118
|
-
sections = []
|
119
|
-
|
120
|
-
vision_models = RubyLLM.models.select { |m| (m.modalities.input || []).include?('image') }
|
121
|
-
if vision_models.any?
|
122
|
-
sections << <<~SECTION
|
123
|
-
### Vision Models (#{vision_models.count})
|
124
|
-
|
125
|
-
Models that can process images:
|
126
|
-
|
127
|
-
#{models_table(vision_models)}
|
128
|
-
SECTION
|
129
|
-
end
|
130
|
-
|
131
|
-
audio_models = RubyLLM.models.select { |m| (m.modalities.input || []).include?('audio') }
|
132
|
-
if audio_models.any?
|
133
|
-
sections << <<~SECTION
|
134
|
-
### Audio Input Models (#{audio_models.count})
|
135
|
-
|
136
|
-
Models that can process audio:
|
137
|
-
|
138
|
-
#{models_table(audio_models)}
|
139
|
-
SECTION
|
140
|
-
end
|
141
|
-
|
142
|
-
pdf_models = RubyLLM.models.select { |m| (m.modalities.input || []).include?('pdf') }
|
143
|
-
if pdf_models.any?
|
144
|
-
sections << <<~SECTION
|
145
|
-
### PDF Models (#{pdf_models.count})
|
146
|
-
|
147
|
-
Models that can process PDF documents:
|
148
|
-
|
149
|
-
#{models_table(pdf_models)}
|
150
|
-
SECTION
|
151
|
-
end
|
152
|
-
|
153
|
-
embedding_models = RubyLLM.models.select { |m| (m.modalities.output || []).include?('embeddings') }
|
154
|
-
if embedding_models.any?
|
155
|
-
sections << <<~SECTION
|
156
|
-
### Embedding Models (#{embedding_models.count})
|
157
|
-
|
158
|
-
Models that generate embeddings:
|
159
|
-
|
160
|
-
#{models_table(embedding_models)}
|
161
|
-
SECTION
|
162
|
-
end
|
163
|
-
|
164
|
-
sections.join("\n\n")
|
165
|
-
end
|
166
|
-
|
167
|
-
def models_table(models)
|
168
|
-
return '*No models found*' if models.none?
|
169
|
-
|
170
|
-
headers = ['Model', 'Provider', 'Context', 'Max Output', 'Standard Pricing (per 1M tokens)']
|
171
|
-
alignment = [':--', ':--', '--:', '--:', ':--']
|
172
|
-
|
173
|
-
rows = models.sort_by { |m| [m.provider, m.name] }.map do |model|
|
174
|
-
pricing = standard_pricing_display(model)
|
175
|
-
|
176
|
-
[
|
177
|
-
model.id,
|
178
|
-
model.provider,
|
179
|
-
model.context_window || '-',
|
180
|
-
model.max_output_tokens || '-',
|
181
|
-
pricing
|
182
|
-
]
|
183
|
-
end
|
184
|
-
|
185
|
-
table = []
|
186
|
-
table << "| #{headers.join(' | ')} |"
|
187
|
-
table << "| #{alignment.join(' | ')} |"
|
188
|
-
|
189
|
-
rows.each do |row|
|
190
|
-
table << "| #{row.join(' | ')} |"
|
191
|
-
end
|
192
|
-
|
193
|
-
table.join("\n")
|
194
|
-
end
|
195
|
-
|
196
|
-
def standard_pricing_display(model)
|
197
|
-
pricing_data = model.pricing.to_h[:text_tokens]&.dig(:standard) || {}
|
198
|
-
|
199
|
-
if pricing_data.any?
|
200
|
-
parts = []
|
201
|
-
|
202
|
-
parts << "In: $#{format('%.2f', pricing_data[:input_per_million])}" if pricing_data[:input_per_million]
|
203
|
-
|
204
|
-
parts << "Out: $#{format('%.2f', pricing_data[:output_per_million])}" if pricing_data[:output_per_million]
|
205
|
-
|
206
|
-
if pricing_data[:cached_input_per_million]
|
207
|
-
parts << "Cache: $#{format('%.2f', pricing_data[:cached_input_per_million])}"
|
208
|
-
end
|
209
|
-
|
210
|
-
return parts.join(', ') if parts.any?
|
211
|
-
end
|
212
|
-
|
213
|
-
'-'
|
214
|
-
end
|