ruby_llm_swarm 1.9.1 → 1.9.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.
- checksums.yaml +4 -4
- data/lib/ruby_llm/active_record/acts_as.rb +130 -128
- data/lib/ruby_llm/active_record/acts_as_legacy.rb +301 -299
- data/lib/ruby_llm/active_record/chat_methods.rb +278 -276
- data/lib/ruby_llm/active_record/message_methods.rb +60 -58
- data/lib/ruby_llm/active_record/model_methods.rb +68 -66
- data/lib/ruby_llm/railtie.rb +4 -0
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +1 -1
- metadata +1 -1
|
@@ -1,349 +1,351 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
module
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
if defined?(ActiveRecord::Base)
|
|
4
|
+
module RubyLLM
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
# Methods mixed into chat models.
|
|
7
|
+
module ChatMethods
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
before_save :resolve_model_from_strings
|
|
12
|
+
end
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
attr_accessor :assume_model_exists, :context
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
def model=(value)
|
|
17
|
+
@model_string = value if value.is_a?(String)
|
|
18
|
+
return if value.is_a?(String)
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
if self.class.model_association_name == :model
|
|
21
|
+
super
|
|
22
|
+
else
|
|
23
|
+
self.model_association = value
|
|
24
|
+
end
|
|
23
25
|
end
|
|
24
|
-
end
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
def model_id=(value)
|
|
28
|
+
@model_string = value
|
|
29
|
+
end
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
def model_id
|
|
32
|
+
model_association&.model_id
|
|
33
|
+
end
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
def provider=(value)
|
|
36
|
+
@provider_string = value
|
|
37
|
+
end
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
def provider
|
|
40
|
+
model_association&.provider
|
|
41
|
+
end
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
self.model_association = model_record
|
|
72
|
-
@model_string = nil
|
|
73
|
-
@provider_string = nil
|
|
74
|
-
end
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def resolve_model_from_strings # rubocop:disable Metrics/PerceivedComplexity
|
|
46
|
+
config = context&.config || RubyLLM.config
|
|
47
|
+
@model_string ||= config.default_model unless model_association
|
|
48
|
+
return unless @model_string
|
|
49
|
+
|
|
50
|
+
model_info, _provider = Models.resolve(
|
|
51
|
+
@model_string,
|
|
52
|
+
provider: @provider_string,
|
|
53
|
+
assume_exists: assume_model_exists || false,
|
|
54
|
+
config: config
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
model_class = self.class.model_class.constantize
|
|
58
|
+
model_record = model_class.find_or_create_by!(
|
|
59
|
+
model_id: model_info.id,
|
|
60
|
+
provider: model_info.provider
|
|
61
|
+
) do |m|
|
|
62
|
+
m.name = model_info.name || model_info.id
|
|
63
|
+
m.family = model_info.family
|
|
64
|
+
m.context_window = model_info.context_window
|
|
65
|
+
m.max_output_tokens = model_info.max_output_tokens
|
|
66
|
+
m.capabilities = model_info.capabilities || []
|
|
67
|
+
m.modalities = model_info.modalities || {}
|
|
68
|
+
m.pricing = model_info.pricing || {}
|
|
69
|
+
m.metadata = model_info.metadata || {}
|
|
70
|
+
end
|
|
75
71
|
|
|
76
|
-
|
|
72
|
+
self.model_association = model_record
|
|
73
|
+
@model_string = nil
|
|
74
|
+
@provider_string = nil
|
|
75
|
+
end
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
model_record = model_association
|
|
80
|
-
@chat ||= (context || RubyLLM).chat(
|
|
81
|
-
model: model_record.model_id,
|
|
82
|
-
provider: model_record.provider.to_sym
|
|
83
|
-
)
|
|
84
|
-
@chat.reset_messages!
|
|
77
|
+
public
|
|
85
78
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
79
|
+
def to_llm
|
|
80
|
+
model_record = model_association
|
|
81
|
+
@chat ||= (context || RubyLLM).chat(
|
|
82
|
+
model: model_record.model_id,
|
|
83
|
+
provider: model_record.provider.to_sym
|
|
84
|
+
)
|
|
85
|
+
@chat.reset_messages!
|
|
89
86
|
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
messages_association.each do |msg|
|
|
88
|
+
@chat.add_message(msg.to_llm)
|
|
89
|
+
end
|
|
92
90
|
|
|
93
|
-
|
|
94
|
-
transaction do
|
|
95
|
-
messages_association.where(role: :system).destroy_all if replace
|
|
96
|
-
messages_association.create!(role: :system, content: instructions)
|
|
91
|
+
setup_persistence_callbacks
|
|
97
92
|
end
|
|
98
|
-
to_llm.with_instructions(instructions)
|
|
99
|
-
self
|
|
100
|
-
end
|
|
101
93
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
94
|
+
def with_instructions(instructions, replace: false)
|
|
95
|
+
transaction do
|
|
96
|
+
messages_association.where(role: :system).destroy_all if replace
|
|
97
|
+
messages_association.create!(role: :system, content: instructions)
|
|
98
|
+
end
|
|
99
|
+
to_llm.with_instructions(instructions)
|
|
100
|
+
self
|
|
101
|
+
end
|
|
106
102
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
103
|
+
def with_tool(...)
|
|
104
|
+
to_llm.with_tool(...)
|
|
105
|
+
self
|
|
106
|
+
end
|
|
111
107
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
resolve_model_from_strings
|
|
117
|
-
save!
|
|
118
|
-
to_llm.with_model(model.model_id, provider: model.provider.to_sym, assume_exists:)
|
|
119
|
-
self
|
|
120
|
-
end
|
|
108
|
+
def with_tools(...)
|
|
109
|
+
to_llm.with_tools(...)
|
|
110
|
+
self
|
|
111
|
+
end
|
|
121
112
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
113
|
+
def with_model(model_name, provider: nil, assume_exists: false)
|
|
114
|
+
self.model = model_name
|
|
115
|
+
self.provider = provider if provider
|
|
116
|
+
self.assume_model_exists = assume_exists
|
|
117
|
+
resolve_model_from_strings
|
|
118
|
+
save!
|
|
119
|
+
to_llm.with_model(model.model_id, provider: model.provider.to_sym, assume_exists:)
|
|
120
|
+
self
|
|
121
|
+
end
|
|
126
122
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
123
|
+
def with_temperature(...)
|
|
124
|
+
to_llm.with_temperature(...)
|
|
125
|
+
self
|
|
126
|
+
end
|
|
131
127
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
128
|
+
def with_params(...)
|
|
129
|
+
to_llm.with_params(...)
|
|
130
|
+
self
|
|
131
|
+
end
|
|
136
132
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
133
|
+
def with_headers(...)
|
|
134
|
+
to_llm.with_headers(...)
|
|
135
|
+
self
|
|
136
|
+
end
|
|
141
137
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
138
|
+
def with_schema(...)
|
|
139
|
+
to_llm.with_schema(...)
|
|
140
|
+
self
|
|
141
|
+
end
|
|
146
142
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
143
|
+
def on_new_message(&)
|
|
144
|
+
to_llm.on_new_message(&)
|
|
145
|
+
self
|
|
146
|
+
end
|
|
151
147
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
148
|
+
def on_end_message(&)
|
|
149
|
+
to_llm.on_end_message(&)
|
|
150
|
+
self
|
|
151
|
+
end
|
|
156
152
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
153
|
+
def on_tool_call(...)
|
|
154
|
+
to_llm.on_tool_call(...)
|
|
155
|
+
self
|
|
156
|
+
end
|
|
161
157
|
|
|
162
|
-
|
|
163
|
-
|
|
158
|
+
def on_tool_result(...)
|
|
159
|
+
to_llm.on_tool_result(...)
|
|
160
|
+
self
|
|
161
|
+
end
|
|
164
162
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
message_record.content_raw = content_raw if message_record.respond_to?(:content_raw=)
|
|
168
|
-
message_record.save!
|
|
163
|
+
def create_user_message(content, with: nil)
|
|
164
|
+
content_text, attachments, content_raw = prepare_content_for_storage(content)
|
|
169
165
|
|
|
170
|
-
|
|
171
|
-
|
|
166
|
+
message_record = messages_association.build(role: :user)
|
|
167
|
+
message_record.content = content_text
|
|
168
|
+
message_record.content_raw = content_raw if message_record.respond_to?(:content_raw=)
|
|
169
|
+
message_record.save!
|
|
172
170
|
|
|
173
|
-
|
|
174
|
-
|
|
171
|
+
persist_content(message_record, with) if with.present?
|
|
172
|
+
persist_content(message_record, attachments) if attachments.present?
|
|
175
173
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
complete(&)
|
|
179
|
-
end
|
|
174
|
+
message_record
|
|
175
|
+
end
|
|
180
176
|
|
|
181
|
-
|
|
177
|
+
def ask(message, with: nil, &)
|
|
178
|
+
create_user_message(message, with:)
|
|
179
|
+
complete(&)
|
|
180
|
+
end
|
|
182
181
|
|
|
183
|
-
|
|
184
|
-
to_llm.complete(...)
|
|
185
|
-
rescue RubyLLM::Error => e
|
|
186
|
-
cleanup_failed_messages if @message&.persisted? && @message.content.blank?
|
|
187
|
-
cleanup_orphaned_tool_results
|
|
188
|
-
raise e
|
|
189
|
-
end
|
|
182
|
+
alias say ask
|
|
190
183
|
|
|
191
|
-
|
|
184
|
+
def complete(...)
|
|
185
|
+
to_llm.complete(...)
|
|
186
|
+
rescue RubyLLM::Error => e
|
|
187
|
+
cleanup_failed_messages if @message&.persisted? && @message.content.blank?
|
|
188
|
+
cleanup_orphaned_tool_results
|
|
189
|
+
raise e
|
|
190
|
+
end
|
|
192
191
|
|
|
193
|
-
|
|
194
|
-
RubyLLM.logger.warn "RubyLLM: API call failed, destroying message: #{@message.id}"
|
|
195
|
-
@message.destroy
|
|
196
|
-
end
|
|
192
|
+
private
|
|
197
193
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
194
|
+
def cleanup_failed_messages
|
|
195
|
+
RubyLLM.logger.warn "RubyLLM: API call failed, destroying message: #{@message.id}"
|
|
196
|
+
@message.destroy
|
|
197
|
+
end
|
|
201
198
|
|
|
202
|
-
|
|
199
|
+
def cleanup_orphaned_tool_results # rubocop:disable Metrics/PerceivedComplexity
|
|
200
|
+
messages_association.reload
|
|
201
|
+
last = messages_association.order(:id).last
|
|
203
202
|
|
|
204
|
-
|
|
205
|
-
last.destroy
|
|
206
|
-
elsif last.tool_result?
|
|
207
|
-
tool_call_message = last.parent_tool_call.message
|
|
208
|
-
expected_results = tool_call_message.tool_calls.pluck(:id)
|
|
209
|
-
actual_results = tool_call_message.tool_results.pluck(:tool_call_id)
|
|
203
|
+
return unless last&.tool_call? || last&.tool_result?
|
|
210
204
|
|
|
211
|
-
if
|
|
212
|
-
|
|
213
|
-
|
|
205
|
+
if last.tool_call?
|
|
206
|
+
last.destroy
|
|
207
|
+
elsif last.tool_result?
|
|
208
|
+
tool_call_message = last.parent_tool_call.message
|
|
209
|
+
expected_results = tool_call_message.tool_calls.pluck(:id)
|
|
210
|
+
actual_results = tool_call_message.tool_results.pluck(:tool_call_id)
|
|
211
|
+
|
|
212
|
+
if expected_results.sort != actual_results.sort
|
|
213
|
+
tool_call_message.tool_results.each(&:destroy)
|
|
214
|
+
tool_call_message.destroy
|
|
215
|
+
end
|
|
214
216
|
end
|
|
215
217
|
end
|
|
216
|
-
end
|
|
217
218
|
|
|
218
|
-
|
|
219
|
-
|
|
219
|
+
def setup_persistence_callbacks
|
|
220
|
+
return @chat if @chat.instance_variable_get(:@_persistence_callbacks_setup)
|
|
220
221
|
|
|
221
|
-
|
|
222
|
-
|
|
222
|
+
@chat.on_new_message { persist_new_message }
|
|
223
|
+
@chat.on_end_message { |msg| persist_message_completion(msg) }
|
|
223
224
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
225
|
+
@chat.instance_variable_set(:@_persistence_callbacks_setup, true)
|
|
226
|
+
@chat
|
|
227
|
+
end
|
|
227
228
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
229
|
+
def persist_new_message
|
|
230
|
+
@message = messages_association.create!(role: :assistant, content: '')
|
|
231
|
+
end
|
|
231
232
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
233
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
|
234
|
+
def persist_message_completion(message)
|
|
235
|
+
return unless message
|
|
235
236
|
|
|
236
|
-
|
|
237
|
+
tool_call_id = find_tool_call_id(message.tool_call_id) if message.tool_call_id
|
|
237
238
|
|
|
238
|
-
|
|
239
|
-
|
|
239
|
+
transaction do
|
|
240
|
+
content_text, attachments_to_persist, content_raw = prepare_content_for_storage(message.content)
|
|
240
241
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
242
|
+
attrs = {
|
|
243
|
+
role: message.role,
|
|
244
|
+
content: content_text,
|
|
245
|
+
input_tokens: message.input_tokens,
|
|
246
|
+
output_tokens: message.output_tokens
|
|
247
|
+
}
|
|
248
|
+
attrs[:cached_tokens] = message.cached_tokens if @message.has_attribute?(:cached_tokens)
|
|
249
|
+
if @message.has_attribute?(:cache_creation_tokens)
|
|
250
|
+
attrs[:cache_creation_tokens] = message.cache_creation_tokens
|
|
251
|
+
end
|
|
251
252
|
|
|
252
|
-
|
|
253
|
-
|
|
253
|
+
# Add model association dynamically
|
|
254
|
+
attrs[self.class.model_association_name] = model_association
|
|
254
255
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
256
|
+
if tool_call_id
|
|
257
|
+
parent_tool_call_assoc = @message.class.reflect_on_association(:parent_tool_call)
|
|
258
|
+
attrs[parent_tool_call_assoc.foreign_key] = tool_call_id
|
|
259
|
+
end
|
|
259
260
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
261
|
+
@message.assign_attributes(attrs)
|
|
262
|
+
@message.content_raw = content_raw if @message.respond_to?(:content_raw=)
|
|
263
|
+
@message.save!
|
|
263
264
|
|
|
264
|
-
|
|
265
|
-
|
|
265
|
+
persist_content(@message, attachments_to_persist) if attachments_to_persist
|
|
266
|
+
persist_tool_calls(message.tool_calls) if message.tool_calls.present?
|
|
267
|
+
end
|
|
266
268
|
end
|
|
267
|
-
|
|
268
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
|
269
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
|
269
270
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
271
|
+
def persist_tool_calls(tool_calls)
|
|
272
|
+
tool_calls.each_value do |tool_call|
|
|
273
|
+
attributes = tool_call.to_h
|
|
274
|
+
attributes[:tool_call_id] = attributes.delete(:id)
|
|
275
|
+
@message.tool_calls_association.create!(**attributes)
|
|
276
|
+
end
|
|
275
277
|
end
|
|
276
|
-
end
|
|
277
278
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
279
|
+
def find_tool_call_id(tool_call_id)
|
|
280
|
+
messages = messages_association
|
|
281
|
+
message_class = messages.klass
|
|
282
|
+
tool_calls_assoc = message_class.tool_calls_association_name
|
|
283
|
+
tool_call_table_name = message_class.reflect_on_association(tool_calls_assoc).table_name
|
|
283
284
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
285
|
+
message_with_tool_call = messages.joins(tool_calls_assoc)
|
|
286
|
+
.find_by(tool_call_table_name => { tool_call_id: tool_call_id })
|
|
287
|
+
return nil unless message_with_tool_call
|
|
287
288
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
289
|
+
tool_call = message_with_tool_call.tool_calls_association.find_by(tool_call_id: tool_call_id)
|
|
290
|
+
tool_call&.id
|
|
291
|
+
end
|
|
291
292
|
|
|
292
|
-
|
|
293
|
-
|
|
293
|
+
def persist_content(message_record, attachments)
|
|
294
|
+
return unless message_record.respond_to?(:attachments)
|
|
294
295
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
296
|
+
attachables = prepare_for_active_storage(attachments)
|
|
297
|
+
message_record.attachments.attach(attachables) if attachables.any?
|
|
298
|
+
end
|
|
298
299
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
300
|
+
def prepare_for_active_storage(attachments)
|
|
301
|
+
Utils.to_safe_array(attachments).filter_map do |attachment|
|
|
302
|
+
case attachment
|
|
303
|
+
when ActionDispatch::Http::UploadedFile, ActiveStorage::Blob
|
|
304
|
+
attachment
|
|
305
|
+
when ActiveStorage::Attached::One, ActiveStorage::Attached::Many
|
|
306
|
+
attachment.blobs
|
|
307
|
+
when Hash
|
|
308
|
+
attachment.values.map { |v| prepare_for_active_storage(v) }
|
|
309
|
+
else
|
|
310
|
+
convert_to_active_storage_format(attachment)
|
|
311
|
+
end
|
|
312
|
+
end.flatten.compact
|
|
313
|
+
end
|
|
313
314
|
|
|
314
|
-
|
|
315
|
-
|
|
315
|
+
def convert_to_active_storage_format(source)
|
|
316
|
+
return if source.blank?
|
|
316
317
|
|
|
317
|
-
|
|
318
|
+
attachment = source.is_a?(RubyLLM::Attachment) ? source : RubyLLM::Attachment.new(source)
|
|
318
319
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
320
|
+
{
|
|
321
|
+
io: StringIO.new(attachment.content),
|
|
322
|
+
filename: attachment.filename,
|
|
323
|
+
content_type: attachment.mime_type
|
|
324
|
+
}
|
|
325
|
+
rescue StandardError => e
|
|
326
|
+
RubyLLM.logger.warn "Failed to process attachment #{source}: #{e.message}"
|
|
327
|
+
nil
|
|
328
|
+
end
|
|
328
329
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
330
|
+
def prepare_content_for_storage(content)
|
|
331
|
+
attachments = nil
|
|
332
|
+
content_raw = nil
|
|
333
|
+
content_text = content
|
|
334
|
+
|
|
335
|
+
case content
|
|
336
|
+
when RubyLLM::Content::Raw
|
|
337
|
+
content_raw = content.value
|
|
338
|
+
content_text = nil
|
|
339
|
+
when RubyLLM::Content
|
|
340
|
+
attachments = content.attachments if content.attachments.any?
|
|
341
|
+
content_text = content.text
|
|
342
|
+
when Hash, Array
|
|
343
|
+
content_raw = content
|
|
344
|
+
content_text = nil
|
|
345
|
+
end
|
|
333
346
|
|
|
334
|
-
|
|
335
|
-
when RubyLLM::Content::Raw
|
|
336
|
-
content_raw = content.value
|
|
337
|
-
content_text = nil
|
|
338
|
-
when RubyLLM::Content
|
|
339
|
-
attachments = content.attachments if content.attachments.any?
|
|
340
|
-
content_text = content.text
|
|
341
|
-
when Hash, Array
|
|
342
|
-
content_raw = content
|
|
343
|
-
content_text = nil
|
|
347
|
+
[content_text, attachments, content_raw]
|
|
344
348
|
end
|
|
345
|
-
|
|
346
|
-
[content_text, attachments, content_raw]
|
|
347
349
|
end
|
|
348
350
|
end
|
|
349
351
|
end
|