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 +4 -4
- data/README.md +16 -3
- data/lib/generators/ruby_llm/generator_helpers.rb +129 -0
- data/lib/generators/ruby_llm/install/install_generator.rb +12 -129
- data/lib/generators/ruby_llm/install/templates/add_references_to_chats_tool_calls_and_messages_migration.rb.tt +9 -0
- data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +0 -1
- data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +0 -3
- data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +1 -4
- data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +0 -1
- data/lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt +8 -0
- data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +47 -96
- data/lib/ruby_llm/attachment.rb +5 -0
- data/lib/ruby_llm/configuration.rb +2 -0
- data/lib/ruby_llm/mime_type.rb +4 -0
- data/lib/ruby_llm/model/info.rb +4 -0
- data/lib/ruby_llm/models.json +780 -511
- data/lib/ruby_llm/models.rb +7 -3
- data/lib/ruby_llm/moderation.rb +56 -0
- data/lib/ruby_llm/provider.rb +6 -0
- data/lib/ruby_llm/providers/gemini/capabilities.rb +5 -0
- data/lib/ruby_llm/providers/openai/moderation.rb +34 -0
- data/lib/ruby_llm/providers/openai.rb +1 -0
- data/lib/ruby_llm/railtie.rb +1 -1
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +4 -0
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 80cc1d17f6a70b28e6f8cfe72ceb5ac79398e5b569f2519a0ba9fe34cba3ae30
|
4
|
+
data.tar.gz: 429561f0d6191e55248ef5610eb42a52dc70acfb97bea116ee70ae53a0919c5f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
[](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)
|
@@ -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
|
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!
|
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
|
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
|
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,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
|
-
|
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
|
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
|
-
|
81
|
-
|
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
|
97
|
-
|
98
|
-
|
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
|
-
|
101
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
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
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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, '
|
89
|
+
say_status :success, 'Upgrade prepared!', :green
|
129
90
|
say <<~INSTRUCTIONS
|
130
91
|
|
131
92
|
Next steps:
|
132
|
-
1. Review the
|
93
|
+
1. Review the generated migrations
|
133
94
|
2. Run: rails db:migrate
|
134
|
-
3. Update
|
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
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
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
|
149
|
-
"
|
150
|
-
|
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
|
-
|
159
|
-
|
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
|
-
|
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
|