dify_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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 26ba52438e460fd7cb45ccae2718eef3f210288bda1c0964a9a3533b3abbb104
4
- data.tar.gz: 36fc10f694fa81a5ce3cc1736a15bb7cbf7fa8f90c835f010ed0d3a4dd2ce924
3
+ metadata.gz: 80cc1d17f6a70b28e6f8cfe72ceb5ac79398e5b569f2519a0ba9fe34cba3ae30
4
+ data.tar.gz: 429561f0d6191e55248ef5610eb42a52dc70acfb97bea116ee70ae53a0919c5f
5
5
  SHA512:
6
- metadata.gz: cded1cb65b8e6e3e5f21bcfcdc7d9acf1d2d9c448fa2f2852e06ff58fc85ec9577dc57cdd6e35bba3c61585cb78c33a534621e987d182cb42b8e48cc5fa8af3e
7
- data.tar.gz: be43b11c4c108f31171ddb0cf27f4335ac506ac74fd25fd970e6cdc09aae182cd1678a91e4c322a5976512eca6572363c19f0bb8538efa97a04dd32a29a3072f
6
+ metadata.gz: f4477d96e5572468bb8128105d97eac680e661da7182d7638e789cf8a6908a48ac9719e884995f39eb40093edc10986afee5137f238129a80d5726bcd0881f8f
7
+ data.tar.gz: ea459447ae6000b3f044da995516c9aeed5683c2c8801ab633601d8c530470d5b35f8076b4592525dc501b36d8a867224be2fcb47cbedbbb3d6ba6bd4dc3b863
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
- [![Gem Version](https://badge.fury.io/rb/ruby_llm.svg?a=8)](https://badge.fury.io/rb/ruby_llm)
12
+ [![Gem Version](https://badge.fury.io/rb/ruby_llm.svg?a=10)](https://badge.fury.io/rb/ruby_llm)
13
13
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
14
14
  [![Gem Downloads](https://img.shields.io/gem/dt/ruby_llm)](https://rubygems.org/gems/ruby_llm)
15
15
  [![codecov](https://codecov.io/gh/crmne/ruby_llm/branch/main/graph/badge.svg?a=2)](https://codecov.io/gh/crmne/ruby_llm)
@@ -41,6 +41,7 @@ chat.ask "What's the best way to learn Ruby?"
41
41
  ```ruby
42
42
  # Analyze any file type
43
43
  chat.ask "What's in this image?", with: "ruby_conf.jpg"
44
+ chat.ask "What's happening in this video?", with: "video.mp4"
44
45
  chat.ask "Describe this meeting", with: "meeting.wav"
45
46
  chat.ask "Summarize this document", with: "contract.pdf"
46
47
  chat.ask "Explain this code", with: "app.rb"
@@ -68,6 +69,11 @@ RubyLLM.paint "a sunset over mountains in watercolor style"
68
69
  RubyLLM.embed "Ruby is elegant and expressive"
69
70
  ```
70
71
 
72
+ ```ruby
73
+ # Moderate content for safety
74
+ RubyLLM.moderate("Check if this text is safe").flagged? # => false
75
+ ```
76
+
71
77
  ```ruby
72
78
  # Let AI use your code
73
79
  class Weather < RubyLLM::Tool
@@ -100,11 +106,12 @@ response = chat.with_schema(ProductSchema).ask "Analyze this product", with: "pr
100
106
  ## Features
101
107
 
102
108
  * **Chat:** Conversational AI with `RubyLLM.chat`
103
- * **Vision:** Analyze images and screenshots
109
+ * **Vision:** Analyze images and videos
104
110
  * **Audio:** Transcribe and understand speech
105
111
  * **Documents:** Extract from PDFs, CSVs, JSON, any file type
106
112
  * **Image generation:** Create images with `RubyLLM.paint`
107
113
  * **Embeddings:** Vector search with `RubyLLM.embed`
114
+ * **Moderation:** Content safety with `RubyLLM.moderate`
108
115
  * **Tools:** Let AI call your Ruby methods
109
116
  * **Structured output:** JSON schemas that just work
110
117
  * **Streaming:** Real-time responses with blocks
@@ -132,7 +139,11 @@ end
132
139
  ## Rails
133
140
 
134
141
  ```bash
142
+ # Install database models
135
143
  rails generate ruby_llm:install
144
+
145
+ # Add chat UI (optional)
146
+ rails generate ruby_llm:chat_ui
136
147
  ```
137
148
 
138
149
  ```ruby
@@ -140,10 +151,12 @@ class Chat < ApplicationRecord
140
151
  acts_as_chat
141
152
  end
142
153
 
143
- chat = Chat.create! model_id: "claude-sonnet-4"
154
+ chat = Chat.create! model: "claude-sonnet-4"
144
155
  chat.ask "What's in this file?", with: "report.pdf"
145
156
  ```
146
157
 
158
+ Visit `http://localhost:3000/chats` for a ready-to-use chat interface!
159
+
147
160
  ## Documentation
148
161
 
149
162
  [rubyllm.com](https://rubyllm.com)
@@ -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
@@ -2,11 +2,13 @@
2
2
 
3
3
  require 'rails/generators'
4
4
  require 'rails/generators/active_record'
5
+ require_relative '../generator_helpers'
5
6
 
6
7
  module RubyLLM
7
8
  # Generator for RubyLLM Rails models and migrations
8
9
  class InstallGenerator < Rails::Generators::Base
9
10
  include Rails::Generators::Migration
11
+ include RubyLLM::GeneratorHelpers
10
12
 
11
13
  namespace 'ruby_llm:install'
12
14
 
@@ -24,139 +26,18 @@ module RubyLLM
24
26
  ::ActiveRecord::Generators::Base.next_migration_number(dirname)
25
27
  end
26
28
 
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
-
61
- define_method("#{type}_table_name") do
62
- table_name_for(send("#{type}_model_name"))
63
- end
64
- end
65
-
66
- def acts_as_chat_declaration
67
- acts_as_chat_params = []
68
- messages_assoc = message_model_name.tableize.to_sym
69
- model_assoc = model_model_name.underscore.to_sym
70
-
71
- if messages_assoc != :messages
72
- acts_as_chat_params << "messages: :#{messages_assoc}"
73
- if message_model_name != messages_assoc.to_s.classify
74
- acts_as_chat_params << "message_class: '#{message_model_name}'"
75
- end
76
- end
77
-
78
- if model_assoc != :model
79
- acts_as_chat_params << "model: :#{model_assoc}"
80
- acts_as_chat_params << "model_class: '#{model_model_name}'" if model_model_name != model_assoc.to_s.classify
81
- end
82
-
83
- if acts_as_chat_params.any?
84
- "acts_as_chat #{acts_as_chat_params.join(', ')}"
85
- else
86
- 'acts_as_chat'
87
- end
88
- end
89
-
90
- def acts_as_message_declaration
91
- params = []
92
-
93
- add_message_association_params(params, :chat, chat_model_name)
94
- add_message_association_params(params, :tool_calls, tool_call_model_name, tableize: true)
95
- add_message_association_params(params, :model, model_model_name)
96
-
97
- params.any? ? "acts_as_message #{params.join(', ')}" : 'acts_as_message'
98
- end
99
-
100
- private
101
-
102
- def add_message_association_params(params, default_assoc, model_name, tableize: false)
103
- assoc = tableize ? model_name.tableize.to_sym : model_name.underscore.to_sym
104
-
105
- return if assoc == default_assoc
106
-
107
- params << "#{default_assoc}: :#{assoc}"
108
- expected_class = assoc.to_s.classify
109
- params << "#{default_assoc.to_s.singularize}_class: '#{model_name}'" if model_name != expected_class
110
- end
111
-
112
- public
113
-
114
- def acts_as_tool_call_declaration
115
- acts_as_tool_call_params = []
116
- message_assoc = message_model_name.underscore.to_sym
117
-
118
- if message_assoc != :message
119
- acts_as_tool_call_params << "message: :#{message_assoc}"
120
- if message_model_name != message_assoc.to_s.classify
121
- acts_as_tool_call_params << "message_class: '#{message_model_name}'"
122
- end
123
- end
124
-
125
- if acts_as_tool_call_params.any?
126
- "acts_as_tool_call #{acts_as_tool_call_params.join(', ')}"
127
- else
128
- 'acts_as_tool_call'
129
- end
130
- end
131
-
132
- def acts_as_model_declaration
133
- acts_as_model_params = []
134
- chats_assoc = chat_model_name.tableize.to_sym
135
-
136
- if chats_assoc != :chats
137
- acts_as_model_params << "chats: :#{chats_assoc}"
138
- acts_as_model_params << "chat_class: '#{chat_model_name}'" if chat_model_name != chats_assoc.to_s.classify
139
- end
140
-
141
- if acts_as_model_params.any?
142
- "acts_as_model #{acts_as_model_params.join(', ')}"
143
- else
144
- 'acts_as_model'
145
- end
146
- end
147
-
148
29
  def create_migration_files
149
30
  # Create migrations with timestamps to ensure proper order
150
31
  # First create chats table
151
32
  migration_template 'create_chats_migration.rb.tt',
152
33
  "db/migrate/create_#{chat_table_name}.rb"
153
34
 
154
- # Then create messages table (must come before tool_calls due to foreign key)
35
+ # Then create messages table
155
36
  sleep 1 # Ensure different timestamp
156
37
  migration_template 'create_messages_migration.rb.tt',
157
38
  "db/migrate/create_#{message_table_name}.rb"
158
39
 
159
- # Then create tool_calls table (references messages)
40
+ # Then create tool_calls table
160
41
  sleep 1 # Ensure different timestamp
161
42
  migration_template 'create_tool_calls_migration.rb.tt',
162
43
  "db/migrate/create_#{tool_call_table_name}.rb"
@@ -165,9 +46,17 @@ module RubyLLM
165
46
  sleep 1 # Ensure different timestamp
166
47
  migration_template 'create_models_migration.rb.tt',
167
48
  "db/migrate/create_#{model_table_name}.rb"
49
+
50
+ # Add references to chats, tool_calls and messages.
51
+ sleep 1 # Ensure different timestamp
52
+ migration_template 'add_references_to_chats_tool_calls_and_messages_migration.rb.tt',
53
+ 'db/migrate/add_references_to_' \
54
+ "#{chat_table_name}_#{tool_call_table_name}_and_#{message_table_name}.rb"
168
55
  end
169
56
 
170
57
  def create_model_files
58
+ create_namespace_modules
59
+
171
60
  template 'chat_model.rb.tt', "app/models/#{chat_model_name.underscore}.rb"
172
61
  template 'message_model.rb.tt', "app/models/#{message_model_name.underscore}.rb"
173
62
  template 'tool_call_model.rb.tt', "app/models/#{tool_call_model_name.underscore}.rb"
@@ -186,12 +75,6 @@ module RubyLLM
186
75
  rails_command 'active_storage:install'
187
76
  end
188
77
 
189
- def table_name_for(model_name)
190
- # Convert namespaced model names to proper table names
191
- # e.g., "Assistant::Chat" -> "assistant_chats" (not "assistant/chats")
192
- model_name.underscore.pluralize.tr('/', '_')
193
- end
194
-
195
78
  def show_install_info
196
79
  say "\n ✅ RubyLLM installed!", :green
197
80
 
@@ -0,0 +1,9 @@
1
+ class AddReferencesTo<%= "#{chat_model_name.gsub('::', '').pluralize}#{tool_call_model_name.gsub('::', '').pluralize}And#{message_model_name.gsub('::', '').pluralize}" %> < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ add_reference :<%= chat_table_name %>, :<%= model_table_name.singularize %>, foreign_key: true
4
+ add_reference :<%= tool_call_table_name %>, :<%= message_table_name.singularize %>, null: false, foreign_key: true
5
+ add_reference :<%= message_table_name %>, :<%= chat_table_name.singularize %>, null: false, foreign_key: true
6
+ add_reference :<%= message_table_name %>, :<%= model_table_name.singularize %>, foreign_key: true
7
+ add_reference :<%= message_table_name %>, :<%= tool_call_table_name.singularize %>, foreign_key: true
8
+ end
9
+ end
@@ -1,7 +1,6 @@
1
1
  class Create<%= chat_model_name.gsub('::', '').pluralize %> < ActiveRecord::Migration<%= migration_version %>
2
2
  def change
3
3
  create_table :<%= chat_table_name %> do |t|
4
- t.references :<%= model_table_name.singularize %>, foreign_key: true
5
4
  t.timestamps
6
5
  end
7
6
  end
@@ -1,13 +1,10 @@
1
1
  class Create<%= message_model_name.gsub('::', '').pluralize %> < ActiveRecord::Migration<%= migration_version %>
2
2
  def change
3
3
  create_table :<%= message_table_name %> do |t|
4
- t.references :<%= chat_table_name.singularize %>, null: false, foreign_key: true
5
4
  t.string :role, null: false
6
5
  t.text :content
7
- t.references :<%= model_table_name.singularize %>, foreign_key: true
8
6
  t.integer :input_tokens
9
7
  t.integer :output_tokens
10
- t.references :<%= tool_call_table_name.singularize %>, foreign_key: true
11
8
  t.timestamps
12
9
  end
13
10
 
@@ -34,10 +34,7 @@ class Create<%= model_model_name.gsub('::', '').pluralize %> < ActiveRecord::Mig
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
- model_class = '<%= model_model_name %>'.constantize
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
@@ -2,7 +2,6 @@
2
2
  class Create<%= tool_call_model_name.gsub('::', '').pluralize %> < ActiveRecord::Migration<%= migration_version %>
3
3
  def change
4
4
  create_table :<%= tool_call_table_name %> do |t|
5
- t.references :<%= message_table_name.singularize %>, null: false, foreign_key: true
6
5
  t.string :tool_call_id, null: false
7
6
  t.string :name, null: false
8
7
  t.<%= postgresql? ? 'jsonb' : 'json' %> :arguments, default: {}
@@ -3,6 +3,14 @@ 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
@@ -2,10 +2,12 @@
2
2
 
3
3
  require 'rails/generators'
4
4
  require 'rails/generators/active_record'
5
+ require_relative '../generator_helpers'
5
6
 
6
7
  module RubyLLM
7
8
  class UpgradeToV17Generator < Rails::Generators::Base # rubocop:disable Style/Documentation
8
9
  include Rails::Generators::Migration
10
+ include RubyLLM::GeneratorHelpers
9
11
 
10
12
  namespace 'ruby_llm:upgrade_to_v1_7'
11
13
  source_root File.expand_path('templates', __dir__)
@@ -27,38 +29,11 @@ module RubyLLM
27
29
  ::ActiveRecord::Generators::Base.next_migration_number(dirname)
28
30
  end
29
31
 
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
-
54
- define_method("#{type}_table_name") do
55
- table_name_for(send("#{type}_model_name"))
56
- end
57
- end
58
-
59
32
  def create_migration_file
33
+ @model_table_already_existed = table_exists?(table_name_for(model_model_name))
34
+
60
35
  # First check if models table exists, if not create it
61
- unless table_exists?(table_name_for(model_model_name))
36
+ unless @model_table_already_existed
62
37
  migration_template 'create_models_migration.rb.tt',
63
38
  "db/migrate/create_#{table_name_for(model_model_name)}.rb",
64
39
  migration_version: migration_version,
@@ -73,98 +48,74 @@ module RubyLLM
73
48
  chat_model_name: chat_model_name,
74
49
  message_model_name: message_model_name,
75
50
  tool_call_model_name: tool_call_model_name,
76
- model_model_name: model_model_name
51
+ model_model_name: model_model_name,
52
+ model_table_already_existed: @model_table_already_existed
77
53
  end
78
54
 
79
55
  def create_model_file
80
- # Check if Model file already exists
81
- model_path = "app/models/#{model_model_name.underscore}.rb"
82
-
83
- if File.exist?(Rails.root.join(model_path))
84
- say_status :skip, model_path, :yellow
85
- else
86
- create_file model_path do
87
- <<~RUBY
88
- class #{model_model_name} < ApplicationRecord
89
- #{acts_as_model_declaration}
90
- end
91
- RUBY
92
- end
93
- end
56
+ create_namespace_modules
57
+
58
+ template 'model_model.rb.tt', "app/models/#{model_model_name.underscore}.rb"
94
59
  end
95
60
 
96
- def acts_as_model_declaration
97
- acts_as_model_params = []
98
- chats_assoc = chat_model_name.tableize.to_sym
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
99
66
 
100
- if chats_assoc != :chats
101
- acts_as_model_params << "chats: :#{chats_assoc}"
102
- acts_as_model_params << "chat_class: '#{chat_model_name}'" if chat_model_name != chats_assoc.to_s.classify
103
- end
67
+ def update_initializer
68
+ initializer_path = 'config/initializers/ruby_llm.rb'
104
69
 
105
- if acts_as_model_params.any?
106
- "acts_as_model #{acts_as_model_params.join(', ')}"
107
- else
108
- 'acts_as_model'
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
109
74
  end
110
- end
111
75
 
112
- def update_initializer
113
- initializer_content = File.read('config/initializers/ruby_llm.rb')
114
-
115
- unless initializer_content.include?('config.use_new_acts_as')
116
- inject_into_file 'config/initializers/ruby_llm.rb', before: /^end/ do
117
- lines = ["\n # Enable the new Rails-like API", ' config.use_new_acts_as = true']
118
- lines << " config.model_registry_class = \"#{model_model_name}\"" if model_model_name != 'Model'
119
- lines << "\n"
120
- lines.join("\n")
121
- end
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")
122
85
  end
123
- rescue Errno::ENOENT
124
- say_status :error, 'config/initializers/ruby_llm.rb not found', :red
125
86
  end
126
87
 
127
88
  def show_next_steps
128
- say_status :success, 'Migration created!', :green
89
+ say_status :success, 'Upgrade prepared!', :green
129
90
  say <<~INSTRUCTIONS
130
91
 
131
92
  Next steps:
132
- 1. Review the migration: db/migrate/*_migrate_to_ruby_llm_model_references.rb
93
+ 1. Review the generated migrations
133
94
  2. Run: rails db:migrate
134
- 3. Update config/initializers/ruby_llm.rb as shown above
135
- 4. Test your application thoroughly
95
+ 3. Update your code to use the new API: #{chat_model_name}.create! now has the same signature as RubyLLM.chat
136
96
 
137
- The migration will:
138
- - Create the Models table if it doesn't exist
139
- - Load all models from models.json
140
- - Migrate your existing data to use foreign keys
141
- - Preserve all existing data (string columns renamed to model_id_string)
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/
142
105
 
143
106
  INSTRUCTIONS
144
107
  end
145
108
 
146
109
  private
147
110
 
148
- def migration_version
149
- "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
150
- end
151
-
152
- def table_name_for(model_name)
153
- # Convert namespaced model names to proper table names
154
- # e.g., "Assistant::Chat" -> "assistant_chats" (not "assistant/chats")
155
- model_name.underscore.pluralize.tr('/', '_')
156
- end
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))
157
114
 
158
- def table_exists?(table_name)
159
- ::ActiveRecord::Base.connection.table_exists?(table_name)
160
- rescue StandardError
161
- false
162
- end
115
+ content = File.read(Rails.root.join(model_path))
116
+ return unless content.match?(/^\s*#{old_acts_as}/)
163
117
 
164
- def postgresql?
165
- ::ActiveRecord::Base.connection.adapter_name.downcase.include?('postgresql')
166
- rescue StandardError
167
- false
118
+ gsub_file model_path, /^\s*#{old_acts_as}.*$/, " #{new_acts_as}"
168
119
  end
169
120
  end
170
121
  end