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.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -2
  3. data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +30 -18
  4. data/lib/generators/ruby_llm/generator_helpers.rb +129 -0
  5. data/lib/generators/ruby_llm/install/install_generator.rb +110 -0
  6. data/lib/generators/ruby_llm/install/templates/add_references_to_chats_tool_calls_and_messages_migration.rb.tt +9 -0
  7. data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +2 -3
  8. data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +3 -6
  9. data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +3 -6
  10. data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +4 -5
  11. data/lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt +21 -13
  12. data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +121 -0
  13. data/lib/ruby_llm/attachment.rb +5 -0
  14. data/lib/ruby_llm/configuration.rb +2 -0
  15. data/lib/ruby_llm/mime_type.rb +4 -0
  16. data/lib/ruby_llm/model/info.rb +4 -0
  17. data/lib/ruby_llm/models.json +780 -511
  18. data/lib/ruby_llm/models.rb +7 -3
  19. data/lib/ruby_llm/moderation.rb +56 -0
  20. data/lib/ruby_llm/provider.rb +6 -0
  21. data/lib/ruby_llm/providers/gemini/capabilities.rb +5 -0
  22. data/lib/ruby_llm/providers/openai/moderation.rb +34 -0
  23. data/lib/ruby_llm/providers/openai.rb +1 -0
  24. data/lib/ruby_llm/railtie.rb +1 -1
  25. data/lib/ruby_llm/version.rb +1 -1
  26. data/lib/ruby_llm.rb +4 -0
  27. metadata +7 -3
  28. data/lib/generators/ruby_llm/install_generator.rb +0 -217
  29. data/lib/generators/ruby_llm/upgrade_to_v1_7_generator.rb +0 -160
@@ -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
@@ -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
@@ -7,6 +7,7 @@ module RubyLLM
7
7
  include OpenAI::Chat
8
8
  include OpenAI::Embeddings
9
9
  include OpenAI::Models
10
+ include OpenAI::Moderation
10
11
  include OpenAI::Streaming
11
12
  include OpenAI::Tools
12
13
  include OpenAI::Images
@@ -5,7 +5,7 @@ module RubyLLM
5
5
  class Railtie < Rails::Railtie
6
6
  initializer 'ruby_llm.inflections' do
7
7
  ActiveSupport::Inflector.inflections(:en) do |inflect|
8
- inflect.acronym 'LLM'
8
+ inflect.acronym 'RubyLLM'
9
9
  end
10
10
  end
11
11
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyLLM
4
- VERSION = '1.7.0'
4
+ VERSION = '1.8.0'
5
5
  end
data/lib/ruby_llm.rb CHANGED
@@ -48,6 +48,10 @@ module RubyLLM
48
48
  Embedding.embed(...)
49
49
  end
50
50
 
51
+ def moderate(...)
52
+ Moderation.moderate(...)
53
+ end
54
+
51
55
  def paint(...)
52
56
  Image.paint(...)
53
57
  end
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.7.0
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