dify_llm 1.6.4 → 1.7.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 +1 -1
- data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +127 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +39 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +24 -0
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +14 -0
- data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +12 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +29 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt +11 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +23 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +21 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt +10 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +9 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +30 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +18 -0
- data/lib/generators/ruby_llm/install/install_generator.rb +227 -0
- data/lib/generators/ruby_llm/install/templates/chat_model.rb.tt +1 -1
- 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 -3
- data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +5 -5
- data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +6 -3
- data/lib/generators/ruby_llm/install/templates/message_model.rb.tt +1 -1
- data/lib/generators/ruby_llm/install/templates/model_model.rb.tt +1 -1
- data/lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt +1 -1
- data/lib/generators/ruby_llm/{migrate_model_fields → upgrade_to_v1_7}/templates/migration.rb.tt +16 -21
- data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +170 -0
- data/lib/ruby_llm/active_record/acts_as.rb +88 -58
- data/lib/ruby_llm/active_record/chat_methods.rb +51 -30
- data/lib/ruby_llm/active_record/message_methods.rb +2 -2
- data/lib/ruby_llm/aliases.json +0 -8
- data/lib/ruby_llm/configuration.rb +5 -0
- data/lib/ruby_llm/models.json +1724 -1497
- data/lib/ruby_llm/railtie.rb +4 -12
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +2 -1
- metadata +23 -6
- data/lib/generators/ruby_llm/install/templates/create_chats_legacy_migration.rb.tt +0 -8
- data/lib/generators/ruby_llm/install/templates/create_messages_legacy_migration.rb.tt +0 -16
- data/lib/generators/ruby_llm/install_generator.rb +0 -184
- data/lib/generators/ruby_llm/migrate_model_fields_generator.rb +0 -84
@@ -31,105 +31,135 @@ module RubyLLM
|
|
31
31
|
end
|
32
32
|
|
33
33
|
class_methods do # rubocop:disable Metrics/BlockLength
|
34
|
-
def acts_as_chat(
|
35
|
-
|
34
|
+
def acts_as_chat(messages: :messages, message_class: nil,
|
35
|
+
model: :model, model_class: nil)
|
36
36
|
include RubyLLM::ActiveRecord::ChatMethods
|
37
37
|
|
38
|
-
|
39
|
-
@tool_call_class = tool_call_class.to_s
|
40
|
-
@model_class = model_class.to_s
|
41
|
-
@model_foreign_key = model_foreign_key || ActiveSupport::Inflector.foreign_key(@model_class)
|
38
|
+
class_attribute :messages_association_name, :model_association_name, :message_class, :model_class
|
42
39
|
|
43
|
-
|
40
|
+
self.messages_association_name = messages
|
41
|
+
self.model_association_name = model
|
42
|
+
self.message_class = (message_class || messages.to_s.classify).to_s
|
43
|
+
self.model_class = (model_class || model.to_s.classify).to_s
|
44
|
+
|
45
|
+
has_many messages,
|
44
46
|
-> { order(created_at: :asc) },
|
45
|
-
class_name:
|
46
|
-
inverse_of: :chat,
|
47
|
+
class_name: self.message_class,
|
47
48
|
dependent: :destroy
|
48
49
|
|
49
|
-
belongs_to
|
50
|
-
class_name:
|
51
|
-
foreign_key: @model_foreign_key,
|
50
|
+
belongs_to model,
|
51
|
+
class_name: self.model_class,
|
52
52
|
optional: true
|
53
53
|
|
54
54
|
delegate :add_message, to: :to_llm
|
55
|
+
|
56
|
+
define_method :messages_association do
|
57
|
+
send(messages_association_name)
|
58
|
+
end
|
59
|
+
|
60
|
+
define_method :model_association do
|
61
|
+
send(model_association_name)
|
62
|
+
end
|
63
|
+
|
64
|
+
define_method :'model_association=' do |value|
|
65
|
+
send("#{model_association_name}=", value)
|
66
|
+
end
|
55
67
|
end
|
56
68
|
|
57
|
-
def acts_as_model(chat_class:
|
69
|
+
def acts_as_model(chats: :chats, chat_class: nil)
|
58
70
|
include RubyLLM::ActiveRecord::ModelMethods
|
59
71
|
|
60
|
-
|
72
|
+
class_attribute :chats_association_name, :chat_class
|
73
|
+
|
74
|
+
self.chats_association_name = chats
|
75
|
+
self.chat_class = (chat_class || chats.to_s.classify).to_s
|
61
76
|
|
62
77
|
validates :model_id, presence: true, uniqueness: { scope: :provider }
|
63
78
|
validates :provider, presence: true
|
64
79
|
validates :name, presence: true
|
65
80
|
|
66
|
-
has_many
|
67
|
-
|
68
|
-
|
81
|
+
has_many chats, class_name: self.chat_class
|
82
|
+
|
83
|
+
define_method :chats_association do
|
84
|
+
send(chats_association_name)
|
85
|
+
end
|
69
86
|
end
|
70
87
|
|
71
|
-
def acts_as_message(chat_class:
|
72
|
-
|
73
|
-
|
74
|
-
tool_call_foreign_key: nil,
|
75
|
-
model_class: 'Model',
|
76
|
-
model_foreign_key: nil,
|
77
|
-
touch_chat: false)
|
88
|
+
def acts_as_message(chat: :chat, chat_class: nil, touch_chat: false, # rubocop:disable Metrics/ParameterLists
|
89
|
+
tool_calls: :tool_calls, tool_call_class: nil,
|
90
|
+
model: :model, model_class: nil)
|
78
91
|
include RubyLLM::ActiveRecord::MessageMethods
|
79
92
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
@tool_call_class = tool_call_class.to_s
|
84
|
-
@tool_call_foreign_key = tool_call_foreign_key || ActiveSupport::Inflector.foreign_key(@tool_call_class)
|
93
|
+
class_attribute :chat_association_name, :tool_calls_association_name, :model_association_name,
|
94
|
+
:chat_class, :tool_call_class, :model_class
|
85
95
|
|
86
|
-
|
87
|
-
|
96
|
+
self.chat_association_name = chat
|
97
|
+
self.tool_calls_association_name = tool_calls
|
98
|
+
self.model_association_name = model
|
99
|
+
self.chat_class = (chat_class || chat.to_s.classify).to_s
|
100
|
+
self.tool_call_class = (tool_call_class || tool_calls.to_s.classify).to_s
|
101
|
+
self.model_class = (model_class || model.to_s.classify).to_s
|
88
102
|
|
89
|
-
belongs_to
|
90
|
-
class_name:
|
91
|
-
foreign_key: @chat_foreign_key,
|
92
|
-
inverse_of: :messages,
|
103
|
+
belongs_to chat,
|
104
|
+
class_name: self.chat_class,
|
93
105
|
touch: touch_chat
|
94
106
|
|
95
|
-
has_many
|
96
|
-
class_name:
|
107
|
+
has_many tool_calls,
|
108
|
+
class_name: self.tool_call_class,
|
97
109
|
dependent: :destroy
|
98
110
|
|
99
111
|
belongs_to :parent_tool_call,
|
100
|
-
class_name:
|
101
|
-
foreign_key:
|
102
|
-
optional: true
|
103
|
-
inverse_of: :result
|
112
|
+
class_name: self.tool_call_class,
|
113
|
+
foreign_key: ActiveSupport::Inflector.foreign_key(tool_calls.to_s.singularize),
|
114
|
+
optional: true
|
104
115
|
|
105
116
|
has_many :tool_results,
|
106
|
-
through:
|
117
|
+
through: tool_calls,
|
107
118
|
source: :result,
|
108
|
-
class_name:
|
119
|
+
class_name: name
|
109
120
|
|
110
|
-
belongs_to
|
111
|
-
class_name:
|
112
|
-
foreign_key: @model_foreign_key,
|
121
|
+
belongs_to model,
|
122
|
+
class_name: self.model_class,
|
113
123
|
optional: true
|
114
124
|
|
115
125
|
delegate :tool_call?, :tool_result?, to: :to_llm
|
126
|
+
|
127
|
+
define_method :chat_association do
|
128
|
+
send(chat_association_name)
|
129
|
+
end
|
130
|
+
|
131
|
+
define_method :tool_calls_association do
|
132
|
+
send(tool_calls_association_name)
|
133
|
+
end
|
134
|
+
|
135
|
+
define_method :model_association do
|
136
|
+
send(model_association_name)
|
137
|
+
end
|
116
138
|
end
|
117
139
|
|
118
|
-
def acts_as_tool_call(
|
119
|
-
|
120
|
-
|
121
|
-
@result_foreign_key = result_foreign_key || ActiveSupport::Inflector.foreign_key(name)
|
140
|
+
def acts_as_tool_call(message: :message, message_class: nil,
|
141
|
+
result: :result, result_class: nil)
|
142
|
+
class_attribute :message_association_name, :result_association_name, :message_class, :result_class
|
122
143
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
144
|
+
self.message_association_name = message
|
145
|
+
self.result_association_name = result
|
146
|
+
self.message_class = (message_class || message.to_s.classify).to_s
|
147
|
+
self.result_class = (result_class || self.message_class).to_s
|
127
148
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
149
|
+
belongs_to message,
|
150
|
+
class_name: self.message_class
|
151
|
+
|
152
|
+
has_one result,
|
153
|
+
class_name: self.result_class,
|
132
154
|
dependent: :nullify
|
155
|
+
|
156
|
+
define_method :message_association do
|
157
|
+
send(message_association_name)
|
158
|
+
end
|
159
|
+
|
160
|
+
define_method :result_association do
|
161
|
+
send(result_association_name)
|
162
|
+
end
|
133
163
|
end
|
134
164
|
end
|
135
165
|
end
|
@@ -10,15 +10,17 @@ module RubyLLM
|
|
10
10
|
before_save :resolve_model_from_strings
|
11
11
|
end
|
12
12
|
|
13
|
-
class_methods do
|
14
|
-
attr_reader :tool_call_class, :model_class
|
15
|
-
end
|
16
|
-
|
17
13
|
attr_accessor :assume_model_exists, :context
|
18
14
|
|
19
15
|
def model=(value)
|
20
16
|
@model_string = value if value.is_a?(String)
|
21
|
-
|
17
|
+
return if value.is_a?(String)
|
18
|
+
|
19
|
+
if self.class.model_association_name == :model
|
20
|
+
super
|
21
|
+
else
|
22
|
+
self.model_association = value
|
23
|
+
end
|
22
24
|
end
|
23
25
|
|
24
26
|
def model_id=(value)
|
@@ -26,7 +28,7 @@ module RubyLLM
|
|
26
28
|
end
|
27
29
|
|
28
30
|
def model_id
|
29
|
-
|
31
|
+
model_association&.model_id
|
30
32
|
end
|
31
33
|
|
32
34
|
def provider=(value)
|
@@ -34,19 +36,21 @@ module RubyLLM
|
|
34
36
|
end
|
35
37
|
|
36
38
|
def provider
|
37
|
-
|
39
|
+
model_association&.provider
|
38
40
|
end
|
39
41
|
|
40
42
|
private
|
41
43
|
|
42
44
|
def resolve_model_from_strings # rubocop:disable Metrics/PerceivedComplexity
|
45
|
+
config = context&.config || RubyLLM.config
|
46
|
+
@model_string ||= config.default_model unless model_association
|
43
47
|
return unless @model_string
|
44
48
|
|
45
49
|
model_info, _provider = Models.resolve(
|
46
50
|
@model_string,
|
47
51
|
provider: @provider_string,
|
48
52
|
assume_exists: assume_model_exists || false,
|
49
|
-
config:
|
53
|
+
config: config
|
50
54
|
)
|
51
55
|
|
52
56
|
model_class = self.class.model_class.constantize
|
@@ -64,7 +68,7 @@ module RubyLLM
|
|
64
68
|
m.metadata = model_info.metadata || {}
|
65
69
|
end
|
66
70
|
|
67
|
-
self.
|
71
|
+
self.model_association = model_record
|
68
72
|
@model_string = nil
|
69
73
|
@provider_string = nil
|
70
74
|
end
|
@@ -72,15 +76,14 @@ module RubyLLM
|
|
72
76
|
public
|
73
77
|
|
74
78
|
def to_llm
|
75
|
-
|
76
|
-
|
79
|
+
model_record = model_association
|
77
80
|
@chat ||= (context || RubyLLM).chat(
|
78
|
-
model:
|
79
|
-
provider:
|
81
|
+
model: model_record.model_id,
|
82
|
+
provider: model_record.provider.to_sym
|
80
83
|
)
|
81
84
|
@chat.reset_messages!
|
82
85
|
|
83
|
-
|
86
|
+
messages_association.each do |msg|
|
84
87
|
@chat.add_message(msg.to_llm)
|
85
88
|
end
|
86
89
|
|
@@ -89,8 +92,8 @@ module RubyLLM
|
|
89
92
|
|
90
93
|
def with_instructions(instructions, replace: false)
|
91
94
|
transaction do
|
92
|
-
|
93
|
-
|
95
|
+
messages_association.where(role: :system).destroy_all if replace
|
96
|
+
messages_association.create!(role: :system, content: instructions)
|
94
97
|
end
|
95
98
|
to_llm.with_instructions(instructions)
|
96
99
|
self
|
@@ -106,12 +109,13 @@ module RubyLLM
|
|
106
109
|
self
|
107
110
|
end
|
108
111
|
|
109
|
-
def with_model(model_name, provider: nil)
|
110
|
-
self.provider = provider if provider
|
112
|
+
def with_model(model_name, provider: nil, assume_exists: false)
|
111
113
|
self.model = model_name
|
114
|
+
self.provider = provider if provider
|
115
|
+
self.assume_model_exists = assume_exists
|
112
116
|
resolve_model_from_strings
|
113
117
|
save!
|
114
|
-
to_llm.with_model(model.model_id, provider: model.provider.to_sym)
|
118
|
+
to_llm.with_model(model.model_id, provider: model.provider.to_sym, assume_exists:)
|
115
119
|
self
|
116
120
|
end
|
117
121
|
|
@@ -170,7 +174,7 @@ module RubyLLM
|
|
170
174
|
end
|
171
175
|
|
172
176
|
def create_user_message(content, with: nil)
|
173
|
-
message_record =
|
177
|
+
message_record = messages_association.create!(role: :user, content: content)
|
174
178
|
persist_content(message_record, with) if with.present?
|
175
179
|
message_record
|
176
180
|
end
|
@@ -198,8 +202,8 @@ module RubyLLM
|
|
198
202
|
end
|
199
203
|
|
200
204
|
def cleanup_orphaned_tool_results # rubocop:disable Metrics/PerceivedComplexity
|
201
|
-
|
202
|
-
last =
|
205
|
+
messages_association.reload
|
206
|
+
last = messages_association.order(:id).last
|
203
207
|
|
204
208
|
return unless last&.tool_call? || last&.tool_result?
|
205
209
|
|
@@ -228,7 +232,7 @@ module RubyLLM
|
|
228
232
|
end
|
229
233
|
|
230
234
|
def persist_new_message
|
231
|
-
@message =
|
235
|
+
@message = messages_association.create!(role: :assistant, content: '')
|
232
236
|
end
|
233
237
|
|
234
238
|
def persist_message_completion(message) # rubocop:disable Metrics/PerceivedComplexity
|
@@ -247,15 +251,22 @@ module RubyLLM
|
|
247
251
|
content = content.to_json
|
248
252
|
end
|
249
253
|
|
250
|
-
|
254
|
+
attrs = {
|
251
255
|
role: message.role,
|
252
256
|
content: content,
|
253
|
-
model: model,
|
254
257
|
input_tokens: message.input_tokens,
|
255
258
|
output_tokens: message.output_tokens
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
+
}
|
260
|
+
|
261
|
+
# Add model association dynamically
|
262
|
+
attrs[self.class.model_association_name] = model_association
|
263
|
+
|
264
|
+
if tool_call_id
|
265
|
+
parent_tool_call_assoc = @message.class.reflect_on_association(:parent_tool_call)
|
266
|
+
attrs[parent_tool_call_assoc.foreign_key] = tool_call_id
|
267
|
+
end
|
268
|
+
|
269
|
+
@message.update!(attrs)
|
259
270
|
|
260
271
|
persist_content(@message, attachments_to_persist) if attachments_to_persist
|
261
272
|
persist_tool_calls(message.tool_calls) if message.tool_calls.present?
|
@@ -266,12 +277,22 @@ module RubyLLM
|
|
266
277
|
tool_calls.each_value do |tool_call|
|
267
278
|
attributes = tool_call.to_h
|
268
279
|
attributes[:tool_call_id] = attributes.delete(:id)
|
269
|
-
@message.
|
280
|
+
@message.tool_calls_association.create!(**attributes)
|
270
281
|
end
|
271
282
|
end
|
272
283
|
|
273
284
|
def find_tool_call_id(tool_call_id)
|
274
|
-
|
285
|
+
messages = messages_association
|
286
|
+
message_class = messages.klass
|
287
|
+
tool_calls_assoc = message_class.tool_calls_association_name
|
288
|
+
tool_call_table_name = message_class.reflect_on_association(tool_calls_assoc).table_name
|
289
|
+
|
290
|
+
message_with_tool_call = messages.joins(tool_calls_assoc)
|
291
|
+
.find_by(tool_call_table_name => { tool_call_id: tool_call_id })
|
292
|
+
return nil unless message_with_tool_call
|
293
|
+
|
294
|
+
tool_call = message_with_tool_call.tool_calls_association.find_by(tool_call_id: tool_call_id)
|
295
|
+
tool_call&.id
|
275
296
|
end
|
276
297
|
|
277
298
|
def persist_content(message_record, attachments)
|
@@ -18,14 +18,14 @@ module RubyLLM
|
|
18
18
|
tool_call_id: extract_tool_call_id,
|
19
19
|
input_tokens: input_tokens,
|
20
20
|
output_tokens: output_tokens,
|
21
|
-
model_id: model_id
|
21
|
+
model_id: model_association&.model_id
|
22
22
|
)
|
23
23
|
end
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
27
|
def extract_tool_calls
|
28
|
-
|
28
|
+
tool_calls_association.to_h do |tool_call|
|
29
29
|
[
|
30
30
|
tool_call.tool_call_id,
|
31
31
|
RubyLLM::ToolCall.new(
|
data/lib/ruby_llm/aliases.json
CHANGED
@@ -227,14 +227,6 @@
|
|
227
227
|
"openai": "gpt-5-nano",
|
228
228
|
"openrouter": "openai/gpt-5-nano"
|
229
229
|
},
|
230
|
-
"gpt-oss-120b": {
|
231
|
-
"openai": "gpt-oss-120b",
|
232
|
-
"openrouter": "openai/gpt-oss-120b"
|
233
|
-
},
|
234
|
-
"gpt-oss-20b": {
|
235
|
-
"openai": "gpt-oss-20b",
|
236
|
-
"openrouter": "openai/gpt-oss-20b"
|
237
|
-
},
|
238
230
|
"o1": {
|
239
231
|
"openai": "o1",
|
240
232
|
"openrouter": "openai/o1"
|
@@ -32,6 +32,8 @@ module RubyLLM
|
|
32
32
|
:default_image_model,
|
33
33
|
# Model registry
|
34
34
|
:model_registry_class,
|
35
|
+
# Rails integration
|
36
|
+
:use_new_acts_as,
|
35
37
|
# Connection configuration
|
36
38
|
:request_timeout,
|
37
39
|
:max_retries,
|
@@ -57,6 +59,9 @@ module RubyLLM
|
|
57
59
|
@default_embedding_model = 'text-embedding-3-small'
|
58
60
|
@default_image_model = 'gpt-image-1'
|
59
61
|
|
62
|
+
@model_registry_class = 'Model'
|
63
|
+
@use_new_acts_as = false
|
64
|
+
|
60
65
|
@log_file = $stdout
|
61
66
|
@log_level = ENV['RUBYLLM_DEBUG'] ? Logger::DEBUG : Logger::INFO
|
62
67
|
@log_stream_debug = ENV['RUBYLLM_STREAM_DEBUG'] == 'true'
|