ruby_llm_community 0.0.6 → 1.1.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 +20 -3
- data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +127 -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/install_generator.rb +227 -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/upgrade_to_v1_7/templates/migration.rb.tt +137 -0
- data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +170 -0
- data/lib/ruby_llm/active_record/acts_as.rb +112 -332
- data/lib/ruby_llm/active_record/acts_as_legacy.rb +403 -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 +130 -11
- data/lib/ruby_llm/aliases.rb +7 -25
- data/lib/ruby_llm/attachment.rb +22 -0
- data/lib/ruby_llm/chat.rb +10 -17
- data/lib/ruby_llm/configuration.rb +11 -12
- data/lib/ruby_llm/connection.rb +4 -4
- data/lib/ruby_llm/connection_multipart.rb +19 -0
- data/lib/ruby_llm/content.rb +5 -2
- data/lib/ruby_llm/embedding.rb +1 -2
- data/lib/ruby_llm/error.rb +0 -8
- data/lib/ruby_llm/image.rb +23 -8
- data/lib/ruby_llm/image_attachment.rb +30 -0
- data/lib/ruby_llm/message.rb +7 -7
- data/lib/ruby_llm/model/info.rb +12 -10
- data/lib/ruby_llm/model/pricing.rb +0 -3
- data/lib/ruby_llm/model/pricing_category.rb +0 -2
- data/lib/ruby_llm/model/pricing_tier.rb +0 -1
- data/lib/ruby_llm/models.json +4705 -2144
- data/lib/ruby_llm/models.rb +56 -35
- data/lib/ruby_llm/provider.rb +14 -12
- data/lib/ruby_llm/providers/anthropic/capabilities.rb +1 -46
- data/lib/ruby_llm/providers/anthropic/chat.rb +2 -2
- data/lib/ruby_llm/providers/anthropic/media.rb +1 -2
- data/lib/ruby_llm/providers/anthropic/tools.rb +1 -2
- data/lib/ruby_llm/providers/anthropic.rb +1 -2
- data/lib/ruby_llm/providers/bedrock/chat.rb +2 -4
- data/lib/ruby_llm/providers/bedrock/media.rb +0 -1
- data/lib/ruby_llm/providers/bedrock/models.rb +19 -3
- data/lib/ruby_llm/providers/bedrock/streaming/base.rb +0 -12
- data/lib/ruby_llm/providers/bedrock/streaming/content_extraction.rb +0 -7
- data/lib/ruby_llm/providers/bedrock/streaming/message_processing.rb +0 -12
- data/lib/ruby_llm/providers/bedrock/streaming/payload_processing.rb +0 -12
- data/lib/ruby_llm/providers/bedrock/streaming/prelude_handling.rb +0 -13
- data/lib/ruby_llm/providers/bedrock/streaming.rb +0 -18
- data/lib/ruby_llm/providers/bedrock.rb +1 -2
- data/lib/ruby_llm/providers/deepseek/capabilities.rb +1 -2
- data/lib/ruby_llm/providers/deepseek/chat.rb +0 -1
- data/lib/ruby_llm/providers/gemini/capabilities.rb +28 -100
- data/lib/ruby_llm/providers/gemini/chat.rb +57 -29
- data/lib/ruby_llm/providers/gemini/embeddings.rb +0 -2
- data/lib/ruby_llm/providers/gemini/images.rb +1 -2
- data/lib/ruby_llm/providers/gemini/media.rb +1 -2
- data/lib/ruby_llm/providers/gemini/models.rb +1 -2
- data/lib/ruby_llm/providers/gemini/streaming.rb +15 -1
- data/lib/ruby_llm/providers/gemini/tools.rb +0 -5
- data/lib/ruby_llm/providers/gpustack/chat.rb +11 -1
- data/lib/ruby_llm/providers/gpustack/media.rb +45 -0
- data/lib/ruby_llm/providers/gpustack/models.rb +44 -9
- data/lib/ruby_llm/providers/gpustack.rb +1 -0
- data/lib/ruby_llm/providers/mistral/capabilities.rb +2 -10
- data/lib/ruby_llm/providers/mistral/chat.rb +0 -2
- data/lib/ruby_llm/providers/mistral/embeddings.rb +0 -3
- data/lib/ruby_llm/providers/mistral/models.rb +0 -1
- data/lib/ruby_llm/providers/ollama/chat.rb +0 -1
- data/lib/ruby_llm/providers/ollama/media.rb +2 -7
- 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/capabilities.rb +3 -16
- data/lib/ruby_llm/providers/openai/chat.rb +1 -3
- data/lib/ruby_llm/providers/openai/embeddings.rb +0 -3
- data/lib/ruby_llm/providers/openai/images.rb +73 -3
- data/lib/ruby_llm/providers/openai/media.rb +4 -5
- data/lib/ruby_llm/providers/openai/response.rb +121 -29
- data/lib/ruby_llm/providers/openai/response_media.rb +3 -3
- data/lib/ruby_llm/providers/openai/streaming.rb +110 -47
- data/lib/ruby_llm/providers/openai/tools.rb +12 -7
- data/lib/ruby_llm/providers/openai.rb +1 -3
- data/lib/ruby_llm/providers/openai_base.rb +2 -2
- data/lib/ruby_llm/providers/openrouter/models.rb +1 -16
- data/lib/ruby_llm/providers/perplexity/capabilities.rb +0 -1
- data/lib/ruby_llm/providers/perplexity/chat.rb +0 -1
- data/lib/ruby_llm/providers/perplexity.rb +1 -5
- 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/providers/xai/capabilities.rb +166 -0
- data/lib/ruby_llm/providers/xai/chat.rb +15 -0
- data/lib/ruby_llm/providers/xai/models.rb +48 -0
- data/lib/ruby_llm/providers/xai.rb +46 -0
- data/lib/ruby_llm/railtie.rb +20 -4
- data/lib/ruby_llm/stream_accumulator.rb +68 -10
- data/lib/ruby_llm/streaming.rb +16 -25
- data/lib/ruby_llm/tool.rb +2 -19
- data/lib/ruby_llm/tool_call.rb +0 -9
- data/lib/ruby_llm/utils.rb +5 -9
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm_community.rb +8 -5
- data/lib/tasks/models.rake +549 -0
- data/lib/tasks/release.rake +37 -2
- data/lib/tasks/ruby_llm.rake +15 -0
- data/lib/tasks/vcr.rake +2 -9
- metadata +44 -6
- data/lib/generators/ruby_llm/install/templates/INSTALL_INFO.md.tt +0 -108
- data/lib/generators/ruby_llm/install_generator.rb +0 -121
- data/lib/tasks/aliases.rake +0 -235
- data/lib/tasks/models_docs.rake +0 -224
- 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_community
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Shippy
|
@@ -136,16 +136,39 @@ 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
|
155
|
+
- lib/generators/ruby_llm/install/install_generator.rb
|
140
156
|
- lib/generators/ruby_llm/install/templates/chat_model.rb.tt
|
141
157
|
- lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt
|
142
158
|
- lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt
|
159
|
+
- lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt
|
143
160
|
- lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt
|
144
161
|
- lib/generators/ruby_llm/install/templates/initializer.rb.tt
|
145
162
|
- lib/generators/ruby_llm/install/templates/message_model.rb.tt
|
163
|
+
- lib/generators/ruby_llm/install/templates/model_model.rb.tt
|
146
164
|
- lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt
|
147
|
-
- lib/generators/ruby_llm/
|
165
|
+
- lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt
|
166
|
+
- lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb
|
148
167
|
- lib/ruby_llm/active_record/acts_as.rb
|
168
|
+
- lib/ruby_llm/active_record/acts_as_legacy.rb
|
169
|
+
- lib/ruby_llm/active_record/chat_methods.rb
|
170
|
+
- lib/ruby_llm/active_record/message_methods.rb
|
171
|
+
- lib/ruby_llm/active_record/model_methods.rb
|
149
172
|
- lib/ruby_llm/aliases.json
|
150
173
|
- lib/ruby_llm/aliases.rb
|
151
174
|
- lib/ruby_llm/attachment.rb
|
@@ -153,11 +176,13 @@ files:
|
|
153
176
|
- lib/ruby_llm/chunk.rb
|
154
177
|
- lib/ruby_llm/configuration.rb
|
155
178
|
- lib/ruby_llm/connection.rb
|
179
|
+
- lib/ruby_llm/connection_multipart.rb
|
156
180
|
- lib/ruby_llm/content.rb
|
157
181
|
- lib/ruby_llm/context.rb
|
158
182
|
- lib/ruby_llm/embedding.rb
|
159
183
|
- lib/ruby_llm/error.rb
|
160
184
|
- lib/ruby_llm/image.rb
|
185
|
+
- lib/ruby_llm/image_attachment.rb
|
161
186
|
- lib/ruby_llm/message.rb
|
162
187
|
- lib/ruby_llm/mime_type.rb
|
163
188
|
- lib/ruby_llm/model.rb
|
@@ -204,6 +229,7 @@ files:
|
|
204
229
|
- lib/ruby_llm/providers/gemini/tools.rb
|
205
230
|
- lib/ruby_llm/providers/gpustack.rb
|
206
231
|
- lib/ruby_llm/providers/gpustack/chat.rb
|
232
|
+
- lib/ruby_llm/providers/gpustack/media.rb
|
207
233
|
- lib/ruby_llm/providers/gpustack/models.rb
|
208
234
|
- lib/ruby_llm/providers/mistral.rb
|
209
235
|
- lib/ruby_llm/providers/mistral/capabilities.rb
|
@@ -213,6 +239,7 @@ files:
|
|
213
239
|
- lib/ruby_llm/providers/ollama.rb
|
214
240
|
- lib/ruby_llm/providers/ollama/chat.rb
|
215
241
|
- lib/ruby_llm/providers/ollama/media.rb
|
242
|
+
- lib/ruby_llm/providers/ollama/models.rb
|
216
243
|
- lib/ruby_llm/providers/openai.rb
|
217
244
|
- lib/ruby_llm/providers/openai/capabilities.rb
|
218
245
|
- lib/ruby_llm/providers/openai/chat.rb
|
@@ -231,6 +258,15 @@ files:
|
|
231
258
|
- lib/ruby_llm/providers/perplexity/capabilities.rb
|
232
259
|
- lib/ruby_llm/providers/perplexity/chat.rb
|
233
260
|
- lib/ruby_llm/providers/perplexity/models.rb
|
261
|
+
- lib/ruby_llm/providers/vertexai.rb
|
262
|
+
- lib/ruby_llm/providers/vertexai/chat.rb
|
263
|
+
- lib/ruby_llm/providers/vertexai/embeddings.rb
|
264
|
+
- lib/ruby_llm/providers/vertexai/models.rb
|
265
|
+
- lib/ruby_llm/providers/vertexai/streaming.rb
|
266
|
+
- lib/ruby_llm/providers/xai.rb
|
267
|
+
- lib/ruby_llm/providers/xai/capabilities.rb
|
268
|
+
- lib/ruby_llm/providers/xai/chat.rb
|
269
|
+
- lib/ruby_llm/providers/xai/models.rb
|
234
270
|
- lib/ruby_llm/railtie.rb
|
235
271
|
- lib/ruby_llm/stream_accumulator.rb
|
236
272
|
- lib/ruby_llm/streaming.rb
|
@@ -240,10 +276,9 @@ files:
|
|
240
276
|
- lib/ruby_llm/version.rb
|
241
277
|
- lib/ruby_llm_community.rb
|
242
278
|
- lib/shims/ruby_llm.rb
|
243
|
-
- lib/tasks/
|
244
|
-
- lib/tasks/models_docs.rake
|
245
|
-
- lib/tasks/models_update.rake
|
279
|
+
- lib/tasks/models.rake
|
246
280
|
- lib/tasks/release.rake
|
281
|
+
- lib/tasks/ruby_llm.rake
|
247
282
|
- lib/tasks/vcr.rake
|
248
283
|
homepage: https://rubyllm.com
|
249
284
|
licenses:
|
@@ -255,6 +290,9 @@ metadata:
|
|
255
290
|
documentation_uri: https://rubyllm.com
|
256
291
|
bug_tracker_uri: https://github.com/tpaulshippy/ruby_llm_community/issues
|
257
292
|
rubygems_mfa_required: 'true'
|
293
|
+
post_install_message: |
|
294
|
+
Upgrading from RubyLLM <= 1.6.x? Check the upgrade guide for new features and migration instructions
|
295
|
+
--> https://rubyllm.com/upgrading-to-1-7/
|
258
296
|
rdoc_options: []
|
259
297
|
require_paths:
|
260
298
|
- 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
|
@@ -1,121 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'rails/generators'
|
4
|
-
require 'rails/generators/active_record'
|
5
|
-
|
6
|
-
module RubyLLM
|
7
|
-
# Generator for RubyLLM Rails models and migrations
|
8
|
-
class InstallGenerator < Rails::Generators::Base
|
9
|
-
include Rails::Generators::Migration
|
10
|
-
|
11
|
-
namespace 'ruby_llm:install'
|
12
|
-
|
13
|
-
source_root File.expand_path('install/templates', __dir__)
|
14
|
-
|
15
|
-
class_option :chat_model_name, type: :string, default: 'Chat',
|
16
|
-
desc: 'Name of the Chat model class'
|
17
|
-
class_option :message_model_name, type: :string, default: 'Message',
|
18
|
-
desc: 'Name of the Message model class'
|
19
|
-
class_option :tool_call_model_name, type: :string, default: 'ToolCall',
|
20
|
-
desc: 'Name of the ToolCall model class'
|
21
|
-
|
22
|
-
desc 'Creates model files for Chat, Message, and ToolCall, and creates migrations for RubyLLM Rails integration'
|
23
|
-
|
24
|
-
def self.next_migration_number(dirname)
|
25
|
-
::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
26
|
-
end
|
27
|
-
|
28
|
-
def migration_version
|
29
|
-
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
30
|
-
end
|
31
|
-
|
32
|
-
def postgresql?
|
33
|
-
::ActiveRecord::Base.connection.adapter_name.downcase.include?('postgresql')
|
34
|
-
rescue StandardError
|
35
|
-
false
|
36
|
-
end
|
37
|
-
|
38
|
-
def acts_as_chat_declaration
|
39
|
-
acts_as_chat_params = []
|
40
|
-
if options[:message_model_name] != 'Message'
|
41
|
-
acts_as_chat_params << "message_class: \"#{options[:message_model_name]}\""
|
42
|
-
end
|
43
|
-
if options[:tool_call_model_name] != 'ToolCall'
|
44
|
-
acts_as_chat_params << "tool_call_class: \"#{options[:tool_call_model_name]}\""
|
45
|
-
end
|
46
|
-
if acts_as_chat_params.any?
|
47
|
-
"acts_as_chat #{acts_as_chat_params.join(', ')}"
|
48
|
-
else
|
49
|
-
'acts_as_chat'
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def acts_as_message_declaration
|
54
|
-
acts_as_message_params = []
|
55
|
-
acts_as_message_params << "chat_class: \"#{options[:chat_model_name]}\"" if options[:chat_model_name] != 'Chat'
|
56
|
-
if options[:tool_call_model_name] != 'ToolCall'
|
57
|
-
acts_as_message_params << "tool_call_class: \"#{options[:tool_call_model_name]}\""
|
58
|
-
end
|
59
|
-
if acts_as_message_params.any?
|
60
|
-
"acts_as_message #{acts_as_message_params.join(', ')}"
|
61
|
-
else
|
62
|
-
'acts_as_message'
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def acts_as_tool_call_declaration
|
67
|
-
acts_as_tool_call_params = []
|
68
|
-
if options[:message_model_name] != 'Message'
|
69
|
-
acts_as_tool_call_params << "message_class: \"#{options[:message_model_name]}\""
|
70
|
-
end
|
71
|
-
if acts_as_tool_call_params.any?
|
72
|
-
"acts_as_tool_call #{acts_as_tool_call_params.join(', ')}"
|
73
|
-
else
|
74
|
-
'acts_as_tool_call'
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def create_migration_files
|
79
|
-
# Create migrations with timestamps to ensure proper order
|
80
|
-
# First create chats table
|
81
|
-
migration_template 'create_chats_migration.rb.tt',
|
82
|
-
"db/migrate/create_#{options[:chat_model_name].tableize}.rb"
|
83
|
-
|
84
|
-
# Then create messages table (must come before tool_calls due to foreign key)
|
85
|
-
sleep 1 # Ensure different timestamp
|
86
|
-
migration_template 'create_messages_migration.rb.tt',
|
87
|
-
"db/migrate/create_#{options[:message_model_name].tableize}.rb"
|
88
|
-
|
89
|
-
# Finally create tool_calls table (references messages)
|
90
|
-
sleep 1 # Ensure different timestamp
|
91
|
-
migration_template 'create_tool_calls_migration.rb.tt',
|
92
|
-
"db/migrate/create_#{options[:tool_call_model_name].tableize}.rb"
|
93
|
-
end
|
94
|
-
|
95
|
-
def create_model_files
|
96
|
-
template 'chat_model.rb.tt', "app/models/#{options[:chat_model_name].underscore}.rb"
|
97
|
-
template 'message_model.rb.tt', "app/models/#{options[:message_model_name].underscore}.rb"
|
98
|
-
template 'tool_call_model.rb.tt', "app/models/#{options[:tool_call_model_name].underscore}.rb"
|
99
|
-
end
|
100
|
-
|
101
|
-
def create_initializer
|
102
|
-
template 'initializer.rb.tt', 'config/initializers/ruby_llm.rb'
|
103
|
-
end
|
104
|
-
|
105
|
-
def show_install_info
|
106
|
-
say "\n ✅ RubyLLM installed!", :green
|
107
|
-
|
108
|
-
say "\n Next steps:", :yellow
|
109
|
-
say ' 1. Run: rails db:migrate'
|
110
|
-
say ' 2. Set your API keys in config/initializers/ruby_llm.rb'
|
111
|
-
say " 3. Start chatting: #{options[:chat_model_name]}.create!(model_id: 'gpt-4.1-nano').ask('Hello!')"
|
112
|
-
|
113
|
-
say "\n 📚 Full docs: https://rubyllm.com", :cyan
|
114
|
-
|
115
|
-
say "\n ❤️ Love RubyLLM?", :magenta
|
116
|
-
say ' • ⭐ Star on GitHub: https://github.com/crmne/ruby_llm'
|
117
|
-
say ' • 💖 Sponsor: https://github.com/sponsors/crmne'
|
118
|
-
say "\n"
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
data/lib/tasks/aliases.rake
DELETED
@@ -1,235 +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 models - group by base name and find latest
|
32
|
-
anthropic_latest = group_anthropic_models_by_base_name(models['anthropic'])
|
33
|
-
|
34
|
-
anthropic_latest.each do |base_name, latest_model|
|
35
|
-
# Check OpenRouter naming patterns for the BASE NAME (not the full dated model)
|
36
|
-
openrouter_variants = [
|
37
|
-
"anthropic/#{base_name}", # anthropic/claude-3-5-sonnet
|
38
|
-
"anthropic/#{base_name.gsub(/-(\d)/, '.\1')}", # anthropic/claude-3.5-sonnet
|
39
|
-
"anthropic/#{base_name.gsub(/claude-(\d+)-(\d+)/, 'claude-\1.\2')}", # claude-3-5 -> claude-3.5
|
40
|
-
"anthropic/#{base_name.gsub(/(\d+)-(\d+)/, '\1.\2')}" # any X-Y -> X.Y pattern
|
41
|
-
]
|
42
|
-
|
43
|
-
openrouter_model = openrouter_variants.find { |v| models['openrouter'].include?(v) }
|
44
|
-
|
45
|
-
# Find corresponding Bedrock model
|
46
|
-
bedrock_model = find_best_bedrock_model(latest_model, models['bedrock'])
|
47
|
-
|
48
|
-
# Create alias if we have any match (OpenRouter OR Bedrock) OR if it's Anthropic-only
|
49
|
-
next unless openrouter_model || bedrock_model || models['anthropic'].include?(latest_model)
|
50
|
-
|
51
|
-
aliases[base_name] = {
|
52
|
-
'anthropic' => latest_model
|
53
|
-
}
|
54
|
-
|
55
|
-
aliases[base_name]['openrouter'] = openrouter_model if openrouter_model
|
56
|
-
aliases[base_name]['bedrock'] = bedrock_model if bedrock_model
|
57
|
-
end
|
58
|
-
|
59
|
-
# Also check if Bedrock has models that Anthropic doesn't
|
60
|
-
models['bedrock'].each do |bedrock_model|
|
61
|
-
next unless bedrock_model.start_with?('anthropic.')
|
62
|
-
|
63
|
-
# Extract the Claude model name
|
64
|
-
next unless bedrock_model =~ /anthropic\.(claude-[\d\.]+-[a-z]+)/
|
65
|
-
|
66
|
-
base_name = Regexp.last_match(1)
|
67
|
-
# Normalize to Anthropic naming convention
|
68
|
-
anthropic_name = base_name.tr('.', '-')
|
69
|
-
|
70
|
-
# Skip if we already have an alias for this
|
71
|
-
next if aliases[anthropic_name]
|
72
|
-
|
73
|
-
# Check if this model exists in OpenRouter
|
74
|
-
openrouter_variants = [
|
75
|
-
"anthropic/#{anthropic_name}",
|
76
|
-
"anthropic/#{base_name}" # Keep the dots
|
77
|
-
]
|
78
|
-
|
79
|
-
openrouter_model = openrouter_variants.find { |v| models['openrouter'].include?(v) }
|
80
|
-
|
81
|
-
aliases[anthropic_name] = {
|
82
|
-
'bedrock' => bedrock_model
|
83
|
-
}
|
84
|
-
|
85
|
-
aliases[anthropic_name]['anthropic'] = anthropic_name if models['anthropic'].include?(anthropic_name)
|
86
|
-
aliases[anthropic_name]['openrouter'] = openrouter_model if openrouter_model
|
87
|
-
end
|
88
|
-
|
89
|
-
# Gemini models
|
90
|
-
models['gemini'].each do |model|
|
91
|
-
# OpenRouter uses "google/" prefix and sometimes different naming
|
92
|
-
openrouter_variants = [
|
93
|
-
"google/#{model}",
|
94
|
-
"google/#{model.gsub('gemini-', 'gemini-').tr('.', '-')}",
|
95
|
-
"google/#{model.gsub('gemini-', 'gemini-')}"
|
96
|
-
]
|
97
|
-
|
98
|
-
openrouter_model = openrouter_variants.find { |v| models['openrouter'].include?(v) }
|
99
|
-
next unless openrouter_model
|
100
|
-
|
101
|
-
alias_key = model.gsub('-latest', '')
|
102
|
-
aliases[alias_key] = {
|
103
|
-
'gemini' => model,
|
104
|
-
'openrouter' => openrouter_model
|
105
|
-
}
|
106
|
-
end
|
107
|
-
|
108
|
-
# DeepSeek models
|
109
|
-
models['deepseek'].each do |model|
|
110
|
-
openrouter_model = "deepseek/#{model}"
|
111
|
-
next unless models['openrouter'].include?(openrouter_model)
|
112
|
-
|
113
|
-
alias_key = model.gsub('-latest', '')
|
114
|
-
aliases[alias_key] = {
|
115
|
-
'deepseek' => model,
|
116
|
-
'openrouter' => openrouter_model
|
117
|
-
}
|
118
|
-
end
|
119
|
-
|
120
|
-
# Write the result
|
121
|
-
sorted_aliases = aliases.sort.to_h
|
122
|
-
File.write('lib/ruby_llm/aliases.json', JSON.pretty_generate(sorted_aliases))
|
123
|
-
|
124
|
-
puts "Generated #{sorted_aliases.size} aliases"
|
125
|
-
end
|
126
|
-
|
127
|
-
def group_anthropic_models_by_base_name(anthropic_models) # rubocop:disable Rake/MethodDefinitionInTask
|
128
|
-
grouped = Hash.new { |h, k| h[k] = [] }
|
129
|
-
|
130
|
-
anthropic_models.each do |model|
|
131
|
-
base_name = extract_base_name(model)
|
132
|
-
grouped[base_name] << model
|
133
|
-
end
|
134
|
-
|
135
|
-
# Find the latest model for each base name
|
136
|
-
latest_models = {}
|
137
|
-
grouped.each do |base_name, model_list|
|
138
|
-
if model_list.size == 1
|
139
|
-
latest_models[base_name] = model_list.first
|
140
|
-
else
|
141
|
-
# Sort by date and take the latest
|
142
|
-
latest_model = model_list.max_by { |model| extract_date_from_model(model) }
|
143
|
-
latest_models[base_name] = latest_model
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
latest_models
|
148
|
-
end
|
149
|
-
|
150
|
-
def extract_base_name(model) # rubocop:disable Rake/MethodDefinitionInTask
|
151
|
-
# Remove date suffix (YYYYMMDD) from model name
|
152
|
-
if model =~ /^(.+)-(\d{8})$/
|
153
|
-
Regexp.last_match(1)
|
154
|
-
else
|
155
|
-
# Models without date suffix (like claude-2.0, claude-2.1)
|
156
|
-
model
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
def extract_date_from_model(model) # rubocop:disable Rake/MethodDefinitionInTask
|
161
|
-
# Extract date for comparison, return '00000000' for models without dates
|
162
|
-
if model =~ /-(\d{8})$/
|
163
|
-
Regexp.last_match(1)
|
164
|
-
else
|
165
|
-
'00000000' # Ensures models without dates sort before dated ones
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
def find_best_bedrock_model(anthropic_model, bedrock_models) # rubocop:disable Metrics/PerceivedComplexity,Rake/MethodDefinitionInTask
|
170
|
-
# Special mapping for Claude 2.x models
|
171
|
-
base_pattern = case anthropic_model
|
172
|
-
when 'claude-2.0', 'claude-2'
|
173
|
-
'claude-v2'
|
174
|
-
when 'claude-2.1'
|
175
|
-
'claude-v2:1'
|
176
|
-
when 'claude-instant-v1', 'claude-instant'
|
177
|
-
'claude-instant'
|
178
|
-
else
|
179
|
-
# For Claude 3+ models, extract base name
|
180
|
-
extract_base_name(anthropic_model)
|
181
|
-
end
|
182
|
-
|
183
|
-
# Find all matching Bedrock models by stripping provider prefix and comparing base name
|
184
|
-
matching_models = bedrock_models.select do |bedrock_model|
|
185
|
-
# Strip any provider prefix (anthropic. or us.anthropic.)
|
186
|
-
model_without_prefix = bedrock_model.sub(/^(?:us\.)?anthropic\./, '')
|
187
|
-
model_without_prefix.start_with?(base_pattern)
|
188
|
-
end
|
189
|
-
|
190
|
-
return nil if matching_models.empty?
|
191
|
-
|
192
|
-
# Get model info to check context window
|
193
|
-
begin
|
194
|
-
model_info = RubyLLM.models.find(anthropic_model)
|
195
|
-
target_context = model_info.context_window
|
196
|
-
rescue StandardError
|
197
|
-
target_context = nil
|
198
|
-
end
|
199
|
-
|
200
|
-
# If we have context window info, try to match it
|
201
|
-
if target_context
|
202
|
-
# Convert to k format (200000 -> 200k)
|
203
|
-
target_k = target_context / 1000
|
204
|
-
|
205
|
-
# Find models with this specific context window
|
206
|
-
with_context = matching_models.select do |m|
|
207
|
-
m.include?(":#{target_k}k") || m.include?(":0:#{target_k}k")
|
208
|
-
end
|
209
|
-
|
210
|
-
return with_context.first if with_context.any?
|
211
|
-
end
|
212
|
-
|
213
|
-
# Otherwise, pick the one with the highest context window or latest version
|
214
|
-
matching_models.min_by do |model|
|
215
|
-
# Extract context window if specified
|
216
|
-
context_priority = if model =~ /:(?:\d+:)?(\d+)k/
|
217
|
-
-Regexp.last_match(1).to_i # Negative for descending sort
|
218
|
-
else
|
219
|
-
0 # No context specified
|
220
|
-
end
|
221
|
-
|
222
|
-
# Extract version if present
|
223
|
-
version_priority = if model =~ /-v(\d+):/
|
224
|
-
-Regexp.last_match(1).to_i # Negative for descending sort (latest version first)
|
225
|
-
else
|
226
|
-
0
|
227
|
-
end
|
228
|
-
|
229
|
-
# Prefer models with explicit context windows
|
230
|
-
has_context_priority = model.include?('k') ? -1 : 0
|
231
|
-
|
232
|
-
[has_context_priority, context_priority, version_priority]
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|