ruby_llm 1.8.1 → 1.8.2

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 (27) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -4
  3. data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +117 -69
  4. data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +12 -12
  5. data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +7 -7
  6. data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +4 -4
  7. data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +6 -6
  8. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +4 -4
  9. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +5 -5
  10. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +5 -5
  11. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt +4 -4
  12. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +8 -8
  13. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +5 -5
  14. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt +9 -6
  15. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool_calls.html.erb.tt +7 -0
  16. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +5 -5
  17. data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +9 -9
  18. data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +4 -6
  19. data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +11 -11
  20. data/lib/generators/ruby_llm/generator_helpers.rb +131 -87
  21. data/lib/generators/ruby_llm/install/install_generator.rb +75 -79
  22. data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +1 -1
  23. data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +88 -85
  24. data/lib/ruby_llm/active_record/acts_as.rb +11 -2
  25. data/lib/ruby_llm/connection.rb +1 -1
  26. data/lib/ruby_llm/version.rb +1 -1
  27. metadata +2 -1
@@ -1,16 +1,16 @@
1
- <tr id="<%%= dom_id <%= model_model_name.underscore %> %>">
2
- <td><%%= <%= model_model_name.underscore %>.provider %></td>
3
- <td><%%= <%= model_model_name.underscore %>.name %></td>
4
- <td><%%= number_with_delimiter(<%= model_model_name.underscore %>.context_window) if <%= model_model_name.underscore %>.context_window %></td>
1
+ <tr id="<%%= dom_id <%= model_model_name.demodulize.underscore %> %>">
2
+ <td><%%= <%= model_model_name.demodulize.underscore %>.provider %></td>
3
+ <td><%%= <%= model_model_name.demodulize.underscore %>.name %></td>
4
+ <td><%%= number_with_delimiter(<%= model_model_name.demodulize.underscore %>.context_window) if <%= model_model_name.demodulize.underscore %>.context_window %></td>
5
5
  <td>
6
- <%% if <%= model_model_name.underscore %>.pricing && <%= model_model_name.underscore %>.pricing['text_tokens'] && <%= model_model_name.underscore %>.pricing['text_tokens']['standard'] %>
7
- <%% input = <%= model_model_name.underscore %>.pricing['text_tokens']['standard']['input_per_million'] %>
8
- <%% output = <%= model_model_name.underscore %>.pricing['text_tokens']['standard']['output_per_million'] %>
6
+ <%% if <%= model_model_name.demodulize.underscore %>.pricing && <%= model_model_name.demodulize.underscore %>.pricing['text_tokens'] && <%= model_model_name.demodulize.underscore %>.pricing['text_tokens']['standard'] %>
7
+ <%% input = <%= model_model_name.demodulize.underscore %>.pricing['text_tokens']['standard']['input_per_million'] %>
8
+ <%% output = <%= model_model_name.demodulize.underscore %>.pricing['text_tokens']['standard']['output_per_million'] %>
9
9
  <%% if input && output %>
10
10
  $<%%= "%.2f" % input %> / $<%%= "%.2f" % output %>
11
11
  <%% end %>
12
12
  <%% end %>
13
13
  </td>
14
- <td><%%= link_to "Show", <%= model_model_name.underscore %> %></td>
15
- <td><%%= link_to "Start <%= chat_model_name.underscore.humanize.downcase %>", new_<%= chat_model_name.underscore %>_path(model: <%= model_model_name.underscore %>.model_id) %></td>
14
+ <td><%%= link_to "Show", <%= model_model_name.demodulize.underscore %> %></td>
15
+ <td><%%= link_to "Start <%= chat_table_name.singularize.humanize.downcase %>", new_<%= chat_variable_name %>_path(model: <%= model_model_name.demodulize.underscore %>.model_id) %></td>
16
16
  </tr>
@@ -5,10 +5,10 @@
5
5
  <h1><%= model_model_name.pluralize %></h1>
6
6
 
7
7
  <p>
8
- <%%= button_to "Refresh <%= model_model_name.pluralize %>", refresh_<%= model_model_name.tableize %>_path, method: :post %>
8
+ <%%= button_to "Refresh <%= model_model_name.pluralize %>", refresh_<%= model_table_name %>_path, method: :post %>
9
9
  </p>
10
10
 
11
- <div id="<%= model_model_name.tableize %>">
11
+ <div id="<%= model_table_name %>">
12
12
  <table>
13
13
  <thead>
14
14
  <tr>
@@ -20,11 +20,9 @@
20
20
  </tr>
21
21
  </thead>
22
22
  <tbody>
23
- <%% @<%= model_model_name.tableize %>.values.flatten.each do |<%= model_model_name.underscore %>| %>
24
- <%%= render <%= model_model_name.underscore %> %>
25
- <%% end %>
23
+ <%%= render @<%= model_variable_name.pluralize %> %>
26
24
  </tbody>
27
25
  </table>
28
26
  </div>
29
27
 
30
- <%%= link_to "Back to <%= chat_model_name.tableize.humanize.downcase %>", <%= chat_model_name.tableize %>_path %>
28
+ <%%= link_to "Back to <%= chat_table_name.humanize.downcase %>", <%= chat_table_name %>_path %>
@@ -1,18 +1,18 @@
1
- <%% content_for :title, @model.name %>
1
+ <%% content_for :title, @<%= model_variable_name %>.name %>
2
2
 
3
- <h1><%%= @model.name %></h1>
3
+ <h1><%%= @<%= model_variable_name %>.name %></h1>
4
4
 
5
- <p><strong>ID:</strong> <%%= @model.model_id %></p>
6
- <p><strong>Provider:</strong> <%%= @model.provider %></p>
7
- <p><strong>Context Window:</strong> <%%= number_with_delimiter(@model.context_window) %> tokens</p>
8
- <p><strong>Max Output:</strong> <%%= number_with_delimiter(@model.max_output_tokens) %> tokens</p>
5
+ <p><strong>ID:</strong> <%%= @<%= model_variable_name %>.model_id %></p>
6
+ <p><strong>Provider:</strong> <%%= @<%= model_variable_name %>.provider %></p>
7
+ <p><strong>Context Window:</strong> <%%= number_with_delimiter(@<%= model_variable_name %>.context_window) %> tokens</p>
8
+ <p><strong>Max Output:</strong> <%%= number_with_delimiter(@<%= model_variable_name %>.max_output_tokens) %> tokens</p>
9
9
 
10
- <%% if @model.capabilities.any? %>
11
- <p><strong>Capabilities:</strong> <%%= @model.capabilities.join(", ") %></p>
10
+ <%% if @<%= model_variable_name %>.capabilities.any? %>
11
+ <p><strong>Capabilities:</strong> <%%= @<%= model_variable_name %>.capabilities.join(", ") %></p>
12
12
  <%% end %>
13
13
 
14
14
  <p>
15
- <%%= link_to "Start chat with this model", new_chat_path(model: @model.model_id) %> |
16
- <%%= link_to "All models", models_path %> |
17
- <%%= link_to "Back to chats", chats_path %>
15
+ <%%= link_to "Start chat with this model", new_<%= chat_variable_name %>_path(model: @<%= model_variable_name %>.model_id) %> |
16
+ <%%= link_to "All models", <%= model_table_name %>_path %> |
17
+ <%%= link_to "Back to chats", <%= chat_table_name %>_path %>
18
18
  </p>
@@ -1,129 +1,173 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
4
+ module Generators
5
+ # Shared helpers for RubyLLM generators
6
+ module GeneratorHelpers
7
+ def parse_model_mappings
8
+ @model_names = {
9
+ chat: 'Chat',
10
+ message: 'Message',
11
+ tool_call: 'ToolCall',
12
+ model: 'Model'
13
+ }
14
+
15
+ model_mappings.each do |mapping|
16
+ if mapping.include?(':')
17
+ key, value = mapping.split(':', 2)
18
+ @model_names[key.to_sym] = value.classify
19
+ end
18
20
  end
21
+
22
+ @model_names
19
23
  end
20
24
 
21
- @model_names
22
- end
25
+ %i[chat message tool_call model].each do |type|
26
+ define_method("#{type}_model_name") do
27
+ @model_names ||= parse_model_mappings
28
+ @model_names[type]
29
+ end
23
30
 
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
31
+ define_method("#{type}_table_name") do
32
+ table_name_for(send("#{type}_model_name"))
33
+ end
34
+
35
+ define_method("#{type}_variable_name") do
36
+ variable_name_for(send("#{type}_model_name"))
37
+ end
38
+
39
+ define_method("#{type}_controller_class_name") do
40
+ controller_class_name_for(send("#{type}_model_name"))
41
+ end
42
+
43
+ define_method("#{type}_job_class_name") do
44
+ "#{variable_name_for(send("#{type}_model_name")).camelize}ResponseJob"
45
+ end
29
46
 
30
- define_method("#{type}_table_name") do
31
- table_name_for(send("#{type}_model_name"))
47
+ define_method("#{type}_partial") do
48
+ partial_path_for(send("#{type}_model_name"))
49
+ end
32
50
  end
33
- end
34
51
 
35
- def acts_as_chat_declaration
36
- params = []
52
+ def acts_as_chat_declaration
53
+ params = []
37
54
 
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)
55
+ add_association_params(params, :messages, message_table_name, message_model_name, plural: true)
56
+ add_association_params(params, :model, model_table_name, model_model_name)
40
57
 
41
- "acts_as_chat#{" #{params.join(', ')}" if params.any?}"
42
- end
58
+ "acts_as_chat#{" #{params.join(', ')}" if params.any?}"
59
+ end
43
60
 
44
- def acts_as_message_declaration
45
- params = []
61
+ def acts_as_message_declaration
62
+ params = []
46
63
 
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)
64
+ add_association_params(params, :chat, chat_table_name, chat_model_name)
65
+ add_association_params(params, :tool_calls, tool_call_table_name, tool_call_model_name, plural: true)
66
+ add_association_params(params, :model, model_table_name, model_model_name)
50
67
 
51
- "acts_as_message#{" #{params.join(', ')}" if params.any?}"
52
- end
68
+ "acts_as_message#{" #{params.join(', ')}" if params.any?}"
69
+ end
53
70
 
54
- def acts_as_model_declaration
55
- params = []
71
+ def acts_as_model_declaration
72
+ params = []
56
73
 
57
- add_association_params(params, :chats, chat_table_name, chat_model_name, plural: true)
74
+ add_association_params(params, :chats, chat_table_name, chat_model_name, plural: true)
58
75
 
59
- "acts_as_model#{" #{params.join(', ')}" if params.any?}"
60
- end
76
+ "acts_as_model#{" #{params.join(', ')}" if params.any?}"
77
+ end
61
78
 
62
- def acts_as_tool_call_declaration
63
- params = []
79
+ def acts_as_tool_call_declaration
80
+ params = []
64
81
 
65
- add_association_params(params, :message, message_table_name, message_model_name)
82
+ add_association_params(params, :message, message_table_name, message_model_name)
66
83
 
67
- "acts_as_tool_call#{" #{params.join(', ')}" if params.any?}"
68
- end
84
+ "acts_as_tool_call#{" #{params.join(', ')}" if params.any?}"
85
+ end
69
86
 
70
- def create_namespace_modules
71
- namespaces = []
87
+ def create_namespace_modules
88
+ namespaces = []
72
89
 
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)
90
+ [chat_model_name, message_model_name, tool_call_model_name, model_model_name].each do |model_name|
91
+ if model_name.include?('::')
92
+ namespace = model_name.split('::').first
93
+ namespaces << namespace unless namespaces.include?(namespace)
94
+ end
77
95
  end
78
- end
79
96
 
80
- namespaces.each do |namespace|
81
- module_path = "app/models/#{namespace.underscore}.rb"
82
- next if File.exist?(Rails.root.join(module_path))
97
+ namespaces.each do |namespace|
98
+ module_path = "app/models/#{namespace.underscore}.rb"
99
+ next if File.exist?(Rails.root.join(module_path))
83
100
 
84
- create_file module_path do
85
- <<~RUBY
86
- module #{namespace}
87
- def self.table_name_prefix
88
- "#{namespace.underscore}_"
101
+ create_file module_path do
102
+ <<~RUBY
103
+ module #{namespace}
104
+ def self.table_name_prefix
105
+ "#{namespace.underscore}_"
106
+ end
89
107
  end
90
- end
91
- RUBY
108
+ RUBY
109
+ end
92
110
  end
93
111
  end
94
- end
95
112
 
96
- def migration_version
97
- "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
98
- end
113
+ def migration_version
114
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
115
+ end
99
116
 
100
- def postgresql?
101
- ::ActiveRecord::Base.connection.adapter_name.downcase.include?('postgresql')
102
- rescue StandardError
103
- false
104
- end
117
+ def postgresql?
118
+ ::ActiveRecord::Base.connection.adapter_name.downcase.include?('postgresql')
119
+ rescue StandardError
120
+ false
121
+ end
105
122
 
106
- def table_exists?(table_name)
107
- ::ActiveRecord::Base.connection.table_exists?(table_name)
108
- rescue StandardError
109
- false
110
- end
123
+ def table_exists?(table_name)
124
+ ::ActiveRecord::Base.connection.table_exists?(table_name)
125
+ rescue StandardError
126
+ false
127
+ end
111
128
 
112
- private
129
+ private
113
130
 
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
131
+ def add_association_params(params, default_assoc, table_name, model_name, plural: false)
132
+ assoc = plural ? table_name.to_sym : table_name.singularize.to_sym
116
133
 
117
- return if assoc == default_assoc
134
+ return if assoc == default_assoc
118
135
 
119
- params << "#{default_assoc}: :#{assoc}"
120
- params << "#{default_assoc.to_s.singularize}_class: '#{model_name}'" if model_name != assoc.to_s.classify
121
- end
136
+ params << "#{default_assoc}: :#{assoc}"
137
+ params << "#{default_assoc.to_s.singularize}_class: '#{model_name}'" if model_name != assoc.to_s.classify
138
+ end
122
139
 
123
- def table_name_for(model_name)
124
140
  # Convert namespaced model names to proper table names
125
141
  # e.g., "Assistant::Chat" -> "assistant_chats" (not "assistant/chats")
126
- model_name.underscore.pluralize.tr('/', '_')
142
+ def table_name_for(model_name)
143
+ model_name.underscore.pluralize.tr('/', '_')
144
+ end
145
+
146
+ # Convert model name to instance variable name
147
+ # e.g., "LLM::Chat" -> "llm_chat" (not "llm/chat")
148
+ def variable_name_for(model_name)
149
+ model_name.underscore.tr('/', '_')
150
+ end
151
+
152
+ # Convert model name to controller class name
153
+ # For namespaced models, use Rails convention: "Llm::Chat" -> "Llm::ChatsController"
154
+ # For regular models: "Chat" -> "ChatsController"
155
+ def controller_class_name_for(model_name)
156
+ if model_name.include?('::')
157
+ parts = model_name.split('::')
158
+ namespace = parts[0..-2].join('::')
159
+ resource = parts.last.pluralize
160
+ "#{namespace}::#{resource}Controller"
161
+ else
162
+ "#{model_name.pluralize}Controller"
163
+ end
164
+ end
165
+
166
+ # Convert model name to partial path
167
+ # e.g., "LLM::Message" -> "llm/message" (not "llm_message")
168
+ def partial_path_for(model_name)
169
+ "#{model_name.underscore.pluralize}/#{model_name.demodulize.underscore}"
170
+ end
127
171
  end
128
172
  end
129
173
  end
@@ -5,106 +5,102 @@ require 'rails/generators/active_record'
5
5
  require_relative '../generator_helpers'
6
6
 
7
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
8
+ module Generators
9
+ # Generator for RubyLLM Rails models and migrations
10
+ class InstallGenerator < Rails::Generators::Base
11
+ include Rails::Generators::Migration
12
+ include RubyLLM::Generators::GeneratorHelpers
12
13
 
13
- namespace 'ruby_llm:install'
14
+ namespace 'ruby_llm:install'
14
15
 
15
- source_root File.expand_path('templates', __dir__)
16
+ source_root File.expand_path('templates', __dir__)
16
17
 
17
- argument :model_mappings, type: :array, default: [], banner: 'chat:ChatName message:MessageName ...'
18
+ argument :model_mappings, type: :array, default: [], banner: 'chat:ChatName message:MessageName ...'
18
19
 
19
- class_option :skip_active_storage, type: :boolean, default: false,
20
- desc: 'Skip ActiveStorage installation and attachment setup'
20
+ class_option :skip_active_storage, type: :boolean, default: false,
21
+ desc: 'Skip ActiveStorage installation and attachment setup'
21
22
 
22
- desc 'Creates models and migrations for RubyLLM Rails integration\n' \
23
- 'Usage: rails g ruby_llm:install [chat:ChatName] [message:MessageName] ...'
23
+ desc 'Creates models and migrations for RubyLLM Rails integration\n' \
24
+ 'Usage: rails g ruby_llm:install [chat:ChatName] [message:MessageName] ...'
24
25
 
25
- def self.next_migration_number(dirname)
26
- ::ActiveRecord::Generators::Base.next_migration_number(dirname)
27
- end
26
+ def self.next_migration_number(dirname)
27
+ ::ActiveRecord::Generators::Base.next_migration_number(dirname)
28
+ end
28
29
 
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
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
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
-
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"
55
- end
30
+ def create_migration_files
31
+ migration_template 'create_chats_migration.rb.tt',
32
+ "db/migrate/create_#{chat_table_name}.rb"
56
33
 
57
- def create_model_files
58
- create_namespace_modules
34
+ sleep 1 # Ensure different timestamp
35
+ migration_template 'create_messages_migration.rb.tt',
36
+ "db/migrate/create_#{message_table_name}.rb"
59
37
 
60
- template 'chat_model.rb.tt', "app/models/#{chat_model_name.underscore}.rb"
61
- template 'message_model.rb.tt', "app/models/#{message_model_name.underscore}.rb"
62
- template 'tool_call_model.rb.tt', "app/models/#{tool_call_model_name.underscore}.rb"
38
+ sleep 1 # Ensure different timestamp
39
+ migration_template 'create_tool_calls_migration.rb.tt',
40
+ "db/migrate/create_#{tool_call_table_name}.rb"
63
41
 
64
- template 'model_model.rb.tt', "app/models/#{model_model_name.underscore}.rb"
65
- end
42
+ sleep 1 # Ensure different timestamp
43
+ migration_template 'create_models_migration.rb.tt',
44
+ "db/migrate/create_#{model_table_name}.rb"
66
45
 
67
- def create_initializer
68
- template 'initializer.rb.tt', 'config/initializers/ruby_llm.rb'
69
- end
46
+ sleep 1 # Ensure different timestamp
47
+ migration_template 'add_references_to_chats_tool_calls_and_messages_migration.rb.tt',
48
+ 'db/migrate/add_references_to_' \
49
+ "#{chat_table_name}_#{tool_call_table_name}_and_#{message_table_name}.rb"
50
+ end
70
51
 
71
- def install_active_storage
72
- return if options[:skip_active_storage]
52
+ def create_model_files
53
+ create_namespace_modules
73
54
 
74
- say ' Installing ActiveStorage for file attachments...', :cyan
75
- rails_command 'active_storage:install'
76
- end
55
+ template 'chat_model.rb.tt', "app/models/#{chat_model_name.underscore}.rb"
56
+ template 'message_model.rb.tt', "app/models/#{message_model_name.underscore}.rb"
57
+ template 'tool_call_model.rb.tt', "app/models/#{tool_call_model_name.underscore}.rb"
58
+
59
+ template 'model_model.rb.tt', "app/models/#{model_model_name.underscore}.rb"
60
+ end
77
61
 
78
- def show_install_info
79
- say "\n ✅ RubyLLM installed!", :green
62
+ def create_initializer
63
+ template 'initializer.rb.tt', 'config/initializers/ruby_llm.rb'
64
+ end
80
65
 
81
- say ' ✅ ActiveStorage configured for file attachments support', :green unless options[:skip_active_storage]
66
+ def install_active_storage
67
+ return if options[:skip_active_storage]
82
68
 
83
- say "\n Next steps:", :yellow
84
- say ' 1. Run: rails db:migrate'
85
- say ' 2. Set your API keys in config/initializers/ruby_llm.rb'
69
+ say ' Installing ActiveStorage for file attachments...', :cyan
70
+ rails_command 'active_storage:install'
71
+ end
86
72
 
87
- say " 3. Start chatting: #{chat_model_name}.create!(model: 'gpt-4.1-nano').ask('Hello!')"
73
+ def show_install_info
74
+ say "\n ✅ RubyLLM installed!", :green
88
75
 
89
- say "\n 🚀 Model registry is database-backed!", :cyan
90
- say ' Models automatically load from the database'
91
- say ' Pass model names as strings - RubyLLM handles the rest!'
92
- say " Specify provider when needed: Chat.create!(model: 'gemini-2.5-flash', provider: 'vertexai')"
76
+ say ' ActiveStorage configured for file attachments support', :green unless options[:skip_active_storage]
93
77
 
94
- if options[:skip_active_storage]
95
- say "\n 📎 Note: ActiveStorage was skipped", :yellow
96
- say ' File attachments won\'t work without ActiveStorage.'
97
- say ' To enable later:'
98
- say ' 1. Run: rails active_storage:install && rails db:migrate'
99
- say " 2. Add to your #{message_model_name} model: has_many_attached :attachments"
100
- end
78
+ say "\n Next steps:", :yellow
79
+ say ' 1. Run: rails db:migrate'
80
+ say ' 2. Set your API keys in config/initializers/ruby_llm.rb'
101
81
 
102
- say "\n 📚 Documentation: https://rubyllm.com", :cyan
82
+ say " 3. Start chatting: #{chat_model_name}.create!(model: 'gpt-4.1-nano').ask('Hello!')"
103
83
 
104
- say "\n ❤️ Love RubyLLM?", :magenta
105
- say ' Star on GitHub: https://github.com/crmne/ruby_llm'
106
- say ' 🐦 Follow for updates: https://x.com/paolino'
107
- say "\n"
84
+ say "\n 🚀 Model registry is database-backed!", :cyan
85
+ say ' Models automatically load from the database'
86
+ say ' Pass model names as strings - RubyLLM handles the rest!'
87
+ say " Specify provider when needed: Chat.create!(model: 'gemini-2.5-flash', provider: 'vertexai')"
88
+
89
+ if options[:skip_active_storage]
90
+ say "\n 📎 Note: ActiveStorage was skipped", :yellow
91
+ say ' File attachments won\'t work without ActiveStorage.'
92
+ say ' To enable later:'
93
+ say ' 1. Run: rails active_storage:install && rails db:migrate'
94
+ say " 2. Add to your #{message_model_name} model: has_many_attached :attachments"
95
+ end
96
+
97
+ say "\n 📚 Documentation: https://rubyllm.com", :cyan
98
+
99
+ say "\n ❤️ Love RubyLLM?", :magenta
100
+ say ' • ⭐ Star on GitHub: https://github.com/crmne/ruby_llm'
101
+ say ' • 🐦 Follow for updates: https://x.com/paolino'
102
+ say "\n"
103
+ end
108
104
  end
109
105
  end
110
106
  end
@@ -1,5 +1,5 @@
1
1
  RubyLLM.configure do |config|
2
- config.openai_api_key = Rails.application.credentials.dig(:openai_api_key)
2
+ config.openai_api_key = ENV['OPENAI_API_KEY'] || Rails.application.credentials.dig(:openai_api_key)
3
3
  # config.default_model = "gpt-4.1-nano"
4
4
 
5
5
  # Use the new association-based acts_as API (recommended)