ruby_llm 1.7.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- 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 +104 -0
- data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +3 -3
- data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +6 -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 +5 -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/version.rb +1 -1
- metadata +4 -3
- data/lib/generators/ruby_llm/install_generator.rb +0 -217
- data/lib/generators/ruby_llm/upgrade_to_v1_7_generator.rb +0 -160
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ceecb8e7cd289f58ac1f76648306f61b54482a9b6f845a2f08b3303ee746a634
|
4
|
+
data.tar.gz: 3c075c03ccd1d7b841b99b58a1a4cda9289370b8dea039070cd78ede2d18a41c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16ac70cc9787c0d845b5ad4c62171cb225752f160506864b07fdb0b2c1b27adc147e9799a1f13e9d7a5169595d247e5b6bc0c1083dc523647aa13e750b1d6f9d
|
7
|
+
data.tar.gz: 80543faec7119044c345f6751059e05e4cf76457ad3b288da38ea7c4e1338570fcbca1c8ad8e9dad84fb7ac5983998fd1ceba481b2e244d3542f59481a4d0e7f
|
data/README.md
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
|
10
10
|
Battle tested at [<picture><source media="(prefers-color-scheme: dark)" srcset="https://chatwithwork.com/logotype-dark.svg"><img src="https://chatwithwork.com/logotype.svg" alt="Chat with Work" height="30" align="absmiddle"></picture>](https://chatwithwork.com) — *Claude Code for your documents*
|
11
11
|
|
12
|
-
[](https://badge.fury.io/rb/ruby_llm)
|
13
13
|
[](https://github.com/testdouble/standard)
|
14
14
|
[](https://rubygems.org/gems/ruby_llm)
|
15
15
|
[](https://codecov.io/gh/crmne/ruby_llm)
|
@@ -38,35 +38,39 @@ module RubyLLM
|
|
38
38
|
@model_names ||= parse_model_mappings
|
39
39
|
@model_names[type]
|
40
40
|
end
|
41
|
+
|
42
|
+
define_method("#{type}_table_name") do
|
43
|
+
table_name_for(send("#{type}_model_name"))
|
44
|
+
end
|
41
45
|
end
|
42
46
|
|
43
47
|
def create_views
|
44
48
|
# Chat views
|
45
|
-
template 'views/chats/index.html.erb', "app/views/#{
|
46
|
-
template 'views/chats/new.html.erb', "app/views/#{
|
47
|
-
template 'views/chats/show.html.erb', "app/views/#{
|
49
|
+
template 'views/chats/index.html.erb', "app/views/#{chat_table_name}/index.html.erb"
|
50
|
+
template 'views/chats/new.html.erb', "app/views/#{chat_table_name}/new.html.erb"
|
51
|
+
template 'views/chats/show.html.erb', "app/views/#{chat_table_name}/show.html.erb"
|
48
52
|
template 'views/chats/_chat.html.erb',
|
49
|
-
"app/views/#{
|
50
|
-
template 'views/chats/_form.html.erb', "app/views/#{
|
53
|
+
"app/views/#{chat_table_name}/_#{chat_model_name.underscore}.html.erb"
|
54
|
+
template 'views/chats/_form.html.erb', "app/views/#{chat_table_name}/_form.html.erb"
|
51
55
|
|
52
56
|
# Message views
|
53
57
|
template 'views/messages/_message.html.erb',
|
54
|
-
"app/views/#{
|
55
|
-
template 'views/messages/_form.html.erb', "app/views/#{
|
58
|
+
"app/views/#{message_table_name}/_#{message_model_name.underscore}.html.erb"
|
59
|
+
template 'views/messages/_form.html.erb', "app/views/#{message_table_name}/_form.html.erb"
|
56
60
|
template 'views/messages/create.turbo_stream.erb',
|
57
|
-
"app/views/#{
|
61
|
+
"app/views/#{message_table_name}/create.turbo_stream.erb"
|
58
62
|
|
59
63
|
# Model views
|
60
|
-
template 'views/models/index.html.erb', "app/views/#{
|
61
|
-
template 'views/models/show.html.erb', "app/views/#{
|
64
|
+
template 'views/models/index.html.erb', "app/views/#{model_table_name}/index.html.erb"
|
65
|
+
template 'views/models/show.html.erb', "app/views/#{model_table_name}/show.html.erb"
|
62
66
|
template 'views/models/_model.html.erb',
|
63
|
-
"app/views/#{
|
67
|
+
"app/views/#{model_table_name}/_#{model_model_name.underscore}.html.erb"
|
64
68
|
end
|
65
69
|
|
66
70
|
def create_controllers
|
67
|
-
template 'controllers/chats_controller.rb', "app/controllers/#{
|
68
|
-
template 'controllers/messages_controller.rb', "app/controllers/#{
|
69
|
-
template 'controllers/models_controller.rb', "app/controllers/#{
|
71
|
+
template 'controllers/chats_controller.rb', "app/controllers/#{chat_table_name}_controller.rb"
|
72
|
+
template 'controllers/messages_controller.rb', "app/controllers/#{message_table_name}_controller.rb"
|
73
|
+
template 'controllers/models_controller.rb', "app/controllers/#{model_table_name}_controller.rb"
|
70
74
|
end
|
71
75
|
|
72
76
|
def create_jobs
|
@@ -75,7 +79,7 @@ module RubyLLM
|
|
75
79
|
|
76
80
|
def add_routes
|
77
81
|
model_routes = <<~ROUTES.strip
|
78
|
-
resources :#{
|
82
|
+
resources :#{model_table_name}, only: [:index, :show] do
|
79
83
|
collection do
|
80
84
|
post :refresh
|
81
85
|
end
|
@@ -83,8 +87,8 @@ module RubyLLM
|
|
83
87
|
ROUTES
|
84
88
|
route model_routes
|
85
89
|
chat_routes = <<~ROUTES.strip
|
86
|
-
resources :#{
|
87
|
-
resources :#{
|
90
|
+
resources :#{chat_table_name} do
|
91
|
+
resources :#{message_table_name}, only: [:create]
|
88
92
|
end
|
89
93
|
ROUTES
|
90
94
|
route chat_routes
|
@@ -107,9 +111,17 @@ module RubyLLM
|
|
107
111
|
return unless behavior == :invoke
|
108
112
|
|
109
113
|
say "\n ✅ Chat UI installed!", :green
|
110
|
-
say "\n Start your server and visit http://localhost:3000/#{
|
114
|
+
say "\n Start your server and visit http://localhost:3000/#{chat_table_name}", :cyan
|
111
115
|
say "\n"
|
112
116
|
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def table_name_for(model_name)
|
121
|
+
# Convert namespaced model names to proper table names
|
122
|
+
# e.g., "Assistant::Chat" -> "assistant_chats" (not "assistant/chats")
|
123
|
+
model_name.underscore.pluralize.tr('/', '_')
|
124
|
+
end
|
113
125
|
end
|
114
126
|
end
|
115
127
|
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
# Shared helpers for RubyLLM generators
|
5
|
+
module GeneratorHelpers
|
6
|
+
def parse_model_mappings
|
7
|
+
@model_names = {
|
8
|
+
chat: 'Chat',
|
9
|
+
message: 'Message',
|
10
|
+
tool_call: 'ToolCall',
|
11
|
+
model: 'Model'
|
12
|
+
}
|
13
|
+
|
14
|
+
model_mappings.each do |mapping|
|
15
|
+
if mapping.include?(':')
|
16
|
+
key, value = mapping.split(':', 2)
|
17
|
+
@model_names[key.to_sym] = value.classify
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
@model_names
|
22
|
+
end
|
23
|
+
|
24
|
+
%i[chat message tool_call model].each do |type|
|
25
|
+
define_method("#{type}_model_name") do
|
26
|
+
@model_names ||= parse_model_mappings
|
27
|
+
@model_names[type]
|
28
|
+
end
|
29
|
+
|
30
|
+
define_method("#{type}_table_name") do
|
31
|
+
table_name_for(send("#{type}_model_name"))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def acts_as_chat_declaration
|
36
|
+
params = []
|
37
|
+
|
38
|
+
add_association_params(params, :messages, message_table_name, message_model_name, plural: true)
|
39
|
+
add_association_params(params, :model, model_table_name, model_model_name)
|
40
|
+
|
41
|
+
"acts_as_chat#{" #{params.join(', ')}" if params.any?}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def acts_as_message_declaration
|
45
|
+
params = []
|
46
|
+
|
47
|
+
add_association_params(params, :chat, chat_table_name, chat_model_name)
|
48
|
+
add_association_params(params, :tool_calls, tool_call_table_name, tool_call_model_name, plural: true)
|
49
|
+
add_association_params(params, :model, model_table_name, model_model_name)
|
50
|
+
|
51
|
+
"acts_as_message#{" #{params.join(', ')}" if params.any?}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def acts_as_model_declaration
|
55
|
+
params = []
|
56
|
+
|
57
|
+
add_association_params(params, :chats, chat_table_name, chat_model_name, plural: true)
|
58
|
+
|
59
|
+
"acts_as_model#{" #{params.join(', ')}" if params.any?}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def acts_as_tool_call_declaration
|
63
|
+
params = []
|
64
|
+
|
65
|
+
add_association_params(params, :message, message_table_name, message_model_name)
|
66
|
+
|
67
|
+
"acts_as_tool_call#{" #{params.join(', ')}" if params.any?}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_namespace_modules
|
71
|
+
namespaces = []
|
72
|
+
|
73
|
+
[chat_model_name, message_model_name, tool_call_model_name, model_model_name].each do |model_name|
|
74
|
+
if model_name.include?('::')
|
75
|
+
namespace = model_name.split('::').first
|
76
|
+
namespaces << namespace unless namespaces.include?(namespace)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
namespaces.each do |namespace|
|
81
|
+
module_path = "app/models/#{namespace.underscore}.rb"
|
82
|
+
next if File.exist?(Rails.root.join(module_path))
|
83
|
+
|
84
|
+
create_file module_path do
|
85
|
+
<<~RUBY
|
86
|
+
module #{namespace}
|
87
|
+
def self.table_name_prefix
|
88
|
+
"#{namespace.underscore}_"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
RUBY
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def migration_version
|
97
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
98
|
+
end
|
99
|
+
|
100
|
+
def postgresql?
|
101
|
+
::ActiveRecord::Base.connection.adapter_name.downcase.include?('postgresql')
|
102
|
+
rescue StandardError
|
103
|
+
false
|
104
|
+
end
|
105
|
+
|
106
|
+
def table_exists?(table_name)
|
107
|
+
::ActiveRecord::Base.connection.table_exists?(table_name)
|
108
|
+
rescue StandardError
|
109
|
+
false
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def add_association_params(params, default_assoc, table_name, model_name, plural: false)
|
115
|
+
assoc = plural ? table_name.to_sym : table_name.singularize.to_sym
|
116
|
+
|
117
|
+
return if assoc == default_assoc
|
118
|
+
|
119
|
+
params << "#{default_assoc}: :#{assoc}"
|
120
|
+
params << "#{default_assoc.to_s.singularize}_class: '#{model_name}'" if model_name != assoc.to_s.classify
|
121
|
+
end
|
122
|
+
|
123
|
+
def table_name_for(model_name)
|
124
|
+
# Convert namespaced model names to proper table names
|
125
|
+
# e.g., "Assistant::Chat" -> "assistant_chats" (not "assistant/chats")
|
126
|
+
model_name.underscore.pluralize.tr('/', '_')
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators'
|
4
|
+
require 'rails/generators/active_record'
|
5
|
+
require_relative '../generator_helpers'
|
6
|
+
|
7
|
+
module RubyLLM
|
8
|
+
# Generator for RubyLLM Rails models and migrations
|
9
|
+
class InstallGenerator < Rails::Generators::Base
|
10
|
+
include Rails::Generators::Migration
|
11
|
+
include RubyLLM::GeneratorHelpers
|
12
|
+
|
13
|
+
namespace 'ruby_llm:install'
|
14
|
+
|
15
|
+
source_root File.expand_path('templates', __dir__)
|
16
|
+
|
17
|
+
argument :model_mappings, type: :array, default: [], banner: 'chat:ChatName message:MessageName ...'
|
18
|
+
|
19
|
+
class_option :skip_active_storage, type: :boolean, default: false,
|
20
|
+
desc: 'Skip ActiveStorage installation and attachment setup'
|
21
|
+
|
22
|
+
desc 'Creates models and migrations for RubyLLM Rails integration\n' \
|
23
|
+
'Usage: rails g ruby_llm:install [chat:ChatName] [message:MessageName] ...'
|
24
|
+
|
25
|
+
def self.next_migration_number(dirname)
|
26
|
+
::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_migration_files
|
30
|
+
# Create migrations with timestamps to ensure proper order
|
31
|
+
# First create chats table
|
32
|
+
migration_template 'create_chats_migration.rb.tt',
|
33
|
+
"db/migrate/create_#{chat_table_name}.rb"
|
34
|
+
|
35
|
+
# Then create messages table (must come before tool_calls due to foreign key)
|
36
|
+
sleep 1 # Ensure different timestamp
|
37
|
+
migration_template 'create_messages_migration.rb.tt',
|
38
|
+
"db/migrate/create_#{message_table_name}.rb"
|
39
|
+
|
40
|
+
# Then create tool_calls table (references messages)
|
41
|
+
sleep 1 # Ensure different timestamp
|
42
|
+
migration_template 'create_tool_calls_migration.rb.tt',
|
43
|
+
"db/migrate/create_#{tool_call_table_name}.rb"
|
44
|
+
|
45
|
+
# Create models table
|
46
|
+
sleep 1 # Ensure different timestamp
|
47
|
+
migration_template 'create_models_migration.rb.tt',
|
48
|
+
"db/migrate/create_#{model_table_name}.rb"
|
49
|
+
end
|
50
|
+
|
51
|
+
def create_model_files
|
52
|
+
create_namespace_modules
|
53
|
+
|
54
|
+
template 'chat_model.rb.tt', "app/models/#{chat_model_name.underscore}.rb"
|
55
|
+
template 'message_model.rb.tt', "app/models/#{message_model_name.underscore}.rb"
|
56
|
+
template 'tool_call_model.rb.tt', "app/models/#{tool_call_model_name.underscore}.rb"
|
57
|
+
|
58
|
+
template 'model_model.rb.tt', "app/models/#{model_model_name.underscore}.rb"
|
59
|
+
end
|
60
|
+
|
61
|
+
def create_initializer
|
62
|
+
template 'initializer.rb.tt', 'config/initializers/ruby_llm.rb'
|
63
|
+
end
|
64
|
+
|
65
|
+
def install_active_storage
|
66
|
+
return if options[:skip_active_storage]
|
67
|
+
|
68
|
+
say ' Installing ActiveStorage for file attachments...', :cyan
|
69
|
+
rails_command 'active_storage:install'
|
70
|
+
end
|
71
|
+
|
72
|
+
def show_install_info
|
73
|
+
say "\n ✅ RubyLLM installed!", :green
|
74
|
+
|
75
|
+
say ' ✅ ActiveStorage configured for file attachments support', :green unless options[:skip_active_storage]
|
76
|
+
|
77
|
+
say "\n Next steps:", :yellow
|
78
|
+
say ' 1. Run: rails db:migrate'
|
79
|
+
say ' 2. Set your API keys in config/initializers/ruby_llm.rb'
|
80
|
+
|
81
|
+
say " 3. Start chatting: #{chat_model_name}.create!(model: 'gpt-4.1-nano').ask('Hello!')"
|
82
|
+
|
83
|
+
say "\n 🚀 Model registry is database-backed!", :cyan
|
84
|
+
say ' Models automatically load from the database'
|
85
|
+
say ' Pass model names as strings - RubyLLM handles the rest!'
|
86
|
+
say " Specify provider when needed: Chat.create!(model: 'gemini-2.5-flash', provider: 'vertexai')"
|
87
|
+
|
88
|
+
if options[:skip_active_storage]
|
89
|
+
say "\n 📎 Note: ActiveStorage was skipped", :yellow
|
90
|
+
say ' File attachments won\'t work without ActiveStorage.'
|
91
|
+
say ' To enable later:'
|
92
|
+
say ' 1. Run: rails active_storage:install && rails db:migrate'
|
93
|
+
say " 2. Add to your #{message_model_name} model: has_many_attached :attachments"
|
94
|
+
end
|
95
|
+
|
96
|
+
say "\n 📚 Documentation: https://rubyllm.com", :cyan
|
97
|
+
|
98
|
+
say "\n ❤️ Love RubyLLM?", :magenta
|
99
|
+
say ' • ⭐ Star on GitHub: https://github.com/crmne/ruby_llm'
|
100
|
+
say ' • 🐦 Follow for updates: https://x.com/paolino'
|
101
|
+
say "\n"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
|
-
class Create<%= chat_model_name.pluralize %> < ActiveRecord::Migration<%= migration_version %>
|
1
|
+
class Create<%= chat_model_name.gsub('::', '').pluralize %> < ActiveRecord::Migration<%= migration_version %>
|
2
2
|
def change
|
3
|
-
create_table :<%=
|
4
|
-
t.references :<%=
|
3
|
+
create_table :<%= chat_table_name %> do |t|
|
4
|
+
t.references :<%= model_table_name.singularize %>, foreign_key: true
|
5
5
|
t.timestamps
|
6
6
|
end
|
7
7
|
end
|
@@ -1,16 +1,16 @@
|
|
1
|
-
class Create<%= message_model_name.pluralize %> < ActiveRecord::Migration<%= migration_version %>
|
1
|
+
class Create<%= message_model_name.gsub('::', '').pluralize %> < ActiveRecord::Migration<%= migration_version %>
|
2
2
|
def change
|
3
|
-
create_table :<%=
|
4
|
-
t.references :<%=
|
3
|
+
create_table :<%= message_table_name %> do |t|
|
4
|
+
t.references :<%= chat_table_name.singularize %>, null: false, foreign_key: true
|
5
5
|
t.string :role, null: false
|
6
6
|
t.text :content
|
7
|
-
t.references :<%=
|
7
|
+
t.references :<%= model_table_name.singularize %>, foreign_key: true
|
8
8
|
t.integer :input_tokens
|
9
9
|
t.integer :output_tokens
|
10
|
-
t.references :<%=
|
10
|
+
t.references :<%= tool_call_table_name.singularize %>, foreign_key: true
|
11
11
|
t.timestamps
|
12
12
|
end
|
13
13
|
|
14
|
-
add_index :<%=
|
14
|
+
add_index :<%= message_table_name %>, :role
|
15
15
|
end
|
16
16
|
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
class Create<%= model_model_name.pluralize %> < ActiveRecord::Migration<%= migration_version %>
|
1
|
+
class Create<%= model_model_name.gsub('::', '').pluralize %> < ActiveRecord::Migration<%= migration_version %>
|
2
2
|
def change
|
3
|
-
create_table :<%=
|
3
|
+
create_table :<%= model_table_name %> do |t|
|
4
4
|
t.string :model_id, null: false
|
5
5
|
t.string :name, null: false
|
6
6
|
t.string :provider, null: false
|
@@ -34,10 +34,7 @@ class Create<%= model_model_name.pluralize %> < ActiveRecord::Migration<%= migra
|
|
34
34
|
# Load models from JSON
|
35
35
|
say_with_time "Loading models from models.json" do
|
36
36
|
RubyLLM.models.load_from_json!
|
37
|
-
|
38
|
-
model_class.save_to_database
|
39
|
-
|
40
|
-
"Loaded #{model_class.count} models"
|
37
|
+
<%= model_model_name %>.save_to_database
|
41
38
|
end
|
42
39
|
end
|
43
40
|
end
|
@@ -1,15 +1,15 @@
|
|
1
1
|
<%#- # Migration for creating tool_calls table with database-specific JSON handling -%>
|
2
|
-
class Create<%= tool_call_model_name.pluralize %> < ActiveRecord::Migration<%= migration_version %>
|
2
|
+
class Create<%= tool_call_model_name.gsub('::', '').pluralize %> < ActiveRecord::Migration<%= migration_version %>
|
3
3
|
def change
|
4
|
-
create_table :<%=
|
5
|
-
t.references :<%=
|
4
|
+
create_table :<%= tool_call_table_name %> do |t|
|
5
|
+
t.references :<%= message_table_name.singularize %>, null: false, foreign_key: true
|
6
6
|
t.string :tool_call_id, null: false
|
7
7
|
t.string :name, null: false
|
8
8
|
t.<%= postgresql? ? 'jsonb' : 'json' %> :arguments, default: {}
|
9
9
|
t.timestamps
|
10
10
|
end
|
11
11
|
|
12
|
-
add_index :<%=
|
13
|
-
add_index :<%=
|
12
|
+
add_index :<%= tool_call_table_name %>, :tool_call_id, unique: true
|
13
|
+
add_index :<%= tool_call_table_name %>, :name
|
14
14
|
end
|
15
15
|
end
|
@@ -3,36 +3,44 @@ class MigrateToRubyLLMModelReferences < ActiveRecord::Migration<%= migration_ver
|
|
3
3
|
model_class = <%= model_model_name %>
|
4
4
|
chat_class = <%= chat_model_name %>
|
5
5
|
message_class = <%= message_model_name %>
|
6
|
+
<% if @model_table_already_existed %>
|
7
|
+
# Load models from models.json if Model table already existed
|
8
|
+
say_with_time "Loading models from models.json" do
|
9
|
+
RubyLLM.models.load_from_json!
|
10
|
+
model_class.save_to_database
|
11
|
+
"Loaded #{model_class.count} models"
|
12
|
+
end
|
13
|
+
<% end %>
|
6
14
|
|
7
15
|
# Then check for any models in existing data that aren't in models.json
|
8
16
|
say_with_time "Checking for additional models in existing data" do
|
9
|
-
collect_and_create_models(chat_class, :<%=
|
10
|
-
collect_and_create_models(message_class, :<%=
|
17
|
+
collect_and_create_models(chat_class, :<%= chat_table_name %>, model_class)
|
18
|
+
collect_and_create_models(message_class, :<%= message_table_name %>, model_class)
|
11
19
|
model_class.count
|
12
20
|
end
|
13
21
|
|
14
22
|
# Migrate foreign keys
|
15
|
-
migrate_foreign_key(:<%=
|
16
|
-
migrate_foreign_key(:<%=
|
23
|
+
migrate_foreign_key(:<%= chat_table_name %>, chat_class, model_class, :<%= model_table_name.singularize %>)
|
24
|
+
migrate_foreign_key(:<%= message_table_name %>, message_class, model_class, :<%= model_table_name.singularize %>)
|
17
25
|
end
|
18
26
|
|
19
27
|
def down
|
20
28
|
# Remove foreign key references
|
21
|
-
if column_exists?(:<%=
|
22
|
-
remove_reference :<%=
|
29
|
+
if column_exists?(:<%= message_table_name %>, :<%= model_table_name.singularize %>_id)
|
30
|
+
remove_reference :<%= message_table_name %>, :<%= model_table_name.singularize %>, foreign_key: true
|
23
31
|
end
|
24
32
|
|
25
|
-
if column_exists?(:<%=
|
26
|
-
remove_reference :<%=
|
33
|
+
if column_exists?(:<%= chat_table_name %>, :<%= model_table_name.singularize %>_id)
|
34
|
+
remove_reference :<%= chat_table_name %>, :<%= model_table_name.singularize %>, foreign_key: true
|
27
35
|
end
|
28
36
|
|
29
37
|
# Restore original model_id string columns
|
30
|
-
if column_exists?(:<%=
|
31
|
-
rename_column :<%=
|
38
|
+
if column_exists?(:<%= message_table_name %>, :model_id_string)
|
39
|
+
rename_column :<%= message_table_name %>, :model_id_string, :model_id
|
32
40
|
end
|
33
41
|
|
34
|
-
if column_exists?(:<%=
|
35
|
-
rename_column :<%=
|
42
|
+
if column_exists?(:<%= chat_table_name %>, :model_id_string)
|
43
|
+
rename_column :<%= chat_table_name %>, :model_id_string, :model_id
|
36
44
|
end
|
37
45
|
end
|
38
46
|
|
@@ -134,4 +142,4 @@ class MigrateToRubyLLMModelReferences < ActiveRecord::Migration<%= migration_ver
|
|
134
142
|
model_class.find_by(model_id: model_id)
|
135
143
|
end
|
136
144
|
end
|
137
|
-
end
|
145
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators'
|
4
|
+
require 'rails/generators/active_record'
|
5
|
+
require_relative '../generator_helpers'
|
6
|
+
|
7
|
+
module RubyLLM
|
8
|
+
class UpgradeToV17Generator < Rails::Generators::Base # rubocop:disable Style/Documentation
|
9
|
+
include Rails::Generators::Migration
|
10
|
+
include RubyLLM::GeneratorHelpers
|
11
|
+
|
12
|
+
namespace 'ruby_llm:upgrade_to_v1_7'
|
13
|
+
source_root File.expand_path('templates', __dir__)
|
14
|
+
|
15
|
+
# Override source_paths to include install templates
|
16
|
+
def self.source_paths
|
17
|
+
[
|
18
|
+
File.expand_path('templates', __dir__),
|
19
|
+
File.expand_path('../install/templates', __dir__)
|
20
|
+
]
|
21
|
+
end
|
22
|
+
|
23
|
+
argument :model_mappings, type: :array, default: [], banner: 'chat:ChatName message:MessageName ...'
|
24
|
+
|
25
|
+
desc 'Upgrades existing RubyLLM apps to v1.7 with new Rails-like API\n' \
|
26
|
+
'Usage: rails g ruby_llm:upgrade_to_v1_7 [chat:ChatName] [message:MessageName] ...'
|
27
|
+
|
28
|
+
def self.next_migration_number(dirname)
|
29
|
+
::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_migration_file
|
33
|
+
@model_table_already_existed = table_exists?(table_name_for(model_model_name))
|
34
|
+
|
35
|
+
# First check if models table exists, if not create it
|
36
|
+
unless @model_table_already_existed
|
37
|
+
migration_template 'create_models_migration.rb.tt',
|
38
|
+
"db/migrate/create_#{table_name_for(model_model_name)}.rb",
|
39
|
+
migration_version: migration_version,
|
40
|
+
model_model_name: model_model_name
|
41
|
+
|
42
|
+
sleep 1 # Ensure different timestamp
|
43
|
+
end
|
44
|
+
|
45
|
+
migration_template 'migration.rb.tt',
|
46
|
+
'db/migrate/migrate_to_ruby_llm_model_references.rb',
|
47
|
+
migration_version: migration_version,
|
48
|
+
chat_model_name: chat_model_name,
|
49
|
+
message_model_name: message_model_name,
|
50
|
+
tool_call_model_name: tool_call_model_name,
|
51
|
+
model_model_name: model_model_name,
|
52
|
+
model_table_already_existed: @model_table_already_existed
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_model_file
|
56
|
+
create_namespace_modules
|
57
|
+
|
58
|
+
template 'model_model.rb.tt', "app/models/#{model_model_name.underscore}.rb"
|
59
|
+
end
|
60
|
+
|
61
|
+
def update_existing_models
|
62
|
+
update_model_acts_as(chat_model_name, 'acts_as_chat', acts_as_chat_declaration)
|
63
|
+
update_model_acts_as(message_model_name, 'acts_as_message', acts_as_message_declaration)
|
64
|
+
update_model_acts_as(tool_call_model_name, 'acts_as_tool_call', acts_as_tool_call_declaration)
|
65
|
+
end
|
66
|
+
|
67
|
+
def update_initializer
|
68
|
+
initializer_path = 'config/initializers/ruby_llm.rb'
|
69
|
+
|
70
|
+
unless File.exist?(initializer_path)
|
71
|
+
say_status :warning, 'No initializer found. Creating one...', :yellow
|
72
|
+
template 'initializer.rb.tt', initializer_path
|
73
|
+
return
|
74
|
+
end
|
75
|
+
|
76
|
+
initializer_content = File.read(initializer_path)
|
77
|
+
|
78
|
+
return if initializer_content.include?('config.use_new_acts_as')
|
79
|
+
|
80
|
+
inject_into_file initializer_path, before: /^end/ do
|
81
|
+
lines = ["\n # Enable the new Rails-like API", ' config.use_new_acts_as = true']
|
82
|
+
lines << " config.model_registry_class = \"#{model_model_name}\"" if model_model_name != 'Model'
|
83
|
+
lines << "\n"
|
84
|
+
lines.join("\n")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def show_next_steps
|
89
|
+
say_status :success, 'Upgrade prepared!', :green
|
90
|
+
say <<~INSTRUCTIONS
|
91
|
+
|
92
|
+
Next steps:
|
93
|
+
1. Review the generated migrations
|
94
|
+
2. Run: rails db:migrate
|
95
|
+
3. Update your code to use the new API: #{chat_model_name}.create! now has the same signature as RubyLLM.chat
|
96
|
+
|
97
|
+
⚠️ If you get "undefined method 'acts_as_model'" during migration:
|
98
|
+
Add this to config/application.rb BEFORE your Application class:
|
99
|
+
|
100
|
+
RubyLLM.configure do |config|
|
101
|
+
config.use_new_acts_as = true
|
102
|
+
end
|
103
|
+
|
104
|
+
📚 See the full migration guide: https://rubyllm.com/upgrading-to-1-7/
|
105
|
+
|
106
|
+
INSTRUCTIONS
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def update_model_acts_as(model_name, old_acts_as, new_acts_as)
|
112
|
+
model_path = "app/models/#{model_name.underscore}.rb"
|
113
|
+
return unless File.exist?(Rails.root.join(model_path))
|
114
|
+
|
115
|
+
content = File.read(Rails.root.join(model_path))
|
116
|
+
return unless content.match?(/^\s*#{old_acts_as}/)
|
117
|
+
|
118
|
+
gsub_file model_path, /^\s*#{old_acts_as}.*$/, " #{new_acts_as}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
data/lib/ruby_llm/version.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.7.
|
4
|
+
version: 1.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carmine Paolino
|
@@ -152,6 +152,8 @@ 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
|
155
157
|
- lib/generators/ruby_llm/install/templates/chat_model.rb.tt
|
156
158
|
- lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt
|
157
159
|
- lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt
|
@@ -161,9 +163,8 @@ files:
|
|
161
163
|
- lib/generators/ruby_llm/install/templates/message_model.rb.tt
|
162
164
|
- lib/generators/ruby_llm/install/templates/model_model.rb.tt
|
163
165
|
- lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt
|
164
|
-
- lib/generators/ruby_llm/install_generator.rb
|
165
166
|
- lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt
|
166
|
-
- lib/generators/ruby_llm/upgrade_to_v1_7_generator.rb
|
167
|
+
- lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb
|
167
168
|
- lib/ruby_llm.rb
|
168
169
|
- lib/ruby_llm/active_record/acts_as.rb
|
169
170
|
- lib/ruby_llm/active_record/acts_as_legacy.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
|