ruby_llm 1.7.0 → 1.8.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 +3 -2
- data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +30 -18
- data/lib/generators/ruby_llm/generator_helpers.rb +129 -0
- data/lib/generators/ruby_llm/install/install_generator.rb +110 -0
- data/lib/generators/ruby_llm/install/templates/add_references_to_chats_tool_calls_and_messages_migration.rb.tt +9 -0
- data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +2 -3
- data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +3 -6
- data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +3 -6
- data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +4 -5
- data/lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt +21 -13
- data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +121 -0
- data/lib/ruby_llm/attachment.rb +5 -0
- data/lib/ruby_llm/configuration.rb +2 -0
- data/lib/ruby_llm/mime_type.rb +4 -0
- data/lib/ruby_llm/model/info.rb +4 -0
- data/lib/ruby_llm/models.json +780 -511
- data/lib/ruby_llm/models.rb +7 -3
- data/lib/ruby_llm/moderation.rb +56 -0
- data/lib/ruby_llm/provider.rb +6 -0
- data/lib/ruby_llm/providers/gemini/capabilities.rb +5 -0
- data/lib/ruby_llm/providers/openai/moderation.rb +34 -0
- data/lib/ruby_llm/providers/openai.rb +1 -0
- data/lib/ruby_llm/railtie.rb +1 -1
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +4 -0
- metadata +7 -3
- data/lib/generators/ruby_llm/install_generator.rb +0 -217
- data/lib/generators/ruby_llm/upgrade_to_v1_7_generator.rb +0 -160
data/lib/ruby_llm/models.rb
CHANGED
@@ -194,15 +194,15 @@ module RubyLLM
|
|
194
194
|
end
|
195
195
|
|
196
196
|
def embedding_models
|
197
|
-
self.class.new(all.select { |m| m.type == 'embedding' })
|
197
|
+
self.class.new(all.select { |m| m.type == 'embedding' || m.modalities.output.include?('embeddings') })
|
198
198
|
end
|
199
199
|
|
200
200
|
def audio_models
|
201
|
-
self.class.new(all.select { |m| m.type == 'audio' })
|
201
|
+
self.class.new(all.select { |m| m.type == 'audio' || m.modalities.output.include?('audio') })
|
202
202
|
end
|
203
203
|
|
204
204
|
def image_models
|
205
|
-
self.class.new(all.select { |m| m.type == 'image' })
|
205
|
+
self.class.new(all.select { |m| m.type == 'image' || m.modalities.output.include?('image') })
|
206
206
|
end
|
207
207
|
|
208
208
|
def by_family(family)
|
@@ -217,6 +217,10 @@ module RubyLLM
|
|
217
217
|
self.class.refresh!(remote_only: remote_only)
|
218
218
|
end
|
219
219
|
|
220
|
+
def resolve(model_id, provider: nil, assume_exists: false, config: nil)
|
221
|
+
self.class.resolve(model_id, provider: provider, assume_exists: assume_exists, config: config)
|
222
|
+
end
|
223
|
+
|
220
224
|
private
|
221
225
|
|
222
226
|
def find_with_provider(model_id, provider)
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
# Identify potentially harmful content in text.
|
5
|
+
# https://platform.openai.com/docs/guides/moderation
|
6
|
+
class Moderation
|
7
|
+
attr_reader :id, :model, :results
|
8
|
+
|
9
|
+
def initialize(id:, model:, results:)
|
10
|
+
@id = id
|
11
|
+
@model = model
|
12
|
+
@results = results
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.moderate(input,
|
16
|
+
model: nil,
|
17
|
+
provider: nil,
|
18
|
+
assume_model_exists: false,
|
19
|
+
context: nil)
|
20
|
+
config = context&.config || RubyLLM.config
|
21
|
+
model ||= config.default_moderation_model || 'omni-moderation-latest'
|
22
|
+
model, provider_instance = Models.resolve(model, provider: provider, assume_exists: assume_model_exists,
|
23
|
+
config: config)
|
24
|
+
model_id = model.id
|
25
|
+
|
26
|
+
provider_instance.moderate(input, model: model_id)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Convenience method to get content from moderation result
|
30
|
+
def content
|
31
|
+
results
|
32
|
+
end
|
33
|
+
|
34
|
+
# Check if any content was flagged
|
35
|
+
def flagged?
|
36
|
+
results.any? { |result| result['flagged'] }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get all flagged categories across all results
|
40
|
+
def flagged_categories
|
41
|
+
results.flat_map do |result|
|
42
|
+
result['categories']&.select { |_category, flagged| flagged }&.keys || []
|
43
|
+
end.uniq
|
44
|
+
end
|
45
|
+
|
46
|
+
# Get category scores for the first result (most common case)
|
47
|
+
def category_scores
|
48
|
+
results.first&.dig('category_scores') || {}
|
49
|
+
end
|
50
|
+
|
51
|
+
# Get categories for the first result (most common case)
|
52
|
+
def categories
|
53
|
+
results.first&.dig('categories') || {}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/ruby_llm/provider.rb
CHANGED
@@ -70,6 +70,12 @@ module RubyLLM
|
|
70
70
|
parse_embedding_response(response, model:, text:)
|
71
71
|
end
|
72
72
|
|
73
|
+
def moderate(input, model:)
|
74
|
+
payload = render_moderation_payload(input, model:)
|
75
|
+
response = @connection.post moderation_url, payload
|
76
|
+
parse_moderation_response(response, model:)
|
77
|
+
end
|
78
|
+
|
73
79
|
def paint(prompt, model:, size:)
|
74
80
|
payload = render_image_payload(prompt, model:, size:)
|
75
81
|
response = @connection.post images_url, payload
|
@@ -52,6 +52,10 @@ module RubyLLM
|
|
52
52
|
model_id.match?(/gemini|flash|pro|imagen/)
|
53
53
|
end
|
54
54
|
|
55
|
+
def supports_video?(model_id)
|
56
|
+
model_id.match?(/gemini/)
|
57
|
+
end
|
58
|
+
|
55
59
|
def supports_functions?(model_id)
|
56
60
|
return false if model_id.match?(/text-embedding|embedding-001|aqa|flash-lite|imagen|gemini-2\.0-flash-lite/)
|
57
61
|
|
@@ -217,6 +221,7 @@ module RubyLLM
|
|
217
221
|
modalities[:input] << 'pdf'
|
218
222
|
end
|
219
223
|
|
224
|
+
modalities[:input] << 'video' if supports_video?(model_id)
|
220
225
|
modalities[:input] << 'audio' if model_id.match?(/audio/)
|
221
226
|
modalities[:output] << 'embeddings' if model_id.match?(/embedding|gemini-embedding/)
|
222
227
|
modalities[:output] = ['image'] if model_id.match?(/imagen/)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
module Providers
|
5
|
+
class OpenAI
|
6
|
+
# Moderation methods of the OpenAI API integration
|
7
|
+
module Moderation
|
8
|
+
module_function
|
9
|
+
|
10
|
+
def moderation_url
|
11
|
+
'moderations'
|
12
|
+
end
|
13
|
+
|
14
|
+
def render_moderation_payload(input, model:)
|
15
|
+
{
|
16
|
+
model: model,
|
17
|
+
input: input
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse_moderation_response(response, model:)
|
22
|
+
data = response.body
|
23
|
+
raise Error.new(response, data.dig('error', 'message')) if data.dig('error', 'message')
|
24
|
+
|
25
|
+
RubyLLM::Moderation.new(
|
26
|
+
id: data['id'],
|
27
|
+
model: model,
|
28
|
+
results: data['results'] || []
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/ruby_llm/railtie.rb
CHANGED
data/lib/ruby_llm/version.rb
CHANGED
data/lib/ruby_llm.rb
CHANGED
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.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carmine Paolino
|
@@ -152,6 +152,9 @@ files:
|
|
152
152
|
- lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt
|
153
153
|
- lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt
|
154
154
|
- lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt
|
155
|
+
- lib/generators/ruby_llm/generator_helpers.rb
|
156
|
+
- lib/generators/ruby_llm/install/install_generator.rb
|
157
|
+
- lib/generators/ruby_llm/install/templates/add_references_to_chats_tool_calls_and_messages_migration.rb.tt
|
155
158
|
- lib/generators/ruby_llm/install/templates/chat_model.rb.tt
|
156
159
|
- lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt
|
157
160
|
- lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt
|
@@ -161,9 +164,8 @@ files:
|
|
161
164
|
- lib/generators/ruby_llm/install/templates/message_model.rb.tt
|
162
165
|
- lib/generators/ruby_llm/install/templates/model_model.rb.tt
|
163
166
|
- lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt
|
164
|
-
- lib/generators/ruby_llm/install_generator.rb
|
165
167
|
- lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt
|
166
|
-
- lib/generators/ruby_llm/upgrade_to_v1_7_generator.rb
|
168
|
+
- lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb
|
167
169
|
- lib/ruby_llm.rb
|
168
170
|
- lib/ruby_llm/active_record/acts_as.rb
|
169
171
|
- lib/ruby_llm/active_record/acts_as_legacy.rb
|
@@ -193,6 +195,7 @@ files:
|
|
193
195
|
- lib/ruby_llm/models.json
|
194
196
|
- lib/ruby_llm/models.rb
|
195
197
|
- lib/ruby_llm/models_schema.json
|
198
|
+
- lib/ruby_llm/moderation.rb
|
196
199
|
- lib/ruby_llm/provider.rb
|
197
200
|
- lib/ruby_llm/providers/anthropic.rb
|
198
201
|
- lib/ruby_llm/providers/anthropic/capabilities.rb
|
@@ -246,6 +249,7 @@ files:
|
|
246
249
|
- lib/ruby_llm/providers/openai/images.rb
|
247
250
|
- lib/ruby_llm/providers/openai/media.rb
|
248
251
|
- lib/ruby_llm/providers/openai/models.rb
|
252
|
+
- lib/ruby_llm/providers/openai/moderation.rb
|
249
253
|
- lib/ruby_llm/providers/openai/streaming.rb
|
250
254
|
- lib/ruby_llm/providers/openai/tools.rb
|
251
255
|
- lib/ruby_llm/providers/openrouter.rb
|
@@ -1,217 +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
|
-
argument :model_mappings, type: :array, default: [], banner: 'chat:ChatName message:MessageName ...'
|
16
|
-
|
17
|
-
class_option :skip_active_storage, type: :boolean, default: false,
|
18
|
-
desc: 'Skip ActiveStorage installation and attachment setup'
|
19
|
-
|
20
|
-
desc 'Creates models and migrations for RubyLLM Rails integration\n' \
|
21
|
-
'Usage: rails g ruby_llm:install [chat:ChatName] [message:MessageName] ...'
|
22
|
-
|
23
|
-
def self.next_migration_number(dirname)
|
24
|
-
::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
25
|
-
end
|
26
|
-
|
27
|
-
def migration_version
|
28
|
-
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
29
|
-
end
|
30
|
-
|
31
|
-
def postgresql?
|
32
|
-
::ActiveRecord::Base.connection.adapter_name.downcase.include?('postgresql')
|
33
|
-
rescue StandardError
|
34
|
-
false
|
35
|
-
end
|
36
|
-
|
37
|
-
def parse_model_mappings
|
38
|
-
@model_names = {
|
39
|
-
chat: 'Chat',
|
40
|
-
message: 'Message',
|
41
|
-
tool_call: 'ToolCall',
|
42
|
-
model: 'Model'
|
43
|
-
}
|
44
|
-
|
45
|
-
model_mappings.each do |mapping|
|
46
|
-
if mapping.include?(':')
|
47
|
-
key, value = mapping.split(':', 2)
|
48
|
-
@model_names[key.to_sym] = value.classify
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
@model_names
|
53
|
-
end
|
54
|
-
|
55
|
-
%i[chat message tool_call model].each do |type|
|
56
|
-
define_method("#{type}_model_name") do
|
57
|
-
@model_names ||= parse_model_mappings
|
58
|
-
@model_names[type]
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def acts_as_chat_declaration
|
63
|
-
acts_as_chat_params = []
|
64
|
-
messages_assoc = message_model_name.tableize.to_sym
|
65
|
-
model_assoc = model_model_name.underscore.to_sym
|
66
|
-
|
67
|
-
if messages_assoc != :messages
|
68
|
-
acts_as_chat_params << "messages: :#{messages_assoc}"
|
69
|
-
if message_model_name != messages_assoc.to_s.classify
|
70
|
-
acts_as_chat_params << "message_class: '#{message_model_name}'"
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
if model_assoc != :model
|
75
|
-
acts_as_chat_params << "model: :#{model_assoc}"
|
76
|
-
acts_as_chat_params << "model_class: '#{model_model_name}'" if model_model_name != model_assoc.to_s.classify
|
77
|
-
end
|
78
|
-
|
79
|
-
if acts_as_chat_params.any?
|
80
|
-
"acts_as_chat #{acts_as_chat_params.join(', ')}"
|
81
|
-
else
|
82
|
-
'acts_as_chat'
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def acts_as_message_declaration
|
87
|
-
params = []
|
88
|
-
|
89
|
-
add_message_association_params(params, :chat, chat_model_name)
|
90
|
-
add_message_association_params(params, :tool_calls, tool_call_model_name, tableize: true)
|
91
|
-
add_message_association_params(params, :model, model_model_name)
|
92
|
-
|
93
|
-
params.any? ? "acts_as_message #{params.join(', ')}" : 'acts_as_message'
|
94
|
-
end
|
95
|
-
|
96
|
-
private
|
97
|
-
|
98
|
-
def add_message_association_params(params, default_assoc, model_name, tableize: false)
|
99
|
-
assoc = tableize ? model_name.tableize.to_sym : model_name.underscore.to_sym
|
100
|
-
|
101
|
-
return if assoc == default_assoc
|
102
|
-
|
103
|
-
params << "#{default_assoc}: :#{assoc}"
|
104
|
-
expected_class = assoc.to_s.classify
|
105
|
-
params << "#{default_assoc.to_s.singularize}_class: '#{model_name}'" if model_name != expected_class
|
106
|
-
end
|
107
|
-
|
108
|
-
public
|
109
|
-
|
110
|
-
def acts_as_tool_call_declaration
|
111
|
-
acts_as_tool_call_params = []
|
112
|
-
message_assoc = message_model_name.underscore.to_sym
|
113
|
-
|
114
|
-
if message_assoc != :message
|
115
|
-
acts_as_tool_call_params << "message: :#{message_assoc}"
|
116
|
-
if message_model_name != message_assoc.to_s.classify
|
117
|
-
acts_as_tool_call_params << "message_class: '#{message_model_name}'"
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
if acts_as_tool_call_params.any?
|
122
|
-
"acts_as_tool_call #{acts_as_tool_call_params.join(', ')}"
|
123
|
-
else
|
124
|
-
'acts_as_tool_call'
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def acts_as_model_declaration
|
129
|
-
acts_as_model_params = []
|
130
|
-
chats_assoc = chat_model_name.tableize.to_sym
|
131
|
-
|
132
|
-
if chats_assoc != :chats
|
133
|
-
acts_as_model_params << "chats: :#{chats_assoc}"
|
134
|
-
acts_as_model_params << "chat_class: '#{chat_model_name}'" if chat_model_name != chats_assoc.to_s.classify
|
135
|
-
end
|
136
|
-
|
137
|
-
if acts_as_model_params.any?
|
138
|
-
"acts_as_model #{acts_as_model_params.join(', ')}"
|
139
|
-
else
|
140
|
-
'acts_as_model'
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def create_migration_files
|
145
|
-
# Create migrations with timestamps to ensure proper order
|
146
|
-
# First create chats table
|
147
|
-
migration_template 'create_chats_migration.rb.tt',
|
148
|
-
"db/migrate/create_#{chat_model_name.tableize}.rb"
|
149
|
-
|
150
|
-
# Then create messages table (must come before tool_calls due to foreign key)
|
151
|
-
sleep 1 # Ensure different timestamp
|
152
|
-
migration_template 'create_messages_migration.rb.tt',
|
153
|
-
"db/migrate/create_#{message_model_name.tableize}.rb"
|
154
|
-
|
155
|
-
# Then create tool_calls table (references messages)
|
156
|
-
sleep 1 # Ensure different timestamp
|
157
|
-
migration_template 'create_tool_calls_migration.rb.tt',
|
158
|
-
"db/migrate/create_#{tool_call_model_name.tableize}.rb"
|
159
|
-
|
160
|
-
# Create models table
|
161
|
-
sleep 1 # Ensure different timestamp
|
162
|
-
migration_template 'create_models_migration.rb.tt',
|
163
|
-
"db/migrate/create_#{model_model_name.tableize}.rb"
|
164
|
-
end
|
165
|
-
|
166
|
-
def create_model_files
|
167
|
-
template 'chat_model.rb.tt', "app/models/#{chat_model_name.underscore}.rb"
|
168
|
-
template 'message_model.rb.tt', "app/models/#{message_model_name.underscore}.rb"
|
169
|
-
template 'tool_call_model.rb.tt', "app/models/#{tool_call_model_name.underscore}.rb"
|
170
|
-
|
171
|
-
template 'model_model.rb.tt', "app/models/#{model_model_name.underscore}.rb"
|
172
|
-
end
|
173
|
-
|
174
|
-
def create_initializer
|
175
|
-
template 'initializer.rb.tt', 'config/initializers/ruby_llm.rb'
|
176
|
-
end
|
177
|
-
|
178
|
-
def install_active_storage
|
179
|
-
return if options[:skip_active_storage]
|
180
|
-
|
181
|
-
say ' Installing ActiveStorage for file attachments...', :cyan
|
182
|
-
rails_command 'active_storage:install'
|
183
|
-
end
|
184
|
-
|
185
|
-
def show_install_info
|
186
|
-
say "\n ✅ RubyLLM installed!", :green
|
187
|
-
|
188
|
-
say ' ✅ ActiveStorage configured for file attachments support', :green unless options[:skip_active_storage]
|
189
|
-
|
190
|
-
say "\n Next steps:", :yellow
|
191
|
-
say ' 1. Run: rails db:migrate'
|
192
|
-
say ' 2. Set your API keys in config/initializers/ruby_llm.rb'
|
193
|
-
|
194
|
-
say " 3. Start chatting: #{chat_model_name}.create!(model: 'gpt-4.1-nano').ask('Hello!')"
|
195
|
-
|
196
|
-
say "\n 🚀 Model registry is database-backed!", :cyan
|
197
|
-
say ' Models automatically load from the database'
|
198
|
-
say ' Pass model names as strings - RubyLLM handles the rest!'
|
199
|
-
say " Specify provider when needed: Chat.create!(model: 'gemini-2.5-flash', provider: 'vertexai')"
|
200
|
-
|
201
|
-
if options[:skip_active_storage]
|
202
|
-
say "\n 📎 Note: ActiveStorage was skipped", :yellow
|
203
|
-
say ' File attachments won\'t work without ActiveStorage.'
|
204
|
-
say ' To enable later:'
|
205
|
-
say ' 1. Run: rails active_storage:install && rails db:migrate'
|
206
|
-
say " 2. Add to your #{message_model_name} model: has_many_attached :attachments"
|
207
|
-
end
|
208
|
-
|
209
|
-
say "\n 📚 Documentation: https://rubyllm.com", :cyan
|
210
|
-
|
211
|
-
say "\n ❤️ Love RubyLLM?", :magenta
|
212
|
-
say ' • ⭐ Star on GitHub: https://github.com/crmne/ruby_llm'
|
213
|
-
say ' • 🐦 Follow for updates: https://x.com/paolino'
|
214
|
-
say "\n"
|
215
|
-
end
|
216
|
-
end
|
217
|
-
end
|
@@ -1,160 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'rails/generators'
|
4
|
-
require 'rails/generators/active_record'
|
5
|
-
|
6
|
-
module RubyLLM
|
7
|
-
class UpgradeToV17Generator < Rails::Generators::Base # rubocop:disable Style/Documentation
|
8
|
-
include Rails::Generators::Migration
|
9
|
-
|
10
|
-
namespace 'ruby_llm:upgrade_to_v1_7'
|
11
|
-
source_root File.expand_path('upgrade_to_v1_7/templates', __dir__)
|
12
|
-
|
13
|
-
# Override source_paths to include install templates
|
14
|
-
def self.source_paths
|
15
|
-
[
|
16
|
-
File.expand_path('upgrade_to_v1_7/templates', __dir__),
|
17
|
-
File.expand_path('install/templates', __dir__)
|
18
|
-
]
|
19
|
-
end
|
20
|
-
|
21
|
-
argument :model_mappings, type: :array, default: [], banner: 'chat:ChatName message:MessageName ...'
|
22
|
-
|
23
|
-
desc 'Upgrades existing RubyLLM apps to v1.7 with new Rails-like API\n' \
|
24
|
-
'Usage: rails g ruby_llm:upgrade_to_v1_7 [chat:ChatName] [message:MessageName] ...'
|
25
|
-
|
26
|
-
def self.next_migration_number(dirname)
|
27
|
-
::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
28
|
-
end
|
29
|
-
|
30
|
-
def parse_model_mappings
|
31
|
-
@model_names = {
|
32
|
-
chat: 'Chat',
|
33
|
-
message: 'Message',
|
34
|
-
tool_call: 'ToolCall',
|
35
|
-
model: 'Model'
|
36
|
-
}
|
37
|
-
|
38
|
-
model_mappings.each do |mapping|
|
39
|
-
if mapping.include?(':')
|
40
|
-
key, value = mapping.split(':', 2)
|
41
|
-
@model_names[key.to_sym] = value.classify
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
@model_names
|
46
|
-
end
|
47
|
-
|
48
|
-
%i[chat message tool_call model].each do |type|
|
49
|
-
define_method("#{type}_model_name") do
|
50
|
-
@model_names ||= parse_model_mappings
|
51
|
-
@model_names[type]
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def create_migration_file
|
56
|
-
# First check if models table exists, if not create it
|
57
|
-
unless table_exists?(model_model_name.tableize)
|
58
|
-
migration_template 'create_models_migration.rb.tt',
|
59
|
-
"db/migrate/create_#{model_model_name.tableize}.rb",
|
60
|
-
migration_version: migration_version,
|
61
|
-
model_model_name: model_model_name
|
62
|
-
|
63
|
-
sleep 1 # Ensure different timestamp
|
64
|
-
end
|
65
|
-
|
66
|
-
migration_template 'migration.rb.tt',
|
67
|
-
'db/migrate/migrate_to_ruby_llm_model_references.rb',
|
68
|
-
migration_version: migration_version,
|
69
|
-
chat_model_name: chat_model_name,
|
70
|
-
message_model_name: message_model_name,
|
71
|
-
tool_call_model_name: tool_call_model_name,
|
72
|
-
model_model_name: model_model_name
|
73
|
-
end
|
74
|
-
|
75
|
-
def create_model_file
|
76
|
-
# Check if Model file already exists
|
77
|
-
model_path = "app/models/#{model_model_name.underscore}.rb"
|
78
|
-
|
79
|
-
if File.exist?(Rails.root.join(model_path))
|
80
|
-
say_status :skip, model_path, :yellow
|
81
|
-
else
|
82
|
-
create_file model_path do
|
83
|
-
<<~RUBY
|
84
|
-
class #{model_model_name} < ApplicationRecord
|
85
|
-
#{acts_as_model_declaration}
|
86
|
-
end
|
87
|
-
RUBY
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def acts_as_model_declaration
|
93
|
-
acts_as_model_params = []
|
94
|
-
chats_assoc = chat_model_name.tableize.to_sym
|
95
|
-
|
96
|
-
if chats_assoc != :chats
|
97
|
-
acts_as_model_params << "chats: :#{chats_assoc}"
|
98
|
-
acts_as_model_params << "chat_class: '#{chat_model_name}'" if chat_model_name != chats_assoc.to_s.classify
|
99
|
-
end
|
100
|
-
|
101
|
-
if acts_as_model_params.any?
|
102
|
-
"acts_as_model #{acts_as_model_params.join(', ')}"
|
103
|
-
else
|
104
|
-
'acts_as_model'
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def update_initializer
|
109
|
-
initializer_content = File.read('config/initializers/ruby_llm.rb')
|
110
|
-
|
111
|
-
unless initializer_content.include?('config.use_new_acts_as')
|
112
|
-
inject_into_file 'config/initializers/ruby_llm.rb', before: /^end/ do
|
113
|
-
lines = ["\n # Enable the new Rails-like API", ' config.use_new_acts_as = true']
|
114
|
-
lines << " config.model_registry_class = \"#{model_model_name}\"" if model_model_name != 'Model'
|
115
|
-
lines << "\n"
|
116
|
-
lines.join("\n")
|
117
|
-
end
|
118
|
-
end
|
119
|
-
rescue Errno::ENOENT
|
120
|
-
say_status :error, 'config/initializers/ruby_llm.rb not found', :red
|
121
|
-
end
|
122
|
-
|
123
|
-
def show_next_steps
|
124
|
-
say_status :success, 'Migration created!', :green
|
125
|
-
say <<~INSTRUCTIONS
|
126
|
-
|
127
|
-
Next steps:
|
128
|
-
1. Review the migration: db/migrate/*_migrate_to_ruby_llm_model_references.rb
|
129
|
-
2. Run: rails db:migrate
|
130
|
-
3. Update config/initializers/ruby_llm.rb as shown above
|
131
|
-
4. Test your application thoroughly
|
132
|
-
|
133
|
-
The migration will:
|
134
|
-
- Create the Models table if it doesn't exist
|
135
|
-
- Load all models from models.json
|
136
|
-
- Migrate your existing data to use foreign keys
|
137
|
-
- Preserve all existing data (string columns renamed to model_id_string)
|
138
|
-
|
139
|
-
INSTRUCTIONS
|
140
|
-
end
|
141
|
-
|
142
|
-
private
|
143
|
-
|
144
|
-
def migration_version
|
145
|
-
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
146
|
-
end
|
147
|
-
|
148
|
-
def table_exists?(table_name)
|
149
|
-
::ActiveRecord::Base.connection.table_exists?(table_name)
|
150
|
-
rescue StandardError
|
151
|
-
false
|
152
|
-
end
|
153
|
-
|
154
|
-
def postgresql?
|
155
|
-
::ActiveRecord::Base.connection.adapter_name.downcase.include?('postgresql')
|
156
|
-
rescue StandardError
|
157
|
-
false
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|